diff --git a/CHANGELOG.md b/CHANGELOG.md index 077afc5b53..e7708df6b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added support for tracing in Python. [#1872](https://github.com/microsoft/kiota/issues/1872) - Added auto-generated comment for TypeScript generation. [#3244](https://github.com/microsoft/kiota/issues/3244) +- Added a new switch to exclude all assets generated only for backward compatibility. [#2952](https://github.com/microsoft/kiota/issues/2952) ### Changed - Updated constructor for request builders in Python to set passed path parameters. [#3352](https://github.com/microsoft/kiota/issues/3352) - Fixed inherited type definition generation where some cases would not generate properly. [#2745](https://github.com/microsoft/kiota/issues/2745) +- Fixed naming convention for inline response types. [#2952](https://github.com/microsoft/kiota/issues/2952) - Localhost based descriptions are not cached anymore to facilitate development workflows. [#3316](https://github.com/microsoft/kiota/issues/3316) - Fixed a bug where the hints would miss quotes for paths and always use the API manifest. [#3342](https://github.com/microsoft/kiota/issues/3342) - Fixed a bug where inline composed types for components schemas would have the wrong name. [#3067](https://github.com/microsoft/kiota/issues/3067) diff --git a/it/compare-generation.ps1 b/it/compare-generation.ps1 index d8aed1293a..58753b89c1 100755 --- a/it/compare-generation.ps1 +++ b/it/compare-generation.ps1 @@ -61,8 +61,8 @@ else { $tmpFolder1 = New-TemporaryDirectory $tmpFolder2 = New-TemporaryDirectory -Start-Process "$kiotaExec" -ArgumentList "generate --clean-output --language ${language} --openapi ${targetOpenapiPath} --dvr all --output $tmpFolder1" -Wait -NoNewWindow -Start-Process "$kiotaExec" -ArgumentList "generate --clean-output --language ${language} --openapi ${targetOpenapiPath} --dvr all --output $tmpFolder2" -Wait -NoNewWindow +Start-Process "$kiotaExec" -ArgumentList "generate --exclude-backward-compatible --clean-output --language ${language} --openapi ${targetOpenapiPath} --dvr all --output $tmpFolder1" -Wait -NoNewWindow +Start-Process "$kiotaExec" -ArgumentList "generate --exclude-backward-compatible --clean-output --language ${language} --openapi ${targetOpenapiPath} --dvr all --output $tmpFolder2" -Wait -NoNewWindow # Remove variable output files Remove-Item (Join-Path -Path $tmpFolder1 -ChildPath "kiota-lock.json") diff --git a/it/config.json b/it/config.json index 8e654b74f0..0231bdd269 100644 --- a/it/config.json +++ b/it/config.json @@ -258,10 +258,6 @@ } ], "IdempotencySuppressions": [ - { - "Language": "csharp", - "Rationale": "https://github.com/microsoft/kiota/issues/2952" - }, { "Language": "go", "Rationale": "https://github.com/microsoft/kiota/issues/2834" @@ -330,4 +326,4 @@ } ] } -} \ No newline at end of file +} diff --git a/it/generate-code.ps1 b/it/generate-code.ps1 index 94e20f1234..da73e36fc4 100755 --- a/it/generate-code.ps1 +++ b/it/generate-code.ps1 @@ -53,4 +53,4 @@ else { Start-Process "$kiotaExec" -ArgumentList "download ${descriptionUrl} --clean-output --output $targetOpenapiPath" -Wait -NoNewWindow } -Start-Process "$kiotaExec" -ArgumentList "generate --clean-output --language ${language} --openapi ${targetOpenapiPath}${additionalArguments}" -Wait -NoNewWindow +Start-Process "$kiotaExec" -ArgumentList "generate --exclude-backward-compatible --clean-output --language ${language} --openapi ${targetOpenapiPath}${additionalArguments}" -Wait -NoNewWindow diff --git a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs index 655d807a05..810ebb5181 100644 --- a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs +++ b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs @@ -43,6 +43,14 @@ public bool UsesBackingStore { get; set; } + public bool ExcludeBackwardCompatible + { + get; set; + } + public bool IncludeBackwardCompatible + { + get => !ExcludeBackwardCompatible; + } public bool IncludeAdditionalData { get; set; } = true; public HashSet Serializers { @@ -118,6 +126,7 @@ public object Clone() { return new GenerationConfiguration { + ExcludeBackwardCompatible = ExcludeBackwardCompatible, OpenAPIFilePath = OpenAPIFilePath, OutputPath = OutputPath, ClientClassName = ClientClassName, diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index dea2f6cc11..e9d174f4c8 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -1113,7 +1113,7 @@ private CodeIndexer[] CreateIndexer(string childIdentifier, string childType, Co IndexParameter = parameter, }}; - if (!"string".Equals(parameter.Type.Name, StringComparison.OrdinalIgnoreCase)) + if (!"string".Equals(parameter.Type.Name, StringComparison.OrdinalIgnoreCase) && config.IncludeBackwardCompatible) { // adding a second indexer for the string version of the parameter so we keep backward compatibility //TODO remove for v2 var backCompatibleValue = (CodeIndexer)result[0].Clone(); @@ -1265,11 +1265,55 @@ codeType.TypeDefinition is CodeClass codeClass && executorMethod.AddErrorMapping(errorCode, errorType); } } - private CodeTypeBase? GetExecutorMethodReturnType(OpenApiUrlTreeNode currentNode, OpenApiSchema? schema, OpenApiOperation operation, CodeClass parentClass) + private (CodeTypeBase?, CodeTypeBase?) GetExecutorMethodReturnType(OpenApiUrlTreeNode currentNode, OpenApiSchema? schema, OpenApiOperation operation, CodeClass parentClass, OperationType operationType) { if (schema != null) { - return CreateModelDeclarations(currentNode, schema, operation, parentClass, "Response"); + var suffix = $"{operationType}Response"; + var modelType = CreateModelDeclarations(currentNode, schema, operation, parentClass, suffix); + if (modelType is not null && config.IncludeBackwardCompatible && config.Language is GenerationLanguage.CSharp or GenerationLanguage.Go && modelType.Name.EndsWith(suffix, StringComparison.Ordinal)) + { //TODO remove for v2 + var obsoleteTypeName = modelType.Name[..^suffix.Length] + "Response"; + if (modelType is CodeType codeType && + codeType.TypeDefinition is CodeClass codeClass) + { + var obsoleteClassDefinition = new CodeClass + { + Kind = CodeClassKind.Model, + Name = obsoleteTypeName, + Deprecation = new($"This class is obsolete. Use {modelType.Name} instead.", IsDeprecated: true), + Documentation = (CodeDocumentation)codeClass.Documentation.Clone() + }; + var originalFactoryMethod = codeClass.Methods.First(static x => x.Kind is CodeMethodKind.Factory); + var obsoleteFactoryMethod = (CodeMethod)originalFactoryMethod.Clone(); + obsoleteFactoryMethod.ReturnType = new CodeType { Name = obsoleteTypeName, TypeDefinition = obsoleteClassDefinition }; + obsoleteClassDefinition.AddMethod(obsoleteFactoryMethod); + obsoleteClassDefinition.StartBlock.Inherits = (CodeType)codeType.Clone(); + var obsoleteClass = codeClass.Parent switch + { + CodeClass modelParentClass => modelParentClass.AddInnerClass(obsoleteClassDefinition).First(), + CodeNamespace modelParentNamespace => modelParentNamespace.AddClass(obsoleteClassDefinition).First(), + _ => throw new InvalidOperationException("Could not find a valid parent for the obsolete class") + }; + return (modelType, new CodeType + { + TypeDefinition = obsoleteClass, + }); + } + else if (modelType is CodeComposedTypeBase codeComposedTypeBase) + { + var obsoleteComposedType = codeComposedTypeBase switch + { + CodeUnionType u => (CodeComposedTypeBase)u.Clone(), + CodeIntersectionType i => (CodeComposedTypeBase)i.Clone(), + _ => throw new InvalidOperationException("Could not create an obsolete composed type"), + }; + obsoleteComposedType.Name = obsoleteTypeName; + obsoleteComposedType.Deprecation = new($"This class is obsolete. Use {modelType.Name} instead.", IsDeprecated: true); + return (modelType, obsoleteComposedType); + } + } + return (modelType, null); } else { @@ -1280,7 +1324,7 @@ codeType.TypeDefinition is CodeClass codeClass && returnType = "string"; else returnType = "binary"; - return new CodeType { Name = returnType, IsExternal = true, }; + return (new CodeType { Name = returnType, IsExternal = true, }, null); } } private void CreateOperationMethods(OpenApiUrlTreeNode currentNode, OperationType operationType, OpenApiOperation operation, CodeClass parentClass) @@ -1301,6 +1345,7 @@ private void CreateOperationMethods(OpenApiUrlTreeNode currentNode, OperationTyp var schema = operation.GetResponseSchema(config.StructuredMimeTypes); var method = (HttpMethod)Enum.Parse(typeof(HttpMethod), operationType.ToString()); var deprecationInformation = operation.GetDeprecationInformation(); + var returnTypes = GetExecutorMethodReturnType(currentNode, schema, operation, parentClass, operationType); var executorMethod = new CodeMethod { Name = operationType.ToString(), @@ -1316,7 +1361,7 @@ private void CreateOperationMethods(OpenApiUrlTreeNode currentNode, OperationTyp operation.Summary) .CleanupDescription(), }, - ReturnType = GetExecutorMethodReturnType(currentNode, schema, operation, parentClass) ?? throw new InvalidSchemaException(), + ReturnType = returnTypes.Item1 ?? throw new InvalidSchemaException(), Deprecation = deprecationInformation, }; @@ -1347,6 +1392,17 @@ private void CreateOperationMethods(OpenApiUrlTreeNode currentNode, OperationTyp Type = new CodeType { Name = "CancellationToken", IsExternal = true }, }; executorMethod.AddParameter(cancellationParam);// Add cancellation token parameter + + if (returnTypes.Item2 is not null && config.IncludeBackwardCompatible) + { //TODO remove for v2 + var additionalExecutorMethod = (CodeMethod)executorMethod.Clone(); + additionalExecutorMethod.ReturnType = returnTypes.Item2; + additionalExecutorMethod.OriginalMethod = executorMethod; + var newName = $"{executorMethod.Name}As{executorMethod.ReturnType.Name.ToFirstCharacterUpperCase()}"; + additionalExecutorMethod.Deprecation = new($"This method is obsolete. Use {newName} instead.", IsDeprecated: true); + parentClass.RenameChildElement(executorMethod.Name, newName); + parentClass.AddMethod(additionalExecutorMethod); + } logger.LogTrace("Creating method {Name} of {Type}", executorMethod.Name, executorMethod.ReturnType); var generatorMethod = new CodeMethod diff --git a/src/Kiota.Builder/Lock/KiotaLock.cs b/src/Kiota.Builder/Lock/KiotaLock.cs index cfdd5b32f0..099d781477 100644 --- a/src/Kiota.Builder/Lock/KiotaLock.cs +++ b/src/Kiota.Builder/Lock/KiotaLock.cs @@ -45,6 +45,13 @@ public bool UsesBackingStore get; set; } /// + /// Whether backward compatible code was excluded for this client. + /// + public bool ExcludeBackwardCompatible + { + get; set; + } + /// /// Whether additional data was used for this client. /// public bool IncludeAdditionalData @@ -89,6 +96,7 @@ public void UpdateGenerationConfigurationFromLock(GenerationConfiguration config if (Enum.TryParse(Language, out var parsedLanguage)) config.Language = parsedLanguage; config.UsesBackingStore = UsesBackingStore; + config.ExcludeBackwardCompatible = ExcludeBackwardCompatible; config.IncludeAdditionalData = IncludeAdditionalData; config.Serializers = Serializers; config.Deserializers = Deserializers; @@ -115,6 +123,7 @@ public KiotaLock(GenerationConfiguration config) ClientClassName = config.ClientClassName; ClientNamespaceName = config.ClientNamespaceName; UsesBackingStore = config.UsesBackingStore; + ExcludeBackwardCompatible = config.ExcludeBackwardCompatible; IncludeAdditionalData = config.IncludeAdditionalData; Serializers = config.Serializers; Deserializers = config.Deserializers; diff --git a/src/Kiota.Builder/Lock/KiotaLockComparer.cs b/src/Kiota.Builder/Lock/KiotaLockComparer.cs index a5e20cde06..d4e564b776 100644 --- a/src/Kiota.Builder/Lock/KiotaLockComparer.cs +++ b/src/Kiota.Builder/Lock/KiotaLockComparer.cs @@ -21,14 +21,15 @@ public int GetHashCode([DisallowNull] KiotaLock obj) { if (obj == null) return 0; return - _stringIEnumerableDeepComparer.GetHashCode(obj.DisabledValidationRules?.Order(StringComparer.OrdinalIgnoreCase) ?? Enumerable.Empty()) * 47 + - obj.KiotaVersion.GetHashCode(StringComparison.OrdinalIgnoreCase) * 43 + - obj.LockFileVersion.GetHashCode(StringComparison.OrdinalIgnoreCase) * 41 + - (string.IsNullOrEmpty(obj.DescriptionLocation) ? 0 : obj.DescriptionLocation.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 37 + - (string.IsNullOrEmpty(obj.DescriptionHash) ? 0 : obj.DescriptionHash.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 31 + - (string.IsNullOrEmpty(obj.ClientClassName) ? 0 : obj.ClientClassName.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 29 + - (string.IsNullOrEmpty(obj.ClientNamespaceName) ? 0 : obj.ClientNamespaceName.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 23 + - (string.IsNullOrEmpty(obj.Language) ? 0 : obj.Language.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 19 + + _stringIEnumerableDeepComparer.GetHashCode(obj.DisabledValidationRules?.Order(StringComparer.OrdinalIgnoreCase) ?? Enumerable.Empty()) * 53 + + obj.KiotaVersion.GetHashCode(StringComparison.OrdinalIgnoreCase) * 47 + + obj.LockFileVersion.GetHashCode(StringComparison.OrdinalIgnoreCase) * 43 + + (string.IsNullOrEmpty(obj.DescriptionLocation) ? 0 : obj.DescriptionLocation.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 41 + + (string.IsNullOrEmpty(obj.DescriptionHash) ? 0 : obj.DescriptionHash.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 37 + + (string.IsNullOrEmpty(obj.ClientClassName) ? 0 : obj.ClientClassName.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 31 + + (string.IsNullOrEmpty(obj.ClientNamespaceName) ? 0 : obj.ClientNamespaceName.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 29 + + (string.IsNullOrEmpty(obj.Language) ? 0 : obj.Language.GetHashCode(StringComparison.OrdinalIgnoreCase)) * 23 + + obj.ExcludeBackwardCompatible.GetHashCode() * 19 + obj.UsesBackingStore.GetHashCode() * 17 + obj.IncludeAdditionalData.GetHashCode() * 13 + _stringIEnumerableDeepComparer.GetHashCode(obj.Serializers?.Order(StringComparer.OrdinalIgnoreCase) ?? Enumerable.Empty()) * 11 + diff --git a/src/Kiota.Builder/Refiners/CSharpRefiner.cs b/src/Kiota.Builder/Refiners/CSharpRefiner.cs index 7472c4c922..94265f40c8 100644 --- a/src/Kiota.Builder/Refiners/CSharpRefiner.cs +++ b/src/Kiota.Builder/Refiners/CSharpRefiner.cs @@ -31,22 +31,23 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance IsExternal = true } }); - //TODO uncomment on the next major version - // RemoveRequestConfigurationClasses(generatedCode, - // new CodeUsing - // { - // Name = "RequestConfiguration", - // Declaration = new CodeType - // { - // Name = AbstractionsNamespaceName, - // IsExternal = true - // } - // }, - // new CodeType - // { - // Name = "DefaultQueryParameters", - // IsExternal = true, - // }); + //TODO remove the condition for v2 + if (_configuration.ExcludeBackwardCompatible) + RemoveRequestConfigurationClasses(generatedCode, + new CodeUsing + { + Name = "RequestConfiguration", + Declaration = new CodeType + { + Name = AbstractionsNamespaceName, + IsExternal = true + } + }, + new CodeType + { + Name = "DefaultQueryParameters", + IsExternal = true, + }); AddDefaultImports(generatedCode, defaultUsingEvaluators); MoveClassesWithNamespaceNamesUnderNamespace(generatedCode); ConvertUnionTypesToWrapper(generatedCode, diff --git a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs index 637ea16b3c..73364bc8fb 100644 --- a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs @@ -42,7 +42,7 @@ public override void WriteCodeElement(CodeMethod codeElement, LanguageWriter wri case CodeMethodKind.RequestGenerator when !codeElement.IsOverload: WriteRequestGeneratorBody(codeElement, requestParams, writer, parentClass); break; - case CodeMethodKind.RequestExecutor when !codeElement.IsOverload: + case CodeMethodKind.RequestExecutor when !codeElement.IsOverload || (codeElement.Deprecation?.IsDeprecated ?? false): //TODO remove deprecation condition for v2 WriteRequestExecutorBody(codeElement, requestParams, returnType, parentClass, writer); break; case CodeMethodKind.Getter: diff --git a/src/kiota/Handlers/KiotaGenerationCommandHandler.cs b/src/kiota/Handlers/KiotaGenerationCommandHandler.cs index 5976c0c455..c94e794751 100644 --- a/src/kiota/Handlers/KiotaGenerationCommandHandler.cs +++ b/src/kiota/Handlers/KiotaGenerationCommandHandler.cs @@ -71,6 +71,7 @@ public override async Task InvokeAsync(InvocationContext context) string openapi = context.ParseResult.GetValueForOption(DescriptionOption) ?? string.Empty; string manifest = context.ParseResult.GetValueForOption(ManifestOption) ?? string.Empty; bool backingStore = context.ParseResult.GetValueForOption(BackingStoreOption); + bool excludeBackwardCompatible = context.ParseResult.GetValueForOption(ExcludeBackwardCompatibleOption); bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption); bool includeAdditionalData = context.ParseResult.GetValueForOption(AdditionalDataOption); string className = context.ParseResult.GetValueForOption(ClassOption) ?? string.Empty; @@ -89,6 +90,7 @@ public override async Task InvokeAsync(InvocationContext context) AssignIfNotNullOrEmpty(className, (c, s) => c.ClientClassName = s); AssignIfNotNullOrEmpty(namespaceName, (c, s) => c.ClientNamespaceName = s); Configuration.Generation.UsesBackingStore = backingStore; + Configuration.Generation.ExcludeBackwardCompatible = excludeBackwardCompatible; Configuration.Generation.IncludeAdditionalData = includeAdditionalData; Configuration.Generation.Language = language; if (serializer.Any()) @@ -167,4 +169,9 @@ public required Option ManifestOption { get; init; } + public required Option ExcludeBackwardCompatibleOption + { + get; + set; + } } diff --git a/src/kiota/KiotaHost.cs b/src/kiota/KiotaHost.cs index 3b60f467c9..66e2e4f37d 100644 --- a/src/kiota/KiotaHost.cs +++ b/src/kiota/KiotaHost.cs @@ -3,7 +3,6 @@ using System.CommandLine; using System.CommandLine.Parsing; using System.Linq; -using System.Reflection; using System.Text.RegularExpressions; using kiota.Handlers; using kiota.Rpc; @@ -11,7 +10,6 @@ using Kiota.Builder.Configuration; using Kiota.Builder.Validation; using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Validations; namespace kiota; public static class KiotaHost @@ -344,6 +342,9 @@ private static Command GetGenerateCommand() var backingStoreOption = new Option("--backing-store", () => defaultConfiguration.UsesBackingStore, "Enables backing store for models."); backingStoreOption.AddAlias("-b"); + var excludeBackwardCompatible = new Option("--exclude-backward-compatible", () => defaultConfiguration.ExcludeBackwardCompatible, "Excludes backward compatible and obsolete assets from the generated result. Should be used for new clients."); + excludeBackwardCompatible.AddAlias("--ebc"); + var additionalDataOption = new Option("--additional-data", () => defaultConfiguration.IncludeAdditionalData, "Will include the 'AdditionalData' property for models."); additionalDataOption.AddAlias("--ad"); @@ -384,6 +385,7 @@ private static Command GetGenerateCommand() namespaceOption, logLevelOption, backingStoreOption, + excludeBackwardCompatible, additionalDataOption, serializerOption, deserializerOption, @@ -404,6 +406,7 @@ private static Command GetGenerateCommand() NamespaceOption = namespaceOption, LogLevelOption = logLevelOption, BackingStoreOption = backingStoreOption, + ExcludeBackwardCompatibleOption = excludeBackwardCompatible, AdditionalDataOption = additionalDataOption, SerializerOption = serializerOption, DeserializerOption = deserializerOption, diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index 4a3ec155e2..1b4f362a23 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -1112,7 +1112,7 @@ public void Object_Arrays_are_supported() var codeModel = builder.CreateSourceModel(node); var userClass = codeModel.FindNamespaceByName("ApiSdk.models").FindChildByName("user"); Assert.NotNull(userClass); - var userResponseClass = codeModel.FindNamespaceByName("ApiSdk.users.item").FindChildByName("UsersResponse", false); + var userResponseClass = codeModel.FindNamespaceByName("ApiSdk.users.item").FindChildByName("UsersGetResponse", false); Assert.NotNull(userResponseClass); var valueProp = userResponseClass.FindChildByName("value", false); Assert.NotNull(valueProp); @@ -1613,9 +1613,9 @@ public void Inline_Property_Inheritance_Is_Supported() var codeModel = builder.CreateSourceModel(node); var resourceClass = codeModel.FindNamespaceByName("ApiSdk.models").FindChildByName("resource"); var itemsNS = codeModel.FindNamespaceByName("ApiSdk.resource.item"); - var responseClass = itemsNS.FindChildByName("ResourceResponse"); - var derivedResourceClass = itemsNS.FindChildByName("ResourceResponse_derivedResource"); - var derivedResourceInfoClass = itemsNS.FindChildByName("ResourceResponse_derivedResource_info"); + var responseClass = itemsNS.FindChildByName("ResourceGetResponse"); + var derivedResourceClass = itemsNS.FindChildByName("ResourceGetResponse_derivedResource"); + var derivedResourceInfoClass = itemsNS.FindChildByName("ResourceGetResponse_derivedResource_info"); Assert.NotNull(resourceClass); @@ -3455,7 +3455,7 @@ public void UnionOfInlineSchemasWorks() Assert.Equal(2, executorReturnType.Types.Count()); var typeNames = executorReturnType.Types.Select(x => x.Name).ToHashSet(StringComparer.OrdinalIgnoreCase); Assert.Contains("simpleObject", typeNames); - Assert.Contains("unionTypeResponseMember1", typeNames); + Assert.Contains("unionTypeGetResponseMember1", typeNames); } [Fact] public void IntersectionOfPrimitiveTypesWorks() @@ -3614,7 +3614,7 @@ public void IntersectionOfInlineSchemasWorks() Assert.Equal(2, executorReturnType.Types.Count()); var typeNames = executorReturnType.Types.Select(x => x.Name).ToHashSet(StringComparer.OrdinalIgnoreCase); Assert.Contains("simpleObject", typeNames); - Assert.Contains("unionTypeResponseMember1", typeNames); + Assert.Contains("unionTypeGetResponseMember1", typeNames); } [Fact] public void InheritedTypeWithInlineSchemaWorks() @@ -4636,8 +4636,10 @@ public void AcceptVendorsTypes(string contentType) Assert.NotNull(executorMethod); Assert.Equal("myobject", executorMethod.ReturnType.Name); } - [Fact] - public void ModelsUseDescriptionWhenAvailable() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ModelsUseDescriptionWhenAvailable(bool excludeBackwardCompatible) { var document = new OpenApiDocument { @@ -4677,20 +4679,35 @@ public void ModelsUseDescriptionWhenAvailable() } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost", ExcludeBackwardCompatible = excludeBackwardCompatible }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var modelsSubNS = codeModel.FindNamespaceByName("TestSdk.answer"); Assert.NotNull(modelsSubNS); - var responseClass = modelsSubNS.Classes.FirstOrDefault(x => x.IsOfKind(CodeClassKind.Model)); + var responseClass = modelsSubNS.FindChildByName("AnswerGetResponse", false); Assert.NotNull(responseClass); Assert.Equal("some description", responseClass.Documentation.Description); - responseClass = modelsSubNS.Classes.FirstOrDefault(c => c.IsOfKind(CodeClassKind.RequestBuilder)); - Assert.NotNull(responseClass); - Assert.Equal("some path item description", responseClass.Documentation.Description); + var obsoleteResponseClass = modelsSubNS.FindChildByName("AnswerResponse", false); + if (excludeBackwardCompatible) + Assert.Null(obsoleteResponseClass); + else + { + Assert.NotNull(obsoleteResponseClass); + Assert.Equal("some description", obsoleteResponseClass.Documentation.Description); + Assert.True(obsoleteResponseClass.Deprecation.IsDeprecated); + } + + var requestBuilderClass = modelsSubNS.Classes.FirstOrDefault(static c => c.IsOfKind(CodeClassKind.RequestBuilder)); + Assert.NotNull(requestBuilderClass); + Assert.Equal("some path item description", requestBuilderClass.Documentation.Description); + + if (excludeBackwardCompatible) + Assert.Single(requestBuilderClass.Methods.Where(static x => x.Kind is CodeMethodKind.RequestExecutor)); + else + Assert.Equal(2, requestBuilderClass.Methods.Where(static x => x.Kind is CodeMethodKind.RequestExecutor).Count()); - var responseProperty = codeModel.FindNamespaceByName("TestSdk").Classes.SelectMany(c => c.Properties).FirstOrDefault(p => p.Kind == CodePropertyKind.RequestBuilder); + var responseProperty = codeModel.FindNamespaceByName("TestSdk").Classes.SelectMany(c => c.Properties).FirstOrDefault(static p => p.Kind == CodePropertyKind.RequestBuilder); Assert.NotNull(responseProperty); Assert.Equal("some path item description", responseProperty.Documentation.Description); } @@ -6213,7 +6230,7 @@ await File.WriteAllTextAsync(tempFilePath, @$"openapi: 3.0.1 var codeModel = builder.CreateSourceModel(node); var rootNS = codeModel.FindNamespaceByName("ApiSdk"); Assert.NotNull(rootNS); - var inlineType = rootNS.FindChildByName($"enumerationResponse_{expected}", true); + var inlineType = rootNS.FindChildByName($"enumerationGetResponse_{expected}", true); Assert.NotNull(inlineType); } [Fact] @@ -6525,7 +6542,7 @@ public async Task MergesIntersectionTypes() var document = await builder.CreateOpenApiDocumentAsync(fs); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); - var resultClass = codeModel.FindChildByName("DirectoryObjectResponse"); + var resultClass = codeModel.FindChildByName("DirectoryObjectGetResponse"); Assert.NotNull(resultClass); Assert.Equal(4, resultClass.Properties.Where(static x => x.IsOfKind(CodePropertyKind.Custom)).Count()); } @@ -6565,7 +6582,7 @@ public async Task SkipsInvalidItemsProperties() var document = await builder.CreateOpenApiDocumentAsync(fs); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); - var resultClass = codeModel.FindChildByName("DirectoryObjectResponse"); + var resultClass = codeModel.FindChildByName("DirectoryObjectGetResponse"); Assert.NotNull(resultClass); var keysToCheck = new HashSet(StringComparer.OrdinalIgnoreCase) { "datasets", "datakeys", "datainfo" }; Assert.Empty(resultClass.Properties.Where(x => x.IsOfKind(CodePropertyKind.Custom) && keysToCheck.Contains(x.Name)));