Skip to content

Commit 787fb96

Browse files
Fix the client diagnostics value for pageable methods (Azure#51961)
Co-authored-by: Copilot <[email protected]>
1 parent 625f71e commit 787fb96

14 files changed

+132
-27
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ public class ManagementOutputLibrary : AzureOutputLibrary
3030
private ProviderConstantsProvider? _providerConstants;
3131
internal ProviderConstantsProvider ProviderConstants => _providerConstants ??= new ProviderConstantsProvider();
3232

33+
// TODO -- this is really a bad practice that this map is not built in one place, but we are building it while generating stuff and in the meantime we might read it.
34+
// but currently this is the best we could do right now.
35+
internal Dictionary<TypeProvider, string> PageableMethodScopes { get; } = new();
36+
3337
private IReadOnlyList<ResourceClientProvider>? _resourceClients;
3438
private IReadOnlyList<ResourceCollectionClientProvider>? _resourceCollections;
3539
private IReadOnlyList<MockableResourceProvider>? _mockableResourceProviders;

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ protected MethodBodyStatement[] BuildBodyStatements()
102102
{
103103
var statements = new List<MethodBodyStatement>();
104104

105-
var collectionResultOfT = ((ScmMethodProvider)_convenienceMethod).CollectionDefinition!.Type;
105+
var collectionResult = ((ScmMethodProvider)_convenienceMethod).CollectionDefinition!;
106+
var diagnosticScope = ResourceHelpers.GetDiagnosticScope(_enclosingType, _methodName, _isAsync);
107+
ManagementClientGenerator.Instance.OutputLibrary.PageableMethodScopes.Add(collectionResult, diagnosticScope);
108+
109+
var collectionResultOfT = collectionResult.Type;
106110
statements.Add(ResourceMethodSnippets.CreateRequestContext(KnownParameters.CancellationTokenParameter, out var contextVariable));
107111

108112
var requestMethod = _restClientInfo.RestClientProvider.GetRequestMethodByOperation(_method.Operation);

eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/src/Utilities/ResourceHelpers.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Azure.ResourceManager.Resources;
77
using Microsoft.TypeSpec.Generator.Input.Extensions;
88
using Microsoft.TypeSpec.Generator.Primitives;
9+
using Microsoft.TypeSpec.Generator.Providers;
910
using System;
1011
using System.Runtime.CompilerServices;
1112

@@ -60,6 +61,12 @@ public static string GetRestClientPropertyName(string clientName)
6061
};
6162
}
6263

64+
public static string GetDiagnosticScope(TypeProvider enclosingType, string methodName, bool isAsync)
65+
{
66+
var rawMethodName = isAsync && methodName.EndsWith("Async") ? methodName[..^5] : methodName; // trim "Async" if the method is async method
67+
return $"{enclosingType.Type.Name}.{rawMethodName}";
68+
}
69+
6370
/// <summary>
6471
/// Determines if the given resource operation kind should also be treated as an LRO (Long-Running Operation).
6572
/// When this happens, an operation is not a true long-running operation at the REST API level,

eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/src/Visitors/PaginationVisitor.cs

Lines changed: 106 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,122 @@ namespace Azure.Generator.Management.Visitors;
1313

