-
Notifications
You must be signed in to change notification settings - Fork 169
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
Review designs to minimize breaking changes and implement the solution - overload for parameter change #1630
Comments
Parameters Overload DesignPrerequisites
Axioms
Use CaseOverall
Case 1
Case 2
Case 3
Case 4
Case 5
Case 6
Algorithm
// sort parameter lists including parameter overloads (change existing codes)
foreach (var overload in operation.SignatureParametersOverloads)
{
// move required parameters to the beginning, in order
}
// implement in maybe ParameterOverloadsChooser.cs?
var parameterLists = GetOverloadParameterLists(...).Append(parameters).Sort(//by length, in order);
var parameterListBranches = new HashSet<IList<IList<Parameter>>>(); // divide parameter lists by sequence, see rule #6 above
while (parameterLists.Count > 0)
{
var branch = new List<List<Parameer>>();
branch.Add(parameterLists.Pop()); // push the shortest to new branch
for (var i = 0; i < parameterLists.Count; i ++)
{
if (IsSubsequence(branch.Last(), parameterLists[i])
{
// remove from Parameter List
// add to branch
}
else
{
//add to newParameterLists
}
}
parameterListBranches.Add(branch);
parameterLists = newParameterLists;
}
foreach (var branch in parameterListBranches)
{
// remove sequence having the same length in each branch
for (var i = 0; i <branch.Count - 1; i ++)
{
if (branch[i].Count == branch[i+1].Count)
{
//remove branch[i]
}
}
// make all parameters except those in last list required
for (var i = 0; i <branch.Count - 1; i ++)
{
// make parameters required
}
} |
About Property BagCriteria to provide property bagProvide a property bag for optional parameters (excluding body parameters) when
Reasons behindIn track2 mgmt SDK, there won't be many required parameters in each method. Providing property bag for optional parameters should be sufficient to control the total number of parameters in each method, because:
Before ( FileShare shareSnapshot = storageMgmtClient.FileShares.Get(rgName, accountName, shareName, "stats", shareSnapshot1.SnapshotTime.Value.ToUniversalTime().ToString("o")); After ( var resourceGroup = await ArmClient.GetDefaultSubscription().GetResourceGroups().GetAsync(rgName);
var storageAccount = await resourceGroup.GetStorageAccounts().GetAsync(accountName);
var fileService = await _storageAccount.GetFileService().GetAsync();
FileShare shareSnapshot = await _fileService.GetFileShares().GetAsync(shareName, "stats", shareSnapshot1.Data.SnapshotTime.Value.UtcDateTime.ToString("o"));
Before ( public async virtual Task<DeploymentWhatIfOperation> WhatIfAsync(bool waitForCompletion, string location, DeploymentWhatIfProperties properties, CancellationToken cancellationToken = default) After public async virtual Task<DeploymentWhatIfOperation> WhatIfAsync(bool waitForCompletion, ScopedDeploymentWhatIf parameters, CancellationToken cancellationToken = default) How toBefore we split parameter lists into branches and do the filtering, for parameter lists which triggers property bag wrapping, create a new parameter list for it. That is For example:
[Note]:
Options classIt's named after ExamplesIn public virtual Pageable<FileShare> GetAll(int? maxpagesize = null, string filter = null, string expand = null, CancellationToken cancellationToken = default) It will be transformed into the following signature: public virtual Pageable<FileShare> GetAll(FileShareCollectionGetAllOptions? options, CancellationToken cancellationToken = default) where the public partial struct FileShareCollectionGetAllOptions
{
public int? maxpagesize = null;
public string filter = null;
public string expand = null;
} |
The above design from @archerzz is generally good to me. |
Hi @archerzz, this is a great discussion -- thank you for organizing this! I have a few comments from the data plane side:
In v1, the client is like this: class Client {
Response DoThing(string a, string b, RequestContext context = default);
} If class Client {
Response DoThing(string b, string a=default, RequestContext context = default);
} Anyone caller whose application was doing I have suggested the following guidance for data plane generated code: Version-Aware Generated Methods: Optional Parameters. I am interested to hear your thoughts on this slightly-modified design! |
@annelo-msft Agree with your comments. For the mgmt plane, the only extra work item is the property bag which can be implemented separately. Let's move parameter overload discussion to the gist you've pasted. |
How to keep the code compatibility when implementing property bag(Updated based on offline discussion on April 29, 2022) Currently We are trying to implement the
As the RP is still in preview, so the breaking changes are allowed, so we can just change the method signature, below is an example in Beforepublic virtual async Task<Response<MetricsResponse>> GetLogAnalyticsMetricsAsync(IEnumerable<LogMetric> metrics, DateTimeOffset dateTimeBegin, DateTimeOffset dateTimeEnd, LogMetricsGranularity granularity, IEnumerable<string> customDomains, IEnumerable<string> protocols, IEnumerable<LogMetricsGroupBy> groupBy = null, IEnumerable<string> continents = null, IEnumerable<string> countryOrRegions = null, CancellationToken cancellationToken = default){} Afterpublic virtual async Task<Response<MetricsResponse>> GetLogAnalyticsMetricsAsync(IEnumerable<LogMetric> metrics, DateTimeOffset dateTimeBegin, DateTimeOffset dateTimeEnd, LogMetricsGranularity granularity, IEnumerable<string> customDomains, IEnumerable<string> protocols, LogAnalyticsGetLogAnalyticsMetricsOptions options, CancellationToken cancellationToken = default){}
As this RP is already GAed, we can't simply delete the old method and add the new method, and because we haven't implemented parameter overloading, so the customization is needed. For now, I think we might have two scenarios here and in the following I will use the following method in
Scenario 1 is that We started with the method with one optional parameter, which then became two, three and up to multiple. In this scenario we need to consider the history of this method and provide the fully parameter overloads when we want to turn the optional parameters that are greater than two into property bag. Before// v1
public virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand = null, CancellationToken cancellationToken = default){}
// v2
public virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand = null, bool? recurse = null, CancellationToken cancellationToken = default){}
// v3
public virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand = null, bool? recurse = null, string filter = null, CancellationToken cancellationToken = default){}
// v4
public virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand = null, bool? recurse = null, string filter = null, string cacheControl = null, CancellationToken cancellationToken = default){} Afterpublic virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupGetOptions options, CancellationToken cancellationToken = default){}
// Customization
public virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand = null, bool? recurse = null, string filter = null, string cacheControl = null, CancellationToken cancellationToken = default){}
public virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand, bool? recurse, string filter, CancellationToken cancellationToken){}
public virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand, bool? recurse, CancellationToken cancellationToken){}
public virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand, CancellationToken cancellationToken){}
Scenario 2 is that we only start with one method in the history and it meets the criteria of the property bag. In this case, we keep the new generated method same as the scenario 1 and only need to add one customized method. Beforepublic virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand = null, bool? recurse = null, string filter = null, string cacheControl = null, CancellationToken cancellationToken = default){} Afterpublic virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupGetOptions options, CancellationToken cancellationToken = default){}
// Customization
public virtual async Task<Response<ManagementGroupResource>> GetAsync(string groupId, ManagementGroupExpandType? expand = null, bool? recurse = null, string filter = null, string cacheControl = null, CancellationToken cancellationToken = default){} |
@Yao725 Thanks for the update. So the conclusion is that:
And I think we cover the following use cases without ambiguity:
|
Need align with DPG |
We have new solution for overload, #3596 |
Parameter ordering such as moving CancellationToken from the end to the same spot it was.
does require us to know what position it was in for the last iteration
Implementation and design
The text was updated successfully, but these errors were encountered: