Skip to content

Commit fc7d07a

Browse files
authored
[http-client-csharp-mgmt] Generate tag related methods (Azure#50482)
Generate tag related methods in Resource Provider.
1 parent aecf7e8 commit fc7d07a

File tree

17 files changed

+1378
-148
lines changed

17 files changed

+1378
-148
lines changed

eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/src/Extensions/ClientProviderExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,10 @@ public static MethodProvider GetConvenienceMethodByOperation(this ClientProvider
1414
var methods = clientProvider.GetMethodCollectionByOperation(operation);
1515
return isAsync ? methods[^1] : methods[^2];
1616
}
17+
18+
public static MethodProvider GetRequestMethodByOperation(this ClientProvider clientProvider, InputOperation operation)
19+
{
20+
return clientProvider.RestClient.GetCreateRequestMethod(operation);
21+
}
1722
}
1823
}

eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/src/Providers/OperationMethodProviders/ResourceOperationMethodProvider.cs

Lines changed: 27 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
// Licensed under the MIT License.
33

44
using Azure.Core;
5-
using Azure.Core.Pipeline;
5+
using Azure.Generator.Management.Extensions;
66
using Azure.Generator.Management.Primitives;
7+
using Azure.Generator.Management.Snippets;
78
using Azure.Generator.Management.Utilities;
89
using Azure.ResourceManager;
910
using Microsoft.TypeSpec.Generator.Expressions;
@@ -58,14 +59,12 @@ public static implicit operator MethodProvider(ResourceOperationMethodProvider r
5859

5960
protected virtual MethodBodyStatement[] BuildBodyStatements()
6061
{
61-
var scopeDeclaration = BuildDiagnosticScopeDeclaration(out var scopeVariable);
62-
return
63-
[
64-
scopeDeclaration,
65-
scopeVariable.Invoke(nameof(DiagnosticScope.Start)).Terminate(),
62+
var scopeStatements = ResourceMethodSnippets.CreateDiagnosticScopeStatements(_resourceClientProvider, _signature.Name, out var scopeVariable);
63+
return [
64+
.. scopeStatements,
6665
new TryCatchFinallyStatement(
6766
BuildTryExpression(),
68-
BuildCatchExpression(scopeVariable)
67+
ResourceMethodSnippets.CreateDiagnosticCatchBlock(scopeVariable)
6968
)
7069
];
7170
}
@@ -86,27 +85,22 @@ protected virtual MethodSignature CreateSignature()
8685
_convenienceMethod.Signature.NonDocumentComment);
8786
}
8887

89-
private MethodBodyStatement BuildDiagnosticScopeDeclaration(out VariableExpression scopeVariable)
90-
{
91-
return UsingDeclare(
92-
"scope",
93-
typeof(DiagnosticScope),
94-
_clientDiagnosticsField.Invoke(
95-
nameof(ClientDiagnostics.CreateScope),
96-
[Literal($"{_resourceClientProvider.Name}.{_signature.Name}")]),
97-
out scopeVariable);
98-
}
99-
10088
private TryExpression BuildTryExpression()
10189
{
10290
var cancellationTokenParameter = _convenienceMethod.Signature.Parameters.Single(p => p.Type.Equals(typeof(CancellationToken)));
10391

92+
var requestMethod = _resourceClientProvider.GetClientProvider().GetRequestMethodByOperation(_serviceMethod.Operation);
93+
10494
var tryStatements = new List<MethodBodyStatement>
10595
{
106-
BuildRequestContextInitialization(cancellationTokenParameter, out var contextVariable),
107-
BuildHttpMessageInitialization(contextVariable, out var messageVariable)
96+
ResourceMethodSnippets.CreateRequestContext(cancellationTokenParameter, out var contextVariable)
10897
};
10998

99+
// Populate arguments for the REST client method call
100+
var arguments = _resourceClientProvider.PopulateArguments(requestMethod.Signature.Parameters, contextVariable, _convenienceMethod);
101+
102+
tryStatements.Add(ResourceMethodSnippets.CreateHttpMessage(_resourceClientProvider, requestMethod.Signature.Name, arguments, out var messageVariable));
103+
110104
tryStatements.AddRange(BuildClientPipelineProcessing(messageVariable, contextVariable, out var responseVariable));
111105

112106
if (_serviceMethod.IsLongRunningOperation())
@@ -124,91 +118,33 @@ private TryExpression BuildTryExpression()
124118
return new TryExpression(tryStatements);
125119
}
126120

