Files
Dan Moseley 042a9d2b2c Reorganize plugins into domain-specific groupings (#274)
* Reorganize plugins per agreed structure (#271)

Split the dotnet plugin into domain-specific plugins per discussion
in #271:

- dotnet: narrowed to common everyday C#/.NET coding tasks
- dotnet-diag: perf investigations, debugging, incident analysis
- dotnet-data: data access and Entity Framework
- dotnet-aspnet, dotnet-wpf, dotnet-winforms, dotnet-maui: scaffolded
  empty to convey intent for future framework-specific skills

Moved skills, tests, and CODEOWNERS entries accordingly.
No file content was changed; all skill/test moves are pure renames.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Improve dotnet-diag and dotnet-winforms descriptions

- dotnet-diag: add '.NET' qualifier to clarify scope
- dotnet-winforms: use official 'Windows Forms (WinForms)' name

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add .NET qualifier to dotnet-data description

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove empty plugins; list them in CONTRIBUTING.md as future options

Remove the four empty placeholder plugins (dotnet-aspnet, dotnet-wpf,
dotnet-winforms, dotnet-maui) that had only plugin.json and no skills.

Add a 'Plugin organization' section to CONTRIBUTING.md that describes the
domain-specific plugin groupings introduced in this PR and lists the four
removed plugin names as reserved candidates for future contributions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Create dotnet-upgrade plugin with migration/upgrade skills

Move migrate-nullable-references, thread-abort-migration, and
dotnet-aot-compat from dotnet to the new dotnet-upgrade plugin.

Update CODEOWNERS paths (same owners), CONTRIBUTING.md plugin table,
and fix the thread-abort-migration SKILL.md link in CONTRIBUTING.md.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-06 21:01:52 -08:00

316 lines
17 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Core.Pipeline;
using Azure.ResourceManager.ManagementGroups;
using Azure.ResourceManager.Resources;
using Azure.ResourceManager.Resources.Models;
namespace Azure.ResourceManager
{
/// <summary>
/// The entry point for all ARM clients.
/// </summary>
public partial class ArmClient
{
private TenantResource _tenant;
private SubscriptionResource _defaultSubscription;
private readonly ClientDiagnostics _subscriptionClientDiagnostics;
private bool? _canUseTagResource;
internal virtual Dictionary<ResourceType, string> ApiVersionOverrides { get; } = new Dictionary<ResourceType, string>();
internal ConcurrentDictionary<string, Dictionary<string, string>> ResourceApiVersionCache { get; } = new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
internal ConcurrentDictionary<string, string> NamespaceVersionCache { get; } = new ConcurrentDictionary<string, string>();
/// <summary>
/// Initializes a new instance of the <see cref="ArmClient"/> class for mocking.
/// </summary>
protected ArmClient()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ArmClient"/> class.
/// </summary>
/// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
/// <exception cref="ArgumentNullException"> If <see cref="TokenCredential"/> is null. </exception>
#pragma warning disable AZC0007 // DO provide a minimal constructor that takes only the parameters required to connect to the service.
public ArmClient(TokenCredential credential) : this(credential, default, default)
#pragma warning restore AZC0007 // DO provide a minimal constructor that takes only the parameters required to connect to the service.
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ArmClient"/> class.
/// </summary>
/// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
/// <param name="defaultSubscriptionId"> The id of the default Azure subscription. </param>
/// <exception cref="ArgumentNullException"> If <see cref="TokenCredential"/> is null. </exception>
public ArmClient(TokenCredential credential, string defaultSubscriptionId) : this(credential, defaultSubscriptionId, default)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ArmClient"/> class.
/// </summary>
/// <param name="credential"> A credential used to authenticate to an Azure Service. </param>
/// <param name="defaultSubscriptionId"> The id of the default Azure subscription. </param>
/// <param name="options"> The client parameters to use in these operations. </param>
/// <exception cref="ArgumentNullException"> If <see cref="TokenCredential"/> is null. </exception>
public ArmClient(TokenCredential credential, string defaultSubscriptionId, ArmClientOptions options)
{
Argument.AssertNotNull(credential, nameof(credential));
options ??= new ArmClientOptions();
ArmEnvironment environment = options.Environment.HasValue ? options.Environment.Value : ArmEnvironment.AzurePublicCloud;
Argument.AssertNotNull(environment.Endpoint, nameof(environment.Endpoint));
Endpoint = environment.Endpoint;
Pipeline = HttpPipelineBuilder.Build(options, new BearerTokenAuthenticationPolicy(credential, environment.DefaultScope));
Diagnostics = options.Diagnostics;
_subscriptionClientDiagnostics = new ClientDiagnostics("Azure.ResourceManager", SubscriptionResource.ResourceType.Namespace, Diagnostics);
CopyApiVersionOverrides(options);
_tenant = new TenantResource(this);
_defaultSubscription = string.IsNullOrWhiteSpace(defaultSubscriptionId) ? null :
new SubscriptionResource(this, SubscriptionResource.CreateResourceIdentifier(defaultSubscriptionId));
}
internal virtual bool CanUseTagResource(CancellationToken cancellationToken = default)
{
if (_canUseTagResource == null)
{
var tagRp = GetDefaultSubscription(cancellationToken).GetResourceProvider(TagResource.ResourceType.Namespace, cancellationToken: cancellationToken);
_canUseTagResource = tagRp.Value.Data.ResourceTypes.Any(rp => rp.ResourceType == TagResource.ResourceType.Type);
}
return _canUseTagResource.Value;
}
internal virtual async Task<bool> CanUseTagResourceAsync(CancellationToken cancellationToken = default)
{
if (_canUseTagResource == null)
{
var tagRp = await GetDefaultSubscription(cancellationToken).GetResourceProviderAsync(TagResource.ResourceType.Namespace, cancellationToken: cancellationToken).ConfigureAwait(false);
_canUseTagResource = tagRp.Value.Data.ResourceTypes.Any(rp => rp.ResourceType == TagResource.ResourceType.Type);
}
return _canUseTagResource.Value;
}
private void CopyApiVersionOverrides(ArmClientOptions options)
{
foreach (var keyValuePair in options.ResourceApiVersionOverrides)
{
ApiVersionOverrides.Add(keyValuePair.Key, keyValuePair.Value);
}
}
/// <summary>
/// Gets the api version override if it has been set for the current client options.
/// </summary>
/// <param name="resourceType"> The resource type to get the version for. </param>
/// <param name="apiVersion"> The api version to variable to set. </param>
internal virtual bool TryGetApiVersion(ResourceType resourceType, out string apiVersion)
{
return ApiVersionOverrides.TryGetValue(resourceType, out apiVersion);
}
/// <summary>
/// Gets the diagnostic options used for this client.
/// </summary>
internal virtual DiagnosticsOptions Diagnostics { get; }
/// <summary>
/// Gets the base URI of the service.
/// </summary>
internal virtual Uri Endpoint { get; private set; }
/// <summary>
/// Gets the HTTP pipeline.
/// </summary>
internal virtual HttpPipeline Pipeline { get; private set; }
/// <summary>
/// Gets the Azure subscriptions.
/// </summary>
/// <returns> Subscription collection. </returns>
public virtual SubscriptionCollection GetSubscriptions() => _tenant.GetSubscriptions();
/// <summary>
/// Gets the tenants.
/// </summary>
/// <returns> Tenant collection. </returns>
public virtual TenantCollection GetTenants()
{
return new TenantCollection(this);
}
/// <summary>
/// Gets the default subscription.
/// </summary>
/// <returns> Resource operations of the Subscription. </returns>
#pragma warning disable AZC0015 // Unexpected client method return type.
public virtual SubscriptionResource GetDefaultSubscription(CancellationToken cancellationToken = default)
#pragma warning restore AZC0015 // Unexpected client method return type.
{
using var scope = _subscriptionClientDiagnostics.CreateScope("ArmClient.GetDefaultSubscription");
scope.Start();
try
{
if (_defaultSubscription == null)
{
_defaultSubscription = GetSubscriptions().GetAll(cancellationToken).FirstOrDefault();
}
else if (_defaultSubscription.HasData)
{
return _defaultSubscription;
}
else
{
_defaultSubscription = _defaultSubscription.Get(cancellationToken);
}
if (_defaultSubscription is null)
{
throw new InvalidOperationException("No subscriptions found for the given credentials");
}
return _defaultSubscription;
}
catch (Exception e)
{
scope.Failed(e);
throw;
}
}
/// <summary>
/// Gets the default subscription.
/// </summary>
/// <returns> Resource operations of the Subscription. </returns>
#pragma warning disable AZC0015 // Unexpected client method return type.
public virtual async Task<SubscriptionResource> GetDefaultSubscriptionAsync(CancellationToken cancellationToken = default)
#pragma warning restore AZC0015 // Unexpected client method return type.
{
using var scope = _subscriptionClientDiagnostics.CreateScope("ArmClient.GetDefaultSubscription");
scope.Start();
try
{
if (_defaultSubscription == null)
{
_defaultSubscription = await GetSubscriptions().GetAllAsync(cancellationToken).FirstOrDefaultAsync(_ => true, cancellationToken).ConfigureAwait(false);
}
else if (_defaultSubscription.HasData)
{
return _defaultSubscription;
}
else
{
_defaultSubscription = await _defaultSubscription.GetAsync(cancellationToken).ConfigureAwait(false);
}
if (_defaultSubscription is null)
{
throw new InvalidOperationException("No subscriptions found for the given credentials");
}
return _defaultSubscription;
}
catch (Exception e)
{
scope.Failed(e);
throw;
}
}
/// <summary> Gets a collection of GenericResources. </summary>
/// <returns> An object representing collection of GenericResources and their operations. </returns>
public virtual GenericResourceCollection GetGenericResources() => _tenant.GetGenericResources();
/// <summary> Gets all resource providers for a subscription. </summary>
/// <param name="top"> [This parameter is no longer supported.] The number of results to return. </param>
/// <param name="expand"> The properties to include in the results. For example, use &amp;$expand=metadata in the query string to retrieve resource provider metadata. To include property aliases in response, use $expand=resourceTypes/aliases. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
[ForwardsClientCalls]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
[System.ObsoleteAttribute("This method is obsolete as the `top` parameter is not supported by service and will be removed in a future release.", false)]
public virtual Pageable<TenantResourceProvider> GetTenantResourceProviders(int? top, string expand, CancellationToken cancellationToken = default) => _tenant.GetTenantResourceProviders(expand, cancellationToken);
/// <summary> Gets all resource providers for a subscription. </summary>
/// <param name="top"> [This parameter is no longer supported.] The number of results to return. </param>
/// <param name="expand"> The properties to include in the results. For example, use &amp;$expand=metadata in the query string to retrieve resource provider metadata. To include property aliases in response, use $expand=resourceTypes/aliases. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
[ForwardsClientCalls]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
[System.ObsoleteAttribute("This method is obsolete as the `top` parameter is not supported by service and will be removed in a future release.", false)]
public virtual AsyncPageable<TenantResourceProvider> GetTenantResourceProvidersAsync(int? top, string expand, CancellationToken cancellationToken = default) => _tenant.GetTenantResourceProvidersAsync(expand, cancellationToken);
/// <summary> Gets all resource providers for a subscription. </summary>
/// <param name="expand"> The properties to include in the results. For example, use &amp;$expand=metadata in the query string to retrieve resource provider metadata. To include property aliases in response, use $expand=resourceTypes/aliases. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
[ForwardsClientCalls]
public virtual Pageable<TenantResourceProvider> GetTenantResourceProviders(string expand = null, CancellationToken cancellationToken = default) => _tenant.GetTenantResourceProviders(expand, cancellationToken);
/// <summary> Gets all resource providers for a subscription. </summary>
/// <param name="expand"> The properties to include in the results. For example, use &amp;$expand=metadata in the query string to retrieve resource provider metadata. To include property aliases in response, use $expand=resourceTypes/aliases. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
[ForwardsClientCalls]
public virtual AsyncPageable<TenantResourceProvider> GetTenantResourceProvidersAsync(string expand = null, CancellationToken cancellationToken = default) => _tenant.GetTenantResourceProvidersAsync(expand, cancellationToken);
/// <summary> Gets the specified resource provider at the tenant level. </summary>
/// <param name="resourceProviderNamespace"> The namespace of the resource provider. </param>
/// <param name="expand"> The $expand query parameter. For example, to include property aliases in response, use $expand=resourceTypes/aliases. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
/// <exception cref="ArgumentNullException"> <paramref name="resourceProviderNamespace"/> is null. </exception>
[ForwardsClientCalls]
public virtual Response<TenantResourceProvider> GetTenantResourceProvider(string resourceProviderNamespace, string expand = null, CancellationToken cancellationToken = default) => _tenant.GetTenantResourceProvider(resourceProviderNamespace, expand, cancellationToken);
/// <summary> Gets the specified resource provider at the tenant level. </summary>
/// <param name="resourceProviderNamespace"> The namespace of the resource provider. </param>
/// <param name="expand"> The $expand query parameter. For example, to include property aliases in response, use $expand=resourceTypes/aliases. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
/// <exception cref="ArgumentNullException"> <paramref name="resourceProviderNamespace"/> is null. </exception>
[ForwardsClientCalls]
public virtual async Task<Response<TenantResourceProvider>> GetTenantResourceProviderAsync(string resourceProviderNamespace, string expand = null, CancellationToken cancellationToken = default) => await _tenant.GetTenantResourceProviderAsync(resourceProviderNamespace, expand, cancellationToken).ConfigureAwait(false);
/// <summary>
/// Gets the management group collection for this tenant.
/// </summary>
/// <returns> A collection of the management groups. </returns>
public virtual ManagementGroupCollection GetManagementGroups() => _tenant.GetManagementGroups();
/// <summary>
/// Gets a client using this instance of ArmClient to copy the client settings from.
/// </summary>
/// <typeparam name="T"> The type of <see cref="ArmResource"/> that will be constructed. </typeparam>
/// <param name="resourceFactory"> Delegate method that will construct the client. </param>
[EditorBrowsable(EditorBrowsableState.Never)]
public virtual T GetResourceClient<T>(Func<T> resourceFactory)
where T : ArmResource
{
return resourceFactory();
}
private readonly ConcurrentDictionary<Type, object> _clientCache = new ConcurrentDictionary<Type, object>();
/// <summary>
/// Gets a cached client to use for extension methods.
/// </summary>
/// <typeparam name="T"> The type of client to get. </typeparam>
/// <param name="clientFactory"> The constructor factory for the client. </param>
[EditorBrowsable(EditorBrowsableState.Never)]
public virtual T GetCachedClient<T>(Func<ArmClient, T> clientFactory)
where T : class
{
return _clientCache.GetOrAdd(typeof(T), (type) => { return clientFactory(this); }) as T;
}
}
}