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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- C#: primary error messages nested in collections now read from the first collection item. [#7926](https://github.com/microsoft/kiota/issues/7926)
- golang: generated code now always uses LF line endings, so `gofmt` no longer reports formatting differences when generating on Windows.
- golang: make sure all generated code adheres to golangs coding standards
- Fixed non-deterministic model class descriptions when a component schema is referenced from multiple properties with differing reference-level descriptions. [#7927](https://github.com/microsoft/kiota/issues/7927)
Expand Down
5 changes: 5 additions & 0 deletions src/Kiota.Builder/Refiners/CSharpRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Kiota.Builder.CodeDOM;
using Kiota.Builder.Configuration;
using Kiota.Builder.Extensions;
using Kiota.Builder.Writers;

namespace Kiota.Builder.Refiners;

Expand Down Expand Up @@ -166,6 +167,10 @@ protected static void MakeEnumPropertiesNullable(CodeElement currentElement)
"System", "String"),
new (static x => x is CodeClass,
"System.Collections.Generic", "List", "Dictionary"),
new (static x => x is CodeClass { IsErrorDefinition: true } @class &&
@class.GetPrimaryMessageCodePath(static y => y.Name, static y => y.Name, pathSegmentFactory: static y => y.IsCollection ? ".FirstOrDefault()." : ".")
.Contains(".FirstOrDefault().", StringComparison.Ordinal),
"System.Linq", "Enumerable"),
new (static x => x is CodeClass @class && @class.IsOfKind(CodeClassKind.Model, CodeClassKind.RequestBuilder),
"System.IO", "Stream"),
new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.RequestExecutor),
Expand Down
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 @@ -60,7 +60,7 @@ private void WritePropertyInternal(CodeProperty codeElement, LanguageWriter writ
writer.CloseBlock();
break;
case CodePropertyKind.ErrorMessageOverride when parentClass.IsErrorDefinition:
if (parentClass.GetPrimaryMessageCodePath(static x => x.Name.ToFirstCharacterUpperCase(), static x => x.Name.ToFirstCharacterUpperCase(), "?.") is string primaryMessageCodePath && !string.IsNullOrEmpty(primaryMessageCodePath))
if (parentClass.GetPrimaryMessageCodePath(static x => x.Name.ToFirstCharacterUpperCase(), static x => x.Name.ToFirstCharacterUpperCase(), "?.", pathSegmentFactory: static x => x.IsCollection ? "?.FirstOrDefault()?." : "?.") 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; }}");
Expand Down
11 changes: 6 additions & 5 deletions src/Kiota.Builder/Writers/ProprietableBlockExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ internal static string GetPrimaryMessageCodePath<TBlockKind, TBlockDeclaration>(
Func<CodeProperty, string> propertyNameNormalization,
Func<CodeMethod, string> methodNameNormalization,
string pathSegment = ".",
HashSet<CodeElement>? visitedElements = default) where TBlockKind : Enum where TBlockDeclaration : ProprietableBlockDeclaration, new()
HashSet<CodeElement>? visitedElements = default,
Func<CodeTypeBase, string>? pathSegmentFactory = default) where TBlockKind : Enum where TBlockDeclaration : ProprietableBlockDeclaration, new()
{
visitedElements ??= new();
if (visitedElements.Contains(block))
Expand All @@ -29,10 +30,10 @@ internal static string GetPrimaryMessageCodePath<TBlockKind, TBlockDeclaration>(
return propertyNameNormalization(primaryErrorMessageProperty);
else if (currentInterface.Methods
.Where(isGetterMethod)
.Select(x => new { Value = x.ReturnType is CodeType codeType && codeType.TypeDefinition is CodeInterface codeInterface && codeInterface.GetPrimaryMessageCodePath(propertyNameNormalization, methodNameNormalization, pathSegment, visitedElements) is string segment && !string.IsNullOrEmpty(segment) ? $"{methodNameNormalization(x)}{pathSegment}{segment}" : string.Empty, IsMethod = true })
.Select(x => new { Value = x.ReturnType is CodeType codeType && codeType.TypeDefinition is CodeInterface codeInterface && codeInterface.GetPrimaryMessageCodePath(propertyNameNormalization, methodNameNormalization, pathSegment, visitedElements, pathSegmentFactory) is string segment && !string.IsNullOrEmpty(segment) ? $"{methodNameNormalization(x)}{pathSegmentFactory?.Invoke(x.ReturnType) ?? pathSegment}{segment}" : string.Empty, IsMethod = true })
.Union(currentInterface.Properties
.Where(isCustomProperty)
.Select(x => new { Value = x.Type is CodeType codeType && codeType.TypeDefinition is CodeInterface codeInterface && codeInterface.GetPrimaryMessageCodePath(propertyNameNormalization, methodNameNormalization, pathSegment, visitedElements) is string segment && !string.IsNullOrEmpty(segment) ? $"{propertyNameNormalization(x)}{pathSegment}{segment}" : string.Empty, IsMethod = false }))
.Select(x => new { Value = x.Type is CodeType codeType && codeType.TypeDefinition is CodeInterface codeInterface && codeInterface.GetPrimaryMessageCodePath(propertyNameNormalization, methodNameNormalization, pathSegment, visitedElements, pathSegmentFactory) is string segment && !string.IsNullOrEmpty(segment) ? $"{propertyNameNormalization(x)}{pathSegmentFactory?.Invoke(x.Type) ?? pathSegment}{segment}" : string.Empty, IsMethod = false }))
.OrderBy(static x => x.IsMethod)
.ThenBy(static x => x.Value, StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(static x => !string.IsNullOrEmpty(x.Value)) is { } primaryMessageCodePath)
Expand All @@ -47,10 +48,10 @@ internal static string GetPrimaryMessageCodePath<TBlockKind, TBlockDeclaration>(
return propertyNameNormalization(primaryErrorMessageProperty);
else if (currentClass.Methods
.Where(isGetterMethod)
.Select(x => new { Value = x.ReturnType is CodeType codeType && codeType.TypeDefinition is CodeClass codeClass && codeClass.GetPrimaryMessageCodePath(propertyNameNormalization, methodNameNormalization, pathSegment, visitedElements) is string segment && !string.IsNullOrEmpty(segment) ? $"{methodNameNormalization(x)}{pathSegment}{segment}" : string.Empty, IsMethod = true })
.Select(x => new { Value = x.ReturnType is CodeType codeType && codeType.TypeDefinition is CodeClass codeClass && codeClass.GetPrimaryMessageCodePath(propertyNameNormalization, methodNameNormalization, pathSegment, visitedElements, pathSegmentFactory) is string segment && !string.IsNullOrEmpty(segment) ? $"{methodNameNormalization(x)}{pathSegmentFactory?.Invoke(x.ReturnType) ?? pathSegment}{segment}" : string.Empty, IsMethod = true })
.Union(currentClass.Properties
.Where(isCustomProperty)
.Select(x => new { Value = x.Type is CodeType codeType && codeType.TypeDefinition is CodeClass codeClass && codeClass.GetPrimaryMessageCodePath(propertyNameNormalization, methodNameNormalization, pathSegment, visitedElements) is string segment && !string.IsNullOrEmpty(segment) ? $"{propertyNameNormalization(x)}{pathSegment}{segment}" : string.Empty, IsMethod = false }))
.Select(x => new { Value = x.Type is CodeType codeType && codeType.TypeDefinition is CodeClass codeClass && codeClass.GetPrimaryMessageCodePath(propertyNameNormalization, methodNameNormalization, pathSegment, visitedElements, pathSegmentFactory) is string segment && !string.IsNullOrEmpty(segment) ? $"{propertyNameNormalization(x)}{pathSegmentFactory?.Invoke(x.Type) ?? pathSegment}{segment}" : string.Empty, IsMethod = false }))
.OrderBy(static x => x.IsMethod)
.ThenBy(static x => x.Value, StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(static x => !string.IsNullOrEmpty(x.Value)) is { } primaryMessageCodePath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,43 @@ public async Task RenamesMatchAndAddsPrimaryErrorMessageIfMatchAlreadyExistsAsyn
Assert.True(properties[1].IsPrimaryErrorMessage);// property is IsPrimaryErrorMessage so that information deserialized into it shows up in the error information.
}
[Fact]
public async Task AddsLinqImportForPrimaryErrorMessageInCollectionAsync()
{
var processResult = root.AddClass(new CodeClass
{
Name = "processResult",
Kind = CodeClassKind.Model,
}).First();
processResult.AddProperty(new CodeProperty
{
Name = "message",
Kind = CodePropertyKind.Custom,
IsPrimaryErrorMessage = true,
Type = new CodeType { Name = "string" },
});
var exception = root.AddClass(new CodeClass
{
Name = "error403",
Kind = CodeClassKind.Model,
IsErrorDefinition = true,
}).First();
exception.AddProperty(new CodeProperty
{
Name = "processResults",
Kind = CodePropertyKind.Custom,
Type = new CodeType
{
Name = processResult.Name,
TypeDefinition = processResult,
CollectionKind = CodeTypeBase.CodeTypeCollectionKind.Array,
},
});

await ILanguageRefiner.RefineAsync(new GenerationConfiguration { Language = GenerationLanguage.CSharp }, root, cancellationToken: TestContext.Current.CancellationToken);

Assert.Contains(exception.StartBlock.Usings, static x => x.Declaration?.Name.Equals("System.Linq", StringComparison.Ordinal) is true);
}
[Fact]
public async Task RenamesExceptionClassWithReservedPropertyNameAsync()
{
var exception = root.AddClass(new CodeClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,5 +271,43 @@ public void WritesMessageOverrideOnPrimary()
// Then
Assert.Contains("public override string Message { get => Prop1 ?? string.Empty; }", result);
}
}
[Fact]
public void WritesMessageOverrideOnPrimaryInCollection()
{
parentClass.IsErrorDefinition = true;
var processResult = rootNamespace.AddClass(new CodeClass
{
Name = "processResult",
Kind = CodeClassKind.Model,
}).First();
processResult.AddProperty(new CodeProperty
{
Name = "message",
Kind = CodePropertyKind.Custom,
IsPrimaryErrorMessage = true,
Type = new CodeType { Name = "string" },
});
parentClass.AddProperty(new CodeProperty
{
Name = "processResults",
Kind = CodePropertyKind.Custom,
Type = new CodeType
{
Name = processResult.Name,
TypeDefinition = processResult,
CollectionKind = CodeTypeBase.CodeTypeCollectionKind.Array,
},
});
var overrideProperty = parentClass.AddProperty(new CodeProperty
{
Name = "Message",
Kind = CodePropertyKind.ErrorMessageOverride,
Type = new CodeType { Name = "string" },
}).First();

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

Assert.Contains("public override string Message { get => ProcessResults?.FirstOrDefault()?.Message ?? string.Empty; }", result);
}
}