127-
private CatchExpression BuildCatchExpression(VariableExpression scopeVariable)
128-
{
129-
return Catch(
130-
Declare<Exception>("e", out var exceptionVariable),
131-
[
132-
scopeVariable.Invoke(nameof(DiagnosticScope.Failed), exceptionVariable).Terminate(),
133-
Throw()
134-
]);
135-
}
136-
137-
private MethodBodyStatement BuildRequestContextInitialization(ParameterProvider cancellationTokenParameter, out VariableExpression contextVariable)
138-
{
139-
var requestContextParams = new Dictionary<ValueExpression, ValueExpression>
140-
{
141-
{ Identifier(nameof(RequestContext.CancellationToken)), cancellationTokenParameter }
142-
};
143-
return Declare(
144-
"context",
145-
typeof(RequestContext),
146-
New.Instance(typeof(RequestContext), requestContextParams),
147-
out contextVariable);
148-
}
149-
150-
private MethodBodyStatement BuildHttpMessageInitialization(VariableExpression contextVariable, out VariableExpression messageVariable)
151-
{
152-
var requestMethod = _serviceMethod.GetCorrespondingRequestMethod(_resourceClientProvider);
153-
154-
var arguments = PopulateArguments(
155-
requestMethod.Signature.Parameters,
156-
contextVariable);
157-
158-
var invokeExpression = _resourceClientProvider
159-
.GetRestClientField()
160-
.Invoke(requestMethod.Signature.Name, arguments);
161-
162-
return Declare(
163-
"message",
164-
typeof(HttpMessage),
165-
invokeExpression,
166-
out messageVariable);
167-
}
168-
169121
private IReadOnlyList<MethodBodyStatement> BuildClientPipelineProcessing(
170122
VariableExpression messageVariable,
171123
VariableExpression contextVariable,
172124
out VariableExpression responseVariable)
173125
{
174-
var statements = new List<MethodBodyStatement>();
175126
var responseType = _convenienceMethod.Signature.ReturnType!.UnWrapAsync();
176-
VariableExpression declaredResponseVariable;
177-
var pipelineInvoke = _isAsync ? "ProcessMessageAsync" : "ProcessMessage";
178127

128+
// Check if the return type is a generic Response<T> or just Response
179129
if (!responseType.Equals(typeof(Response)))
180130
{
181-
var resultDeclaration = Declare(
182-
"result",
183-
typeof(Response),
184-
Identifier("this")
185-
.Property("Pipeline")
186-
.Invoke(pipelineInvoke, [messageVariable, contextVariable], null, _isAsync),
187-
out var resultVariable);
188-
statements.Add(resultDeclaration);
189-
190-
var responseDeclaration = Declare(
191-
"response",
131+
// For methods returning Response<T> (e.g., Response<MyResource>), use generic response processing
132+
return ResourceMethodSnippets.CreateGenericResponsePipelineProcessing(
133+
messageVariable,
134+
contextVariable,
192135
responseType,
193-
Static(typeof(Response)).Invoke(
194-
nameof(Response.FromValue),
195-
[resultVariable.CastTo(responseType.Arguments[0]), resultVariable]),
196-
out declaredResponseVariable);
197-
statements.Add(responseDeclaration);
136+
_isAsync,
137+
out responseVariable);
198138
}
199139
else
200140
{
201-
var responseDeclaration = Declare(
202-
"response",
203-
typeof(Response),
204-
Identifier("this")
205-
.Property("Pipeline")
206-
.Invoke(pipelineInvoke, [messageVariable, contextVariable], null, _isAsync),
207-
out declaredResponseVariable);
208-
statements.Add(responseDeclaration);
141+
// For methods returning just Response (no generic type), use non-generic response processing
142+
return ResourceMethodSnippets.CreateNonGenericResponsePipelineProcessing(
143+
messageVariable,
144+
contextVariable,
145+
_isAsync,
146+
out responseVariable);
209147
}
210-
responseVariable = declaredResponseVariable;
211-
return statements;
212148
}
213149

