Skip to content
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

Add lookup mechanism for ID to apiversion #19437

Merged
merged 48 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
bd06bf1
WIP: updates
bquantump Mar 8, 2021
a11a769
WIP: updates
bquantump Mar 10, 2021
71dabee
Merge branch 'feature/mgmt-track2' of https://github.com/Azure/azure-…
bquantump Mar 10, 2021
b72173b
Update test
bquantump Mar 10, 2021
79cb491
Update ResourceListOperationsTest.cs
bquantump Mar 11, 2021
f4162e3
Update CreateSingleVmExample.cs
bquantump Mar 11, 2021
ff8d333
Update CreateSingleVmExample.cs
bquantump Mar 11, 2021
9fa1e0c
Update CreateSingleVmExample.cs
bquantump Mar 11, 2021
a9da33b
Update CreateSingleVmExample.cs
bquantump Mar 11, 2021
e35f07c
WIP: Updates
bquantump Mar 11, 2021
d884f56
Add extra line
bquantump Mar 11, 2021
fcc2aab
Update
bquantump Mar 11, 2021
4f2dae5
WIP:
bquantump Mar 11, 2021
aee7547
WIP: updates
bquantump Mar 15, 2021
6b7ba0f
Merge branch 'mgmt-track2' of https://github.com/AME-Redmond/azure-sd…
bquantump Mar 15, 2021
db3b7f4
WIp: updates
bquantump Mar 15, 2021
e5fd067
Remove API version extensions
bquantump Mar 16, 2021
93da13a
WIP: updates
bquantump Mar 16, 2021
baacd99
Updates
bquantump Mar 16, 2021
c398fb8
Update test
bquantump Mar 16, 2021
8e5f11d
WIP updates
bquantump Mar 16, 2021
2f47da4
Merge branch 'feature/mgmt-track2' into stevens_5117
bquantump Mar 16, 2021
757015c
Updates
bquantump Mar 17, 2021
2bc016e
WIP
bquantump Mar 17, 2021
6bdf327
Remove blank lines
bquantump Mar 17, 2021
45c23e7
Update filtering
bquantump Mar 17, 2021
af3d5e6
Updates
bquantump Mar 17, 2021
5ec195c
Update
bquantump Mar 18, 2021
847e593
WIP: updates
bquantump Mar 18, 2021
d76be3f
WIP: updates
bquantump Mar 18, 2021
f267bf3
Add space
bquantump Mar 18, 2021
26f4385
Remove debug
bquantump Mar 18, 2021
9e26522
Updates
bquantump Mar 18, 2021
fcba084
WIP: updates
bquantump Mar 22, 2021
3dc030d
WIP
bquantump Mar 22, 2021
10f6ad0
Change the accessbility to virtual for Resource.Id
YalinLi0312 Mar 24, 2021
b8291aa
WIP: updates
bquantump Mar 25, 2021
db74f22
WIP Updates
bquantump Mar 25, 2021
ff09cb4
Merge branch 'mgmt-track2' of https://github.com/AME-Redmond/azure-sd…
bquantump Mar 25, 2021
428f6bc
WIP updates
bquantump Mar 26, 2021
2013b78
WIP
bquantump Mar 26, 2021
6a2dec1
WIP
bquantump Mar 26, 2021
9ea1997
WIP:
bquantump Mar 26, 2021
00b5d31
revert
bquantump Mar 26, 2021
431479a
WIP: updates
bquantump Mar 26, 2021
69a6f6b
WIP: updates
bquantump Mar 26, 2021
804515d
WIP: updated
bquantump Mar 26, 2021
b01050c
WIP
bquantump Mar 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions sdk/resourcemanager/Azure.ResourceManager.Core/src/ApiVersions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using Azure.ResourceManager.Resources;
using System.Reflection;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Threading;

