Skip to content

Commit 6682bf6

Browse files
authored
[httpclient-chsharp-mgmt] implement resourceCollectionClientProvider (Azure#49831)
* first commit. * add readonly modifier. * refine. * remove useless using statement. * address Wei's review comments. * small refine. * add one more todo. * address code review comments. * fix rebase issue. * add unit tests for ResourceCollectionClientProvider.
1 parent 59c7d42 commit 6682bf6

File tree

11 files changed

+732
-54
lines changed

11 files changed

+732
-54
lines changed

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

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ namespace Azure.Generator.Management.Providers
3232
internal class ResourceClientProvider : TypeProvider
3333
{
3434
private IReadOnlyCollection<InputServiceMethod> _resourceServiceMethods;
35-
private readonly IReadOnlyList<string> _contextualParameters;
3635

3736
private FieldProvider _dataField;
3837
private FieldProvider _resourcetypeField;
@@ -56,11 +55,11 @@ public ResourceClientProvider(InputClient inputClient)
5655
ResourceData = ManagementClientGenerator.Instance.TypeFactory.CreateModel(resourceModel)!;
5756
_clientProvider = ManagementClientGenerator.Instance.TypeFactory.CreateClient(inputClient)!;
5857

59-
_contextualParameters = GetContextualParameters(requestPath);
58+
ContextualParameters = GetContextualParameters(requestPath);
6059

61-
_dataField = new FieldProvider(FieldModifiers.Private, ResourceData.Type, "_data", this);
62-
_clientDiagonosticsField = new FieldProvider(FieldModifiers.Private, typeof(ClientDiagnostics), $"_{SpecName.ToLower()}ClientDiagnostics", this);
63-
_restClientField = new FieldProvider(FieldModifiers.Private, _clientProvider.Type, $"_{SpecName.ToLower()}RestClient", this);
60+
_dataField = new FieldProvider(FieldModifiers.Private | FieldModifiers.ReadOnly, ResourceData.Type, "_data", this);
61+
_clientDiagonosticsField = new FieldProvider(FieldModifiers.Private | FieldModifiers.ReadOnly, typeof(ClientDiagnostics), $"_{SpecName.ToLower()}ClientDiagnostics", this);
62+
_restClientField = new FieldProvider(FieldModifiers.Private | FieldModifiers.ReadOnly, _clientProvider.Type, $"_{SpecName.ToLower()}RestClient", this);
6463
}
6564

6665
private IReadOnlyList<string> GetContextualParameters(string contextualRequestPath)
@@ -77,6 +76,8 @@ private IReadOnlyList<string> GetContextualParameters(string contextualRequestPa
7776
return contextualParameters;
7877
}
7978

79+
protected IReadOnlyList<string> ContextualParameters { get; }
80+
8081
protected override string BuildName() => $"{SpecName}Resource";
8182

8283
private OperationSourceProvider? _source;
@@ -89,7 +90,7 @@ private IReadOnlyList<string> GetContextualParameters(string contextualRequestPa
8990

9091
protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", $"{Name}.cs");
9192

92-
protected override FieldProvider[] BuildFields() => [_dataField, _clientDiagonosticsField, _restClientField, _resourcetypeField];
93+
protected override FieldProvider[] BuildFields() => [_clientDiagonosticsField, _restClientField, _dataField, _resourcetypeField];
9394

9495
protected override PropertyProvider[] BuildProperties()
9596
{
@@ -199,6 +200,8 @@ protected MethodProvider BuildValidateResourceIdMethod()
199200

200201
protected virtual ValueExpression ExpectedResourceTypeForValidation => _resourcetypeField;
201202

203+
protected virtual CSharpType ResourceClientCharpType => this.Type;
204+
202205
protected override CSharpType[] BuildImplements() => [typeof(ArmResource)];
203206

204207
protected override MethodProvider[] BuildMethods()
@@ -224,14 +227,14 @@ protected override MethodProvider[] BuildMethods()
224227
return [BuildValidateResourceIdMethod(), .. operationMethods];
225228
}
226229

227-
private MethodProvider BuildOperationMethod(InputServiceMethod method, MethodProvider convenienceMethod, bool isAsync, bool isUpdateOnly = false)
230+
// TODO: the BuildOperationMethod related code is kind of messy now need to be refactored in a following up PR
231+
protected MethodProvider BuildOperationMethod(InputServiceMethod method, MethodProvider convenienceMethod, bool isAsync, bool isUpdateOnly = false)
228232
{
229-
var operation = method.Operation;
230233
var signature = new MethodSignature(
231234
isUpdateOnly ? (isAsync ? "UpdateAsync" : "Update") : convenienceMethod.Signature.Name,
232235
isUpdateOnly ? $"Update a {SpecName}" : convenienceMethod.Signature.Description,
233236
convenienceMethod.Signature.Modifiers,
234-
GetOperationMethodReturnType(isAsync, method is InputLongRunningServiceMethod || method is InputLongRunningPagingServiceMethod, operation.Responses, out var isGeneric),
237+
GetOperationMethodReturnType(method, isAsync, out var isGeneric),
235238
convenienceMethod.Signature.ReturnDescription,
236239
GetOperationMethodParameters(convenienceMethod, method is InputLongRunningServiceMethod),
237240
convenienceMethod.Signature.Attributes,
@@ -240,58 +243,85 @@ private MethodProvider BuildOperationMethod(InputServiceMethod method, MethodPro
240243
convenienceMethod.Signature.ExplicitInterface,
241244
convenienceMethod.Signature.NonDocumentComment);
242245

246+
return BuildOperationMethodCore(method, convenienceMethod, signature, isAsync, isGeneric);
247+
}
248+
249+
protected MethodProvider BuildOperationMethodCore(InputServiceMethod method, MethodProvider convenienceMethod, MethodSignature signature, bool isAsync, bool isGeneric)
250+
{
243251
var bodyStatements = new MethodBodyStatement[]
244252
{
245-
UsingDeclare("scope", typeof(DiagnosticScope), _clientDiagonosticsField.Invoke(nameof(ClientDiagnostics.CreateScope), [Literal($"{Type.Namespace}.{operation.Name}")]), out var scopeVariable),
253+
UsingDeclare("scope", typeof(DiagnosticScope), _clientDiagonosticsField.Invoke(nameof(ClientDiagnostics.CreateScope), [Literal($"{Name}.{signature.Name}")]), out var scopeVariable),
246254
scopeVariable.Invoke(nameof(DiagnosticScope.Start)).Terminate(),
247255
new TryCatchFinallyStatement
248-
(BuildOperationMethodTryStatement(convenienceMethod, isAsync, method, isGeneric), Catch(Declare<Exception>("e", out var exceptionVarialble), [scopeVariable.Invoke(nameof(DiagnosticScope.Failed), exceptionVarialble).Terminate(), Throw()]))
256+
(BuildOperationMethodTryStatement(method, convenienceMethod, signature, isAsync, isGeneric),
257+
Catch(Declare<Exception>("e", out var exceptionVarialble),
258+
[scopeVariable.Invoke(nameof(DiagnosticScope.Failed), exceptionVarialble).Terminate(), Throw()]))
249259
};
250260

251261
return new MethodProvider(signature, bodyStatements, this);
252262
}
253263

264+
protected virtual bool SkipMethodParameter(ParameterProvider parameter)
265+
{
266+
return ContextualParameters.Contains(parameter.Name);
267+
}
268+
254269
protected IReadOnlyList<ParameterProvider> GetOperationMethodParameters(MethodProvider convenienceMethod, bool isLongRunning)
255270
{
256271
var result = new List<ParameterProvider>();
257272
if (isLongRunning)
258273
{
259274
result.Add(KnownAzureParameters.WaitUntil);
260275
}
276+
261277
foreach (var parameter in convenienceMethod.Signature.Parameters)
262278
{
263-
if (!_contextualParameters.Contains(parameter.Name))
279+
if (!SkipMethodParameter(parameter))
264280
{
265281
result.Add(parameter);
266282
}
267283
}
284+
268285
return result;
269286
}
270287

271-
protected CSharpType GetOperationMethodReturnType(bool isAsync, bool isLongRunningOperation, IReadOnlyList<InputOperationResponse> operationResponses, out bool isGeneric)
288+
protected bool IsLongRunningOperation(InputServiceMethod method)
289+
{
290+
return method is InputLongRunningServiceMethod || method is InputLongRunningPagingServiceMethod;
291+
}
292+
293+
protected bool IsReturnTypeGeneric(InputServiceMethod method)
294+
{
295+
var operationResponses = method.Operation.Responses;
296+
var response = operationResponses.FirstOrDefault(r => !r.IsErrorResponse);
297+
var responseBodyType = response?.BodyType is null ? null : ManagementClientGenerator.Instance.TypeFactory.CreateCSharpType(response.BodyType);
298+
return IsLongRunningOperation(method) && responseBodyType is not null;
299+
}
300+
301+
protected CSharpType GetOperationMethodReturnType(InputServiceMethod method, bool isAsync, out bool isGeneric)
272302
{
273-
isGeneric = false;
303+
bool isLongRunningOperation = IsLongRunningOperation(method);
304+
isGeneric = IsReturnTypeGeneric(method);
305+
274306
if (isLongRunningOperation)
275307
{
276-
var response = operationResponses.FirstOrDefault(r => !r.IsErrorResponse);
277-
var responseBodyType = response?.BodyType is null ? null : ManagementClientGenerator.Instance.TypeFactory.CreateCSharpType(response.BodyType);
278-
if (responseBodyType is null)
308+
if (!isGeneric)
279309
{
280310
return isAsync ? new CSharpType(typeof(Task<>), typeof(ArmOperation)) : typeof(ArmOperation);
281311
}
282312
else
283313
{
284-
isGeneric = true;
285-
return isAsync ? new CSharpType(typeof(Task<>), new CSharpType(typeof(ArmOperation<>), Type)) : new CSharpType(typeof(ArmOperation<>), Type);
314+
return isAsync ? new CSharpType(typeof(Task<>), new CSharpType(typeof(ArmOperation<>), ResourceClientCharpType)) : new CSharpType(typeof(ArmOperation<>), ResourceClientCharpType);
286315
}
287316
}
288-
return isAsync ? new CSharpType(typeof(Task<>), new CSharpType(typeof(Response<>), Type)) : new CSharpType(typeof(Response<>), Type);
317+
return isAsync ? new CSharpType(typeof(Task<>), new CSharpType(typeof(Response<>), ResourceClientCharpType)) : new CSharpType(typeof(Response<>), ResourceClientCharpType);
289318
}
290319

291-
private TryStatement BuildOperationMethodTryStatement(MethodProvider convenienceMethod, bool isAsync, InputServiceMethod method, bool isGeneric)
320+
private TryStatement BuildOperationMethodTryStatement(InputServiceMethod method, MethodProvider convenienceMethod, MethodSignature signature, bool isAsync, bool isGeneric)
292321
{
293322
var operation = method.Operation;
294323
var cancellationToken = convenienceMethod.Signature.Parameters.Single(p => p.Type.Equals(typeof(CancellationToken)));
324+
295325
var tryStatement = new TryStatement();
296326
var contextDeclaration = Declare("context", typeof(RequestContext), New.Instance(typeof(RequestContext), new Dictionary<ValueExpression, ValueExpression> { { Identifier(nameof(RequestContext.CancellationToken)), cancellationToken } }), out var contextVariable);
297327
tryStatement.Add(contextDeclaration);
@@ -325,7 +355,7 @@ private TryStatement BuildOperationMethodTryStatement(MethodProvider convenience
325355
finalStateVia = (OperationFinalStateVia)lroPagingMethod.LongRunningServiceMetadata.FinalStateVia;
326356
}
327357

328-
var armOperationType = !isGeneric ? ManagementClientGenerator.Instance.OutputLibrary.ArmOperation.Type : ManagementClientGenerator.Instance.OutputLibrary.GenericArmOperation.Type.MakeGenericType([Type]);
358+
var armOperationType = !isGeneric ? ManagementClientGenerator.Instance.OutputLibrary.ArmOperation.Type : ManagementClientGenerator.Instance.OutputLibrary.GenericArmOperation.Type.MakeGenericType([ResourceClientCharpType]);
329359
ValueExpression[] armOperationArguments = [_clientDiagonosticsField, This.Property("Pipeline"), messageVariable.Property("Request"), isGeneric ? responseVariable.Invoke("GetRawResponse") : responseVariable, Static(typeof(OperationFinalStateVia)).Property(finalStateVia.ToString())];
330360
var operationDeclaration = Declare("operation", armOperationType, New.Instance(armOperationType, isGeneric ? [New.Instance(Source.Type, This.Property("Client")), .. armOperationArguments] : armOperationArguments), out var operationVariable);
331361

@@ -340,15 +370,27 @@ private TryStatement BuildOperationMethodTryStatement(MethodProvider convenience
340370
}
341371
else
342372
{
343-
tryStatement.Add(new IfStatement(responseVariable.Property("Value").Equal(Null))
344-
{
345-
((KeywordExpression)ThrowExpression(New.Instance(typeof(RequestFailedException), responseVariable.Invoke("GetRawResponse")))).Terminate()
346-
});
347-
tryStatement.Add(Return(Static(typeof(Response)).Invoke(nameof(Response.FromValue), New.Instance(Type, This.Property("Client"), responseVariable.Property("Value")), responseVariable.Invoke("GetRawResponse"))));
373+
tryStatement.Add(BuildReturnStatements(responseVariable, signature));
348374
}
375+
349376
return tryStatement;
350377
}
351378

379+
protected virtual MethodBodyStatement BuildReturnStatements(ValueExpression responseVariable, MethodSignature signature)
380+
{
381+
List<MethodBodyStatement> statements =
382+
[
383+
new IfStatement(responseVariable.Property("Value").Equal(Null))
384+
{
385+
((KeywordExpression)ThrowExpression(New.Instance(typeof(RequestFailedException), responseVariable.Invoke("GetRawResponse")))).Terminate()
386+
},
387+
];
388+
var returnValueExpression = New.Instance(ResourceClientCharpType, This.Property("Client"), responseVariable.Property("Value"));
389+
statements.Add(Return(Static(typeof(Response)).Invoke(nameof(Response.FromValue), returnValueExpression, responseVariable.Invoke("GetRawResponse"))));
390+
391+
return statements;
392+
}
393+
352394
private static CSharpType GetResponseType(MethodProvider convenienceMethod, bool isAsync) => isAsync ? convenienceMethod.Signature.ReturnType?.Arguments[0]! : convenienceMethod.Signature.ReturnType!;
353395

354396
private ValueExpression[] PopulateArguments(IReadOnlyList<ParameterProvider> parameters, MethodProvider convenienceMethod, VariableExpression contextVariable)
@@ -365,7 +407,7 @@ private ValueExpression[] PopulateArguments(IReadOnlyList<ParameterProvider> par
365407
arguments.Add(This.Property(nameof(ArmResource.Id)).Property(nameof(ResourceIdentifier.ResourceGroupName)));
366408
}
367409
// TODO: handle parents
368-
else if (parameter.Name.Equals(_contextualParameters.Last(), StringComparison.InvariantCultureIgnoreCase))
410+
else if (parameter.Name.Equals(ContextualParameters.Last(), StringComparison.InvariantCultureIgnoreCase))
369411
{
370412
arguments.Add(This.Property(nameof(ArmResource.Id)).Property(nameof(ResourceIdentifier.Name)));
371413
}

0 commit comments

Comments
 (0)