From 30411d3f989be3b0f48eacfcbeee38a5110abe40 Mon Sep 17 00:00:00 2001 From: Koen van Leeuwen Date: Wed, 24 Sep 2025 11:59:26 +0200 Subject: [PATCH 1/7] override Message getter to include ResponseStatusCode --- .../Writers/CSharp/CodePropertyWriter.cs | 2 +- .../Writers/CSharp/CodePropertyWriterTests.cs | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs index 07d2272db5..6bcf01bad1 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs @@ -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}\")]"); diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs index c0fec9dc0d..4004e9d008 100644 --- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs @@ -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); + } } From 4535be2edf3e511ddeae2b14e3a7f350f5259b67 Mon Sep 17 00:00:00 2001 From: Koen van Leeuwen Date: Thu, 25 Sep 2025 10:47:57 +0200 Subject: [PATCH 2/7] Implement similar languages --- .../Writers/Go/CodeMethodWriter.cs | 2 +- .../Writers/Java/CodeMethodWriter.cs | 2 +- .../Writers/Php/CodeMethodWriter.cs | 2 +- .../Writers/Python/CodePropertyWriter.cs | 2 +- .../Writers/Go/CodeMethodWriterTests.cs | 33 ++++++++++++++++++ .../Writers/Java/CodeMethodWriterTests.cs | 34 +++++++++++++++++++ .../Writers/Php/CodeMethodWriterTests.cs | 26 ++++++++++++++ .../Writers/Python/CodePropertyWriterTests.cs | 11 ++++++ 8 files changed, 108 insertions(+), 4 deletions(-) diff --git a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs index ea026bd4c8..5bfb8f6c00 100644 --- a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs @@ -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) diff --git a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs index 67e8dbdab6..c281c05649 100644 --- a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs @@ -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) diff --git a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs index c1b25d996f..b63f60c8c6 100644 --- a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs @@ -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"; diff --git a/src/Kiota.Builder/Writers/Python/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/Python/CodePropertyWriter.cs index 4e5c40cebd..8f96fb00f3 100644 --- a/src/Kiota.Builder/Writers/Python/CodePropertyWriter.cs +++ b/src/Kiota.Builder/Writers/Python/CodePropertyWriter.cs @@ -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; } diff --git a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs index e9b5f586fa..c43e3a2f85 100644 --- a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs @@ -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() { diff --git a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs index f69dc1f24d..6bb30c65bd 100644 --- a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs @@ -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() { diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs index ad5c54b056..f3f8cf7a09 100644 --- a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs @@ -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() { diff --git a/tests/Kiota.Builder.Tests/Writers/Python/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Python/CodePropertyWriterTests.cs index ce0163df05..b410a8ef73 100644 --- a/tests/Kiota.Builder.Tests/Writers/Python/CodePropertyWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Python/CodePropertyWriterTests.cs @@ -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() { From c899bbb13f9353865d06fac15dbb324a2b2e7225 Mon Sep 17 00:00:00 2001 From: Koen van Leeuwen Date: Thu, 25 Sep 2025 10:57:03 +0200 Subject: [PATCH 3/7] Add Dart --- src/Kiota.Builder/Writers/Dart/CodePropertyWriter.cs | 3 ++- .../Writers/Dart/CodePropertyWriterTests.cs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Writers/Dart/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/Dart/CodePropertyWriter.cs index 16ceee96eb..f1977cc8b4 100644 --- a/src/Kiota.Builder/Writers/Dart/CodePropertyWriter.cs +++ b/src/Kiota.Builder/Writers/Dart/CodePropertyWriter.cs @@ -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; diff --git a/tests/Kiota.Builder.Tests/Writers/Dart/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Dart/CodePropertyWriterTests.cs index a8c7e6223c..50c2c0dfc3 100644 --- a/tests/Kiota.Builder.Tests/Writers/Dart/CodePropertyWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Dart/CodePropertyWriterTests.cs @@ -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); + } } From 8d55c2bf006e994859acf10be5396daa70b07013 Mon Sep 17 00:00:00 2001 From: Koen van Leeuwen Date: Thu, 25 Sep 2025 12:28:58 +0200 Subject: [PATCH 4/7] Add Typescript --- .../Writers/TypeScript/CodeFunctionWriter.cs | 20 ++++- .../TypeScript/CodeFunctionWriterTests.cs | 81 +++++++++++++++++++ 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs index 44e22e600b..3649a739e9 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs @@ -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(); } @@ -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); diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs index 22c56a3566..29289d5299 100644 --- a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs @@ -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 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); + } } From f9834fba5721d9929d43519142d199fd79a46643 Mon Sep 17 00:00:00 2001 From: Koen van Leeuwen Date: Fri, 19 Dec 2025 21:58:09 +0100 Subject: [PATCH 5/7] Go import compile fix --- src/Kiota.Builder/Refiners/GoRefiner.cs | 7 ++-- .../Writers/Go/CodeMethodWriterTests.cs | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/Kiota.Builder/Refiners/GoRefiner.cs b/src/Kiota.Builder/Refiners/GoRefiner.cs index b7c2e93589..7dbcf9adbd 100644 --- a/src/Kiota.Builder/Refiners/GoRefiner.cs +++ b/src/Kiota.Builder/Refiners/GoRefiner.cs @@ -117,9 +117,6 @@ public override Task RefineAsync(CodeNamespace generatedCode, CancellationToken true, false, true); - AddDefaultImports( - generatedCode, - defaultUsingEvaluators); CorrectCoreType( generatedCode, CorrectMethodType, @@ -218,6 +215,9 @@ public override Task RefineAsync(CodeNamespace generatedCode, CancellationToken "Error", () => new CodeType { Name = "string", IsNullable = false, IsExternal = true } ); + AddDefaultImports( + generatedCode, + defaultUsingEvaluators); GenerateCodeFiles(generatedCode); }, cancellationToken); } @@ -821,6 +821,7 @@ x.Type is CodeType pType && new (static x => x is CodeMethod @method && @method.IsOfKind(CodeMethodKind.RequestExecutor) && (method.ReturnType.Name.Equals(KiotaBuilder.UntypedNodeName, StringComparison.OrdinalIgnoreCase) || method.Parameters.Any(x => x.Kind is CodeParameterKind.RequestBody && x.Type.Name.Equals(KiotaBuilder.UntypedNodeName, StringComparison.OrdinalIgnoreCase))), SerializationNamespaceName, KiotaBuilder.UntypedNodeName), + new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.ErrorMessageOverride), "fmt", "*fmt"), new (static x => x is CodeEnum @enum && @enum.Flags,"", "math"), }; private const string MultipartBodyClassName = "MultipartBody"; diff --git a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs index fc5736474b..36e04376d6 100644 --- a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs @@ -2338,6 +2338,38 @@ public void WritesMessageOverrideWithStatusCodeWhenNoPrimary() Assert.Contains("return fmt.Sprintf(\"%d: %s\", m.ResponseStatusCode, m.ApiError.Error())", result); } + [Fact] + public async Task AddsFormatImportForErrorMessageOverride() + { + // Given + parentClass = root.AddClass(new CodeClass + { + Name = "parentClass", + IsErrorDefinition = true, + Kind = CodeClassKind.Model, + }).First(); + var method = parentClass.AddMethod(new CodeMethod + { + Kind = CodeMethodKind.ErrorMessageOverride, + ReturnType = new CodeType + { + Name = "string", + IsNullable = false, + }, + IsAsync = false, + IsStatic = false, + Name = "Error" + }).First(); + + // When - Run refiner to add imports + await ILanguageRefiner.RefineAsync(new GenerationConfiguration { Language = GenerationLanguage.Go }, root); + + // Then - Verify fmt import was added by the refiner + var fmtUsing = parentClass.StartBlock.Usings.FirstOrDefault(u => u.Declaration?.Name == "fmt"); + Assert.NotNull(fmtUsing); + Assert.Equal("*fmt", fmtUsing.Name); + } + [Fact] public void WritesRequestGeneratorAcceptHeaderQuotes() { From e91b7fc92f4c474ac38f055427800e350992d276 Mon Sep 17 00:00:00 2001 From: Koen van Leeuwen Date: Sat, 20 Dec 2025 10:22:02 +0100 Subject: [PATCH 6/7] Revert Typescript; the architecture doesn't support this We'd need to include a hook before throwing in https://github.com/microsoft/kiota-typescript/blob/a49ffd53bcf8fca8cc74bc285b7cad05cb9e9cb0/packages/http/fetch/src/fetchRequestAdapter.ts#L424 for this to work. --- .../Writers/TypeScript/CodeFunctionWriter.cs | 20 +---- .../TypeScript/CodeFunctionWriterTests.cs | 81 ------------------- 2 files changed, 3 insertions(+), 98 deletions(-) diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs index a50d2925cd..72aa88dfcf 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs @@ -694,13 +694,6 @@ 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(); } @@ -710,17 +703,10 @@ private static (string, string) GetPrimaryErrorMapping(CodeFunction codeFunction var primaryErrorMappingKey = string.Empty; var parentClass = codeFunction.OriginalMethodParentClass; - if (parentClass.IsErrorDefinition) + 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.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 ?? \"\"}}`;"; - } + primaryErrorMapping = $" {param.Name.ToFirstCharacterLowerCase()}.message = {param.Name.ToFirstCharacterLowerCase()}.{primaryMessageCodePath} ?? \"\";"; + primaryErrorMappingKey = primaryMessageCodePath.Split("?.", StringSplitOptions.RemoveEmptyEntries)[0]; } return (primaryErrorMapping, primaryErrorMappingKey); diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs index 13dff79cdb..02e13813af 100644 --- a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs @@ -1718,87 +1718,6 @@ public void WritesByteArrayPropertyDeserialization() 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 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); - } - [Fact] public async Task WritesOneOfWithInheritanceDeserializationAsync() { From b81ce4f186ce5ca03fb1a218e307c470aeba1090 Mon Sep 17 00:00:00 2001 From: Koen van Leeuwen Date: Sun, 4 Jan 2026 13:10:09 +0100 Subject: [PATCH 7/7] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe3d0ebe85..808528597a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support for OpenAPI 3.2.0 +- Added HTTP status code to API exception messages by default for all languages except TypeScript ### Changed