namespace Azure.ResourceManager.Core
{
/// <summary>
/// A class representing Azure resource manager client options.
/// </summary>
public class ApiVersions
{
/// <summary>
/// Initializes a new instance of the <see cref="ApiVersions"/> class.
/// </summary>
internal ApiVersions(AzureResourceManagerClientOptions clientOptions)
{
BuildApiTable(clientOptions);
}

private Dictionary<string, PropertyWrapper> _loadedResourceToApiVersions = new Dictionary<string, PropertyWrapper>();
private Dictionary<string, string> _nonLoadedResourceToApiVersion = new Dictionary<string, string>();

private void BuildApiTable(AzureResourceManagerClientOptions clientOptions)
{
var methods = GetExtensionMethods();
foreach (var method in methods)
{
if (method.Name.EndsWith("RestApiVersions", StringComparison.Ordinal))
{
var apiObject = method.Invoke(null, new object[] { clientOptions });
var properties = apiObject.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetValue(apiObject) is ApiVersionsBase propVal)
{
var key = propVal.ResourceType;
_loadedResourceToApiVersions.Add(key.ToString(), new PropertyWrapper(prop, apiObject));
}
}
}
}
}

private static IEnumerable<MethodInfo> GetExtensionMethods()
{
// See TODO ADO #5692
var results =
m-nash marked this conversation as resolved.
Show resolved Hide resolved
from assembly in AppDomain.CurrentDomain.GetAssemblies()
where assembly.GetName().ToString().StartsWith("Azure.", StringComparison.Ordinal ) || assembly.GetName().ToString().StartsWith("Proto.", StringComparison.Ordinal)
from type in assembly.GetTypes()
where type.IsSealed && !type.IsGenericType && !type.IsNested && type.Name.Equals("AzureResourceManagerClientOptionsExtensions", StringComparison.Ordinal)
from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public)
where method.IsDefined(typeof(ExtensionAttribute), false)
where method.GetParameters()[0].ParameterType == typeof(AzureResourceManagerClientOptions)
select method;
return results;
}

internal string LoadApiVersion(ProvidersOperations providers, ResourceIdentifier id, CancellationToken cancellationToken)
{
var results = providers.Get(id.Type.Namespace, null, cancellationToken);
bquantump marked this conversation as resolved.
Show resolved Hide resolved
foreach (var type in results.Value.ResourceTypes)
{
if (type.ResourceType.Equals(id.Type.Type))
{
_nonLoadedResourceToApiVersion.Add(id.Type.ToString(), type.ApiVersions[0]);
return type.ApiVersions[0];
}
}
return string.Empty;
bquantump marked this conversation as resolved.
Show resolved Hide resolved
bquantump marked this conversation as resolved.
Show resolved Hide resolved
}

internal async Task<string> LoadApiVersionAsync(ProvidersOperations providers, ResourceIdentifier id, CancellationToken cancellationToken)
{
var results = await providers.GetAsync(id.Type.Namespace, null, cancellationToken).ConfigureAwait(false);
foreach (var type in results.Value.ResourceTypes)
{
if (type.ResourceType.Equals(id.Type.Type))
{
_nonLoadedResourceToApiVersion.Add(id.Type.ToString(), type.ApiVersions[0]);
return type.ApiVersions[0];
}
}
return string.Empty;
}

/// <summary>
/// Gets the API version give a resource ID if it exist, else will return null.
/// </summary>
/// <returns> API version string. </returns>
bquantump marked this conversation as resolved.
Show resolved Hide resolved
public string TryGetApiVersion(string resourceId)
bquantump marked this conversation as resolved.
Show resolved Hide resolved
{
PropertyWrapper propertyWrapper;
if (_loadedResourceToApiVersions.TryGetValue(resourceId, out propertyWrapper))
{
return propertyWrapper.Info.GetValue(propertyWrapper.PropertyObject).ToString();
}

string val;
if (_nonLoadedResourceToApiVersion.TryGetValue(resourceId, out val))
{
return val;
}
return null;
}

/// <summary>
/// Set the API version given a resource ID
/// </summary>
public void SetApiVersion(string resourceId, string apiVersion)
{
PropertyWrapper propertyWrapper;
if (_loadedResourceToApiVersions.TryGetValue(resourceId, out propertyWrapper))
{
Type type = propertyWrapper.Info.PropertyType;
ConstructorInfo ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string) }, null);
propertyWrapper.Info.SetValue(propertyWrapper.PropertyObject, ctor.Invoke(new object[] { apiVersion }));
}
else
{
_nonLoadedResourceToApiVersion[resourceId] = apiVersion;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ public static implicit operator string(ApiVersionsBase version)
return version._value;
}

/// <summary>
/// Implicit operator to convert ApiVersionsBase to string.
/// </summary>
/// <returns> API version value. </returns>
public virtual ResourceType ResourceType {get; }

