diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e439f607f..83f4e52a26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Reverts modification of responses in output openApi file when generating plugins [#4945](https://github.com/microsoft/kiota/issues/4945) - Expand properties types with null type for Typescript. [#4993](https://github.com/microsoft/kiota-typescript/issues/1188) - Added Collection, HashMap, Map, Objects, InputStream, BigDecimal to the list of reserved names for Java generation. [#5135](https://github.com/microsoft/kiota/issues/5135) +- C# refiner now fixes data types for indexers. [#5201](https://github.com/microsoft/kiota/issues/5201) ## [1.17.0] - 2024-08-09 diff --git a/src/Kiota.Builder/Refiners/CSharpRefiner.cs b/src/Kiota.Builder/Refiners/CSharpRefiner.cs index 2a78396e7d..6e6739c035 100644 --- a/src/Kiota.Builder/Refiners/CSharpRefiner.cs +++ b/src/Kiota.Builder/Refiners/CSharpRefiner.cs @@ -97,7 +97,7 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance static s => s.ToPascalCase(UnderscoreArray)); DisambiguatePropertiesWithClassNames(generatedCode); // Correct the core types after reserved names for types/properties are done to avoid collision of types e.g. renaming custom model called `DateOnly` to `Date` - CorrectCoreType(generatedCode, CorrectMethodType, CorrectPropertyType); + CorrectCoreType(generatedCode, CorrectMethodType, CorrectPropertyType, correctIndexer: CorrectIndexerType); cancellationToken.ThrowIfCancellationRequested(); AddSerializationModulesImport(generatedCode); AddParentClassToErrorClasses( @@ -226,6 +226,11 @@ protected static void CorrectMethodType(CodeMethod currentMethod) .Union(new[] { currentMethod.ReturnType }) .ToArray()); } + protected static void CorrectIndexerType(CodeIndexer currentIndexer) + { + ArgumentNullException.ThrowIfNull(currentIndexer); + CorrectCoreTypes(currentIndexer.Parent as CodeClass, DateTypesReplacements, currentIndexer.IndexParameter.Type); + } private static readonly Dictionary DateTypesReplacements = new(StringComparer.OrdinalIgnoreCase) { diff --git a/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs b/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs index 3bff4b8ff3..6ce8cf9365 100644 --- a/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs +++ b/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs @@ -787,7 +787,7 @@ protected static void CrawlTree(CodeElement currentElement, Action foreach (var childElement in currentElement.GetChildElements(innerOnly)) function.Invoke(childElement); } - protected static void CorrectCoreType(CodeElement currentElement, Action? correctMethodType, Action? correctPropertyType, Action? correctImplements = default) + protected static void CorrectCoreType(CodeElement currentElement, Action? correctMethodType, Action? correctPropertyType, Action? correctImplements = default, Action? correctIndexer = default) { switch (currentElement) { @@ -800,8 +800,11 @@ protected static void CorrectCoreType(CodeElement currentElement, Action CorrectCoreType(x, correctMethodType, correctPropertyType, correctImplements), false); + CrawlTree(currentElement, x => CorrectCoreType(x, correctMethodType, correctPropertyType, correctImplements, correctIndexer), false); } protected static void MakeModelPropertiesNullable(CodeElement currentElement) { diff --git a/tests/Kiota.Builder.Tests/Refiners/CSharpLanguageRefinerTests.cs b/tests/Kiota.Builder.Tests/Refiners/CSharpLanguageRefinerTests.cs index cb85b7cb0a..b0a464f459 100644 --- a/tests/Kiota.Builder.Tests/Refiners/CSharpLanguageRefinerTests.cs +++ b/tests/Kiota.Builder.Tests/Refiners/CSharpLanguageRefinerTests.cs @@ -733,6 +733,153 @@ public async Task ReplacesLocallyDefinedTimeOnlyByNativeType() Assert.NotEmpty(model.StartBlock.Usings); Assert.Equal("TimeOnlyObject", method.ReturnType.Name); } + + [Fact] + public async Task ReplacesIndexerDateOnlyTypeWithAbstractedDateType() + { + // Arrange + var requestBuilder = root.AddClass(new CodeClass + { + Name = "requestBuilder" + }).First(); + + requestBuilder.AddIndexer(new CodeIndexer + { + Name = "indexer", + IndexParameter = new CodeParameter + { + Type = new CodeType + { + Name = "DateOnly", + IsExternal = true + } + }, + ReturnType = new CodeType + { + Name = "SomeType" + } + }); + + // Act + await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.CSharp }, root); + + // Assert + Assert.Equal("Date", requestBuilder.Indexer.IndexParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + } + + [Fact] + public async Task ReplacesIndexerTimeOnlyTypeWithAbstractedTimeType() + { + // Arrange + var requestBuilder = root.AddClass(new CodeClass + { + Name = "requestBuilder" + }).First(); + + requestBuilder.AddIndexer(new CodeIndexer + { + Name = "indexer", + IndexParameter = new CodeParameter + { + Type = new CodeType + { + Name = "TimeOnly", + IsExternal = true + } + }, + ReturnType = new CodeType + { + Name = "SomeType" + } + }); + + // Act + await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.CSharp }, root); + + // Assert + Assert.Equal("Time", requestBuilder.Indexer.IndexParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + } + + [Fact] + public async Task ReplacesIndexerLocallyDefinedDateOnlyTypeWithAbstractedDateType() + { + // Arrange + var requestBuilder = root.AddClass(new CodeClass + { + Name = "requestBuilder" + }).First(); + + var dateOnlyModel = root.AddClass(new CodeClass + { + Name = "DateOnly", + Kind = CodeClassKind.Model + }).First(); + + requestBuilder.AddIndexer(new CodeIndexer + { + Name = "indexer", + IndexParameter = new CodeParameter + { + Type = new CodeType + { + Name = "DateOnly", + IsExternal = false, + TypeDefinition = dateOnlyModel + } + }, + ReturnType = new CodeType + { + Name = "SomeType" + } + }); + + // Act + await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.CSharp }, root); + + // Assert + Assert.Equal("DateOnlyObject", requestBuilder.Indexer.IndexParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + } + + [Fact] + public async Task ReplacesIndexerLocallyDefinedTimeOnlyTypeWithAbstractedTimeType() + { + // Arrange + var requestBuilder = root.AddClass(new CodeClass + { + Name = "requestBuilder" + }).First(); + + var timeOnlyModel = root.AddClass(new CodeClass + { + Name = "TimeOnly", + Kind = CodeClassKind.Model + }).First(); + + requestBuilder.AddIndexer(new CodeIndexer + { + Name = "indexer", + IndexParameter = new CodeParameter + { + Type = new CodeType + { + Name = "TimeOnly", + IsExternal = false, + TypeDefinition = timeOnlyModel + } + }, + ReturnType = new CodeType + { + Name = "SomeType" + } + }); + + // Act + await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.CSharp }, root); + + // Assert + Assert.Equal("TimeOnlyObject", requestBuilder.Indexer.IndexParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + } + [Fact] public async Task AddsUsingForUntypedNode() {