Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private void WritePropertyInternal(CodeProperty codeElement, LanguageWriter writ
if (parentClass.GetPrimaryMessageCodePath(static x => x.Name.ToFirstCharacterUpperCase(), static x => x.Name.ToFirstCharacterUpperCase(), "?.") is string primaryMessageCodePath && !string.IsNullOrEmpty(primaryMessageCodePath))
writer.WriteLine($"public override {propertyType} {codeElement.Name.ToFirstCharacterUpperCase()} {{ get => {primaryMessageCodePath} ?? string.Empty; }}");
else
writer.WriteLine($"public override {propertyType} {codeElement.Name.ToFirstCharacterUpperCase()} {{ get => base.Message; }}");
writer.WriteLine($"public override {propertyType} {codeElement.Name.ToFirstCharacterUpperCase()} {{ get => $\"{{ResponseStatusCode}}: {{base.Message}}\"; }}");
break;
case CodePropertyKind.QueryParameter when codeElement.IsNameEscaped:
writer.WriteLine($"[QueryParameter(\"{codeElement.SerializationName}\")]");
Expand Down
3 changes: 2 additions & 1 deletion src/Kiota.Builder/Writers/Dart/CodePropertyWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ private void WritePropertyInternal(CodeProperty codeElement, LanguageWriter writ
break;
case CodePropertyKind.ErrorMessageOverride when parentClass.IsErrorDefinition:
writer.WriteLine("@override");
goto default;
writer.WriteLine($"{propertyType} get {codeElement.Name} => '$responseStatusCode: ${{super.message}}';");
break;
case CodePropertyKind.QueryParameter when codeElement.IsNameEscaped:
writer.WriteLine($"/// @QueryParameter('{codeElement.SerializationName}')");
goto default;
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private static void WriteErrorMethodOverride(CodeClass parentClass, LanguageWrit
}
else
{
writer.WriteLine("return m.ApiError.Error()");
writer.WriteLine("return fmt.Sprintf(\"%d: %s\", m.ResponseStatusCode, m.ApiError.Error())");
}
}
private void WriteRawUrlBuilderBody(CodeClass parentClass, CodeMethod codeElement, LanguageWriter writer)
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ private static void WriteErrorMethodOverride(CodeClass parentClass, LanguageWrit
}
else
{
writer.WriteLine("return super.getMessage();");
writer.WriteLine("return getResponseStatusCode() + \": \" + super.getMessage();");
}
}
private void WriteRawUrlBuilderBody(CodeClass parentClass, CodeMethod codeElement, LanguageWriter writer)
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private void WriteErrorMessageOverride(CodeClass parentClass, LanguageWriter wri
}
else
{
writer.WriteLine("return parent::getMessage();");
writer.WriteLine("return $this->getResponseStatusCode() . ': ' . parent::getMessage();");
}
}
private const string UrlTemplateTempVarName = "$urlTplParams";
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Writers/Python/CodePropertyWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public override void WriteCodeElement(CodeProperty codeElement, LanguageWriter w
writer.WriteLine("return ''");
}
else
writer.WriteLine("return super().message");
writer.WriteLine("return f'{self.response_status_code}: {super().message}'");
writer.DecreaseIndent();
break;
}
Expand Down
20 changes: 17 additions & 3 deletions src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,13 @@ private void WriteDeserializerFunctionProperties(CodeParameter param, CodeInterf
WritePropertyDeserializationBlock(otherProp, param, primaryErrorMapping, primaryErrorMappingKey, codeFile, writer);
}

// Fallback: If no primaryErrorMappingKey, emit error message assignment here
if (string.IsNullOrEmpty(primaryErrorMappingKey) && !string.IsNullOrEmpty(primaryErrorMapping))
{
writer.WriteLine("// Fallback error message assignment for error definitions without primary message");
writer.WriteLine(primaryErrorMapping.Trim());
}

writer.CloseBlock();
}

