From c39f733bc7bd3d5213e44e34fd9dcdb4f99e8e9f Mon Sep 17 00:00:00 2001 From: Yang Yuan Date: Fri, 17 Aug 2018 13:41:49 -0700 Subject: [PATCH] [Cognitive Services] Support ApiProperties which enable creating QnAMaker (#7034) --- azure-cli2017.pyproj | 1 + .../azure-cli-cognitiveservices/HISTORY.rst | 4 + .../cognitiveservices/_params.py | 56 ++++++ .../cognitiveservices/custom.py | 8 +- ...gnitiveservices_create_api_properties.yaml | 185 ++++++++++++++++++ .../tests/latest/test_api_properties.py | 51 +++++ .../azure-cli-cognitiveservices/setup.py | 2 +- 7 files changed, 304 insertions(+), 3 deletions(-) create mode 100644 src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/tests/latest/recordings/test_cognitiveservices_create_api_properties.yaml create mode 100644 src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/tests/latest/test_api_properties.py diff --git a/azure-cli2017.pyproj b/azure-cli2017.pyproj index cd5c5e541d6..51f41905122 100644 --- a/azure-cli2017.pyproj +++ b/azure-cli2017.pyproj @@ -241,6 +241,7 @@ + diff --git a/src/command_modules/azure-cli-cognitiveservices/HISTORY.rst b/src/command_modules/azure-cli-cognitiveservices/HISTORY.rst index 914193a68f0..0d300273205 100644 --- a/src/command_modules/azure-cli-cognitiveservices/HISTORY.rst +++ b/src/command_modules/azure-cli-cognitiveservices/HISTORY.rst @@ -3,6 +3,10 @@ Release History =============== +0.2.1 ++++++ +* Add new parameter --api-properties, which is required for creating some of the services. + 0.2.0 +++++ * BREAKING CHANGE: 'show' commands log error message and fail with exit code of 3 upon a missing resource. diff --git a/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/_params.py b/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/_params.py index 7a076a4eb62..42bad9a991e 100644 --- a/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/_params.py +++ b/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/_params.py @@ -3,17 +3,72 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +import re from knack.arguments import CLIArgumentType +from knack.log import get_logger from azure.cli.core.commands.parameters import ( tags_type, resource_group_name_type, get_resource_name_completion_list) +from azure.cli.core.util import (shell_safe_json_parse, CLIError) +from azure.cli.core.commands.validators import validate_tag +logger = get_logger(__name__) name_arg_type = CLIArgumentType(options_list=['--name', '-n'], metavar='NAME') +def extract_key_values_pairs(api_properties): + api_properties_dict = {} + for item in api_properties: + api_properties_dict.update(validate_tag(item)) + return api_properties_dict + + +def validate_api_properties(ns): + """ Extracts JSON format or 'a=b c=d' format as api properties """ + api_properties = ns.api_properties + + if api_properties is None: + return + + if len(api_properties) > 1: + ns.api_properties = extract_key_values_pairs(api_properties) + else: + string = api_properties[0] + try: + ns.api_properties = shell_safe_json_parse(string) + return + except CLIError: + result = extract_key_values_pairs([string]) + if _is_suspected_json(string): + logger.warning('Api properties looks like a JSON format but not valid, interpreted as key=value pairs:' + ' %s', str(result)) + ns.api_properties = result + return + + +def _is_suspected_json(string): + """ If the string looks like a JSON """ + if string.startswith('{') or string.startswith('\'{') or string.startswith('\"{'): + return True + if string.startswith('[') or string.startswith('\'[') or string.startswith('\"['): + return True + if re.match(r"^['\"\s]*{.+}|\[.+\]['\"\s]*$", string): + return True + + return False + + +api_properties_type = CLIArgumentType( + validator=validate_api_properties, + help="Api properties in JSON format or a=b c=d format. Some cognitive services (i.e. QnA Maker) " + "require extra api properties to create the account.", + nargs='*' +) + + def load_arguments(self, _): with self.argument_context('cognitiveservices') as c: c.argument('account_name', arg_type=name_arg_type, help='cognitive service account name', @@ -23,6 +78,7 @@ def load_arguments(self, _): c.argument('kind', help='the API name of cognitive services account') c.argument('tags', tags_type) c.argument('key_name', required=True, help='Key name to generate', choices=['key1', 'key2']) + c.argument('api_properties', api_properties_type) with self.argument_context('cognitiveservices account create') as c: c.argument('yes', action='store_true', help='Do not prompt for terms confirmation') diff --git a/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/custom.py b/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/custom.py index 7c579f3f791..80180144372 100644 --- a/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/custom.py +++ b/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/custom.py @@ -19,7 +19,7 @@ def list_resources(client, resource_group_name=None): def create( - client, resource_group_name, account_name, sku_name, kind, location, tags=None, yes=None): + client, resource_group_name, account_name, sku_name, kind, location, tags=None, api_properties=None, yes=None): terms = 'Notice\nMicrosoft will use data you send to Bing Search Services'\ ' or the Translator Speech API to improve Microsoft products and services.'\ @@ -43,7 +43,11 @@ def create( if not option: raise CLIError('Operation cancelled.') sku = Sku(sku_name) - properties = {} + + if api_properties is None: + properties = {} + else: + properties = {"apiProperties": api_properties} params = CognitiveServicesAccountCreateParameters(sku, kind, location, properties, tags) return client.create(resource_group_name, account_name, params) diff --git a/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/tests/latest/recordings/test_cognitiveservices_create_api_properties.yaml b/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/tests/latest/recordings/test_cognitiveservices_create_api_properties.yaml new file mode 100644 index 00000000000..84a90951739 --- /dev/null +++ b/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/tests/latest/recordings/test_cognitiveservices_create_api_properties.yaml @@ -0,0 +1,185 @@ +interactions: +- request: + body: '{"location": "westus", "tags": {"product": "azurecli", "cause": "automation", + "date": "2018-08-13T20:59:32Z"}}' + headers: + Accept: [application/json] + Accept-Encoding: ['gzip, deflate'] + CommandName: [group create] + Connection: [keep-alive] + Content-Length: ['110'] + Content-Type: [application/json; charset=utf-8] + User-Agent: [python/3.6.5 (Windows-10-10.0.17134-SP0) requests/2.19.1 msrest/0.5.1 + msrest_azure/0.4.34 resourcemanagementclient/2.0.0 Azure-SDK-For-Python + AZURECLI/2.0.44] + accept-language: [en-US] + method: PUT + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/clitest.rg000001?api-version=2018-05-01 + response: + body: {string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001","name":"clitest.rg000001","location":"westus","tags":{"product":"azurecli","cause":"automation","date":"2018-08-13T20:59:32Z"},"properties":{"provisioningState":"Succeeded"}}'} + headers: + cache-control: [no-cache] + content-length: ['384'] + content-type: [application/json; charset=utf-8] + date: ['Mon, 13 Aug 2018 20:59:34 GMT'] + expires: ['-1'] + pragma: [no-cache] + strict-transport-security: [max-age=31536000; includeSubDomains] + x-content-type-options: [nosniff] + x-ms-ratelimit-remaining-subscription-writes: ['1199'] + status: {code: 201, message: Created} +- request: + body: '{"sku": {"name": "S0"}, "kind": "QnAMaker", "location": "westus", "properties": + {"apiProperties": {"qnaRuntimeEndpoint": "https://cs-cli-test-qnamaker.azurewebsites.net"}}}' + headers: + Accept: [application/json] + Accept-Encoding: ['gzip, deflate'] + CommandName: [cognitiveservices account create] + Connection: [keep-alive] + Content-Length: ['172'] + Content-Type: [application/json; charset=utf-8] + User-Agent: [python/3.6.5 (Windows-10-10.0.17134-SP0) requests/2.19.1 msrest/0.5.1 + msrest_azure/0.4.34 cognitiveservicesmanagementclient/1.0.0 Azure-SDK-For-Python + AZURECLI/2.0.44] + accept-language: [en-US] + method: PUT + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.CognitiveServices/accounts/cs_cli_test_000002?api-version=2017-04-18 + response: + body: {string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.CognitiveServices/accounts/cs_cli_test_000002","name":"cs_cli_test_000002","type":"Microsoft.CognitiveServices/accounts","etag":"\"6900ddcd-0000-0000-0000-5b71f1370000\"","location":"westus","sku":{"name":"S0"},"kind":"QnAMaker","properties":{"endpoint":"https://westus.api.cognitive.microsoft.com/qnamaker/v4.0","internalId":"5b7a3f0503a14481bffdd72294e77ff1","dateCreated":"2018-08-13T20:59:35.8473162Z","apiProperties":{"qnaRuntimeEndpoint":"https://cs-cli-test-qnamaker.azurewebsites.net"},"provisioningState":"Succeeded"}}'} + headers: + cache-control: [no-cache] + content-length: ['693'] + content-type: [application/json; charset=utf-8] + date: ['Mon, 13 Aug 2018 20:59:35 GMT'] + etag: ['"6900ddcd-0000-0000-0000-5b71f1370000"'] + expires: ['-1'] + pragma: [no-cache] + server: [Microsoft-IIS/10.0] + strict-transport-security: [max-age=31536000; includeSubDomains] + x-aspnet-version: [4.0.30319] + x-content-type-options: [nosniff] + x-ms-ratelimit-remaining-subscription-writes: ['1199'] + x-powered-by: [ASP.NET] + status: {code: 201, message: Created} +- request: + body: null + headers: + Accept: [application/json] + Accept-Encoding: ['gzip, deflate'] + CommandName: [cognitiveservices account delete] + Connection: [keep-alive] + Content-Length: ['0'] + Content-Type: [application/json; charset=utf-8] + User-Agent: [python/3.6.5 (Windows-10-10.0.17134-SP0) requests/2.19.1 msrest/0.5.1 + msrest_azure/0.4.34 cognitiveservicesmanagementclient/1.0.0 Azure-SDK-For-Python + AZURECLI/2.0.44] + accept-language: [en-US] + method: DELETE + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.CognitiveServices/accounts/cs_cli_test_000002?api-version=2017-04-18 + response: + body: {string: ''} + headers: + cache-control: [no-cache] + content-length: ['0'] + date: ['Mon, 13 Aug 2018 20:59:37 GMT'] + expires: ['-1'] + pragma: [no-cache] + server: [Microsoft-IIS/10.0] + strict-transport-security: [max-age=31536000; includeSubDomains] + x-aspnet-version: [4.0.30319] + x-content-type-options: [nosniff] + x-ms-ratelimit-remaining-subscription-deletes: ['14999'] + x-powered-by: [ASP.NET] + status: {code: 200, message: OK} +- request: + body: '{"sku": {"name": "S0"}, "kind": "QnAMaker", "location": "westus", "properties": + {"apiProperties": {"qnaRuntimeEndpoint": "https://cs-cli-test-qnamaker.azurewebsites.net"}}}' + headers: + Accept: [application/json] + Accept-Encoding: ['gzip, deflate'] + CommandName: [cognitiveservices account create] + Connection: [keep-alive] + Content-Length: ['172'] + Content-Type: [application/json; charset=utf-8] + User-Agent: [python/3.6.5 (Windows-10-10.0.17134-SP0) requests/2.19.1 msrest/0.5.1 + msrest_azure/0.4.34 cognitiveservicesmanagementclient/1.0.0 Azure-SDK-For-Python + AZURECLI/2.0.44] + accept-language: [en-US] + method: PUT + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.CognitiveServices/accounts/cs_cli_test_000002?api-version=2017-04-18 + response: + body: {string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.CognitiveServices/accounts/cs_cli_test_000002","name":"cs_cli_test_000002","type":"Microsoft.CognitiveServices/accounts","etag":"\"6900e1cd-0000-0000-0000-5b71f13a0000\"","location":"westus","sku":{"name":"S0"},"kind":"QnAMaker","properties":{"endpoint":"https://westus.api.cognitive.microsoft.com/qnamaker/v4.0","internalId":"8c302dd2c300473297d139048b8ed560","dateCreated":"2018-08-13T20:59:38.1208213Z","apiProperties":{"qnaRuntimeEndpoint":"https://cs-cli-test-qnamaker.azurewebsites.net"},"provisioningState":"Succeeded"}}'} + headers: + cache-control: [no-cache] + content-length: ['693'] + content-type: [application/json; charset=utf-8] + date: ['Mon, 13 Aug 2018 20:59:37 GMT'] + etag: ['"6900e1cd-0000-0000-0000-5b71f13a0000"'] + expires: ['-1'] + pragma: [no-cache] + server: [Microsoft-IIS/10.0] + strict-transport-security: [max-age=31536000; includeSubDomains] + x-aspnet-version: [4.0.30319] + x-content-type-options: [nosniff] + x-ms-ratelimit-remaining-subscription-writes: ['1199'] + x-powered-by: [ASP.NET] + status: {code: 201, message: Created} +- request: + body: null + headers: + Accept: [application/json] + Accept-Encoding: ['gzip, deflate'] + CommandName: [cognitiveservices account delete] + Connection: [keep-alive] + Content-Length: ['0'] + Content-Type: [application/json; charset=utf-8] + User-Agent: [python/3.6.5 (Windows-10-10.0.17134-SP0) requests/2.19.1 msrest/0.5.1 + msrest_azure/0.4.34 cognitiveservicesmanagementclient/1.0.0 Azure-SDK-For-Python + AZURECLI/2.0.44] + accept-language: [en-US] + method: DELETE + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.CognitiveServices/accounts/cs_cli_test_000002?api-version=2017-04-18 + response: + body: {string: ''} + headers: + cache-control: [no-cache] + content-length: ['0'] + date: ['Mon, 13 Aug 2018 20:59:38 GMT'] + expires: ['-1'] + pragma: [no-cache] + server: [Microsoft-IIS/10.0] + strict-transport-security: [max-age=31536000; includeSubDomains] + x-aspnet-version: [4.0.30319] + x-content-type-options: [nosniff] + x-ms-ratelimit-remaining-subscription-deletes: ['14999'] + x-powered-by: [ASP.NET] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: [application/json] + Accept-Encoding: ['gzip, deflate'] + CommandName: [group delete] + Connection: [keep-alive] + Content-Length: ['0'] + Content-Type: [application/json; charset=utf-8] + User-Agent: [python/3.6.5 (Windows-10-10.0.17134-SP0) requests/2.19.1 msrest/0.5.1 + msrest_azure/0.4.34 resourcemanagementclient/2.0.0 Azure-SDK-For-Python + AZURECLI/2.0.44] + accept-language: [en-US] + method: DELETE + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/clitest.rg000001?api-version=2018-05-01 + response: + body: {string: ''} + headers: + cache-control: [no-cache] + content-length: ['0'] + date: ['Mon, 13 Aug 2018 20:59:39 GMT'] + expires: ['-1'] + location: ['https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/operationresults/eyJqb2JJZCI6IlJFU09VUkNFR1JPVVBERUxFVElPTkpPQi1DTElURVNUOjJFUkdLWUtQN1E3M1pBVzVRQ0lYUjZZNFpBQ1haUVNGQVJMT1RVMnwwQjQxMUJEQUMwOEY4OEZELVdFU1RVUyIsImpvYkxvY2F0aW9uIjoid2VzdHVzIn0?api-version=2018-05-01'] + pragma: [no-cache] + strict-transport-security: [max-age=31536000; includeSubDomains] + x-content-type-options: [nosniff] + x-ms-ratelimit-remaining-subscription-deletes: ['14999'] + status: {code: 202, message: Accepted} +version: 1 diff --git a/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/tests/latest/test_api_properties.py b/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/tests/latest/test_api_properties.py new file mode 100644 index 00000000000..20f5b106bbc --- /dev/null +++ b/src/command_modules/azure-cli-cognitiveservices/azure/cli/command_modules/cognitiveservices/tests/latest/test_api_properties.py @@ -0,0 +1,51 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import unittest + +from azure.cli.testsdk import ScenarioTest, ResourceGroupPreparer + + +class CognitiveServicesApiPropertiesTests(ScenarioTest): + @ResourceGroupPreparer() + def test_cognitiveservices_create_api_properties(self, resource_group): + sname = self.create_random_name(prefix='cs_cli_test_', length=16) + + self.kwargs.update({ + 'sname': sname, + 'kind': 'QnAMaker', + 'sku': 'S0', + 'location': 'westus', + 'apiProperties': 'qnaRuntimeEndpoint=https://cs-cli-test-qnamaker.azurewebsites.net', + 'apiPropertiesJson': '{\\\"qnaRuntimeEndpoint\\\":\\\"https://cs-cli-test-qnamaker.azurewebsites.net\\\"}', + }) + + # test to create cognitive services account + self.cmd('az cognitiveservices account create -n {sname} -g {rg} --kind {kind} --sku {sku} -l {location} ' + '--api-properties {apiProperties} --yes', + checks=[self.check('name', '{sname}'), + self.check('location', '{location}'), + self.check('sku.name', '{sku}'), + self.check('provisioningState', 'Succeeded')]) + + # delete the cognitive services account + ret = self.cmd('az cognitiveservices account delete -n {sname} -g {rg}') + self.assertEqual(ret.exit_code, 0) + + # test to create cognitive services account + self.cmd('az cognitiveservices account create -n {sname} -g {rg} --kind {kind} --sku {sku} -l {location} ' + '--api-properties {apiPropertiesJson} --yes', + checks=[self.check('name', '{sname}'), + self.check('location', '{location}'), + self.check('sku.name', '{sku}'), + self.check('provisioningState', 'Succeeded')]) + + # delete the cognitive services account + ret = self.cmd('az cognitiveservices account delete -n {sname} -g {rg}') + self.assertEqual(ret.exit_code, 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/command_modules/azure-cli-cognitiveservices/setup.py b/src/command_modules/azure-cli-cognitiveservices/setup.py index da85266962e..68f83442a73 100644 --- a/src/command_modules/azure-cli-cognitiveservices/setup.py +++ b/src/command_modules/azure-cli-cognitiveservices/setup.py @@ -13,7 +13,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") cmdclass = {} -VERSION = "0.2.0" +VERSION = "0.2.1" # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers CLASSIFIERS = [