Skip to content

Commit 287180f

Browse files
authored
Build CreateResourceIdentifierMethod. (Azure#51071)
* Build CreateResourceIdentifierMethod. * refine. * address review comments. * address review comments.
1 parent dd58f00 commit 287180f

File tree

7 files changed

+136
-3
lines changed

7 files changed

+136
-3
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,14 @@ public static implicit operator string(RequestPath requestPath)
149149

150150
public static bool IsSegmentConstant(string segment)
151151
{
152-
var trimmed = segment.TrimStart('{').TrimEnd('}');
152+
var trimmed = TrimSegment(segment);
153153
var isScope = trimmed == "scope";
154154
return !isScope && !segment.StartsWith('{');
155155
}
156+
157+
public static string TrimSegment(string segment)
158+
{
159+
return segment.TrimStart('{').TrimEnd('}');
160+
}
156161
}
157162
}

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

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
using System.Diagnostics;
2525
using System.IO;
2626
using System.Linq;
27+
using System.Text;
2728
using static Microsoft.TypeSpec.Generator.Snippets.Snippet;
2829
using OperationKind = Azure.Generator.Management.Models.OperationKind;
2930

@@ -53,13 +54,19 @@ public static ResourceClientProvider Create(InputModelType model, ResourceMetada
5354
private bool _hasGetMethod = false;
5455
private bool _shouldGenerateTagMethods = false;
5556

57+
private RequestPath _requestPath;
58+
private ResourceMetadata _resourceMetadata;
59+
private InputOperation _inputOperationForRequestPath;
60+
5661
protected ClientProvider _restClientProvider;
5762
protected FieldProvider _clientDiagnosticsField;
5863
protected FieldProvider _clientField;
5964

6065
private protected ResourceClientProvider(InputModelType model, ResourceMetadata resourceMetadata)
6166
{
6267
IsSingleton = resourceMetadata.IsSingleton;
68+
_resourceMetadata = resourceMetadata;
69+
_inputOperationForRequestPath = ManagementClientGenerator.Instance.InputLibrary.GetMethodByCrossLanguageDefinitionId(_resourceMetadata.Methods.First().Id)!.Operation;
6370
ResourceScope = resourceMetadata.ResourceScope;
6471
var resourceType = resourceMetadata.ResourceType;
6572
_hasGetMethod = resourceMetadata.Methods.Any(m => m.Kind == OperationKind.Get);
@@ -70,15 +77,15 @@ private protected ResourceClientProvider(InputModelType model, ResourceMetadata
7077
SpecName = model.Name.ToIdentifierName();
7178

7279
// We should be able to assume that all operations in the resource client are for the same resource
73-
var requestPath = new RequestPath(ManagementClientGenerator.Instance.InputLibrary.GetMethodByCrossLanguageDefinitionId(resourceMetadata.Methods.First().Id)!.Operation.Path);
80+
_requestPath = new RequestPath(_inputOperationForRequestPath.Path);
7481
_resourceServiceMethods = resourceMetadata.Methods.Select(m => (m.Kind, ManagementClientGenerator.Instance.InputLibrary.GetMethodByCrossLanguageDefinitionId(m.Id)!));
7582
ResourceData = ManagementClientGenerator.Instance.TypeFactory.CreateModel(model)!;
7683

7784
// TODO: handle multiple clients in the future, for now we assume that there is only one client for the resource.
7885
var inputClients = resourceMetadata.Methods.Select(m => ManagementClientGenerator.Instance.InputLibrary.GetClientByMethod(ManagementClientGenerator.Instance.InputLibrary.GetMethodByCrossLanguageDefinitionId(m.Id)!)!).Distinct();
7986
_restClientProvider = ManagementClientGenerator.Instance.TypeFactory.CreateClient(inputClients.First())!;
8087

81-
ContextualParameters = GetContextualParameters(requestPath);
88+
ContextualParameters = GetContextualParameters(_requestPath);
8289

8390
_dataField = new FieldProvider(FieldModifiers.Private | FieldModifiers.ReadOnly, ResourceData.Type, "_data", this);
8491
_clientDiagnosticsField = new FieldProvider(FieldModifiers.Private | FieldModifiers.ReadOnly, typeof(ClientDiagnostics), $"_{SpecName.ToLower()}ClientDiagnostics", this);
@@ -89,6 +96,8 @@ private protected ResourceClientProvider(InputModelType model, ResourceMetadata
8996

9097
internal ResourceCollectionClientProvider? ResourceCollection { get; private set; }
9198

99+
private InputOperation InputOperationForRequestPath => _inputOperationForRequestPath;
100+
92101
private IReadOnlyList<string> GetContextualParameters(string contextualRequestPath)
93102
{
94103
var contextualParametersList = new List<string>();
@@ -225,6 +234,65 @@ protected MethodProvider BuildValidateResourceIdMethod()
225234
return new MethodProvider(signature, bodyStatements, this);
226235
}
227236

237+
private CSharpType GetPathParameterType(string parameterName)
238+
{
239+
var operation = InputOperationForRequestPath;
240+
241+
var operationParameter = operation.Parameters.First(p => p.Name.Equals(parameterName, StringComparison.OrdinalIgnoreCase));
242+
243+
var csharpType = ManagementClientGenerator.Instance.TypeFactory.CreateCSharpType(operationParameter.Type) ?? throw new InvalidOperationException($"Could not create C# type for parameter '{parameterName}' with type '{operationParameter.Type}' in operation '{operation.Name}'.");
244+
return parameterName switch
245+
{
246+
"subscriptionId" when csharpType.Equals(typeof(Guid)) => typeof(string),
247+
// Cases will be added later
248+
_ => csharpType
249+
};
250+
}
251+
252+
private MethodProvider BuildCreateResourceIdentifierMethod()
253+
{
254+
var parameters = new List<ParameterProvider>();
255+
var formatBuilder = new StringBuilder();
256+
var refCount = 0;
257+
258+
foreach (var segment in _requestPath)
259+
{
260+
bool isConstant = RequestPath.IsSegmentConstant(segment);
261+
262+
if (isConstant)
263+
{
264+
formatBuilder.Append($"/{segment}");
265+
}
266+
else
267+
{
268+
if (formatBuilder.Length > 0)
269+
{
270+
formatBuilder.Append('/');
271+
}
272+
var trimmed = RequestPath.TrimSegment(segment);
273+
var parameter = new ParameterProvider(trimmed, $"The {trimmed}", GetPathParameterType(trimmed));
274+
parameters.Add(parameter);
275+
formatBuilder.Append($"{{{refCount++}}}");
276+
}
277+
}
278+
279+
var signature = new MethodSignature(
280+
"CreateResourceIdentifier",
281+
$"Generate the resource identifier for this resource.",
282+
MethodSignatureModifiers.Public | MethodSignatureModifiers.Static,
283+
typeof(ResourceIdentifier),
284+
null,
285+
parameters);
286+
287+
var bodyStatements = new MethodBodyStatement[]
288+
{
289+
Declare("resourceId", typeof(string), new FormattableStringExpression(formatBuilder.ToString(), parameters.Select(p => p.AsExpression()).ToArray()), out var resourceIdVar),
290+
Return(New.Instance(typeof(ResourceIdentifier), resourceIdVar))
291+
};
292+
293+
return new MethodProvider(signature, bodyStatements, this);
294+
}
295+
228296
protected virtual ScopedApi<ResourceType> ResourceTypeExpression => _resourceTypeField.As<ResourceType>();
229297

230298
protected internal virtual CSharpType ResourceClientCSharpType => Type;
@@ -283,6 +351,7 @@ protected override MethodProvider[] BuildMethods()
283351

284352
var methods = new List<MethodProvider>
285353
{
354+
BuildCreateResourceIdentifierMethod(),
286355
BuildValidateResourceIdMethod()
287356
};
288357
methods.AddRange(operationMethods);

eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/test/Providers/ResourceClientProviderTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,34 @@ public void Verify_ConstructorWithId()
132132
var exptected = Helpers.GetExpectedFromFile();
133133
Assert.AreEqual(exptected, bodyStatements);
134134
}
135+
136+
[TestCase]
137+
public void Verify_CreateResourceIdentifierMethod()
138+
{
139+
MethodProvider createResourceIdentifierMethod = GetResourceClientProviderMethodByName("CreateResourceIdentifier");
140+
141+
var signature = createResourceIdentifierMethod.Signature;
142+
Assert.AreEqual(MethodSignatureModifiers.Public | MethodSignatureModifiers.Static, signature.Modifiers);
143+
Assert.AreEqual(typeof(ResourceIdentifier), signature.ReturnType?.FrameworkType);
144+
145+
Assert.AreEqual(3, signature.Parameters.Count);
146+
147+
var subscriptionIdParam = signature.Parameters.FirstOrDefault(p => p.Name == "subscriptionId");
148+
Assert.NotNull(subscriptionIdParam);
149+
Assert.AreEqual(typeof(string), subscriptionIdParam!.Type.FrameworkType);
150+
151+
var resourceGroupParam = signature.Parameters.FirstOrDefault(p => p.Name == "resourceGroupName");
152+
Assert.NotNull(resourceGroupParam);
153+
Assert.AreEqual(typeof(string), resourceGroupParam!.Type.FrameworkType);
154+
155+
var testNameParam = signature.Parameters.FirstOrDefault(p => p.Name == "testName");
156+
Assert.NotNull(testNameParam);
157+
Assert.AreEqual(typeof(string), testNameParam!.Type.FrameworkType);
158+
159+
var bodyStatements = createResourceIdentifierMethod.BodyStatements?.ToDisplayString();
160+
Assert.NotNull(bodyStatements);
161+
var exptected = Helpers.GetExpectedFromFile();
162+
Assert.AreEqual(exptected, bodyStatements);
163+
}
135164
}
136165
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
string resourceId = $"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/a/test/{testName}";
2+
return new global::Azure.Core.ResourceIdentifier(resourceId);

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

Lines changed: 10 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-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/FooSettingsResource.cs

Lines changed: 9 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-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/PrivateLinkResource.cs

Lines changed: 9 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)