diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient.cs index fd4151d1a1f4..11f29f7be0db 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient.cs @@ -707,7 +707,7 @@ public virtual AsyncPageable GetConfigurationSettingsAsync var pageableImplementation = GetConfigurationSettingsPageableImplementation(selector, cancellationToken); - return new AsyncConditionalPageable(pageableImplementation); + return new AsyncConditionalPageable(pageableImplementation); } /// @@ -721,10 +721,10 @@ public virtual Pageable GetConfigurationSettings(SettingSe var pageableImplementation = GetConfigurationSettingsPageableImplementation(selector, cancellationToken); - return new ConditionalPageable(pageableImplementation); + return new ConditionalPageable(pageableImplementation); } - private ConditionalPageableImplementation GetConfigurationSettingsPageableImplementation(SettingSelector selector, CancellationToken cancellationToken) + private ConditionalPageableImplementation GetConfigurationSettingsPageableImplementation(SettingSelector selector, CancellationToken cancellationToken) { var key = selector.KeyFilter; var label = selector.LabelFilter; @@ -747,7 +747,7 @@ HttpMessage NextPageRequest(MatchConditions conditions, int? pageSizeHint, strin return CreateNextGetConfigurationSettingsRequest(nextLink, key, label, _syncToken, null, dateTime, fieldsString, null, conditions, tags, context); } - return new ConditionalPageableImplementation(FirstPageRequest, NextPageRequest, ParseGetConfigurationSettingsResponse, Pipeline, ClientDiagnostics, "ConfigurationClient.GetConfigurationSettings", context); + return new ConditionalPageableImplementation(FirstPageRequest, NextPageRequest, ParseGetConfigurationSettingsResponse, Pipeline, ClientDiagnostics, "ConfigurationClient.GetConfigurationSettings", context); } /// diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClientExtensions.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClientExtensions.cs index 9af4e38e2a79..de419004220c 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClientExtensions.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClientExtensions.cs @@ -34,7 +34,7 @@ public static IAsyncEnumerable> AsPages(this AsyncPag { Argument.AssertNotNull(conditions, nameof(conditions)); - var conditionalPageable = pageable as AsyncConditionalPageable; + var conditionalPageable = pageable as AsyncConditionalPageable; if (conditionalPageable is null) { @@ -66,7 +66,7 @@ public static IEnumerable> AsPages(this Pageable; if (conditionalPageable is null) { diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/FeatureFlagClient.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/FeatureFlagClient.cs new file mode 100644 index 000000000000..1515eb0af714 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/FeatureFlagClient.cs @@ -0,0 +1,866 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; +using Azure.Core.Pipeline; +using static Azure.Core.Pipeline.TaskExtensions; + +#pragma warning disable AZC0007 + +namespace Azure.Data.AppConfiguration +{ + // CUSTOM: + // - Renamed. + // - Suppressed convenience methods. These are implemented through custom code. + // - Suppressed protocol methods that do not have an existing convenience method API. + /// + /// The client to use for interacting with the Azure Configuration Store. + /// + [CodeGenSuppress("FeatureFlagClient", typeof(Uri), typeof(AzureKeyCredential), typeof(FeatureFlagClientOptions))] + + public partial class FeatureFlagClient + { + private const string OTelAttributeKey = "az.appconfiguration.key"; + private readonly SyncTokenPolicy _syncTokenPolicy; + private readonly string _syncToken; + + /// + /// Initializes a new instance of the class. + /// + /// Connection string with authentication option and related parameters. + public FeatureFlagClient(string connectionString) + : this(connectionString, new FeatureFlagClientOptions()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection string with authentication option and related parameters. + /// Options that allow configuration of requests sent to the configuration store. + public FeatureFlagClient(string connectionString, FeatureFlagClientOptions options) + { + if (connectionString == null) + throw new ArgumentNullException(nameof(connectionString)); + if (options == null) + throw new ArgumentNullException(nameof(options)); + + ParseConnectionString(connectionString, out _endpoint, out var credential, out var secret); + _apiVersion = options.Version; + _syncTokenPolicy = new SyncTokenPolicy(); + Pipeline = CreatePipeline(options, new AuthenticationPolicy(credential, secret), _syncTokenPolicy); + + ClientDiagnostics = new ClientDiagnostics(options, true); + } + + /// + /// Initializes a new instance of the class. + /// + /// The referencing the app configuration storage. + /// The token credential used to sign requests. + /// Options that allow configuration of requests sent to the configuration store. + /// The 's Microsoft Entra audience is configurable via the property. + /// If no token audience is set, Azure Public Cloud is used. If using an Azure sovereign cloud, configure the audience accordingly. + /// + public FeatureFlagClient(Uri endpoint, TokenCredential credential, FeatureFlagClientOptions options) + { + Argument.AssertNotNull(endpoint, nameof(endpoint)); + Argument.AssertNotNull(credential, nameof(credential)); + + _endpoint = endpoint; + _syncTokenPolicy = new SyncTokenPolicy(); + Pipeline = CreatePipeline(options, new BearerTokenAuthenticationPolicy(credential, options.GetDefaultScope(endpoint)), _syncTokenPolicy); + _apiVersion = options.Version; + + ClientDiagnostics = new ClientDiagnostics(options, true); + } + + /// Initializes a new instance of FeatureFlagClient. + /// The endpoint of the App Configuration instance to send requests to. + /// A credential used to authenticate to an Azure Service. + /// or is null. + internal FeatureFlagClient(Uri endpoint, AzureKeyCredential credential) : this(endpoint, credential, null, new FeatureFlagClientOptions()) + { + } + + /// Initializes a new instance of FeatureFlagClient. + /// The endpoint of the App Configuration instance to send requests to. + /// A credential used to authenticate to an Azure Service. + /// Used to guarantee real-time consistency between requests. + /// The options for configuring the client. + /// or is null. + internal FeatureFlagClient(Uri endpoint, AzureKeyCredential credential, string syncToken, FeatureFlagClientOptions options) + { + Argument.AssertNotNull(endpoint, nameof(endpoint)); + Argument.AssertNotNull(credential, nameof(credential)); + options ??= new FeatureFlagClientOptions(); + + ClientDiagnostics = new ClientDiagnostics(options, true); + _keyCredential = credential; + Pipeline = HttpPipelineBuilder.Build(options, Array.Empty(), new HttpPipelinePolicy[] { new AzureKeyCredentialPolicy(_keyCredential, AuthorizationHeader) }, new ResponseClassifier()); + _endpoint = endpoint; + _syncToken = syncToken; + _apiVersion = options.Version; + } + + /// Initializes a new instance of FeatureFlagClient. + /// The endpoint of the App Configuration instance to send requests to. + /// A credential used to authenticate to an Azure Service. + /// Used to guarantee real-time consistency between requests. + /// The options for configuring the client. + /// or is null. + internal FeatureFlagClient(Uri endpoint, TokenCredential credential, string syncToken, FeatureFlagClientOptions options) + { + Argument.AssertNotNull(endpoint, nameof(endpoint)); + Argument.AssertNotNull(credential, nameof(credential)); + options ??= new FeatureFlagClientOptions(); + + ClientDiagnostics = new ClientDiagnostics(options, true); + _tokenCredential = credential; + Pipeline = HttpPipelineBuilder.Build(options, Array.Empty(), new HttpPipelinePolicy[] { new BearerTokenAuthenticationPolicy(_tokenCredential, AuthorizationScopes) }, new ResponseClassifier()); + _endpoint = endpoint; + _syncToken = syncToken; + _apiVersion = options.Version; + } + + private static HttpPipeline CreatePipeline(FeatureFlagClientOptions options, HttpPipelinePolicy authenticationPolicy, HttpPipelinePolicy syncTokenPolicy) + { + return HttpPipelineBuilder.Build(options, + new HttpPipelinePolicy[] { new CustomHeadersPolicy() }, + new HttpPipelinePolicy[] { authenticationPolicy, syncTokenPolicy }, + new ResponseClassifier()); + } + + /// + /// Creates a if the flag, uniquely identified by name and label, does not already exist in the configuration store. + /// + /// The primary identifier of the feature flag. + /// The enabled state of the feature flag. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// A response containing the added . + public virtual async Task> AddFeatureFlagAsync(string name, bool? enabled = null, string label = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return await AddFeatureFlagAsync(ConfigurationModelFactory.FeatureFlag(name, enabled, label), cancellationToken).ConfigureAwait(false); + } + + /// + /// Creates a if the flag, uniquely identified by name and label, does not already exist in the configuration store. + /// + /// The primary identifier of the feature flag. + /// The enabled state of the feature flag. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// A response containing the added . + public virtual Response AddFeatureFlag(string name, bool? enabled = null, string label = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return AddFeatureFlag(ConfigurationModelFactory.FeatureFlag(name, enabled, label), cancellationToken); + } + + /// + /// Creates a only if the flag does not already exist in the configuration store. + /// + /// The to create. + /// A controlling the request lifetime. + /// A response containing the added . + public virtual async Task> AddFeatureFlagAsync(FeatureFlag flag, CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope($"{nameof(FeatureFlagClient)}.{nameof(AddFeatureFlag)}"); + scope.AddAttribute(OTelAttributeKey, flag?.Name); + scope.Start(); + + try + { + MatchConditions requestOptions = new MatchConditions { IfNoneMatch = ETag.All }; + return await PutFeatureFlagAsync(flag.Name, flag, flag.Label, _syncToken, requestOptions, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// Creates a only if the flag does not already exist in the configuration store. + /// + /// The to create. + /// A controlling the request lifetime. + /// A response containing the added . + public virtual Response AddFeatureFlag(FeatureFlag flag, CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope($"{nameof(FeatureFlagClient)}.{nameof(AddFeatureFlag)}"); + scope.AddAttribute(OTelAttributeKey, flag?.Name); + scope.Start(); + + try + { + MatchConditions requestOptions = new MatchConditions { IfNoneMatch = ETag.All }; + return PutFeatureFlag(flag.Name, flag, flag.Label, _syncToken, requestOptions, cancellationToken); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// Creates a , uniquely identified by name and label, if it doesn't exist or overwrites the existing flag in the configuration store. + /// + /// The primary identifier of the feature flag. + /// The enabled state of the feature flag. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// A response containing the written to the configuration store. + public virtual async Task> SetFeatureFlagAsync(string name, bool? enabled = null, string label = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return await SetFeatureFlagAsync(ConfigurationModelFactory.FeatureFlag(name, enabled, label), false, cancellationToken).ConfigureAwait(false); + } + + /// + /// Creates a , uniquely identified by name and label, if it doesn't exist or overwrites the existing flag in the configuration store. + /// + /// The primary identifier of the feature flag. + /// The enabled state of the feature flag. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// A response containing the written to the configuration store. + public virtual Response SetFeatureFlag(string name, bool? enabled = null, string label = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return SetFeatureFlag(ConfigurationModelFactory.FeatureFlag(name, enabled, label), false, cancellationToken); + } + + /// + /// Creates a if it doesn't exist or overwrites the existing flag in the configuration store. + /// + /// The to create. + /// If set to true and the feature flag exists in the configuration store, overwrite the flag + /// if the passed-in is the same version as the one in the configuration store. The flag versions + /// are the same if their ETag fields match. If the two flags are different versions, this method will throw an exception to indicate + /// that the flag in the configuration store was modified since it was last obtained by the client. + /// A controlling the request lifetime. + /// A response containing the written to the configuration store. + public virtual async Task> SetFeatureFlagAsync(FeatureFlag flag, bool onlyIfUnchanged = false, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + using DiagnosticScope scope = ClientDiagnostics.CreateScope($"{nameof(FeatureFlagClient)}.{nameof(SetFeatureFlag)}"); + scope.AddAttribute(OTelAttributeKey, flag?.Name); + scope.Start(); + + try + { + MatchConditions requestOptions = onlyIfUnchanged ? new MatchConditions { IfMatch = flag.ETag } : default; + return await PutFeatureFlagAsync(flag.Name, flag, flag.Label, _syncToken, requestOptions, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// Creates a if it doesn't exist or overwrites the existing flag in the configuration store. + /// + /// The to create. + /// If set to true and the feature flag exists in the configuration store, overwrite the flag + /// if the passed-in is the same version as the one in the configuration store. The flag versions + /// are the same if their ETag fields match. If the two flags are different versions, this method will throw an exception to indicate + /// that the flag in the configuration store was modified since it was last obtained by the client. + /// A controlling the request lifetime. + /// A response containing the written to the configuration store. + public virtual Response SetFeatureFlag(FeatureFlag flag, bool onlyIfUnchanged = false, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + using DiagnosticScope scope = ClientDiagnostics.CreateScope($"{nameof(FeatureFlagClient)}.{nameof(SetFeatureFlag)}"); + scope.AddAttribute(OTelAttributeKey, flag?.Name); + scope.Start(); + + try + { + MatchConditions requestOptions = onlyIfUnchanged ? new MatchConditions { IfMatch = flag.ETag } : default; + return PutFeatureFlag(flag.Name, flag, flag.Label, _syncToken, requestOptions, cancellationToken); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// Delete a from the configuration store. + /// + /// The primary identifier of the feature flag. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// A response indicating the success of the operation. + public virtual async Task DeleteFeatureFlagAsync(string name, string label = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return await DeleteFeatureFlagAsync(name, label, default, cancellationToken).ConfigureAwait(false); + } + + /// + /// Delete a from the configuration store. + /// + /// The primary identifier of the feature flag. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// A response indicating the success of the operation. + public virtual Response DeleteFeatureFlag(string name, string label = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return DeleteFeatureFlag(name, label, default, cancellationToken); + } + + /// + /// Delete a from the configuration store. + /// + /// The to delete. + /// If set to true and the feature flag exists in the configuration store, delete the flag + /// if the passed-in is the same version as the one in the configuration store. The flag versions + /// are the same if their ETag fields match. If the two flags are different versions, this method will throw an exception to indicate + /// that the flag in the configuration store was modified since it was last obtained by the client. + /// A controlling the request lifetime. + /// A response indicating the success of the operation. + public virtual async Task DeleteFeatureFlagAsync(FeatureFlag flag, bool onlyIfUnchanged = false, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + MatchConditions requestOptions = onlyIfUnchanged ? new MatchConditions { IfMatch = flag.ETag } : default; + return await DeleteFeatureFlagAsync(flag.Name, flag.Label, requestOptions, cancellationToken).ConfigureAwait(false); + } + + /// + /// Delete a from the configuration store. + /// + /// The to delete. + /// If set to true and the feature flag exists in the configuration store, delete the flag + /// if the passed-in is the same version as the one in the configuration store. The flag versions + /// are the same if their ETag fields match. If the two flags are different versions, this method will throw an exception to indicate + /// that the flag in the configuration store was modified since it was last obtained by the client. + /// A controlling the request lifetime. + /// A response indicating the success of the operation. + public virtual Response DeleteFeatureFlag(FeatureFlag flag, bool onlyIfUnchanged = false, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + MatchConditions requestOptions = onlyIfUnchanged ? new MatchConditions { IfMatch = flag.ETag } : default; + return DeleteFeatureFlag(flag.Name, flag.Label, requestOptions, cancellationToken); + } + + private async Task DeleteFeatureFlagAsync(string name, string label, MatchConditions requestOptions, CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope($"{nameof(FeatureFlagClient)}.{nameof(DeleteFeatureFlag)}"); + scope.AddAttribute(OTelAttributeKey, name); + scope.Start(); + + try + { + RequestContext context = CreateRequestContext(ErrorOptions.NoThrow, cancellationToken); + + using Response response = await DeleteFeatureFlagAsync(name, label, _syncToken, requestOptions?.IfMatch, context).ConfigureAwait(false); + + return response.Status switch + { + 200 => response, + 204 => response, + + // Throws on 412 if resource was modified. + _ => throw new RequestFailedException(response, null, new FeatureFlagRequestFailedDetailsParser()), + }; + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + private Response DeleteFeatureFlag(string name, string label, MatchConditions requestOptions, CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope($"{nameof(FeatureFlagClient)}.{nameof(DeleteFeatureFlag)}"); + scope.AddAttribute(OTelAttributeKey, name); + scope.Start(); + + try + { + RequestContext context = CreateRequestContext(ErrorOptions.NoThrow, cancellationToken); + + using Response response = DeleteFeatureFlag(name, label, _syncToken, requestOptions?.IfMatch, context); + + return response.Status switch + { + 200 => response, + 204 => response, + + // Throws on 412 if resource was modified. + _ => throw new RequestFailedException(response, null, new FeatureFlagRequestFailedDetailsParser()), + }; + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// Retrieve an existing , uniquely identified by name and label, from the configuration store. + /// + /// The primary identifier of the feature flag to retrieve. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// A response containing the retrieved . + public virtual async Task> GetFeatureFlagAsync(string name, string label = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return await GetFeatureFlagAsync(name, label, acceptDateTime: default, conditions: default, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieve an existing , uniquely identified by name and label, from the configuration store. + /// + /// The primary identifier of the feature flag to retrieve. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// A response containing the retrieved . + public virtual Response GetFeatureFlag(string name, string label = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return GetFeatureFlag(name, label, acceptDateTime: default, conditions: default, cancellationToken); + } + + /// + /// Retrieve an existing from the configuration store. + /// + /// The to retrieve. + /// If set to true, only retrieve the flag from the configuration store if it has changed since the client last retrieved it. + /// It is determined to have changed if the ETag field on the passed-in is different from the ETag of the flag in the + /// configuration store. If it has not changed, the returned response will have have no value, and will throw if response.Value is accessed. Callers may + /// check the status code on the response to avoid triggering the exception. + /// A controlling the request lifetime. + /// A response containing the retrieved . + public virtual async Task> GetFeatureFlagAsync(FeatureFlag flag, bool onlyIfChanged = false, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + MatchConditions requestOptions = onlyIfChanged ? new MatchConditions { IfNoneMatch = flag.ETag } : default; + return await GetFeatureFlagAsync(flag.Name, flag.Label, acceptDateTime: default, requestOptions, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieve an existing from the configuration store. + /// + /// The to retrieve. + /// If set to true, only retrieve the flag from the configuration store if it has changed since the client last retrieved it. + /// It is determined to have changed if the ETag field on the passed-in is different from the ETag of the flag in the + /// configuration store. If it has not changed, the returned response will have have no value, and will throw if response.Value is accessed. Callers may + /// check the status code on the response to avoid triggering the exception. + /// A controlling the request lifetime. + /// A response containing the retrieved . + public virtual Response GetFeatureFlag(FeatureFlag flag, bool onlyIfChanged = false, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + MatchConditions requestOptions = onlyIfChanged ? new MatchConditions { IfNoneMatch = flag.ETag } : default; + return GetFeatureFlag(flag.Name, flag.Label, acceptDateTime: default, requestOptions, cancellationToken); + } + + /// + /// Retrieve an existing from the configuration store. + /// + /// The to retrieve. + /// The flag will be retrieved exactly as it existed at the provided time. + /// A controlling the request lifetime. + /// A response containing the retrieved . + public virtual async Task> GetFeatureFlagAsync(FeatureFlag flag, DateTimeOffset acceptDateTime, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + return await GetFeatureFlagAsync(flag.Name, flag.Label, acceptDateTime, conditions: default, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieve an existing from the configuration store. + /// + /// The to retrieve. + /// The flag will be retrieved exactly as it existed at the provided time. + /// A controlling the request lifetime. + /// A response containing the retrieved . + public virtual Response GetFeatureFlag(FeatureFlag flag, DateTimeOffset acceptDateTime, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + return GetFeatureFlag(flag.Name, flag.Label, acceptDateTime, conditions: default, cancellationToken); + } + + /// + /// Retrieve an existing , uniquely identified by name and label, from the configuration store. + /// + /// The primary identifier of the feature flag to retrieve. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// The flag will be retrieved exactly as it existed at the provided time. + /// The match conditions to apply to request. + /// A response containing the retrieved . + internal virtual async Task> GetFeatureFlagAsync(string name, string label, DateTimeOffset? acceptDateTime, MatchConditions conditions, CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope($"{nameof(FeatureFlagClient)}.{nameof(GetFeatureFlag)}"); + scope.AddAttribute(OTelAttributeKey, name); + scope.Start(); + + try + { + RequestContext context = CreateRequestContext(ErrorOptions.NoThrow, cancellationToken); + context.AddClassifier(304, isError: false); + + var dateTime = acceptDateTime.HasValue ? acceptDateTime.Value.UtcDateTime.ToString(AcceptDateTimeFormat, CultureInfo.InvariantCulture) : null; + + using Response response = await GetFeatureFlagAsync(name, label, null, _syncToken, dateTime, conditions, null, context).ConfigureAwait(false); + + return response.Status switch + { + 200 => CreateResponse(response), + 304 => CreateResourceModifiedResponse(response), + _ => throw new RequestFailedException(response, null, new FeatureFlagRequestFailedDetailsParser()) + }; + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// Retrieve an existing , uniquely identified by name and label, from the configuration store. + /// + /// The primary identifier of the feature flag to retrieve. + /// A label used to group this feature flag with others. + /// A controlling the request lifetime. + /// The flag will be retrieved exactly as it existed at the provided time. + /// The match conditions to apply to request. + /// A response containing the retrieved . + internal virtual Response GetFeatureFlag(string name, string label, DateTimeOffset? acceptDateTime, MatchConditions conditions, CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope($"{nameof(FeatureFlagClient)}.{nameof(GetFeatureFlag)}"); + scope.AddAttribute(OTelAttributeKey, name); + scope.Start(); + + try + { + RequestContext context = CreateRequestContext(ErrorOptions.NoThrow, cancellationToken); + context.AddClassifier(304, isError: false); + + var dateTime = acceptDateTime.HasValue ? acceptDateTime.Value.UtcDateTime.ToString(AcceptDateTimeFormat, CultureInfo.InvariantCulture) : null; + using Response response = GetFeatureFlag(name, label, null, _syncToken, dateTime, conditions, null, context); + + return response.Status switch + { + 200 => CreateResponse(response), + 304 => CreateResourceModifiedResponse(response), + _ => throw new RequestFailedException(response, null, new FeatureFlagRequestFailedDetailsParser()) + }; + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// Retrieves one or more entities that match the options specified in the passed-in . + /// + /// Options used to select a set of entities from the configuration store. + /// A controlling the request lifetime. + /// An enumerable collection containing the retrieved entities. + public virtual AsyncPageable GetFeatureFlagsAsync(FeatureFlagSelector selector, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(selector, nameof(selector)); + + var pageableImplementation = GetFeatureFlagsPageableImplementation(selector, cancellationToken); + + return new AsyncConditionalPageable(pageableImplementation); + } + + /// + /// Retrieves one or more entities that match the options specified in the passed-in . + /// + /// Set of options for selecting from the configuration store. + /// A controlling the request lifetime. + public virtual Pageable GetFeatureFlags(FeatureFlagSelector selector, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(selector, nameof(selector)); + + var pageableImplementation = GetFeatureFlagsPageableImplementation(selector, cancellationToken); + + return new ConditionalPageable(pageableImplementation); + } + + private ConditionalPageableImplementation GetFeatureFlagsPageableImplementation(FeatureFlagSelector selector, CancellationToken cancellationToken) + { + var name = selector.NameFilter; + var label = selector.LabelFilter; + var dateTime = selector.AcceptDateTime?.UtcDateTime.ToString(AcceptDateTimeFormat, CultureInfo.InvariantCulture); + var tags = selector.TagsFilter; + IEnumerable fieldsString = selector.Fields.Split(); + + RequestContext context = CreateRequestContext(ErrorOptions.Default, cancellationToken); + + context.AddClassifier(304, false); + + HttpMessage FirstPageRequest(MatchConditions conditions, int? pageSizeHint) + { + var test = CreateGetFeatureFlagsRequest(name, label, _syncToken, null, dateTime, fieldsString, conditions, tags, context); + return test; + } + + HttpMessage NextPageRequest(MatchConditions conditions, int? pageSizeHint, string nextLink) + { + var test = CreateNextGetFeatureFlagsRequest(nextLink, _syncToken, dateTime, conditions, context); + return test; + } + + return new ConditionalPageableImplementation(FirstPageRequest, NextPageRequest, ParseGetGetFeatureFlagsResponse, Pipeline, ClientDiagnostics, "FeatureFlagClient.GetFeatureFlags", context); + } + + /// + /// Retrieves the revisions of one or more entities that match the specified and . + /// + /// Name filter that will be used to select a set of entities. + /// Label filter that will be used to select a set of entities. + /// A controlling the request lifetime. + public virtual AsyncPageable GetRevisionsAsync(string nameFilter, string labelFilter = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(nameFilter, nameof(nameFilter)); + return GetRevisionsAsync(new FeatureFlagSelector { NameFilter = nameFilter, LabelFilter = labelFilter }, cancellationToken); + } + + /// + /// Retrieves the revisions of one or more entities that match the specified and . + /// + /// Name filter that will be used to select a set of entities. + /// Label filter that will be used to select a set of entities. + /// A controlling the request lifetime. + public virtual Pageable GetRevisions(string nameFilter, string labelFilter = default, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(nameFilter, nameof(nameFilter)); + return GetRevisions(new FeatureFlagSelector { NameFilter = nameFilter, LabelFilter = labelFilter }, cancellationToken); + } + + /// + /// Retrieves the revisions of one or more entities that satisfy the options of the . + /// + /// Set of options for selecting from the configuration store. + /// A controlling the request lifetime. + public virtual AsyncPageable GetRevisionsAsync(FeatureFlagSelector selector, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(selector, nameof(selector)); + var name = selector.NameFilter; + var label = selector.LabelFilter; + var dateTime = selector.AcceptDateTime?.UtcDateTime.ToString(AcceptDateTimeFormat, CultureInfo.InvariantCulture); + var tags = selector.TagsFilter; + RequestContext context = CreateRequestContext(ErrorOptions.Default, cancellationToken); + IEnumerable fieldsSplit = selector.Fields.Split(); + + HttpMessage FirstPageRequest(int? pageSizeHint) => CreateGetRevisionsRequest(name, label, null, fieldsSplit, tags, _syncToken, null, context); + HttpMessage NextPageRequest(int? pageSizeHint, string nextLink) => CreateGetRevisionsRequest(name, label, nextLink, fieldsSplit, tags, _syncToken, null, context); + return PageableHelpers.CreateAsyncPageable(FirstPageRequest, NextPageRequest, element => FeatureFlag.DeserializeFeatureFlag(element, default), ClientDiagnostics, Pipeline, "FeatureFlagClient.GetRevisions", "items", "@nextLink", context); + } + + /// + /// Retrieves the revisions of one or more entities that satisfy the options of the . + /// + /// Set of options for selecting from the configuration store. + /// A controlling the request lifetime. + public virtual Pageable GetRevisions(FeatureFlagSelector selector, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(selector, nameof(selector)); + var name = selector.NameFilter; + var label = selector.LabelFilter; + var dateTime = selector.AcceptDateTime?.UtcDateTime.ToString(AcceptDateTimeFormat, CultureInfo.InvariantCulture); + var tags = selector.TagsFilter; + RequestContext context = CreateRequestContext(ErrorOptions.Default, cancellationToken); + IEnumerable fieldsString = selector.Fields.Split(); + + HttpMessage FirstPageRequest(int? pageSizeHint) => CreateGetRevisionsRequest(name, label, null, fieldsString, tags, _syncToken, null, context); + HttpMessage NextPageRequest(int? pageSizeHint, string nextLink) => CreateNextGetRevisionsRequest(nextLink, _syncToken, dateTime, context); + return PageableHelpers.CreatePageable(FirstPageRequest, NextPageRequest, element => FeatureFlag.DeserializeFeatureFlag(element, default), ClientDiagnostics, Pipeline, "FeatureFlagClient.GetRevisions", "items", "@nextLink", context); + } + + /// + /// Sets an existing to read only or read write state in the configuration store. + /// + /// The primary identifier of the feature flag. + /// If true, the will be set to read only in the configuration store. If false, it will be set to read write. + /// A controlling the request lifetime. + public virtual async Task> SetReadOnlyAsync(string name, bool isReadOnly, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return await SetReadOnlyAsync(name, default, isReadOnly, cancellationToken).ConfigureAwait(false); + } + + /// + /// Sets an existing to read only or read write state in the configuration store. + /// + /// The primary identifier of the feature flag. + /// If true, the will be set to read only in the configuration store. If false, it will be set to read write. + /// A controlling the request lifetime. + public virtual Response SetReadOnly(string name, bool isReadOnly, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return SetReadOnlyAsync(name, default, default, isReadOnly, false, cancellationToken).EnsureCompleted(); + } + + /// + /// Sets an existing to read only or read write state in the configuration store. + /// + /// The primary identifier of the feature flag. + /// A label used to group this feature flag with others. + /// If true, the will be set to read only in the configuration store. If false, it will be set to read write. + /// A controlling the request lifetime. + public virtual async Task> SetReadOnlyAsync(string name, string label, bool isReadOnly, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return await SetReadOnlyAsync(name, label, default, isReadOnly, true, cancellationToken).ConfigureAwait(false); + } + + /// + /// Sets an existing to read only or read write state in the configuration store. + /// + /// The primary identifier of the feature flag. + /// A label used to group this feature flag with others. + /// If true, the will be set to read only in the configuration store. If false, it will be set to read write. + /// A controlling the request lifetime. + public virtual Response SetReadOnly(string name, string label, bool isReadOnly, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + return SetReadOnlyAsync(name, label, default, isReadOnly, false, cancellationToken).EnsureCompleted(); + } + + /// + /// Sets an existing to read only or read write state in the configuration store. + /// + /// The to update. + /// If set to true and the feature flag exists in the configuration store, update the flag + /// if the passed-in is the same version as the one in the configuration store. The flag versions + /// are the same if their ETag fields match. If the two flags are different versions, this method will throw an exception to indicate + /// that the flag in the configuration store was modified since it was last obtained by the client. + /// If true, the will be set to read only in the configuration store. If false, it will be set to read write. + /// A controlling the request lifetime. + public virtual async Task> SetReadOnlyAsync(FeatureFlag flag, bool isReadOnly, bool onlyIfUnchanged = false, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + MatchConditions requestOptions = onlyIfUnchanged ? new MatchConditions { IfMatch = flag.ETag } : default; + return await SetReadOnlyAsync(flag.Name, flag.Label, requestOptions, isReadOnly, true, cancellationToken).ConfigureAwait(false); + } + + /// + /// Sets an existing to read only or read write state in the configuration store. + /// + /// The to update. + /// If set to true and the feature flag exists in the configuration store, update the flag + /// if the passed-in is the same version as the one in the configuration store. The flag versions + /// are the same if their ETag fields match. If the two flags are different versions, this method will throw an exception to indicate + /// that the flag in the configuration store was modified since it was last obtained by the client. + /// If true, the will be set to read only in the configuration store. If false, it will be set to read write. + /// A controlling the request lifetime. + public virtual Response SetReadOnly(FeatureFlag flag, bool isReadOnly, bool onlyIfUnchanged = false, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(flag, nameof(flag)); + MatchConditions requestOptions = onlyIfUnchanged ? new MatchConditions { IfMatch = flag.ETag } : default; + return SetReadOnlyAsync(flag.Name, flag.Label, requestOptions, isReadOnly, false, cancellationToken).EnsureCompleted(); + } + + private async ValueTask> SetReadOnlyAsync(string name, string label, MatchConditions requestOptions, bool isReadOnly, bool async, CancellationToken cancellationToken) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope($"{nameof(FeatureFlagClient)}.{nameof(SetReadOnly)}"); + scope.AddAttribute(OTelAttributeKey, name); + scope.Start(); + + try + { + RequestContext context = CreateRequestContext(ErrorOptions.NoThrow, cancellationToken); + using Response response = async ? await ToCreateAsyncResponse(name, label, requestOptions, isReadOnly, context).ConfigureAwait(false) : ToCreateResponse(name, label, requestOptions, isReadOnly, context); + + return response.Status switch + { + 200 => CreateResponse(response), + _ => throw new RequestFailedException(response, null, new FeatureFlagRequestFailedDetailsParser()), + }; + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + private async Task ToCreateAsyncResponse(string name, string label, MatchConditions requestOptions, bool isReadOnly, RequestContext context) + { + Response response = isReadOnly + ? await CreateReadOnlyLockAsync(name, label, _syncToken, requestOptions, context).ConfigureAwait(false) + : await DeleteReadOnlyLockAsync(name, label, _syncToken, requestOptions, context).ConfigureAwait(false); + return response; + } + + private Response ToCreateResponse(string name, string label, MatchConditions requestOptions, bool isReadOnly, RequestContext context) + { + Response response = isReadOnly + ? CreateReadOnlyLock(name, label, _syncToken, requestOptions, context) + : DeleteReadOnlyLock(name, label, _syncToken, requestOptions, context); + return response; + } + + /// + /// Adds an external synchronization token to ensure service requests receive up-to-date values. + /// + /// The synchronization token value. + public virtual void UpdateSyncToken(string token) + { + Argument.AssertNotNull(token, nameof(token)); + _syncTokenPolicy.AddToken(token); + } + + private static RequestContext CreateRequestContext(ErrorOptions errorOptions, CancellationToken cancellationToken) + { + return new RequestContext() + { + ErrorOptions = errorOptions, + CancellationToken = cancellationToken + }; + } + + private class FeatureFlagRequestFailedDetailsParser : RequestFailedDetailsParser + { + private const string TroubleshootingMessage = + "For troubleshooting information, see https://aka.ms/azsdk/net/appconfiguration/troubleshoot."; + public override bool TryParse(Response response, out ResponseError error, out IDictionary data) + { + switch (response.Status) + { + case 409: + error = new ResponseError(null, $"The flag is read only. {TroubleshootingMessage}"); + data = null; + return true; + case 412: + error = new ResponseError(null, $"Flag was already present. {TroubleshootingMessage}"); + data = null; + return true; + default: + error = new ResponseError(null, TroubleshootingMessage); + data = null; + return true; + } + } + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/FeatureFlagClientOptions.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/FeatureFlagClientOptions.cs new file mode 100644 index 000000000000..11c847d9acee --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/FeatureFlagClientOptions.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Core; + +namespace Azure.Data.AppConfiguration +{ + /// + /// Options that allow users to configure the requests sent to the App Configuration feature flags service. + /// + [CodeGenSuppress("FeatureFlagClientOptions", typeof(ServiceVersion))] + public partial class FeatureFlagClientOptions : ClientOptions + { + private const ServiceVersion LatestVersion = ServiceVersion.V2023_11_01; + private const string AzConfigUsGovCloudHostName = "azconfig.azure.us"; + private const string AzConfigChinaCloudHostName = "azconfig.azure.cn"; + private const string AppConfigUsGovCloudHostName = "appconfig.azure.us"; + private const string AppConfigChinaCloudHostName = "appconfig.azure.cn"; + + /// + /// The versions of the App Configuration service supported by this client library. + /// + public enum ServiceVersion + { +#pragma warning disable CA1707 // Identifiers should not contain underscores + /// + /// Version 1.0. + /// + V1_0 = 0, + + /// + /// Version 2023-10-01. + /// + V2023_10_01 = 1, + + /// + /// Version 2023-11-01. + /// + V2023_11_01 = 2 + } + + /// + /// Gets or sets the Audience to use for authentication with Microsoft Entra. The audience is not considered when using a shared key. + /// + public AppConfigurationAudience? Audience { get; set; } + + internal string Version { get; } + + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// The of the service API used when + /// making requests. + /// + public FeatureFlagClientOptions(ServiceVersion version = LatestVersion) + { + Version = version switch + { + ServiceVersion.V1_0 => "1.0", + ServiceVersion.V2023_10_01 => "2023-10-01", + ServiceVersion.V2023_11_01 => "2023-11-01", + + _ => throw new NotSupportedException() + }; + this.ConfigureLogging(); + } + + internal string GetDefaultScope(Uri uri) + { + if (string.IsNullOrEmpty(Audience?.ToString())) + { + string host = uri.GetComponents(UriComponents.Host, UriFormat.SafeUnescaped); + return host switch + { + _ when host.EndsWith(AzConfigUsGovCloudHostName, StringComparison.InvariantCultureIgnoreCase) || host.EndsWith(AppConfigUsGovCloudHostName, StringComparison.InvariantCultureIgnoreCase) + => $"{AppConfigurationAudience.AzureGovernment}/.default", + _ when host.EndsWith(AzConfigChinaCloudHostName, StringComparison.InvariantCultureIgnoreCase) || host.EndsWith(AppConfigChinaCloudHostName, StringComparison.InvariantCultureIgnoreCase) + => $"{AppConfigurationAudience.AzureChina}/.default", + _ => $"{AppConfigurationAudience.AzurePublicCloud}/.default" + }; + } + + return $"{Audience}/.default"; + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/FeatureFlagClient_private.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/FeatureFlagClient_private.cs new file mode 100644 index 000000000000..61db9f4d6d6f --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/FeatureFlagClient_private.cs @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; + +namespace Azure.Data.AppConfiguration +{ + public partial class FeatureFlagClient + { + private const string AcceptDateTimeFormat = "R"; + + private static Response CreateResponse(Response response) + { + var options = ModelReaderWriterOptions.Json; + + FeatureFlag result = FeatureFlag.DeserializeFeatureFlag(response.Content.ToObjectFromJson(), options); + return Response.FromValue(result, response); + } + + private static Response CreateResourceModifiedResponse(Response response) + { + return new NoBodyResponse(response); + } + + private static void ParseConnectionString(string connectionString, out Uri uri, out string credential, out byte[] secret) + { + Debug.Assert(connectionString != null); // callers check this + + var parsed = ConnectionString.Parse(connectionString); + + uri = new Uri(parsed.GetRequired("Endpoint")); + credential = parsed.GetRequired("Id"); + try + { + secret = Convert.FromBase64String(parsed.GetRequired("Secret")); + } + catch (FormatException) + { + throw new InvalidOperationException("Specified Secret value isn't a valid base64 string"); + } + } + + private HttpMessage CreateNextGetFeatureFlagsRequest(string nextLink, string syncToken, string acceptDatetime, MatchConditions matchConditions, RequestContext context) + { + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Method = RequestMethod.Get; + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendRawNextLink(nextLink, false); + request.Uri = uri; + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (acceptDatetime != null) + { + request.Headers.SetValue("Accept-Datetime", acceptDatetime); + } + if (matchConditions != null) + { + request.Headers.Add(matchConditions); + } + request.Headers.SetValue("Accept", "application/problem+json, application/vnd.microsoft.appconfig.kvset+json"); + return message; + } + + private HttpMessage CreateNextGetRevisionsRequest(string nextLink, string syncToken, string acceptDatetime, RequestContext context) + { + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Method = RequestMethod.Get; + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendRawNextLink(nextLink, false); + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (acceptDatetime != null) + { + request.Headers.SetValue("Accept-Datetime", acceptDatetime); + } + request.Headers.SetValue("Accept", "application/problem+json, application/vnd.microsoft.appconfig.kvset+json"); + return message; + } + + /// + /// Parses the response of a request. + /// The "@nextLink" JSON property is not reliable since the service does not return a response body for 304 + /// responses. This method also attempts to extract the next link address from the "Link" header. + /// + private (List Values, string NextLink) ParseGetGetFeatureFlagsResponse(Response response) + { + var values = new List(); + string nextLink = null; + + if (response.Status == 200) + { + var document = response.ContentStream != null ? JsonDocument.Parse(response.ContentStream) : JsonDocument.Parse(response.Content); + + if (document.RootElement.TryGetProperty("items", out var itemsValue)) + { + foreach (var jsonItem in itemsValue.EnumerateArray()) + { + FeatureFlag setting = FeatureFlag.DeserializeFeatureFlag(jsonItem, default); + values.Add(setting); + } + } + + if (document.RootElement.TryGetProperty("@nextLink", out var nextLinkValue)) + { + nextLink = nextLinkValue.GetString(); + } + } + + // The "Link" header is formatted as: + // ; rel="next" + if (nextLink == null && response.Headers.TryGetValue("Link", out string linkHeader)) + { + int nextLinkEndIndex = linkHeader.IndexOf('>'); + nextLink = linkHeader.Substring(1, nextLinkEndIndex - 1); + } + + return (values, nextLink); + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationClientBuilderExtensions.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationClientBuilderExtensions.cs index 602e6b4b9c2f..078218fc04e8 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationClientBuilderExtensions.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationClientBuilderExtensions.cs @@ -5,6 +5,7 @@ #nullable disable +using System; using System.Diagnostics.CodeAnalysis; using Azure.Core.Extensions; using Azure.Data.AppConfiguration; @@ -24,5 +25,34 @@ public static IAzureClientBuilder(configuration); } + + /// Registers a client with the specified . + /// The builder to register with. + /// Connection string with authentication option and related parameters. + public static IAzureClientBuilder AddFeatureFlagClient(this TBuilder builder, string connectionString) + where TBuilder : IAzureClientFactoryBuilder + { + return builder.RegisterClientFactory(options => new FeatureFlagClient(connectionString, options)); + } + + /// Registers a client with the specified . + /// The builder to register with. + /// The referencing the app configuration storage. + public static IAzureClientBuilder AddFeatureFlagClient(this TBuilder builder, Uri endpoint) + where TBuilder : IAzureClientFactoryBuilderWithCredential + { + return builder.RegisterClientFactory((options, credential) => new FeatureFlagClient(endpoint, credential, options)); + } + + /// Registers a client with the specified . + /// The builder to register with. + /// The configuration to use for the client. + [RequiresUnreferencedCode("Requires unreferenced code until we opt into EnableConfigurationBindingGenerator.")] + [RequiresDynamicCode("Requires unreferenced code until we opt into EnableConfigurationBindingGenerator.")] + public static IAzureClientBuilder AddFeatureFlagClient(this TBuilder builder, TConfiguration configuration) + where TBuilder : IAzureClientFactoryBuilderWithConfiguration + { + return builder.RegisterClientFactory(configuration); + } } } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationModelFactory.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationModelFactory.cs index 820491f9d4f1..0e13937ff06d 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationModelFactory.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationModelFactory.cs @@ -16,6 +16,49 @@ namespace Azure.Data.AppConfiguration public static partial class ConfigurationModelFactory { + /// A key-value pair representing application settings. + /// + /// The primary identifier of the configuration setting. + /// A is used together with a to uniquely identify a configuration setting. + /// + /// + /// A value used to group configuration settings. + /// A is used together with a to uniquely identify a configuration setting. + /// + /// + /// The content type of the configuration setting's value. + /// Providing a proper content-type can enable transformations of values when they are retrieved by applications. + /// + /// The configuration setting's value. + /// The last time a modifying operation was performed on the given configuration setting. + /// + /// A dictionary of tags used to assign additional properties to a configuration setting. + /// These can be used to indicate how a configuration setting may be applied. + /// + /// The description of the key-value. + /// + /// A value indicating whether the configuration setting is read only. + /// A read only configuration setting may not be modified until it is made writable. + /// + /// An ETag indicating the state of a configuration setting within a configuration store. + /// A new instance for mocking. + public static ConfigurationSetting ConfigurationSetting(string key = default, string label = default, string contentType = default, string value = default, DateTimeOffset? lastModified = default, IDictionary tags = default, string description = default, bool? isReadOnly = default, ETag eTag = default) + { + tags ??= new ChangeTrackingDictionary(); + + return new ConfigurationSetting( + key, + label, + contentType, + value, + lastModified, + tags, + description, + isReadOnly, + eTag, + additionalBinaryDataProperties: null); + } + /// A snapshot is a named, immutable subset of an App Configuration store's key-values. /// The name of the snapshot. /// The current status of the snapshot. @@ -27,9 +70,10 @@ public static partial class ConfigurationModelFactory /// The size in bytes of the snapshot. /// The amount of key-values in the snapshot. /// The tags of the snapshot. + /// The description of the snapshot. /// A value representing the current state of the snapshot. /// A new instance for mocking. - public static ConfigurationSnapshot ConfigurationSnapshot(string name = default, ConfigurationSnapshotStatus? status = default, IEnumerable filters = default, SnapshotComposition? snapshotComposition = default, DateTimeOffset? createdOn = default, DateTimeOffset? expiresOn = default, TimeSpan? retentionPeriod = default, long? sizeInBytes = default, long? itemCount = default, IDictionary tags = default, ETag eTag = default) + public static ConfigurationSnapshot ConfigurationSnapshot(string name = default, ConfigurationSnapshotStatus? status = default, IEnumerable filters = default, SnapshotComposition? snapshotComposition = default, DateTimeOffset? createdOn = default, DateTimeOffset? expiresOn = default, TimeSpan? retentionPeriod = default, long? sizeInBytes = default, long? itemCount = default, IDictionary tags = default, string description = default, ETag eTag = default) { filters ??= new ChangeTrackingList(); tags ??= new ChangeTrackingDictionary(); @@ -45,6 +89,7 @@ public static ConfigurationSnapshot ConfigurationSnapshot(string name = default, sizeInBytes, itemCount, tags, + description, eTag, additionalBinaryDataProperties: null); } @@ -64,12 +109,112 @@ public static ConfigurationSettingsFilter ConfigurationSettingsFilter(string key return new ConfigurationSettingsFilter(key, label, tags.ToList(), additionalBinaryDataProperties: null); } - /// Labels are used to group key-values. + /// Labels are used to group key values or feature flags. /// The name of the label. /// A new instance for mocking. public static SettingLabel SettingLabel(string name = default) { return new SettingLabel(name, additionalBinaryDataProperties: null); } + + /// The conditions that must be met for the feature flag to be enabled. + /// The requirement type for the conditions. + /// The filters that will conditionally enable or disable the flag. + /// A new instance for mocking. + public static FeatureFlagConditions FeatureFlagConditions(RequirementType? requirementType = default, IEnumerable filters = default) + { + filters ??= new ChangeTrackingList(); + + return new FeatureFlagConditions(requirementType, filters.ToList(), additionalBinaryDataProperties: null); + } + + /// Feature Flag Filter object. + /// Gets the name of the feature filter. + /// Gets the parameters of the feature filter. + /// A new instance for mocking. + public static FeatureFlagFilter FeatureFlagFilter(string name = default, IDictionary parameters = default) + { + parameters ??= new ChangeTrackingDictionary(); + + return new FeatureFlagFilter(name, parameters, additionalBinaryDataProperties: null); + } + + /// Feature Flag Variants object. + /// The name of the variant. + /// The value of the variant. + /// The content type of the value stored within the key-value. + /// Determines if the variant should override the status of the flag. + /// A new instance for mocking. + public static FeatureFlagVariantDefinition FeatureFlagVariantDefinition(string name = default, string value = default, string contentType = default, StatusOverride? statusOverride = default) + { + return new FeatureFlagVariantDefinition(name, value, contentType, statusOverride, additionalBinaryDataProperties: null); + } + + /// Defines how to allocate variants based on context. + /// The default variant to use when disabled. + /// The default variant to use when enabled but not allocated. + /// Allocates percentiles to variants. + /// Allocates users to variants. + /// Allocates groups to variants. + /// The seed used for random allocation. + /// A new instance for mocking. + public static FeatureFlagAllocation FeatureFlagAllocation(string defaultWhenDisabled = default, string defaultWhenEnabled = default, IEnumerable percentile = default, IEnumerable user = default, IEnumerable @group = default, string seed = default) + { + percentile ??= new ChangeTrackingList(); + user ??= new ChangeTrackingList(); + @group ??= new ChangeTrackingList(); + + return new FeatureFlagAllocation( + defaultWhenDisabled, + defaultWhenEnabled, + percentile.ToList(), + user.ToList(), + @group.ToList(), + seed, + additionalBinaryDataProperties: null); + } + + /// Feature Flag PercentileAllocation object. + /// The variant to allocate these percentiles to. + /// The lower bounds for this percentile allocation. + /// The upper bounds for this percentile allocation. + /// A new instance for mocking. + public static PercentileAllocation PercentileAllocation(string variant = default, int @from = default, int to = default) + { + return new PercentileAllocation(variant, @from, to, additionalBinaryDataProperties: null); + } + + /// Feature Flag UserAllocation object. + /// The variant to allocate these percentiles to. + /// The users to get this variant. + /// A new instance for mocking. + public static UserAllocation UserAllocation(string variant = default, IEnumerable users = default) + { + users ??= new ChangeTrackingList(); + + return new UserAllocation(variant, users.ToList(), additionalBinaryDataProperties: null); + } + + /// Feature Flag GroupAllocation object. + /// The variant to allocate these percentiles to. + /// The groups to get this variant. + /// A new instance for mocking. + public static GroupAllocation GroupAllocation(string variant = default, IEnumerable groups = default) + { + groups ??= new ChangeTrackingList(); + + return new GroupAllocation(variant, groups.ToList(), additionalBinaryDataProperties: null); + } + + /// Feature Flag Telemetry object. + /// The enabled state of the telemetry. + /// The metadata to include on outbound telemetry. + /// A new instance for mocking. + public static FeatureFlagTelemetryConfiguration FeatureFlagTelemetryConfiguration(bool enabled = default, IDictionary metadata = default) + { + metadata ??= new ChangeTrackingDictionary(); + + return new FeatureFlagTelemetryConfiguration(enabled, metadata, additionalBinaryDataProperties: null); + } } } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSetting.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSetting.Serialization.cs index b1bb248b129a..11c2d90d70d2 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSetting.Serialization.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSetting.Serialization.cs @@ -76,6 +76,11 @@ protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWrit } writer.WriteEndObject(); } + if (Optional.IsDefined(Description)) + { + writer.WritePropertyName("description"u8); + writer.WriteStringValue(Description); + } if (Optional.IsDefined(IsReadOnly)) { writer.WritePropertyName("locked"u8); @@ -131,6 +136,7 @@ internal static ConfigurationSetting DeserializeConfigurationSetting(JsonElement string value = default; DateTimeOffset? lastModified = default; IDictionary tags = default; + string description = default; bool? isReadOnly = default; ETag eTag = default; IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); @@ -186,6 +192,11 @@ internal static ConfigurationSetting DeserializeConfigurationSetting(JsonElement tags = dictionary; continue; } + if (prop.NameEquals("description"u8)) + { + description = prop.Value.GetString(); + continue; + } if (prop.NameEquals("locked"u8)) { if (prop.Value.ValueKind == JsonValueKind.Null) @@ -212,6 +223,7 @@ internal static ConfigurationSetting DeserializeConfigurationSetting(JsonElement value, lastModified, tags ?? new ChangeTrackingDictionary(), + description, isReadOnly, eTag, additionalBinaryDataProperties); diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSetting.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSetting.cs index be3868133dad..32d1bf36ba07 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSetting.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSetting.cs @@ -36,13 +36,14 @@ public partial class ConfigurationSetting /// A dictionary of tags used to assign additional properties to a configuration setting. /// These can be used to indicate how a configuration setting may be applied. /// + /// The description of the key-value. /// /// A value indicating whether the configuration setting is read only. /// A read only configuration setting may not be modified until it is made writable. /// /// An ETag indicating the state of a configuration setting within a configuration store. /// Keeps track of any properties unknown to the library. - internal ConfigurationSetting(string key, string label, string contentType, string value, DateTimeOffset? lastModified, IDictionary tags, bool? isReadOnly, ETag eTag, IDictionary additionalBinaryDataProperties) + internal ConfigurationSetting(string key, string label, string contentType, string value, DateTimeOffset? lastModified, IDictionary tags, string description, bool? isReadOnly, ETag eTag, IDictionary additionalBinaryDataProperties) { Key = key; Label = label; @@ -50,6 +51,7 @@ internal ConfigurationSetting(string key, string label, string contentType, stri Value = value; LastModified = lastModified; Tags = tags; + Description = description; IsReadOnly = isReadOnly; ETag = eTag; _additionalBinaryDataProperties = additionalBinaryDataProperties; diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSnapshot.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSnapshot.cs index 150591d5fce8..c4894989fd1c 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSnapshot.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/ConfigurationSnapshot.cs @@ -40,9 +40,10 @@ public ConfigurationSnapshot(IEnumerable filters) /// The size in bytes of the snapshot. /// The amount of key-values in the snapshot. /// The tags of the snapshot. + /// The description of the snapshot. /// A value representing the current state of the snapshot. /// Keeps track of any properties unknown to the library. - internal ConfigurationSnapshot(string name, ConfigurationSnapshotStatus? status, IList filters, SnapshotComposition? snapshotComposition, DateTimeOffset? createdOn, DateTimeOffset? expiresOn, TimeSpan? retentionPeriod, long? sizeInBytes, long? itemCount, IDictionary tags, ETag eTag, IDictionary additionalBinaryDataProperties) + internal ConfigurationSnapshot(string name, ConfigurationSnapshotStatus? status, IList filters, SnapshotComposition? snapshotComposition, DateTimeOffset? createdOn, DateTimeOffset? expiresOn, TimeSpan? retentionPeriod, long? sizeInBytes, long? itemCount, IDictionary tags, string description, ETag eTag, IDictionary additionalBinaryDataProperties) { Name = name; Status = status; @@ -54,8 +55,12 @@ internal ConfigurationSnapshot(string name, ConfigurationSnapshotStatus? status, SizeInBytes = sizeInBytes; ItemCount = itemCount; Tags = tags; + Description = description; ETag = eTag; _additionalBinaryDataProperties = additionalBinaryDataProperties; } + + /// The description of the snapshot. + public string Description { get; set; } } } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlag.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlag.Serialization.cs new file mode 100644 index 000000000000..72aef63c87e3 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlag.Serialization.cs @@ -0,0 +1,373 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; +using Azure; +using Azure.Core; + +namespace Azure.Data.AppConfiguration +{ + /// A feature flag. + public partial class FeatureFlag : IJsonModel + { + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlag)} does not support writing '{format}' format."); + } + if (options.Format != "W") + { + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + } + if (Optional.IsDefined(Enabled)) + { + writer.WritePropertyName("enabled"u8); + writer.WriteBooleanValue(Enabled.Value); + } + if (Optional.IsDefined(Label)) + { + writer.WritePropertyName("label"u8); + writer.WriteStringValue(Label); + } + if (Optional.IsDefined(Description)) + { + writer.WritePropertyName("description"u8); + writer.WriteStringValue(Description); + } + if (Optional.IsDefined(Alias)) + { + writer.WritePropertyName("alias"u8); + writer.WriteStringValue(Alias); + } + if (Optional.IsDefined(Conditions)) + { + writer.WritePropertyName("conditions"u8); + writer.WriteObjectValue(Conditions, options); + } + if (Optional.IsCollectionDefined(Variants)) + { + writer.WritePropertyName("variants"u8); + writer.WriteStartArray(); + foreach (FeatureFlagVariantDefinition item in Variants) + { + writer.WriteObjectValue(item, options); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(Allocation)) + { + writer.WritePropertyName("allocation"u8); + writer.WriteObjectValue(Allocation, options); + } + if (Optional.IsDefined(Telemetry)) + { + writer.WritePropertyName("telemetry"u8); + writer.WriteObjectValue(Telemetry, options); + } + if (Optional.IsCollectionDefined(Tags)) + { + writer.WritePropertyName("tags"u8); + writer.WriteStartObject(); + foreach (var item in Tags) + { + writer.WritePropertyName(item.Key); + if (item.Value == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item.Value); + } + writer.WriteEndObject(); + } + if (Optional.IsDefined(IsReadOnly)) + { + writer.WritePropertyName("locked"u8); + writer.WriteBooleanValue(IsReadOnly.Value); + } + if (options.Format != "W" && Optional.IsDefined(LastModified)) + { + writer.WritePropertyName("last_modified"u8); + writer.WriteStringValue(LastModified.Value, "O"); + } + if (options.Format != "W" && Optional.IsDefined(ETag)) + { + writer.WritePropertyName("etag"u8); + SerializationEtag(writer, options); + } + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + FeatureFlag IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual FeatureFlag JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlag)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeFeatureFlag(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static FeatureFlag DeserializeFeatureFlag(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string name = default; + bool? enabled = default; + string label = default; + string description = default; + string @alias = default; + FeatureFlagConditions conditions = default; + IList variants = default; + FeatureFlagAllocation allocation = default; + FeatureFlagTelemetryConfiguration telemetry = default; + IDictionary tags = default; + bool? isReadOnly = default; + DateTimeOffset? lastModified = default; + ETag eTag = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("name"u8)) + { + name = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("enabled"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + enabled = prop.Value.GetBoolean(); + continue; + } + if (prop.NameEquals("label"u8)) + { + label = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("description"u8)) + { + description = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("alias"u8)) + { + @alias = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("conditions"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + conditions = FeatureFlagConditions.DeserializeFeatureFlagConditions(prop.Value, options); + continue; + } + if (prop.NameEquals("variants"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in prop.Value.EnumerateArray()) + { + array.Add(FeatureFlagVariantDefinition.DeserializeFeatureFlagVariantDefinition(item, options)); + } + variants = array; + continue; + } + if (prop.NameEquals("allocation"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + allocation = FeatureFlagAllocation.DeserializeFeatureFlagAllocation(prop.Value, options); + continue; + } + if (prop.NameEquals("telemetry"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + telemetry = FeatureFlagTelemetryConfiguration.DeserializeFeatureFlagTelemetryConfiguration(prop.Value, options); + continue; + } + if (prop.NameEquals("tags"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + Dictionary dictionary = new Dictionary(); + foreach (var prop0 in prop.Value.EnumerateObject()) + { + if (prop0.Value.ValueKind == JsonValueKind.Null) + { + dictionary.Add(prop0.Name, null); + } + else + { + dictionary.Add(prop0.Name, prop0.Value.GetString()); + } + } + tags = dictionary; + continue; + } + if (prop.NameEquals("locked"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + isReadOnly = prop.Value.GetBoolean(); + continue; + } + if (prop.NameEquals("last_modified"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + lastModified = prop.Value.GetDateTimeOffset("O"); + continue; + } + if (prop.NameEquals("etag"u8)) + { + DeserializeEtag(prop, ref eTag); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new FeatureFlag( + name, + enabled, + label, + description, + @alias, + conditions, + variants ?? new ChangeTrackingList(), + allocation, + telemetry, + tags ?? new ChangeTrackingDictionary(), + isReadOnly, + lastModified, + eTag, + additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(FeatureFlag)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + FeatureFlag IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual FeatureFlag PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializeFeatureFlag(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(FeatureFlag)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// The to serialize into . + public static implicit operator RequestContent(FeatureFlag featureFlag) + { + if (featureFlag == null) + { + return null; + } + Utf8JsonRequestContent content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteObjectValue(featureFlag, ModelSerializationExtensions.WireOptions); + return content; + } + + /// The to deserialize the from. + public static explicit operator FeatureFlag(Response result) + { + using Response response = result; + using JsonDocument document = JsonDocument.Parse(response.Content); + return DeserializeFeatureFlag(document.RootElement, ModelSerializationExtensions.WireOptions); + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlag.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlag.cs new file mode 100644 index 000000000000..9db401497d12 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlag.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using Azure; + +namespace Azure.Data.AppConfiguration +{ + /// A feature flag. + public partial class FeatureFlag + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + /// The name of the feature flag. + /// The enabled state of the feature flag. + /// The label the feature flag belongs to. + /// The description of the feature flag. + /// The alias of the feature flag. + /// The conditions of the feature flag. + /// A list of variant definitions for the feature flag. + /// The allocation of the feature flag. + /// The telemetry settings of the feature flag. + /// + /// A dictionary of tags used to assign additional properties to a feature flag. + /// These can be used to indicate how a feature flag may be applied. + /// + /// + /// A value indicating whether the feature flag is read only. + /// A read only feature flag may not be modified until it is made writable. + /// + /// The last time a modifying operation was performed on the given feature flag. + /// An ETag indicating the state of a feature flag within a configuration store. + /// Keeps track of any properties unknown to the library. + internal FeatureFlag(string name, bool? enabled, string label, string description, string @alias, FeatureFlagConditions conditions, IList variants, FeatureFlagAllocation allocation, FeatureFlagTelemetryConfiguration telemetry, IDictionary tags, bool? isReadOnly, DateTimeOffset? lastModified, ETag eTag, IDictionary additionalBinaryDataProperties) + { + Name = name; + Enabled = enabled; + Label = label; + Description = description; + Alias = @alias; + Conditions = conditions; + Variants = variants; + Allocation = allocation; + Telemetry = telemetry; + Tags = tags; + IsReadOnly = isReadOnly; + LastModified = lastModified; + ETag = eTag; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// The name of the feature flag. + public string Name { get; } + + /// The enabled state of the feature flag. + public bool? Enabled { get; set; } + + /// The label the feature flag belongs to. + public string Label { get; set; } + + /// The description of the feature flag. + public string Description { get; set; } + + /// The alias of the feature flag. + public string Alias { get; set; } + + /// The conditions of the feature flag. + public FeatureFlagConditions Conditions { get; set; } + + /// The allocation of the feature flag. + public FeatureFlagAllocation Allocation { get; set; } + + /// The telemetry settings of the feature flag. + public FeatureFlagTelemetryConfiguration Telemetry { get; set; } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagAllocation.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagAllocation.Serialization.cs new file mode 100644 index 000000000000..d436521c5cb2 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagAllocation.Serialization.cs @@ -0,0 +1,244 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace Azure.Data.AppConfiguration +{ + /// Defines how to allocate variants based on context. + public partial class FeatureFlagAllocation : IJsonModel + { + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagAllocation)} does not support writing '{format}' format."); + } + if (Optional.IsDefined(DefaultWhenDisabled)) + { + writer.WritePropertyName("default_when_disabled"u8); + writer.WriteStringValue(DefaultWhenDisabled); + } + if (Optional.IsDefined(DefaultWhenEnabled)) + { + writer.WritePropertyName("default_when_enabled"u8); + writer.WriteStringValue(DefaultWhenEnabled); + } + if (Optional.IsCollectionDefined(Percentile)) + { + writer.WritePropertyName("percentile"u8); + writer.WriteStartArray(); + foreach (PercentileAllocation item in Percentile) + { + writer.WriteObjectValue(item, options); + } + writer.WriteEndArray(); + } + if (Optional.IsCollectionDefined(User)) + { + writer.WritePropertyName("user"u8); + writer.WriteStartArray(); + foreach (UserAllocation item in User) + { + writer.WriteObjectValue(item, options); + } + writer.WriteEndArray(); + } + if (Optional.IsCollectionDefined(Group)) + { + writer.WritePropertyName("group"u8); + writer.WriteStartArray(); + foreach (GroupAllocation item in Group) + { + writer.WriteObjectValue(item, options); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(Seed)) + { + writer.WritePropertyName("seed"u8); + writer.WriteStringValue(Seed); + } + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + FeatureFlagAllocation IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual FeatureFlagAllocation JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagAllocation)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeFeatureFlagAllocation(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static FeatureFlagAllocation DeserializeFeatureFlagAllocation(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string defaultWhenDisabled = default; + string defaultWhenEnabled = default; + IList percentile = default; + IList user = default; + IList @group = default; + string seed = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("default_when_disabled"u8)) + { + defaultWhenDisabled = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("default_when_enabled"u8)) + { + defaultWhenEnabled = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("percentile"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in prop.Value.EnumerateArray()) + { + array.Add(PercentileAllocation.DeserializePercentileAllocation(item, options)); + } + percentile = array; + continue; + } + if (prop.NameEquals("user"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in prop.Value.EnumerateArray()) + { + array.Add(UserAllocation.DeserializeUserAllocation(item, options)); + } + user = array; + continue; + } + if (prop.NameEquals("group"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in prop.Value.EnumerateArray()) + { + array.Add(GroupAllocation.DeserializeGroupAllocation(item, options)); + } + @group = array; + continue; + } + if (prop.NameEquals("seed"u8)) + { + seed = prop.Value.GetString(); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new FeatureFlagAllocation( + defaultWhenDisabled, + defaultWhenEnabled, + percentile ?? new ChangeTrackingList(), + user ?? new ChangeTrackingList(), + @group ?? new ChangeTrackingList(), + seed, + additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(FeatureFlagAllocation)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + FeatureFlagAllocation IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual FeatureFlagAllocation PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializeFeatureFlagAllocation(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(FeatureFlagAllocation)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagAllocation.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagAllocation.cs new file mode 100644 index 000000000000..b18df8bb60ea --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagAllocation.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Azure.Data.AppConfiguration +{ + /// Defines how to allocate variants based on context. + public partial class FeatureFlagAllocation + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + public FeatureFlagAllocation() + { + Percentile = new ChangeTrackingList(); + User = new ChangeTrackingList(); + Group = new ChangeTrackingList(); + } + + /// Initializes a new instance of . + /// The default variant to use when disabled. + /// The default variant to use when enabled but not allocated. + /// Allocates percentiles to variants. + /// Allocates users to variants. + /// Allocates groups to variants. + /// The seed used for random allocation. + /// Keeps track of any properties unknown to the library. + internal FeatureFlagAllocation(string defaultWhenDisabled, string defaultWhenEnabled, IList percentile, IList user, IList @group, string seed, IDictionary additionalBinaryDataProperties) + { + DefaultWhenDisabled = defaultWhenDisabled; + DefaultWhenEnabled = defaultWhenEnabled; + Percentile = percentile; + User = user; + Group = @group; + Seed = seed; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// The default variant to use when disabled. + public string DefaultWhenDisabled { get; set; } + + /// The default variant to use when enabled but not allocated. + public string DefaultWhenEnabled { get; set; } + + /// Allocates percentiles to variants. + public IList Percentile { get; } + + /// Allocates users to variants. + public IList User { get; } + + /// Allocates groups to variants. + public IList Group { get; } + + /// The seed used for random allocation. + public string Seed { get; set; } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClient.RestClient.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClient.RestClient.cs new file mode 100644 index 000000000000..61b25a920582 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClient.RestClient.cs @@ -0,0 +1,408 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using Azure; +using Azure.Core; + +namespace Azure.Data.AppConfiguration +{ + /// + public partial class FeatureFlagClient + { + private static ResponseClassifier _pipelineMessageClassifier200; + private static ResponseClassifier _pipelineMessageClassifier200204; + + private static ResponseClassifier PipelineMessageClassifier200 => _pipelineMessageClassifier200 = new StatusCodeClassifier(stackalloc ushort[] { 200 }); + + private static ResponseClassifier PipelineMessageClassifier200204 => _pipelineMessageClassifier200204 = new StatusCodeClassifier(stackalloc ushort[] { 200, 204 }); + + internal HttpMessage CreateGetFeatureFlagsRequest(string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/ff", false); + uri.AppendQuery("api-version", _apiVersion, true); + if (name != null) + { + uri.AppendQuery("name", name, true); + } + if (label != null) + { + uri.AppendQuery("label", label, true); + } + if (after != null) + { + uri.AppendQuery("After", after, true); + } + if (@select != null && !(@select is ChangeTrackingList changeTrackingList && changeTrackingList.IsUndefined)) + { + uri.AppendQueryDelimited("$Select", @select, ",", null, true); + } + if (tags != null && !(tags is ChangeTrackingList changeTrackingList0 && changeTrackingList0.IsUndefined)) + { + foreach (var @param in tags) + { + uri.AppendQuery("tags", @param, true); + } + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Get; + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (acceptDatetime != null) + { + request.Headers.SetValue("Accept-Datetime", acceptDatetime); + } + if (matchConditions != null) + { + request.Headers.Add(matchConditions); + } + request.Headers.SetValue("Accept", "application/json;profile=\\\"https://azconfig.io/mime-profiles/feature-management/ffset\\\";charset=utf-8, application/problem+json"); + return message; + } + + internal HttpMessage CreateNextGetFeatureFlagsRequest(Uri nextPage, string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(nextPage); + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Get; + request.Headers.SetValue("Accept", "application/json;profile=\\\"https://azconfig.io/mime-profiles/feature-management/ffset\\\";charset=utf-8, application/problem+json"); + return message; + } + + internal HttpMessage CreateCheckFeatureFlagsRequest(string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/ff", false); + uri.AppendQuery("api-version", _apiVersion, true); + if (name != null) + { + uri.AppendQuery("name", name, true); + } + if (label != null) + { + uri.AppendQuery("label", label, true); + } + if (after != null) + { + uri.AppendQuery("After", after, true); + } + if (@select != null && !(@select is ChangeTrackingList changeTrackingList && changeTrackingList.IsUndefined)) + { + uri.AppendQueryDelimited("$Select", @select, ",", null, true); + } + if (tags != null && !(tags is ChangeTrackingList changeTrackingList0 && changeTrackingList0.IsUndefined)) + { + foreach (var @param in tags) + { + uri.AppendQuery("tags", @param, true); + } + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Head; + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (acceptDatetime != null) + { + request.Headers.SetValue("Accept-Datetime", acceptDatetime); + } + if (matchConditions != null) + { + request.Headers.Add(matchConditions); + } + return message; + } + + internal HttpMessage CreateGetFeatureFlagRequest(string name, string label, IEnumerable @select, string syncToken, string acceptDatetime, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/ff/", false); + uri.AppendPath(name, true); + uri.AppendQuery("api-version", _apiVersion, true); + if (label != null) + { + uri.AppendQuery("label", label, true); + } + if (@select != null && !(@select is ChangeTrackingList changeTrackingList && changeTrackingList.IsUndefined)) + { + uri.AppendQueryDelimited("$Select", @select, ",", null, true); + } + if (tags != null && !(tags is ChangeTrackingList changeTrackingList0 && changeTrackingList0.IsUndefined)) + { + foreach (var @param in tags) + { + uri.AppendQuery("tags", @param, true); + } + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Get; + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (acceptDatetime != null) + { + request.Headers.SetValue("Accept-Datetime", acceptDatetime); + } + if (matchConditions != null) + { + request.Headers.Add(matchConditions); + } + request.Headers.SetValue("Accept", "application/json;profile=\\\"https://azconfig.io/mime-profiles/feature-management/ff\\\"​;charset=utf-8, application/problem+json"); + return message; + } + + internal HttpMessage CreateCheckFeatureFlagRequest(string name, string label, string syncToken, string acceptDatetime, MatchConditions matchConditions, IEnumerable @select, IEnumerable tags, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/ff/", false); + uri.AppendPath(name, true); + uri.AppendQuery("api-version", _apiVersion, true); + if (label != null) + { + uri.AppendQuery("label", label, true); + } + if (@select != null && !(@select is ChangeTrackingList changeTrackingList && changeTrackingList.IsUndefined)) + { + uri.AppendQueryDelimited("$Select", @select, ",", null, true); + } + if (tags != null && !(tags is ChangeTrackingList changeTrackingList0 && changeTrackingList0.IsUndefined)) + { + foreach (var @param in tags) + { + uri.AppendQuery("tags", @param, true); + } + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Head; + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (acceptDatetime != null) + { + request.Headers.SetValue("Accept-Datetime", acceptDatetime); + } + if (matchConditions != null) + { + request.Headers.Add(matchConditions); + } + return message; + } + + internal HttpMessage CreatePutFeatureFlagRequest(string name, RequestContent content, string label, string syncToken, MatchConditions matchConditions, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/ff/", false); + uri.AppendPath(name, true); + uri.AppendQuery("api-version", _apiVersion, true); + if (label != null) + { + uri.AppendQuery("label", label, true); + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Put; + request.Headers.SetValue("Content-Type", "application/json;profile=\\\"https://azconfig.io/mime-profiles/feature-management/ff\\\"​;charset=utf-8"); + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (matchConditions != null) + { + request.Headers.Add(matchConditions); + } + request.Headers.SetValue("Accept", "application/json;profile=\\\"https://azconfig.io/mime-profiles/feature-management/ff\\\"​;charset=utf-8, application/problem+json"); + request.Content = content; + return message; + } + + internal HttpMessage CreateDeleteFeatureFlagRequest(string name, string label, string syncToken, ETag? ifMatch, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/ff/", false); + uri.AppendPath(name, true); + uri.AppendQuery("api-version", _apiVersion, true); + if (label != null) + { + uri.AppendQuery("label", label, true); + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200204); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Delete; + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (ifMatch != null) + { + request.Headers.Add("If-Match", ifMatch.Value); + } + request.Headers.SetValue("Accept", "application/json;profile=\\\"https://azconfig.io/mime-profiles/feature-management/ff\\\"​;charset=utf-8, application/problem+json"); + return message; + } + + internal HttpMessage CreateGetRevisionsRequest(string name, string label, string after, IEnumerable @select, IEnumerable tags, string syncToken, MatchConditions matchConditions, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/revisions", false); + uri.AppendQuery("api-version", _apiVersion, true); + if (name != null) + { + uri.AppendQuery("name", name, true); + } + if (label != null) + { + uri.AppendQuery("label", label, true); + } + if (after != null) + { + uri.AppendQuery("After", after, true); + } + if (@select != null && !(@select is ChangeTrackingList changeTrackingList && changeTrackingList.IsUndefined)) + { + uri.AppendQueryDelimited("$Select", @select, ",", null, true); + } + if (tags != null && !(tags is ChangeTrackingList changeTrackingList0 && changeTrackingList0.IsUndefined)) + { + foreach (var @param in tags) + { + uri.AppendQuery("tags", @param, true); + } + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Get; + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (matchConditions != null) + { + request.Headers.Add(matchConditions); + } + request.Headers.SetValue("Accept", "application/json;profile=\\\"https://azconfig.io/mime-profiles/feature-management/ffset\\\"​;charset=utf-8, application/problem+json"); + return message; + } + + internal HttpMessage CreateCheckRevisionsRequest(string name, string label, string after, IEnumerable @select, IEnumerable tags, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/revisions", false); + uri.AppendQuery("api-version", _apiVersion, true); + if (name != null) + { + uri.AppendQuery("name", name, true); + } + if (label != null) + { + uri.AppendQuery("label", label, true); + } + if (after != null) + { + uri.AppendQuery("After", after, true); + } + if (@select != null && !(@select is ChangeTrackingList changeTrackingList && changeTrackingList.IsUndefined)) + { + uri.AppendQueryDelimited("$Select", @select, ",", null, true); + } + if (tags != null && !(tags is ChangeTrackingList changeTrackingList0 && changeTrackingList0.IsUndefined)) + { + foreach (var @param in tags) + { + uri.AppendQuery("tags", @param, true); + } + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Head; + return message; + } + + internal HttpMessage CreateCreateReadOnlyLockRequest(string name, string label, string syncToken, MatchConditions matchConditions, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/locks/", false); + uri.AppendPath(name, true); + uri.AppendQuery("api-version", _apiVersion, true); + if (label != null) + { + uri.AppendQuery("label", label, true); + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Put; + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (matchConditions != null) + { + request.Headers.Add(matchConditions); + } + request.Headers.SetValue("Accept", "application/json;profile=\\\"https://azconfig.io/mime-profiles/feature-management/ff\\\"​;charset=utf-8, application/problem+json"); + return message; + } + + internal HttpMessage CreateDeleteReadOnlyLockRequest(string name, string label, string syncToken, MatchConditions matchConditions, RequestContext context) + { + RawRequestUriBuilder uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/feature-management/locks/", false); + uri.AppendPath(name, true); + uri.AppendQuery("api-version", _apiVersion, true); + if (label != null) + { + uri.AppendQuery("label", label, true); + } + HttpMessage message = Pipeline.CreateMessage(context, PipelineMessageClassifier200); + Request request = message.Request; + request.Uri = uri; + request.Method = RequestMethod.Delete; + if (syncToken != null) + { + request.Headers.SetValue("Sync-Token", syncToken); + } + if (matchConditions != null) + { + request.Headers.Add(matchConditions); + } + request.Headers.SetValue("Accept", "application/json;profile=\\\"https://azconfig.io/mime-profiles/feature-management/ff\\\"​;charset=utf-8, application/problem+json"); + return message; + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClient.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClient.cs new file mode 100644 index 000000000000..3d79050a30c5 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClient.cs @@ -0,0 +1,1263 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; + +namespace Azure.Data.AppConfiguration +{ + /// The FeatureFlagClient. + public partial class FeatureFlagClient + { + private readonly Uri _endpoint; + /// A credential used to authenticate to the service. + private readonly AzureKeyCredential _keyCredential; + private const string AuthorizationHeader = "Connection String"; + /// A credential used to authenticate to the service. + private readonly TokenCredential _tokenCredential; + private static readonly string[] AuthorizationScopes = new string[] { "https://azconfig.io/.default" }; + private readonly string _apiVersion; + + /// Initializes a new instance of FeatureFlagClient for mocking. + protected FeatureFlagClient() + { + } + + /// Initializes a new instance of FeatureFlagClient. + /// Service endpoint. + /// A credential used to authenticate to the service. + /// or is null. + public FeatureFlagClient(Uri endpoint, TokenCredential credential) : this(endpoint, credential, new FeatureFlagClientOptions()) + { + } + + /// The HTTP pipeline for sending and receiving REST requests and responses. + public virtual HttpPipeline Pipeline { get; } + + /// The ClientDiagnostics is used to provide tracing support for the client library. + internal ClientDiagnostics ClientDiagnostics { get; } + + /// + /// [Protocol Method] Gets a list of feature flags. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Pageable GetFeatureFlags(string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.GetFeatureFlags"); + scope.Start(); + try + { + return new FeatureFlagClientGetFeatureFlagsCollectionResult( + this, + name, + label, + syncToken, + after, + acceptDatetime, + @select, + matchConditions, + tags, + context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Gets a list of feature flags. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual AsyncPageable GetFeatureFlagsAsync(string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.GetFeatureFlags"); + scope.Start(); + try + { + return new FeatureFlagClientGetFeatureFlagsAsyncCollectionResult( + this, + name, + label, + syncToken, + after, + acceptDatetime, + @select, + matchConditions, + tags, + context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Gets a list of feature flags. + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Pageable GetFeatureFlags(string name = default, string label = default, string syncToken = default, string after = default, string acceptDatetime = default, IEnumerable @select = default, MatchConditions matchConditions = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + return new FeatureFlagClientGetFeatureFlagsCollectionResultOfT( + this, + name, + label, + syncToken, + after, + acceptDatetime, + @select, + matchConditions, + tags, + cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + } + + /// Gets a list of feature flags. + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual AsyncPageable GetFeatureFlagsAsync(string name = default, string label = default, string syncToken = default, string after = default, string acceptDatetime = default, IEnumerable @select = default, MatchConditions matchConditions = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + return new FeatureFlagClientGetFeatureFlagsAsyncCollectionResultOfT( + this, + name, + label, + syncToken, + after, + acceptDatetime, + @select, + matchConditions, + tags, + cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + } + + /// + /// [Protocol Method] Requests the headers and status of the given resource. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response CheckFeatureFlags(string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.CheckFeatureFlags"); + scope.Start(); + try + { + using HttpMessage message = CreateCheckFeatureFlagsRequest(name, label, syncToken, after, acceptDatetime, @select, matchConditions, tags, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Requests the headers and status of the given resource. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task CheckFeatureFlagsAsync(string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.CheckFeatureFlags"); + scope.Start(); + try + { + using HttpMessage message = CreateCheckFeatureFlagsRequest(name, label, syncToken, after, acceptDatetime, @select, matchConditions, tags, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Requests the headers and status of the given resource. + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Response CheckFeatureFlags(string name = default, string label = default, string syncToken = default, string after = default, string acceptDatetime = default, IEnumerable @select = default, MatchConditions matchConditions = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + return CheckFeatureFlags(name, label, syncToken, after, acceptDatetime, @select, matchConditions, tags, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + } + + /// Requests the headers and status of the given resource. + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual async Task CheckFeatureFlagsAsync(string name = default, string label = default, string syncToken = default, string after = default, string acceptDatetime = default, IEnumerable @select = default, MatchConditions matchConditions = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + return await CheckFeatureFlagsAsync(name, label, syncToken, after, acceptDatetime, @select, matchConditions, tags, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); + } + + /// + /// [Protocol Method] Gets a single feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag. + /// The label of the key-value to retrieve. + /// Used to select what fields are present in the returned resource(s). + /// Used to guarantee real-time consistency between requests. + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response GetFeatureFlag(string name, string label, IEnumerable @select, string syncToken, string acceptDatetime, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.GetFeatureFlag"); + scope.Start(); + try + { + using HttpMessage message = CreateGetFeatureFlagRequest(name, label, @select, syncToken, acceptDatetime, matchConditions, tags, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Gets a single feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag. + /// The label of the key-value to retrieve. + /// Used to select what fields are present in the returned resource(s). + /// Used to guarantee real-time consistency between requests. + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task GetFeatureFlagAsync(string name, string label, IEnumerable @select, string syncToken, string acceptDatetime, MatchConditions matchConditions, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.GetFeatureFlag"); + scope.Start(); + try + { + using HttpMessage message = CreateGetFeatureFlagRequest(name, label, @select, syncToken, acceptDatetime, matchConditions, tags, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Gets a single feature flag. + /// The name of the feature flag. + /// The label of the key-value to retrieve. + /// Used to select what fields are present in the returned resource(s). + /// Used to guarantee real-time consistency between requests. + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Response GetFeatureFlag(string name, string label = default, IEnumerable @select = default, string syncToken = default, string acceptDatetime = default, MatchConditions matchConditions = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + Response result = GetFeatureFlag(name, label, @select, syncToken, acceptDatetime, matchConditions, tags, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + return Response.FromValue((FeatureFlag)result, result); + } + + /// Gets a single feature flag. + /// The name of the feature flag. + /// The label of the key-value to retrieve. + /// Used to select what fields are present in the returned resource(s). + /// Used to guarantee real-time consistency between requests. + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual async Task> GetFeatureFlagAsync(string name, string label = default, IEnumerable @select = default, string syncToken = default, string acceptDatetime = default, MatchConditions matchConditions = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + Response result = await GetFeatureFlagAsync(name, label, @select, syncToken, acceptDatetime, matchConditions, tags, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); + return Response.FromValue((FeatureFlag)result, result); + } + + /// + /// [Protocol Method] Requests the headers and status of the given resource. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag to retrieve. + /// The label of the feature flag to retrieve. + /// Used to guarantee real-time consistency between requests. + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// The content to send as the request conditions of the request. + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response CheckFeatureFlag(string name, string label, string syncToken, string acceptDatetime, MatchConditions matchConditions, IEnumerable @select, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.CheckFeatureFlag"); + scope.Start(); + try + { + using HttpMessage message = CreateCheckFeatureFlagRequest(name, label, syncToken, acceptDatetime, matchConditions, @select, tags, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Requests the headers and status of the given resource. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag to retrieve. + /// The label of the feature flag to retrieve. + /// Used to guarantee real-time consistency between requests. + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// The content to send as the request conditions of the request. + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task CheckFeatureFlagAsync(string name, string label, string syncToken, string acceptDatetime, MatchConditions matchConditions, IEnumerable @select, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.CheckFeatureFlag"); + scope.Start(); + try + { + using HttpMessage message = CreateCheckFeatureFlagRequest(name, label, syncToken, acceptDatetime, matchConditions, @select, tags, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Requests the headers and status of the given resource. + /// The name of the feature flag to retrieve. + /// The label of the feature flag to retrieve. + /// Used to guarantee real-time consistency between requests. + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// The content to send as the request conditions of the request. + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Response CheckFeatureFlag(string name, string label = default, string syncToken = default, string acceptDatetime = default, MatchConditions matchConditions = default, IEnumerable @select = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + return CheckFeatureFlag(name, label, syncToken, acceptDatetime, matchConditions, @select, tags, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + } + + /// Requests the headers and status of the given resource. + /// The name of the feature flag to retrieve. + /// The label of the feature flag to retrieve. + /// Used to guarantee real-time consistency between requests. + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// The content to send as the request conditions of the request. + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual async Task CheckFeatureFlagAsync(string name, string label = default, string syncToken = default, string acceptDatetime = default, MatchConditions matchConditions = default, IEnumerable @select = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + return await CheckFeatureFlagAsync(name, label, syncToken, acceptDatetime, matchConditions, @select, tags, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); + } + + /// + /// [Protocol Method] Creates a feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag to create. + /// The content to send as the body of the request. + /// The label of the feature flag to create. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response PutFeatureFlag(string name, RequestContent content, string label = default, string syncToken = default, MatchConditions matchConditions = default, RequestContext context = null) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.PutFeatureFlag"); + scope.Start(); + try + { + using HttpMessage message = CreatePutFeatureFlagRequest(name, content, label, syncToken, matchConditions, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Creates a feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag to create. + /// The content to send as the body of the request. + /// The label of the feature flag to create. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task PutFeatureFlagAsync(string name, RequestContent content, string label = default, string syncToken = default, MatchConditions matchConditions = default, RequestContext context = null) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.PutFeatureFlag"); + scope.Start(); + try + { + using HttpMessage message = CreatePutFeatureFlagRequest(name, content, label, syncToken, matchConditions, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Creates a feature flag. + /// The name of the feature flag to create. + /// The feature flag to create. + /// The label of the feature flag to create. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Response PutFeatureFlag(string name, FeatureFlag entity = default, string label = default, string syncToken = default, MatchConditions matchConditions = default, CancellationToken cancellationToken = default) + { + Response result = PutFeatureFlag(name, entity, label, syncToken, matchConditions, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + return Response.FromValue((FeatureFlag)result, result); + } + + /// Creates a feature flag. + /// The name of the feature flag to create. + /// The feature flag to create. + /// The label of the feature flag to create. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual async Task> PutFeatureFlagAsync(string name, FeatureFlag entity = default, string label = default, string syncToken = default, MatchConditions matchConditions = default, CancellationToken cancellationToken = default) + { + Response result = await PutFeatureFlagAsync(name, entity, label, syncToken, matchConditions, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); + return Response.FromValue((FeatureFlag)result, result); + } + + /// + /// [Protocol Method] Deletes a feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag to delete. + /// The label of the feature flag to delete. + /// Used to guarantee real-time consistency between requests. + /// + /// Used to perform an operation only if the targeted resource's etag matches the + /// value provided. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response DeleteFeatureFlag(string name, string label, string syncToken, ETag? ifMatch, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.DeleteFeatureFlag"); + scope.Start(); + try + { + using HttpMessage message = CreateDeleteFeatureFlagRequest(name, label, syncToken, ifMatch, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Deletes a feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag to delete. + /// The label of the feature flag to delete. + /// Used to guarantee real-time consistency between requests. + /// + /// Used to perform an operation only if the targeted resource's etag matches the + /// value provided. + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task DeleteFeatureFlagAsync(string name, string label, string syncToken, ETag? ifMatch, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.DeleteFeatureFlag"); + scope.Start(); + try + { + using HttpMessage message = CreateDeleteFeatureFlagRequest(name, label, syncToken, ifMatch, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Deletes a feature flag. + /// The name of the feature flag to delete. + /// The label of the feature flag to delete. + /// Used to guarantee real-time consistency between requests. + /// + /// Used to perform an operation only if the targeted resource's etag matches the + /// value provided. + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Response DeleteFeatureFlag(string name, string label = default, string syncToken = default, ETag? ifMatch = default, CancellationToken cancellationToken = default) + { + Response result = DeleteFeatureFlag(name, label, syncToken, ifMatch, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + return Response.FromValue((FeatureFlag)result, result); + } + + /// Deletes a feature flag. + /// The name of the feature flag to delete. + /// The label of the feature flag to delete. + /// Used to guarantee real-time consistency between requests. + /// + /// Used to perform an operation only if the targeted resource's etag matches the + /// value provided. + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual async Task> DeleteFeatureFlagAsync(string name, string label = default, string syncToken = default, ETag? ifMatch = default, CancellationToken cancellationToken = default) + { + Response result = await DeleteFeatureFlagAsync(name, label, syncToken, ifMatch, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); + return Response.FromValue((FeatureFlag)result, result); + } + + /// + /// [Protocol Method] Gets a list of feature flag revisions. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// A filter used to match names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response GetRevisions(string name, string label, string after, IEnumerable @select, IEnumerable tags, string syncToken, MatchConditions matchConditions, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.GetRevisions"); + scope.Start(); + try + { + using HttpMessage message = CreateGetRevisionsRequest(name, label, after, @select, tags, syncToken, matchConditions, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Gets a list of feature flag revisions. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// A filter used to match names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task GetRevisionsAsync(string name, string label, string after, IEnumerable @select, IEnumerable tags, string syncToken, MatchConditions matchConditions, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.GetRevisions"); + scope.Start(); + try + { + using HttpMessage message = CreateGetRevisionsRequest(name, label, after, @select, tags, syncToken, matchConditions, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Gets a list of feature flag revisions. + /// A filter used to match names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Response GetRevisions(string name = default, string label = default, string after = default, IEnumerable @select = default, IEnumerable tags = default, string syncToken = default, MatchConditions matchConditions = default, CancellationToken cancellationToken = default) + { + Response result = GetRevisions(name, label, after, @select, tags, syncToken, matchConditions, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + return Response.FromValue((FeatureFlagListResult)result, result); + } + + /// Gets a list of feature flag revisions. + /// A filter used to match names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual async Task> GetRevisionsAsync(string name = default, string label = default, string after = default, IEnumerable @select = default, IEnumerable tags = default, string syncToken = default, MatchConditions matchConditions = default, CancellationToken cancellationToken = default) + { + Response result = await GetRevisionsAsync(name, label, after, @select, tags, syncToken, matchConditions, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); + return Response.FromValue((FeatureFlagListResult)result, result); + } + + /// + /// [Protocol Method] Requests the headers and status of the given resource. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// A filter used to match names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response CheckRevisions(string name, string label, string after, IEnumerable @select, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.CheckRevisions"); + scope.Start(); + try + { + using HttpMessage message = CreateCheckRevisionsRequest(name, label, after, @select, tags, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Requests the headers and status of the given resource. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// A filter used to match names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task CheckRevisionsAsync(string name, string label, string after, IEnumerable @select, IEnumerable tags, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.CheckRevisions"); + scope.Start(); + try + { + using HttpMessage message = CreateCheckRevisionsRequest(name, label, after, @select, tags, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Requests the headers and status of the given resource. + /// A filter used to match names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Response CheckRevisions(string name = default, string label = default, string after = default, IEnumerable @select = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + return CheckRevisions(name, label, after, @select, tags, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + } + + /// Requests the headers and status of the given resource. + /// A filter used to match names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// Used to select what fields are present in the returned resource(s). + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/restapirevisions + /// + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual async Task CheckRevisionsAsync(string name = default, string label = default, string after = default, IEnumerable @select = default, IEnumerable tags = default, CancellationToken cancellationToken = default) + { + return await CheckRevisionsAsync(name, label, after, @select, tags, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); + } + + /// + /// [Protocol Method] Locks a feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag to lock. + /// The label, if any, of the feature flag to lock. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response CreateReadOnlyLock(string name, string label, string syncToken, MatchConditions matchConditions, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.CreateReadOnlyLock"); + scope.Start(); + try + { + using HttpMessage message = CreateCreateReadOnlyLockRequest(name, label, syncToken, matchConditions, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Locks a feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The name of the feature flag to lock. + /// The label, if any, of the feature flag to lock. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task CreateReadOnlyLockAsync(string name, string label, string syncToken, MatchConditions matchConditions, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.CreateReadOnlyLock"); + scope.Start(); + try + { + using HttpMessage message = CreateCreateReadOnlyLockRequest(name, label, syncToken, matchConditions, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Locks a feature flag. + /// The name of the feature flag to lock. + /// The label, if any, of the feature flag to lock. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Response CreateReadOnlyLock(string name, string label = default, string syncToken = default, MatchConditions matchConditions = default, CancellationToken cancellationToken = default) + { + Response result = CreateReadOnlyLock(name, label, syncToken, matchConditions, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + return Response.FromValue((FeatureFlag)result, result); + } + + /// Locks a feature flag. + /// The name of the feature flag to lock. + /// The label, if any, of the feature flag to lock. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual async Task> CreateReadOnlyLockAsync(string name, string label = default, string syncToken = default, MatchConditions matchConditions = default, CancellationToken cancellationToken = default) + { + Response result = await CreateReadOnlyLockAsync(name, label, syncToken, matchConditions, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); + return Response.FromValue((FeatureFlag)result, result); + } + + /// + /// [Protocol Method] Unlocks a feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The key of the feature flag to unlock. + /// The label, if any, of the feature flag to unlock. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response DeleteReadOnlyLock(string name, string label, string syncToken, MatchConditions matchConditions, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.DeleteReadOnlyLock"); + scope.Start(); + try + { + using HttpMessage message = CreateDeleteReadOnlyLockRequest(name, label, syncToken, matchConditions, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Unlocks a feature flag. + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// The key of the feature flag to unlock. + /// The label, if any, of the feature flag to unlock. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task DeleteReadOnlyLockAsync(string name, string label, string syncToken, MatchConditions matchConditions, RequestContext context) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("FeatureFlagClient.DeleteReadOnlyLock"); + scope.Start(); + try + { + using HttpMessage message = CreateDeleteReadOnlyLockRequest(name, label, syncToken, matchConditions, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Unlocks a feature flag. + /// The key of the feature flag to unlock. + /// The label, if any, of the feature flag to unlock. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual Response DeleteReadOnlyLock(string name, string label = default, string syncToken = default, MatchConditions matchConditions = default, CancellationToken cancellationToken = default) + { + Response result = DeleteReadOnlyLock(name, label, syncToken, matchConditions, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null); + return Response.FromValue((FeatureFlag)result, result); + } + + /// Unlocks a feature flag. + /// The key of the feature flag to unlock. + /// The label, if any, of the feature flag to unlock. + /// Used to guarantee real-time consistency between requests. + /// The content to send as the request conditions of the request. + /// The cancellation token that can be used to cancel the operation. + /// Service returned a non-success status code. + internal virtual async Task> DeleteReadOnlyLockAsync(string name, string label = default, string syncToken = default, MatchConditions matchConditions = default, CancellationToken cancellationToken = default) + { + Response result = await DeleteReadOnlyLockAsync(name, label, syncToken, matchConditions, cancellationToken.CanBeCanceled ? new RequestContext { CancellationToken = cancellationToken } : null).ConfigureAwait(false); + return Response.FromValue((FeatureFlag)result, result); + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsAsyncCollectionResult.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsAsyncCollectionResult.cs new file mode 100644 index 000000000000..1ea616fabc02 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsAsyncCollectionResult.cs @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; + +namespace Azure.Data.AppConfiguration +{ + internal partial class FeatureFlagClientGetFeatureFlagsAsyncCollectionResult : AsyncPageable + { + private readonly FeatureFlagClient _client; + private readonly string _name; + private readonly string _label; + private readonly string _syncToken; + private readonly string _after; + private readonly string _acceptDatetime; + private readonly IEnumerable _select; + private readonly MatchConditions _matchConditions; + private readonly IEnumerable _tags; + private readonly RequestContext _context; + + /// Initializes a new instance of FeatureFlagClientGetFeatureFlagsAsyncCollectionResult, which is used to iterate over the pages of a collection. + /// The FeatureFlagClient client used to send requests. + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + public FeatureFlagClientGetFeatureFlagsAsyncCollectionResult(FeatureFlagClient client, string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) : base(context?.CancellationToken ?? default) + { + _client = client; + _name = name; + _label = label; + _syncToken = syncToken; + _after = after; + _acceptDatetime = acceptDatetime; + _select = @select; + _matchConditions = matchConditions; + _tags = tags; + _context = context; + } + + /// Gets the pages of FeatureFlagClientGetFeatureFlagsAsyncCollectionResult as an enumerable collection. + /// A continuation token indicating where to resume paging. + /// The number of items per page. + /// The pages of FeatureFlagClientGetFeatureFlagsAsyncCollectionResult as an enumerable collection. + public override async IAsyncEnumerable> AsPages(string continuationToken, int? pageSizeHint) + { + Uri nextPage = continuationToken != null ? new Uri(continuationToken) : null; + while (true) + { + Response response = await GetNextResponseAsync(pageSizeHint, nextPage).ConfigureAwait(false); + if (response is null) + { + yield break; + } + FeatureFlagListResult result = (FeatureFlagListResult)response; + List items = new List(); + foreach (var item in result.Items) + { + items.Add(BinaryData.FromObjectAsJson(item)); + } + yield return Page.FromValues(items, nextPage?.AbsoluteUri, response); + string nextPageString = result.NextLink; + if (nextPageString == null) + { + yield break; + } + nextPage = new Uri(nextPageString); + } + } + + /// Get next page. + /// The number of items per page. + /// The next link to use for the next page of results. + private async ValueTask GetNextResponseAsync(int? pageSizeHint, Uri nextLink) + { + HttpMessage message = nextLink != null ? _client.CreateNextGetFeatureFlagsRequest(nextLink, _name, _label, _syncToken, _after, _acceptDatetime, _select, _matchConditions, _tags, _context) : _client.CreateGetFeatureFlagsRequest(_name, _label, _syncToken, _after, _acceptDatetime, _select, _matchConditions, _tags, _context); + using DiagnosticScope scope = _client.ClientDiagnostics.CreateScope("FeatureFlagClient.GetFeatureFlags"); + scope.Start(); + try + { + return await _client.Pipeline.ProcessMessageAsync(message, _context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsAsyncCollectionResultOfT.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsAsyncCollectionResultOfT.cs new file mode 100644 index 000000000000..76aa5601f0cd --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsAsyncCollectionResultOfT.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; + +namespace Azure.Data.AppConfiguration +{ + internal partial class FeatureFlagClientGetFeatureFlagsAsyncCollectionResultOfT : AsyncPageable + { + private readonly FeatureFlagClient _client; + private readonly string _name; + private readonly string _label; + private readonly string _syncToken; + private readonly string _after; + private readonly string _acceptDatetime; + private readonly IEnumerable _select; + private readonly MatchConditions _matchConditions; + private readonly IEnumerable _tags; + private readonly RequestContext _context; + + /// Initializes a new instance of FeatureFlagClientGetFeatureFlagsAsyncCollectionResultOfT, which is used to iterate over the pages of a collection. + /// The FeatureFlagClient client used to send requests. + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + public FeatureFlagClientGetFeatureFlagsAsyncCollectionResultOfT(FeatureFlagClient client, string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) : base(context?.CancellationToken ?? default) + { + _client = client; + _name = name; + _label = label; + _syncToken = syncToken; + _after = after; + _acceptDatetime = acceptDatetime; + _select = @select; + _matchConditions = matchConditions; + _tags = tags; + _context = context; + } + + /// Gets the pages of FeatureFlagClientGetFeatureFlagsAsyncCollectionResultOfT as an enumerable collection. + /// A continuation token indicating where to resume paging. + /// The number of items per page. + /// The pages of FeatureFlagClientGetFeatureFlagsAsyncCollectionResultOfT as an enumerable collection. + public override async IAsyncEnumerable> AsPages(string continuationToken, int? pageSizeHint) + { + Uri nextPage = continuationToken != null ? new Uri(continuationToken) : null; + while (true) + { + Response response = await GetNextResponseAsync(pageSizeHint, nextPage).ConfigureAwait(false); + if (response is null) + { + yield break; + } + FeatureFlagListResult result = (FeatureFlagListResult)response; + yield return Page.FromValues((IReadOnlyList)result.Items, nextPage?.AbsoluteUri, response); + string nextPageString = result.NextLink; + if (nextPageString == null) + { + yield break; + } + nextPage = new Uri(nextPageString); + } + } + + /// Get next page. + /// The number of items per page. + /// The next link to use for the next page of results. + private async ValueTask GetNextResponseAsync(int? pageSizeHint, Uri nextLink) + { + HttpMessage message = nextLink != null ? _client.CreateNextGetFeatureFlagsRequest(nextLink, _name, _label, _syncToken, _after, _acceptDatetime, _select, _matchConditions, _tags, _context) : _client.CreateGetFeatureFlagsRequest(_name, _label, _syncToken, _after, _acceptDatetime, _select, _matchConditions, _tags, _context); + using DiagnosticScope scope = _client.ClientDiagnostics.CreateScope("FeatureFlagClient.GetFeatureFlags"); + scope.Start(); + try + { + return await _client.Pipeline.ProcessMessageAsync(message, _context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsCollectionResult.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsCollectionResult.cs new file mode 100644 index 000000000000..26e50cba8d55 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsCollectionResult.cs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; + +namespace Azure.Data.AppConfiguration +{ + internal partial class FeatureFlagClientGetFeatureFlagsCollectionResult : Pageable + { + private readonly FeatureFlagClient _client; + private readonly string _name; + private readonly string _label; + private readonly string _syncToken; + private readonly string _after; + private readonly string _acceptDatetime; + private readonly IEnumerable _select; + private readonly MatchConditions _matchConditions; + private readonly IEnumerable _tags; + private readonly RequestContext _context; + + /// Initializes a new instance of FeatureFlagClientGetFeatureFlagsCollectionResult, which is used to iterate over the pages of a collection. + /// The FeatureFlagClient client used to send requests. + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + public FeatureFlagClientGetFeatureFlagsCollectionResult(FeatureFlagClient client, string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) : base(context?.CancellationToken ?? default) + { + _client = client; + _name = name; + _label = label; + _syncToken = syncToken; + _after = after; + _acceptDatetime = acceptDatetime; + _select = @select; + _matchConditions = matchConditions; + _tags = tags; + _context = context; + } + + /// Gets the pages of FeatureFlagClientGetFeatureFlagsCollectionResult as an enumerable collection. + /// A continuation token indicating where to resume paging. + /// The number of items per page. + /// The pages of FeatureFlagClientGetFeatureFlagsCollectionResult as an enumerable collection. + public override IEnumerable> AsPages(string continuationToken, int? pageSizeHint) + { + Uri nextPage = continuationToken != null ? new Uri(continuationToken) : null; + while (true) + { + Response response = GetNextResponse(pageSizeHint, nextPage); + if (response is null) + { + yield break; + } + FeatureFlagListResult result = (FeatureFlagListResult)response; + List items = new List(); + foreach (var item in result.Items) + { + items.Add(BinaryData.FromObjectAsJson(item)); + } + yield return Page.FromValues(items, nextPage?.AbsoluteUri, response); + string nextPageString = result.NextLink; + if (nextPageString == null) + { + yield break; + } + nextPage = new Uri(nextPageString); + } + } + + /// Get next page. + /// The number of items per page. + /// The next link to use for the next page of results. + private Response GetNextResponse(int? pageSizeHint, Uri nextLink) + { + HttpMessage message = nextLink != null ? _client.CreateNextGetFeatureFlagsRequest(nextLink, _name, _label, _syncToken, _after, _acceptDatetime, _select, _matchConditions, _tags, _context) : _client.CreateGetFeatureFlagsRequest(_name, _label, _syncToken, _after, _acceptDatetime, _select, _matchConditions, _tags, _context); + using DiagnosticScope scope = _client.ClientDiagnostics.CreateScope("FeatureFlagClient.GetFeatureFlags"); + scope.Start(); + try + { + return _client.Pipeline.ProcessMessage(message, _context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsCollectionResultOfT.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsCollectionResultOfT.cs new file mode 100644 index 000000000000..621a7244d69a --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientGetFeatureFlagsCollectionResultOfT.cs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; + +namespace Azure.Data.AppConfiguration +{ + internal partial class FeatureFlagClientGetFeatureFlagsCollectionResultOfT : Pageable + { + private readonly FeatureFlagClient _client; + private readonly string _name; + private readonly string _label; + private readonly string _syncToken; + private readonly string _after; + private readonly string _acceptDatetime; + private readonly IEnumerable _select; + private readonly MatchConditions _matchConditions; + private readonly IEnumerable _tags; + private readonly RequestContext _context; + + /// Initializes a new instance of FeatureFlagClientGetFeatureFlagsCollectionResultOfT, which is used to iterate over the pages of a collection. + /// The FeatureFlagClient client used to send requests. + /// A filter used to match feature flag names. + /// + /// A filter used to match labels. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// Used to guarantee real-time consistency between requests. + /// + /// Instructs the server to return elements that appear after the element referred + /// to by the specified token. + /// + /// + /// Requests the server to respond with the state of the resource at the specified + /// time. + /// + /// Used to select what fields are present in the returned resource(s). + /// The content to send as the request conditions of the request. + /// + /// A filter used to query by tags. Syntax reference: + /// https://aka.ms/azconfig/docs/keyvaluefiltering + /// + /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + public FeatureFlagClientGetFeatureFlagsCollectionResultOfT(FeatureFlagClient client, string name, string label, string syncToken, string after, string acceptDatetime, IEnumerable @select, MatchConditions matchConditions, IEnumerable tags, RequestContext context) : base(context?.CancellationToken ?? default) + { + _client = client; + _name = name; + _label = label; + _syncToken = syncToken; + _after = after; + _acceptDatetime = acceptDatetime; + _select = @select; + _matchConditions = matchConditions; + _tags = tags; + _context = context; + } + + /// Gets the pages of FeatureFlagClientGetFeatureFlagsCollectionResultOfT as an enumerable collection. + /// A continuation token indicating where to resume paging. + /// The number of items per page. + /// The pages of FeatureFlagClientGetFeatureFlagsCollectionResultOfT as an enumerable collection. + public override IEnumerable> AsPages(string continuationToken, int? pageSizeHint) + { + Uri nextPage = continuationToken != null ? new Uri(continuationToken) : null; + while (true) + { + Response response = GetNextResponse(pageSizeHint, nextPage); + if (response is null) + { + yield break; + } + FeatureFlagListResult result = (FeatureFlagListResult)response; + yield return Page.FromValues((IReadOnlyList)result.Items, nextPage?.AbsoluteUri, response); + string nextPageString = result.NextLink; + if (nextPageString == null) + { + yield break; + } + nextPage = new Uri(nextPageString); + } + } + + /// Get next page. + /// The number of items per page. + /// The next link to use for the next page of results. + private Response GetNextResponse(int? pageSizeHint, Uri nextLink) + { + HttpMessage message = nextLink != null ? _client.CreateNextGetFeatureFlagsRequest(nextLink, _name, _label, _syncToken, _after, _acceptDatetime, _select, _matchConditions, _tags, _context) : _client.CreateGetFeatureFlagsRequest(_name, _label, _syncToken, _after, _acceptDatetime, _select, _matchConditions, _tags, _context); + using DiagnosticScope scope = _client.ClientDiagnostics.CreateScope("FeatureFlagClient.GetFeatureFlags"); + scope.Start(); + try + { + return _client.Pipeline.ProcessMessage(message, _context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientOptions.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientOptions.cs new file mode 100644 index 000000000000..acdaac305a5e --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagClientOptions.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using Azure.Core; + +namespace Azure.Data.AppConfiguration +{ + /// Client options for . + public partial class FeatureFlagClientOptions : ClientOptions + { + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagConditions.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagConditions.Serialization.cs new file mode 100644 index 000000000000..c964bcc837a1 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagConditions.Serialization.cs @@ -0,0 +1,169 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace Azure.Data.AppConfiguration +{ + /// The conditions that must be met for the feature flag to be enabled. + public partial class FeatureFlagConditions : IJsonModel + { + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagConditions)} does not support writing '{format}' format."); + } + if (Optional.IsDefined(RequirementType)) + { + writer.WritePropertyName("requirement_type"u8); + writer.WriteStringValue(RequirementType.Value.ToString()); + } + if (Optional.IsCollectionDefined(Filters)) + { + writer.WritePropertyName("filters"u8); + writer.WriteStartArray(); + foreach (FeatureFlagFilter item in Filters) + { + writer.WriteObjectValue(item, options); + } + writer.WriteEndArray(); + } + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + FeatureFlagConditions IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual FeatureFlagConditions JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagConditions)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeFeatureFlagConditions(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static FeatureFlagConditions DeserializeFeatureFlagConditions(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + RequirementType? requirementType = default; + IList filters = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("requirement_type"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + requirementType = new RequirementType(prop.Value.GetString()); + continue; + } + if (prop.NameEquals("filters"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in prop.Value.EnumerateArray()) + { + array.Add(FeatureFlagFilter.DeserializeFeatureFlagFilter(item, options)); + } + filters = array; + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new FeatureFlagConditions(requirementType, filters ?? new ChangeTrackingList(), additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(FeatureFlagConditions)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + FeatureFlagConditions IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual FeatureFlagConditions PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializeFeatureFlagConditions(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(FeatureFlagConditions)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagConditions.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagConditions.cs new file mode 100644 index 000000000000..a672b046bb8a --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagConditions.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Azure.Data.AppConfiguration +{ + /// The conditions that must be met for the feature flag to be enabled. + public partial class FeatureFlagConditions + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + public FeatureFlagConditions() + { + Filters = new ChangeTrackingList(); + } + + /// Initializes a new instance of . + /// The requirement type for the conditions. + /// The filters that will conditionally enable or disable the flag. + /// Keeps track of any properties unknown to the library. + internal FeatureFlagConditions(RequirementType? requirementType, IList filters, IDictionary additionalBinaryDataProperties) + { + RequirementType = requirementType; + Filters = filters; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// The requirement type for the conditions. + public RequirementType? RequirementType { get; set; } + + /// The filters that will conditionally enable or disable the flag. + public IList Filters { get; } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagFields.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagFields.Serialization.cs new file mode 100644 index 000000000000..fe1351bb8ee1 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagFields.Serialization.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; + +namespace Azure.Data.AppConfiguration +{ + internal static partial class FeatureFlagFieldsExtensions + { + /// The value to serialize. + public static string ToSerialString(this FeatureFlagFields value) => value switch + { + FeatureFlagFields.Name => "name", + FeatureFlagFields.Enabled => "enabled", + FeatureFlagFields.Label => "label", + FeatureFlagFields.Description => "description", + FeatureFlagFields.Alias => "alias", + FeatureFlagFields.Conditions => "conditions", + FeatureFlagFields.Variants => "variants", + FeatureFlagFields.Allocation => "allocation", + FeatureFlagFields.Telemetry => "telemetry", + FeatureFlagFields.Tags => "tags", + FeatureFlagFields.LastModified => "last_modified", + FeatureFlagFields.ETag => "etag", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown FeatureFlagFields value.") + }; + + /// The value to deserialize. + public static FeatureFlagFields ToFeatureFlagFields(this string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "name")) + { + return FeatureFlagFields.Name; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "enabled")) + { + return FeatureFlagFields.Enabled; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "label")) + { + return FeatureFlagFields.Label; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "description")) + { + return FeatureFlagFields.Description; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "alias")) + { + return FeatureFlagFields.Alias; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "conditions")) + { + return FeatureFlagFields.Conditions; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "variants")) + { + return FeatureFlagFields.Variants; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "allocation")) + { + return FeatureFlagFields.Allocation; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "telemetry")) + { + return FeatureFlagFields.Telemetry; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "tags")) + { + return FeatureFlagFields.Tags; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "last_modified")) + { + return FeatureFlagFields.LastModified; + } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "etag")) + { + return FeatureFlagFields.ETag; + } + throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown FeatureFlagFields value."); + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagFilter.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagFilter.Serialization.cs new file mode 100644 index 000000000000..58d96de924f1 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagFilter.Serialization.cs @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag Filter object. + public partial class FeatureFlagFilter : IJsonModel + { + /// Initializes a new instance of for deserialization. + internal FeatureFlagFilter() + { + } + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagFilter)} does not support writing '{format}' format."); + } + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + if (Optional.IsCollectionDefined(Parameters)) + { + writer.WritePropertyName("parameters"u8); + writer.WriteStartObject(); + foreach (var item in Parameters) + { + writer.WritePropertyName(item.Key); + if (item.Value == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteObjectValue(item.Value, options); + } + writer.WriteEndObject(); + } + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + FeatureFlagFilter IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual FeatureFlagFilter JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagFilter)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeFeatureFlagFilter(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static FeatureFlagFilter DeserializeFeatureFlagFilter(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string name = default; + IDictionary parameters = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("name"u8)) + { + name = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("parameters"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + Dictionary dictionary = new Dictionary(); + foreach (var prop0 in prop.Value.EnumerateObject()) + { + if (prop0.Value.ValueKind == JsonValueKind.Null) + { + dictionary.Add(prop0.Name, null); + } + else + { + dictionary.Add(prop0.Name, prop0.Value.GetObject()); + } + } + parameters = dictionary; + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new FeatureFlagFilter(name, parameters ?? new ChangeTrackingDictionary(), additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(FeatureFlagFilter)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + FeatureFlagFilter IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual FeatureFlagFilter PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializeFeatureFlagFilter(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(FeatureFlagFilter)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagFilter.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagFilter.cs new file mode 100644 index 000000000000..8cc954c291c5 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagFilter.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag Filter object. + public partial class FeatureFlagFilter + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + /// Gets the name of the feature filter. + /// Gets the parameters of the feature filter. + /// Keeps track of any properties unknown to the library. + internal FeatureFlagFilter(string name, IDictionary parameters, IDictionary additionalBinaryDataProperties) + { + Name = name; + Parameters = parameters; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagListResult.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagListResult.Serialization.cs new file mode 100644 index 000000000000..f3a119e200fe --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagListResult.Serialization.cs @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; +using Azure; + +namespace Azure.Data.AppConfiguration +{ + /// The result of a Feature Flag list request. + internal partial class FeatureFlagListResult : IJsonModel + { + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagListResult)} does not support writing '{format}' format."); + } + if (Optional.IsCollectionDefined(Items)) + { + writer.WritePropertyName("items"u8); + writer.WriteStartArray(); + foreach (FeatureFlag item in Items) + { + writer.WriteObjectValue(item, options); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(Etag)) + { + writer.WritePropertyName("etag"u8); + writer.WriteStringValue(Etag); + } + if (Optional.IsDefined(NextLink)) + { + writer.WritePropertyName("@nextLink"u8); + writer.WriteStringValue(NextLink); + } + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + FeatureFlagListResult IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual FeatureFlagListResult JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagListResult)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeFeatureFlagListResult(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static FeatureFlagListResult DeserializeFeatureFlagListResult(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + IList items = default; + string etag = default; + string nextLink = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("items"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in prop.Value.EnumerateArray()) + { + array.Add(FeatureFlag.DeserializeFeatureFlag(item, options)); + } + items = array; + continue; + } + if (prop.NameEquals("etag"u8)) + { + etag = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("@nextLink"u8)) + { + nextLink = prop.Value.GetString(); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new FeatureFlagListResult(items ?? new ChangeTrackingList(), etag, nextLink, additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(FeatureFlagListResult)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + FeatureFlagListResult IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual FeatureFlagListResult PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializeFeatureFlagListResult(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(FeatureFlagListResult)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// The to deserialize the from. + public static explicit operator FeatureFlagListResult(Response result) + { + using Response response = result; + using JsonDocument document = JsonDocument.Parse(response.Content); + return DeserializeFeatureFlagListResult(document.RootElement, ModelSerializationExtensions.WireOptions); + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagListResult.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagListResult.cs new file mode 100644 index 000000000000..f34548e449e4 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagListResult.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Azure.Data.AppConfiguration +{ + /// The result of a Feature Flag list request. + internal partial class FeatureFlagListResult + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + internal FeatureFlagListResult() + { + Items = new ChangeTrackingList(); + } + + /// Initializes a new instance of . + /// The collection value. + /// An identifier representing the returned state of the resource. + /// The URI that can be used to request the next set of paged results. + /// Keeps track of any properties unknown to the library. + internal FeatureFlagListResult(IList items, string etag, string nextLink, IDictionary additionalBinaryDataProperties) + { + Items = items; + Etag = etag; + NextLink = nextLink; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// The collection value. + public IList Items { get; } + + /// An identifier representing the returned state of the resource. + public string Etag { get; } + + /// The URI that can be used to request the next set of paged results. + public string NextLink { get; } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagTelemetryConfiguration.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagTelemetryConfiguration.Serialization.cs new file mode 100644 index 000000000000..6000ad5e4aba --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagTelemetryConfiguration.Serialization.cs @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag Telemetry object. + public partial class FeatureFlagTelemetryConfiguration : IJsonModel + { + /// Initializes a new instance of for deserialization. + internal FeatureFlagTelemetryConfiguration() + { + } + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagTelemetryConfiguration)} does not support writing '{format}' format."); + } + writer.WritePropertyName("enabled"u8); + writer.WriteBooleanValue(Enabled); + if (Optional.IsCollectionDefined(Metadata)) + { + writer.WritePropertyName("metadata"u8); + writer.WriteStartObject(); + foreach (var item in Metadata) + { + writer.WritePropertyName(item.Key); + if (item.Value == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item.Value); + } + writer.WriteEndObject(); + } + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + FeatureFlagTelemetryConfiguration IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual FeatureFlagTelemetryConfiguration JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagTelemetryConfiguration)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeFeatureFlagTelemetryConfiguration(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static FeatureFlagTelemetryConfiguration DeserializeFeatureFlagTelemetryConfiguration(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + bool enabled = default; + IDictionary metadata = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("enabled"u8)) + { + enabled = prop.Value.GetBoolean(); + continue; + } + if (prop.NameEquals("metadata"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + Dictionary dictionary = new Dictionary(); + foreach (var prop0 in prop.Value.EnumerateObject()) + { + if (prop0.Value.ValueKind == JsonValueKind.Null) + { + dictionary.Add(prop0.Name, null); + } + else + { + dictionary.Add(prop0.Name, prop0.Value.GetString()); + } + } + metadata = dictionary; + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new FeatureFlagTelemetryConfiguration(enabled, metadata ?? new ChangeTrackingDictionary(), additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(FeatureFlagTelemetryConfiguration)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + FeatureFlagTelemetryConfiguration IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual FeatureFlagTelemetryConfiguration PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializeFeatureFlagTelemetryConfiguration(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(FeatureFlagTelemetryConfiguration)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagTelemetryConfiguration.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagTelemetryConfiguration.cs new file mode 100644 index 000000000000..b42ebf469da7 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagTelemetryConfiguration.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag Telemetry object. + public partial class FeatureFlagTelemetryConfiguration + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + /// The enabled state of the telemetry. + public FeatureFlagTelemetryConfiguration(bool enabled) + { + Enabled = enabled; + Metadata = new ChangeTrackingDictionary(); + } + + /// Initializes a new instance of . + /// The enabled state of the telemetry. + /// The metadata to include on outbound telemetry. + /// Keeps track of any properties unknown to the library. + internal FeatureFlagTelemetryConfiguration(bool enabled, IDictionary metadata, IDictionary additionalBinaryDataProperties) + { + Enabled = enabled; + Metadata = metadata; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// The enabled state of the telemetry. + public bool Enabled { get; set; } + + /// The metadata to include on outbound telemetry. + public IDictionary Metadata { get; } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagVariantDefinition.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagVariantDefinition.Serialization.cs new file mode 100644 index 000000000000..820ae4c1c820 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagVariantDefinition.Serialization.cs @@ -0,0 +1,179 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag Variants object. + public partial class FeatureFlagVariantDefinition : IJsonModel + { + /// Initializes a new instance of for deserialization. + internal FeatureFlagVariantDefinition() + { + } + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagVariantDefinition)} does not support writing '{format}' format."); + } + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + if (Optional.IsDefined(Value)) + { + writer.WritePropertyName("value"u8); + writer.WriteStringValue(Value); + } + if (Optional.IsDefined(ContentType)) + { + writer.WritePropertyName("content_type"u8); + writer.WriteStringValue(ContentType); + } + if (Optional.IsDefined(StatusOverride)) + { + writer.WritePropertyName("status_override"u8); + writer.WriteStringValue(StatusOverride.Value.ToString()); + } + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + FeatureFlagVariantDefinition IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual FeatureFlagVariantDefinition JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(FeatureFlagVariantDefinition)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeFeatureFlagVariantDefinition(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static FeatureFlagVariantDefinition DeserializeFeatureFlagVariantDefinition(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string name = default; + string value = default; + string contentType = default; + StatusOverride? statusOverride = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("name"u8)) + { + name = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("value"u8)) + { + value = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("content_type"u8)) + { + contentType = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("status_override"u8)) + { + if (prop.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + statusOverride = new StatusOverride(prop.Value.GetString()); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new FeatureFlagVariantDefinition(name, value, contentType, statusOverride, additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(FeatureFlagVariantDefinition)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + FeatureFlagVariantDefinition IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual FeatureFlagVariantDefinition PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializeFeatureFlagVariantDefinition(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(FeatureFlagVariantDefinition)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagVariantDefinition.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagVariantDefinition.cs new file mode 100644 index 000000000000..436c03513460 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/FeatureFlagVariantDefinition.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag Variants object. + public partial class FeatureFlagVariantDefinition + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + /// The name of the variant. + /// is null. + public FeatureFlagVariantDefinition(string name) + { + Argument.AssertNotNull(name, nameof(name)); + + Name = name; + } + + /// Initializes a new instance of . + /// The name of the variant. + /// The value of the variant. + /// The content type of the value stored within the key-value. + /// Determines if the variant should override the status of the flag. + /// Keeps track of any properties unknown to the library. + internal FeatureFlagVariantDefinition(string name, string value, string contentType, StatusOverride? statusOverride, IDictionary additionalBinaryDataProperties) + { + Name = name; + Value = value; + ContentType = contentType; + StatusOverride = statusOverride; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// The name of the variant. + public string Name { get; set; } + + /// The value of the variant. + public string Value { get; set; } + + /// The content type of the value stored within the key-value. + public string ContentType { get; set; } + + /// Determines if the variant should override the status of the flag. + public StatusOverride? StatusOverride { get; set; } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/GroupAllocation.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/GroupAllocation.Serialization.cs new file mode 100644 index 000000000000..0c485168f929 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/GroupAllocation.Serialization.cs @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag GroupAllocation object. + public partial class GroupAllocation : IJsonModel + { + /// Initializes a new instance of for deserialization. + internal GroupAllocation() + { + } + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(GroupAllocation)} does not support writing '{format}' format."); + } + writer.WritePropertyName("variant"u8); + writer.WriteStringValue(Variant); + writer.WritePropertyName("groups"u8); + writer.WriteStartArray(); + foreach (string item in Groups) + { + if (item == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item); + } + writer.WriteEndArray(); + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + GroupAllocation IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual GroupAllocation JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(GroupAllocation)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeGroupAllocation(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static GroupAllocation DeserializeGroupAllocation(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string variant = default; + IList groups = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("variant"u8)) + { + variant = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("groups"u8)) + { + List array = new List(); + foreach (var item in prop.Value.EnumerateArray()) + { + if (item.ValueKind == JsonValueKind.Null) + { + array.Add(null); + } + else + { + array.Add(item.GetString()); + } + } + groups = array; + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new GroupAllocation(variant, groups, additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(GroupAllocation)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + GroupAllocation IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual GroupAllocation PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializeGroupAllocation(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(GroupAllocation)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/GroupAllocation.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/GroupAllocation.cs new file mode 100644 index 000000000000..ff7a614c3661 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/GroupAllocation.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag GroupAllocation object. + public partial class GroupAllocation + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + /// The variant to allocate these percentiles to. + /// The groups to get this variant. + /// or is null. + public GroupAllocation(string variant, IEnumerable groups) + { + Argument.AssertNotNull(variant, nameof(variant)); + Argument.AssertNotNull(groups, nameof(groups)); + + Variant = variant; + Groups = groups.ToList(); + } + + /// Initializes a new instance of . + /// The variant to allocate these percentiles to. + /// The groups to get this variant. + /// Keeps track of any properties unknown to the library. + internal GroupAllocation(string variant, IList groups, IDictionary additionalBinaryDataProperties) + { + Variant = variant; + Groups = groups; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// The variant to allocate these percentiles to. + public string Variant { get; set; } + + /// The groups to get this variant. + public IList Groups { get; } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/Models/AzureDataAppConfigurationContext.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/Models/AzureDataAppConfigurationContext.cs index 407a57ddb599..f3f7a7515146 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/Models/AzureDataAppConfigurationContext.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/Models/AzureDataAppConfigurationContext.cs @@ -17,9 +17,19 @@ namespace Azure.Data.AppConfiguration [ModelReaderWriterBuildable(typeof(ConfigurationSetting))] [ModelReaderWriterBuildable(typeof(ConfigurationSettingsFilter))] [ModelReaderWriterBuildable(typeof(ConfigurationSnapshot))] + [ModelReaderWriterBuildable(typeof(FeatureFlag))] + [ModelReaderWriterBuildable(typeof(FeatureFlagAllocation))] + [ModelReaderWriterBuildable(typeof(FeatureFlagConditions))] + [ModelReaderWriterBuildable(typeof(FeatureFlagFilter))] + [ModelReaderWriterBuildable(typeof(FeatureFlagListResult))] + [ModelReaderWriterBuildable(typeof(FeatureFlagTelemetryConfiguration))] + [ModelReaderWriterBuildable(typeof(FeatureFlagVariantDefinition))] + [ModelReaderWriterBuildable(typeof(GroupAllocation))] + [ModelReaderWriterBuildable(typeof(PercentileAllocation))] [ModelReaderWriterBuildable(typeof(ResponseError))] [ModelReaderWriterBuildable(typeof(SettingLabel))] [ModelReaderWriterBuildable(typeof(SnapshotUpdateParameters))] + [ModelReaderWriterBuildable(typeof(UserAllocation))] public partial class AzureDataAppConfigurationContext : ModelReaderWriterContext { } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/PercentileAllocation.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/PercentileAllocation.Serialization.cs new file mode 100644 index 000000000000..f2a24a81d1f9 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/PercentileAllocation.Serialization.cs @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag PercentileAllocation object. + public partial class PercentileAllocation : IJsonModel + { + /// Initializes a new instance of for deserialization. + internal PercentileAllocation() + { + } + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(PercentileAllocation)} does not support writing '{format}' format."); + } + writer.WritePropertyName("variant"u8); + writer.WriteStringValue(Variant); + writer.WritePropertyName("from"u8); + writer.WriteNumberValue(From); + writer.WritePropertyName("to"u8); + writer.WriteNumberValue(To); + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + PercentileAllocation IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual PercentileAllocation JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(PercentileAllocation)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializePercentileAllocation(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static PercentileAllocation DeserializePercentileAllocation(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string variant = default; + int @from = default; + int to = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("variant"u8)) + { + variant = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("from"u8)) + { + @from = prop.Value.GetInt32(); + continue; + } + if (prop.NameEquals("to"u8)) + { + to = prop.Value.GetInt32(); + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new PercentileAllocation(variant, @from, to, additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(PercentileAllocation)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + PercentileAllocation IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual PercentileAllocation PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializePercentileAllocation(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(PercentileAllocation)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/PercentileAllocation.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/PercentileAllocation.cs new file mode 100644 index 000000000000..176df5f10e75 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/PercentileAllocation.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag PercentileAllocation object. + public partial class PercentileAllocation + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + /// The variant to allocate these percentiles to. + /// The lower bounds for this percentile allocation. + /// The upper bounds for this percentile allocation. + /// is null. + public PercentileAllocation(string variant, int @from, int to) + { + Argument.AssertNotNull(variant, nameof(variant)); + + Variant = variant; + From = @from; + To = to; + } + + /// Initializes a new instance of . + /// The variant to allocate these percentiles to. + /// The lower bounds for this percentile allocation. + /// The upper bounds for this percentile allocation. + /// Keeps track of any properties unknown to the library. + internal PercentileAllocation(string variant, int @from, int to, IDictionary additionalBinaryDataProperties) + { + Variant = variant; + From = @from; + To = to; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// The variant to allocate these percentiles to. + public string Variant { get; set; } + + /// The lower bounds for this percentile allocation. + public int From { get; set; } + + /// The upper bounds for this percentile allocation. + public int To { get; set; } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/RequirementType.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/RequirementType.cs new file mode 100644 index 000000000000..83e7aed984ee --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/RequirementType.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Data.AppConfiguration +{ + /// Requirement Type. + public readonly partial struct RequirementType : IEquatable + { + private readonly string _value; + /// Any. + private const string AnyValue = "Any"; + /// All. + private const string AllValue = "All"; + + /// Initializes a new instance of . + /// The value. + /// is null. + public RequirementType(string value) + { + Argument.AssertNotNull(value, nameof(value)); + + _value = value; + } + + /// Any. + public static RequirementType Any { get; } = new RequirementType(AnyValue); + + /// All. + public static RequirementType All { get; } = new RequirementType(AllValue); + + /// Determines if two values are the same. + /// The left value to compare. + /// The right value to compare. + public static bool operator ==(RequirementType left, RequirementType right) => left.Equals(right); + + /// Determines if two values are not the same. + /// The left value to compare. + /// The right value to compare. + public static bool operator !=(RequirementType left, RequirementType right) => !left.Equals(right); + + /// Converts a string to a . + /// The value. + public static implicit operator RequirementType(string value) => new RequirementType(value); + + /// Converts a string to a . + /// The value. + public static implicit operator RequirementType?(string value) => value == null ? null : new RequirementType(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is RequirementType other && Equals(other); + + /// + public bool Equals(RequirementType other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; + + /// + public override string ToString() => _value; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingFields.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingFields.Serialization.cs index d40f48899f40..6d4d5dc3d84c 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingFields.Serialization.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingFields.Serialization.cs @@ -20,6 +20,7 @@ internal static partial class SettingFieldsExtensions SettingFields.Value => "value", SettingFields.LastModified => "last_modified", SettingFields.Tags => "tags", + SettingFields.Description => "description", SettingFields.IsReadOnly => "locked", SettingFields.ETag => "etag", _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown SettingFields value.") @@ -52,6 +53,10 @@ public static SettingFields ToSettingFields(this string value) { return SettingFields.Tags; } + if (StringComparer.OrdinalIgnoreCase.Equals(value, "description")) + { + return SettingFields.Description; + } if (StringComparer.OrdinalIgnoreCase.Equals(value, "locked")) { return SettingFields.IsReadOnly; diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingLabel.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingLabel.Serialization.cs index a2e18a6f24d0..f60e6d5270a5 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingLabel.Serialization.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingLabel.Serialization.cs @@ -12,7 +12,7 @@ namespace Azure.Data.AppConfiguration { - /// Labels are used to group key-values. + /// Labels are used to group key values or feature flags. public partial class SettingLabel : IJsonModel { /// The JSON writer. diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingLabel.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingLabel.cs index 5d9a6b8b28cb..d00d3dde3c8b 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingLabel.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SettingLabel.cs @@ -10,7 +10,7 @@ namespace Azure.Data.AppConfiguration { - /// Labels are used to group key-values. + /// Labels are used to group key values or feature flags. public partial class SettingLabel { /// Keeps track of any properties unknown to the library. diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SnapshotFields.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SnapshotFields.cs index 5f69a2b3ee19..8aed97ec9602 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SnapshotFields.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/SnapshotFields.cs @@ -34,6 +34,8 @@ namespace Azure.Data.AppConfiguration private const string ItemsCountValue = "items_count"; /// Tags field. private const string TagsValue = "tags"; + /// Description field. + private const string DescriptionValue = "description"; /// Etag field. private const string EtagValue = "etag"; @@ -59,6 +61,9 @@ public SnapshotFields(string value) /// Tags field. public static SnapshotFields Tags { get; } = new SnapshotFields(TagsValue); + /// Description field. + public static SnapshotFields Description { get; } = new SnapshotFields(DescriptionValue); + /// Determines if two values are the same. /// The left value to compare. /// The right value to compare. diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/StatusOverride.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/StatusOverride.cs new file mode 100644 index 000000000000..61519182824e --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/StatusOverride.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Data.AppConfiguration +{ + /// Status Override. + public readonly partial struct StatusOverride : IEquatable + { + private readonly string _value; + /// None. + private const string NoneValue = "None"; + /// Enabled. + private const string EnabledValue = "Enabled"; + /// Disabled. + private const string DisabledValue = "Disabled"; + + /// Initializes a new instance of . + /// The value. + /// is null. + public StatusOverride(string value) + { + Argument.AssertNotNull(value, nameof(value)); + + _value = value; + } + + /// None. + public static StatusOverride None { get; } = new StatusOverride(NoneValue); + + /// Enabled. + public static StatusOverride Enabled { get; } = new StatusOverride(EnabledValue); + + /// Disabled. + public static StatusOverride Disabled { get; } = new StatusOverride(DisabledValue); + + /// Determines if two values are the same. + /// The left value to compare. + /// The right value to compare. + public static bool operator ==(StatusOverride left, StatusOverride right) => left.Equals(right); + + /// Determines if two values are not the same. + /// The left value to compare. + /// The right value to compare. + public static bool operator !=(StatusOverride left, StatusOverride right) => !left.Equals(right); + + /// Converts a string to a . + /// The value. + public static implicit operator StatusOverride(string value) => new StatusOverride(value); + + /// Converts a string to a . + /// The value. + public static implicit operator StatusOverride?(string value) => value == null ? null : new StatusOverride(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is StatusOverride other && Equals(other); + + /// + public bool Equals(StatusOverride other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; + + /// + public override string ToString() => _value; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/UserAllocation.Serialization.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/UserAllocation.Serialization.cs new file mode 100644 index 000000000000..8c8f67583161 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/UserAllocation.Serialization.cs @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag UserAllocation object. + public partial class UserAllocation : IJsonModel + { + /// Initializes a new instance of for deserialization. + internal UserAllocation() + { + } + + /// The JSON writer. + /// The client options for reading and writing models. + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(UserAllocation)} does not support writing '{format}' format."); + } + writer.WritePropertyName("variant"u8); + writer.WriteStringValue(Variant); + writer.WritePropertyName("users"u8); + writer.WriteStartArray(); + foreach (string item in Users) + { + if (item == null) + { + writer.WriteNullValue(); + continue; + } + writer.WriteStringValue(item); + } + writer.WriteEndArray(); + if (options.Format != "W" && _additionalBinaryDataProperties != null) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + /// The JSON reader. + /// The client options for reading and writing models. + UserAllocation IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) => JsonModelCreateCore(ref reader, options); + + /// The JSON reader. + /// The client options for reading and writing models. + protected virtual UserAllocation JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(UserAllocation)} does not support reading '{format}' format."); + } + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeUserAllocation(document.RootElement, options); + } + + /// The JSON element to deserialize. + /// The client options for reading and writing models. + internal static UserAllocation DeserializeUserAllocation(JsonElement element, ModelReaderWriterOptions options) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string variant = default; + IList users = default; + IDictionary additionalBinaryDataProperties = new ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("variant"u8)) + { + variant = prop.Value.GetString(); + continue; + } + if (prop.NameEquals("users"u8)) + { + List array = new List(); + foreach (var item in prop.Value.EnumerateArray()) + { + if (item.ValueKind == JsonValueKind.Null) + { + array.Add(null); + } + else + { + array.Add(item.GetString()); + } + } + users = array; + continue; + } + if (options.Format != "W") + { + additionalBinaryDataProperties.Add(prop.Name, BinaryData.FromString(prop.Value.GetRawText())); + } + } + return new UserAllocation(variant, users, additionalBinaryDataProperties); + } + + /// The client options for reading and writing models. + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) => PersistableModelWriteCore(options); + + /// The client options for reading and writing models. + protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureDataAppConfigurationContext.Default); + default: + throw new FormatException($"The model {nameof(UserAllocation)} does not support writing '{options.Format}' format."); + } + } + + /// The data to parse. + /// The client options for reading and writing models. + UserAllocation IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) => PersistableModelCreateCore(data, options); + + /// The data to parse. + /// The client options for reading and writing models. + protected virtual UserAllocation PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options) + { + string format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + switch (format) + { + case "J": + using (JsonDocument document = JsonDocument.Parse(data)) + { + return DeserializeUserAllocation(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(UserAllocation)} does not support reading '{options.Format}' format."); + } + } + + /// The client options for reading and writing models. + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/UserAllocation.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/UserAllocation.cs new file mode 100644 index 000000000000..ef05d986a2d6 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Generated/UserAllocation.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Azure.Data.AppConfiguration +{ + /// Feature Flag UserAllocation object. + public partial class UserAllocation + { + /// Keeps track of any properties unknown to the library. + private protected readonly IDictionary _additionalBinaryDataProperties; + + /// Initializes a new instance of . + /// The variant to allocate these percentiles to. + /// The users to get this variant. + /// or is null. + public UserAllocation(string variant, IEnumerable users) + { + Argument.AssertNotNull(variant, nameof(variant)); + Argument.AssertNotNull(users, nameof(users)); + + Variant = variant; + Users = users.ToList(); + } + + /// Initializes a new instance of . + /// The variant to allocate these percentiles to. + /// The users to get this variant. + /// Keeps track of any properties unknown to the library. + internal UserAllocation(string variant, IList users, IDictionary additionalBinaryDataProperties) + { + Variant = variant; + Users = users; + _additionalBinaryDataProperties = additionalBinaryDataProperties; + } + + /// The variant to allocate these percentiles to. + public string Variant { get; set; } + + /// The users to get this variant. + public IList Users { get; } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/ConfigurationModelFactory.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/ConfigurationModelFactory.cs index 36511878615a..0b21799cdb15 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/ConfigurationModelFactory.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/ConfigurationModelFactory.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Azure.Data.AppConfiguration { @@ -93,5 +94,45 @@ public static SecretReferenceConfigurationSetting SecretReferenceConfigurationSe IsReadOnly = isReadOnly }; } + + /// A feature flag. + /// The name of the feature flag. + /// The alias of the feature flag. + /// The label the feature flag belongs to. + /// The description of the feature flag. + /// The enabled state of the feature flag. + /// The conditions of the feature flag. + /// The variants of the feature flag. + /// The allocation of the feature flag. + /// The telemetry settings of the feature flag. + /// + /// A dictionary of tags used to assign additional properties to a feature flag. + /// These can be used to indicate how a feature flag may be applied. + /// + /// + /// A value indicating whether the feature flag is read only. + /// A read only feature flag may not be modified until it is made writable. + /// + /// The last time a modifying operation was performed on the given feature flag. + /// An ETag indicating the state of a feature flag within a configuration store. + /// A new instance for mocking. + public static FeatureFlag FeatureFlag(string name = default, bool? enabled = default, string label = default, string description = default, string @alias = default, FeatureFlagConditions conditions = default, IEnumerable variants = default, FeatureFlagAllocation allocation = default, FeatureFlagTelemetryConfiguration telemetry = default, IDictionary tags = default, bool? isReadOnly = default, DateTimeOffset? lastModified = default, ETag eTag = default) + { + return new FeatureFlag( + name, + enabled, + label, + description, + @alias, + conditions, + variants != null ? variants.ToList() : null, + allocation, + telemetry, + tags, + isReadOnly, + lastModified, + eTag, + additionalBinaryDataProperties: null); + } } } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/ConfigurationSetting.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/ConfigurationSetting.cs index 506e853205e9..ffaf6c332a2a 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/ConfigurationSetting.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/ConfigurationSetting.cs @@ -126,6 +126,9 @@ public IDictionary Tags internal set => _tags = value; } + /// The description of the key-value. + public string Description { get; set; } + /// /// Check if two ConfigurationSetting instances are equal. /// diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlag.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlag.cs new file mode 100644 index 000000000000..cdb9a618f331 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlag.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text.Json; +using Azure.Core; + +namespace Azure.Data.AppConfiguration +{ + /// + /// A Feature Flag, defined by a unique combination of a name and label. + /// + [CodeGenSerialization(nameof(ETag), SerializationValueHook = nameof(SerializationEtag), DeserializationValueHook = nameof(DeserializeEtag))] + public partial class FeatureFlag + { + private IList _variants; + private IDictionary _tags; + + /// Initializes a new instance of . + public FeatureFlag() + { + } + + /// + /// Creates a feature flag and sets it's enabled status and label. + /// + /// The primary identifier of the feature flag. + /// The enabled status of the flag. + /// A label used to group this feature flag with others. + /// The ETag value for the feature flag. + public FeatureFlag(string name, bool? enabled = default, string label = null, ETag etag = default) : this(name, enabled, label, null, null, null, null, null, null, null, false, null, etag, null) + { + } + + internal static RequestContent ToRequestContent(FeatureFlag featureFlag) + { + var content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteObjectValue(featureFlag); + return content; + } + + /// + /// An ETag indicating the state of a feature flag within a configuration store. + /// + [CodeGenMember("Etag")] + public ETag ETag { get; internal set; } + + /// + /// The last time a modifying operation was performed on the given feature flag. + /// + public DateTimeOffset? LastModified { get; internal set; } + + /// + /// A value indicating whether the feature flag is read only. + /// A read only feature flag may not be modified until it is made writable. + /// + [CodeGenMember("Locked")] + public bool? IsReadOnly { get; internal set; } + + /// + /// A list of variant definitions for the feature flag. + /// + public IList Variants + { + get => _variants ?? (_variants = new ChangeTrackingList()); + internal set => _variants = value; + } + + /// + /// A dictionary of tags used to assign additional properties to a feature flag. + /// These can be used to indicate how a feature flag may be applied. + /// + public IDictionary Tags + { + get => _tags ?? (_tags = new Dictionary()); + internal set => _tags = value; + } + + /// + /// Check if two FeatureFlag instances are equal. + /// + /// The instance to compare to. + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => base.Equals(obj); + + /// + /// Get a hash code for the FeatureFlag. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => base.GetHashCode(); + + private void SerializationEtag(Utf8JsonWriter writer, ModelReaderWriterOptions options) + => writer.WriteStringValue(ETag.ToString()); + + private static void DeserializeEtag(JsonProperty property, ref ETag val) + => val = new ETag(property.Value.GetString()); + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagConfigurationSetting.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagConfigurationSetting.cs index 2ff30ce27e55..2147ab59f371 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagConfigurationSetting.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagConfigurationSetting.cs @@ -116,23 +116,6 @@ public string FeatureId } } - /// - /// Gets or sets a description of the feature. - /// - public string Description - { - get - { - CheckValid(); - return _description; - } - set - { - CheckValid(); - _description = value; - } - } - /// /// Gets or sets a display name for the feature to use for display rather than the ID. /// diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFields.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFields.cs new file mode 100644 index 000000000000..7b0f07088d3b --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFields.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.Data.AppConfiguration +{ + // CUSTOM: + // - Rename and convert to an enum. + // - Renamed enum members. + /// + /// Fields to retrieve from a feature flag configuration setting. + /// + [Flags] + [CodeGenType("FeatureFlagFields")] + public enum FeatureFlagFields : uint + { + /// + /// No fields specified. + /// + None = 0, + + /// + /// The primary identifier of the feature flag. + /// + Name = 0x001, + + /// + /// A label used to group feature flag settings. + /// + Label = 0x002, + + /// + /// The last time a modifying operation was performed on the given feature flag. + /// + LastModified = 0x004, + + /// + /// A dictionary of tags that can help identify what a feature flag may be applicable for. + /// + Tags = 0x008, + + /// + /// An ETag indicating the version of a feature flag within a configuration store. + /// + [CodeGenMember("Etag")] + ETag = 0x010, + + /// + /// A value indicating whether the feature flag is read-only. + /// + [CodeGenMember("Locked")] + IsReadOnly = 0x020, + + /// + /// A value indicating whether the feature flag is enabled or disabled. + /// + Enabled = 0x040, + + /// + /// A description of the feature flag and its purpose. + /// + Description = 0x080, + + /// + /// The variants configuration for the feature flag, defining different feature variations. + /// + Variants = 0x100, + + /// + /// The allocation configuration for the feature flag, controlling how traffic is distributed among variants. + /// + Allocation = 0x200, + + /// + /// The conditions configuration for the feature flag, defining when the feature should be enabled. + /// + Conditions = 0x400, + + /// + /// The telemetry configuration for the feature flag, controlling what telemetry data is collected. + /// + Telemetry = 0x800, + + /// + /// An alias for the feature flag, providing an alternative identifier. + /// + Alias = 0x20000, + + /// + /// Allows for all the properties of a feature flag to be retrieved. + /// + All = uint.MaxValue + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFieldsExtension.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFieldsExtension.cs new file mode 100644 index 000000000000..39f5cf03163b --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFieldsExtension.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; + +namespace Azure.Data.AppConfiguration +{ + internal static partial class FeatureFlagFieldsExtensions + { + /// + /// Maps a FeatureFlagFields member to its corresponding service name in accordance with the REST API specification. + /// + private static readonly IReadOnlyDictionary s_serviceNameMap = new Dictionary() + { + { FeatureFlagFields.Name , "name" }, + { FeatureFlagFields.Label , "label" }, + { FeatureFlagFields.Alias , "alias" }, + { FeatureFlagFields.Allocation , "allocation" }, + { FeatureFlagFields.ETag , "etag" }, + { FeatureFlagFields.LastModified, "last_modified" }, + { FeatureFlagFields.IsReadOnly , "locked" }, + { FeatureFlagFields.Tags , "tags" }, + { FeatureFlagFields.Variants , "variants" }, + { FeatureFlagFields.Enabled , "enabled" }, + { FeatureFlagFields.Description , "description" }, + { FeatureFlagFields.Conditions , "conditions" }, + { FeatureFlagFields.Telemetry , "telemetry" } + }; + + /// + /// Splits flags into their corresponding service names. + /// + /// The flags to split. + /// An enumerable containing the names of the flags. The method returns null for . + public static IEnumerable Split(this FeatureFlagFields fields) + { + if (fields == FeatureFlagFields.All) + { + return null; + } + + var splitFields = new List(); + + foreach (FeatureFlagFields currentField in s_serviceNameMap.Keys) + { + if ((fields & currentField) == currentField) + { + splitFields.Add(s_serviceNameMap[currentField]); + } + } + + return splitFields; + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFilter.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFilter.cs index 53c7d7123f28..60fd03b7b580 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFilter.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagFilter.cs @@ -8,7 +8,7 @@ namespace Azure.Data.AppConfiguration /// /// A Feature filter represents a filter definition that should be evaluated by the consumer to determine if the feature is enabled. /// - public class FeatureFlagFilter + public partial class FeatureFlagFilter { /// /// Initializes a new instance of the . @@ -39,4 +39,4 @@ public FeatureFlagFilter(string name, IDictionary parameters) /// public IDictionary Parameters { get; } } -} \ No newline at end of file +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagSelector.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagSelector.cs new file mode 100644 index 000000000000..2e14fc13f393 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/FeatureFlagSelector.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace Azure.Data.AppConfiguration +{ + /// + /// is a set of options that allows selecting + /// a filtered set of entities from the + /// configuration store, and optionally allows indicating which fields of + /// each setting to retrieve. + /// Literals or filters may be specified for names and labels. + /// For more information, Filtering. + /// + public class FeatureFlagSelector + { + /// + /// A wildcard that matches any name or any label when passed as a filter + /// to Names or Labels. + /// + public static readonly string Any = "*"; + + /// + /// Name filter that will be used to select a set of entities. + /// + /// See the documentation for this client library for details on the format of filter expressions. + public string NameFilter { get; set; } + + /// + /// Label filter that will be used to select a set of entities. + /// + /// See the documentation for this client library for details on the format of filter expressions. + public string LabelFilter { get; set; } + + /// + /// Tag filter that will be used to select a set of entities. + /// Each tag in the list should be expressed as a string in the format `tag=value`. + /// + /// See the documentation for this client library for details on the format of filter expressions. + public IList TagsFilter { get; } + + /// + /// The fields of the to retrieve for each setting in the retrieved group. + /// + public FeatureFlagFields Fields { get; set; } = FeatureFlagFields.All; + + /// + /// Indicates the point in time in the revision history of the selected entities to retrieve. + /// If set, all properties of the entities in the returned group will be exactly what they + /// were at this time. + /// + public DateTimeOffset? AcceptDateTime { get; set; } + + /// + /// Creates a default that will retrieve all entities in the configuration store. + /// + public FeatureFlagSelector() + { + TagsFilter = new List(); + } + + #region nobody wants to see these + /// + /// Check if two SettingSelector instances are equal. + /// + /// The instance to compare to. + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => base.Equals(obj); + + /// + /// Get a hash code for the SettingSelector. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => base.GetHashCode(); + + /// + /// Creates a string in reference to the SettingSelector. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override string ToString() => base.ToString(); + #endregion + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/SettingFields.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/SettingFields.cs index da65b86f22d2..b91688d8df1d 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/SettingFields.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Models/SettingFields.cs @@ -57,6 +57,11 @@ public enum SettingFields : uint /// Tags = 0x0080, + /// + /// A description of this key value. + /// + Description = 0x0160, + /// /// Allows for all the properties of a ConfigurationSetting to be retrieved. /// diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/AsyncConditionalPageable.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/AsyncConditionalPageable.cs index 346d10b799fd..aaecec5a05ac 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/AsyncConditionalPageable.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/AsyncConditionalPageable.cs @@ -7,17 +7,17 @@ namespace Azure.Data.AppConfiguration { - internal class AsyncConditionalPageable : AsyncPageable + internal class AsyncConditionalPageable : AsyncPageable { - private readonly ConditionalPageableImplementation _implementation; + private readonly ConditionalPageableImplementation _implementation; - public AsyncConditionalPageable(ConditionalPageableImplementation implementation) + public AsyncConditionalPageable(ConditionalPageableImplementation implementation) { _implementation = implementation; } - public override IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => _implementation.GetAsyncEnumerator(cancellationToken); - public override IAsyncEnumerable> AsPages(string continuationToken = null, int? pageSizeHint = null) => _implementation.AsPagesAsync(Array.Empty(), continuationToken, pageSizeHint, default); - public IAsyncEnumerable> AsPages(IEnumerable conditions, string continuationToken = null, int? pageSizeHint = null) => _implementation.AsPagesAsync(conditions, continuationToken, pageSizeHint, default); + public override IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => _implementation.GetAsyncEnumerator(cancellationToken); + public override IAsyncEnumerable> AsPages(string continuationToken = null, int? pageSizeHint = null) => _implementation.AsPagesAsync(Array.Empty(), continuationToken, pageSizeHint, default); + public IAsyncEnumerable> AsPages(IEnumerable conditions, string continuationToken = null, int? pageSizeHint = null) => _implementation.AsPagesAsync(conditions, continuationToken, pageSizeHint, default); } } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/ConditionalPageable.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/ConditionalPageable.cs index 57e651f63426..daa83ae1179a 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/ConditionalPageable.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/ConditionalPageable.cs @@ -6,16 +6,16 @@ namespace Azure.Data.AppConfiguration { - internal class ConditionalPageable : Pageable + internal class ConditionalPageable : Pageable { - private readonly ConditionalPageableImplementation _implementation; + private readonly ConditionalPageableImplementation _implementation; - public ConditionalPageable(ConditionalPageableImplementation implementation) + public ConditionalPageable(ConditionalPageableImplementation implementation) { _implementation = implementation; } - public override IEnumerable> AsPages(string continuationToken = null, int? pageSizeHint = null) => _implementation.AsPages(Array.Empty(), continuationToken, pageSizeHint); - public IEnumerable> AsPages(IEnumerable conditions, string continuationToken = null, int? pageSizeHint = null) => _implementation.AsPages(conditions, continuationToken, pageSizeHint); + public override IEnumerable> AsPages(string continuationToken = null, int? pageSizeHint = null) => _implementation.AsPages(Array.Empty(), continuationToken, pageSizeHint); + public IEnumerable> AsPages(IEnumerable conditions, string continuationToken = null, int? pageSizeHint = null) => _implementation.AsPages(conditions, continuationToken, pageSizeHint); } } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/ConditionalPageableImplementation.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/ConditionalPageableImplementation.cs index 8486cf8556ef..370986270372 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/ConditionalPageableImplementation.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/Pageable/ConditionalPageableImplementation.cs @@ -11,18 +11,18 @@ namespace Azure.Data.AppConfiguration { - internal class ConditionalPageableImplementation + internal class ConditionalPageableImplementation { private readonly Func _createFirstPageRequest; private readonly Func _createNextPageRequest; private readonly HttpPipeline _pipeline; private readonly ClientDiagnostics _clientDiagnostics; - private readonly Func Values, string NextLink)> _responseParser; + private readonly Func Values, string NextLink)> _responseParser; private readonly string _scopeName; private readonly CancellationToken _cancellationToken; private readonly ErrorOptions _errorOptions; - public ConditionalPageableImplementation(Func createFirstPageRequest, Func createNextPageRequest, Func Values, string NextLink)> responseParser, HttpPipeline pipeline, ClientDiagnostics clientDiagnostics, string scopeName, RequestContext requestContext) + public ConditionalPageableImplementation(Func createFirstPageRequest, Func createNextPageRequest, Func Values, string NextLink)> responseParser, HttpPipeline pipeline, ClientDiagnostics clientDiagnostics, string scopeName, RequestContext requestContext) { _createFirstPageRequest = createFirstPageRequest; _createNextPageRequest = createNextPageRequest; @@ -34,7 +34,7 @@ public ConditionalPageableImplementation(Func GetAsyncEnumerator(CancellationToken cancellationToken = default) + public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { string nextLink = null; do @@ -52,7 +52,7 @@ public async IAsyncEnumerator GetAsyncEnumerator(Cancellat } while (!string.IsNullOrEmpty(nextLink)); } - public async IAsyncEnumerable> AsPagesAsync(IEnumerable conditionsEnumerable, string continuationToken, int? pageSizeHint, [EnumeratorCancellation] CancellationToken cancellationToken) + public async IAsyncEnumerable> AsPagesAsync(IEnumerable conditionsEnumerable, string continuationToken, int? pageSizeHint, [EnumeratorCancellation] CancellationToken cancellationToken) { var enumerator = conditionsEnumerable.GetEnumerator(); string nextLink = continuationToken; @@ -68,7 +68,7 @@ public async IAsyncEnumerable> AsPagesAsync(IEnumerab } while (!string.IsNullOrEmpty(nextLink)); } - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { string nextLink = null; do @@ -86,7 +86,7 @@ public IEnumerator GetEnumerator() } while (!string.IsNullOrEmpty(nextLink)); } - public IEnumerable> AsPages(IEnumerable conditionsEnumerable, string continuationToken, int? pageSizeHint) + public IEnumerable> AsPages(IEnumerable conditionsEnumerable, string continuationToken, int? pageSizeHint) { var enumerator = conditionsEnumerable.GetEnumerator(); string nextLink = continuationToken; @@ -167,7 +167,7 @@ private Response GetResponse(HttpMessage message) return message.Response; } - private bool TryGetItemsFromResponse(Response response, out string nextLink, out List items) + private bool TryGetItemsFromResponse(Response response, out string nextLink, out List items) { if (response is null) { @@ -184,9 +184,9 @@ private bool TryGetItemsFromResponse(Response response, out string nextLink, out } } - private Page CreatePage(Response response, out string nextLink) => + private Page CreatePage(Response response, out string nextLink) => TryGetItemsFromResponse(response, out nextLink, out var items) - ? Page.FromValues(items, nextLink, response) - : Page.FromValues(Array.Empty(), nextLink, response); + ? Page.FromValues(items, nextLink, response) + : Page.FromValues(Array.Empty(), nextLink, response); } } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureFlagEqualityComparer.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureFlagEqualityComparer.cs new file mode 100644 index 000000000000..b877760da710 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureFlagEqualityComparer.cs @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Azure.Core; +using System.ClientModel.Primitives; + +namespace Azure.Data.AppConfiguration.Tests { + internal class FeatureFlagEqualityComparer : IEqualityComparer + { + public static IEqualityComparer Instance { get; } = new FeatureFlagEqualityComparer(); + + private FeatureFlagEqualityComparer() { } + + public bool Equals(FeatureFlag x, FeatureFlag y) + { + if (x == null || y == null) + return ReferenceEquals(x, y); + + // Compare ETag, LastModified, and IsReadOnly if both are set + if (x.ETag != default && y.ETag != default) + { + if (x.ETag != y.ETag) + return false; + if (x.LastModified != y.LastModified) + return false; + if (x.IsReadOnly != y.IsReadOnly) + return false; + } + + // Compare all FeatureFlag-specific properties + if (!string.Equals(x.Name, y.Name, StringComparison.Ordinal)) + return false; + if (!string.Equals(x.Alias, y.Alias, StringComparison.Ordinal)) + return false; + if (!string.Equals(x.Label, y.Label, StringComparison.Ordinal)) + return false; + if (!string.Equals(x.Description, y.Description, StringComparison.Ordinal)) + return false; + if (x.Enabled != y.Enabled) + return false; + if (!ConditionsEquals(x.Conditions, y.Conditions)) + return false; + if (!VariantsEquals(x.Variants, y.Variants)) + return false; + if (!AllocationEquals(x.Allocation, y.Allocation)) + return false; + if (!TelemetryEquals(x.Telemetry, y.Telemetry)) + return false; + if (!TagsEquals(x, y)) + return false; + + return true; + } + + public int GetHashCode(FeatureFlag setting) + { + var hashCode = new HashCodeBuilder(); + hashCode.Add(setting.Name, StringComparer.Ordinal); + hashCode.Add(setting.Alias, StringComparer.Ordinal); + hashCode.Add(setting.Label, StringComparer.Ordinal); + hashCode.Add(setting.Description, StringComparer.Ordinal); + hashCode.Add(setting.Enabled); + hashCode.Add(setting.LastModified); + hashCode.Add(setting.ETag); + hashCode.Add(setting.IsReadOnly); + hashCode.Add(setting.Tags); + hashCode.Add(setting.Conditions); + hashCode.Add(setting.Variants); + hashCode.Add(setting.Allocation); + hashCode.Add(setting.Telemetry); + return hashCode.ToHashCode(); + } + + private bool TagsEquals(FeatureFlag x, FeatureFlag y) + { + if (x.Tags == null) + return y.Tags == null; + if (y.Tags == null) + return false; + if (x.Tags.Count != y.Tags.Count) + return false; + foreach (var kvp in x.Tags) + { + if (!y.Tags.TryGetValue(kvp.Key, out string yValue)) + return false; + if (!string.Equals(yValue, kvp.Value, StringComparison.Ordinal)) + return false; + } + return true; + } + + private bool ConditionsEquals(FeatureFlagConditions x, FeatureFlagConditions y) + { + if (x == null && y == null) + return true; + if (x == null || y == null) + return false; + + // Use serialization to compare complex objects + return SerializeAndCompare(x, y); + } + + private bool VariantsEquals(IList x, IList y) + { + if (x == null && y == null) + return true; + if (x == null || y == null) + return false; + if (x.Count != y.Count) + return false; + + // Use serialization to compare variants collections + return SerializeAndCompare(x, y); + } + + private bool AllocationEquals(FeatureFlagAllocation x, FeatureFlagAllocation y) + { + if (x == null && y == null) + return true; + if (x == null || y == null) + return false; + + // Use serialization to compare complex objects + return SerializeAndCompare(x, y); + } + + private bool TelemetryEquals(FeatureFlagTelemetryConfiguration x, FeatureFlagTelemetryConfiguration y) + { + if (x == null && y == null) + return true; + if (x == null || y == null) + return false; + + // Use serialization to compare complex objects + return SerializeAndCompare(x, y); + } + + private bool SerializeAndCompare(T x, T y) where T : IPersistableModel + { + try + { + var options = ModelReaderWriterOptions.Json; + var serializedX = x.Write(options).ToString(); + var serializedY = y.Write(options).ToString(); + return string.Equals(serializedX, serializedY, StringComparison.Ordinal); + } + catch + { + // Fallback to reference equality if serialization fails + return ReferenceEquals(x, y); + } + } + + private bool SerializeAndCompare(IList x, IList y) where T : IPersistableModel + { + try + { + var options = ModelReaderWriterOptions.Json; + var serializedX = System.Text.Json.JsonSerializer.Serialize(x.Select(item => item.Write(options).ToString())); + var serializedY = System.Text.Json.JsonSerializer.Serialize(y.Select(item => item.Write(options).ToString())); + return string.Equals(serializedX, serializedY, StringComparison.Ordinal); + } + catch + { + // Fallback to element-by-element comparison + for (int i = 0; i < x.Count; i++) + { + if (!SerializeAndCompare(x[i], y[i])) + return false; + } + return true; + } + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureFlagExtensions.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureFlagExtensions.cs new file mode 100644 index 000000000000..99c72cb9d176 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureFlagExtensions.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; + +namespace Azure.Data.AppConfiguration.Tests +{ + public static class FeatureFlagExtensions + { + public static FeatureFlag Clone(this FeatureFlag featureFlag) + { + Dictionary tags = new Dictionary(); + foreach (string key in featureFlag.Tags.Keys) + { + tags.Add(key, featureFlag.Tags[key]); + } + + var cloned = ConfigurationModelFactory.FeatureFlag( + featureFlag.Name, + alias: featureFlag.Alias, + label: featureFlag.Label, + description: featureFlag.Description, + enabled: featureFlag.Enabled, + conditions: featureFlag.Conditions, + allocation: featureFlag.Allocation, + telemetry: featureFlag.Telemetry, + tags: tags, + eTag: featureFlag.ETag, + lastModified: featureFlag.LastModified, + isReadOnly: featureFlag.IsReadOnly + ); + + // Copy variants + if (featureFlag.Variants != null && featureFlag.Variants.Count > 0) + { + foreach (var variant in featureFlag.Variants) + { + cloned.Variants.Add(variant); + } + } + + return cloned; + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureManagementMockTests.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureManagementMockTests.cs new file mode 100644 index 000000000000..8744640cc775 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureManagementMockTests.cs @@ -0,0 +1,915 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; +using Azure.Core.Pipeline; +using Azure.Core.TestFramework; +using NUnit.Framework; + +namespace Azure.Data.AppConfiguration.Tests +{ + [TestFixture(true)] + [TestFixture(false)] + public class FeatureManagementMockTests : ClientTestBase + { + private static readonly string s_endpoint = "https://contoso.appconfig.io"; + private static readonly string s_credential = "b1d9b31"; + private static readonly string s_secret = "aabbccdd"; + private static readonly string s_connectionString = $"Endpoint={s_endpoint};Id={s_credential};Secret={s_secret}"; + private static readonly string s_troubleshootingLink = "https://aka.ms/azsdk/net/appconfiguration/troubleshoot"; + private static readonly string s_version = new FeatureFlagClientOptions().Version; + + private static readonly FeatureFlag s_testFlag = ConfigurationModelFactory.FeatureFlag( + name: "test_flag", + enabled: true, + label: "test_label", + description: "test_description", + tags: new Dictionary + { + { "tag1", "value1" }, + { "tag2", "value2" } + } + ); + + public FeatureManagementMockTests(bool isAsync) : base(isAsync) { } + + private FeatureFlagClient CreateTestService(HttpPipelineTransport transport) + { + var options = new FeatureFlagClientOptions + { + Transport = transport + }; + + return InstrumentClient(new FeatureFlagClient(s_connectionString, options)); + } + + [Test] + public async Task GetFeatureFlag() + { + var response = new MockResponse(200); + response.SetContent(SerializationHelpers.Serialize(s_testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + FeatureFlag flag = await service.GetFeatureFlagAsync(s_testFlag.Name, null, default); + + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Get, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}", request.Uri.ToString()); + Assert.True(FeatureFlagEqualityComparer.Instance.Equals(s_testFlag, flag)); + } + + [Test] + public async Task GetFeatureFlagWithLabel() + { + var response = new MockResponse(200); + response.SetContent(SerializationHelpers.Serialize(s_testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + FeatureFlag flag = await service.GetFeatureFlagAsync(s_testFlag.Name, s_testFlag.Label, default); + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Get, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(FeatureFlagEqualityComparer.Instance.Equals(s_testFlag, flag)); + } + + [Test] + public void GetFeatureFlagNotFound() + { + var response = new MockResponse(404); + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + RequestFailedException exception = Assert.ThrowsAsync(async () => + { + await service.GetFeatureFlagAsync(s_testFlag.Name, null, default); + }); + + Assert.AreEqual(404, exception.Status); + } + + // This test validates that the client throws an exception with the expected error message when it receives a + // non-success status code from the service. + [TestCase((int)HttpStatusCode.Unauthorized)] + [TestCase(403)] + [TestCase((int)HttpStatusCode.NotFound)] + public void GetFeatureFlagUnsuccessfulResponse(int statusCode) + { + var response = new MockResponse(statusCode); + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + RequestFailedException exception = Assert.ThrowsAsync(async () => + { + await service.GetFeatureFlagAsync(s_testFlag.Name, null, default); + }); + + Assert.AreEqual(statusCode, exception.Status); + + Assert.True(exception?.Message.Contains(s_troubleshootingLink)); + } + + [Test] + public async Task GetFeatureFlagIfChangedModified() + { + var requestFlag = s_testFlag.Clone(); + requestFlag.ETag = new ETag("v1"); + + var responseFlag = s_testFlag.Clone(); + responseFlag.ETag = new ETag("v2"); + + var mockResponse = new MockResponse(200); + mockResponse.SetContent(SerializationHelpers.Serialize(responseFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(mockResponse); + FeatureFlagClient service = CreateTestService(mockTransport); + + Response response = await service.GetFeatureFlagAsync(requestFlag, true); + + Assert.AreEqual(200, response.GetRawResponse().Status); + FeatureFlag flag = ConfigurationModelFactory.FeatureFlag(); + Assert.DoesNotThrow(() => { flag = response; }); + + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Get, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(request.Headers.TryGetValue("If-None-Match", out var ifNoneMatch)); + Assert.AreEqual("\"v1\"", ifNoneMatch); + Assert.True(FeatureFlagEqualityComparer.Instance.Equals(responseFlag, flag)); + } + + [Test] + public async Task GetFeatureFlagIfChangedUnmodified() + { + var requestFlag = s_testFlag.Clone(); + requestFlag.ETag = new ETag("v1"); + + var mockTransport = new MockTransport(new MockResponse(304)); + FeatureFlagClient service = CreateTestService(mockTransport); + + Response response = await service.GetFeatureFlagAsync(requestFlag, true); + + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Get, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(request.Headers.TryGetValue("If-None-Match", out var ifNoneMatch)); + Assert.AreEqual("\"v1\"", ifNoneMatch); + Assert.AreEqual(304, response.GetRawResponse().Status); + bool throws = false; + try + { + FeatureFlag flag = response.Value; + } + catch + { + throws = true; + } + + Assert.True(throws); + } + + [Test] + public async Task GetFeatureFlagWithAcceptDateTime() + { + var mockResponse = new MockResponse(200); + mockResponse.SetContent(SerializationHelpers.Serialize(s_testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(mockResponse); + FeatureFlagClient service = CreateTestService(mockTransport); + + Response response = await service.GetFeatureFlagAsync(s_testFlag, DateTimeOffset.MaxValue.UtcDateTime); + + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Get, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(request.Headers.TryGetValue("Accept-Datetime", out var acceptDateTime)); + Assert.AreEqual(DateTimeOffset.MaxValue.UtcDateTime.ToString("R", CultureInfo.InvariantCulture), acceptDateTime); + Assert.False(request.Headers.TryGetValue("If-Match", out var ifMatch)); + Assert.False(request.Headers.TryGetValue("If-None-Match", out var ifNoneMatch)); + } + + [Test] + public async Task SetFeatureFlag() + { + var response = new MockResponse(200); + response.SetContent(SerializationHelpers.Serialize(s_testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + FeatureFlag flag = await service.SetFeatureFlagAsync(s_testFlag); + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Put, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + AssertContent(ModelReaderWriter.Write(s_testFlag, ModelSerializationExtensions.WireOptions).ToArray(), request); + Assert.True(FeatureFlagEqualityComparer.Instance.Equals(s_testFlag, flag)); + } + + [Test] + public void SetFeatureFlagAlreadyExists() + { + var response = new MockResponse(412); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + RequestFailedException exception = Assert.ThrowsAsync(async () => + { + FeatureFlag flag = await service.AddFeatureFlagAsync(s_testFlag); + }); + Assert.AreEqual(412, exception.Status); + } + + [Test] + public void SetReadOnlyFlagError() + { + var response = new MockResponse(409); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + RequestFailedException exception = Assert.ThrowsAsync(async () => + { + FeatureFlag flag = await service.SetFeatureFlagAsync(s_testFlag); + }); + Assert.AreEqual(409, exception.Status); + } + + [Test] + public async Task SetFeatureFlagIfUnchangedUnmodified() + { + var requestFlag = s_testFlag.Clone(); + requestFlag.ETag = new ETag("v1"); + + var responseFlag = s_testFlag.Clone(); + responseFlag.ETag = new ETag("v1"); + + var response = new MockResponse(200); + response.SetContent(SerializationHelpers.Serialize(responseFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + FeatureFlag flag = await service.SetFeatureFlagAsync(requestFlag, onlyIfUnchanged: true); + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Put, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(request.Headers.TryGetValue("If-Match", out var ifMatch)); + Assert.AreEqual("\"v1\"", ifMatch); + AssertContent(SerializationHelpers.Serialize(requestFlag, SerializeRequestFeatureFlag), request); + Assert.True(FeatureFlagEqualityComparer.Instance.Equals(responseFlag, flag)); + } + + [Test] + public void SetFeatureFlagIfUnchangedModified() + { + var requestFlag = s_testFlag.Clone(); + requestFlag.ETag = new ETag("v1"); + + var responseFlag = s_testFlag.Clone(); + responseFlag.ETag = new ETag("v2"); + + var mockResponse = new MockResponse(412); + mockResponse.SetContent(SerializationHelpers.Serialize(responseFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(mockResponse); + FeatureFlagClient service = CreateTestService(mockTransport); + + RequestFailedException exception = Assert.ThrowsAsync(async () => + { + Response response = await service.SetFeatureFlagAsync(requestFlag, onlyIfUnchanged: true); + }); + Assert.AreEqual(412, exception.Status); + + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Put, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(request.Headers.TryGetValue("If-Match", out var ifMatch)); + Assert.AreEqual("\"v1\"", ifMatch); + } + + [Test] + public async Task DeleteFeatureFlag() + { + var response = new MockResponse(200); + response.SetContent(SerializationHelpers.Serialize(s_testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + await service.DeleteFeatureFlagAsync(s_testFlag.Name, null, default); + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Delete, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}", request.Uri.ToString()); + } + + [Test] + public async Task DeleteFeatureFlagWithLabel() + { + var response = new MockResponse(200); + response.SetContent(SerializationHelpers.Serialize(s_testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + await service.DeleteFeatureFlagAsync(s_testFlag.Name, s_testFlag.Label, default); + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Delete, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + } + + [Test] + public void DeleteFeatureFlagNotFound() + { + var response = new MockResponse(404); + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + RequestFailedException exception = Assert.ThrowsAsync(async () => + { + await service.DeleteFeatureFlagAsync(s_testFlag.Name, null, default); + }); + + Assert.AreEqual(404, exception.Status); + } + + [Test] + public void DeleteFeatureFlagReadOnlyError() + { + var mockTransport = new MockTransport(new MockResponse(409)); + FeatureFlagClient service = CreateTestService(mockTransport); + + RequestFailedException exception = Assert.ThrowsAsync(async () => + { + await service.DeleteFeatureFlagAsync(s_testFlag.Name, s_testFlag.Label, default); + }); + Assert.AreEqual(409, exception.Status); + } + + [Test] + public async Task DeleteFeatureFlagIfUnchangedUnmodified() + { + var requestFlag = s_testFlag.Clone(); + requestFlag.ETag = new ETag("v1"); + + var responseFlag = s_testFlag.Clone(); + responseFlag.ETag = new ETag("v1"); + + var mockResponse = new MockResponse(200); + mockResponse.SetContent(SerializationHelpers.Serialize(responseFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(mockResponse); + FeatureFlagClient service = CreateTestService(mockTransport); + + Response response = await service.DeleteFeatureFlagAsync(requestFlag, onlyIfUnchanged: true); + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Delete, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(request.Headers.TryGetValue("If-Match", out var ifMatch)); + Assert.AreEqual("\"v1\"", ifMatch); + Assert.AreEqual(200, response.Status); + } + + [Test] + public void DeleteFeatureFlagIfUnchangedModified() + { + var requestFlag = s_testFlag.Clone(); + requestFlag.ETag = new ETag("v1"); + + var responseFlag = s_testFlag.Clone(); + responseFlag.ETag = new ETag("v2"); + + var mockResponse = new MockResponse(412); + mockResponse.SetContent(SerializationHelpers.Serialize(responseFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(mockResponse); + FeatureFlagClient service = CreateTestService(mockTransport); + + RequestFailedException exception = Assert.ThrowsAsync(async () => + { + Response response = await service.DeleteFeatureFlagAsync(requestFlag, onlyIfUnchanged: true); + }); + Assert.AreEqual(412, exception.Status); + + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Delete, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(request.Headers.TryGetValue("If-Match", out var ifMatch)); + Assert.AreEqual("\"v1\"", ifMatch); + } + + [Test] + public async Task GetFeatureFlagsBatch() + { + var response1 = new MockResponse(200); + var response1Flags = new[] + { + CreateFeatureFlag(0), + CreateFeatureFlag(1) + }; + response1.SetContent(SerializationHelpers.Serialize((Flags: response1Flags, NextLink: $"/feature-management/ff?after=5&api-version={s_version}"), SerializeFeatureFlagBatch)); + + var response2 = new MockResponse(200); + var response2Flags = new[] + { + CreateFeatureFlag(2), + CreateFeatureFlag(3), + CreateFeatureFlag(4), + }; + response2.SetContent(SerializationHelpers.Serialize((Flags: response2Flags, NextLink: (string)null), SerializeFeatureFlagBatch)); + + var mockTransport = new MockTransport(response1, response2); + FeatureFlagClient service = CreateTestService(mockTransport); + + int flagIndex = 0; + + await foreach (FeatureFlag value in service.GetFeatureFlagsAsync(new FeatureFlagSelector())) + { + Assert.AreEqual("flag" + flagIndex, value.Name); + flagIndex++; + } + + Assert.AreEqual(2, mockTransport.Requests.Count); + + MockRequest request1 = mockTransport.Requests[0]; + Assert.AreEqual(RequestMethod.Get, request1.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff?api-version={s_version}", request1.Uri.ToString()); + AssertRequestCommon(request1); + + MockRequest request2 = mockTransport.Requests[1]; + Assert.AreEqual(RequestMethod.Get, request2.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff?after=5&api-version={s_version}", request2.Uri.ToString()); + AssertRequestCommon(request1); + } + + [Test] + public async Task GetFeatureFlagsBatchUsingTags() + { + var response1 = new MockResponse(200); + var mockTags = new Dictionary + { + { "tag1", "value1" }, + { "tag2", "value2" } + }; + var response1Flags = new[] + { + CreateFeatureFlag(0, mockTags), + CreateFeatureFlag(1, mockTags) + }; + response1.SetContent(SerializationHelpers.Serialize((Flags: response1Flags, NextLink: $"/feature-management/ff?after=5&api-version={s_version}"), SerializeFeatureFlagBatch)); + + var response2 = new MockResponse(200); + var response2Flags = new[] + { + CreateFeatureFlag(2, mockTags), + CreateFeatureFlag(3, mockTags), + CreateFeatureFlag(4, mockTags), + }; + response2.SetContent(SerializationHelpers.Serialize((Flags: response2Flags, NextLink: (string)null), SerializeFeatureFlagBatch)); + + var mockTransport = new MockTransport(response1, response2); + FeatureFlagClient service = CreateTestService(mockTransport); + + var parsedTags = mockTags.Select(t => $"{t.Key}={t.Value}").ToList(); + int flagIndex = 0; + + var selector = new FeatureFlagSelector(); + foreach (var tag in parsedTags) + { + selector.TagsFilter.Add(tag); + } + + await foreach (FeatureFlag value in service.GetFeatureFlagsAsync(selector, CancellationToken.None)) + { + Assert.AreEqual("flag" + flagIndex, value.Name); + Assert.AreEqual(mockTags, value.Tags); + flagIndex++; + } + + Assert.AreEqual(2, mockTransport.Requests.Count); + + MockRequest request1 = mockTransport.Requests[0]; + var expectedTagsQuery = string.Join("&tags=", parsedTags.Select(Uri.EscapeDataString)); + Assert.AreEqual(RequestMethod.Get, request1.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff?api-version={s_version}&tags={expectedTagsQuery}", request1.Uri.ToString()); + AssertRequestCommon(request1); + + MockRequest request2 = mockTransport.Requests[1]; + Assert.AreEqual(RequestMethod.Get, request2.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/ff?after=5&api-version={s_version}", request2.Uri.ToString()); + AssertRequestCommon(request1); + } + + [Test] + public async Task FeatureManagementConfigurableClient() + { + var response = new MockResponse(200); + response.SetContent(SerializationHelpers.Serialize(s_testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(new MockResponse(503), response); + + var options = new FeatureFlagClientOptions(); + options.Diagnostics.ApplicationId = "test_application"; + options.Transport = mockTransport; + + FeatureFlagClient client = CreateClient(s_connectionString, options); + + FeatureFlag flag = await client.GetFeatureFlagAsync(s_testFlag.Name, null, default); + Assert.True(FeatureFlagEqualityComparer.Instance.Equals(s_testFlag, flag)); + Assert.AreEqual(2, mockTransport.Requests.Count); + } + + [Test] + public async Task FeatureManagementHasApiVersionQuery() + { + var response = new MockResponse(200); + response.SetContent(SerializationHelpers.Serialize(s_testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + FeatureFlag flag = await service.SetFeatureFlagAsync(s_testFlag); + MockRequest request = mockTransport.SingleRequest; + + StringAssert.Contains("api-version", request.Uri.ToUri().ToString()); + } + + [Test] + public async Task FeatureManagementAuthorizationHeaderFormat() + { + var expectedSyntax = $"HMAC-SHA256 Credential={s_credential}&SignedHeaders=date;host;x-ms-content-sha256&Signature=(.+)"; + + var response = new MockResponse(200); + response.SetContent(SerializationHelpers.Serialize(s_testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + FeatureFlag flag = await service.SetFeatureFlagAsync(s_testFlag); + MockRequest request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.True(request.Headers.TryGetValue("Authorization", out var authHeader)); + + Assert.True(Regex.IsMatch(authHeader, expectedSyntax)); + } + + [Test] + public async Task LockFeatureFlag() + { + var response = new MockResponse(200); + var testFlag = ConfigurationModelFactory.FeatureFlag( + name: "test_flag", + enabled: true, + isReadOnly: true, + label: "test_label", + description: "test_description", + tags: new Dictionary + { + { "tag1", "value1" }, + { "tag2", "value2" } + } + ); + response.SetContent(SerializationHelpers.Serialize(testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + FeatureFlag flag = await service.SetReadOnlyAsync(testFlag.Name, testFlag.Label, true); + var request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Put, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/locks/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(FeatureFlagEqualityComparer.Instance.Equals(testFlag, flag)); + } + + [Test] + public async Task UnlockFeatureFlag() + { + var response = new MockResponse(200); + var testFlag = ConfigurationModelFactory.FeatureFlag( + name: "test_flag", + enabled: true, + isReadOnly: false, + label: "test_label", + description: "test_description", + tags: new Dictionary + { + { "tag1", "value1" }, + { "tag2", "value2" } + } + ); + response.SetContent(SerializationHelpers.Serialize(testFlag, SerializeFeatureFlag)); + + var mockTransport = new MockTransport(response); + FeatureFlagClient service = CreateTestService(mockTransport); + + FeatureFlag flag = await service.SetReadOnlyAsync(testFlag.Name, testFlag.Label, false); + var request = mockTransport.SingleRequest; + + AssertRequestCommon(request); + Assert.AreEqual(RequestMethod.Delete, request.Method); + Assert.AreEqual($"https://contoso.appconfig.io/feature-management/locks/test_flag?api-version={s_version}&label=test_label", request.Uri.ToString()); + Assert.True(FeatureFlagEqualityComparer.Instance.Equals(testFlag, flag)); + } + + [Test] + public async Task SupportsCustomTransportUse() + { + var expectedName = "abc"; + var expectedLabel = "def"; + var expectedEnabled = true; + var expectedContent = @$"{{""name"":""{expectedName}"",""label"":""{expectedLabel}"",""enabled"":{(expectedEnabled ? "true" : "false")}}}"; + + var client = new FeatureFlagClient( + s_connectionString, + new FeatureFlagClientOptions + { + Transport = new HttpClientTransport(new EchoHttpMessageHandler(expectedContent)) + } + ); + + var result = await client.GetFeatureFlagAsync("doesnt-matter", null, default); + Assert.AreEqual(expectedName, result.Value.Name); + Assert.AreEqual(expectedEnabled, result.Value.Enabled); + Assert.AreEqual(expectedLabel, result.Value.Label); + + var result2 = await client.SetFeatureFlagAsync("whatever", false, "somevalue"); + Assert.AreEqual(expectedName, result.Value.Name); + Assert.AreEqual(expectedEnabled, result.Value.Enabled); + Assert.AreEqual(expectedLabel, result.Value.Label); + } + + private void AssertContent(byte[] expected, MockRequest request, bool compareAsString = true) + { + using (var stream = new MemoryStream()) + { + request.Content.WriteTo(stream, CancellationToken.None); + if (compareAsString) + { + Assert.AreEqual(Encoding.UTF8.GetString(expected), Encoding.UTF8.GetString(stream.ToArray())); + } + else + { + CollectionAssert.AreEqual(expected, stream.ToArray()); + } + } + } + + private void AssertRequestCommon(MockRequest request) + { + Assert.True(request.Headers.TryGetValue("User-Agent", out var value)); + Version version = typeof(FeatureFlagClient).Assembly.GetName().Version; + StringAssert.Contains($"azsdk-net-Data.AppConfiguration/{version.Major}.{version.Minor}.{version.Build}", value); + } + + private static FeatureFlag CreateFeatureFlag(int i, IDictionary tags = null) + { + var flag = ConfigurationModelFactory.FeatureFlag( + name: $"flag{i}", + enabled: true, + label: "label", + description: "test description", + eTag: new ETag("c3c231fd-39a0-4cb6-3237-4614474b92c1") + ); + + if (tags != null) + { + foreach (var tag in tags) + { + flag.Tags[tag.Key] = tag.Value; + } + } + + return flag; + } + + private static SettingLabel CreateLabel(int i) + { + return new SettingLabel($"label{i}"); + } + + private void SerializeRequestFeatureFlag(ref Utf8JsonWriter json, FeatureFlag flag) + { + json.WriteStartObject(); + if (flag.Enabled.HasValue) + { + json.WriteBoolean("enabled", flag.Enabled.Value); + } + if (!string.IsNullOrEmpty(flag.Label)) + { + json.WriteString("label", flag.Label); + } + if (!string.IsNullOrEmpty(flag.Description)) + { + json.WriteString("description", flag.Description); + } + if (!string.IsNullOrEmpty(flag.Alias)) + { + json.WriteString("alias", flag.Alias); + } + if (flag.Conditions != null) + { + json.WritePropertyName("conditions"); + ((IJsonModel)flag.Conditions).Write(json, ModelSerializationExtensions.WireOptions); + } + if (flag.Variants != null && flag.Variants.Count > 0) + { + json.WriteStartArray("variants"); + foreach (var variant in flag.Variants) + { + ((IJsonModel)variant).Write(json, ModelSerializationExtensions.WireOptions); + } + json.WriteEndArray(); + } + if (flag.Allocation != null) + { + json.WritePropertyName("allocation"); + ((IJsonModel)flag.Allocation).Write(json, ModelSerializationExtensions.WireOptions); + } + if (flag.Telemetry != null) + { + json.WritePropertyName("telemetry"); + ((IJsonModel)flag.Telemetry).Write(json, ModelSerializationExtensions.WireOptions); + } + if (flag.Tags != null && flag.Tags.Count > 0) + { + json.WriteStartObject("tags"); + foreach (KeyValuePair tag in flag.Tags) + { + if (tag.Value == null) + { + json.WriteNull(tag.Key); + } + else + { + json.WriteString(tag.Key, tag.Value); + } + } + json.WriteEndObject(); + } + if (flag.IsReadOnly.HasValue) + { + json.WriteBoolean("locked", flag.IsReadOnly.Value); + } + json.WriteEndObject(); + } + + private void SerializeFeatureFlag(ref Utf8JsonWriter json, FeatureFlag flag) + { + json.WriteStartObject(); + json.WriteString("name", flag.Name); + if (flag.Alias != null) + json.WriteString("alias", flag.Alias); + json.WriteString("label", flag.Label); + json.WriteString("description", flag.Description); + json.WriteBoolean("enabled", flag.Enabled ?? false); + + if (flag.Conditions != null) + { + json.WritePropertyName("conditions"); + ((IJsonModel)flag.Conditions).Write(json, ModelSerializationExtensions.WireOptions); + } + + if (flag.Variants != null && flag.Variants.Count > 0) + { + json.WriteStartArray("variants"); + foreach (var variant in flag.Variants) + { + ((IJsonModel)variant).Write(json, ModelSerializationExtensions.WireOptions); + } + json.WriteEndArray(); + } + + if (flag.Allocation != null) + { + json.WritePropertyName("allocation"); + ((IJsonModel)flag.Allocation).Write(json, ModelSerializationExtensions.WireOptions); + } + + if (flag.Telemetry != null) + { + json.WritePropertyName("telemetry"); + ((IJsonModel)flag.Telemetry).Write(json, ModelSerializationExtensions.WireOptions); + } + + if (flag.Tags != null && flag.Tags.Count > 0) + { + json.WriteStartObject("tags"); + foreach (KeyValuePair tag in flag.Tags) + { + json.WriteString(tag.Key, tag.Value); + } + json.WriteEndObject(); + } + if (flag.ETag != default) + json.WriteString("etag", flag.ETag.ToString()); + if (flag.LastModified.HasValue) + json.WriteString("last_modified", flag.LastModified.Value.ToString()); + if (flag.IsReadOnly.HasValue) + json.WriteBoolean("locked", flag.IsReadOnly.Value); + json.WriteEndObject(); + } + + private void SerializeFeatureFlagBatch(ref Utf8JsonWriter json, (FeatureFlag[] Flags, string NextLink) content) + { + json.WriteStartObject(); + if (content.NextLink != null) + { + json.WriteString("@nextLink", content.NextLink); + } + json.WriteStartArray("items"); + foreach (FeatureFlag item in content.Flags) + { + SerializeFeatureFlag(ref json, item); + } + json.WriteEndArray(); + json.WriteEndObject(); + } + + private static void SerializeLabel(ref Utf8JsonWriter json, SettingLabel label) + { + json.WriteStartObject(); + json.WritePropertyName("name"u8); + json.WriteStringValue(label.Name); + json.WriteEndObject(); + } + + private void SerializeLabels(ref Utf8JsonWriter json, (SettingLabel[] Labels, string NextLink) content) + { + json.WriteStartObject(); + if (content.NextLink != null) + { + json.WriteString("@nextLink", content.NextLink); + } + json.WriteStartArray("items"); + foreach (SettingLabel label in content.Labels) + { + FeatureManagementMockTests.SerializeLabel(ref json, label); + } + json.WriteEndArray(); + json.WriteEndObject(); + } + + private class EchoHttpMessageHandler : HttpMessageHandler + { + private readonly string _expectedContent; + + public EchoHttpMessageHandler(string expectedJsonContent) + { + _expectedContent = expectedJsonContent; + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + return Task.FromResult(new HttpResponseMessage() + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(_expectedContent, Encoding.UTF8, "application/json") + }); + } + } + } +} diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureManagementTests.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureManagementTests.cs new file mode 100644 index 000000000000..e06b75df7505 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/FeatureManagementTests.cs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using NUnit.Framework; +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; +using Azure; + +namespace Azure.Data.AppConfiguration.Tests +{ + public class FeatureManagementTests + { + private static readonly FeatureFlag s_testFlag = ConfigurationModelFactory.FeatureFlag( + name: "testFlag", + description: "This is a test feature flag." + ); + + // + // + // Equality and clone tests + [Test] + public void FeatureFlagEquals() + { + var comparer = FeatureFlagEqualityComparer.Instance; + + //Case tests + FeatureFlag testFlagUpperCase = s_testFlag.Clone(); + testFlagUpperCase.Alias = testFlagUpperCase.Name.ToUpper(); + + FeatureFlag testFlagLowerCase = s_testFlag.Clone(); + testFlagLowerCase.Alias = testFlagLowerCase.Name.ToLower(); + Assert.IsFalse(comparer.Equals(testFlagUpperCase, testFlagLowerCase)); + + FeatureFlag testSettingSameCase = s_testFlag.Clone(); + Assert.IsTrue(comparer.Equals(s_testFlag, testSettingSameCase)); + + //Etag tests + FeatureFlag testSettingEtagDiff = testSettingSameCase.Clone(); + testSettingSameCase.ETag = new ETag(Guid.NewGuid().ToString()); + testSettingEtagDiff.ETag = new ETag(Guid.NewGuid().ToString()); + Assert.IsFalse(comparer.Equals(testSettingSameCase, testSettingEtagDiff)); + + // Different tags + FeatureFlag testFlagDiffTags = s_testFlag.Clone(); + testFlagDiffTags.Tags.Add("tag3", "test_value3"); + Assert.IsFalse(comparer.Equals(s_testFlag, testFlagDiffTags)); + } + + [Test] + public void FeatureFlagSerialization() + { + var comparer = FeatureFlagEqualityComparer.Instance; + var options = ModelReaderWriterOptions.Json; + var serialized = ((IPersistableModel)s_testFlag).Write(options); + var deserialized = ((IPersistableModel)s_testFlag).Create(serialized, options); + + Assert.IsTrue(comparer.Equals(s_testFlag, deserialized)); + } + + [Test] + public void FeatureFlagDictionarySerialization() + { + var comparer = FeatureFlagEqualityComparer.Instance; + IDictionary dict = new Dictionary + { + { s_testFlag.Name, s_testFlag }, + { "null_key", null } + }; + + var options = ModelReaderWriterOptions.Json; + var serializedDict = new Dictionary(); + foreach (var kvp in dict) + { + if (kvp.Value != null) + { + serializedDict[kvp.Key] = ((IPersistableModel)kvp.Value).Write(options); + } + else + { + serializedDict[kvp.Key] = null; + } + } + + var deserializedDict = new Dictionary(); + foreach (var kvp in serializedDict) + { + if (kvp.Value != null) + { + deserializedDict[kvp.Key] = ((IPersistableModel)s_testFlag).Create(kvp.Value, options); + } + else + { + deserializedDict[kvp.Key] = null; + } + } + + CollectionAssert.IsNotEmpty(deserializedDict); + Assert.IsTrue(comparer.Equals(s_testFlag, deserializedDict[s_testFlag.Name])); + Assert.IsNull(deserializedDict["null_key"]); + } + + [Test] + public void FeatureFlagEtagConstructor() + { + var featureFlag = ConfigurationModelFactory.FeatureFlag( + name: "name", + enabled: true, + label: "label", + eTag: new ETag("etag") + ); + Assert.AreEqual("name", featureFlag.Name); + Assert.AreEqual(true, featureFlag.Enabled); + Assert.AreEqual("label", featureFlag.Label); + Assert.AreEqual("etag", featureFlag.ETag.ToString()); + } + } +}