Expand All @@ -630,10 +637,17 @@ private static (string, string) GetPrimaryErrorMapping(CodeFunction codeFunction
var primaryErrorMappingKey = string.Empty;
var parentClass = codeFunction.OriginalMethodParentClass;

if (parentClass.IsErrorDefinition && parentClass.AssociatedInterface is not null && parentClass.AssociatedInterface.GetPrimaryMessageCodePath(static x => x.Name.ToFirstCharacterLowerCase(), static x => x.Name.ToFirstCharacterLowerCase(), "?.") is string primaryMessageCodePath && !string.IsNullOrEmpty(primaryMessageCodePath))
if (parentClass.IsErrorDefinition)
{
primaryErrorMapping = $" {param.Name.ToFirstCharacterLowerCase()}.message = {param.Name.ToFirstCharacterLowerCase()}.{primaryMessageCodePath} ?? \"\";";
primaryErrorMappingKey = primaryMessageCodePath.Split("?.", StringSplitOptions.RemoveEmptyEntries)[0];
if (parentClass.AssociatedInterface is not null && parentClass.AssociatedInterface.GetPrimaryMessageCodePath(static x => x.Name.ToFirstCharacterLowerCase(), static x => x.Name.ToFirstCharacterLowerCase(), "?.") is string primaryMessageCodePath && !string.IsNullOrEmpty(primaryMessageCodePath))
{
primaryErrorMapping = $" {param.Name.ToFirstCharacterLowerCase()}.message = {param.Name.ToFirstCharacterLowerCase()}.{primaryMessageCodePath} ?? \"\";";
primaryErrorMappingKey = primaryMessageCodePath.Split("?.", StringSplitOptions.RemoveEmptyEntries)[0];
}
else
{
primaryErrorMapping = $" {param.Name.ToFirstCharacterLowerCase()}.message = `${{{param.Name.ToFirstCharacterLowerCase()}.responseStatusCode}}: ${{super.message ?? \"\"}}`;";
}
}

return (primaryErrorMapping, primaryErrorMappingKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,5 +249,29 @@ public void WritesMessageOverrideOnPrimary()
// Then
Assert.Contains("public override string Message { get => Prop1 ?? string.Empty; }", result);
}

[Fact]
public void WritesMessageOverrideWithStatusCodeWhenNoPrimaryMessage()
{
// Given
parentClass.IsErrorDefinition = true;
// No primary error message property added
var overrideProperty = parentClass.AddProperty(new CodeProperty
{
Name = "Message",
Kind = CodePropertyKind.ErrorMessageOverride,
Type = new CodeType
{
Name = "string",
},
}).First();

// When
writer.Write(overrideProperty);
var result = tw.ToString();

// Then
Assert.Contains("public override string Message { get => $\"{ResponseStatusCode}: {base.Message}\"; }", result);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,15 @@ public void DoesntWritePropertiesExistingInParentType()
var result = tw.ToString();
Assert.Empty(result);
}

[Fact]
public void WriteErrorMessageOverrideWithStatusCodeWhenNoPrimary()
{
property.Kind = CodePropertyKind.ErrorMessageOverride;
parentClass.IsErrorDefinition = true;
writer.Write(property);
var result = tw.ToString();
Assert.Contains("@override", result);
Assert.Contains("get propertyName => '$responseStatusCode: ${super.message}';", result);
}
}
33 changes: 33 additions & 0 deletions tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2304,6 +2304,39 @@ public void WritesMessageOverrideOnPrimary()
Assert.Contains("return *(m.GetProp1()", result);
}

[Fact]
public void WritesMessageOverrideWithStatusCodeWhenNoPrimary()
{
// Given
parentClass = root.AddClass(new CodeClass
{
Name = "parentClass",
IsErrorDefinition = true,
Kind = CodeClassKind.Model,
}).First();
// No primary error message property added
var method = parentClass.AddMethod(new CodeMethod
{
Kind = CodeMethodKind.ErrorMessageOverride,
ReturnType = new CodeType
{
Name = "string",
IsNullable = false,
},
IsAsync = false,
IsStatic = false,
Name = "Error"
}).First();

// When
writer.Write(method);
var result = tw.ToString();

// Then
Assert.Contains("Error()(string) {", result);
Assert.Contains("return fmt.Sprintf(\"%d: %s\", m.ResponseStatusCode, m.ApiError.Error())", result);
}

[Fact]
public void WritesRequestGeneratorAcceptHeaderQuotes()
{
Expand Down
34 changes: 34 additions & 0 deletions tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2165,6 +2165,40 @@ public void WritesMessageOverrideOnPrimary()
Assert.Contains("return this.getProp1()", result);
}

[Fact]
public void WritesMessageOverrideWithStatusCodeWhenNoPrimary()
{
// Given
parentClass = root.AddClass(new CodeClass
{
Name = "parentClass",
IsErrorDefinition = true,
Kind = CodeClassKind.Model,
}).First();
// No primary error message property added
var method = parentClass.AddMethod(new CodeMethod
{
Kind = CodeMethodKind.ErrorMessageOverride,
ReturnType = new CodeType
{
Name = "String",
IsNullable = false,
},
IsAsync = false,
IsStatic = false,
Name = "getErrorMessage"
}).First();

// When
writer.Write(method);
var result = tw.ToString();

// Then
Assert.Contains("@Override", result);
Assert.Contains("String getErrorMessage() ", result);
Assert.Contains("return getResponseStatusCode() + \": \" + super.getMessage();", result);
}