1414
internal class PaginationVisitor : ScmLibraryVisitor
1515
{
16+
private bool _isCollectionResultType;
17+
18+
/// <summary>
19+
/// This method is the entrance of the visitor.
20+
/// We check the name of this type to ensure this visitor is only applied to the collection result types.
21+
/// </summary>
22+
/// <param name="type"></param>
23+
/// <returns></returns>
24+
protected override TypeProvider? VisitType(TypeProvider type)
25+
{
26+
_isCollectionResultType = type.Name.AsSpan().Contains("CollectionResult".AsSpan(), StringComparison.Ordinal);
27+
return base.VisitType(type);
28+
}
29+
1630
protected override MethodBodyStatement? VisitStatements(MethodBodyStatements statements, MethodProvider method)
1731
{
18-
if (method.EnclosingType.Name.AsSpan().Contains("CollectionResult".AsSpan(), StringComparison.Ordinal) && method.Signature.Name.Equals("AsPages"))
32+
if (_isCollectionResultType && IsAsPagesMethod(method))
1933
{
20-
var doWhileStatement = statements.OfType<DoWhileStatement>().FirstOrDefault();
21-
if (doWhileStatement is not null)
22-
{
23-
var body = doWhileStatement.Body;
34+
DoVisitAsPagesMethodStatements(statements, method);
35+
}
36+
return base.VisitStatements(statements, method);
37+
}
38+
39+
private static bool IsAsPagesMethod(MethodProvider method) => method.Signature.Name.Equals("AsPages");
40+
private static bool IsGetNextResponseMethod(MethodProvider method) => method.Signature.Name.Equals("GetNextResponse");
2441

25-
// get the response to model casting expression
26-
var responseToModelStatement = body.OfType<ExpressionStatement>().Skip(1).FirstOrDefault();
27-
if (responseToModelStatement is not null)
42+
private void DoVisitAsPagesMethodStatements(MethodBodyStatements statements, MethodProvider method)
43+
{
44+
var doWhileStatement = statements.OfType<DoWhileStatement>().FirstOrDefault();
45+
if (doWhileStatement is not null)
46+
{
47+
// we manually go over the body statements because currently the framework does not do this.
48+
// TODO -- we do not have to do this once https://github.com/microsoft/typespec/issues/8177 is fixed.
49+
foreach (var statement in doWhileStatement.Body)
50+
{
51+
if (statement is ExpressionStatement { Expression: AssignmentExpression assignment } expressionStatement)
2852
{
29-
responseToModelStatement.Update(ConstructFromResponseExpression(responseToModelStatement));
53+
var updatedExpression = DoVisitAssignmentExpressionForAsPagesMethod(assignment, method);
54+
if (updatedExpression is not null)
55+
{
56+
// update the expression in the statement.
57+
expressionStatement.Update(updatedExpression);
58+
}
3059
}
3160
}
3261
}
33-
return base.VisitStatements(statements, method);
3462
}
3563

36-
private static AssignmentExpression ConstructFromResponseExpression(ExpressionStatement responseToModelStatement)
64+
protected override ValueExpression? VisitAssignmentExpression(AssignmentExpression expression, MethodProvider method)
65+
{
66+
if (_isCollectionResultType && IsAsPagesMethod(method))
67+
{
68+
var newExpression = DoVisitAssignmentExpressionForAsPagesMethod(expression, method);
69+
if (newExpression is not null)
70+
{
71+
// we have updated the expression, so we return it.
72+
return newExpression;
73+
}
74+
}
75+
76+
if (_isCollectionResultType && IsGetNextResponseMethod(method))
77+
{
78+
var newExpression = DoVisitAssignmentExpressionForGetNextResponseMethod(expression, method);
79+
if (newExpression is not null)
80+
{
81+
// we have updated the expression, so we return it.
82+
return newExpression;
83+
}
84+
}
85+
return base.VisitAssignmentExpression(expression, method);
86+
}
87+
88+
private ValueExpression? DoVisitAssignmentExpressionForAsPagesMethod(AssignmentExpression expression, MethodProvider method)
3789
{
38-
var assignmentExpression = responseToModelStatement.Expression as AssignmentExpression;
39-
var castExpression = assignmentExpression?.Value as CastExpression;
40-
var value = Static(castExpression?.Type!).Invoke(SerializationVisitor.FromResponseMethodName, [castExpression?.Inner!]);
41-
var variable = assignmentExpression!.Variable;
42-
return variable.Assign(value);
90+
if (expression.Value is CastExpression castExpression && IsResponseToModelCastExpression(castExpression))
91+
{
92+
var value = Static(castExpression.Type!).Invoke(SerializationVisitor.FromResponseMethodName, [castExpression.Inner!]);
93+
var variable = expression.Variable;
94+
return variable.Assign(value);
95+
}
96+
// do nothing if nothing is changed.
97+
return null;
98+
99+
static bool IsResponseToModelCastExpression(CastExpression castExpression)
100+
{
101+
if (castExpression.Inner is VariableExpression variableExpression &&
102+
variableExpression.Type is { IsFrameworkType: true, FrameworkType: { } frameworkType } &&
103+
frameworkType == typeof(Response))
104+
{
105+
return true;
106+
}
107+
return false;
108+
}
109+
}
110+
111+
private ValueExpression? DoVisitAssignmentExpressionForGetNextResponseMethod(AssignmentExpression expression, MethodProvider method)
112+
{
113+
if (expression is
114+
{
115+
Variable: DeclarationExpression { Variable: { } variable, IsUsing: true } declaration,
116+
Value: InvokeMethodExpression invokeMethodExpression
117+
}
118+
&& variable.Declaration.RequestedName == "scope")
119+
{
120+
// first we fetch the diagnostics scope from outputlibrary
121+
if (ManagementClientGenerator.Instance.OutputLibrary.PageableMethodScopes.TryGetValue(method.EnclosingType, out var diagnosticsScopeValue))
122+
{
123+
// change its first argument to be the new diagnostics scope value.
124+
invokeMethodExpression.Update(
125+
arguments: [Literal(diagnosticsScopeValue)]);
126+
127+
return expression;
128+
}
129+
return null;
130+
}
131+
// do nothing if nothing is changed.
132+
return null;
43133
}
44134
}

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/BarsGetAsyncCollectionResultOfT.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/BarsGetCollectionResultOfT.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/FoosGetAsyncCollectionResultOfT.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/FoosGetCollectionResultOfT.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/ZoosGetAsyncCollectionResultOfT.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/ZoosGetBySubscriptionAsyncCollectionResultOfT.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)