diff --git a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/CHANGELOG.md b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/CHANGELOG.md index b438f70799af..a0b1b96487cb 100644 --- a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/CHANGELOG.md +++ b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/CHANGELOG.md @@ -16,6 +16,7 @@ - Moved `DatasourceCredential`, `DataFeedSource`, `NotificationHook`, and all of their concrete child types to the `Azure.AI.MetricsAdvisor.Administration` namespace. - Moved `MetricFeedback` and all of its concrete child types to the `Azure.AI.MetricsAdvisor` namespace. - Changed order of parameters of `MetricsAdvisorClient.GetMetricEnrichedSeriesData`. Now, `detectionConfigurationId` appears first. +- In `MetricsAdvisorKeyCredential`, merged `UpdateSubscriptionKey` and `UpdateApiKey` into a single method, `Update`, to make it an atomic operation. ## 1.0.0-beta.4 (2021-06-07) diff --git a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/api/Azure.AI.MetricsAdvisor.netstandard2.0.cs b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/api/Azure.AI.MetricsAdvisor.netstandard2.0.cs index 09fdebdf631e..139bd8c1d7af 100644 --- a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/api/Azure.AI.MetricsAdvisor.netstandard2.0.cs +++ b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/api/Azure.AI.MetricsAdvisor.netstandard2.0.cs @@ -224,8 +224,7 @@ public enum ServiceVersion public partial class MetricsAdvisorKeyCredential { public MetricsAdvisorKeyCredential(string subscriptionKey, string apiKey) { } - public void UpdateApiKey(string apiKey) { } - public void UpdateSubscriptionKey(string subscriptionKey) { } + public void Update(string subscriptionKey, string apiKey) { } } } namespace Azure.AI.MetricsAdvisor.Administration diff --git a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/src/MetricsAdvisorKeyCredential.cs b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/src/MetricsAdvisorKeyCredential.cs index a8139bb1b394..611548600cba 100644 --- a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/src/MetricsAdvisorKeyCredential.cs +++ b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/src/MetricsAdvisorKeyCredential.cs @@ -13,59 +13,40 @@ namespace Azure.AI.MetricsAdvisor /// public class MetricsAdvisorKeyCredential { - private string _subscriptionKey; - - private string _apiKey; + private Tuple _keyPair; /// /// Initializes a new instance of the class. /// /// The subscription key to use to authenticate with the Azure service. - /// The API key to use to authenticate the user with the Metrics Advisor service. Used to identify administrators. + /// The API key to use to authenticate the user with the Metrics Advisor service. Used to differentiate administrators and viewers. /// or is null. /// or is empty. public MetricsAdvisorKeyCredential(string subscriptionKey, string apiKey) { - UpdateSubscriptionKey(subscriptionKey); - UpdateApiKey(apiKey); - } - - internal string SubscriptionKey - { - get => Volatile.Read(ref _subscriptionKey); - private set => Volatile.Write(ref _subscriptionKey, value); + Update(subscriptionKey, apiKey); } - internal string ApiKey + internal Tuple KeyPair { - get => Volatile.Read(ref _apiKey); - private set => Volatile.Write(ref _apiKey, value); + get => Volatile.Read(ref _keyPair); + private set => Volatile.Write(ref _keyPair, value); } /// - /// Updates the subscription key. This is intended to be used when you've regenerated - /// your subscription key and want to update long lived clients. + /// Updates the subscription and API keys. This is intended to be used when you've regenerated + /// your keys and want to update long lived clients. /// /// The subscription key to authenticate the service against. - /// is null. - /// is empty. - public void UpdateSubscriptionKey(string subscriptionKey) + /// The API key to use to authenticate the user with the Metrics Advisor service. Used to differentiate administrators and viewers. + /// or is null. + /// or is empty. + public void Update(string subscriptionKey, string apiKey) { Argument.AssertNotNullOrEmpty(subscriptionKey, nameof(subscriptionKey)); - SubscriptionKey = subscriptionKey; - } - - /// - /// Updates the API key. This is intended to be used when you've regenerated your - /// API key and want to update long lived clients. - /// - /// The API key to use to authenticate the user with the Metrics Advisor service. Used to identify administrators. - /// is null. - /// is empty. - public void UpdateApiKey(string apiKey) - { Argument.AssertNotNullOrEmpty(apiKey, nameof(apiKey)); - ApiKey = apiKey; + + KeyPair = Tuple.Create(subscriptionKey, apiKey); } } } diff --git a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/src/MetricsAdvisorKeyCredentialPolicy.cs b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/src/MetricsAdvisorKeyCredentialPolicy.cs index 0a2bea706b7e..d0f3b0bc2ec1 100644 --- a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/src/MetricsAdvisorKeyCredentialPolicy.cs +++ b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/src/MetricsAdvisorKeyCredentialPolicy.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using Azure.Core; using Azure.Core.Pipeline; @@ -20,8 +21,10 @@ public override void OnSendingRequest(HttpMessage message) { base.OnSendingRequest(message); - message.Request.Headers.SetValue(Constants.SubscriptionAuthorizationHeader, _credential.SubscriptionKey); - message.Request.Headers.SetValue(Constants.ApiAuthorizationHeader, _credential.ApiKey); + Tuple keyPair = _credential.KeyPair; + + message.Request.Headers.SetValue(Constants.SubscriptionAuthorizationHeader, keyPair.Item1); + message.Request.Headers.SetValue(Constants.ApiAuthorizationHeader, keyPair.Item2); } } } diff --git a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/MetricsAdvisorAdministrationClient/NotificationHookLiveTests.cs b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/MetricsAdvisorAdministrationClient/NotificationHookLiveTests.cs index bdf4e3d7172d..f9ab53d22263 100644 --- a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/MetricsAdvisorAdministrationClient/NotificationHookLiveTests.cs +++ b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/MetricsAdvisorAdministrationClient/NotificationHookLiveTests.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading.Tasks; using Azure.AI.MetricsAdvisor.Administration; -using Azure.AI.MetricsAdvisor.Models; using Azure.Core.TestFramework; using NUnit.Framework; diff --git a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/MockClientTestBase.cs b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/MockClientTestBase.cs index 7d6ae1f3c697..f2e76a366257 100644 --- a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/MockClientTestBase.cs +++ b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/MockClientTestBase.cs @@ -18,11 +18,14 @@ public MockClientTestBase(bool isAsync) : base(isAsync) public string FakeGuid => "00000000-0000-0000-0000-000000000000"; - public MetricsAdvisorClient CreateInstrumentedClient(MockResponse response) + public MetricsAdvisorClient CreateInstrumentedClient(MockResponse response) => + CreateInstrumentedClient(new MockTransport(response)); + + public MetricsAdvisorClient CreateInstrumentedClient(MockTransport transport, MetricsAdvisorKeyCredential credential = null) { var fakeEndpoint = new Uri("http://notreal.azure.com"); - var fakeCredential = new MetricsAdvisorKeyCredential("fakeSubscriptionKey", "fakeApiKey"); - var options = new MetricsAdvisorClientsOptions() { Transport = new MockTransport(response) }; + var fakeCredential = credential ?? new MetricsAdvisorKeyCredential("fakeSubscriptionKey", "fakeApiKey"); + var options = new MetricsAdvisorClientsOptions() { Transport = transport }; return InstrumentClient(new MetricsAdvisorClient(fakeEndpoint, fakeCredential, options)); } @@ -30,10 +33,10 @@ public MetricsAdvisorClient CreateInstrumentedClient(MockResponse response) public MetricsAdvisorAdministrationClient CreateInstrumentedAdministrationClient(MockResponse response) => CreateInstrumentedAdministrationClient(new MockTransport(response)); - public MetricsAdvisorAdministrationClient CreateInstrumentedAdministrationClient(MockTransport transport) + public MetricsAdvisorAdministrationClient CreateInstrumentedAdministrationClient(MockTransport transport, MetricsAdvisorKeyCredential credential = null) { var fakeEndpoint = new Uri("http://notreal.azure.com"); - var fakeCredential = new MetricsAdvisorKeyCredential("fakeSubscriptionKey", "fakeApiKey"); + var fakeCredential = credential ?? new MetricsAdvisorKeyCredential("fakeSubscriptionKey", "fakeApiKey"); var options = new MetricsAdvisorClientsOptions() { Transport = transport }; return InstrumentClient(new MetricsAdvisorAdministrationClient(fakeEndpoint, fakeCredential, options)); diff --git a/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/Models/MetricsAdvisorKeyCredentialTests.cs b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/Models/MetricsAdvisorKeyCredentialTests.cs new file mode 100644 index 000000000000..b56424ca928c --- /dev/null +++ b/sdk/metricsadvisor/Azure.AI.MetricsAdvisor/tests/Models/MetricsAdvisorKeyCredentialTests.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Azure.AI.MetricsAdvisor.Administration; +using Azure.Core.TestFramework; +using NUnit.Framework; + +namespace Azure.AI.MetricsAdvisor.Tests +{ + public class MetricsAdvisorKeyCredentialTests : MockClientTestBase + { + public MetricsAdvisorKeyCredentialTests(bool isAsync) : base(isAsync) + { + } + + [Test] + public async Task MetricsAdvisorKeyCredentialSendsSecretInMetricsAdvisorClient() + { + MockResponse response = new MockResponse(200); + response.SetContent("{}"); + + MockTransport mockTransport = new MockTransport(response); + + string expectedSubscriptionKey = "fakeSubscriptionKey"; + string expectedApiKey = "fakeApiKey"; + MetricsAdvisorKeyCredential credential = new MetricsAdvisorKeyCredential(expectedSubscriptionKey, expectedApiKey); + + MetricsAdvisorClient client = CreateInstrumentedClient(mockTransport, credential); + + IAsyncEnumerator asyncEnumerator = client.GetAllFeedbackAsync(FakeGuid).GetAsyncEnumerator(); + await asyncEnumerator.MoveNextAsync(); + + MockRequest request = mockTransport.Requests.First(); + + Assert.That(request.Headers.TryGetValue(Constants.SubscriptionAuthorizationHeader, out string subscriptionKey)); + Assert.That(request.Headers.TryGetValue(Constants.ApiAuthorizationHeader, out string apiKey)); + + Assert.That(subscriptionKey, Is.EqualTo(expectedSubscriptionKey)); + Assert.That(apiKey, Is.EqualTo(expectedApiKey)); + } + + [Test] + public async Task MetricsAdvisorKeyCredentialSendsSecretInMetricsAdvisorAdministrationClient() + { + MockResponse response = new MockResponse(204); + MockTransport mockTransport = new MockTransport(response); + + string expectedSubscriptionKey = "fakeSubscriptionKey"; + string expectedApiKey = "fakeApiKey"; + MetricsAdvisorKeyCredential credential = new MetricsAdvisorKeyCredential(expectedSubscriptionKey, expectedApiKey); + + MetricsAdvisorAdministrationClient adminClient = CreateInstrumentedAdministrationClient(mockTransport, credential); + + await adminClient.DeleteAlertConfigurationAsync(FakeGuid); + + MockRequest request = mockTransport.Requests.First(); + + Assert.That(request.Headers.TryGetValue(Constants.SubscriptionAuthorizationHeader, out string subscriptionKey)); + Assert.That(request.Headers.TryGetValue(Constants.ApiAuthorizationHeader, out string apiKey)); + + Assert.That(subscriptionKey, Is.EqualTo(expectedSubscriptionKey)); + Assert.That(apiKey, Is.EqualTo(expectedApiKey)); + } + + [Test] + public async Task MetricsAdvisorKeyCredentialUpdatesSecret() + { + MockResponse response = new MockResponse(204); + MockTransport mockTransport = new MockTransport(response); + MetricsAdvisorKeyCredential credential = new MetricsAdvisorKeyCredential("fakeSubscriptionKey", "fakeApiKey"); + + string expectedSubscriptionKey = "newFakeSubscriptionKey"; + string expectedApiKey = "newFakeApiKey"; + + MetricsAdvisorAdministrationClient adminClient = CreateInstrumentedAdministrationClient(mockTransport, credential); + + credential.Update(expectedSubscriptionKey, expectedApiKey); + + await adminClient.DeleteAlertConfigurationAsync(FakeGuid); + + MockRequest request = mockTransport.Requests.First(); + + Assert.That(request.Headers.TryGetValue(Constants.SubscriptionAuthorizationHeader, out string subscriptionKey)); + Assert.That(request.Headers.TryGetValue(Constants.ApiAuthorizationHeader, out string apiKey)); + + Assert.That(subscriptionKey, Is.EqualTo(expectedSubscriptionKey)); + Assert.That(apiKey, Is.EqualTo(expectedApiKey)); + } + } +}