diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d5b2b6b61..98616c366a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added support for multipart form data request body in PHP. [#3029](https://github.com/microsoft/kiota/issues/3029) +- Added uri-form encoded serialization for PHP. [#2074](https://github.com/microsoft/kiota/issues/2074) + ### Changed - Aligns naming of sliced OpenAPI description generated by `plugin add` should be named `-openapi.json|yml` diff --git a/README.md b/README.md index d83424d85a..b08fc62b10 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,17 @@ This library builds on top of the [Microsoft.OpenAPI.NET](https://github.com/mic The following table provides an overview of the languages supported by Kiota and the progress in the implementation of the different components. -| Language | Generation | Abstractions | Serialization | Authentication | HTTP | Required tools & dependencies | -| -------- | ---------- |--------------------------------|-----------------------------------------------------------------| -------------- | ---- | -------------- | -| CSharp | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-dotnet) | [FORM](https://github.com/microsoft/kiota-serialization-form-dotnet), [JSON](https://github.com/microsoft/kiota-serialization-json-dotnet), [MULTIPART](https://github.com/microsoft/kiota-serialization-multipart-dotnet), [TEXT](https://github.com/microsoft/kiota-serialization-text-dotnet) | [Anonymous](https://github.com/microsoft/kiota-abstractions-dotnet/blob/main/src/authentication/AnonymousAuthenticationProvider.cs), [API Key](https://github.com/microsoft/kiota-abstractions-dotnet/blob/main/src/authentication/ApiKeyAuthenticationProvider.cs), [Azure](https://github.com/microsoft/kiota-authentication-azure-dotnet) | [✔](https://github.com/microsoft/kiota-http-dotnet) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/dotnet) | -| Go | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-go) | [FORM](https://github.com/microsoft/kiota-serialization-form-go), [JSON](https://github.com/microsoft/kiota-serialization-json-go), [MULTIPART](https://github.com/microsoft/kiota-serialization-multipart-go), [TEXT](https://github.com/microsoft/kiota-serialization-text-go) | [Anonymous](https://github.com/microsoft/kiota-abstractions-go/blob/main/authentication/anonymous_authentication_provider.go), [API Key](https://github.com/microsoft/kiota-abstractions-go/blob/main/authentication/api_key_authentication_provider.go), [Azure](https://github.com/microsoft/kiota-authentication-azure-go/) | [✔](https://github.com/microsoft/kiota-http-go/) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/go) | -| Java | ✔ | [✔](https://github.com/microsoft/kiota-java/tree/main/components/abstractions) | [FORM](https://github.com/microsoft/kiota-java/tree/main/components/serialization/form), [JSON](https://github.com/microsoft/kiota-java/tree/main/components/serialization/json), [MULTIPART](https://github.com/microsoft/kiota-java/tree/main/components/serialization/multipart), [TEXT](https://github.com/microsoft/kiota-java/tree/main/components/serialization/text) | [Anonymous](https://github.com/microsoft/kiota-java/blob/main/components/abstractions/src/main/java/com/microsoft/kiota/authentication/AnonymousAuthenticationProvider.java), [API Key](https://github.com/microsoft/kiota-java/blob/main/components/abstractions/src/main/java/com/microsoft/kiota/authentication/ApiKeyAuthenticationProvider.java), [Azure](https://github.com/microsoft/kiota-java/tree/main/components/authentication/azure) | [✔](https://github.com/microsoft/kiota-java/tree/main/components/http/okHttp) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/java) | -| PHP | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-php) | [JSON](https://github.com/microsoft/kiota-serialization-json-php), [❌ FORM](https://github.com/microsoft/kiota/issues/2074), [❌ MULTIPART](https://github.com/microsoft/kiota/issues/3029), [TEXT](https://github.com/microsoft/kiota-serialization-text-php) | [Anonymous](https://github.com/microsoft/kiota-abstractions-php/blob/main/src/Authentication/AnonymousAuthenticationProvider.php), [✔️ PHP League](https://github.com/microsoft/kiota-authentication-phpleague-php) | [✔](https://github.com/microsoft/kiota-http-guzzle-php) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/php) | -| Python | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-python) | [FORM](https://github.com/microsoft/kiota-serialization-form-python), [JSON](https://github.com/microsoft/kiota-serialization-json-python), [MULTIPART](https://github.com/microsoft/kiota-serialization-multipart-python), [TEXT](https://github.com/microsoft/kiota-serialization-text-python) | [Anonymous](https://github.com/microsoft/kiota-abstractions-python/blob/main/kiota_abstractions/authentication/anonymous_authentication_provider.py), [Azure](https://github.com/microsoft/kiota-authentication-azure-python) | [✔](https://github.com/microsoft/kiota-http-python) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/python) | -| Ruby | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-ruby) | [❌ FORM](https://github.com/microsoft/kiota/issues/2077), [JSON](https://github.com/microsoft/kiota-serialization-json-ruby), [❌ MULTIPART](https://github.com/microsoft/kiota/issues/3032), [❌ TEXT](https://github.com/microsoft/kiota/issues/1049) | [Anonymous](https://github.com/microsoft/kiota-abstractions-ruby/blob/main/lib/microsoft_kiota_abstractions/authentication/anonymous_authentication_provider.rb), [✔️ OAuth2](https://github.com/microsoft/kiota-authentication-oauth-ruby) | [✔](https://github.com/microsoft/kiota-http-ruby)| | -| CLI | ✔ | (see CSharp) + [✔](https://github.com/microsoft/kiota-cli-commons) | (see CSharp) | (see CSharp) | (see CSharp) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/cli) | -| Swift | [▶](https://github.com/microsoft/kiota/issues/1449) | [✔](./abstractions/swift) | [❌ FORM](https://github.com/microsoft/kiota/issues/2076), [❌ JSON](https://github.com/microsoft/kiota/issues/1451), [❌ FORM](https://github.com/microsoft/kiota/issues/3033), [❌ TEXT](https://github.com/microsoft/kiota/issues/1452) | [Anonymous](./abstractions/swift/Source/MicrosoftKiotaAbstractions/Authentication/AnonymousAuthenticationProvider.swift), [❌ Azure](https://github.com/microsoft/kiota/issues/1453) | [❌](https://github.com/microsoft/kiota/issues/1454)| | -| TypeScript/JavaScript | ✔ | [✔](https://github.com/microsoft/kiota-typescript/tree/main/packages/abstractions) | [FORM](https://github.com/microsoft/kiota-typescript/tree/main/packages/serialization/form), [JSON](https://github.com/microsoft/kiota-typescript/tree/main/packages/serialization/json), [MULTIPART](https://github.com/microsoft/kiota-typescript/tree/main/packages/serialization/multipart), [TEXT](https://github.com/microsoft/kiota-typescript/tree/main/packages/serialization/text) | [Anonymous](https://github.com/microsoft/kiota-typescript/blob/main/packages/abstractions/src/authentication/anonymousAuthenticationProvider.ts), [API Key](https://github.com/microsoft/kiota-typescript/blob/main/packages/abstractions/src/authentication/apiKeyAuthenticationProvider.ts), [Azure](https://github.com/microsoft/kiota-typescript/tree/main/packages/authentication/azure), [SPFx](https://github.com/microsoft/kiota-typescript/tree/main/packages/authentication/spfx) | [✔](https://github.com/microsoft/kiota-typescript/tree/main/packages/http/fetch) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/typescript) | +| Language | Generation | Abstractions | Serialization | Authentication | HTTP | Required tools & dependencies | +| -------- | ---------- |--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -------------- | ---- | -------------- | +| CSharp | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-dotnet) | [FORM](https://github.com/microsoft/kiota-serialization-form-dotnet), [JSON](https://github.com/microsoft/kiota-serialization-json-dotnet), [MULTIPART](https://github.com/microsoft/kiota-serialization-multipart-dotnet), [TEXT](https://github.com/microsoft/kiota-serialization-text-dotnet) | [Anonymous](https://github.com/microsoft/kiota-abstractions-dotnet/blob/main/src/authentication/AnonymousAuthenticationProvider.cs), [API Key](https://github.com/microsoft/kiota-abstractions-dotnet/blob/main/src/authentication/ApiKeyAuthenticationProvider.cs), [Azure](https://github.com/microsoft/kiota-authentication-azure-dotnet) | [✔](https://github.com/microsoft/kiota-http-dotnet) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/dotnet) | +| Go | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-go) | [FORM](https://github.com/microsoft/kiota-serialization-form-go), [JSON](https://github.com/microsoft/kiota-serialization-json-go), [MULTIPART](https://github.com/microsoft/kiota-serialization-multipart-go), [TEXT](https://github.com/microsoft/kiota-serialization-text-go) | [Anonymous](https://github.com/microsoft/kiota-abstractions-go/blob/main/authentication/anonymous_authentication_provider.go), [API Key](https://github.com/microsoft/kiota-abstractions-go/blob/main/authentication/api_key_authentication_provider.go), [Azure](https://github.com/microsoft/kiota-authentication-azure-go/) | [✔](https://github.com/microsoft/kiota-http-go/) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/go) | +| Java | ✔ | [✔](https://github.com/microsoft/kiota-java/tree/main/components/abstractions) | [FORM](https://github.com/microsoft/kiota-java/tree/main/components/serialization/form), [JSON](https://github.com/microsoft/kiota-java/tree/main/components/serialization/json), [MULTIPART](https://github.com/microsoft/kiota-java/tree/main/components/serialization/multipart), [TEXT](https://github.com/microsoft/kiota-java/tree/main/components/serialization/text) | [Anonymous](https://github.com/microsoft/kiota-java/blob/main/components/abstractions/src/main/java/com/microsoft/kiota/authentication/AnonymousAuthenticationProvider.java), [API Key](https://github.com/microsoft/kiota-java/blob/main/components/abstractions/src/main/java/com/microsoft/kiota/authentication/ApiKeyAuthenticationProvider.java), [Azure](https://github.com/microsoft/kiota-java/tree/main/components/authentication/azure) | [✔](https://github.com/microsoft/kiota-java/tree/main/components/http/okHttp) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/java) | +| PHP | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-php) | [JSON](https://github.com/microsoft/kiota-serialization-json-php), [FORM](https://github.com/microsoft/kiota-serialization-form-php), [MULTIPART](https://github.com/microsoft/kiota-serialization-multipart-php), [TEXT](https://github.com/microsoft/kiota-serialization-text-php) | [Anonymous](https://github.com/microsoft/kiota-abstractions-php/blob/main/src/Authentication/AnonymousAuthenticationProvider.php), [✔️ PHP League](https://github.com/microsoft/kiota-authentication-phpleague-php) | [✔](https://github.com/microsoft/kiota-http-guzzle-php) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/php) | +| Python | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-python) | [FORM](https://github.com/microsoft/kiota-serialization-form-python), [JSON](https://github.com/microsoft/kiota-serialization-json-python), [MULTIPART](https://github.com/microsoft/kiota-serialization-multipart-python), [TEXT](https://github.com/microsoft/kiota-serialization-text-python) | [Anonymous](https://github.com/microsoft/kiota-abstractions-python/blob/main/kiota_abstractions/authentication/anonymous_authentication_provider.py), [Azure](https://github.com/microsoft/kiota-authentication-azure-python) | [✔](https://github.com/microsoft/kiota-http-python) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/python) | +| Ruby | ✔ | [✔](https://github.com/microsoft/kiota-abstractions-ruby) | [❌ FORM](https://github.com/microsoft/kiota/issues/2077), [JSON](https://github.com/microsoft/kiota-serialization-json-ruby), [❌ MULTIPART](https://github.com/microsoft/kiota/issues/3032), [❌ TEXT](https://github.com/microsoft/kiota/issues/1049) | [Anonymous](https://github.com/microsoft/kiota-abstractions-ruby/blob/main/lib/microsoft_kiota_abstractions/authentication/anonymous_authentication_provider.rb), [✔️ OAuth2](https://github.com/microsoft/kiota-authentication-oauth-ruby) | [✔](https://github.com/microsoft/kiota-http-ruby)| | +| CLI | ✔ | (see CSharp) + [✔](https://github.com/microsoft/kiota-cli-commons) | (see CSharp) | (see CSharp) | (see CSharp) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/cli) | +| Swift | [▶](https://github.com/microsoft/kiota/issues/1449) | [✔](./abstractions/swift) | [❌ FORM](https://github.com/microsoft/kiota/issues/2076), [❌ JSON](https://github.com/microsoft/kiota/issues/1451), [❌ FORM](https://github.com/microsoft/kiota/issues/3033), [❌ TEXT](https://github.com/microsoft/kiota/issues/1452) | [Anonymous](./abstractions/swift/Source/MicrosoftKiotaAbstractions/Authentication/AnonymousAuthenticationProvider.swift), [❌ Azure](https://github.com/microsoft/kiota/issues/1453) | [❌](https://github.com/microsoft/kiota/issues/1454)| | +| TypeScript/JavaScript | ✔ | [✔](https://github.com/microsoft/kiota-typescript/tree/main/packages/abstractions) | [FORM](https://github.com/microsoft/kiota-typescript/tree/main/packages/serialization/form), [JSON](https://github.com/microsoft/kiota-typescript/tree/main/packages/serialization/json), [MULTIPART](https://github.com/microsoft/kiota-typescript/tree/main/packages/serialization/multipart), [TEXT](https://github.com/microsoft/kiota-typescript/tree/main/packages/serialization/text) | [Anonymous](https://github.com/microsoft/kiota-typescript/blob/main/packages/abstractions/src/authentication/anonymousAuthenticationProvider.ts), [API Key](https://github.com/microsoft/kiota-typescript/blob/main/packages/abstractions/src/authentication/apiKeyAuthenticationProvider.ts), [Azure](https://github.com/microsoft/kiota-typescript/tree/main/packages/authentication/azure), [SPFx](https://github.com/microsoft/kiota-typescript/tree/main/packages/authentication/spfx) | [✔](https://github.com/microsoft/kiota-typescript/tree/main/packages/http/fetch) | [link](https://learn.microsoft.com/openapi/kiota/quickstarts/typescript) | > Legend: ✔ -> in preview, ❌ -> not started, ▶ -> in progress. diff --git a/it/config.json b/it/config.json index bef7691eac..13468a0cde 100644 --- a/it/config.json +++ b/it/config.json @@ -164,10 +164,6 @@ "Language": "ruby", "Rationale": "https://github.com/microsoft/kiota/issues/2484" }, - { - "Language": "php", - "Rationale": "https://github.com/microsoft/kiota/issues/3029" - }, { "Language": "python", "Rationale": "https://github.com/microsoft/kiota/issues/2957" @@ -258,10 +254,6 @@ "Language": "go", "Rationale": "https://github.com/microsoft/kiota/issues/2834" }, - { - "Language": "php", - "Rationale": "https://github.com/microsoft/kiota/issues/3029" - }, { "Language": "java", "Rationale": "https://github.com/microsoft/kiota/issues/2842" @@ -315,10 +307,6 @@ { "Language": "python", "Rationale": "https://github.com/microsoft/kiota/issues/3030" - }, - { - "Language": "php", - "Rationale": "https://github.com/microsoft/kiota/issues/3029" } ] }, diff --git a/it/php/composer.json b/it/php/composer.json index 0b97c43f56..0d6db4c0e6 100644 --- a/it/php/composer.json +++ b/it/php/composer.json @@ -6,6 +6,8 @@ "microsoft/kiota-serialization-text": "*", "microsoft/kiota-http-guzzle": "*", "microsoft/kiota-serialization-json": "*", + "microsoft/kiota-serialization-form": "*", + "microsoft/kiota-serialization-multipart": "*", "microsoft/kiota-authentication-phpleague": "*" }, "require-dev": { diff --git a/src/Kiota.Builder/Refiners/PhpRefiner.cs b/src/Kiota.Builder/Refiners/PhpRefiner.cs index 1a9e65896e..c861b2405e 100644 --- a/src/Kiota.Builder/Refiners/PhpRefiner.cs +++ b/src/Kiota.Builder/Refiners/PhpRefiner.cs @@ -91,13 +91,19 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance defaultConfiguration.Serializers, new(StringComparer.OrdinalIgnoreCase) { "Microsoft\\Kiota\\Serialization\\Json\\JsonSerializationWriterFactory", - "Microsoft\\Kiota\\Serialization\\Text\\TextSerializationWriterFactory"} + "Microsoft\\Kiota\\Serialization\\Text\\TextSerializationWriterFactory", + @"Microsoft\Kiota\Serialization\Form\FormSerializationWriterFactory", + @"Microsoft\Kiota\Serialization\Multipart\MultipartSerializationWriterFactory" + + } ); ReplaceDefaultDeserializationModules(generatedCode, defaultConfiguration.Deserializers, new(StringComparer.OrdinalIgnoreCase) { "Microsoft\\Kiota\\Serialization\\Json\\JsonParseNodeFactory", - "Microsoft\\Kiota\\Serialization\\Text\\TextParseNodeFactory"} + "Microsoft\\Kiota\\Serialization\\Text\\TextParseNodeFactory", + @"Microsoft\Kiota\Serialization\Form\FormParseNodeFactory" + } ); cancellationToken.ThrowIfCancellationRequested(); AddSerializationModulesImport(generatedCode, ["Microsoft\\Kiota\\Abstractions\\ApiClientBuilder"], null, '\\'); @@ -204,7 +210,12 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance new(static x => x is CodeClass codeClass && codeClass.IsOfKind(CodeClassKind.RequestConfiguration), "Microsoft\\Kiota\\Abstractions", "RequestOption"), new (static x => x is CodeClass { OriginalComposedType: CodeIntersectionType intersectionType } && intersectionType.Types.Any(static y => !y.IsExternal), "Microsoft\\Kiota\\Abstractions\\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) }; + + private const string MultipartBodyClassName = "MultiPartBody"; + private static void CorrectPropertyType(CodeProperty currentProperty) { if (currentProperty.IsOfKind(CodePropertyKind.RequestAdapter)) @@ -276,6 +287,11 @@ private static void CorrectParameterType(CodeElement codeElement) x.Type.Name = "BackingStoreFactory"; x.DefaultValue = "null"; }); + currentMethod.Parameters.Where(x => x.Type.Name.Equals("MultipartBody", StringComparison.OrdinalIgnoreCase)) + .ToList().ForEach(static y => + { + y.Type.Name = "MultiPartBody"; + }); } CrawlTree(codeElement, CorrectParameterType); } diff --git a/src/Kiota.Builder/Refiners/PhpReservedNamesProvider.cs b/src/Kiota.Builder/Refiners/PhpReservedNamesProvider.cs index ac8a3bfc64..88fa2fca48 100644 --- a/src/Kiota.Builder/Refiners/PhpReservedNamesProvider.cs +++ b/src/Kiota.Builder/Refiners/PhpReservedNamesProvider.cs @@ -69,7 +69,8 @@ public class PhpReservedNamesProvider : IReservedNamesProvider "xor", "yield", "yield from", - "BaseRequestBuilder" + "BaseRequestBuilder", + "MultipartBody" }); public HashSet ReservedNames => _reservedNames.Value; diff --git a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs index 81ce773396..9fd505b322 100644 --- a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs @@ -578,7 +578,7 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req writer.WriteLine($"{RequestInfoVarName}->setStreamContent({conventions.GetParameterName(requestParams.requestBody)}, \"{sanitizedRequestBodyContentType}\");"); } else if (currentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) - if (requestParams.requestBody.Type is CodeType bodyType && bodyType.TypeDefinition is CodeClass) + if (requestParams.requestBody.Type is CodeType bodyType && (bodyType.TypeDefinition is CodeClass || bodyType.Name.Equals("MultiPartBody", StringComparison.OrdinalIgnoreCase))) writer.WriteLine($"{RequestInfoVarName}->setContentFromParsable{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{sanitizedRequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); else writer.WriteLine($"{RequestInfoVarName}->setContentFromScalar{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{sanitizedRequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); diff --git a/src/kiota/appsettings.json b/src/kiota/appsettings.json index 2f8cc3845c..85fcd3870c 100644 --- a/src/kiota/appsettings.json +++ b/src/kiota/appsettings.json @@ -184,6 +184,14 @@ { "Name": "microsoft/kiota-serialization-text", "Version": "1.0.1" + }, + { + "Name": "microsoft/kiota-serialization-multipart", + "Version": "1.0.0" + }, + { + "Name": "microsoft/kiota-serialization-form", + "Version": "1.0.0" } ], "DependencyInstallCommand": "composer require {0}:{1}" diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs index f5dd725799..b1233a7f84 100644 --- a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs @@ -2508,4 +2508,24 @@ public void WritesRequestGeneratorContentTypeQuotes() var result = stringWriter.ToString(); Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); } + [Fact] + public async Task WritesRequestGeneratorBodyForMultipart() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.Parameters.OfKind(CodeParameterKind.RequestBody)!.Type = new CodeType + { + Name = "MultipartBody", + IsExternal = true + }; + method.RequestBodyContentType = "multipart/form-data"; + await _refiner.Refine(root, new CancellationToken(false)); + languageWriter.Write(method); + var result = stringWriter.ToString(); + Assert.Contains("MultiPartBody $body", result); + Assert.Contains("$requestInfo->setContentFromParsable($this->requestAdapter, \"multipart/form-data\", $body);", result); + } }