-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement mgmt resource #47944
base: main
Are you sure you want to change the base?
Implement mgmt resource #47944
Conversation
live1206
commented
Jan 23, 2025
•
edited
Loading
edited
- Resolves Implement ResourceProvdier #47744
- Resolves Generate OperationSource in Azure plugin #48135
- Adjust to make MPG have similar file structure as DPG.
- Put CreateRequest methods from RestClientProvider to ClientProvider
- Implement client methods in Resource directly
- Omit RestClientProvider
- Add test to verify the generation
- Generated MgmtTypeSpec test can build now
- Leverage existing resource related decorators directly from TypeSpec
eng/packages/http-client-csharp/generator/Azure.Generator/test/TestHelpers/Configuration.json
Outdated
Show resolved
Hide resolved
…Mgmt/Models/RequestPath.cs Co-authored-by: Copilot <[email protected]>
|
||
namespace Azure.Generator.Providers | ||
{ | ||
internal class ResourceProvider : TypeProvider |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets call this ResourceClientProvider and see if we can inherit from ClientProvider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As offline discussed, we would like to reorg the rest clients, operation clients and resource clients. So that, the structure of mgmt resource clients are similar to the DPG clients.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted.
@@ -174,7 +174,7 @@ private ConstructorProvider BuildInitializationConstructor() | |||
skipApiVersionOverrideParameter, | |||
apiVersionOverrideValueParameter, | |||
}; | |||
var signature = new ConstructorSignature(Type, $"", MethodSignatureModifiers.Internal, parameters, null); | |||
var signature = new ConstructorSignature(Type, $"", MethodSignatureModifiers.Internal, parameters); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason this class is prefixed with Mgmt? I think Dpg and Mpg LROs work the same other than the type that gets used ArmOperation vs Operation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, it only contains logic for Mgmt, rename it to LongRunningOperationProvider
.
private Dictionary<string, OperationSet> _pathToOperationSetMap; | ||
private Dictionary<string, HashSet<OperationSet>> _resourceDataBySpecNameMap; | ||
//TODO: Move these to InputLibrary instead | ||
private Dictionary<RequestPath, OperationSet> _pathToOperationSetMap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a lot of code in here that is mgmt specific and it starts to get difficult to tell which is which have we considered a mgmt plugin?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most likely.
Given I am fully working with MPG now, I think it would be better to split them when we have more implementation on DPG, then we can clarify the things in common for both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#48481 to track the split, it will happen soon after this PR.
@@ -30,6 +31,9 @@ public class AzureClientPlugin : ScmCodeModelPlugin | |||
public override AzureOutputLibrary OutputLibrary => _azureOutputLibrary ??= new(); | |||
|
|||
internal ResourceDetection ResourceDetection { get; } = new(); | |||
internal ParentDetection ParentDetection { get; } = new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't your parent just a property on the client from typespec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For TypeSpec, we should be able to get the resource hierarchy directly via #48281.
The resource detection logic is mainly for swagger input.
For now, given getArmResources
API is not ready for integration. We use it as a temporary workaround.
Eventually, when we have the native way ready, we should get rid of them. And the resource detection should be part of the M4 output conversion to tspCodeModel.json in autorest.csharp.
Added TODO to remove them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As offline discussed, we need to clarify the contract of input types containing the resource hierarchy. And it should be an extensible way of holding it in Azure plugin only.
Then, we can put the temp workaround of resource detection close to the input instead of output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isolated Resource related logic in ResourceBuilder, so we have separated the temp workaround to a central place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we will need to have resource related logic in MPG emitter, such as call getArmResources
API to get the resource hierarchy. MGC doesn't need to provide any extension for this. MPG can have additional input models to hold them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is only needed for swagger input, have we considered having this in autorest.csharp only?
We can make it part of the codemodel -> input type conversion before we serialize out to the new plugin. That way the new plugin only has 1 way to do things which is to read the information directly from the codemodel.json file and it doesn't care how the data got there.
...ckages/http-client-csharp/generator/Azure.Generator/src/Providers/OperationSourceProvider.cs
Outdated
Show resolved
Hide resolved
eng/packages/http-client-csharp/generator/Azure.Generator/src/Providers/ResourceProvider.cs
Outdated
Show resolved
Hide resolved
TryGetApiVersion(ResourceType, out string fooApiVersion); | ||
_fooRestClient = new Foos(Pipeline, Endpoint, fooApiVersion); | ||
#if DEBUG | ||
global::MgmtTypeSpec.FooResource.ValidateResourceId(id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this not getting reduced because its in an if debug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, you were talking about the namespace not reduced.
Yes. I tried to remove the debug block, the namespace was simplified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
raise issue in MGC to fix this
we can replace it with Debug.Assert here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debug.Assert
can't be used here, it needs a bool condition input, but this method is void.
Use [Conditional("DEBUG")]
in the method signature instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
microsoft/typespec#6160 to track the issue in MGC
namespace Samples | ||
{ | ||
/// <summary></summary> | ||
public partial class ResponseTypeResource : global::Azure.ResourceManager.ArmResource |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets trim this down to at most 1 method validation at a time. Otherwise this test will be susceptible to any change in generation of any portion of a resource
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to write a method of TypeProvider?
As I can see, there is no public API from TypeProviderWriter. Do we want to expose such API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separated the tests for ResourceClientProvider.
And after exposing AzureOutputLibrary.CreateResourceCore, I realized that we need to simplify the creation of ResourceClientProvider. And it ties to the contract of Input types from MGC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isolated Resource related logic to ResourceBuilder.
...ackages/http-client-csharp/generator/Azure.Generator/test/Providers/ResourceProviderTests.cs
Outdated
Show resolved
Hide resolved
public IReadOnlyList<(InputClient ResourceClient, string RequestPath, bool IsSingleton, string RequestType, string SpecName)> BuildResourceClients() | ||
{ | ||
var result = new List<(InputClient ResourceClient, string RequestPath, bool IsSingleton, string RequestType, string SpecName)>(); | ||
var singletonDetection = new SingletonDetection(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be a readonly field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only usage I have now is this place, so that I make it a local variable for now.
We can make it a field if we have other usages later.
|
||
private Dictionary<string, (RequestPath ResourcePath, InputClient Client)> EnsureOperationsetMap() | ||
{ | ||
var pathToOperationSetMap = CategorizeClients(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok - looks like now we never generate operations by operation groups even in "rest operations" class, I like this a lot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well - no - we still may have the issue that our rest operations are still organized by input clients instead of resources.
But in the future if we could get the extra information about resources from typespec side, this should be resolved automatically therefore this is fine now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's say we will have resource across inputClients, we can change the output to Dictionary<string, (RequestPath ResourcePath, IReadOnlyList<InputClient> Clients)
.
The point is to leverage the existing clients without constructing extra objects.
|
||
Response result = await ListAsync(subscriptionId, resourceGroupName, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); | ||
return Response.FromValue((FooListResult)result, result); | ||
internal HttpMessage CreateCreateOrUpdateRequest(Guid subscriptionId, string resourceGroupName, string fooName, RequestContent content, RequestContext context) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so we made a step ahead that in the rest client, we no longer keep the protocol methods and convenience methods, and the resource class would just call the create request methods here.
Do we consider making another step ahead that we could remove this class, and move all these create request methods into the resource class, and put them in another partial class like FooResource.RestClient.cs
?
This could save us an object in resource class - now it has to construct this class to create the request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, I have removed Foos.RestClient.cs
and put its CreateRequest
methods to FoosRestOperations.cs
. I am fine with renaming it to Foos.RestClient.cs
. The thing is, we only keep the internal CreateRequest
methods.
/// <summary> | ||
/// Create a resource client provider | ||
/// </summary> | ||
/// <param name="inputClient"></param> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fill in comments with something meaningful
/// <param name="resourceType"></param> | ||
/// <param name="isSingleton"></param> | ||
/// <returns></returns> | ||
public virtual TypeProvider CreateResourceCore(InputClient inputClient, string requestPath, string schemaName, ModelProvider resourceData, string resourceType, bool isSingleton) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since this is returning a resource client should it be named CreateResourceClientCore?
Does it need to be public or can it be protected?
private FieldProvider _restClientField; | ||
private FieldProvider _resourcetypeField; | ||
|
||
public ResourceClientProvider(InputClient inputClient, string requestPath, string specName, ModelProvider resourceData, string resrouceType, bool isSingleton) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't specName part of inputClient? the name from the spec?
I think requestPath and resourceType are redundant or perhaps resourceType is redundant with the resourceData?
|
||
namespace Azure.Generator | ||
{ | ||
internal class ResourceBuilder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is a resource builder and how does it fit into the new design of Providers?
@@ -27,7 +27,7 @@ internal class ResourceVisitor : ScmLibraryVisitor | |||
|
|||
private static void TransformResource(TypeProvider type) | |||
{ | |||
if (type is ModelProvider && AzureClientPlugin.Instance.OutputLibrary.IsResource(type.Name)) | |||
if (type is ModelProvider && AzureClientPlugin.Instance.ResourceBuilder.IsResource(type.Name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't we type check here if its a ResourceDataProvider which inherits from ModelProvider?
|
||
namespace Azure.Generator.Utilities | ||
{ | ||
internal class SingletonDetection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure these Detection
classes fit into the new design. It feels like we are pulling in autorest.csharp concepts.
_client = client; | ||
} | ||
|
||
global::Samples.ResponseTypeResource global::Azure.Core.IOperationSource<global::Samples.ResponseTypeResource>.CreateResult(global::Azure.Response response, global::System.Threading.CancellationToken cancellationToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend trimming down these test files to single method only to minimize test churn when we tweak internal implementations.