diff --git a/.github/workflows/project-auto-add.yml b/.github/workflows/project-auto-add.yml index 00534c925a..a6ebb10d30 100644 --- a/.github/workflows/project-auto-add.yml +++ b/.github/workflows/project-auto-add.yml @@ -13,7 +13,7 @@ on: jobs: track_issue: - if: github.actor != 'dependabot[bot]' + if: github.actor != 'dependabot[bot]' && github.event.pull_request.head.repo.fork == false runs-on: ubuntu-latest steps: - name: Generate token diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a91a69048..98ce954783 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixes a bug where CLI Generation does not handle path parameters of type "string" and format "date", "date-time", "time", etc. [#4615](https://github.com/microsoft/kiota/issues/4615) - Fixes a bug where request executors would be missing Untyped parameters in dotnet [#4692](https://github.com/microsoft/kiota/issues/4692) - Fixes a bug where indexers in include/exclude patters were not normalized if the indexer was the last segment without a slash at the end [#4715](https://github.com/microsoft/kiota/issues/4715) -- Fixes a bug where CLI generation doesnot handle parameters of type string array. [#4707](https://github.com/microsoft/kiota/issues/4707) +- Fixes a bug where CLI generation doesn't handle parameters of type string array. [#4707](https://github.com/microsoft/kiota/issues/4707) +- Fixed a bug where models would not be created when a multipart content schema existed with no encoding [#4734](https://github.com/microsoft/kiota/issues/4734) +- Types generated by Kiota are now referenced with their full name to avoid namespace ambiguities [#4475](https://github.com/microsoft/kiota/issues/4475) +- Fixes a bug where warnings about discriminator not being inherited were generated [#4761](https://github.com/microsoft/kiota/issues/4761) +- Fix scalar member composed type serialization in PHP [#2827](https://github.com/microsoft/kiota/issues/2827) +- Trims unused components from output openApi document when generating plugins [#4672](https://github.com/microsoft/kiota/issues/4672) +- Fixes missing imports for UntypedNode when backingstore is enabled in Java. ## [1.14.0] - 2024-05-02 diff --git a/it/csharp/dotnet.csproj b/it/csharp/dotnet.csproj index 92cd67ba5e..dc7a740ebb 100644 --- a/it/csharp/dotnet.csproj +++ b/it/csharp/dotnet.csproj @@ -10,7 +10,7 @@ - + diff --git a/it/python/requirements-dev.txt b/it/python/requirements-dev.txt index babd89e635..5f1eff911c 100644 --- a/it/python/requirements-dev.txt +++ b/it/python/requirements-dev.txt @@ -2,7 +2,7 @@ astroid==3.2.2 ; python_full_version >= '3.7.2' -certifi==2024.2.2 ; python_version >= '3.6' +certifi==2024.6.2 ; python_version >= '3.6' charset-normalizer==3.3.2 ; python_full_version >= '3.7.0' @@ -56,7 +56,7 @@ tomli-w==1.0.0 ; python_version >= '3.7' tomlkit==0.12.5 ; python_version >= '3.7' -typing-extensions==4.12.0 ; python_version >= '3.7' +typing-extensions==4.12.1 ; python_version >= '3.7' urllib3==2.2.1 ; python_version >= '3.7' @@ -64,7 +64,7 @@ wrapt==1.15.0 ; python_version < '3.11' yapf==0.40.2 -zipp==3.19.0 ; python_version >= '3.7' +zipp==3.19.1 ; python_version >= '3.7' aiohttp==3.9.5 ; python_version >= '3.6' diff --git a/it/typescript/package-lock.json b/it/typescript/package-lock.json index 6b55934cd6..5c9fff831b 100644 --- a/it/typescript/package-lock.json +++ b/it/typescript/package-lock.json @@ -22,14 +22,14 @@ }, "devDependencies": { "@es-exec/esbuild-plugin-start": "^0.0.5", - "@types/node": "^20.12.13", + "@types/node": "^20.14.1", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.8.0", "esbuild": "^0.21.4", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "minimist": "^1.2.8", - "prettier": "^3.2.5", + "prettier": "^3.3.0", "typescript": "^4.9.5" } }, @@ -827,9 +827,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.13.tgz", - "integrity": "sha512-gBGeanV41c1L171rR7wjbMiEpEI/l5XFQdLLfhr/REwpgDy/4U8y89+i8kRiLzDyZdOkXh+cRaTetUnCYutoXA==", + "version": "20.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.1.tgz", + "integrity": "sha512-T2MzSGEu+ysB/FkWfqmhV3PLyQlowdptmmgD20C6QxsS8Fmv5SjpZ1ayXaEC0S21/h5UJ9iA6W/5vSNU5l00OA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2878,9 +2878,9 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.0.tgz", + "integrity": "sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" diff --git a/it/typescript/package.json b/it/typescript/package.json index 35730498cd..abc57622aa 100644 --- a/it/typescript/package.json +++ b/it/typescript/package.json @@ -19,14 +19,14 @@ "prettier": "./.prettierrc.json", "devDependencies": { "@es-exec/esbuild-plugin-start": "^0.0.5", - "@types/node": "^20.12.13", + "@types/node": "^20.14.1", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.8.0", "esbuild": "^0.21.4", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "minimist": "^1.2.8", - "prettier": "^3.2.5", + "prettier": "^3.3.0", "typescript": "^4.9.5" }, "dependencies": { diff --git a/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs index 6d14c22441..aca62940e2 100644 --- a/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs @@ -47,6 +47,15 @@ internal static bool IsMultipartFormDataSchema(this IDictionary source, StructuredMimeTypesCollection structuredMimeTypes) + { + ArgumentNullException.ThrowIfNull(source); + ArgumentNullException.ThrowIfNull(structuredMimeTypes); + if (structuredMimeTypes.Count == 0) return false; + if (!source.ContainsKey(multipartMimeTypes.First())) return false; + if (source.Count == 1) return true; + return structuredMimeTypes.First() == multipartMimeTypes.First(); + } internal static IEnumerable GetValidSchemas(this IDictionary source, StructuredMimeTypesCollection structuredMimeTypes) { ArgumentNullException.ThrowIfNull(source); diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index ef05625247..226e831e78 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -1473,26 +1473,37 @@ private void AddRequestBuilderMethodParameters(OpenApiUrlTreeNode currentNode, O if (operation.GetRequestSchema(config.StructuredMimeTypes) is OpenApiSchema requestBodySchema) { CodeTypeBase requestBodyType; - if (operation.RequestBody.Content.IsMultipartFormDataSchema(config.StructuredMimeTypes)) + if (operation.RequestBody.Content.IsMultipartFormDataSchema(config.StructuredMimeTypes) + && operation.RequestBody.Content.IsMultipartTopMimeType(config.StructuredMimeTypes)) { - requestBodyType = new CodeType - { - Name = "MultipartBody", - IsExternal = true, - }; var mediaType = operation.RequestBody.Content.First(x => x.Value.Schema == requestBodySchema).Value; - foreach (var encodingEntry in mediaType.Encoding - .Where(x => !string.IsNullOrEmpty(x.Value.ContentType) && - config.StructuredMimeTypes.Contains(x.Value.ContentType))) + if (mediaType.Encoding.Any()) { - if (CreateModelDeclarations(currentNode, requestBodySchema.Properties[encodingEntry.Key], operation, method, $"{operationType}RequestBody", isRequestBody: true) is CodeType propertyType && - propertyType.TypeDefinition is not null) - multipartPropertiesModels.TryAdd(propertyType.TypeDefinition, true); + requestBodyType = new CodeType { Name = "MultipartBody", IsExternal = true, }; + foreach (var encodingEntry in mediaType.Encoding + .Where(x => !string.IsNullOrEmpty(x.Value.ContentType) && + config.StructuredMimeTypes.Contains(x.Value.ContentType))) + { + if (CreateModelDeclarations(currentNode, requestBodySchema.Properties[encodingEntry.Key], + operation, method, $"{operationType}RequestBody", + isRequestBody: true) is CodeType propertyType && + propertyType.TypeDefinition is not null) + multipartPropertiesModels.TryAdd(propertyType.TypeDefinition, true); + } + } + else + { + requestBodyType = CreateModelDeclarations(currentNode, requestBodySchema, operation, method, + $"{operationType}RequestBody", isRequestBody: true) ?? + throw new InvalidSchemaException(); } } else - requestBodyType = CreateModelDeclarations(currentNode, requestBodySchema, operation, method, $"{operationType}RequestBody", isRequestBody: true) ?? - throw new InvalidSchemaException(); + { + requestBodyType = CreateModelDeclarations(currentNode, requestBodySchema, operation, method, + $"{operationType}RequestBody", isRequestBody: true) ?? + throw new InvalidSchemaException(); + } method.AddParameter(new CodeParameter { Name = "body", @@ -1550,6 +1561,7 @@ private void AddRequestBuilderMethodParameters(OpenApiUrlTreeNode currentNode, O PossibleValues = contentTypes.ToList() }); } + method.AddParameter(new CodeParameter { Name = "requestConfiguration", @@ -2152,7 +2164,8 @@ internal static void AddDiscriminatorMethod(CodeClass newClass, string discrimin } if (baseClass is not null && (result.TypeDefinition is not CodeClass codeClass || codeClass.StartBlock.Inherits is null)) { - logger.LogWarning("Discriminator {ComponentKey} is not inherited from {ClassName}.", componentKey, baseClass.Name); + if (!baseClass.Equals(result.TypeDefinition))// don't log warning if the discriminator points to the base type itself as this is implicitly the default case. + logger.LogWarning("Discriminator {ComponentKey} is not inherited from {ClassName}.", componentKey, baseClass.Name); return null; } return result; diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index 7a51fe4b2e..7b5daca469 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -51,7 +51,8 @@ public async Task GenerateManifestAsync(CancellationToken cancellationToken = de await using var fileWriter = new StreamWriter(descriptionStream); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task var descriptionWriter = new OpenApiYamlWriter(fileWriter); - OAIDocument.SerializeAsV3(descriptionWriter); + var trimmedPluginDocument = GetDocumentWithTrimmedComponentsAndResponses(OAIDocument); + trimmedPluginDocument.SerializeAsV3(descriptionWriter); descriptionWriter.Flush(); // write the plugins @@ -93,6 +94,55 @@ public async Task GenerateManifestAsync(CancellationToken cancellationToken = de await writer.FlushAsync(cancellationToken).ConfigureAwait(false); } } + + + private OpenApiDocument GetDocumentWithTrimmedComponentsAndResponses(OpenApiDocument doc) + { + // ensure the info and components are not null + doc.Info ??= new OpenApiInfo(); + doc.Components ??= new OpenApiComponents(); + + if (string.IsNullOrEmpty(doc.Info?.Version)) // filtering fails if there's no version. + doc.Info!.Version = "1.0"; + + //empty out all the responses with a single empty 2XX + foreach (var operation in doc.Paths.SelectMany(static item => item.Value.Operations.Values)) + { + operation.Responses = new OpenApiResponses() + { + { + "2XX",new OpenApiResponse + { + Content = new Dictionary + { + { + "text/plain", new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "string" + } + } + } + } + } + } + }; + } + + // remove unused components using the OpenApi.Net + var requestUrls = new Dictionary>(); + var basePath = doc.GetAPIRootUrl(Configuration.OpenAPIFilePath); + foreach (var path in doc.Paths.Where(static path => path.Value.Operations.Count > 0)) + { + var key = string.IsNullOrEmpty(basePath) ? path.Key : $"{basePath}/{path.Key.TrimStart(KiotaBuilder.ForwardSlash)}"; + requestUrls[key] = path.Value.Operations.Keys.Select(static key => key.ToString().ToUpperInvariant()).ToList(); + } + + var predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source: doc); + return OpenApiFilterService.CreateFilteredDocument(doc, predicate); + } + private PluginManifestDocument GetV1ManifestDocument(string openApiDocumentPath) { var descriptionForHuman = OAIDocument.Info?.Description.CleanupXMLString() is string d && !string.IsNullOrEmpty(d) ? d : $"Description for {OAIDocument.Info?.Title.CleanupXMLString()}"; diff --git a/src/Kiota.Builder/Refiners/JavaRefiner.cs b/src/Kiota.Builder/Refiners/JavaRefiner.cs index 1890dd04c7..db5593f75f 100644 --- a/src/Kiota.Builder/Refiners/JavaRefiner.cs +++ b/src/Kiota.Builder/Refiners/JavaRefiner.cs @@ -264,7 +264,7 @@ private static void AddEnumSetImport(CodeElement currentElement) AbstractionsNamespaceName, "QueryParameters"), new (static x => x is CodeClass @class && @class.OriginalComposedType is CodeIntersectionType intersectionType && intersectionType.Types.Any(static y => !y.IsExternal), SerializationNamespaceName, "ParseNodeHelper"), - new (static x => x is CodeProperty prop && prop.IsOfKind(CodePropertyKind.Custom) && prop.Type.Name.Equals(KiotaBuilder.UntypedNodeName, StringComparison.OrdinalIgnoreCase), + new (static x => (x is CodeMethod @method && @method.IsOfKind(CodeMethodKind.Getter, CodeMethodKind.Setter) && @method.AccessedProperty != null && (@method.AccessedProperty.IsOfKind(CodePropertyKind.Custom) && @method.AccessedProperty.Type.Name.Equals(KiotaBuilder.UntypedNodeName, StringComparison.OrdinalIgnoreCase) )) , SerializationNamespaceName, KiotaBuilder.UntypedNodeName), new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.RequestExecutor, CodeMethodKind.RequestGenerator) && method.Parameters.Any(static y => y.IsOfKind(CodeParameterKind.RequestBody) && y.Type.Name.Equals(MultipartBodyClassName, StringComparison.OrdinalIgnoreCase)), AbstractionsNamespaceName, MultipartBodyClassName) diff --git a/src/Kiota.Builder/Refiners/PhpRefiner.cs b/src/Kiota.Builder/Refiners/PhpRefiner.cs index c861b2405e..95985de135 100644 --- a/src/Kiota.Builder/Refiners/PhpRefiner.cs +++ b/src/Kiota.Builder/Refiners/PhpRefiner.cs @@ -14,6 +14,7 @@ public class PhpRefiner : CommonLanguageRefiner private static readonly CodeUsingDeclarationNameComparer usingComparer = new(); public PhpRefiner(GenerationConfiguration configuration) : base(configuration) { } + private const string AbstractionsNamespaceName = @"Microsoft\Kiota\Abstractions"; public override Task Refine(CodeNamespace generatedCode, CancellationToken cancellationToken) { @@ -31,7 +32,7 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance Name = "BaseRequestBuilder", Declaration = new CodeType { - Name = "Microsoft\\Kiota\\Abstractions", + Name = $@"{AbstractionsNamespaceName}", IsExternal = true } }, AccessModifier.Public); @@ -45,7 +46,10 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance ConvertUnionTypesToWrapper(generatedCode, _configuration.UsesBackingStore, static s => s, - false); + false, + $@"{AbstractionsNamespaceName}\Serialization", + "ComposedTypeWrapper" + ); ReplaceReservedNames(generatedCode, new PhpReservedNamesProvider(), reservedWord => $"Escaped{reservedWord.ToFirstCharacterUpperCase()}", new HashSet { typeof(CodeEnumOption) }); AddQueryParameterFactoryMethod(generatedCode); AddPrimaryErrorMessage(generatedCode, "getPrimaryErrorMessage", () => new CodeType { IsExternal = true, IsNullable = false, Name = "string" }); @@ -71,7 +75,7 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance AddParentClassToErrorClasses( generatedCode, "ApiException", - "Microsoft\\Kiota\\Abstractions" + AbstractionsNamespaceName ); MoveClassesWithNamespaceNamesUnderNamespace(generatedCode); AddConstructorsForDefaultValues(generatedCode, true); @@ -106,7 +110,7 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance } ); cancellationToken.ThrowIfCancellationRequested(); - AddSerializationModulesImport(generatedCode, ["Microsoft\\Kiota\\Abstractions\\ApiClientBuilder"], null, '\\'); + AddSerializationModulesImport(generatedCode, [$@"{AbstractionsNamespaceName}\ApiClientBuilder"], null, '\\'); cancellationToken.ThrowIfCancellationRequested(); AddPropertiesAndMethodTypesImports(generatedCode, true, false, true); CorrectBackingStoreSetterParam(generatedCode); @@ -119,7 +123,7 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance Name = "BaseRequestConfiguration", Declaration = new CodeType { - Name = "Microsoft\\Kiota\\Abstractions", + Name = AbstractionsNamespaceName, IsExternal = true } }); @@ -143,7 +147,7 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance Name = "Date", Declaration = new CodeType { - Name = "Microsoft\\Kiota\\Abstractions\\Types", + Name = $@"{AbstractionsNamespaceName}\Types", IsExternal = true, }, }) @@ -154,7 +158,7 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance Name = "Time", Declaration = new CodeType { - Name = "Microsoft\\Kiota\\Abstractions\\Types", + Name = $@"{AbstractionsNamespaceName}\Types", IsExternal = true, }, }) @@ -184,34 +188,34 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance }; private static readonly AdditionalUsingEvaluator[] defaultUsingEvaluators = { new (static x => x is CodeProperty prop && prop.IsOfKind(CodePropertyKind.RequestAdapter), - "Microsoft\\Kiota\\Abstractions", "RequestAdapter"), + AbstractionsNamespaceName, "RequestAdapter"), new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.RequestGenerator), - "Microsoft\\Kiota\\Abstractions", "HttpMethod", "RequestInformation"), + AbstractionsNamespaceName, "HttpMethod", "RequestInformation"), new (static x => x is CodeClass @class && @class.IsOfKind(CodeClassKind.Model) && @class.Properties.Any(static y => y.IsOfKind(CodePropertyKind.AdditionalData)), - "Microsoft\\Kiota\\Abstractions\\Serialization", "AdditionalDataHolder"), + $@"{AbstractionsNamespaceName}\Serialization", "AdditionalDataHolder"), new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.Serializer), - "Microsoft\\Kiota\\Abstractions\\Serialization", "SerializationWriter"), + $@"{AbstractionsNamespaceName}\Serialization", "SerializationWriter"), new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.Deserializer), - "Microsoft\\Kiota\\Abstractions\\Serialization", "ParseNode"), + $@"{AbstractionsNamespaceName}\Serialization", "ParseNode"), new (static x => x is CodeClass @class && @class.IsOfKind(CodeClassKind.Model), - "Microsoft\\Kiota\\Abstractions\\Serialization", "Parsable"), + $@"{AbstractionsNamespaceName}\Serialization", "Parsable"), new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.ClientConstructor) && method.Parameters.Any(y => y.IsOfKind(CodeParameterKind.BackingStore)), - "Microsoft\\Kiota\\Abstractions\\Store", "BackingStoreFactory", "BackingStoreFactorySingleton"), + $@"{AbstractionsNamespaceName}\Store", "BackingStoreFactory", "BackingStoreFactorySingleton"), new (static x => x is CodeProperty prop && prop.IsOfKind(CodePropertyKind.BackingStore), - "Microsoft\\Kiota\\Abstractions\\Store", "BackingStore", "BackedModel", "BackingStoreFactorySingleton" ), + $@"{AbstractionsNamespaceName}\Store", "BackingStore", "BackedModel", "BackingStoreFactorySingleton" ), new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.RequestExecutor), "Http\\Promise", "Promise"), new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.RequestExecutor), "", "Exception"), - new (static x => x is CodeEnum, "Microsoft\\Kiota\\Abstractions\\", "Enum"), + new (static x => x is CodeEnum, AbstractionsNamespaceName, "Enum"), new(static x => x is CodeProperty {Type.Name: {}} property && property.Type.Name.Equals("DateTime", StringComparison.OrdinalIgnoreCase), "", "\\DateTime"), new(static x => x is CodeProperty {Type.Name: {}} property && property.Type.Name.Equals("DateTimeOffset", StringComparison.OrdinalIgnoreCase), "", "\\DateTime"), - new(static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.ClientConstructor), "Microsoft\\Kiota\\Abstractions", "ApiClientBuilder"), - new(static x => x is CodeProperty property && property.IsOfKind(CodePropertyKind.QueryParameter) && !string.IsNullOrEmpty(property.SerializationName), "Microsoft\\Kiota\\Abstractions", "QueryParameter"), - new(static x => x is CodeClass codeClass && codeClass.IsOfKind(CodeClassKind.RequestConfiguration), "Microsoft\\Kiota\\Abstractions", "RequestOption"), + new(static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.ClientConstructor), AbstractionsNamespaceName, "ApiClientBuilder"), + new(static x => x is CodeProperty property && property.IsOfKind(CodePropertyKind.QueryParameter) && !string.IsNullOrEmpty(property.SerializationName), AbstractionsNamespaceName, "QueryParameter"), + new(static x => x is CodeClass codeClass && codeClass.IsOfKind(CodeClassKind.RequestConfiguration), AbstractionsNamespaceName, "RequestOption"), new (static x => x is CodeClass { OriginalComposedType: CodeIntersectionType intersectionType } && intersectionType.Types.Any(static y => !y.IsExternal), - "Microsoft\\Kiota\\Abstractions\\Serialization", "ParseNodeHelper"), + $@"{AbstractionsNamespaceName}\Serialization", "ParseNodeHelper"), new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.RequestExecutor, CodeMethodKind.RequestGenerator) && method.Parameters.Any(static y => y.IsOfKind(CodeParameterKind.RequestBody) && y.Type.Name.Equals(MultipartBodyClassName, StringComparison.OrdinalIgnoreCase)), - @"Microsoft\Kiota\Abstractions", MultipartBodyClassName) + AbstractionsNamespaceName, MultipartBodyClassName) }; private const string MultipartBodyClassName = "MultiPartBody"; @@ -339,7 +343,7 @@ private void AddCollectionValidationUtilImportToModels(CodeElement codeElement) { if (codeElement is CodeClass codeClass && codeClass.Kind == CodeClassKind.Model) { - var typeUtilsUsing = new CodeUsing { Name = "TypeUtils", Declaration = new CodeType { Name = "Microsoft\\Kiota\\Abstractions\\Types", IsExternal = true } }; + var typeUtilsUsing = new CodeUsing { Name = "TypeUtils", Declaration = new CodeType { Name = $@"{AbstractionsNamespaceName}\Types", IsExternal = true } }; if (codeClass.Properties.Any(x => x.Kind == CodePropertyKind.Custom && x.Type is CodeType codeType diff --git a/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs b/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs index 5a5c1309e8..9522fa678e 100644 --- a/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs +++ b/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs @@ -177,7 +177,7 @@ public string GetTypeString(CodeTypeBase code, CodeElement targetElement, bool i throw new InvalidOperationException($"CSharp does not support union types, the union type {code.Name} should have been filtered out by the refiner"); if (code is CodeType currentType) { - var typeName = TranslateTypeAndAvoidUsingNamespaceSegmentNames(currentType, targetElement); + var typeName = TranslateType(currentType); var nullableSuffix = ShouldTypeHaveNullableMarker(code, typeName) && includeNullableInformation ? NullableMarkerAsString : string.Empty; var collectionPrefix = currentType.CollectionKind == CodeTypeCollectionKind.Complex && includeCollectionInformation ? "List<" : string.Empty; var collectionSuffix = currentType.CollectionKind switch @@ -196,76 +196,14 @@ public string GetTypeString(CodeTypeBase code, CodeElement targetElement, bool i throw new InvalidOperationException($"type of type {code?.GetType()} is unknown"); } - private string TranslateTypeAndAvoidUsingNamespaceSegmentNames(CodeType currentType, CodeElement targetElement) - { - var parentElementsHash = targetElement.Parent is CodeClass parentClass ? - parentClass.Methods.Select(static x => x.Name) - .Union(parentClass.Properties.Select(static x => x.Name)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToHashSet(StringComparer.OrdinalIgnoreCase) : - new HashSet(0, StringComparer.OrdinalIgnoreCase); - - var typeName = TranslateType(currentType); - var areElementsInSameNamesSpace = DoesTypeExistsInSameNamesSpaceAsTarget(currentType, targetElement); - if (currentType.TypeDefinition != null && - ( - GetNamesInUseByNamespaceSegments(targetElement).Contains(typeName) && !areElementsInSameNamesSpace // match if elements are not in the same namespace and the type name is used in the namespace segments - || parentElementsHash.Contains(typeName) // match if type name is used in the parent elements segments - || !areElementsInSameNamesSpace && DoesTypeExistsInTargetAncestorNamespace(currentType, targetElement) // match if elements are not in the same namespace and the type exists in target ancestor namespace - || !areElementsInSameNamesSpace && DoesTypeExistsInOtherImportedNamespaces(currentType, targetElement) // match if elements is not imported already by another namespace. - ) - ) - return $"{currentType.TypeDefinition.GetImmediateParentOfType().Name}.{typeName}"; - return typeName; - } - - private static bool DoesTypeExistsInSameNamesSpaceAsTarget(CodeType currentType, CodeElement targetElement) - { - return currentType?.TypeDefinition?.GetImmediateParentOfType()?.Name.Equals(targetElement?.GetImmediateParentOfType()?.Name, StringComparison.OrdinalIgnoreCase) ?? false; - } - - private static bool DoesTypeExistsInTargetAncestorNamespace(CodeType currentType, CodeElement targetElement) - { - // Avoid type ambiguity on similarly named classes. Currently, if we have namespaces A and A.B where both namespaces have type T, - // Trying to use type A.B.T in namespace A without using a qualified name will break the build. - // Similarly, if we have type A.B.C.D.T1 that needs to be used within type A.B.C.T2, but there's also a type - // A.B.T1, using T1 in T2 will resolve A.B.T1 even if you have a using statement with A.B.C.D. - var hasChildWithName = false; - if (currentType != null && currentType.TypeDefinition != null && !currentType.IsExternal && targetElement != null) - { - var typeName = currentType.TypeDefinition.Name; - var ns = targetElement.GetImmediateParentOfType(); - var rootNs = ns?.GetRootNamespace(); - while (ns is not null && ns != rootNs && !hasChildWithName) - { - hasChildWithName = ns.GetChildElements(true).OfType().Any(c => c.Name?.Equals(typeName, StringComparison.OrdinalIgnoreCase) == true); - ns = ns.Parent is CodeNamespace n ? n : (ns.GetImmediateParentOfType()); - } - } - return hasChildWithName; - } - - private static bool DoesTypeExistsInOtherImportedNamespaces(CodeType currentType, CodeElement targetElement) - { - if (currentType.TypeDefinition is CodeClass { Parent: CodeNamespace currentTypeNamespace } codeClass) - { - var targetClass = targetElement.GetImmediateParentOfType(); - var importedNamespaces = targetClass.StartBlock.Usings - .Where(codeUsing => !codeUsing.IsExternal // 1. Are defined during generation(not external) - && codeUsing.Declaration?.TypeDefinition != null - && !codeUsing.Name.Equals(currentTypeNamespace.Name, StringComparison.OrdinalIgnoreCase)) // 2. Do not match the namespace of the current type - .Select(static codeUsing => codeUsing.Declaration!.TypeDefinition!.GetImmediateParentOfType()) - .DistinctBy(static declaredNamespace => declaredNamespace.Name); - - return importedNamespaces.Any(importedNamespace => (importedNamespace.FindChildByName(codeClass.Name, false) != null) - || (importedNamespace.FindChildByName(codeClass.Name, false) != null)); - } - return false; - } public override string TranslateType(CodeType type) { ArgumentNullException.ThrowIfNull(type); + + if (type.TypeDefinition is ITypeDefinition typeDefinition) + return typeDefinition.GetFullName(); + return type.Name switch { "integer" => "int", diff --git a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs index 66d29a759f..796e6a91ac 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs @@ -105,7 +105,9 @@ private void WriteRawUrlBuilderBody(CodeClass parentClass, CodeMethod codeElemen { var rawUrlParameter = codeElement.Parameters.OfKind(CodeParameterKind.RawUrl) ?? throw new InvalidOperationException("RawUrlBuilder method should have a RawUrl parameter"); var requestAdapterProperty = parentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) ?? throw new InvalidOperationException("RawUrlBuilder method should have a RequestAdapter property"); - writer.WriteLine($"return new {parentClass.Name.ToFirstCharacterUpperCase()}({rawUrlParameter.Name.ToFirstCharacterLowerCase()}, {requestAdapterProperty.Name.ToFirstCharacterUpperCase()});"); + + var fullName = parentClass.GetFullName(); + writer.WriteLine($"return new {fullName}({rawUrlParameter.Name.ToFirstCharacterLowerCase()}, {requestAdapterProperty.Name.ToFirstCharacterUpperCase()});"); } private static readonly CodePropertyTypeComparer CodePropertyTypeForwardComparer = new(); private static readonly CodePropertyTypeComparer CodePropertyTypeBackwardComparer = new(true); @@ -117,13 +119,13 @@ private void WriteFactoryMethodBodyForInheritedModel(CodeMethod codeElement, Cod { writer.WriteLine($"\"{mappedType.Key}\" => new {conventions.GetTypeString(mappedType.Value.AllTypes.First(), codeElement)}(),"); } - writer.WriteLine($"_ => new {parentClass.Name.ToFirstCharacterUpperCase()}(),"); + writer.WriteLine($"_ => new {parentClass.GetFullName()}(),"); writer.CloseBlock("};"); } private const string ResultVarName = "result"; private void WriteFactoryMethodBodyForUnionModel(CodeMethod codeElement, CodeClass parentClass, CodeParameter parseNodeParameter, LanguageWriter writer) { - writer.WriteLine($"var {ResultVarName} = new {parentClass.Name.ToFirstCharacterUpperCase()}();"); + writer.WriteLine($"var {ResultVarName} = new {parentClass.GetFullName()}();"); var includeElse = false; foreach (var property in parentClass.GetPropertiesOfKind(CodePropertyKind.Custom) .OrderBy(static x => x, CodePropertyTypeForwardComparer) @@ -150,7 +152,7 @@ private void WriteFactoryMethodBodyForUnionModel(CodeMethod codeElement, CodeCla } private void WriteFactoryMethodBodyForIntersectionModel(CodeMethod codeElement, CodeClass parentClass, CodeParameter parseNodeParameter, LanguageWriter writer) { - writer.WriteLine($"var {ResultVarName} = new {parentClass.Name.ToFirstCharacterUpperCase()}();"); + writer.WriteLine($"var {ResultVarName} = new {parentClass.GetFullName()}();"); var includeElse = false; foreach (var property in parentClass.GetPropertiesOfKind(CodePropertyKind.Custom) .Where(static x => x.Type is not CodeType propertyType || propertyType.IsCollection || propertyType.TypeDefinition is not CodeClass) @@ -202,7 +204,7 @@ private void WriteFactoryMethodBody(CodeMethod codeElement, CodeClass parentClas else if (parentClass.DiscriminatorInformation.ShouldWriteDiscriminatorForIntersectionType) WriteFactoryMethodBodyForIntersectionModel(codeElement, parentClass, parseNodeParameter, writer); else - writer.WriteLine($"return new {parentClass.Name.ToFirstCharacterUpperCase()}();"); + writer.WriteLine($"return new {parentClass.GetFullName()}();"); } private void WriteRequestBuilderBody(CodeClass parentClass, CodeMethod codeElement, LanguageWriter writer) { @@ -357,7 +359,7 @@ private string GetDeserializationMethodName(CodeTypeBase propType, CodeMethod me return $"GetCollectionOfObjectValues<{propertyType}>({propertyType}.CreateFromDiscriminatorValue){collectionMethod}"; } else if (currentType.TypeDefinition is CodeEnum enumType) - return $"GetEnumValue<{enumType.Name.ToFirstCharacterUpperCase()}>()"; + return $"GetEnumValue<{enumType.GetFullName()}>()"; } return propertyType switch { @@ -662,7 +664,7 @@ private string GetSerializationMethodName(CodeTypeBase propType, CodeMethod meth else return $"WriteCollectionOfObjectValues<{propertyType}>"; else if (currentType.TypeDefinition is CodeEnum enumType) - return $"WriteEnumValue<{enumType.Name.ToFirstCharacterUpperCase()}>"; + return $"WriteEnumValue<{enumType.GetFullName()}>"; } return propertyType switch diff --git a/src/Kiota.Builder/Writers/CSharp/TypeDefinitionExtensions.cs b/src/Kiota.Builder/Writers/CSharp/TypeDefinitionExtensions.cs new file mode 100644 index 0000000000..c6ef2fac85 --- /dev/null +++ b/src/Kiota.Builder/Writers/CSharp/TypeDefinitionExtensions.cs @@ -0,0 +1,44 @@ +using System; +using System.Text; +using Kiota.Builder.CodeDOM; +using Kiota.Builder.Extensions; + +namespace Kiota.Builder.Writers.CSharp; + +internal static class TypeDefinitionExtensions +{ + public static string GetFullName(this ITypeDefinition typeDefinition) + { + ArgumentNullException.ThrowIfNull(typeDefinition); + + var fullNameBuilder = new StringBuilder(); + return AppendTypeName(typeDefinition, fullNameBuilder).ToString(); + } + + private static StringBuilder AppendTypeName(ITypeDefinition typeDefinition, StringBuilder fullNameBuilder) + { + if (string.IsNullOrEmpty(typeDefinition.Name)) + throw new ArgumentException("Cannot append a full name for a type without a name.", nameof(typeDefinition)); + + fullNameBuilder.Insert(0, typeDefinition.Name.ToFirstCharacterUpperCase()); + switch (typeDefinition.Parent) + { + case null: + return fullNameBuilder; + case ITypeDefinition parentTypeDefinition: + { + fullNameBuilder.Insert(0, '.'); + return AppendTypeName(parentTypeDefinition, fullNameBuilder); + } + case CodeNamespace codeNamespace: + { + if (!string.IsNullOrEmpty(codeNamespace.Name)) + fullNameBuilder.Insert(0, $"{codeNamespace.Name}."); + + return fullNameBuilder; + } + default: + throw new InvalidOperationException($"Type {typeDefinition.Name} contains an invalid parent of type {typeDefinition.Parent.GetType().FullName}."); + } + } +} diff --git a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs index 4a96bd3eec..8b8c870165 100644 --- a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs @@ -855,7 +855,7 @@ private void WriteModelFactoryMethodBody(CodeMethod codeElement, CodeClass paren { if (parentClass.DiscriminatorInformation.ShouldWriteDiscriminatorForUnionType || parentClass.DiscriminatorInformation.ShouldWriteDiscriminatorForIntersectionType) writer.WriteLine($"{ResultVarName} = new {parentClass.Name.ToFirstCharacterUpperCase()}();"); - var writeDiscriminatorValueRead = parentClass.DiscriminatorInformation.ShouldWriteParseNodeCheck && !parentClass.DiscriminatorInformation.ShouldWriteDiscriminatorForIntersectionType; + var writeDiscriminatorValueRead = parentClass.DiscriminatorInformation is { ShouldWriteParseNodeCheck: true, ShouldWriteDiscriminatorForIntersectionType: false, HasBasicDiscriminatorInformation: true }; if (writeDiscriminatorValueRead && codeElement.Parameters.OfKind(CodeParameterKind.ParseNode) is CodeParameter parseNodeParameter) @@ -868,7 +868,7 @@ private void WriteModelFactoryMethodBody(CodeMethod codeElement, CodeClass paren if (parentClass.DiscriminatorInformation.ShouldWriteDiscriminatorForInheritedType) WriteFactoryMethodBodyForInheritedModel(parentClass.DiscriminatorInformation.DiscriminatorMappings, writer, codeElement); - else if (parentClass.DiscriminatorInformation.ShouldWriteDiscriminatorForUnionType && parentClass.DiscriminatorInformation.HasBasicDiscriminatorInformation) + else if (parentClass.DiscriminatorInformation is { ShouldWriteDiscriminatorForUnionType: true, HasBasicDiscriminatorInformation: true }) WriteFactoryMethodBodyForUnionModelForDiscriminatedTypes(codeElement, parentClass, writer); else if (parentClass.DiscriminatorInformation.ShouldWriteDiscriminatorForIntersectionType) WriteFactoryMethodBodyForIntersectionModel(codeElement, parentClass, writer); diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index 19c2d8a249..fc0df2d7d5 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -3426,6 +3426,13 @@ public async Task AddsDiscriminatorMappingsAllOfImplicit() var entityClass = codeModel.FindChildByName("entity"); var directoryObjectClass = codeModel.FindChildByName("directoryObject"); var userClass = codeModel.FindChildByName("user"); + mockLogger.Verify(logger => logger.Log( + It.Is(logLevel => logLevel == LogLevel.Warning), + It.Is(eventId => eventId.Id == 0), + It.Is((@object, @type) => @object.ToString().Contains(" is not inherited from ", StringComparison.OrdinalIgnoreCase) && @type.Name == "FormattedLogValues"), + It.IsAny(), + It.IsAny>()), + Times.Never); Assert.NotNull(entityClass); Assert.NotNull(directoryObjectClass); Assert.NotNull(userClass); @@ -7180,7 +7187,7 @@ public async Task CleanupSymbolNameDoesNotCauseNameConflictsInQueryParameters() Assert.Equal("int64", select.Type.Name); } [Fact] - public async Task SupportsMultiPartFormAsRequestBody() + public async Task SupportsMultiPartFormAsRequestBodyWithDefaultMimeTypes() { var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); await using var fs = await GetDocumentStream(@"openapi: 3.0.1 @@ -7246,6 +7253,285 @@ public async Task SupportsMultiPartFormAsRequestBody() Assert.NotNull(addressClass); } [Fact] + public async Task SupportsMultiPartFormAsRequestBodyWithoutEncodingWithDefaultMimeTypes() + { + var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await using var fs = await GetDocumentStream(@"openapi: 3.0.1 +info: + title: Example + description: Example + version: 1.0.1 +servers: + - url: https://example.org +paths: + /directoryObject: + post: + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + id: + type: string + format: uuid + address: + $ref: '#/components/schemas/address' + profileImage: + type: string + format: binary + responses: + '204': + content: + application/json: + schema: + type: string +components: + schemas: + address: + type: object + properties: + street: + type: string + city: + type: string"); + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath, IncludeAdditionalData = false }, _httpClient); + var document = await builder.CreateOpenApiDocumentAsync(fs); + var node = builder.CreateUriSpace(document); + var codeModel = builder.CreateSourceModel(node); + Assert.NotNull(codeModel); + var rbClass = codeModel.FindChildByName("directoryObjectRequestBuilder"); + Assert.NotNull(rbClass); + var postMethod = rbClass.FindChildByName("Post", false); + Assert.NotNull(postMethod); + var bodyParameter = postMethod.Parameters.FirstOrDefault(static x => x.IsOfKind(CodeParameterKind.RequestBody)); + Assert.NotNull(bodyParameter); + Assert.Equal("directoryObjectPostRequestBody", bodyParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + var addressClass = codeModel.FindChildByName("Address"); + Assert.NotNull(addressClass); + } + [Fact] + public async Task SupportsMultipleContentTypesAsRequestBodyWithDefaultMimeTypes() + { + var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await using var fs = await GetDocumentStream(@"openapi: 3.0.1 +info: + title: Example + description: Example + version: 1.0.1 +servers: + - url: https://example.org +paths: + /directoryObject: + post: + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + id: + type: string + format: uuid + address: + $ref: '#/components/schemas/address' + profileImage: + type: string + format: binary + application/json: + schema: + type: object + properties: + id: + type: string + format: uuid + address: + $ref: '#/components/schemas/address' + profileImage: + type: string + format: binary + responses: + '204': + content: + application/json: + schema: + type: string +components: + schemas: + address: + type: object + properties: + street: + type: string + city: + type: string"); + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath, IncludeAdditionalData = false }, _httpClient); + var document = await builder.CreateOpenApiDocumentAsync(fs); + var node = builder.CreateUriSpace(document); + var codeModel = builder.CreateSourceModel(node); + Assert.NotNull(codeModel); + var rbClass = codeModel.FindChildByName("directoryObjectRequestBuilder"); + Assert.NotNull(rbClass); + var postMethod = rbClass.FindChildByName("Post", false); + Assert.NotNull(postMethod); + var bodyParameter = postMethod.Parameters.FirstOrDefault(static x => x.IsOfKind(CodeParameterKind.RequestBody)); + Assert.NotNull(bodyParameter); + Assert.Equal("directoryObjectPostRequestBody", bodyParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + var addressClass = codeModel.FindChildByName("Address"); + Assert.NotNull(addressClass); + } + [Fact] + public async Task SupportsMultipleContentTypesAsRequestBodyWithMultipartPriorityNoEncoding() + { + var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await using var fs = await GetDocumentStream(@"openapi: 3.0.1 +info: + title: Example + description: Example + version: 1.0.1 +servers: + - url: https://example.org +paths: + /directoryObject: + post: + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + id: + type: string + format: uuid + address: + $ref: '#/components/schemas/address' + profileImage: + type: string + format: binary + application/json: + schema: + type: object + properties: + id: + type: string + format: uuid + address: + $ref: '#/components/schemas/address' + profileImage: + type: string + format: binary + responses: + '204': + content: + application/json: + schema: + type: string +components: + schemas: + address: + type: object + properties: + street: + type: string + city: + type: string"); + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath, IncludeAdditionalData = false, StructuredMimeTypes = new StructuredMimeTypesCollection { "multipart/form-data;q=1", "application/json;q=0.1" } }, _httpClient); + var document = await builder.CreateOpenApiDocumentAsync(fs); + var node = builder.CreateUriSpace(document); + var codeModel = builder.CreateSourceModel(node); + Assert.NotNull(codeModel); + var rbClass = codeModel.FindChildByName("directoryObjectRequestBuilder"); + Assert.NotNull(rbClass); + var postMethod = rbClass.FindChildByName("Post", false); + Assert.NotNull(postMethod); + var bodyParameter = postMethod.Parameters.FirstOrDefault(static x => x.IsOfKind(CodeParameterKind.RequestBody)); + Assert.NotNull(bodyParameter); + Assert.Equal("directoryObjectPostRequestBody", bodyParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + var addressClass = codeModel.FindChildByName("Address"); + Assert.NotNull(addressClass); + } + [Fact] + public async Task SupportsMultipleContentTypesAsRequestBodyWithMultipartPriorityAndEncoding() + { + var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await using var fs = await GetDocumentStream(@"openapi: 3.0.1 +info: + title: Example + description: Example + version: 1.0.1 +servers: + - url: https://example.org +paths: + /directoryObject: + post: + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + id: + type: string + format: uuid + address: + $ref: '#/components/schemas/address' + profileImage: + type: string + format: binary + encoding: + id: + contentType: text/plain + address: + contentType: application/json + profileImage: + contentType: image/png + application/json: + schema: + type: object + properties: + id: + type: string + format: uuid + address: + $ref: '#/components/schemas/address' + profileImage: + type: string + format: binary + responses: + '204': + content: + application/json: + schema: + type: string +components: + schemas: + address: + type: object + properties: + street: + type: string + city: + type: string"); + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath, IncludeAdditionalData = false, StructuredMimeTypes = new StructuredMimeTypesCollection { "multipart/form-data;q=1", "application/json;q=0.1" } }, _httpClient); + var document = await builder.CreateOpenApiDocumentAsync(fs); + var node = builder.CreateUriSpace(document); + var codeModel = builder.CreateSourceModel(node); + Assert.NotNull(codeModel); + var rbClass = codeModel.FindChildByName("directoryObjectRequestBuilder"); + Assert.NotNull(rbClass); + var postMethod = rbClass.FindChildByName("Post", false); + Assert.NotNull(postMethod); + var bodyParameter = postMethod.Parameters.FirstOrDefault(static x => x.IsOfKind(CodeParameterKind.RequestBody)); + Assert.NotNull(bodyParameter); + Assert.Equal("MultipartBody", bodyParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + var addressClass = codeModel.FindChildByName("Address"); + Assert.NotNull(addressClass); + } + [Fact] public async Task ComplexInheritanceStructures() { var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs index 04e52db82c..d3b4e3cfbb 100644 --- a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs @@ -7,6 +7,8 @@ using Kiota.Builder.Configuration; using Kiota.Builder.Plugins; using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Services; using Microsoft.Plugins.Manifest; using Moq; @@ -109,4 +111,113 @@ public async Task GeneratesManifest() private const string ManifestFileName = "client-microsoft.json"; private const string OpenAIPluginFileName = "openai-plugins.json"; private const string OpenApiFileName = "client-openapi.yml"; + + [Fact] + public async Task GeneratesManifestAndCleansUpInputDescription() + { + var simpleDescriptionContent = @"openapi: 3.0.0 +info: + title: test + version: 1.0 +x-test-root-extension: test +servers: + - url: http://localhost/ + description: There's no place like home +paths: + /test: + get: + description: description for test path + responses: + '200': + description: test + '400': + description: client error response + /test/{id}: + get: + description: description for test path with id + operationId: test.WithId + parameters: + - name: id + in: path + required: true + description: The id of the test + schema: + type: integer + format: int32 + responses: + '200': + description: test + '500': + description: api error response +components: + schemas: + microsoft.graph.entity: + title: entity + required: + - '@odata.type' + type: object + properties: + id: + type: string + '@odata.type': + type: string"; + var workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; + await File.WriteAllTextAsync(simpleDescriptionPath, simpleDescriptionContent); + var mockLogger = new Mock>(); + var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object); + var outputDirectory = Path.Combine(workingDirectory, "output"); + var generationConfiguration = new GenerationConfiguration + { + OutputPath = outputDirectory, + OpenAPIFilePath = "openapiPath", + PluginTypes = [PluginType.Microsoft], + ClientClassName = "client", + ApiRootUrl = "http://localhost/", //Kiota builder would set this for us + }; + var (openAPIDocumentStream, _) = await openAPIDocumentDS.LoadStreamAsync(simpleDescriptionPath, generationConfiguration, null, false); + var openApiDocument = await openAPIDocumentDS.GetDocumentFromStreamAsync(openAPIDocumentStream, generationConfiguration); + KiotaBuilder.CleanupOperationIdForPlugins(openApiDocument); + var urlTreeNode = OpenApiUrlTreeNode.Create(openApiDocument, Constants.DefaultOpenApiLabel); + + var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory); + await pluginsGenerationService.GenerateManifestAsync(); + + Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName))); + Assert.True(File.Exists(Path.Combine(outputDirectory, OpenApiFileName))); + + // Validate the v2 plugin + var manifestContent = await File.ReadAllTextAsync(Path.Combine(outputDirectory, ManifestFileName)); + using var jsonDocument = JsonDocument.Parse(manifestContent); + var resultingManifest = PluginManifestDocument.Load(jsonDocument.RootElement); + Assert.NotNull(resultingManifest.Document); + Assert.Equal(OpenApiFileName, resultingManifest.Document.Runtimes.OfType().First().Spec.Url); + Assert.Equal(2, resultingManifest.Document.Functions.Count);// all functions are generated despite missing operationIds + Assert.Empty(resultingManifest.Problems);// no problems are expected with names + + var openApiReader = new OpenApiStreamReader(); + + // Validate the original file. + var originalOpenApiFile = File.OpenRead(simpleDescriptionPath); + var originalDocument = openApiReader.Read(originalOpenApiFile, out var originalDiagnostic); + Assert.Empty(originalDiagnostic.Errors); + + Assert.Single(originalDocument.Components.Schemas);// one schema originally + Assert.Single(originalDocument.Extensions); // single unsupported extension at root + Assert.Equal(2, originalDocument.Paths.Count); // document has only two paths + Assert.Equal(2, originalDocument.Paths["/test"].Operations[OperationType.Get].Responses.Count); // 2 responses originally + Assert.Equal(2, originalDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses.Count); // 2 responses originally + + // Validate the output open api file + var resultOpenApiFile = File.OpenRead(Path.Combine(outputDirectory, OpenApiFileName)); + var resultDocument = openApiReader.Read(resultOpenApiFile, out var diagnostic); + Assert.Empty(diagnostic.Errors); + + // Assertions / validations + Assert.Empty(resultDocument.Components.Schemas);// no schema is referenced. so ensure they are all removed + Assert.Empty(resultDocument.Extensions); // no extension at root (unsupported extension is removed) + Assert.Equal(2, resultDocument.Paths.Count); // document has only two paths + Assert.Single(resultDocument.Paths["/test"].Operations[OperationType.Get].Responses); // other responses are removed from the document + Assert.Single(resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses); // 2 responses originally + } } diff --git a/tests/Kiota.Builder.Tests/Refiners/JavaLanguageRefinerTests.cs b/tests/Kiota.Builder.Tests/Refiners/JavaLanguageRefinerTests.cs index 8c36e68216..df2087c009 100644 --- a/tests/Kiota.Builder.Tests/Refiners/JavaLanguageRefinerTests.cs +++ b/tests/Kiota.Builder.Tests/Refiners/JavaLanguageRefinerTests.cs @@ -703,8 +703,10 @@ public async Task SplitsLongRefiners() Assert.Equal("String", model.Methods.First(static x => x.IsOverload).Parameters.First().Type.Name); } - [Fact] - public async Task AddsUsingForUntypedNode() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task AddsUsingForUntypedNode(bool usesBackingStore) { var model = root.AddClass(new CodeClass { @@ -720,11 +722,11 @@ public async Task AddsUsingForUntypedNode() IsExternal = true }, }).First(); - await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.Java }, root); + await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.Java, UsesBackingStore = usesBackingStore }, root); Assert.Equal(KiotaBuilder.UntypedNodeName, property.Type.Name); Assert.NotEmpty(model.StartBlock.Usings); var nodeUsing = model.StartBlock.Usings.Where(static declaredUsing => declaredUsing.Name.Equals(KiotaBuilder.UntypedNodeName, StringComparison.OrdinalIgnoreCase)).ToArray(); - Assert.Single(nodeUsing); + Assert.Equal(2, nodeUsing.Length); // one for the getter and another for setter. Writer will unionise Assert.Equal("com.microsoft.kiota.serialization", nodeUsing[0].Declaration.Name); } #endregion diff --git a/tests/Kiota.Builder.Tests/Writers/CLI/CliCodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CLI/CliCodeMethodWriterTests.cs index ab5d455c24..1755ff0519 100644 --- a/tests/Kiota.Builder.Tests/Writers/CLI/CliCodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CLI/CliCodeMethodWriterTests.cs @@ -241,7 +241,7 @@ public void WritesIndexerCommands() writer.Write(method); var result = tw.ToString(); - Assert.Contains("var builder = new TestClass", result); + Assert.Contains("var builder = new Test.Name.Sub.TestClass", result); Assert.Contains("var commands = new List();", result); Assert.Contains("commands.Add(builder.BuildTestMethod1());", result); Assert.Contains("commands.AddRange(builder.BuildTestMethod2());", result); @@ -325,7 +325,7 @@ public void WritesMatchingIndexerCommandsIntoExecutableCommand() writer.Write(method); var result = tw.ToString(); - Assert.Contains("var testItemIdx = new TestItemRequestBuilder();", result); + Assert.Contains("var testItemIdx = new Test.Name.Sub.TestItemRequestBuilder();", result); Assert.Contains("var command = testItemIdx.BuildTestMethod1();", result); Assert.Contains("var cmds = testItemIdx.BuildTestMethod2();", result); Assert.DoesNotContain("execCommands.AddRange(cmds.Item1)", result); @@ -418,12 +418,12 @@ public void WritesMatchingIndexerCommandsIntoContainerCommand() writer.Write(method); var result = tw.ToString(); - Assert.Contains("var testItemIndexer = new TestIndexItemRequestBuilder();", result); + Assert.Contains("var testItemIndexer = new Test.Name.Sub.TestIndexItemRequestBuilder();", result); Assert.Contains("var command = testItemIndexer.BuildTestMethod1();", result); Assert.Contains("var cmds = testItemIndexer.BuildTestMethod2();", result); Assert.DoesNotContain("execCommands.AddRange(cmds.Item1);", result); Assert.Contains("nonExecCommands.AddRange(cmds.Item2);", result); - Assert.Contains("var builder = new TestNavItemRequestBuilder", result); + Assert.Contains("var builder = new Test.TestNavItemRequestBuilder", result); Assert.Contains("nonExecCommands.Add(builder.BuildTestMethod11());", result); Assert.Contains("return command;", result); Assert.DoesNotContain("nonExecCommands.Add(builder.BuildTestMethod3());", result); @@ -553,7 +553,7 @@ public void WritesNavCommandThatSkipsReusedNavCommandInstance() var result = tw.ToString(); Assert.Contains("var command = new Command(\"user\");", result); - Assert.Contains("var builder = new TestNavItemRequestBuilder();", result); + Assert.Contains("var builder = new Test.TestNavItemRequestBuilder();", result); Assert.Contains("execCommands.Add(builder.BuildExecutableTestMethod());", result); Assert.Contains("return command;", result); Assert.DoesNotContain("BuildNavTestMethod", result); @@ -596,7 +596,7 @@ public void WritesContainerCommands() var result = tw.ToString(); Assert.Contains("var command = new Command(\"user\");", result); - Assert.Contains("var builder = new TestClass1", result); + Assert.Contains("var builder = new Test.Name.Sub1.Sub2.TestClass1", result); Assert.Contains("nonExecCommands.Add(builder.BuildTestMethod1());", result); Assert.Contains("nonExecCommands.Add(builder.BuildTestMethod2());", result); Assert.Contains("return command;", result); @@ -637,7 +637,7 @@ public void WritesRequestBuilderWithParametersCommands() var result = tw.ToString(); Assert.Contains("var command = new Command(\"user\");", result); - Assert.Contains("var builder = new TestClass1", result); + Assert.Contains("var builder = new Test.Name.Sub1.Sub2.TestClass1", result); Assert.Contains("nonExecCommands.Add(builder.BuildTestMethod1());", result); Assert.Contains("nonExecCommands.Add(builder.BuildTestMethod2());", result); Assert.Contains("return command;", result); @@ -1085,7 +1085,7 @@ public void WritesExecutableCommandForPostRequestWithModelBody() Assert.Contains("command.AddOption(bodyOption);", result); Assert.Contains("var body = invocationContext.ParseResult.GetValueForOption(bodyOption) ?? string.Empty;", result); Assert.Contains("using var stream = new MemoryStream(Encoding.UTF8.GetBytes(body));", result); - Assert.Contains("var model = parseNode.GetObjectValue(Content.CreateFromDiscriminatorValue);", result); + Assert.Contains("var model = parseNode.GetObjectValue(Test.Content.CreateFromDiscriminatorValue);", result); Assert.Contains("if (model is null)", result); Assert.Contains("Console.Error.WriteLine(\"No model data to send.\")", result); Assert.Contains("var requestInfo = CreatePostRequestInformation", result); @@ -1171,7 +1171,7 @@ public void WritesExecutableCommandForPostRequestWithModelBodyAndContentType() Assert.Contains("var body = invocationContext.ParseResult.GetValueForOption(bodyOption) ?? string.Empty;", result); Assert.Contains("var contentType = invocationContext.ParseResult.GetValueForOption(contentTypeOption);", result); Assert.Contains("using var stream = new MemoryStream(Encoding.UTF8.GetBytes(body));", result); - Assert.Contains("var model = parseNode.GetObjectValue(Content.CreateFromDiscriminatorValue);", result); + Assert.Contains("var model = parseNode.GetObjectValue(Test.Content.CreateFromDiscriminatorValue);", result); Assert.Contains("if (model is null)", result); Assert.Contains("Console.Error.WriteLine(\"No model data to send.\")", result); Assert.Contains("var requestInfo = CreatePostRequestInformation(model, contentType", result); @@ -1242,7 +1242,7 @@ public void WritesExecutableCommandForPostRequestWithCollectionModel() Assert.Contains("command.AddOption(bodyOption);", result); Assert.Contains("var body = invocationContext.ParseResult.GetValueForOption(bodyOption) ?? string.Empty;", result); Assert.Contains("using var stream = new MemoryStream(Encoding.UTF8.GetBytes(body));", result); - Assert.Contains("var model = parseNode.GetCollectionOfObjectValues(Content.CreateFromDiscriminatorValue)?.ToList();", result); + Assert.Contains("var model = parseNode.GetCollectionOfObjectValues(Test.Content.CreateFromDiscriminatorValue)?.ToList();", result); Assert.Contains("if (model is null)", result); Assert.Contains("Console.Error.WriteLine(\"No model data to send.\")", result); Assert.Contains("var requestInfo = CreatePostRequestInformation", result); diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs index ae87bd46ba..2fe8182ed3 100644 --- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs @@ -738,7 +738,7 @@ public void WritesModelFactoryBodyAndDisambiguateAmbiguousImportedTypes() Assert.Contains("var mappingValue = parseNode.GetChildNode(\"@odata.type\")?.GetStringValue()", result); Assert.Contains("return mappingValue switch", result); Assert.Contains("\"namespaceLevelOne.ConflictingModel\" => new namespaceLevelOne.ConflictingModel(),", result); //Assert the disambiguation happens due to the enum imported - Assert.Contains("_ => new ConflictingModelBaseClass()", result); + Assert.Contains("_ => new models.ConflictingModelBaseClass()", result); AssertExtensions.CurlyBracesAreClosed(result); } [Fact] @@ -1532,7 +1532,7 @@ public void WritesConstructorWithEnumValue() writer.Write(method); var result = tw.ToString(); Assert.Contains(parentClass.Name.ToFirstCharacterUpperCase(), result); - Assert.Contains($"{propName.ToFirstCharacterUpperCase()} = {codeEnum.Name.ToFirstCharacterUpperCase()}.{defaultValue.CleanupSymbolName()}", result);//ensure symbol is cleaned up + Assert.Contains($"{propName.ToFirstCharacterUpperCase()} = {modelsNamespace.Name}.{codeEnum.Name.ToFirstCharacterUpperCase()}.{defaultValue.CleanupSymbolName()}", result);//ensure symbol is cleaned up } [Fact] public void WritesConstructorAndIncludesSanitizedEnumValue() @@ -1557,7 +1557,7 @@ public void WritesConstructorAndIncludesSanitizedEnumValue() var result = tw.ToString(); Assert.Contains(parentClass.Name.ToFirstCharacterUpperCase(), result); Assert.Contains("PictureSize.Slash", result);//ensure symbol is cleaned up - Assert.Contains($"{propName.ToFirstCharacterUpperCase()} = {codeEnum.Name.ToFirstCharacterUpperCase()}.{defaultValue.CleanupSymbolName()}", result);//ensure symbol is cleaned up + Assert.Contains($"{propName.ToFirstCharacterUpperCase()} = {modelsNamespace.Name}.{codeEnum.Name.ToFirstCharacterUpperCase()}.{defaultValue.CleanupSymbolName()}", result);//ensure symbol is cleaned up } [Fact] public void WritesConstructorAndDisambiguatesEnumType() diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs index a12cd43520..96524a9e7f 100644 --- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs @@ -74,7 +74,7 @@ public void WritesRequestBuilder() writer.Write(property); var result = tw.ToString(); Assert.Contains("get =>", result); - Assert.Contains($"new {TypeName}", result); + Assert.Contains($"new {rootNamespace.Name}.{TypeName}", result); Assert.Contains("RequestAdapter", result); Assert.Contains("PathParameters", result); } @@ -103,7 +103,7 @@ public void MapsCustomPropertiesToBackingStore() property.Kind = CodePropertyKind.Custom; writer.Write(property); var result = tw.ToString(); - Assert.Contains("get { return BackingStore?.Get(\"propertyName\"); }", result); + Assert.Contains("get { return BackingStore?.Get<" + rootNamespace.Name + ".SomeCustomClass>(\"propertyName\"); }", result); Assert.Contains("set { BackingStore?.Set(\"propertyName\", value);", result); } [Fact] @@ -113,7 +113,7 @@ public void MapsAdditionalDataPropertiesToBackingStore() property.Kind = CodePropertyKind.AdditionalData; writer.Write(property); var result = tw.ToString(); - Assert.Contains("get { return BackingStore.Get(\"propertyName\") ?? new Dictionary(); }", result); + Assert.Contains("get { return BackingStore.Get<" + rootNamespace.Name + ".SomeCustomClass>(\"propertyName\") ?? new Dictionary(); }", result); Assert.Contains("set { BackingStore.Set(\"propertyName\", value);", result); } [Fact] @@ -207,7 +207,7 @@ public void DisambiguateAmbiguousImportedTypes() // Assert: properties types are disambiguated. Assert.Contains("namespaceLevelOne.SomeCustomClass", result); - Assert.Contains("defaultNamespace.SomeCustomClass", result); + Assert.Contains($"{rootNamespace.Name}.SomeCustomClass", result); } [Fact] public void WritesDeprecationInformation() diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/TypeDefinitionExtensionsTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/TypeDefinitionExtensionsTests.cs new file mode 100644 index 0000000000..41c397b34f --- /dev/null +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/TypeDefinitionExtensionsTests.cs @@ -0,0 +1,140 @@ +using System; +using Kiota.Builder.CodeDOM; +using Kiota.Builder.Writers.CSharp; + +using Xunit; + +namespace Kiota.Builder.Tests.Writers.CSharp; + +public sealed class TypeDefinitionExtensionsTests +{ + [Fact] + public void ReturnsFullNameForTypeWithoutNamespace() + { + var rootNamespace = CodeNamespace.InitRootNamespace(); + var myClass = new CodeClass + { + Name = "myClass" + }; + rootNamespace.AddClass(myClass); + + var fullName = TypeDefinitionExtensions.GetFullName(myClass); + + Assert.Equal("MyClass", fullName); + } + + [Fact] + public void ReturnsFullNameForTypeInNamespace() + { + var rootNamespace = CodeNamespace.InitRootNamespace(); + + var myNamespace = rootNamespace.AddNamespace("MyNamespace"); + var myClass = new CodeClass + { + Name = "myClass", + }; + myNamespace.AddClass(myClass); + + var fullName = TypeDefinitionExtensions.GetFullName(myClass); + + Assert.Equal("MyNamespace.MyClass", fullName); + } + + [Fact] + public void ReturnsFullNameForNestedTypes() + { + var rootNamespace = CodeNamespace.InitRootNamespace(); + + var myNamespace = rootNamespace.AddNamespace("MyNamespace"); + + var myParentClass = new CodeClass + { + Name = "myParentClass" + }; + myNamespace.AddClass(myParentClass); + + var myNestedClass = new CodeClass + { + Name = "myNestedClass", + }; + myParentClass.AddInnerClass(myNestedClass); + + var parentClassFullName = TypeDefinitionExtensions.GetFullName(myParentClass); + var nestedClassFullName = TypeDefinitionExtensions.GetFullName(myNestedClass); + + Assert.Equal("MyNamespace.MyParentClass", parentClassFullName); + Assert.Equal("MyNamespace.MyParentClass.MyNestedClass", nestedClassFullName); + } + + [Fact] + public void ThrowsIfTypeIsNull() + { + Assert.Throws("typeDefinition", () => TypeDefinitionExtensions.GetFullName(null!)); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public void ThrowsIfTypeDoesNotHaveAName(string typeName) + { + var myClass = new CodeClass + { + Name = typeName + }; + + Assert.Throws("typeDefinition", () => TypeDefinitionExtensions.GetFullName(myClass)); + } + + [Fact] + public void ThrowsIfTypesParentIsInvalid() + { + var myClass = new CodeClass + { + Name = "myClass", + Parent = new CodeConstant() + }; + + Assert.Throws(() => TypeDefinitionExtensions.GetFullName(myClass)); + } + + [Fact] + public void CapitalizesTypeNamesInTypeHierarchyButNotTheNamespace() + { + var rootNamespace = CodeNamespace.InitRootNamespace(); + var myNamespace = rootNamespace.AddNamespace("myNamespace"); + + var myParentClass = new CodeClass + { + Name = "myParentClass" + }; + myNamespace.AddClass(myParentClass); + + var myNestedClass = new CodeClass + { + Name = "myNestedClass", + }; + myParentClass.AddInnerClass(myNestedClass); + + var nestedClassFullName = TypeDefinitionExtensions.GetFullName(myNestedClass); + + Assert.Equal("myNamespace.MyParentClass.MyNestedClass", nestedClassFullName); + } + + [Fact] + public void DoesNotAppendNamespaceSegmentIfNamespaceNameIsEmpty() + { + var rootNamespace = CodeNamespace.InitRootNamespace(); + var myNamespace = rootNamespace.AddNamespace("ThisWillBeEmpty"); + myNamespace.Name = ""; + + var myClass = new CodeClass + { + Name = "myClass" + }; + myNamespace.AddClass(myClass); + + var fullName = TypeDefinitionExtensions.GetFullName(myClass); + + Assert.Equal("MyClass", fullName); + } +} diff --git a/vscode/microsoft-kiota/package-lock.json b/vscode/microsoft-kiota/package-lock.json index 4c3d02d17b..d452230274 100644 --- a/vscode/microsoft-kiota/package-lock.json +++ b/vscode/microsoft-kiota/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@vscode/extension-telemetry": "^0.9.6", "@vscode/l10n": "^0.0.18", - "adm-zip": "^0.5.12", + "adm-zip": "^0.5.13", "is-online": "^10.0.0", "original-fs": "^1.2.0", "vscode-jsonrpc": "^8.2.1" @@ -483,9 +483,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.13.tgz", - "integrity": "sha512-gBGeanV41c1L171rR7wjbMiEpEI/l5XFQdLLfhr/REwpgDy/4U8y89+i8kRiLzDyZdOkXh+cRaTetUnCYutoXA==", + "version": "20.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.1.tgz", + "integrity": "sha512-T2MzSGEu+ysB/FkWfqmhV3PLyQlowdptmmgD20C6QxsS8Fmv5SjpZ1ayXaEC0S21/h5UJ9iA6W/5vSNU5l00OA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -990,11 +990,11 @@ } }, "node_modules/adm-zip": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.12.tgz", - "integrity": "sha512-6TVU49mK6KZb4qG6xWaaM4C7sA/sgUMLy/JYMOzkcp3BvVLpW0fXDFQiIzAuxFCt/2+xD7fNIiPFAoLZPhVNLQ==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.13.tgz", + "integrity": "sha512-4U51tTl9J8UVEcuKGr6zRzY95tWoAa9l+ureGBNmsfleszjZblm5NyEEL/ZQxkhi86co5mZhSvL2T7gkZ6feYQ==", "engines": { - "node": ">=6.0" + "node": ">=12.0" } }, "node_modules/agent-base": { diff --git a/vscode/microsoft-kiota/package.json b/vscode/microsoft-kiota/package.json index 76cd86b8ac..a3a236448b 100644 --- a/vscode/microsoft-kiota/package.json +++ b/vscode/microsoft-kiota/package.json @@ -456,7 +456,7 @@ "dependencies": { "@vscode/extension-telemetry": "^0.9.6", "@vscode/l10n": "^0.0.18", - "adm-zip": "^0.5.12", + "adm-zip": "^0.5.13", "is-online": "^10.0.0", "original-fs": "^1.2.0", "vscode-jsonrpc": "^8.2.1"