diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/Azure.Data.AppConfiguration.sln b/sdk/appconfiguration/Azure.Data.AppConfiguration/Azure.Data.AppConfiguration.sln index aafdf9c5d3fd..cfeea542b9c3 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/Azure.Data.AppConfiguration.sln +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/Azure.Data.AppConfiguration.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Data.AppConfiguration EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Data.AppConfiguration.Samples.Tests", "samples\Azure.Data.AppConfiguration.Samples.Tests.csproj", "{4C4126CB-6EC1-4DFB-9A1F-C52C440BC5CC}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Identity", "..\..\identity\Azure.Identity\src\Azure.Identity.csproj", "{042736B0-D082-4366-BD2B-02D49FEC6073}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {4C4126CB-6EC1-4DFB-9A1F-C52C440BC5CC}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C4126CB-6EC1-4DFB-9A1F-C52C440BC5CC}.Release|Any CPU.ActiveCfg = Release|Any CPU {4C4126CB-6EC1-4DFB-9A1F-C52C440BC5CC}.Release|Any CPU.Build.0 = Release|Any CPU + {042736B0-D082-4366-BD2B-02D49FEC6073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {042736B0-D082-4366-BD2B-02D49FEC6073}.Debug|Any CPU.Build.0 = Debug|Any CPU + {042736B0-D082-4366-BD2B-02D49FEC6073}.Release|Any CPU.ActiveCfg = Release|Any CPU + {042736B0-D082-4366-BD2B-02D49FEC6073}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/api/Azure.Data.AppConfiguration.netstandard2.0.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/api/Azure.Data.AppConfiguration.netstandard2.0.cs index cb0a2d0b11ec..15129fe0eb27 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/api/Azure.Data.AppConfiguration.netstandard2.0.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/api/Azure.Data.AppConfiguration.netstandard2.0.cs @@ -5,6 +5,8 @@ public partial class ConfigurationClient protected ConfigurationClient() { } public ConfigurationClient(string connectionString) { } public ConfigurationClient(string connectionString, Azure.Data.AppConfiguration.ConfigurationClientOptions options) { } + public ConfigurationClient(System.Uri endpoint, Azure.Core.TokenCredential credential) { } + public ConfigurationClient(System.Uri endpoint, Azure.Core.TokenCredential credential, Azure.Data.AppConfiguration.ConfigurationClientOptions options) { } public virtual Azure.Response AddConfigurationSetting(Azure.Data.AppConfiguration.ConfigurationSetting setting, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response AddConfigurationSetting(string key, string value, string label = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> AddConfigurationSettingAsync(Azure.Data.AppConfiguration.ConfigurationSetting setting, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/AuthenticationPolicy.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/AuthenticationPolicy.cs index 5beec54ffe60..713215861ce8 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/AuthenticationPolicy.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/AuthenticationPolicy.cs @@ -23,62 +23,76 @@ public AuthenticationPolicy(string credential, byte[] secret) _secret = secret; } - public override async ValueTask ProcessAsync(HttpMessage message, ReadOnlyMemory pipeline) + public override void Process(HttpMessage message, ReadOnlyMemory pipeline) { - await ProcessAsync(message, async: true).ConfigureAwait(false); + string contentHash = CreateContentHash(message); + AddHeaders(message, contentHash); + ProcessNext(message, pipeline); + } + public override async ValueTask ProcessAsync(HttpMessage message, ReadOnlyMemory pipeline) + { + string contentHash = await CreateContentHashAsync(message).ConfigureAwait(false); + AddHeaders(message, contentHash); await ProcessNextAsync(message, pipeline).ConfigureAwait(false); } - private async ValueTask ProcessAsync(HttpMessage message, bool async) + private static string CreateContentHash(HttpMessage message) { - string contentHash; + using var alg = SHA256.Create(); - using (var alg = SHA256.Create()) + using (var memoryStream = new MemoryStream()) + using (var contentHashStream = new CryptoStream(memoryStream, alg, CryptoStreamMode.Write)) { - using (var memoryStream = new MemoryStream()) - using (var contentHashStream = new CryptoStream(memoryStream, alg, CryptoStreamMode.Write)) - { - if (message.Request.Content != null) - { - if (async) - { - await message.Request.Content.WriteToAsync(contentHashStream, message.CancellationToken).ConfigureAwait(false); - } - else - { - message.Request.Content.WriteTo(contentHashStream, message.CancellationToken); - } - } - } - - contentHash = Convert.ToBase64String(alg.Hash); + message.Request.Content?.WriteTo(contentHashStream, message.CancellationToken); } - using (var hmac = new HMACSHA256(_secret)) + return Convert.ToBase64String(alg.Hash); + } + + private static async ValueTask CreateContentHashAsync(HttpMessage message) + { + using var alg = SHA256.Create(); + + using (var memoryStream = new MemoryStream()) + using (var contentHashStream = new CryptoStream(memoryStream, alg, CryptoStreamMode.Write)) { - Uri uri = message.Request.Uri.ToUri(); - var host = uri.Host; - var pathAndQuery = uri.PathAndQuery; - - string method = message.Request.Method.Method; - DateTimeOffset utcNow = DateTimeOffset.UtcNow; - var utcNowString = utcNow.ToString("r", CultureInfo.InvariantCulture); - var stringToSign = $"{method}\n{pathAndQuery}\n{utcNowString};{host};{contentHash}"; - var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.ASCII.GetBytes(stringToSign))); // Calculate the signature - string signedHeaders = "date;host;x-ms-content-sha256"; // Semicolon separated header names - - message.Request.Headers.SetValue("Date", utcNowString); - message.Request.Headers.SetValue("x-ms-content-sha256", contentHash); - message.Request.Headers.SetValue("Authorization", $"HMAC-SHA256 Credential={_credential}&SignedHeaders={signedHeaders}&Signature={signature}"); + if (message.Request.Content != null) + { + await message.Request.Content.WriteToAsync(contentHashStream, message.CancellationToken).ConfigureAwait(false); + } } + + return Convert.ToBase64String(alg.Hash); } - public override void Process(HttpMessage message, ReadOnlyMemory pipeline) + private void AddHeaders(HttpMessage message, string contentHash) { - ProcessAsync(message, async: false).GetAwaiter().GetResult(); + var utcNowString = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture); + var authorization = GetAuthorizationHeader(message.Request, contentHash, utcNowString); - ProcessNext(message, pipeline); + message.Request.Headers.SetValue("x-ms-content-sha256", contentHash); + message.Request.Headers.SetValue(HttpHeader.Names.Date, utcNowString); + message.Request.Headers.SetValue(HttpHeader.Names.Authorization, authorization); + } + + private string GetAuthorizationHeader(Request request, string contentHash, string date) { + const string signedHeaders = "date;host;x-ms-content-sha256"; // Semicolon separated header names + + var uri = request.Uri.ToUri(); + var host = uri.Host; + var pathAndQuery = uri.PathAndQuery; + var method = request.Method.Method; + + var stringToSign = $"{method}\n{pathAndQuery}\n{date};{host};{contentHash}"; + var signature = ComputeHash(stringToSign); // Calculate the signature + return $"HMAC-SHA256 Credential={_credential}&SignedHeaders={signedHeaders}&Signature={signature}"; + } + + private string ComputeHash(string value) + { + using var hmac = new HMACSHA256(_secret); + return Convert.ToBase64String(hmac.ComputeHash(Encoding.ASCII.GetBytes(value))); } } } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient.cs index 59242dff47ed..2a81a3b9e674 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Globalization; using System.Threading; using System.Threading.Tasks; @@ -15,7 +16,7 @@ namespace Azure.Data.AppConfiguration /// public partial class ConfigurationClient { - private readonly Uri _baseUri; + private readonly Uri _endpoint; private readonly HttpPipeline _pipeline; private readonly ClientDiagnostics _clientDiagnostics; @@ -47,19 +48,49 @@ public ConfigurationClient(string connectionString, ConfigurationClientOptions o if (options == null) throw new ArgumentNullException(nameof(options)); - ParseConnectionString(connectionString, out _baseUri, out var credential, out var secret); + ParseConnectionString(connectionString, out _endpoint, out var credential, out var secret); - _pipeline = HttpPipelineBuilder.Build(options, - new HttpPipelinePolicy[] { new CustomHeadersPolicy() }, - new HttpPipelinePolicy[] { - new ApiVersionPolicy(options.GetVersionString()), - new AuthenticationPolicy(credential, secret), - new SyncTokenPolicy() }, - new ResponseClassifier()); + _pipeline = CreatePipeline(options, new AuthenticationPolicy(credential, secret)); _clientDiagnostics = new ClientDiagnostics(options); } + /// + /// Initializes a new instance of the class. + /// + /// The referencing the app configuration storage. + /// The token credential used to sign requests. + public ConfigurationClient(Uri endpoint, TokenCredential credential) + : this(endpoint, credential, new ConfigurationClientOptions()) + { + } + + /// + /// 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. + public ConfigurationClient(Uri endpoint, TokenCredential credential, ConfigurationClientOptions options) + { + Argument.AssertNotNull(endpoint, nameof(endpoint)); + Argument.AssertNotNull(credential, nameof(credential)); + + _endpoint = endpoint; + _pipeline = CreatePipeline(options, new BearerTokenAuthenticationPolicy(credential, GetDefaultScope(endpoint))); + + _clientDiagnostics = new ClientDiagnostics(options); + } + + private static HttpPipeline CreatePipeline(ConfigurationClientOptions options, HttpPipelinePolicy authenticationPolicy) + => HttpPipelineBuilder.Build(options, + new HttpPipelinePolicy[] { new CustomHeadersPolicy() }, + new HttpPipelinePolicy[] { new ApiVersionPolicy(options.GetVersionString()), authenticationPolicy, new SyncTokenPolicy() }, + new ResponseClassifier()); + + private static string GetDefaultScope(Uri uri) + => $"{uri.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped)}/.default"; + /// /// Creates a if the setting, uniquely identified by key and label, does not already exist in the configuration store. /// diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient_private.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient_private.cs index e507d47006d4..87e951db4971 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient_private.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/src/ConfigurationClient_private.cs @@ -92,7 +92,7 @@ private void BuildUriForKvRoute(RequestUriBuilder builder, ConfigurationSetting private void BuildUriForKvRoute(RequestUriBuilder builder, string key, string label) { - builder.Reset(_baseUri); + builder.Reset(_endpoint); builder.AppendPath(KvRoute, escape: false); builder.AppendPath(key); @@ -104,7 +104,7 @@ private void BuildUriForKvRoute(RequestUriBuilder builder, string key, string la private void BuildUriForLocksRoute(RequestUriBuilder builder, string key, string label) { - builder.Reset(_baseUri); + builder.Reset(_endpoint); builder.AppendPath(LocksRoute, escape: false); builder.AppendPath(key); @@ -172,14 +172,14 @@ internal static void BuildBatchQuery(RequestUriBuilder builder, SettingSelector private void BuildUriForGetBatch(RequestUriBuilder builder, SettingSelector selector, string pageLink) { - builder.Reset(_baseUri); + builder.Reset(_endpoint); builder.AppendPath(KvRoute, escape: false); BuildBatchQuery(builder, selector, pageLink); } private void BuildUriForRevisions(RequestUriBuilder builder, SettingSelector selector, string pageLink) { - builder.Reset(_baseUri); + builder.Reset(_endpoint); builder.AppendPath(RevisionsRoute, escape: false); BuildBatchQuery(builder, selector, pageLink); } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/Azure.Data.AppConfiguration.Tests.csproj b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/Azure.Data.AppConfiguration.Tests.csproj index 3fe57638ac64..0a998b8f68fb 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/Azure.Data.AppConfiguration.Tests.csproj +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/Azure.Data.AppConfiguration.Tests.csproj @@ -12,6 +12,7 @@ + diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/ConfigurationLiveTests.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/ConfigurationLiveTests.cs index 1c7e4bd033fc..3eacfc09ce3b 100644 --- a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/ConfigurationLiveTests.cs +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/ConfigurationLiveTests.cs @@ -6,7 +6,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Azure.Core; using Azure.Core.Testing; +using Azure.Identity; using NUnit.Framework; namespace Azure.Data.AppConfiguration.Tests @@ -34,6 +36,14 @@ private ConfigurationClient GetClient() Recording.InstrumentClientOptions(new ConfigurationClientOptions()))); } + private ConfigurationClient GetAADClient() + { + string endpoint = Recording.RequireVariableFromEnvironment("APPCONFIGURATION_ENDPOINT_STRING"); + TokenCredential credential = Recording.GetCredential(new DefaultAzureCredential()); + ConfigurationClientOptions options = Recording.InstrumentClientOptions(new ConfigurationClientOptions()); + return InstrumentClient(new ConfigurationClient(new Uri(endpoint), credential, options)); + } + private ConfigurationSetting CreateSetting() { return new ConfigurationSetting() @@ -1264,5 +1274,22 @@ public async Task ClearReadOnlySettingNotFound() await service.DeleteConfigurationSettingAsync(testSetting.Key, testSetting.Label); } } + + [Test] + public async Task AddSettingDefaultAAD() + { + ConfigurationClient service = GetAADClient(); + ConfigurationSetting testSetting = CreateSetting(); + + try + { + ConfigurationSetting setting = await service.AddConfigurationSettingAsync(testSetting); + Assert.True(ConfigurationSettingEqualityComparer.Instance.Equals(testSetting, setting)); + } + finally + { + await service.DeleteConfigurationSettingAsync(testSetting.Key, testSetting.Label); + } + } } } diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/SessionRecords/ConfigurationLiveTests/AddSettingDefaultAAD.json b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/SessionRecords/ConfigurationLiveTests/AddSettingDefaultAAD.json new file mode 100644 index 000000000000..0b1501585bee --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/SessionRecords/ConfigurationLiveTests/AddSettingDefaultAAD.json @@ -0,0 +1,116 @@ +{ + "Entries": [ + { + "RequestUri": "https://pakrym-azconfig-ui.azconfig.io/kv/key-2026823494?label=test_label\u0026api-version=1.0", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/vnd.microsoft.appconfig.kv\u002Bjson", + "Authorization": "Sanitized", + "Content-Length": "98", + "Content-Type": "application/json", + "If-None-Match": "*", + "traceparent": "00-08551d33afaa6e43a1b7dba697c58aa9-8a91a2e4c0fb8c46-00", + "User-Agent": [ + "azsdk-net-Data.AppConfiguration/1.0.0-dev.20191111.1\u002B5e9c11c2b896d540b7abb4e7448141d3ff4f1cc7", + "(.NET Core 4.6.28008.01; Microsoft Windows 10.0.18362 )" + ], + "x-ms-client-request-id": "44ff319f3d99e21b7f1662bb9c839020", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "value": "test_value", + "content_type": "test_content_type", + "tags": { + "tag1": "value1", + "tag2": "value2" + } + }, + "StatusCode": 200, + "ResponseHeaders": { + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Headers": "DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, x-ms-retry-after, x-ms-request-id, WWW-Authenticate", + "Access-Control-Allow-Methods": "GET, PUT, POST, DELETE, PATCH, OPTIONS", + "Access-Control-Allow-Origin": "*", + "Access-Control-Expose-Headers": "DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, x-ms-retry-after, x-ms-request-id, WWW-Authenticate", + "Connection": "keep-alive", + "Content-Type": "application/vnd.microsoft.appconfig.kv\u002Bjson; charset=utf-8", + "Date": "Mon, 11 Nov 2019 21:48:41 GMT", + "ETag": "\u0022TiWG7RSrx9UVoqrXwOz29rW5CCU\u0022", + "Last-Modified": "Mon, 11 Nov 2019 21:48:42 GMT", + "Server": "openresty/1.15.8.1", + "Strict-Transport-Security": "max-age=15724800; includeSubDomains", + "Sync-Token": "zAJw6V16=ODotMSM4MDA5Mjk=;sn=800929", + "Transfer-Encoding": "chunked", + "x-ms-client-request-id": "44ff319f3d99e21b7f1662bb9c839020", + "x-ms-correlation-request-id": "34513690-fef5-4201-b6df-06896bfa8392", + "x-ms-request-id": "34513690-fef5-4201-b6df-06896bfa8392" + }, + "ResponseBody": { + "etag": "TiWG7RSrx9UVoqrXwOz29rW5CCU", + "key": "key-2026823494", + "label": "test_label", + "content_type": "test_content_type", + "value": "test_value", + "tags": { + "tag1": "value1", + "tag2": "value2" + }, + "locked": false, + "last_modified": "2019-11-11T21:48:42\u002B00:00" + } + }, + { + "RequestUri": "https://pakrym-azconfig-ui.azconfig.io/kv/key-2026823494?label=test_label\u0026api-version=1.0", + "RequestMethod": "DELETE", + "RequestHeaders": { + "Authorization": "Sanitized", + "Sync-Token": "zAJw6V16=ODotMSM4MDA5Mjk=", + "traceparent": "00-aa1e6307e193654da2e32369d6e7ba4f-da044fda83314942-00", + "User-Agent": [ + "azsdk-net-Data.AppConfiguration/1.0.0-dev.20191111.1\u002B5e9c11c2b896d540b7abb4e7448141d3ff4f1cc7", + "(.NET Core 4.6.28008.01; Microsoft Windows 10.0.18362 )" + ], + "x-ms-client-request-id": "fab67614156be9558cdd8ee4d38bdc0d", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Headers": "DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, x-ms-retry-after, x-ms-request-id, WWW-Authenticate", + "Access-Control-Allow-Methods": "GET, PUT, POST, DELETE, PATCH, OPTIONS", + "Access-Control-Allow-Origin": "*", + "Access-Control-Expose-Headers": "DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, x-ms-retry-after, x-ms-request-id, WWW-Authenticate", + "Connection": "keep-alive", + "Content-Type": "application/vnd.microsoft.appconfig.kv\u002Bjson; charset=utf-8", + "Date": "Mon, 11 Nov 2019 21:48:41 GMT", + "ETag": "\u0022TiWG7RSrx9UVoqrXwOz29rW5CCU\u0022", + "Last-Modified": "Mon, 11 Nov 2019 21:48:42 GMT", + "Server": "openresty/1.15.8.1", + "Strict-Transport-Security": "max-age=15724800; includeSubDomains", + "Sync-Token": "zAJw6V16=ODotMSM4MDA5MzA=;sn=800930", + "Transfer-Encoding": "chunked", + "x-ms-client-request-id": "fab67614156be9558cdd8ee4d38bdc0d", + "x-ms-correlation-request-id": "fc7dbbf0-5231-47b2-8176-12709aca965a", + "x-ms-request-id": "fc7dbbf0-5231-47b2-8176-12709aca965a" + }, + "ResponseBody": { + "etag": "TiWG7RSrx9UVoqrXwOz29rW5CCU", + "key": "key-2026823494", + "label": "test_label", + "content_type": "test_content_type", + "value": "test_value", + "tags": { + "tag1": "value1", + "tag2": "value2" + }, + "locked": false, + "last_modified": "2019-11-11T21:48:42\u002B00:00" + } + } + ], + "Variables": { + "APPCONFIGURATION_ENDPOINT_STRING": "https://pakrym-azconfig-ui.azconfig.io", + "RandomSeed": "581282501" + } +} \ No newline at end of file diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/SessionRecords/ConfigurationLiveTests/AddSettingDefaultAADAsync.json b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/SessionRecords/ConfigurationLiveTests/AddSettingDefaultAADAsync.json new file mode 100644 index 000000000000..3cd2a697cbee --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/SessionRecords/ConfigurationLiveTests/AddSettingDefaultAADAsync.json @@ -0,0 +1,116 @@ +{ + "Entries": [ + { + "RequestUri": "https://pakrym-azconfig-ui.azconfig.io/kv/key-990160948?label=test_label\u0026api-version=1.0", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/vnd.microsoft.appconfig.kv\u002Bjson", + "Authorization": "Sanitized", + "Content-Length": "98", + "Content-Type": "application/json", + "If-None-Match": "*", + "traceparent": "00-4fdc7e7f8766e74383262888faf92c06-07d4802766cecc42-00", + "User-Agent": [ + "azsdk-net-Data.AppConfiguration/1.0.0-dev.20191111.1\u002B5e9c11c2b896d540b7abb4e7448141d3ff4f1cc7", + "(.NET Core 4.6.28008.01; Microsoft Windows 10.0.18362 )" + ], + "x-ms-client-request-id": "ae92c91dae1a136dd13a89cf4212227b", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "value": "test_value", + "content_type": "test_content_type", + "tags": { + "tag1": "value1", + "tag2": "value2" + } + }, + "StatusCode": 200, + "ResponseHeaders": { + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Headers": "DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, x-ms-retry-after, x-ms-request-id, WWW-Authenticate", + "Access-Control-Allow-Methods": "GET, PUT, POST, DELETE, PATCH, OPTIONS", + "Access-Control-Allow-Origin": "*", + "Access-Control-Expose-Headers": "DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, x-ms-retry-after, x-ms-request-id, WWW-Authenticate", + "Connection": "keep-alive", + "Content-Type": "application/vnd.microsoft.appconfig.kv\u002Bjson; charset=utf-8", + "Date": "Mon, 11 Nov 2019 21:48:42 GMT", + "ETag": "\u0022yUB3lJiCfBlzvuDuAdWtUb73tAc\u0022", + "Last-Modified": "Mon, 11 Nov 2019 21:48:42 GMT", + "Server": "openresty/1.15.8.1", + "Strict-Transport-Security": "max-age=15724800; includeSubDomains", + "Sync-Token": "zAJw6V16=ODotMSM4MDA5MzE=;sn=800931", + "Transfer-Encoding": "chunked", + "x-ms-client-request-id": "ae92c91dae1a136dd13a89cf4212227b", + "x-ms-correlation-request-id": "5767a4a9-926a-4d56-bea5-48a33a552a0e", + "x-ms-request-id": "5767a4a9-926a-4d56-bea5-48a33a552a0e" + }, + "ResponseBody": { + "etag": "yUB3lJiCfBlzvuDuAdWtUb73tAc", + "key": "key-990160948", + "label": "test_label", + "content_type": "test_content_type", + "value": "test_value", + "tags": { + "tag1": "value1", + "tag2": "value2" + }, + "locked": false, + "last_modified": "2019-11-11T21:48:42\u002B00:00" + } + }, + { + "RequestUri": "https://pakrym-azconfig-ui.azconfig.io/kv/key-990160948?label=test_label\u0026api-version=1.0", + "RequestMethod": "DELETE", + "RequestHeaders": { + "Authorization": "Sanitized", + "Sync-Token": "zAJw6V16=ODotMSM4MDA5MzE=", + "traceparent": "00-9d5ab7cb9efeff4f8b11010ae11d1c7c-b5ebda30a4dcd544-00", + "User-Agent": [ + "azsdk-net-Data.AppConfiguration/1.0.0-dev.20191111.1\u002B5e9c11c2b896d540b7abb4e7448141d3ff4f1cc7", + "(.NET Core 4.6.28008.01; Microsoft Windows 10.0.18362 )" + ], + "x-ms-client-request-id": "10951946b0a967507aade2aac5f7f607", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Headers": "DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, x-ms-retry-after, x-ms-request-id, WWW-Authenticate", + "Access-Control-Allow-Methods": "GET, PUT, POST, DELETE, PATCH, OPTIONS", + "Access-Control-Allow-Origin": "*", + "Access-Control-Expose-Headers": "DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, x-ms-retry-after, x-ms-request-id, WWW-Authenticate", + "Connection": "keep-alive", + "Content-Type": "application/vnd.microsoft.appconfig.kv\u002Bjson; charset=utf-8", + "Date": "Mon, 11 Nov 2019 21:48:42 GMT", + "ETag": "\u0022yUB3lJiCfBlzvuDuAdWtUb73tAc\u0022", + "Last-Modified": "Mon, 11 Nov 2019 21:48:42 GMT", + "Server": "openresty/1.15.8.1", + "Strict-Transport-Security": "max-age=15724800; includeSubDomains", + "Sync-Token": "zAJw6V16=ODotMSM4MDA5MzI=;sn=800932", + "Transfer-Encoding": "chunked", + "x-ms-client-request-id": "10951946b0a967507aade2aac5f7f607", + "x-ms-correlation-request-id": "a94cf0d4-7a70-476b-9052-ca1b761fa47f", + "x-ms-request-id": "a94cf0d4-7a70-476b-9052-ca1b761fa47f" + }, + "ResponseBody": { + "etag": "yUB3lJiCfBlzvuDuAdWtUb73tAc", + "key": "key-990160948", + "label": "test_label", + "content_type": "test_content_type", + "value": "test_value", + "tags": { + "tag1": "value1", + "tag2": "value2" + }, + "locked": false, + "last_modified": "2019-11-11T21:48:42\u002B00:00" + } + } + ], + "Variables": { + "APPCONFIGURATION_ENDPOINT_STRING": "https://pakrym-azconfig-ui.azconfig.io", + "RandomSeed": "1755329843" + } +} \ No newline at end of file diff --git a/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/TestRecordingExtensions.cs b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/TestRecordingExtensions.cs new file mode 100644 index 000000000000..be41a232d5f2 --- /dev/null +++ b/sdk/appconfiguration/Azure.Data.AppConfiguration/tests/TestRecordingExtensions.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core.Testing; +using NUnit.Framework; + +namespace Azure.Data.AppConfiguration.Tests +{ + public static class TestRecordingExtensions + { + public static string RequireVariableFromEnvironment(this TestRecording recording, string variableName) + { + var variable = recording.GetVariableFromEnvironment(variableName); + Assert.NotNull(variable, $"Set {variableName} environment variable"); + return variable; + } + } +}