[Fact]
public void WritesRequestGeneratorAcceptHeaderQuotes()
{
Expand Down
26 changes: 26 additions & 0 deletions tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,32 @@ public async Task WriteErrorMessageOverrideAsync()

Assert.Contains("return $primaryError->getMessage() ?? '';", result);
}

[Fact]
public async Task WriteErrorMessageOverrideWithStatusCodeWhenNoPrimaryAsync()
{
setup();
var error401 = root.AddClass(new CodeClass
{
Name = "Error401",
IsErrorDefinition = true
}).First();
// No primary error message property added

var codeMethod = new CodeMethod
{
Kind = CodeMethodKind.ErrorMessageOverride,
ReturnType = new CodeType { Name = "string" },
SimpleName = "getPrimaryErrorMessage",
};
error401.AddMethod(codeMethod);
await ILanguageRefiner.RefineAsync(new GenerationConfiguration { Language = GenerationLanguage.PHP }, root);
_codeMethodWriter.WriteCodeElement(codeMethod, languageWriter);
var result = stringWriter.ToString();

Assert.Contains("return $this->getResponseStatusCode() . ': ' . parent::getMessage();", result);
}

[Fact]
public async Task WritesRequestExecutorForEnumTypesAsync()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ public void WritePrimaryErrorMessagePropertyOption1()
var result = tw.ToString();
Assert.Contains("super().message", result);
}

[Fact]
public void WriteMessageOverrideWithStatusCodeWhenNoPrimary()
{
property.Kind = CodePropertyKind.ErrorMessageOverride;
parentClass.IsErrorDefinition = true;
writer.Write(property);
var result = tw.ToString();
Assert.Contains("return f'{self.response_status_code}: {super().message}'", result);
}

[Fact]
public void WritePrimaryErrorMessagePropertyOption2()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1717,5 +1717,86 @@ public void WritesByteArrayPropertyDeserialization()
var result = tw.ToString();
Assert.Contains("\"property\": n => { model.property = n.getByteArrayValue(); }", result, StringComparison.Ordinal);
}

[Fact]
public void WritesErrorMessageAssignmentWithStatusCodeWhenNoPrimary()
{
var errorClass = root.AddClass(new CodeClass
{
Name = "Error4XX",
IsErrorDefinition = true,
Kind = CodeClassKind.Model,
}).First();

// Create associated interface (required for TypeScript)
var errorInterface = root.AddInterface(new CodeInterface
{
Name = "Error4XXInterface",
Kind = CodeInterfaceKind.Model,
OriginalClass = errorClass
}).First();

// Add the same property to both class and interface
var codeProperty = new CodeProperty
{
Name = "code",
Kind = CodePropertyKind.Custom,
Type = new CodeType { Name = "string" },
};
errorClass.AddProperty(codeProperty);
errorInterface.AddProperty(new CodeProperty
{
Name = "code",
Kind = CodePropertyKind.Custom,
Type = new CodeType { Name = "string" },
});

// Link class to interface
errorClass.AssociatedInterface = errorInterface;

// No primary error message property added - this should trigger our fallback

// Create deserializer method manually (like the refiner would)
var deserializerMethod = errorClass.AddMethod(new CodeMethod
{
Name = "deserializeIntoError4XX",
Kind = CodeMethodKind.Deserializer,
IsStatic = true,
ReturnType = new CodeType
{
Name = "Record<string, (node: ParseNode) => void>",
},
}).First();

deserializerMethod.AddParameter(new CodeParameter
{
Name = "error4XX",
Kind = CodeParameterKind.RequestBody,
Type = new CodeType
{
Name = "Error4XXInterface",
TypeDefinition = errorInterface,
},
});

var function = new CodeFunction(deserializerMethod);
root.TryAddCodeFile("error4XX", function);
writer.Write(function);
var result = tw.ToString();

// The result should contain deserializer properties and the error message assignment
Assert.Contains("error4XX.code = n.getStringValue()", result); // Basic property assignment should exist

// The error message assignment should be generated because this is an error definition without primary message
// It should contain our enhancement that includes the status code
var expectedErrorMessage = "error4XX.message = `${error4XX.responseStatusCode}: ${super.message ?? \"\"}`";
Assert.Contains(expectedErrorMessage, result);

// Verify the error message assignment appears exactly once
var occurrences = result.Split(new[] { expectedErrorMessage }, StringSplitOptions.None).Length - 1;
Assert.Equal(1, occurrences);

Assert.Contains("deserializeIntoError4XX", result);
}
}