From 95b40c2cab1d14b689d947feff2f139f3803e912 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 21 Jan 2021 14:11:10 -0800 Subject: [PATCH] [metricsadvisor] support key rotation for MetricsAdvisorKeyCredential (#16276) * allow rotation of the api/sub keys for MetricsAdvisorKeyCredential * fix tests * fix tests * update changelog --- .../azure-ai-metricsadvisor/CHANGELOG.md | 1 + .../_metrics_advisor_key_credential.py | 44 ++++++- ...l.test_credential_rotate_api_key_only.yaml | 112 ++++++++++++++++++ ...tial.test_credential_rotate_both_keys.yaml | 102 ++++++++++++++++ ...l.test_credential_rotate_sub_key_only.yaml | 102 ++++++++++++++++ .../tests/test_credential.py | 112 ++++++++++++++++++ 6 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_api_key_only.yaml create mode 100644 sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_both_keys.yaml create mode 100644 sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_sub_key_only.yaml create mode 100644 sdk/metricsadvisor/azure-ai-metricsadvisor/tests/test_credential.py diff --git a/sdk/metricsadvisor/azure-ai-metricsadvisor/CHANGELOG.md b/sdk/metricsadvisor/azure-ai-metricsadvisor/CHANGELOG.md index bb0f24a01cb0..bc7b26f601f4 100644 --- a/sdk/metricsadvisor/azure-ai-metricsadvisor/CHANGELOG.md +++ b/sdk/metricsadvisor/azure-ai-metricsadvisor/CHANGELOG.md @@ -5,6 +5,7 @@ **New Features** - AAD support authentication #15922 +- `MetricsAdvisorKeyCredential` support for rotating the subscription and api keys to update long-lived clients ## 1.0.0b2 (2020-11-10) diff --git a/sdk/metricsadvisor/azure-ai-metricsadvisor/azure/ai/metricsadvisor/_metrics_advisor_key_credential.py b/sdk/metricsadvisor/azure-ai-metricsadvisor/azure/ai/metricsadvisor/_metrics_advisor_key_credential.py index b595f02f9be6..9e4cabb2d9da 100644 --- a/sdk/metricsadvisor/azure-ai-metricsadvisor/azure/ai/metricsadvisor/_metrics_advisor_key_credential.py +++ b/sdk/metricsadvisor/azure-ai-metricsadvisor/azure/ai/metricsadvisor/_metrics_advisor_key_credential.py @@ -18,5 +18,45 @@ def __init__(self, subscription_key, api_key): # type: (str, str) -> None if not (isinstance(subscription_key, six.string_types) and isinstance(api_key, six.string_types)): raise TypeError("key must be a string.") - self.subscription_key = subscription_key # type: str - self.api_key = api_key # type: str + self._subscription_key = subscription_key # type: str + self._api_key = api_key # type: str + + @property + def subscription_key(self): + # type: () -> str + """The value of the subscription key. + + :rtype: str + """ + return self._subscription_key + + @property + def api_key(self): + # type: () -> str + """The value of the api key. + + :rtype: str + """ + return self._api_key + + def update(self, subscription_key=None, api_key=None): + # type: (str, str) -> None + """Update the subscription and/or api key. + + This can be used when you've regenerated your service keys and want + to update long-lived clients. + + :param str subscription_key: The subscription key + :param str api_key: The api key + :raises: ValueError or TypeError + """ + if not subscription_key and not api_key: + raise ValueError("Pass at least one non-empty key for updating.") + if subscription_key: + if not isinstance(subscription_key, six.string_types): + raise TypeError("The subscription_key used for updating must be a string.") + self._subscription_key = subscription_key + if api_key: + if not isinstance(api_key, six.string_types): + raise TypeError("The api_key used for updating must be a string.") + self._api_key = api_key diff --git a/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_api_key_only.yaml b/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_api_key_only.yaml new file mode 100644 index 000000000000..1e54b8487286 --- /dev/null +++ b/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_api_key_only.yaml @@ -0,0 +1,112 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-metricsadvisor/1.0.0b3 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://js-metrics-advisor.cognitiveservices.azure.com/metricsadvisor/v1.0/feedback/metric/feedback_id + response: + body: + string: '{"feedbackId":"feedback_id","createdTime":"2020-10-21T20:53:05.91Z","userPrincipal":"krpratic@microsoft.com","metricId":"metric_id","dimensionFilter":{"dimension":{"dimension_name":"Mumbai","category":"Shoes + Handbags & Sunglasses"}},"feedbackType":"Anomaly","startTime":"2020-10-01T00:00:00Z","endTime":"2020-10-01T00:00:00Z","value":{"anomalyValue":"NotAnomaly"},"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","anomalyDetectionConfigurationSnapshot":{"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","name":"detection-config","description":"","metricId":"metric_id","wholeMetricConfiguration":{"smartDetectionCondition":{"sensitivity":100.0,"anomalyDetectorDirection":"Both","suppressCondition":{"minNumber":1,"minRatio":100.0}}},"dimensionGroupOverrideConfigurations":[],"seriesOverrideConfigurations":[]}}' + headers: + apim-request-id: + - ff207a4a-c67b-4009-b4de-3f32d0e144ed + content-length: + - '927' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 21 Jan 2021 00:19:44 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '202' + x-request-id: + - ff207a4a-c67b-4009-b4de-3f32d0e144ed + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-metricsadvisor/1.0.0b3 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://js-metrics-advisor.cognitiveservices.azure.com/metricsadvisor/v1.0/feedback/metric/feedback_id + response: + body: + string: '{"code":"Unauthorized","message":"Access denied due to invalid API + key or wrong API endpoint. Make sure to provide a valid key and use a correct + regional API endpoint."}' + headers: + apim-request-id: + - 722f2e0a-c248-4a9b-8e14-0575b51d0010 + content-type: + - application/json; charset=utf-8 + date: + - Thu, 21 Jan 2021 00:19:44 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '86' + status: + code: 400 + message: Bad Request +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-metricsadvisor/1.0.0b3 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://js-metrics-advisor.cognitiveservices.azure.com/metricsadvisor/v1.0/feedback/metric/feedback_id + response: + body: + string: '{"feedbackId":"feedback_id","createdTime":"2020-10-21T20:53:05.91Z","userPrincipal":"krpratic@microsoft.com","metricId":"metric_id","dimensionFilter":{"dimension":{"dimension_name":"Mumbai","category":"Shoes + Handbags & Sunglasses"}},"feedbackType":"Anomaly","startTime":"2020-10-01T00:00:00Z","endTime":"2020-10-01T00:00:00Z","value":{"anomalyValue":"NotAnomaly"},"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","anomalyDetectionConfigurationSnapshot":{"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","name":"detection-config","description":"","metricId":"metric_id","wholeMetricConfiguration":{"smartDetectionCondition":{"sensitivity":100.0,"anomalyDetectorDirection":"Both","suppressCondition":{"minNumber":1,"minRatio":100.0}}},"dimensionGroupOverrideConfigurations":[],"seriesOverrideConfigurations":[]}}' + headers: + apim-request-id: + - 2107621b-7bb9-403a-b461-a199eb738921 + content-length: + - '927' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 21 Jan 2021 00:19:45 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '230' + x-request-id: + - 2107621b-7bb9-403a-b461-a199eb738921 + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_both_keys.yaml b/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_both_keys.yaml new file mode 100644 index 000000000000..0f5487b5f4ce --- /dev/null +++ b/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_both_keys.yaml @@ -0,0 +1,102 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-metricsadvisor/1.0.0b3 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://js-metrics-advisor.cognitiveservices.azure.com/metricsadvisor/v1.0/feedback/metric/feedback_id + response: + body: + string: '{"feedbackId":"feedback_id","createdTime":"2020-10-21T20:53:05.91Z","userPrincipal":"krpratic@microsoft.com","metricId":"metric_id","dimensionFilter":{"dimension":{"dimension_name":"Mumbai","category":"Shoes + Handbags & Sunglasses"}},"feedbackType":"Anomaly","startTime":"2020-10-01T00:00:00Z","endTime":"2020-10-01T00:00:00Z","value":{"anomalyValue":"NotAnomaly"},"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","anomalyDetectionConfigurationSnapshot":{"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","name":"detection-config","description":"","metricId":"metric_id","wholeMetricConfiguration":{"smartDetectionCondition":{"sensitivity":100.0,"anomalyDetectorDirection":"Both","suppressCondition":{"minNumber":1,"minRatio":100.0}}},"dimensionGroupOverrideConfigurations":[],"seriesOverrideConfigurations":[]}}' + headers: + apim-request-id: + - bd25f508-276f-4a03-9c4f-9c01ca86cb09 + content-length: + - '927' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 21 Jan 2021 00:19:45 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '105' + x-request-id: + - bd25f508-276f-4a03-9c4f-9c01ca86cb09 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-metricsadvisor/1.0.0b3 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://js-metrics-advisor.cognitiveservices.azure.com/metricsadvisor/v1.0/feedback/metric/feedback_id + response: + body: + string: '{"error":{"code":"401","message":"Access denied due to invalid subscription + key or wrong API endpoint. Make sure to provide a valid key for an active + subscription and use a correct regional API endpoint for your resource."}}' + headers: + content-length: + - '224' + date: + - Thu, 21 Jan 2021 00:19:45 GMT + status: + code: 401 + message: PermissionDenied +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-metricsadvisor/1.0.0b3 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://js-metrics-advisor.cognitiveservices.azure.com/metricsadvisor/v1.0/feedback/metric/feedback_id + response: + body: + string: '{"feedbackId":"feedback_id","createdTime":"2020-10-21T20:53:05.91Z","userPrincipal":"krpratic@microsoft.com","metricId":"metric_id","dimensionFilter":{"dimension":{"dimension_name":"Mumbai","category":"Shoes + Handbags & Sunglasses"}},"feedbackType":"Anomaly","startTime":"2020-10-01T00:00:00Z","endTime":"2020-10-01T00:00:00Z","value":{"anomalyValue":"NotAnomaly"},"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","anomalyDetectionConfigurationSnapshot":{"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","name":"detection-config","description":"","metricId":"metric_id","wholeMetricConfiguration":{"smartDetectionCondition":{"sensitivity":100.0,"anomalyDetectorDirection":"Both","suppressCondition":{"minNumber":1,"minRatio":100.0}}},"dimensionGroupOverrideConfigurations":[],"seriesOverrideConfigurations":[]}}' + headers: + apim-request-id: + - 0e520a36-d235-4784-b83c-c7fc43a1a47c + content-length: + - '927' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 21 Jan 2021 00:19:46 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '106' + x-request-id: + - 0e520a36-d235-4784-b83c-c7fc43a1a47c + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_sub_key_only.yaml b/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_sub_key_only.yaml new file mode 100644 index 000000000000..44aa6bfe8a07 --- /dev/null +++ b/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/recordings/test_credential.test_credential_rotate_sub_key_only.yaml @@ -0,0 +1,102 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-metricsadvisor/1.0.0b3 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://js-metrics-advisor.cognitiveservices.azure.com/metricsadvisor/v1.0/feedback/metric/feedback_id + response: + body: + string: '{"feedbackId":"feedback_id","createdTime":"2020-10-21T20:53:05.91Z","userPrincipal":"krpratic@microsoft.com","metricId":"metric_id","dimensionFilter":{"dimension":{"dimension_name":"Mumbai","category":"Shoes + Handbags & Sunglasses"}},"feedbackType":"Anomaly","startTime":"2020-10-01T00:00:00Z","endTime":"2020-10-01T00:00:00Z","value":{"anomalyValue":"NotAnomaly"},"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","anomalyDetectionConfigurationSnapshot":{"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","name":"detection-config","description":"","metricId":"metric_id","wholeMetricConfiguration":{"smartDetectionCondition":{"sensitivity":100.0,"anomalyDetectorDirection":"Both","suppressCondition":{"minNumber":1,"minRatio":100.0}}},"dimensionGroupOverrideConfigurations":[],"seriesOverrideConfigurations":[]}}' + headers: + apim-request-id: + - 1b8c4f41-01bc-49dc-a6fb-a52a9d5b5e38 + content-length: + - '927' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 21 Jan 2021 00:19:46 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '100' + x-request-id: + - 1b8c4f41-01bc-49dc-a6fb-a52a9d5b5e38 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-metricsadvisor/1.0.0b3 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://js-metrics-advisor.cognitiveservices.azure.com/metricsadvisor/v1.0/feedback/metric/feedback_id + response: + body: + string: '{"error":{"code":"401","message":"Access denied due to invalid subscription + key or wrong API endpoint. Make sure to provide a valid key for an active + subscription and use a correct regional API endpoint for your resource."}}' + headers: + content-length: + - '224' + date: + - Thu, 21 Jan 2021 00:19:46 GMT + status: + code: 401 + message: PermissionDenied +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-ai-metricsadvisor/1.0.0b3 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://js-metrics-advisor.cognitiveservices.azure.com/metricsadvisor/v1.0/feedback/metric/feedback_id + response: + body: + string: '{"feedbackId":"feedback_id","createdTime":"2020-10-21T20:53:05.91Z","userPrincipal":"krpratic@microsoft.com","metricId":"metric_id","dimensionFilter":{"dimension":{"dimension_name":"Mumbai","category":"Shoes + Handbags & Sunglasses"}},"feedbackType":"Anomaly","startTime":"2020-10-01T00:00:00Z","endTime":"2020-10-01T00:00:00Z","value":{"anomalyValue":"NotAnomaly"},"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","anomalyDetectionConfigurationSnapshot":{"anomalyDetectionConfigurationId":"e17f32d4-3ddf-4dc7-84ee-b4130c7e1777","name":"detection-config","description":"","metricId":"metric_id","wholeMetricConfiguration":{"smartDetectionCondition":{"sensitivity":100.0,"anomalyDetectorDirection":"Both","suppressCondition":{"minNumber":1,"minRatio":100.0}}},"dimensionGroupOverrideConfigurations":[],"seriesOverrideConfigurations":[]}}' + headers: + apim-request-id: + - 5bbd22d2-10b9-4d29-ac53-47bb62ff2588 + content-length: + - '927' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 21 Jan 2021 00:19:46 GMT + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-envoy-upstream-service-time: + - '116' + x-request-id: + - 5bbd22d2-10b9-4d29-ac53-47bb62ff2588 + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/test_credential.py b/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/test_credential.py new file mode 100644 index 000000000000..bd728606fcf8 --- /dev/null +++ b/sdk/metricsadvisor/azure-ai-metricsadvisor/tests/test_credential.py @@ -0,0 +1,112 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +import pytest +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError +from azure.ai.metricsadvisor import MetricsAdvisorClient, MetricsAdvisorKeyCredential +from base_testcase import TestMetricsAdvisorClientBase + + +class TestMetricsAdvisorCredential(TestMetricsAdvisorClientBase): + + def __init__(self, method_name): + super(TestMetricsAdvisorCredential, self).__init__(method_name) + if self.is_live: + self.service_endpoint = self.get_settings_value("METRICS_ADVISOR_ENDPOINT") + self.subscription_key = self.get_settings_value("METRICS_ADVISOR_SUBSCRIPTION_KEY") + self.api_key = self.get_settings_value("METRICS_ADVISOR_API_KEY") + else: + self.service_endpoint = "https://endpointname.cognitiveservices.azure.com" + self.subscription_key = "METRICS_ADVISOR_SUBSCRIPTION_KEY" + self.api_key = "METRICS_ADVISOR_API_KEY" + + def test_credential_rotate_both_keys(self): + credential = MetricsAdvisorKeyCredential(self.subscription_key, self.api_key) + client = MetricsAdvisorClient(self.service_endpoint, credential) + + # make successful call + result = client.get_feedback(feedback_id=self.feedback_id) + assert result + + # rotate both keys + credential.update(subscription_key="xxx", api_key="xxx") + assert credential.subscription_key == "xxx" + assert credential.api_key == "xxx" + + # call fails + with pytest.raises(ClientAuthenticationError): + result = client.get_feedback(feedback_id=self.feedback_id) + + # rotate back to valid credentials + credential.update(subscription_key=self.subscription_key, api_key=self.api_key) + assert credential.subscription_key == self.subscription_key + assert credential.api_key == self.api_key + + # make successful call + result = client.get_feedback(feedback_id=self.feedback_id) + assert result + + def test_credential_rotate_sub_key_only(self): + credential = MetricsAdvisorKeyCredential(self.subscription_key, self.api_key) + client = MetricsAdvisorClient(self.service_endpoint, credential) + + # make successful call + result = client.get_feedback(feedback_id=self.feedback_id) + assert result + + # rotate one key + credential.update(subscription_key="xxx") + assert credential.subscription_key == "xxx" + assert credential.api_key == self.api_key + + # call fails + with pytest.raises(ClientAuthenticationError): + result = client.get_feedback(feedback_id=self.feedback_id) + + # rotate back to valid credentials + credential.update(subscription_key=self.subscription_key) + assert credential.subscription_key == self.subscription_key + assert credential.api_key == self.api_key + + # make successful call + result = client.get_feedback(feedback_id=self.feedback_id) + assert result + + def test_credential_rotate_api_key_only(self): + credential = MetricsAdvisorKeyCredential(self.subscription_key, self.api_key) + client = MetricsAdvisorClient(self.service_endpoint, credential) + + # make successful call + result = client.get_feedback(feedback_id=self.feedback_id) + assert result + + # rotate one key + credential.update(api_key="xxx") + assert credential.subscription_key == self.subscription_key + assert credential.api_key == "xxx" + + # call fails + with pytest.raises(HttpResponseError): + result = client.get_feedback(feedback_id=self.feedback_id) + + # rotate back to valid credentials + credential.update(api_key=self.api_key) + assert credential.subscription_key == self.subscription_key + assert credential.api_key == self.api_key + + # make successful call + result = client.get_feedback(feedback_id=self.feedback_id) + assert result + + def test_credential_bad_input(self): + credential = MetricsAdvisorKeyCredential(self.subscription_key, self.api_key) + + with pytest.raises(ValueError): + credential.update() + with pytest.raises(TypeError): + credential.update(subscription_key=34) + with pytest.raises(TypeError): + credential.update(api_key=34)