/// <summary>
/// Overrides == operator for comparing ApiVersionsBase object with string object.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Linq;
using System.Runtime.CompilerServices;

namespace Azure.ResourceManager.Core
{
Expand All @@ -17,6 +20,11 @@ public sealed class AzureResourceManagerClientOptions : ClientOptions
{
private readonly ConcurrentDictionary<Type, object> _overrides = new ConcurrentDictionary<Type, object>();

/// <summary>
/// Gets the ApiVersions object
/// </summary>
public ApiVersions ApiVersions { get; }

/// <summary>
/// Initializes a new instance of the <see cref="AzureResourceManagerClientOptions"/> class.
/// </summary>
Expand Down Expand Up @@ -50,6 +58,7 @@ internal AzureResourceManagerClientOptions(LocationData defaultLocation, AzureRe
Copy(other);
DefaultLocation = defaultLocation;
ApiVersionOverrides = new Dictionary<string, string>();
ApiVersions = new ApiVersions(this);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ namespace Azure.ResourceManager.Core
/// </summary>
public class GenericResourceOperations : ResourceOperationsBase<GenericResource>, ITaggableResource<GenericResource>, IDeletableResource
{
private readonly string _apiVersion;

private string _apiVersion {get; set;}
/// <summary>
/// Initializes a new instance of the <see cref="GenericResourceOperations"/> class.
/// </summary>
Expand All @@ -24,7 +24,7 @@ public class GenericResourceOperations : ResourceOperationsBase<GenericResource>
internal GenericResourceOperations(ResourceOperationsBase operations, ResourceIdentifier id)
: base(operations, id)
{
_apiVersion = "BAD VALUE";
_apiVersion = ClientOptions.ApiVersions.TryGetApiVersion(id.Type.ToString());
}

/// <inheritdoc/>
Expand All @@ -36,6 +36,12 @@ internal GenericResourceOperations(ResourceOperationsBase operations, ResourceId
Credential,
ClientOptions.Convert<ResourcesManagementClientOptions>()).Resources;

private ProvidersOperations ProviderOperations => new ResourcesManagementClient(
BaseUri,
Id.Subscription,
Credential,
ClientOptions.Convert<ResourcesManagementClientOptions>()).Providers;

/// <summary>
/// Delete the resource.
/// </summary>
Expand All @@ -53,6 +59,7 @@ public ArmResponse<Response> Delete(CancellationToken cancellationToken = defaul
/// <returns> A <see cref="Task"/> that on completion returns the status of the delete operation. </returns>
public async Task<ArmResponse<Response>> DeleteAsync(CancellationToken cancellationToken = default)
{
_apiVersion ??= await ClientOptions.ApiVersions.LoadApiVersionAsync(ProviderOperations, Id, cancellationToken).ConfigureAwait(false);
var operation = await Operations.StartDeleteByIdAsync(Id, _apiVersion, cancellationToken).ConfigureAwait(false);
var result = await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false);
return new ArmResponse(result);
Expand All @@ -69,6 +76,7 @@ public async Task<ArmResponse<Response>> DeleteAsync(CancellationToken cancellat
/// </remarks>
public ArmOperation<Response> StartDelete(CancellationToken cancellationToken = default)
{
_apiVersion ??= ClientOptions.ApiVersions.LoadApiVersion(ProviderOperations, Id, cancellationToken);
return new ArmVoidOperation(Operations.StartDeleteById(Id, _apiVersion, cancellationToken));
}

Expand All @@ -85,6 +93,7 @@ public ArmOperation<Response> StartDelete(CancellationToken cancellationToken =
/// </remarks>
public async Task<ArmOperation<Response>> StartDeleteAsync(CancellationToken cancellationToken = default)
{
_apiVersion ??= await ClientOptions.ApiVersions.LoadApiVersionAsync(ProviderOperations, Id, cancellationToken).ConfigureAwait(false);
var operation = await Operations.StartDeleteByIdAsync(Id, _apiVersion, cancellationToken).ConfigureAwait(false);
return new ArmVoidOperation(operation);
}
Expand All @@ -93,7 +102,7 @@ public async Task<ArmOperation<Response>> StartDeleteAsync(CancellationToken can
public ArmResponse<GenericResource> AddTag(string key, string value, CancellationToken cancellationToken = default)
{
GenericResource resource = GetResource();

_apiVersion ??= ClientOptions.ApiVersions.LoadApiVersion(ProviderOperations, Id, cancellationToken);
// Potential optimization on tags set, remove NOOP to bypass the call.
resource.Data.Tags[key] = value;
return new PhArmResponse<GenericResource, ResourceManager.Resources.Models.GenericResource>(
Expand All @@ -104,6 +113,7 @@ public ArmResponse<GenericResource> AddTag(string key, string value, Cancellatio
/// <inheritdoc/>
public async Task<ArmResponse<GenericResource>> AddTagAsync(string key, string value, CancellationToken cancellationToken = default)
{
_apiVersion ??= await ClientOptions.ApiVersions.LoadApiVersionAsync(ProviderOperations, Id, cancellationToken).ConfigureAwait(false);
GenericResource resource = await GetResourceAsync(cancellationToken).ConfigureAwait(false);
resource.Data.Tags[key] = value;
var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false);
Expand All @@ -117,6 +127,7 @@ public ArmOperation<GenericResource> StartAddTag(string key, string value, Cance
{
GenericResource resource = GetResource();
resource.Data.Tags[key] = value;
_apiVersion ??= ClientOptions.ApiVersions.LoadApiVersion(ProviderOperations, Id, cancellationToken);
return new PhArmOperation<GenericResource, ResourceManager.Resources.Models.GenericResource>(
Operations.StartUpdateById(Id, _apiVersion, resource.Data, cancellationToken).WaitForCompletionAsync(cancellationToken).EnsureCompleted(),
v => new GenericResource(this, new GenericResourceData(v)));
Expand All @@ -125,6 +136,7 @@ public ArmOperation<GenericResource> StartAddTag(string key, string value, Cance
/// <inheritdoc/>
public async Task<ArmOperation<GenericResource>> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default)
{
_apiVersion ??= await ClientOptions.ApiVersions.LoadApiVersionAsync(ProviderOperations, Id, cancellationToken).ConfigureAwait(false);
GenericResource resource = await GetResourceAsync(cancellationToken).ConfigureAwait(false);
resource.Data.Tags[key] = value;
var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false);
Expand All @@ -136,6 +148,7 @@ await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false),
/// <inheritdoc/>
public override ArmResponse<GenericResource> Get(CancellationToken cancellationToken = default)
{
_apiVersion ??= ClientOptions.ApiVersions.LoadApiVersion(ProviderOperations, Id, cancellationToken);
return new PhArmResponse<GenericResource, ResourceManager.Resources.Models.GenericResource>(
Operations.GetById(Id, _apiVersion, cancellationToken),
v => new GenericResource(this, new GenericResourceData(v)));
Expand All @@ -144,6 +157,7 @@ public override ArmResponse<GenericResource> Get(CancellationToken cancellationT
/// <inheritdoc/>
public override async Task<ArmResponse<GenericResource>> GetAsync(CancellationToken cancellationToken = default)
{
_apiVersion ??= await ClientOptions.ApiVersions.LoadApiVersionAsync(ProviderOperations, Id, cancellationToken).ConfigureAwait(false);
return new PhArmResponse<GenericResource, ResourceManager.Resources.Models.GenericResource>(
await Operations.GetByIdAsync(Id, _apiVersion, cancellationToken).ConfigureAwait(false),
v => new GenericResource(this, new GenericResourceData(v)));
Expand All @@ -163,6 +177,7 @@ protected override void Validate(ResourceIdentifier identifier)
public ArmResponse<GenericResource> SetTags(IDictionary<string, string> tags, CancellationToken cancellationToken = default)
{
GenericResource resource = GetResource();
_apiVersion ??= ClientOptions.ApiVersions.LoadApiVersion(ProviderOperations, Id, cancellationToken);
resource.Data.Tags.ReplaceWith(tags);
return new PhArmResponse<GenericResource, ResourceManager.Resources.Models.GenericResource>(
Operations.StartUpdateById(Id, _apiVersion, resource.Data, cancellationToken).WaitForCompletionAsync(cancellationToken).EnsureCompleted(),
Expand All @@ -172,6 +187,7 @@ public ArmResponse<GenericResource> SetTags(IDictionary<string, string> tags, Ca
/// <inheritdoc/>
public async Task<ArmResponse<GenericResource>> SetTagsAsync(IDictionary<string, string> tags, CancellationToken cancellationToken = default)
{
_apiVersion ??= await ClientOptions.ApiVersions.LoadApiVersionAsync(ProviderOperations, Id, cancellationToken).ConfigureAwait(false);
GenericResource resource = await GetResourceAsync(cancellationToken).ConfigureAwait(false);
resource.Data.Tags.ReplaceWith(tags);
var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false);
Expand All @@ -184,6 +200,7 @@ await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false),
public ArmOperation<GenericResource> StartSetTags(IDictionary<string, string> tags, CancellationToken cancellationToken = default)
{
GenericResource resource = GetResource();
_apiVersion ??= ClientOptions.ApiVersions.LoadApiVersion(ProviderOperations, Id, cancellationToken);
resource.Data.Tags.ReplaceWith(tags);
return new PhArmOperation<GenericResource, ResourceManager.Resources.Models.GenericResource>(
Operations.StartUpdateById(Id, _apiVersion, resource.Data, cancellationToken).WaitForCompletionAsync(cancellationToken).EnsureCompleted(),
Expand All @@ -193,6 +210,7 @@ public ArmOperation<GenericResource> StartSetTags(IDictionary<string, string> ta
/// <inheritdoc/>
public async Task<ArmOperation<GenericResource>> StartSetTagsAsync(IDictionary<string, string> tags, CancellationToken cancellationToken = default)
{
_apiVersion ??= await ClientOptions.ApiVersions.LoadApiVersionAsync(ProviderOperations, Id, cancellationToken).ConfigureAwait(false);
GenericResource resource = await GetResourceAsync(cancellationToken).ConfigureAwait(false);
resource.Data.Tags.ReplaceWith(tags);
var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false);
Expand All @@ -205,6 +223,7 @@ await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false),
public ArmResponse<GenericResource> RemoveTag(string key, CancellationToken cancellationToken = default)
{
GenericResource resource = GetResource();
_apiVersion ??= ClientOptions.ApiVersions.LoadApiVersion(ProviderOperations, Id, cancellationToken);
resource.Data.Tags.Remove(key);
return new PhArmResponse<GenericResource, ResourceManager.Resources.Models.GenericResource>(
Operations.StartUpdateById(Id, _apiVersion, resource.Data, cancellationToken).WaitForCompletionAsync(cancellationToken).EnsureCompleted(),
Expand All @@ -214,6 +233,7 @@ public ArmResponse<GenericResource> RemoveTag(string key, CancellationToken canc
/// <inheritdoc/>
public async Task<ArmResponse<GenericResource>> RemoveTagAsync(string key, CancellationToken cancellationToken = default)
{
_apiVersion ??= await ClientOptions.ApiVersions.LoadApiVersionAsync(ProviderOperations, Id, cancellationToken).ConfigureAwait(false);
GenericResource resource = await GetResourceAsync(cancellationToken).ConfigureAwait(false);
resource.Data.Tags.Remove(key);
var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false);
Expand All @@ -226,6 +246,7 @@ await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false),
public ArmOperation<GenericResource> StartRemoveTag(string key, CancellationToken cancellationToken = default)
{
GenericResource resource = GetResource();
_apiVersion ??= ClientOptions.ApiVersions.LoadApiVersion(ProviderOperations, Id, cancellationToken);
resource.Data.Tags.Remove(key);
return new PhArmOperation<GenericResource, ResourceManager.Resources.Models.GenericResource>(
Operations.StartUpdateById(Id, _apiVersion, resource.Data, cancellationToken).WaitForCompletionAsync(cancellationToken).EnsureCompleted(),
Expand All @@ -235,6 +256,7 @@ public ArmOperation<GenericResource> StartRemoveTag(string key, CancellationToke
/// <inheritdoc/>
public async Task<ArmOperation<GenericResource>> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default)
{
_apiVersion ??= await ClientOptions.ApiVersions.LoadApiVersionAsync(ProviderOperations, Id, cancellationToken).ConfigureAwait(false);
GenericResource resource = await GetResourceAsync(cancellationToken).ConfigureAwait(false);
resource.Data.Tags.Remove(key);
var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false);
Expand Down
Loading