diff --git a/CHANGELOG.md b/CHANGELOG.md index 853f45f7e4..5d3939d0db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Fixed cyclic depencies in generated Go code. [#2834](https://github.com/microsoft/kiota/issues/2834) + ## [1.19.0] - 2024-10-03 ### Added @@ -38,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed registration of default serialization and deserialization classes in client constructor. [#5478](https://github.com/microsoft/kiota/pull/5478) - Fixed incorrect type name generation in aliased scenario in TS due to broad searching of types in root namespaces. [#5404](https://github.com/microsoft/kiota/issues/5404) - Fixed incorrect type mapping in request builders with subsequent paths with the same name. [#5462](https://github.com/microsoft/kiota/issues/5462) -- Fixed cyclic depencies in generated Go code. [#2834](https://github.com/microsoft/kiota/issues/2834) +- Fixed multipart generation to default Content-Types are defined for multipart [#5504](https://github.com/microsoft/kiota/issues/5504) ## [1.18.0] - 2024-09-05 diff --git a/it/java/pom.xml b/it/java/pom.xml index b0a9bf1f90..c4c04176ed 100644 --- a/it/java/pom.xml +++ b/it/java/pom.xml @@ -15,7 +15,7 @@ UTF-8 UTF-8 - 1.5.0 + 1.5.1 diff --git a/it/python/requirements-dev.txt b/it/python/requirements-dev.txt index 95548b7ce0..cb9821a583 100644 --- a/it/python/requirements-dev.txt +++ b/it/python/requirements-dev.txt @@ -104,7 +104,7 @@ microsoft-kiota-authentication-azure==1.1.0 microsoft-kiota-http==1.3.3 -microsoft-kiota-serialization-json==1.3.2 +microsoft-kiota-serialization-json==1.3.3 microsoft-kiota-serialization-text==1.0.0 diff --git a/it/typescript/package-lock.json b/it/typescript/package-lock.json index f9dd728763..a3d41e8b5d 100644 --- a/it/typescript/package-lock.json +++ b/it/typescript/package-lock.json @@ -10,13 +10,13 @@ "license": "MIT", "dependencies": { "@azure/identity": "^4.4.1", - "@microsoft/kiota-abstractions": "^1.0.0-preview.66", - "@microsoft/kiota-authentication-azure": "^1.0.0-preview.61", - "@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.65", - "@microsoft/kiota-serialization-form": "^1.0.0-preview.54", - "@microsoft/kiota-serialization-json": "^1.0.0-preview.66", - "@microsoft/kiota-serialization-multipart": "^1.0.0-preview.44", - "@microsoft/kiota-serialization-text": "^1.0.0-preview.63", + "@microsoft/kiota-abstractions": "^1.0.0-preview.68", + "@microsoft/kiota-authentication-azure": "^1.0.0-preview.63", + "@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.67", + "@microsoft/kiota-serialization-form": "^1.0.0-preview.56", + "@microsoft/kiota-serialization-json": "^1.0.0-preview.68", + "@microsoft/kiota-serialization-multipart": "^1.0.0-preview.46", + "@microsoft/kiota-serialization-text": "^1.0.0-preview.65", "express": "^5.0.0", "node-fetch": "^2.7.0" }, @@ -772,9 +772,9 @@ } }, "node_modules/@microsoft/kiota-abstractions": { - "version": "1.0.0-preview.66", - "resolved": "https://registry.npmjs.org/@microsoft/kiota-abstractions/-/kiota-abstractions-1.0.0-preview.66.tgz", - "integrity": "sha512-mP7P+aHVLhT5A0A1nhpPQvghwNtMO9+LOV6RKYHzhzmKAvycM2rOMCpxsivHn+vtOArTiAqdfyePCpUhzziRjg==", + "version": "1.0.0-preview.68", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-abstractions/-/kiota-abstractions-1.0.0-preview.68.tgz", + "integrity": "sha512-RDKMEqEUDr1GUhgN52Q31+qCWRU5VKcr+TeWSCTbQs3fMFIQIP5l68+OfHEwsa75f7hBuOZ0RLxEuS3hpJgmzQ==", "dependencies": { "@opentelemetry/api": "^1.7.0", "@std-uritemplate/std-uritemplate": "^1.0.1", @@ -797,9 +797,9 @@ } }, "node_modules/@microsoft/kiota-authentication-azure": { - "version": "1.0.0-preview.61", - "resolved": "https://registry.npmjs.org/@microsoft/kiota-authentication-azure/-/kiota-authentication-azure-1.0.0-preview.61.tgz", - "integrity": "sha512-Vsx6hmkMLOdrw0IHYpkTohymUjdcypTjurkEh9BZkZz9OceKgx+3pt8DmtAxc7zvCa+H23P68KBGRVZaT32YGw==", + "version": "1.0.0-preview.63", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-authentication-azure/-/kiota-authentication-azure-1.0.0-preview.63.tgz", + "integrity": "sha512-CzBtENeYnb6wu9XKcTAFTYxkm89FlS5uo7lGZpG3ddEz1Ac8stvZgSatdMeB9zzAhbf7SvBlB0Je94NGlrrGVA==", "dependencies": { "@azure/core-auth": "^1.5.0", "@microsoft/kiota-abstractions": "*", @@ -808,9 +808,9 @@ } }, "node_modules/@microsoft/kiota-http-fetchlibrary": { - "version": "1.0.0-preview.65", - "resolved": "https://registry.npmjs.org/@microsoft/kiota-http-fetchlibrary/-/kiota-http-fetchlibrary-1.0.0-preview.65.tgz", - "integrity": "sha512-b6HsVhojYjABhkspvdWK56I2FZy+6pq8nmmBXDUuSbI22/Ep6T8xExjw1r0lzEEZVw08yihexWGZIofy1dvhcQ==", + "version": "1.0.0-preview.67", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-http-fetchlibrary/-/kiota-http-fetchlibrary-1.0.0-preview.67.tgz", + "integrity": "sha512-nEddpr+iLw8Eglmd+v7YMFLRZin2paSB+/BiQK5JbDIFnN9FYwtM6gdRnqacGK0TxNm0m0KHK0AqK9kwxZwDEw==", "dependencies": { "@microsoft/kiota-abstractions": "*", "@opentelemetry/api": "^1.7.0", @@ -819,9 +819,9 @@ } }, "node_modules/@microsoft/kiota-serialization-form": { - "version": "1.0.0-preview.54", - "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-form/-/kiota-serialization-form-1.0.0-preview.54.tgz", - "integrity": "sha512-Yd8GOae8zq3AK2koMK3RjLDzMfVRf+bx2je/MMZlUOrfKXF1rnaCWcXiyJJRlreawDI9WSeuD0mY/+pUTxSOsw==", + "version": "1.0.0-preview.56", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-form/-/kiota-serialization-form-1.0.0-preview.56.tgz", + "integrity": "sha512-n4wPs8LDpgo4w2zC40RSmNIvuibcRCY/PfjR9gogQA7gQmPKDYnyk5JvfOn0yr2f0V8PKX76yQicXO1SHkG7BA==", "dependencies": { "@microsoft/kiota-abstractions": "*", "guid-typescript": "^1.0.9", @@ -829,9 +829,9 @@ } }, "node_modules/@microsoft/kiota-serialization-json": { - "version": "1.0.0-preview.66", - "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-json/-/kiota-serialization-json-1.0.0-preview.66.tgz", - "integrity": "sha512-H9ja9wu+e68jlzqQ548Dcu5U18dh3HAEnPcrypG+YDzgpXqXJwujZa7fOzKbR3mNRD3coUT7H9PMQfaNGZus+g==", + "version": "1.0.0-preview.68", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-json/-/kiota-serialization-json-1.0.0-preview.68.tgz", + "integrity": "sha512-5mOjnrmEGMWyPrYLW6eEOXegOip8AiqlH3/zD+HGZhdNM/RrVO/xJLsSk5RaVxXK8ViJk6BG58Esx2grUF3bxQ==", "dependencies": { "@microsoft/kiota-abstractions": "*", "guid-typescript": "^1.0.9", @@ -839,9 +839,9 @@ } }, "node_modules/@microsoft/kiota-serialization-multipart": { - "version": "1.0.0-preview.44", - "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-multipart/-/kiota-serialization-multipart-1.0.0-preview.44.tgz", - "integrity": "sha512-+g/1euy/iZXxQPlmN6zX4rkas47BShFI7bK47jLIdt6VFtZU6deWjiplnTpfR2dGg3ipQAD9RBdU4mHVoqx1Ow==", + "version": "1.0.0-preview.46", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-multipart/-/kiota-serialization-multipart-1.0.0-preview.46.tgz", + "integrity": "sha512-ZFq6wiB5jBfilyuZjmm/xHFUQJP0Q1OG/qW9BaHWlOoh546JkVHqpchHJXKy9fhEhFZeHsIxz6ZqaF/deruxDw==", "dependencies": { "@microsoft/kiota-abstractions": "*", "guid-typescript": "^1.0.9", @@ -849,9 +849,9 @@ } }, "node_modules/@microsoft/kiota-serialization-text": { - "version": "1.0.0-preview.63", - "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-text/-/kiota-serialization-text-1.0.0-preview.63.tgz", - "integrity": "sha512-tfbsG7EYukBLECdcd2UVJglOVhjjjmqUtxosZ/B/oiYAKCyooPwdOLDM5WmM4Vcgm6rMaCOaEluPKQMjfhD3xg==", + "version": "1.0.0-preview.65", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-text/-/kiota-serialization-text-1.0.0-preview.65.tgz", + "integrity": "sha512-N3msWVFk5t2WrVOgmzUwo9bK/HEtSOskw+p0Fi1OAlhz1HSAJEbSHKfedIt62j+7Xbd1PEpbzOQZgJfFc6+hug==", "dependencies": { "@microsoft/kiota-abstractions": "*", "guid-typescript": "^1.0.9", diff --git a/it/typescript/package.json b/it/typescript/package.json index d27d9f41b8..c9e3111a0b 100644 --- a/it/typescript/package.json +++ b/it/typescript/package.json @@ -32,13 +32,13 @@ }, "dependencies": { "@azure/identity": "^4.4.1", - "@microsoft/kiota-abstractions": "^1.0.0-preview.66", - "@microsoft/kiota-authentication-azure": "^1.0.0-preview.61", - "@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.65", - "@microsoft/kiota-serialization-form": "^1.0.0-preview.54", - "@microsoft/kiota-serialization-json": "^1.0.0-preview.66", - "@microsoft/kiota-serialization-multipart": "^1.0.0-preview.44", - "@microsoft/kiota-serialization-text": "^1.0.0-preview.63", + "@microsoft/kiota-abstractions": "^1.0.0-preview.68", + "@microsoft/kiota-authentication-azure": "^1.0.0-preview.63", + "@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.67", + "@microsoft/kiota-serialization-form": "^1.0.0-preview.56", + "@microsoft/kiota-serialization-json": "^1.0.0-preview.68", + "@microsoft/kiota-serialization-multipart": "^1.0.0-preview.46", + "@microsoft/kiota-serialization-text": "^1.0.0-preview.65", "express": "^5.0.0", "node-fetch": "^2.7.0" } diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index d575562412..8a1a4e69f2 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -1490,6 +1490,23 @@ private static void AddRequestConfigurationProperties(CodeClass? parameterClass, } private readonly ConcurrentDictionary multipartPropertiesModels = new(); + + private static bool IsSupportedMultipartDefault(OpenApiSchema openApiSchema, + StructuredMimeTypesCollection structuredMimeTypes) + { + // https://spec.openapis.org/oas/v3.0.3.html#special-considerations-for-multipart-content + if (openApiSchema.IsObjectType() && structuredMimeTypes.Contains("application/json")) + return true; + + if (GetPrimitiveType(openApiSchema) is { IsExternal: true } primitiveType && // it s a primitive + (primitiveType.Name.Equals("binary", StringComparison.OrdinalIgnoreCase) + || primitiveType.Name.Equals("base64", StringComparison.OrdinalIgnoreCase) // streams are handled irrespective of configs + || structuredMimeTypes.Contains("text/plain"))) // other primitives need text/plain + return true; + + return false; + } + private void AddRequestBuilderMethodParameters(OpenApiUrlTreeNode currentNode, OperationType operationType, OpenApiOperation operation, CodeClass requestConfigClass, CodeMethod method) { if (operation.GetRequestSchema(config.StructuredMimeTypes) is OpenApiSchema requestBodySchema) @@ -1513,6 +1530,18 @@ private void AddRequestBuilderMethodParameters(OpenApiUrlTreeNode currentNode, O multipartPropertiesModels.TryAdd(propertyType.TypeDefinition, true); } } + else if (requestBodySchema.Properties.Values.Any(schema => IsSupportedMultipartDefault(schema, config.StructuredMimeTypes)) + && operation.RequestBody.Content.Count == 1)// it's the only content type. + { + requestBodyType = new CodeType { Name = "MultipartBody", IsExternal = true, }; + foreach (var property in requestBodySchema.Properties.Values.Where(schema => IsSupportedMultipartDefault(schema, config.StructuredMimeTypes))) + { + if (CreateModelDeclarations(currentNode, property, + operation, method, $"{operationType}RequestBody", + isRequestBody: true) is CodeType { TypeDefinition: not null } propertyType) + multipartPropertiesModels.TryAdd(propertyType.TypeDefinition, true); + } + } else { requestBodyType = CreateModelDeclarations(currentNode, requestBodySchema, operation, method, diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index 6b17ad738e..7d7382168b 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -7706,7 +7706,66 @@ public async Task SupportsMultiPartFormAsRequestBodyWithoutEncodingWithDefaultMi 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); + Assert.Equal("MultipartBody", bodyParameter.Type.Name, StringComparer.OrdinalIgnoreCase); + var addressClass = codeModel.FindChildByName("Address"); + Assert.NotNull(addressClass); + } + [Fact] + public async Task SupportsMultiPartFormAsRequestBodyWithoutEncodingWithDefaultMimeTypesAsyncWithNonDefaultMimeTypesAsync() + { + var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await using var fs = await GetDocumentStreamAsync(@"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, StructuredMimeTypes = new StructuredMimeTypesCollection { "multipart/form-data;q=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); //generate the model type as we do not have the serializer for the schema registered. var addressClass = codeModel.FindChildByName("Address"); Assert.NotNull(addressClass); } diff --git a/vscode/microsoft-kiota/package-lock.json b/vscode/microsoft-kiota/package-lock.json index 6332e8af7c..0a887eb428 100644 --- a/vscode/microsoft-kiota/package-lock.json +++ b/vscode/microsoft-kiota/package-lock.json @@ -21,7 +21,7 @@ "@types/adm-zip": "^0.5.5", "@types/mocha": "^10.0.8", "@types/node": "22.x", - "@types/vscode": "^1.93.0", + "@types/vscode": "^1.94.0", "@typescript-eslint/eslint-plugin": "^8.8.0", "@typescript-eslint/parser": "^8.8.0", "@vscode/test-electron": "^2.4.1", @@ -540,9 +540,9 @@ } }, "node_modules/@types/vscode": { - "version": "1.93.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.93.0.tgz", - "integrity": "sha512-kUK6jAHSR5zY8ps42xuW89NLcBpw1kOabah7yv38J8MyiYuOHxLQBi0e7zeXbQgVefDy/mZZetqEFC+Fl5eIEQ==", + "version": "1.94.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.94.0.tgz", + "integrity": "sha512-UyQOIUT0pb14XSqJskYnRwD2aG0QrPVefIfrW1djR+/J4KeFQ0i1+hjZoaAmeNf3Z2jleK+R2hv+EboG/m8ruw==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { diff --git a/vscode/microsoft-kiota/package.json b/vscode/microsoft-kiota/package.json index 9c6283c730..abf57d64dc 100644 --- a/vscode/microsoft-kiota/package.json +++ b/vscode/microsoft-kiota/package.json @@ -8,7 +8,7 @@ "telemetryInstrumentationKey": "4c6357e0-daf9-42b5-bdfb-67878f8957b5", "icon": "images/logo.png", "engines": { - "vscode": "^1.93.0" + "vscode": "^1.94.0" }, "license": "MIT", "categories": [ @@ -470,7 +470,7 @@ "@types/adm-zip": "^0.5.5", "@types/mocha": "^10.0.8", "@types/node": "22.x", - "@types/vscode": "^1.93.0", + "@types/vscode": "^1.94.0", "@typescript-eslint/eslint-plugin": "^8.8.0", "@typescript-eslint/parser": "^8.8.0", "@vscode/test-electron": "^2.4.1",