Skip to content

Commit 109d098

Browse files
Add visitor for MultiPartFormData WriteTo method (Azure#52933)
* Add visitor for MultiPartFormData WriteTo method * Include TaskExtensions * fix * revert * regen
1 parent fe36a4c commit 109d098

File tree

12 files changed

+1017
-399
lines changed

12 files changed

+1017
-399
lines changed

eng/packages/http-client-csharp/generator/Azure.Generator/src/AzureClientGenerator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,6 @@ protected override void Configure()
6464
AddVisitor(new MatchConditionsHeadersVisitor());
6565
AddVisitor(new RequestClientIdHeaderVisitor());
6666
AddVisitor(new SystemTextJsonConverterVisitor());
67+
AddVisitor(new MultiPartFormDataVisitor());
6768
}
6869
}

eng/packages/http-client-csharp/generator/Azure.Generator/src/Primitives/NewAzureProjectScaffolding.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ protected override IReadOnlyList<CSharpProjectCompileInclude> BuildCompileInclud
173173
}
174174
}
175175

176+
// Add TaskExtensions if there are multipart form data operations and it hasn't already been added for LRO
177+
if (!hasLongRunningOperation && AzureClientGenerator.Instance.InputLibrary.HasMultipartFormDataOperation)
178+
{
179+
compileIncludes.Add(new CSharpProjectCompileInclude(GetCompileInclude("TaskExtensions.cs"), SharedSourceLinkBase));
180+
}
181+
176182
return compileIncludes;
177183
}
178184
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Azure.Core.Pipeline;
5+
using Microsoft.TypeSpec.Generator.ClientModel;
6+
using Microsoft.TypeSpec.Generator.ClientModel.Providers;
7+
using Microsoft.TypeSpec.Generator.Expressions;
8+
using Microsoft.TypeSpec.Generator.Providers;
9+
using Microsoft.TypeSpec.Generator.Statements;
10+
11+
namespace Azure.Generator.Visitors
12+
{
13+
internal class MultiPartFormDataVisitor : ScmLibraryVisitor
14+
{
15+
protected override MethodBodyStatement VisitExpressionStatement(ExpressionStatement statement, MethodProvider method)
16+
{
17+
if (method is { EnclosingType: MultiPartFormDataBinaryContentDefinition, Signature.Name: "WriteTo" })
18+
{
19+
// Replaces GetAwaiter().GetResult() with EnsureCompleted() for the sync over async path.
20+
if (statement.Expression is InvokeMethodExpression invokeMethod)
21+
{
22+
if (invokeMethod.InstanceReference is InvokeMethodExpression innerInvoke)
23+
{
24+
invokeMethod.Update(
25+
instanceReference: innerInvoke.InstanceReference,
26+
methodName: nameof(TaskExtensions.EnsureCompleted),
27+
extensionType: typeof(TaskExtensions));
28+
}
29+
}
30+
}
31+
32+
return statement;
33+
}
34+
}
35+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Linq;
5+
using Azure.Generator.Tests.Common;
6+
using Azure.Generator.Tests.TestHelpers;
7+
using Azure.Generator.Visitors;
8+
using Microsoft.TypeSpec.Generator;
9+
using Microsoft.TypeSpec.Generator.ClientModel.Providers;
10+
using NUnit.Framework;
11+
12+
namespace Azure.Generator.Tests.Visitors
13+
{
14+
public class MultiPartFormDataVisitorTests
15+
{
16+
[Test]
17+
public void UpdatesWriteToMethod()
18+
{
19+
var operation = InputFactory.Operation("SomeOperation", requestMediaTypes: ["multipart/form-data"]);
20+
var serviceMethod = InputFactory.BasicServiceMethod("SomeOperation", operation);
21+
var client = InputFactory.Client("client", methods: [serviceMethod]);
22+
var plugin = MockHelpers.LoadMockGenerator(clients: () => [client]);
23+
24+
var visitor = new TestMultiPartFormDataVisitor();
25+
visitor.InvokeVisitLibrary(plugin.Object.OutputLibrary);
26+
27+
var multiPartFormData = plugin.Object.OutputLibrary.TypeProviders.OfType<MultiPartFormDataBinaryContentDefinition>().SingleOrDefault();
28+
29+
Assert.IsNotNull(multiPartFormData);
30+
var writeToMethod = multiPartFormData!.Methods.SingleOrDefault(m => m.Signature.Name == "WriteTo");
31+
Assert.IsNotNull(writeToMethod);
32+
Assert.AreEqual(Helpers.GetExpectedFromFile(), writeToMethod!.BodyStatements!.ToDisplayString());
33+
}
34+
35+
private class TestMultiPartFormDataVisitor : MultiPartFormDataVisitor
36+
{
37+
public void InvokeVisitLibrary(OutputLibrary library)
38+
{
39+
base.VisitLibrary(library);
40+
}
41+
}
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#if NET6_0_OR_GREATER
2+
_multipartContent.CopyTo(stream, default, cancellationToken);
3+
#else
4+
_multipartContent.CopyToAsync(stream).EnsureCompleted();
5+
#endif

eng/packages/http-client-csharp/generator/TestProjects/Local/Basic-TypeSpec/Basic-TypeSpec.tsp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,4 +450,10 @@ op ConditionalRequest(@header("If-Match") ifMatch?: string, @header("If-None-Mat
450450
@doc("A sample operation with conditional requests")
451451
@convenientAPI(true)
452452
@get
453-
op ConditionalRequestDate(@header("If-Match") ifMatch?: string, @header("If-None-Match") ifNoneMatch?: string, @header("If-Modified-Since") ifModifiedSince?: string): void;
453+
op ConditionalRequestDate(@header("If-Match") ifMatch?: string, @header("If-None-Match") ifNoneMatch?: string, @header("If-Modified-Since") ifModifiedSince?: string): void;
454+
455+
@route("multipart")
456+
@doc("A sample operation with multipart request")
457+
@convenientAPI(true)
458+
@post
459+
op MultipartRequest(@header("Content-Type") contentType: "multipart/form-data", @multipartBody body: {name: HttpPart<string>}): void;

eng/packages/http-client-csharp/generator/TestProjects/Local/Basic-TypeSpec/src/BasicTypeSpec.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<Compile Include="$(AzureCoreSharedSources)DiagnosticScope.cs" LinkBase="Shared/Core" />
1919
<Compile Include="$(AzureCoreSharedSources)HttpMessageSanitizer.cs" LinkBase="Shared/Core" />
2020
<Compile Include="$(AzureCoreSharedSources)TrimmingAttribute.cs" LinkBase="Shared/Core" />
21+
<Compile Include="$(AzureCoreSharedSources)TaskExtensions.cs" LinkBase="Shared/Core" />
2122
</ItemGroup>
2223

2324
<ItemGroup>

eng/packages/http-client-csharp/generator/TestProjects/Local/Basic-TypeSpec/src/Generated/BasicTypeSpecClient.RestClient.cs

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eng/packages/http-client-csharp/generator/TestProjects/Local/Basic-TypeSpec/src/Generated/BasicTypeSpecClient.cs

Lines changed: 64 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)