214150
private IReadOnlyList<MethodBodyStatement> BuildLroHandling(
@@ -297,52 +233,6 @@ protected virtual IReadOnlyList<MethodBodyStatement> BuildReturnStatements(Value
297233
return statements;
298234
}
299235

300-
private ValueExpression[] PopulateArguments(
301-
IReadOnlyList<ParameterProvider> parameters,
302-
VariableExpression contextVariable)
303-
{
304-
var arguments = new List<ValueExpression>();
305-
foreach (var parameter in parameters)
306-
{
307-
if (parameter.Name.Equals("subscriptionId", StringComparison.InvariantCultureIgnoreCase))
308-
{
309-
arguments.Add(
310-
Static(typeof(Guid)).Invoke(
311-
nameof(Guid.Parse),
312-
This.Property(nameof(ArmResource.Id)).Property(nameof(ResourceIdentifier.SubscriptionId))));
313-
}
314-
else if (parameter.Name.Equals("resourceGroupName", StringComparison.InvariantCultureIgnoreCase))
315-
{
316-
arguments.Add(
317-
This.Property(nameof(ArmResource.Id)).Property(nameof(ResourceIdentifier.ResourceGroupName)));
318-
}
319-
// TODO: handle parents
320-
// Handle resource name - the last contextual parameter
321-
else if (parameter.Name.Equals(_resourceClientProvider.ContextualParameters.Last(), StringComparison.InvariantCultureIgnoreCase))
322-
{
323-
arguments.Add(
324-
This.Property(nameof(ArmResource.Id)).Property(nameof(ResourceIdentifier.Name)));
325-
}
326-
else if (parameter.Type.Equals(typeof(RequestContent)))
327-
{
328-
var resource = _convenienceMethod.Signature.Parameters
329-
.Single(p => p.Type.Equals(_resourceClientProvider.ResourceData.Type));
330-
arguments.Add(resource);
331-
}
332-
else if (parameter.Type.Equals(typeof(RequestContext)))
333-
{
334-
var cancellationToken = _convenienceMethod.Signature.Parameters
335-
.Single(p => p.Type.Equals(typeof(CancellationToken)));
336-
arguments.Add(contextVariable);
337-
}
338-
else
339-
{
340-
arguments.Add(parameter);
341-
}
342-
}
343-
return [.. arguments];
344-
}
345-
346236
protected IReadOnlyList<ParameterProvider> GetOperationMethodParameters()
347237
{
348238
var result = new List<ParameterProvider>();

eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/src/Providers/ResourceClientProvider.cs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
using Azure.Core.Pipeline;
66
using Azure.Generator.Management.Extensions;
77
using Azure.Generator.Management.Models;
8-
using Azure.Generator.Management.Primitives;
98
using Azure.Generator.Management.Providers.OperationMethodProviders;
109
using Azure.Generator.Management.Snippets;
10+
using Azure.Generator.Management.Providers.TagMethodProviders;
1111
using Azure.Generator.Management.Utilities;
1212
using Azure.ResourceManager;
1313
using Microsoft.TypeSpec.Generator.ClientModel.Providers;
@@ -102,6 +102,7 @@ private IReadOnlyList<string> GetContextualParameters(string contextualRequestPa
102102

103103
internal ModelProvider ResourceData { get; }
104104
internal string SpecName { get; }
105+
internal IReadOnlyCollection<InputServiceMethod> ResourceServiceMethods => _resourceServiceMethods;
105106

106107
public bool IsSingleton { get; }
107108

@@ -195,6 +196,7 @@ protected ConstructorProvider BuildResourceIdentifierConstructor()
195196
}
196197

197198
internal const string ValidateResourceIdMethodName = "ValidateResourceId";
199+
198200
protected MethodProvider BuildValidateResourceIdMethod()
199201
{
200202
var idParameter = new ParameterProvider("id", $"", typeof(ResourceIdentifier));
@@ -265,7 +267,17 @@ protected override MethodProvider[] BuildMethods()
265267
}
266268
}
267269

268-
return [BuildValidateResourceIdMethod(), .. operationMethods];
270+
return [
271+
BuildValidateResourceIdMethod(),
272+
.. operationMethods,
273+
new AddTagMethodProvider(this, true),
274+
new AddTagMethodProvider(this, false),
275+
// Disabled SetTag methods generation temporarily: The extension method ReplaceWith for IDictionary<string, string> is defined in SharedExtensions.cs, which is not included in the project yet.
276+
// new SetTagsMethodProvider(this, true),
277+
// new SetTagsMethodProvider(this, false),
278+
new RemoveTagMethodProvider(this, true),
279+
new RemoveTagMethodProvider(this, false)
280+
];
269281
}
270282

271283
protected MethodProvider BuildOperationMethod(InputServiceMethod method, MethodProvider convenienceMethod, bool isAsync)
@@ -289,5 +301,59 @@ public ScopedApi<bool> TryGetApiVersion(out ScopedApi<string> apiVersion)
289301
var invocation = new InvokeMethodExpression(This, "TryGetApiVersion", [ResourceTypeExpression, new DeclarationExpression(apiVersionDeclaration, true)]);
290302
return invocation.As<bool>();
291303
}
304+
305+
public ValueExpression[] PopulateArguments(
306+
IReadOnlyList<ParameterProvider> parameters,
307+
VariableExpression contextVariable,
308+
MethodProvider? convenienceMethod = null)
309+
{
310+
var arguments = new List<ValueExpression>();
311+
foreach (var parameter in parameters)
312+
{
313+
if (parameter.Name.Equals("subscriptionId", StringComparison.InvariantCultureIgnoreCase))
314+
{
315+
arguments.Add(
316+
Static(typeof(Guid)).Invoke(
317+
nameof(Guid.Parse),
318+
This.Property(nameof(ArmResource.Id)).Property(nameof(ResourceIdentifier.SubscriptionId))));
319+
}
320+
else if (parameter.Name.Equals("resourceGroupName", StringComparison.InvariantCultureIgnoreCase))
321+
{
322+
arguments.Add(
323+
This.Property(nameof(ArmResource.Id)).Property(nameof(ResourceIdentifier.ResourceGroupName)));
324+
}
325+
// TODO: handle parents
326+
// Handle resource name - the last contextual parameter
327+
else if (parameter.Name.Equals(ContextualParameters.Last(), StringComparison.InvariantCultureIgnoreCase))
328+
{
329+
arguments.Add(
330+
This.Property(nameof(ArmResource.Id)).Property(nameof(ResourceIdentifier.Name)));
331+
}
332+
else if (parameter.Type.Equals(typeof(RequestContent)))
333+
{
334+
// If convenience method is provided, find the resource parameter from it
335+
if (convenienceMethod != null)
336+
{
337+
var resource = convenienceMethod.Signature.Parameters
338+
.Single(p => p.Type.Equals(ResourceData.Type));
339+
arguments.Add(resource);
340+
}
341+
else
342+
{
343+
// Otherwise just add the parameter as-is
344+
arguments.Add(parameter);
345+
}
346+
}
347+
else if (parameter.Type.Equals(typeof(RequestContext)))
348+
{
349+
arguments.Add(contextVariable);
350+
}
351+
else
352+
{
353+
arguments.Add(parameter);
354+
}
355+
}
356+
return [.. arguments];
357+
}
292358
}
293359
}

0 commit comments

Comments
 (0)