diff --git a/src/communication/azext_communication/generated/_client_factory.py b/src/communication/azext_communication/generated/_client_factory.py index 98dce81dedb..eeb293bc0f8 100644 --- a/src/communication/azext_communication/generated/_client_factory.py +++ b/src/communication/azext_communication/generated/_client_factory.py @@ -18,3 +18,23 @@ def cf_communication_cl(cli_ctx, *_): def cf_communication_service(cli_ctx, *_): return cf_communication_cl(cli_ctx).communication_service + + +def cf_communication_sms(cli_ctx, *_): + from azure.cli.core.commands.client_factory import get_mgmt_service_client + from azext_communication.vendored_sdks.sms import SmsClient + return get_mgmt_service_client(cli_ctx, SmsClient) + + +def cf_communication_identity(cli_ctx, *_): + + from azure.cli.core.commands.client_factory import get_mgmt_service_client + from azext_communication.vendored_sdks.identity import CommunicationIdentityClient + return get_mgmt_service_client(cli_ctx, CommunicationIdentityClient) + + +def cf_communication_phonenumbers(cli_ctx, *_): + + from azure.cli.core.commands.client_factory import get_mgmt_service_client + from azext_communication.vendored_sdks.phonenumbers import PhoneNumbersClient + return get_mgmt_service_client(cli_ctx, PhoneNumbersClient) diff --git a/src/communication/azext_communication/generated/_help.py b/src/communication/azext_communication/generated/_help.py index 2952f316776..5e7a61d6805 100644 --- a/src/communication/azext_communication/generated/_help.py +++ b/src/communication/azext_communication/generated/_help.py @@ -111,3 +111,49 @@ text: |- az communication wait --name "MyCommunicationResource" --resource-group "MyResourceGroup" --deleted """ + +helps['communication send-sms'] = """ + type: command + short-summary: "Sends SMS to phone numbers." + examples: + - name: send sms + text: |- + az communication send-sms --connection-string \ +"Endpoint=sb://MyNamespace.servicebus.windows.net/;SharedAccessKey=abcd1234" \ +--sender "+155555555555" --recipient "+155555555666" --message "This is an SMS" +""" + + +helps['communication create-useraccesstoken'] = """ + type: command + short-summary: "Creates user access token" + examples: + - name: create user access token + text: |- + az communication create-useraccesstoken --connection-string \ +"Endpoint=sb://MyNamespace.servicebus.windows.net/;SharedAccessKey=abcd1234" \ +--scope "Scope for access token. Ex: voip" +""" + + +helps['communication list-phonenumbers'] = """ + type: command + short-summary: "Lists communication phone numbers" + examples: + - name: list phone numbers + text: |- + az communication list-phonenumbers --connection-string \ +"Endpoint=sb://MyNamespace.servicebus.windows.net/;SharedAccessKey=abcd1234" +""" + + +helps['communication show-phonenumber-info'] = """ + type: command + short-summary: "Shows communication phonenumber information" + examples: + - name: show phonenumber info + text: |- + az communication show-phonenumber-info --connection-string \ +"Endpoint=sb://MyNamespace.servicebus.windows.net/;SharedAccessKey=abcd1234" \ +--phone-number "+155555555555" +""" diff --git a/src/communication/azext_communication/generated/_params.py b/src/communication/azext_communication/generated/_params.py index 92d78176074..3f492c1e55d 100644 --- a/src/communication/azext_communication/generated/_params.py +++ b/src/communication/azext_communication/generated/_params.py @@ -17,10 +17,16 @@ get_location_type ) from azure.cli.core.commands.validators import get_default_location_from_resource_group +from knack.arguments import CLIArgumentType def load_arguments(self, _): + country_code_type = CLIArgumentType( + options_list='--country-code-name', + help='Name of the Communicationphonenumbers.', + id_part='name') + with self.argument_context('communication list') as c: c.argument('resource_group_name', resource_group_name_type) @@ -77,3 +83,19 @@ def load_arguments(self, _): c.argument('resource_group_name', resource_group_name_type) c.argument('name', options_list=['--name', '-n'], type=str, help='The name of the CommunicationService ' 'resource.', id_part='name') + + with self.argument_context('communication send-sms') as c: + c.argument('connection-string', type=str, help='connection string') + c.argument('sender', type=str, help='The sender of the SMS') + c.argument('recipient', type=str, help='The recipient of the SMS') + c.argument('message', type=str, help='The message in the SMS') + + with self.argument_context('communication create-useraccesstoken') as c: + c.argument('connection-string', type=str, help = 'connection string') + c.argument('scope', type=str, help = 'scope for an access token chat/voip') + + with self.argument_context('communication list-phonenumbers') as c: + c.argument('country_code', country_code_type, id_part=None) + + with self.argument_context('communication show-phonenumber-info') as c: + c.argument('country_code', country_code_type, id_part=None) diff --git a/src/communication/azext_communication/generated/commands.py b/src/communication/azext_communication/generated/commands.py index 97020e860f9..f539990871d 100644 --- a/src/communication/azext_communication/generated/commands.py +++ b/src/communication/azext_communication/generated/commands.py @@ -31,3 +31,43 @@ def load_command_table(self, _): g.custom_command('list-key', 'communication_list_key') g.custom_command('regenerate-key', 'communication_regenerate_key') g.custom_wait_command('wait', 'communication_show') + + + from azext_communication.generated._client_factory import cf_communication_sms + communication_sms_sdk = CliCommandType( + operations_tmpl='azext_communication.vendored_sdks.sms._generated.operations._sms_operations#SmsOperations.{}', + client_factory=cf_communication_sms) + + with self.command_group( + 'communication', + communication_sms_sdk, + client_factory=cf_communication_sms, + is_preview=True) as g: + g.custom_command('send-sms', 'communication_send_sms') + + + from azext_communication.generated._client_factory import cf_communication_identity + communication_identity_sdk = CliCommandType( + operations_tmpl='azext_communication.vendored_sdks.identity._generated.operations.' + '_communication_identity_operations#CommunicationIdentityOperations.{}', + client_factory=cf_communication_identity) + + with self.command_group( + 'communication', + communication_identity_sdk, + client_factory=cf_communication_identity) as g: + g.custom_command('create-useraccesstoken', 'communication_create_useraccesstoken') + + + from azext_communication.generated._client_factory import cf_communication_phonenumbers + communication_phonenumbers_sdk = CliCommandType( + operations_tmpl='azext_communication.vendored_sdks.phonenumbers._generated.operations' + '#PhoneNumbersOperations.{}', + client_factory=cf_communication_phonenumbers) + + with self.command_group( + 'communication', + communication_phonenumbers_sdk, + client_factory=cf_communication_phonenumbers) as g: + g.custom_command('list-phonenumbers', 'communication_list_phonenumbers') + g.custom_show_command('show-phonenumber-info', 'communication_show_phonenumber_info') diff --git a/src/communication/azext_communication/generated/custom.py b/src/communication/azext_communication/generated/custom.py index ceac075124e..ff7ab2863dc 100644 --- a/src/communication/azext_communication/generated/custom.py +++ b/src/communication/azext_communication/generated/custom.py @@ -10,6 +10,8 @@ # pylint: disable=too-many-lines from azure.cli.core.util import sdk_no_wait +from knack.util import CLIError +from azext_communication.vendored_sdks.identity._generated.models import CommunicationTokenScope def communication_list(client, @@ -98,3 +100,31 @@ def communication_regenerate_key(client, return client.regenerate_key(resource_group_name=resource_group_name, communication_service_name=name, parameters=parameters) + + +def communication_send_sms(client, connection_string, sender, recipient, message): + sms_client=client.from_connection_string(connection_string) + return sms_client.send(from_=sender, to=recipient, message=message) + + +def communication_create_useraccesstoken(client, connection_string, scope): + communication_client = client.from_connection_string(conn_str=connection_string) + if scope == CommunicationTokenScope.CHAT: + scopes = [CommunicationTokenScope.CHAT] + elif scope == CommunicationTokenScope.VOIP: + scopes = [CommunicationTokenScope.VOIP] + else: + raise CLIError('select scope as chat/voip') + + user = communication_client.create_user() + return communication_client.get_token(user, scopes) + + +def communication_list_phonenumbers(client, connection_string): + phone_client = client.from_connection_string(connection_string) + return phone_client.list_purchased_phone_numbers() + + +def communication_show_phonenumber_info(client, connection_string, phone_number): + phone_client = client.from_connection_string(connection_string) + return phone_client.get_purchased_phone_number(phone_number) diff --git a/src/communication/azext_communication/manual/_help.py b/src/communication/azext_communication/manual/_help.py index 2952f316776..2ea2679a8a0 100644 --- a/src/communication/azext_communication/manual/_help.py +++ b/src/communication/azext_communication/manual/_help.py @@ -111,3 +111,49 @@ text: |- az communication wait --name "MyCommunicationResource" --resource-group "MyResourceGroup" --deleted """ + +helps['communication send-sms'] = """ + type: command + short-summary: "Sends SMS to phone numbers." + examples: + - name: send sms + text: |- + az communication send-sms --connection-string \ +"Endpoint=sb://MyNamespace.servicebus.windows.net/;SharedAccessKey=abcd1234" \ +--sender "+155555555555" --recipient "+155555555666" --message "This is an SMS" +""" + + +helps['communication create-useraccesstoken'] = """ + type: command + short-summary: "Creates user access token" + examples: + - name: create user access token + text: |- + az communication create-useraccesstoken --connection-string \ +"Endpoint=sb://MyNamespace.servicebus.windows.net/;SharedAccessKey=abcd1234" \ +--scope "Scope for access token. Ex: voip" +""" + + +helps['communication list-phonenumbers'] = """ + type: command + short-summary: "Lists communication phone numbers" + examples: + - name: list phone numbers + text: |- + az communication list-phonenumbers --connection-string \ +"Endpoint=sb://MyNamespace.servicebus.windows.net/;SharedAccessKey=abcd1234" +""" + + +helps['communication show-phonenumber-info'] = """ + type: command + short-summary: "Shows communication phonenumber information" + examples: + - name: show phonenumber info + text: |- + az communication show-phonenumber-info --connection-string \ +"Endpoint=sb://MyNamespace.servicebus.windows.net/;SharedAccessKey=abcd1234" \ +--phone-number "+155555555555" +""" \ No newline at end of file diff --git a/src/communication/azext_communication/vendored_sdks/identity/__init__.py b/src/communication/azext_communication/vendored_sdks/identity/__init__.py new file mode 100644 index 00000000000..19d7301c6bf --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/__init__.py @@ -0,0 +1,31 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from ._communication_identity_client import CommunicationIdentityClient + +from ._generated.models import ( + CommunicationTokenScope +) + +from ._shared.models import ( + CommunicationIdentifier, + CommunicationIdentifierKind, + CommunicationUserIdentifier, + CommunicationUserProperties +) + +__all__ = [ + 'CommunicationIdentityClient', + + # from _identity + 'CommunicationTokenScope', + + # from _shared + 'CommunicationIdentifier', + 'CommunicationIdentifierKind', + 'CommunicationUserIdentifier', + 'CommunicationUserProperties' +] diff --git a/src/communication/azext_communication/vendored_sdks/identity/_communication_identity_client.py b/src/communication/azext_communication/vendored_sdks/identity/_communication_identity_client.py new file mode 100644 index 00000000000..8b4a9638e95 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_communication_identity_client.py @@ -0,0 +1,175 @@ +# coding=utf-8 +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +from typing import TYPE_CHECKING, Any, List, Union, Tuple + +from azure.core.tracing.decorator import distributed_trace +from azure.core.credentials import AccessToken + +from ._generated._communication_identity_client\ + import CommunicationIdentityClient as CommunicationIdentityClientGen +from ._shared.utils import parse_connection_str, get_authentication_policy +from ._shared.models import CommunicationUserIdentifier +from ._version import SDK_MONIKER + +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential + from ._generated.models import CommunicationTokenScope + + +class CommunicationIdentityClient(object): + """Azure Communication Services Identity client. + + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param TokenCredential credential: + The TokenCredential we use to authenticate against the service. + + .. admonition:: Example: + + .. literalinclude:: ../samples/identity_samples.py + :language: python + :dedent: 8 + """ + def __init__( + self, + endpoint_, # type: str + credential, # type: TokenCredential + **kwargs # type: Any + ): + # type: (...) -> None + endpoint = str(endpoint_) + try: + if not endpoint.lower().startswith('http'): + endpoint = "https://" + endpoint + except AttributeError: + raise ValueError("Account URL must be a string.") + + if not credential: + raise ValueError( + "You need to provide account shared key to authenticate.") + + self._endpoint = endpoint + self._identity_service_client = CommunicationIdentityClientGen( + self._endpoint, + authentication_policy=get_authentication_policy(endpoint, credential), + sdk_moniker=SDK_MONIKER, + **kwargs) + + @classmethod + def from_connection_string( + cls, conn_str, # type: str + **kwargs # type: Any + ): # type: (...) -> CommunicationIdentityClient + """Create CommunicationIdentityClient from a Connection String. + + :param str conn_str: A connection string to an Azure Communication Service resource. + :returns: Instance of CommunicationIdentityClient. + :rtype: ~azure.communication.identity.CommunicationIdentityClient + + .. admonition:: Example: + + .. literalinclude:: ../samples/identity_samples.py + :start-after: [START auth_from_connection_string] + :end-before: [END auth_from_connection_string] + :language: python + :dedent: 8 + :caption: Creating the CommunicationIdentityClient from a connection string. + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) + + @distributed_trace + def create_user(self, **kwargs): + # type: (Any) -> CommunicationUserIdentifier + """create a single Communication user + + :return: CommunicationUserIdentifier + :rtype: ~azure.communication.identity.CommunicationUserIdentifier + """ + return self._identity_service_client.communication_identity.create( + cls=lambda pr, u, e: CommunicationUserIdentifier(u.identity.id, raw_id=u.identity.id), + **kwargs) + + @distributed_trace + def create_user_and_token( + self, + scopes, # type: List[Union[str, CommunicationTokenScope]] + **kwargs # type: Any + ): + # type: (...) -> Tuple[CommunicationUserIdentifier, AccessToken] + """Create a single Communication user with an identity token. + + :param scopes: List of scopes to be added to the token. + :type scopes: list[str or ~azure.communication.identity.CommunicationTokenScope] + :return: A tuple of a CommunicationUserIdentifier and a AccessToken. + :rtype: + tuple of (~azure.communication.identity.CommunicationUserIdentifier, ~azure.core.credentials.AccessToken) + """ + return self._identity_service_client.communication_identity.create( + cls=lambda pr, u, e: (CommunicationUserIdentifier(u.identity.id, raw_id=u.identity.id), + AccessToken(u.access_token.token, u.access_token.expires_on)), + create_token_with_scopes=scopes, + **kwargs) + + @distributed_trace + def delete_user( + self, + user, # type: CommunicationUserIdentifier + **kwargs # type: Any + ): + # type: (...) -> None + """Triggers revocation event for user and deletes all its data. + + :param user: Azure Communication User to delete + :type user: ~azure.communication.identity.CommunicationUserIdentifier + :return: None + :rtype: None + """ + self._identity_service_client.communication_identity.delete( + user.properties['id'], **kwargs) + + @distributed_trace + def get_token( + self, + user, # type: CommunicationUserIdentifier + scopes, # List[Union[str, CommunicationTokenScope]] + **kwargs # type: Any + ): + # type: (...) -> AccessToken + """Generates a new token for an identity. + + :param user: Azure Communication User + :type user: ~azure.communication.identity.CommunicationUserIdentifier + :param scopes: List of scopes to be added to the token. + :type scopes: list[str or ~azure.communication.identity.CommunicationTokenScope] + :return: AccessToken + :rtype: ~azure.core.credentials.AccessToken + """ + return self._identity_service_client.communication_identity.issue_access_token( + user.properties['id'], + scopes, + cls=lambda pr, u, e: AccessToken(u.token, u.expires_on), + **kwargs) + + @distributed_trace + def revoke_tokens( + self, + user, # type: CommunicationUserIdentifier + **kwargs # type: Any + ): + # type: (...) -> None + """Schedule revocation of all tokens of an identity. + + :param user: Azure Communication User. + :type user: ~azure.communication.identity.CommunicationUserIdentifier. + :return: None + :rtype: None + """ + return self._identity_service_client.communication_identity.revoke_access_tokens( + user.properties['id'] if user else None, + **kwargs) diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/__init__.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/__init__.py new file mode 100644 index 00000000000..733b64f83c3 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/__init__.py @@ -0,0 +1,16 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._communication_identity_client import CommunicationIdentityClient +__all__ = ['CommunicationIdentityClient'] + +try: + from ._patch import patch_sdk # type: ignore + patch_sdk() +except ImportError: + pass diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/_communication_identity_client.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/_communication_identity_client.py new file mode 100644 index 00000000000..58de4b02c4c --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/_communication_identity_client.py @@ -0,0 +1,82 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core import PipelineClient +from msrest import Deserializer, Serializer + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + from azure.core.pipeline.transport import HttpRequest, HttpResponse + +from ._configuration import CommunicationIdentityClientConfiguration +from .operations import CommunicationIdentityOperations +from . import models + + +class CommunicationIdentityClient(object): + """Azure Communication Identity Service. + + :ivar communication_identity: CommunicationIdentityOperations operations + :vartype communication_identity: azure.communication.identity.operations.CommunicationIdentityOperations + :param endpoint: The communication resource, for example https://my-resource.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint, # type: str + base_url=None, + **kwargs # type: Any + ): + # type: (...) -> None + base_url = '{endpoint}' + self._config = CommunicationIdentityClientConfiguration(endpoint, **kwargs) + self._client = PipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._serialize.client_side_validation = False + self._deserialize = Deserializer(client_models) + + self.communication_identity = CommunicationIdentityOperations( + self._client, self._config, self._serialize, self._deserialize) + + def _send_request(self, http_request, **kwargs): + # type: (HttpRequest, Any) -> HttpResponse + """Runs the network request through the client's chained policies. + + :param http_request: The network request you want to make. Required. + :type http_request: ~azure.core.pipeline.transport.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to True. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.pipeline.transport.HttpResponse + """ + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + http_request.url = self._client.format_url(http_request.url, **path_format_arguments) + stream = kwargs.pop("stream", True) + pipeline_response = self._client._pipeline.run(http_request, stream=stream, **kwargs) + return pipeline_response.http_response + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> CommunicationIdentityClient + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/_configuration.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/_configuration.py new file mode 100644 index 00000000000..68363ff6515 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/_configuration.py @@ -0,0 +1,58 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + +VERSION = "unknown" + +class CommunicationIdentityClientConfiguration(Configuration): + """Configuration for CommunicationIdentityClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example https://my-resource.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + super(CommunicationIdentityClientConfiguration, self).__init__(**kwargs) + + self.endpoint = endpoint + self.api_version = "2021-03-07" + kwargs.setdefault('sdk_moniker', 'communicationidentityclient/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs # type: Any + ): + # type: (...) -> None + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/__init__.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/__init__.py new file mode 100644 index 00000000000..198816835b2 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/__init__.py @@ -0,0 +1,10 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._communication_identity_client import CommunicationIdentityClient +__all__ = ['CommunicationIdentityClient'] diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/_communication_identity_client.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/_communication_identity_client.py new file mode 100644 index 00000000000..f7cec3b79af --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/_communication_identity_client.py @@ -0,0 +1,71 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core import AsyncPipelineClient +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest +from msrest import Deserializer, Serializer + +from ._configuration import CommunicationIdentityClientConfiguration +from .operations import CommunicationIdentityOperations +from .. import models + + +class CommunicationIdentityClient(object): + """Azure Communication Identity Service. + + :ivar communication_identity: CommunicationIdentityOperations operations + :vartype communication_identity: azure.communication.identity.aio.operations.CommunicationIdentityOperations + :param endpoint: The communication resource, for example https://my-resource.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + base_url = '{endpoint}' + self._config = CommunicationIdentityClientConfiguration(endpoint, **kwargs) + self._client = AsyncPipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._serialize.client_side_validation = False + self._deserialize = Deserializer(client_models) + + self.communication_identity = CommunicationIdentityOperations( + self._client, self._config, self._serialize, self._deserialize) + + async def _send_request(self, http_request: HttpRequest, **kwargs: Any) -> AsyncHttpResponse: + """Runs the network request through the client's chained policies. + + :param http_request: The network request you want to make. Required. + :type http_request: ~azure.core.pipeline.transport.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to True. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.pipeline.transport.AsyncHttpResponse + """ + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + http_request.url = self._client.format_url(http_request.url, **path_format_arguments) + stream = kwargs.pop("stream", True) + pipeline_response = await self._client._pipeline.run(http_request, stream=stream, **kwargs) + return pipeline_response.http_response + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "CommunicationIdentityClient": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/_configuration.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/_configuration.py new file mode 100644 index 00000000000..ffd8616506d --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/_configuration.py @@ -0,0 +1,52 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +VERSION = "unknown" + +class CommunicationIdentityClientConfiguration(Configuration): + """Configuration for CommunicationIdentityClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example https://my-resource.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + super(CommunicationIdentityClientConfiguration, self).__init__(**kwargs) + + self.endpoint = endpoint + self.api_version = "2021-03-07" + kwargs.setdefault('sdk_moniker', 'communicationidentityclient/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs: Any + ) -> None: + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/operations/__init__.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/operations/__init__.py new file mode 100644 index 00000000000..09dd0f2d066 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/operations/__init__.py @@ -0,0 +1,13 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._communication_identity_operations import CommunicationIdentityOperations + +__all__ = [ + 'CommunicationIdentityOperations', +] diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/operations/_communication_identity_operations.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/operations/_communication_identity_operations.py new file mode 100644 index 00000000000..3577e531259 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/aio/operations/_communication_identity_operations.py @@ -0,0 +1,281 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar, Union +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest + +from ... import models as _models + +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class CommunicationIdentityOperations: + """CommunicationIdentityOperations async operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.communication.identity.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer) -> None: + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + async def create( + self, + create_token_with_scopes: Optional[List[Union[str, "_models.CommunicationTokenScope"]]] = None, + **kwargs: Any + ) -> "_models.CommunicationIdentityAccessTokenResult": + """Create a new identity. + + Create a new identity. + + :param create_token_with_scopes: Also create access token for the created identity. + :type create_token_with_scopes: list[str or ~azure.communication.identity.models.CommunicationTokenScope] + :keyword callable cls: A custom type or function that will be passed the direct response + :return: CommunicationIdentityAccessTokenResult, or the result of cls(response) + :rtype: ~azure.communication.identity.models.CommunicationIdentityAccessTokenResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.CommunicationIdentityAccessTokenResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _body = _models.CommunicationIdentityCreateRequest(create_token_with_scopes=create_token_with_scopes) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.create.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + if _body is not None: + body_content = self._serialize.body(_body, 'CommunicationIdentityCreateRequest') + else: + body_content = None + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [201]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + deserialized = self._deserialize('CommunicationIdentityAccessTokenResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + create.metadata = {'url': '/identities'} # type: ignore + + async def delete( + self, + id: str, + **kwargs: Any + ) -> None: + """Delete the identity, revoke all tokens for the identity and delete all associated data. + + Delete the identity, revoke all tokens for the identity and delete all associated data. + + :param id: Identifier of the identity to be deleted. + :type id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None, or the result of cls(response) + :rtype: None + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.delete.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'id': self._serialize.url("id", id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.delete(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + if cls: + return cls(pipeline_response, None, {}) + + delete.metadata = {'url': '/identities/{id}'} # type: ignore + + async def revoke_access_tokens( + self, + id: str, + **kwargs: Any + ) -> None: + """Revoke all access tokens for the specific identity. + + Revoke all access tokens for the specific identity. + + :param id: Identifier of the identity. + :type id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None, or the result of cls(response) + :rtype: None + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.revoke_access_tokens.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'id': self._serialize.url("id", id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.post(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + if cls: + return cls(pipeline_response, None, {}) + + revoke_access_tokens.metadata = {'url': '/identities/{id}/:revokeAccessTokens'} # type: ignore + + async def issue_access_token( + self, + id: str, + scopes: List[Union[str, "_models.CommunicationTokenScope"]], + **kwargs: Any + ) -> "_models.CommunicationIdentityAccessToken": + """Issue a new token for an identity. + + Issue a new token for an identity. + + :param id: Identifier of the identity to issue token for. + :type id: str + :param scopes: List of scopes attached to the token. + :type scopes: list[str or ~azure.communication.identity.models.CommunicationTokenScope] + :keyword callable cls: A custom type or function that will be passed the direct response + :return: CommunicationIdentityAccessToken, or the result of cls(response) + :rtype: ~azure.communication.identity.models.CommunicationIdentityAccessToken + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.CommunicationIdentityAccessToken"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _body = _models.CommunicationIdentityAccessTokenRequest(scopes=scopes) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.issue_access_token.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'id': self._serialize.url("id", id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(_body, 'CommunicationIdentityAccessTokenRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + deserialized = self._deserialize('CommunicationIdentityAccessToken', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + issue_access_token.metadata = {'url': '/identities/{id}/:issueAccessToken'} # type: ignore diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/models/__init__.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/models/__init__.py new file mode 100644 index 00000000000..c5df5a06678 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/models/__init__.py @@ -0,0 +1,39 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import CommunicationError + from ._models_py3 import CommunicationErrorResponse + from ._models_py3 import CommunicationIdentity + from ._models_py3 import CommunicationIdentityAccessToken + from ._models_py3 import CommunicationIdentityAccessTokenRequest + from ._models_py3 import CommunicationIdentityAccessTokenResult + from ._models_py3 import CommunicationIdentityCreateRequest +except (SyntaxError, ImportError): + from ._models import CommunicationError # type: ignore + from ._models import CommunicationErrorResponse # type: ignore + from ._models import CommunicationIdentity # type: ignore + from ._models import CommunicationIdentityAccessToken # type: ignore + from ._models import CommunicationIdentityAccessTokenRequest # type: ignore + from ._models import CommunicationIdentityAccessTokenResult # type: ignore + from ._models import CommunicationIdentityCreateRequest # type: ignore + +from ._communication_identity_client_enums import ( + CommunicationTokenScope, +) + +__all__ = [ + 'CommunicationError', + 'CommunicationErrorResponse', + 'CommunicationIdentity', + 'CommunicationIdentityAccessToken', + 'CommunicationIdentityAccessTokenRequest', + 'CommunicationIdentityAccessTokenResult', + 'CommunicationIdentityCreateRequest', + 'CommunicationTokenScope', +] diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/models/_communication_identity_client_enums.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/models/_communication_identity_client_enums.py new file mode 100644 index 00000000000..fb650aac3d1 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/models/_communication_identity_client_enums.py @@ -0,0 +1,34 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum, EnumMeta +from six import with_metaclass + +class _CaseInsensitiveEnumMeta(EnumMeta): + def __getitem__(self, name): + return super().__getitem__(name.upper()) + + def __getattr__(cls, name): + """Return the enum member matching `name` + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + """ + try: + return cls._member_map_[name.upper()] + except KeyError: + raise AttributeError(name) + + +class CommunicationTokenScope(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """List of scopes for an access token. + """ + + CHAT = "chat" + VOIP = "voip" diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/models/_models.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/models/_models.py new file mode 100644 index 00000000000..ead5ab1c6db --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/models/_models.py @@ -0,0 +1,211 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from azure.core.exceptions import HttpResponseError +import msrest.serialization + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param code: Required. The error code. + :type code: str + :param message: Required. The error message. + :type message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.identity.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.identity.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'target': {'key': 'target', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[CommunicationError]'}, + 'inner_error': {'key': 'innererror', 'type': 'CommunicationError'}, + } + + def __init__( + self, + **kwargs + ): + super(CommunicationError, self).__init__(**kwargs) + self.code = kwargs['code'] + self.message = kwargs['message'] + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :param error: Required. The Communication Services error. + :type error: ~azure.communication.identity.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + 'error': {'key': 'error', 'type': 'CommunicationError'}, + } + + def __init__( + self, + **kwargs + ): + super(CommunicationErrorResponse, self).__init__(**kwargs) + self.error = kwargs['error'] + + +class CommunicationIdentity(msrest.serialization.Model): + """A communication identity. + + All required parameters must be populated in order to send to Azure. + + :param id: Required. Identifier of the identity. + :type id: str + """ + + _validation = { + 'id': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(CommunicationIdentity, self).__init__(**kwargs) + self.id = kwargs['id'] + + +class CommunicationIdentityAccessToken(msrest.serialization.Model): + """An access token. + + All required parameters must be populated in order to send to Azure. + + :param token: Required. The access token issued for the identity. + :type token: str + :param expires_on: Required. The expiry time of the token. + :type expires_on: ~datetime.datetime + """ + + _validation = { + 'token': {'required': True}, + 'expires_on': {'required': True}, + } + + _attribute_map = { + 'token': {'key': 'token', 'type': 'str'}, + 'expires_on': {'key': 'expiresOn', 'type': 'iso-8601'}, + } + + def __init__( + self, + **kwargs + ): + super(CommunicationIdentityAccessToken, self).__init__(**kwargs) + self.token = kwargs['token'] + self.expires_on = kwargs['expires_on'] + + +class CommunicationIdentityAccessTokenRequest(msrest.serialization.Model): + """CommunicationIdentityAccessTokenRequest. + + All required parameters must be populated in order to send to Azure. + + :param scopes: Required. List of scopes attached to the token. + :type scopes: list[str or ~azure.communication.identity.models.CommunicationTokenScope] + """ + + _validation = { + 'scopes': {'required': True}, + } + + _attribute_map = { + 'scopes': {'key': 'scopes', 'type': '[str]'}, + } + + def __init__( + self, + **kwargs + ): + super(CommunicationIdentityAccessTokenRequest, self).__init__(**kwargs) + self.scopes = kwargs['scopes'] + + +class CommunicationIdentityAccessTokenResult(msrest.serialization.Model): + """A communication identity with access token. + + All required parameters must be populated in order to send to Azure. + + :param identity: Required. A communication identity. + :type identity: ~azure.communication.identity.models.CommunicationIdentity + :param access_token: An access token. + :type access_token: ~azure.communication.identity.models.CommunicationIdentityAccessToken + """ + + _validation = { + 'identity': {'required': True}, + } + + _attribute_map = { + 'identity': {'key': 'identity', 'type': 'CommunicationIdentity'}, + 'access_token': {'key': 'accessToken', 'type': 'CommunicationIdentityAccessToken'}, + } + + def __init__( + self, + **kwargs + ): + super(CommunicationIdentityAccessTokenResult, self).__init__(**kwargs) + self.identity = kwargs['identity'] + self.access_token = kwargs.get('access_token', None) + + +class CommunicationIdentityCreateRequest(msrest.serialization.Model): + """CommunicationIdentityCreateRequest. + + :param create_token_with_scopes: Also create access token for the created identity. + :type create_token_with_scopes: list[str or + ~azure.communication.identity.models.CommunicationTokenScope] + """ + + _attribute_map = { + 'create_token_with_scopes': {'key': 'createTokenWithScopes', 'type': '[str]'}, + } + + def __init__( + self, + **kwargs + ): + super(CommunicationIdentityCreateRequest, self).__init__(**kwargs) + self.create_token_with_scopes = kwargs.get('create_token_with_scopes', None) diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/models/_models_py3.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/models/_models_py3.py new file mode 100644 index 00000000000..088037d79eb --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/models/_models_py3.py @@ -0,0 +1,231 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +import datetime +from typing import List, Optional, Union + +from azure.core.exceptions import HttpResponseError +import msrest.serialization + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param code: Required. The error code. + :type code: str + :param message: Required. The error message. + :type message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.identity.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.identity.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'target': {'key': 'target', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[CommunicationError]'}, + 'inner_error': {'key': 'innererror', 'type': 'CommunicationError'}, + } + + def __init__( + self, + *, + code: str, + message: str, + **kwargs + ): + super(CommunicationError, self).__init__(**kwargs) + self.code = code + self.message = message + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :param error: Required. The Communication Services error. + :type error: ~azure.communication.identity.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + 'error': {'key': 'error', 'type': 'CommunicationError'}, + } + + def __init__( + self, + *, + error: "CommunicationError", + **kwargs + ): + super(CommunicationErrorResponse, self).__init__(**kwargs) + self.error = error + + +class CommunicationIdentity(msrest.serialization.Model): + """A communication identity. + + All required parameters must be populated in order to send to Azure. + + :param id: Required. Identifier of the identity. + :type id: str + """ + + _validation = { + 'id': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + } + + def __init__( + self, + *, + id: str, + **kwargs + ): + super(CommunicationIdentity, self).__init__(**kwargs) + self.id = id + + +class CommunicationIdentityAccessToken(msrest.serialization.Model): + """An access token. + + All required parameters must be populated in order to send to Azure. + + :param token: Required. The access token issued for the identity. + :type token: str + :param expires_on: Required. The expiry time of the token. + :type expires_on: ~datetime.datetime + """ + + _validation = { + 'token': {'required': True}, + 'expires_on': {'required': True}, + } + + _attribute_map = { + 'token': {'key': 'token', 'type': 'str'}, + 'expires_on': {'key': 'expiresOn', 'type': 'iso-8601'}, + } + + def __init__( + self, + *, + token: str, + expires_on: datetime.datetime, + **kwargs + ): + super(CommunicationIdentityAccessToken, self).__init__(**kwargs) + self.token = token + self.expires_on = expires_on + + +class CommunicationIdentityAccessTokenRequest(msrest.serialization.Model): + """CommunicationIdentityAccessTokenRequest. + + All required parameters must be populated in order to send to Azure. + + :param scopes: Required. List of scopes attached to the token. + :type scopes: list[str or ~azure.communication.identity.models.CommunicationTokenScope] + """ + + _validation = { + 'scopes': {'required': True}, + } + + _attribute_map = { + 'scopes': {'key': 'scopes', 'type': '[str]'}, + } + + def __init__( + self, + *, + scopes: List[Union[str, "CommunicationTokenScope"]], + **kwargs + ): + super(CommunicationIdentityAccessTokenRequest, self).__init__(**kwargs) + self.scopes = scopes + + +class CommunicationIdentityAccessTokenResult(msrest.serialization.Model): + """A communication identity with access token. + + All required parameters must be populated in order to send to Azure. + + :param identity: Required. A communication identity. + :type identity: ~azure.communication.identity.models.CommunicationIdentity + :param access_token: An access token. + :type access_token: ~azure.communication.identity.models.CommunicationIdentityAccessToken + """ + + _validation = { + 'identity': {'required': True}, + } + + _attribute_map = { + 'identity': {'key': 'identity', 'type': 'CommunicationIdentity'}, + 'access_token': {'key': 'accessToken', 'type': 'CommunicationIdentityAccessToken'}, + } + + def __init__( + self, + *, + identity: "CommunicationIdentity", + access_token: Optional["CommunicationIdentityAccessToken"] = None, + **kwargs + ): + super(CommunicationIdentityAccessTokenResult, self).__init__(**kwargs) + self.identity = identity + self.access_token = access_token + + +class CommunicationIdentityCreateRequest(msrest.serialization.Model): + """CommunicationIdentityCreateRequest. + + :param create_token_with_scopes: Also create access token for the created identity. + :type create_token_with_scopes: list[str or + ~azure.communication.identity.models.CommunicationTokenScope] + """ + + _attribute_map = { + 'create_token_with_scopes': {'key': 'createTokenWithScopes', 'type': '[str]'}, + } + + def __init__( + self, + *, + create_token_with_scopes: Optional[List[Union[str, "CommunicationTokenScope"]]] = None, + **kwargs + ): + super(CommunicationIdentityCreateRequest, self).__init__(**kwargs) + self.create_token_with_scopes = create_token_with_scopes diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/operations/__init__.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/operations/__init__.py new file mode 100644 index 00000000000..09dd0f2d066 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/operations/__init__.py @@ -0,0 +1,13 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._communication_identity_operations import CommunicationIdentityOperations + +__all__ = [ + 'CommunicationIdentityOperations', +] diff --git a/src/communication/azext_communication/vendored_sdks/identity/_generated/operations/_communication_identity_operations.py b/src/communication/azext_communication/vendored_sdks/identity/_generated/operations/_communication_identity_operations.py new file mode 100644 index 00000000000..507b932cb68 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_generated/operations/_communication_identity_operations.py @@ -0,0 +1,289 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpRequest, HttpResponse + +from .. import models as _models + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar, Union + + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +class CommunicationIdentityOperations(object): + """CommunicationIdentityOperations operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.communication.identity.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer): + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + def create( + self, + create_token_with_scopes=None, # type: Optional[List[Union[str, "_models.CommunicationTokenScope"]]] + **kwargs # type: Any + ): + # type: (...) -> "_models.CommunicationIdentityAccessTokenResult" + """Create a new identity. + + Create a new identity. + + :param create_token_with_scopes: Also create access token for the created identity. + :type create_token_with_scopes: list[str or ~azure.communication.identity.models.CommunicationTokenScope] + :keyword callable cls: A custom type or function that will be passed the direct response + :return: CommunicationIdentityAccessTokenResult, or the result of cls(response) + :rtype: ~azure.communication.identity.models.CommunicationIdentityAccessTokenResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.CommunicationIdentityAccessTokenResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _body = _models.CommunicationIdentityCreateRequest(create_token_with_scopes=create_token_with_scopes) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.create.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + if _body is not None: + body_content = self._serialize.body(_body, 'CommunicationIdentityCreateRequest') + else: + body_content = None + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [201]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + deserialized = self._deserialize('CommunicationIdentityAccessTokenResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + create.metadata = {'url': '/identities'} # type: ignore + + def delete( + self, + id, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + """Delete the identity, revoke all tokens for the identity and delete all associated data. + + Delete the identity, revoke all tokens for the identity and delete all associated data. + + :param id: Identifier of the identity to be deleted. + :type id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None, or the result of cls(response) + :rtype: None + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.delete.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'id': self._serialize.url("id", id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.delete(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + if cls: + return cls(pipeline_response, None, {}) + + delete.metadata = {'url': '/identities/{id}'} # type: ignore + + def revoke_access_tokens( + self, + id, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + """Revoke all access tokens for the specific identity. + + Revoke all access tokens for the specific identity. + + :param id: Identifier of the identity. + :type id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None, or the result of cls(response) + :rtype: None + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.revoke_access_tokens.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'id': self._serialize.url("id", id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.post(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + if cls: + return cls(pipeline_response, None, {}) + + revoke_access_tokens.metadata = {'url': '/identities/{id}/:revokeAccessTokens'} # type: ignore + + def issue_access_token( + self, + id, # type: str + scopes, # type: List[Union[str, "_models.CommunicationTokenScope"]] + **kwargs # type: Any + ): + # type: (...) -> "_models.CommunicationIdentityAccessToken" + """Issue a new token for an identity. + + Issue a new token for an identity. + + :param id: Identifier of the identity to issue token for. + :type id: str + :param scopes: List of scopes attached to the token. + :type scopes: list[str or ~azure.communication.identity.models.CommunicationTokenScope] + :keyword callable cls: A custom type or function that will be passed the direct response + :return: CommunicationIdentityAccessToken, or the result of cls(response) + :rtype: ~azure.communication.identity.models.CommunicationIdentityAccessToken + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.CommunicationIdentityAccessToken"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _body = _models.CommunicationIdentityAccessTokenRequest(scopes=scopes) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.issue_access_token.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'id': self._serialize.url("id", id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(_body, 'CommunicationIdentityAccessTokenRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + deserialized = self._deserialize('CommunicationIdentityAccessToken', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + issue_access_token.metadata = {'url': '/identities/{id}/:issueAccessToken'} # type: ignore diff --git a/src/communication/azext_communication/vendored_sdks/identity/_shared/__init__.py b/src/communication/azext_communication/vendored_sdks/identity/_shared/__init__.py new file mode 100644 index 00000000000..5b396cd202e --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_shared/__init__.py @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- diff --git a/src/communication/azext_communication/vendored_sdks/identity/_shared/models.py b/src/communication/azext_communication/vendored_sdks/identity/_shared/models.py new file mode 100644 index 00000000000..7820b25a7d1 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_shared/models.py @@ -0,0 +1,156 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +# pylint: skip-file + +from enum import Enum, EnumMeta +from six import with_metaclass +from typing import Mapping, Optional, Union, Any +try: + from typing import Protocol, TypedDict +except ImportError: + from typing_extensions import Protocol, TypedDict + +from azure.core import CaseInsensitiveEnumMeta + + +class CommunicationIdentifierKind(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """Communication Identifier Kind.""" + + UNKNOWN = "unknown" + COMMUNICATION_USER = "communication_user" + PHONE_NUMBER = "phone_number" + MICROSOFT_TEAMS_USER = "microsoft_teams_user" + + +class CommunicationCloudEnvironment(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """The cloud enviornment that the identifier belongs to""" + + PUBLIC = "PUBLIC" + DOD = "DOD" + GCCH = "GCCH" + + +class CommunicationIdentifier(Protocol): + """Communication Identifier. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping[str, Any] properties: The properties of the identifier. + """ + raw_id = None # type: Optional[str] + kind = None # type: Optional[Union[CommunicationIdentifierKind, str]] + properties = {} # type: Mapping[str, Any] + + +CommunicationUserProperties = TypedDict( + 'CommunicationUserProperties', + id=str +) + + +class CommunicationUserIdentifier(object): + """Represents a user in Azure Communication Service. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping[str, Any] properties: The properties of the identifier. + The keys in this mapping include: + - `id`(str): ID of the Communication user as returned from Azure Communication Identity. + + :param str id: ID of the Communication user as returned from Azure Communication Identity. + """ + kind = CommunicationIdentifierKind.COMMUNICATION_USER + + def __init__(self, id, **kwargs): + # type: (str, Any) -> None + self.raw_id = kwargs.get('raw_id') + self.properties = CommunicationUserProperties(id=id) + + +PhoneNumberProperties = TypedDict( + 'PhoneNumberProperties', + value=str +) + + +class PhoneNumberIdentifier(object): + """Represents a phone number. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping properties: The properties of the identifier. + The keys in this mapping include: + - `value`(str): The phone number in E.164 format. + + :param str value: The phone number. + """ + kind = CommunicationIdentifierKind.PHONE_NUMBER + + def __init__(self, value, **kwargs): + # type: (str, Any) -> None + self.raw_id = kwargs.get('raw_id') + self.properties = PhoneNumberProperties(value=value) + + +class UnknownIdentifier(object): + """Represents an identifier of an unknown type. + + It will be encountered in communications with endpoints that are not + identifiable by this version of the SDK. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping properties: The properties of the identifier. + :param str identifier: The ID of the identifier. + """ + kind = CommunicationIdentifierKind.UNKNOWN + + def __init__(self, identifier): + # type: (str) -> None + self.raw_id = identifier + self.properties = {} + + +MicrosoftTeamsUserProperties = TypedDict( + 'MicrosoftTeamsUserProperties', + user_id=str, + is_anonymous=bool, + cloud=Union[CommunicationCloudEnvironment, str] +) + + +class MicrosoftTeamsUserIdentifier(object): + """Represents an identifier for a Microsoft Teams user. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping properties: The properties of the identifier. + The keys in this mapping include: + - `user_id`(str): The id of the Microsoft Teams user. If the user isn't anonymous, + the id is the AAD object id of the user. + - `is_anonymous` (bool): Set this to true if the user is anonymous for example when joining + a meeting with a share link. + - `cloud` (str): Cloud environment that this identifier belongs to. + + :param str user_id: Microsoft Teams user id. + :keyword bool is_anonymous: `True` if the identifier is anonymous. Default value is `False`. + :keyword cloud: Cloud environment that the user belongs to. Default value is `PUBLIC`. + :paramtype cloud: str or ~azure.communication.chat.CommunicationCloudEnvironment + """ + kind = CommunicationIdentifierKind.MICROSOFT_TEAMS_USER + + def __init__(self, user_id, **kwargs): + # type: (str, Any) -> None + self.raw_id = kwargs.get('raw_id') + self.properties = MicrosoftTeamsUserProperties( + user_id=user_id, + is_anonymous=kwargs.get('is_anonymous', False), + cloud=kwargs.get('cloud') or CommunicationCloudEnvironment.PUBLIC + ) diff --git a/src/communication/azext_communication/vendored_sdks/identity/_shared/policy.py b/src/communication/azext_communication/vendored_sdks/identity/_shared/policy.py new file mode 100644 index 00000000000..b2a0de8d423 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_shared/policy.py @@ -0,0 +1,91 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import hashlib +import urllib +import base64 +import hmac +from azure.core.pipeline.policies import SansIOHTTPPolicy +from .utils import get_current_utc_time + +class HMACCredentialsPolicy(SansIOHTTPPolicy): + """Implementation of HMAC authentication policy. + """ + + def __init__(self, + host, # type: str + access_key, # type: str + decode_url=False # type: bool + ): + # type: (...) -> None + super(HMACCredentialsPolicy, self).__init__() + + if host.startswith("https://"): + self._host = host.replace("https://", "") + + if host.startswith("http://"): + self._host = host.replace("http://", "") + + self._access_key = access_key + self._decode_url = decode_url + + def _compute_hmac(self, + value # type: str + ): + decoded_secret = base64.b64decode(self._access_key) + digest = hmac.new( + decoded_secret, value.encode("utf-8"), hashlib.sha256 + ).digest() + + return base64.b64encode(digest).decode("utf-8") + + def _sign_request(self, request): + verb = request.http_request.method.upper() + + # Get the path and query from url, which looks like https://host/path/query + query_url = str(request.http_request.url[len(self._host) + 8:]) + + if self._decode_url: + query_url = urllib.parse.unquote(query_url) + + signed_headers = "date;host;x-ms-content-sha256" + + utc_now = get_current_utc_time() + if request.http_request.body is None: + request.http_request.body = "" + content_digest = hashlib.sha256( + (request.http_request.body.encode("utf-8")) + ).digest() + content_hash = base64.b64encode(content_digest).decode("utf-8") + + string_to_sign = ( + verb + + "\n" + + query_url + + "\n" + + utc_now + + ";" + + self._host + + ";" + + content_hash + ) + + signature = self._compute_hmac(string_to_sign) + + signature_header = { + "Date": utc_now, + "x-ms-content-sha256": content_hash, + "x-ms-return-client-request-id": "true", + "Authorization": "HMAC-SHA256 SignedHeaders=" +\ + signed_headers + "&Signature=" + signature, + } + + request.http_request.headers.update(signature_header) + + return request + + def on_request(self, request): + self._sign_request(request) diff --git a/src/communication/azext_communication/vendored_sdks/identity/_shared/user_credential.py b/src/communication/azext_communication/vendored_sdks/identity/_shared/user_credential.py new file mode 100644 index 00000000000..9c3228b2861 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_shared/user_credential.py @@ -0,0 +1,90 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from threading import Lock, Condition +from datetime import datetime, timedelta +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) + +from msrest.serialization import TZ_UTC + +from .user_token_refresh_options import CommunicationTokenRefreshOptions + +class CommunicationTokenCredential(object): + """Credential type used for authenticating to an Azure Communication service. + :param str token: The token used to authenticate to an Azure Communication service + :keyword token_refresher: The token refresher to provide capacity to fetch fresh token + :raises: TypeError + """ + + _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2 + + def __init__(self, + token, # type: str + **kwargs + ): + token_refresher = kwargs.pop('token_refresher', None) + communication_token_refresh_options = CommunicationTokenRefreshOptions(token=token, + token_refresher=token_refresher) + self._token = communication_token_refresh_options.get_token() + self._token_refresher = communication_token_refresh_options.get_token_refresher() + self._lock = Condition(Lock()) + self._some_thread_refreshing = False + + def get_token(self): + # type () -> ~azure.core.credentials.AccessToken + """The value of the configured token. + :rtype: ~azure.core.credentials.AccessToken + """ + + if not self._token_refresher or not self._token_expiring(): + return self._token + + should_this_thread_refresh = False + + with self._lock: + while self._token_expiring(): + if self._some_thread_refreshing: + if self._is_currenttoken_valid(): + return self._token + + self._wait_till_inprogress_thread_finish_refreshing() + else: + should_this_thread_refresh = True + self._some_thread_refreshing = True + break + + if should_this_thread_refresh: + try: + newtoken = self._token_refresher() # pylint:disable=not-callable + + with self._lock: + self._token = newtoken + self._some_thread_refreshing = False + self._lock.notify_all() + except: + with self._lock: + self._some_thread_refreshing = False + self._lock.notify_all() + + raise + return self._token + + def _wait_till_inprogress_thread_finish_refreshing(self): + self._lock.release() + self._lock.acquire() + + def _token_expiring(self): + return self._token.expires_on - self._get_utc_now() <\ + timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES) + + def _is_currenttoken_valid(self): + return self._get_utc_now() < self._token.expires_on + + @classmethod + def _get_utc_now(cls): + return datetime.now().replace(tzinfo=TZ_UTC) diff --git a/src/communication/azext_communication/vendored_sdks/identity/_shared/user_credential_async.py b/src/communication/azext_communication/vendored_sdks/identity/_shared/user_credential_async.py new file mode 100644 index 00000000000..b49c593a066 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_shared/user_credential_async.py @@ -0,0 +1,93 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from asyncio import Condition, Lock +from datetime import datetime, timedelta +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) + +from msrest.serialization import TZ_UTC + +from .user_token_refresh_options import CommunicationTokenRefreshOptions + +class CommunicationTokenCredential(object): + """Credential type used for authenticating to an Azure Communication service. + :param str token: The token used to authenticate to an Azure Communication service + :keyword token_refresher: The token refresher to provide capacity to fetch fresh token + :raises: TypeError + """ + + _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2 + + def __init__(self, + token, # type: str + **kwargs + ): + token_refresher = kwargs.pop('token_refresher', None) + communication_token_refresh_options = CommunicationTokenRefreshOptions(token=token, + token_refresher=token_refresher) + self._token = communication_token_refresh_options.get_token() + self._token_refresher = communication_token_refresh_options.get_token_refresher() + self._lock = Condition(Lock()) + self._some_thread_refreshing = False + + def get_token(self): + # type () -> ~azure.core.credentials.AccessToken + """The value of the configured token. + :rtype: ~azure.core.credentials.AccessToken + """ + + if not self._token_refresher or not self._token_expiring(): + return self._token + + should_this_thread_refresh = False + + with self._lock: + + while self._token_expiring(): + if self._some_thread_refreshing: + if self._is_currenttoken_valid(): + return self._token + + self._wait_till_inprogress_thread_finish_refreshing() + else: + should_this_thread_refresh = True + self._some_thread_refreshing = True + break + + + if should_this_thread_refresh: + try: + newtoken = self._token_refresher() # pylint:disable=not-callable + + with self._lock: + self._token = newtoken + self._some_thread_refreshing = False + self._lock.notify_all() + except: + with self._lock: + self._some_thread_refreshing = False + self._lock.notify_all() + + raise + + return self._token + + def _wait_till_inprogress_thread_finish_refreshing(self): + self._lock.release() + self._lock.acquire() + + def _token_expiring(self): + return self._token.expires_on - self._get_utc_now() <\ + timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES) + + def _is_currenttoken_valid(self): + return self._get_utc_now() < self._token.expires_on + + @classmethod + def _get_utc_now(cls): + return datetime.now().replace(tzinfo=TZ_UTC) diff --git a/src/communication/azext_communication/vendored_sdks/identity/_shared/user_token_refresh_options.py b/src/communication/azext_communication/vendored_sdks/identity/_shared/user_token_refresh_options.py new file mode 100644 index 00000000000..6bdc0d45602 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_shared/user_token_refresh_options.py @@ -0,0 +1,36 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) +import six +from .utils import create_access_token + +class CommunicationTokenRefreshOptions(object): + """Options for refreshing CommunicationTokenCredential. + :param str token: The token used to authenticate to an Azure Communication service + :param token_refresher: The token refresher to provide capacity to fetch fresh token + :raises: TypeError + """ + + def __init__(self, + token, # type: str + token_refresher=None + ): + # type: (str) -> None + if not isinstance(token, six.string_types): + raise TypeError("token must be a string.") + self._token = token + self._token_refresher = token_refresher + + def get_token(self): + """Return the the serialized JWT token.""" + return create_access_token(self._token) + + def get_token_refresher(self): + """Return the token refresher to provide capacity to fetch fresh token.""" + return self._token_refresher diff --git a/src/communication/azext_communication/vendored_sdks/identity/_shared/utils.py b/src/communication/azext_communication/vendored_sdks/identity/_shared/utils.py new file mode 100644 index 00000000000..40d605b4fc8 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_shared/utils.py @@ -0,0 +1,128 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import base64 +import json +import time +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) +from datetime import datetime +import calendar +from msrest.serialization import TZ_UTC +from azure.core.credentials import AccessToken + +def _convert_datetime_to_utc_int(expires_on): + return int(calendar.timegm(expires_on.utctimetuple())) + +def parse_connection_str(conn_str): + # type: (str) -> Tuple[str, str, str, str] + if conn_str is None: + raise ValueError( + "Connection string is undefined." + ) + endpoint = None + shared_access_key = None + for element in conn_str.split(";"): + key, _, value = element.partition("=") + if key.lower() == "endpoint": + endpoint = value.rstrip("/") + elif key.lower() == "accesskey": + shared_access_key = value + if not all([endpoint, shared_access_key]): + raise ValueError( + "Invalid connection string. You can get the connection string from your resource page in the Azure Portal. " + "The format should be as follows: endpoint=https:///;accesskey=" + ) + left_slash_pos = cast(str, endpoint).find("//") + if left_slash_pos != -1: + host = cast(str, endpoint)[left_slash_pos + 2:] + else: + host = str(endpoint) + + return host, str(shared_access_key) + +def get_current_utc_time(): + # type: () -> str + return str(datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S ")) + "GMT" + +def get_current_utc_as_int(): + # type: () -> int + current_utc_datetime = datetime.utcnow().replace(tzinfo=TZ_UTC) + return _convert_datetime_to_utc_int(current_utc_datetime) + +def create_access_token(token): + # type: (str) -> azure.core.credentials.AccessToken + """Creates an instance of azure.core.credentials.AccessToken from a + string token. The input string is jwt token in the following form: + .. + This method looks into the token_payload which is a json and extracts the expiry time + for that token and creates a tuple of type azure.core.credentials.AccessToken + (, ) + :param token: User token + :type token: str + :return: Instance of azure.core.credentials.AccessToken - token and expiry date of it + :rtype: ~azure.core.credentials.AccessToken + """ + + token_parse_err_msg = "Token is not formatted correctly" + parts = token.split(".") + + if len(parts) < 3: + raise ValueError(token_parse_err_msg) + + try: + padded_base64_payload = base64.b64decode(parts[1] + "==").decode('ascii') + payload = json.loads(padded_base64_payload) + return AccessToken(token, + _convert_datetime_to_utc_int(datetime.fromtimestamp(payload['exp']).replace(tzinfo=TZ_UTC))) + except ValueError: + raise ValueError(token_parse_err_msg) + +def _convert_expires_on_datetime_to_utc_int(expires_on): + epoch = time.mktime(datetime(1970, 1, 1).timetuple()) + return epoch-time.mktime(expires_on.timetuple()) + +def get_authentication_policy( + endpoint, # type: str + credential, # type: TokenCredential or str + decode_url=False, # type: bool + is_async=False, # type: bool +): + # type: (...) -> BearerTokenCredentialPolicy or HMACCredentialPolicy + """Returns the correct authentication policy based + on which credential is being passed. + :param endpoint: The endpoint to which we are authenticating to. + :type endpoint: str + :param credential: The credential we use to authenticate to the service + :type credential: TokenCredential or str + :param isAsync: For async clients there is a need to decode the url + :type bool: isAsync or str + :rtype: ~azure.core.pipeline.policies.BearerTokenCredentialPolicy + ~HMACCredentialsPolicy + """ + + if credential is None: + raise ValueError("Parameter 'credential' must not be None.") + if hasattr(credential, "get_token"): + if is_async: + from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy + return AsyncBearerTokenCredentialPolicy( + credential, "https://communication.azure.com//.default") + from azure.core.pipeline.policies import BearerTokenCredentialPolicy + return BearerTokenCredentialPolicy( + credential, "https://communication.azure.com//.default") + if isinstance(credential, str): + from .._shared.policy import HMACCredentialsPolicy + return HMACCredentialsPolicy(endpoint, credential, decode_url=decode_url) + + raise TypeError("Unsupported credential: {}. Use an access token string to use HMACCredentialsPolicy" + "or a token credential from azure.identity".format(type(credential))) + +def _convert_expires_on_datetime_to_utc_int(expires_on): + epoch = time.mktime(datetime(1970, 1, 1).timetuple()) + return epoch-time.mktime(expires_on.timetuple()) diff --git a/src/communication/azext_communication/vendored_sdks/identity/_version.py b/src/communication/azext_communication/vendored_sdks/identity/_version.py new file mode 100644 index 00000000000..7769c094291 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/_version.py @@ -0,0 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +VERSION = "1.0.1" + +SDK_MONIKER = "communication-identity/{}".format(VERSION) # type: str diff --git a/src/communication/azext_communication/vendored_sdks/identity/aio/__init__.py b/src/communication/azext_communication/vendored_sdks/identity/aio/__init__.py new file mode 100644 index 00000000000..3276fcb588c --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/aio/__init__.py @@ -0,0 +1,11 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from ._communication_identity_client_async import CommunicationIdentityClient + +__all__ = [ + 'CommunicationIdentityClient' +] diff --git a/src/communication/azext_communication/vendored_sdks/identity/aio/_communication_identity_client_async.py b/src/communication/azext_communication/vendored_sdks/identity/aio/_communication_identity_client_async.py new file mode 100644 index 00000000000..cf43aa6d793 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/identity/aio/_communication_identity_client_async.py @@ -0,0 +1,180 @@ +# coding=utf-8 +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from typing import TYPE_CHECKING, Any, List, Union, Tuple + +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.core.credentials import AccessToken + +from .._generated.aio._communication_identity_client\ + import CommunicationIdentityClient as CommunicationIdentityClientGen +from .._shared.utils import parse_connection_str, get_authentication_policy +from .._shared.models import CommunicationUserIdentifier +from .._version import SDK_MONIKER + +if TYPE_CHECKING: + from azure.core.credentials_async import AsyncTokenCredential + from .._generated.models import CommunicationTokenScope + + +class CommunicationIdentityClient: + """Azure Communication Services Identity client. + + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param AsyncTokenCredential credential: + The AsyncTokenCredential we use to authenticate against the service. + + .. admonition:: Example: + + .. literalinclude:: ../../samples/identity_samples_async.py + :language: python + :dedent: 8 + """ + def __init__( + self, + endpoint: str, + credential: 'AsyncTokenCredential', + **kwargs + ) -> None: + try: + if not endpoint.lower().startswith('http'): + endpoint = "https://" + endpoint + except AttributeError: + raise ValueError("Account URL must be a string.") + + if not credential: + raise ValueError( + "You need to provide account shared key to authenticate.") + + self._endpoint = endpoint + self._identity_service_client = CommunicationIdentityClientGen( + self._endpoint, + authentication_policy=get_authentication_policy(endpoint, credential, decode_url=True, is_async=True), + sdk_moniker=SDK_MONIKER, + **kwargs) + + @classmethod + def from_connection_string(cls, conn_str: str, **kwargs) -> 'CommunicationIdentityClient': + """Create CommunicationIdentityClient from a Connection String. + + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of CommunicationIdentityClient. + :rtype: ~azure.communication.identity.aio.CommunicationIdentityClient + + .. admonition:: Example: + + .. literalinclude:: ../samples/identity_samples.py + :start-after: [START auth_from_connection_string] + :end-before: [END auth_from_connection_string] + :language: python + :dedent: 8 + :caption: Creating the CommunicationIdentityClient from a connection string. + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) + + @distributed_trace_async + async def create_user(self, **kwargs) -> 'CommunicationUserIdentifier': + """create a single Communication user + + :return: CommunicationUserIdentifier + :rtype: ~azure.communication.identity.CommunicationUserIdentifier + """ + return await self._identity_service_client.communication_identity.create( + cls=lambda pr, u, e: CommunicationUserIdentifier(u.identity.id, raw_id=u.identity.id), + **kwargs) + + @distributed_trace_async + async def create_user_and_token( + self, + scopes: List[Union[str, 'CommunicationTokenScope']], + **kwargs + ) -> Tuple['CommunicationUserIdentifier', AccessToken]: + """create a single Communication user with an identity token. + :param scopes: + List of scopes to be added to the token. + :type scopes: list[str or ~azure.communication.identity.CommunicationTokenScope] + :return: A tuple of a CommunicationUserIdentifier and a AccessToken. + :rtype: + tuple of (~azure.communication.identity.CommunicationUserIdentifier, ~azure.core.credentials.AccessToken) + """ + return await self._identity_service_client.communication_identity.create( + create_token_with_scopes=scopes, + cls=lambda pr, u, e: (CommunicationUserIdentifier(u.identity.id, raw_id=u.identity.id), + AccessToken(u.access_token.token, u.access_token.expires_on)), + **kwargs) + + @distributed_trace_async + async def delete_user( + self, + user: CommunicationUserIdentifier, + **kwargs + ) -> None: + """Triggers revocation event for user and deletes all its data. + + :param user: + Azure Communication User to delete + :type user: ~azure.communication.identity.CommunicationUserIdentifier + :return: None + :rtype: None + """ + await self._identity_service_client.communication_identity.delete( + user.properties['id'], **kwargs) + + @distributed_trace_async + async def get_token( + self, + user: CommunicationUserIdentifier, + scopes: List[Union[str, 'CommunicationTokenScope']], + **kwargs + ) -> AccessToken: + """Generates a new token for an identity. + + :param user: Azure Communication User + :type user: ~azure.communication.identity.CommunicationUserIdentifier + :param scopes: + List of scopes to be added to the token. + :type scopes: list[str or ~azure.communication.identity.CommunicationTokenScope] + :return: AccessToken + :rtype: ~azure.core.credentials.AccessToken + """ + return await self._identity_service_client.communication_identity.issue_access_token( + user.properties['id'], + scopes, + cls=lambda pr, u, e: AccessToken(u.token, u.expires_on), + **kwargs) + + @distributed_trace_async + async def revoke_tokens( + self, + user: CommunicationUserIdentifier, + **kwargs + ) -> None: + """Schedule revocation of all tokens of an identity. + + :param user: Azure Communication User. + :type user: ~azure.communication.identity.CommunicationUserIdentifier + :return: None + :rtype: None + """ + return await self._identity_service_client.communication_identity.revoke_access_tokens( + user.properties['id'] if user else None, + **kwargs) + + async def __aenter__(self) -> "CommunicationIdentityClient": + await self._identity_service_client.__aenter__() + return self + + async def __aexit__(self, *args) -> None: + await self.close() + + async def close(self) -> None: + """Close the :class: + `~azure.communication.identity.aio.CommunicationIdentityClient` session. + """ + await self._identity_service_client.__aexit__() diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/__init__.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/__init__.py new file mode 100644 index 00000000000..4af1eafae58 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/__init__.py @@ -0,0 +1,30 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from ._phone_numbers_client import PhoneNumbersClient + +from ._generated.models import ( + PurchasedPhoneNumber, + PhoneNumberCapabilities, + PhoneNumberCost, + PhoneNumberSearchResult, + BillingFrequency, + PhoneNumberAssignmentType, + PhoneNumberCapabilityType, + PhoneNumberType, +) + +__all__ = [ + 'PurchasedPhoneNumber', + 'PhoneNumberCapabilities', + 'PhoneNumberCost', + 'PhoneNumberSearchResult', + 'BillingFrequency', + 'PhoneNumberAssignmentType', + 'PhoneNumberCapabilityType', + 'PhoneNumberType', + 'PhoneNumbersClient', +] diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/__init__.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/__init__.py new file mode 100644 index 00000000000..abbd9c6a0b9 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/__init__.py @@ -0,0 +1,16 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._phone_numbers_client import PhoneNumbersClient +__all__ = ['PhoneNumbersClient'] + +try: + from ._patch import patch_sdk # type: ignore + patch_sdk() +except ImportError: + pass diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/_configuration.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/_configuration.py new file mode 100644 index 00000000000..6905dc67648 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/_configuration.py @@ -0,0 +1,58 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + +VERSION = "unknown" + +class PhoneNumbersClientConfiguration(Configuration): + """Configuration for PhoneNumbersClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example https://resourcename.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + super(PhoneNumbersClientConfiguration, self).__init__(**kwargs) + + self.endpoint = endpoint + self.api_version = "2021-03-07" + kwargs.setdefault('sdk_moniker', 'phonenumbersclient/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs # type: Any + ): + # type: (...) -> None + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/_phone_numbers_client.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/_phone_numbers_client.py new file mode 100644 index 00000000000..db8b0ccfa69 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/_phone_numbers_client.py @@ -0,0 +1,83 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core import PipelineClient +from msrest import Deserializer, Serializer + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + from azure.core.pipeline.transport import HttpRequest, HttpResponse + +from ._configuration import PhoneNumbersClientConfiguration +from .operations import PhoneNumbersOperations +from . import models + + +class PhoneNumbersClient(object): + """The phone numbers client uses Azure Communication Services to purchase and manage phone numbers. + + :ivar phone_numbers: PhoneNumbersOperations operations + :vartype phone_numbers: azure.communication.phonenumbers.operations.PhoneNumbersOperations + :param endpoint: The communication resource, for example https://resourcename.communication.azure.com. + :type endpoint: str + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + """ + + def __init__( + self, + endpoint, # type: str + base_url=None, + **kwargs # type: Any + ): + # type: (...) -> None + base_url = '{endpoint}' + self._config = PhoneNumbersClientConfiguration(endpoint, **kwargs) + self._client = PipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._serialize.client_side_validation = False + self._deserialize = Deserializer(client_models) + + self.phone_numbers = PhoneNumbersOperations( + self._client, self._config, self._serialize, self._deserialize) + + def _send_request(self, http_request, **kwargs): + # type: (HttpRequest, Any) -> HttpResponse + """Runs the network request through the client's chained policies. + + :param http_request: The network request you want to make. Required. + :type http_request: ~azure.core.pipeline.transport.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to True. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.pipeline.transport.HttpResponse + """ + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + http_request.url = self._client.format_url(http_request.url, **path_format_arguments) + stream = kwargs.pop("stream", True) + pipeline_response = self._client._pipeline.run(http_request, stream=stream, **kwargs) + return pipeline_response.http_response + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> PhoneNumbersClient + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/__init__.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/__init__.py new file mode 100644 index 00000000000..0f7fab5f7e3 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/__init__.py @@ -0,0 +1,10 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._phone_numbers_client import PhoneNumbersClient +__all__ = ['PhoneNumbersClient'] diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/_configuration.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/_configuration.py new file mode 100644 index 00000000000..92da00d6b71 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/_configuration.py @@ -0,0 +1,52 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +VERSION = "unknown" + +class PhoneNumbersClientConfiguration(Configuration): + """Configuration for PhoneNumbersClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example https://resourcename.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + super(PhoneNumbersClientConfiguration, self).__init__(**kwargs) + + self.endpoint = endpoint + self.api_version = "2021-03-07" + kwargs.setdefault('sdk_moniker', 'phonenumbersclient/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs: Any + ) -> None: + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/_phone_numbers_client.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/_phone_numbers_client.py new file mode 100644 index 00000000000..a9b20f599ac --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/_phone_numbers_client.py @@ -0,0 +1,72 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core import AsyncPipelineClient +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest +from msrest import Deserializer, Serializer + +from ._configuration import PhoneNumbersClientConfiguration +from .operations import PhoneNumbersOperations +from .. import models + + +class PhoneNumbersClient(object): + """The phone numbers client uses Azure Communication Services to purchase and manage phone numbers. + + :ivar phone_numbers: PhoneNumbersOperations operations + :vartype phone_numbers: azure.communication.phonenumbers.aio.operations.PhoneNumbersOperations + :param endpoint: The communication resource, for example https://resourcename.communication.azure.com. + :type endpoint: str + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + base_url = '{endpoint}' + self._config = PhoneNumbersClientConfiguration(endpoint, **kwargs) + self._client = AsyncPipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._serialize.client_side_validation = False + self._deserialize = Deserializer(client_models) + + self.phone_numbers = PhoneNumbersOperations( + self._client, self._config, self._serialize, self._deserialize) + + async def _send_request(self, http_request: HttpRequest, **kwargs: Any) -> AsyncHttpResponse: + """Runs the network request through the client's chained policies. + + :param http_request: The network request you want to make. Required. + :type http_request: ~azure.core.pipeline.transport.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to True. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.pipeline.transport.AsyncHttpResponse + """ + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + http_request.url = self._client.format_url(http_request.url, **path_format_arguments) + stream = kwargs.pop("stream", True) + pipeline_response = await self._client._pipeline.run(http_request, stream=stream, **kwargs) + return pipeline_response.http_response + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "PhoneNumbersClient": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/operations/__init__.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/operations/__init__.py new file mode 100644 index 00000000000..3da9bcb904f --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/operations/__init__.py @@ -0,0 +1,13 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._phone_numbers_operations import PhoneNumbersOperations + +__all__ = [ + 'PhoneNumbersOperations', +] diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/operations/_phone_numbers_operations.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/operations/_phone_numbers_operations.py new file mode 100644 index 00000000000..af9414213ae --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/aio/operations/_phone_numbers_operations.py @@ -0,0 +1,852 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, AsyncIterable, Callable, Dict, Generic, Optional, TypeVar, Union +import warnings + +from azure.core.async_paging import AsyncItemPaged, AsyncList +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest +from azure.core.polling import AsyncLROPoller, AsyncNoPolling, AsyncPollingMethod +from azure.core.polling.async_base_polling import AsyncLROBasePolling + +from ... import models as _models + +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class PhoneNumbersOperations: + """PhoneNumbersOperations async operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.communication.phonenumbers.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer) -> None: + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + async def _search_available_phone_numbers_initial( + self, + country_code: str, + body: "_models.PhoneNumberSearchRequest", + **kwargs: Any + ) -> "_models.PhoneNumberSearchResult": + cls = kwargs.pop('cls', None) # type: ClsType["_models.PhoneNumberSearchResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self._search_available_phone_numbers_initial.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'countryCode': self._serialize.url("country_code", country_code, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(body, 'PhoneNumberSearchRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['search-id']=self._deserialize('str', response.headers.get('search-id')) + deserialized = self._deserialize('PhoneNumberSearchResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + _search_available_phone_numbers_initial.metadata = {'url': '/availablePhoneNumbers/countries/{countryCode}/:search'} # type: ignore + + async def begin_search_available_phone_numbers( + self, + country_code: str, + body: "_models.PhoneNumberSearchRequest", + **kwargs: Any + ) -> AsyncLROPoller["_models.PhoneNumberSearchResult"]: + """Search for available phone numbers to purchase. + + Search for available phone numbers to purchase. + + :param country_code: The ISO 3166-2 country code, e.g. US. + :type country_code: str + :param body: The phone number search request. + :type body: ~azure.communication.phonenumbers.models.PhoneNumberSearchRequest + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be AsyncLROBasePolling. + Pass in False for this operation to not poll, or pass in your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.AsyncPollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + :return: An instance of AsyncLROPoller that returns either PhoneNumberSearchResult or the result of cls(response) + :rtype: ~azure.core.polling.AsyncLROPoller[~azure.communication.phonenumbers.models.PhoneNumberSearchResult] + :raises ~azure.core.exceptions.HttpResponseError: + """ + polling = kwargs.pop('polling', True) # type: Union[bool, AsyncPollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType["_models.PhoneNumberSearchResult"] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = await self._search_available_phone_numbers_initial( + country_code=country_code, + body=body, + cls=lambda x,y,z: x, + **kwargs + ) + + kwargs.pop('error_map', None) + kwargs.pop('content_type', None) + + def get_long_running_output(pipeline_response): + response_headers = {} + response = pipeline_response.http_response + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['search-id']=self._deserialize('str', response.headers.get('search-id')) + deserialized = self._deserialize('PhoneNumberSearchResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + return deserialized + + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'countryCode': self._serialize.url("country_code", country_code, 'str'), + } + + if polling is True: polling_method = AsyncLROBasePolling(lro_delay, lro_options={'final-state-via': 'location'}, path_format_arguments=path_format_arguments, **kwargs) + elif polling is False: polling_method = AsyncNoPolling() + else: polling_method = polling + if cont_token: + return AsyncLROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return AsyncLROPoller(self._client, raw_result, get_long_running_output, polling_method) + begin_search_available_phone_numbers.metadata = {'url': '/availablePhoneNumbers/countries/{countryCode}/:search'} # type: ignore + + async def get_search_result( + self, + search_id: str, + **kwargs: Any + ) -> "_models.PhoneNumberSearchResult": + """Gets a phone number search result by search id. + + Gets a phone number search result by search id. + + :param search_id: The search Id. + :type search_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: PhoneNumberSearchResult, or the result of cls(response) + :rtype: ~azure.communication.phonenumbers.models.PhoneNumberSearchResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.PhoneNumberSearchResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.get_search_result.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'searchId': self._serialize.url("search_id", search_id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + deserialized = self._deserialize('PhoneNumberSearchResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + get_search_result.metadata = {'url': '/availablePhoneNumbers/searchResults/{searchId}'} # type: ignore + + async def _purchase_phone_numbers_initial( + self, + search_id: Optional[str] = None, + **kwargs: Any + ) -> None: + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _body = _models.PhoneNumberPurchaseRequest(search_id=search_id) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self._purchase_phone_numbers_initial.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(_body, 'PhoneNumberPurchaseRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['purchase-id']=self._deserialize('str', response.headers.get('purchase-id')) + + if cls: + return cls(pipeline_response, None, response_headers) + + _purchase_phone_numbers_initial.metadata = {'url': '/availablePhoneNumbers/:purchase'} # type: ignore + + async def begin_purchase_phone_numbers( + self, + search_id: Optional[str] = None, + **kwargs: Any + ) -> AsyncLROPoller[None]: + """Purchases phone numbers. + + Purchases phone numbers. + + :param search_id: The search id. + :type search_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be AsyncLROBasePolling. + Pass in False for this operation to not poll, or pass in your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.AsyncPollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + :return: An instance of AsyncLROPoller that returns either None or the result of cls(response) + :rtype: ~azure.core.polling.AsyncLROPoller[None] + :raises ~azure.core.exceptions.HttpResponseError: + """ + polling = kwargs.pop('polling', True) # type: Union[bool, AsyncPollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType[None] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = await self._purchase_phone_numbers_initial( + search_id=search_id, + cls=lambda x,y,z: x, + **kwargs + ) + + kwargs.pop('error_map', None) + kwargs.pop('content_type', None) + + def get_long_running_output(pipeline_response): + if cls: + return cls(pipeline_response, None, {}) + + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + + if polling is True: polling_method = AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) + elif polling is False: polling_method = AsyncNoPolling() + else: polling_method = polling + if cont_token: + return AsyncLROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return AsyncLROPoller(self._client, raw_result, get_long_running_output, polling_method) + begin_purchase_phone_numbers.metadata = {'url': '/availablePhoneNumbers/:purchase'} # type: ignore + + async def get_operation( + self, + operation_id: str, + **kwargs: Any + ) -> "_models.PhoneNumberOperation": + """Gets an operation by its id. + + Gets an operation by its id. + + :param operation_id: The id of the operation. + :type operation_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: PhoneNumberOperation, or the result of cls(response) + :rtype: ~azure.communication.phonenumbers.models.PhoneNumberOperation + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.PhoneNumberOperation"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.get_operation.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'operationId': self._serialize.url("operation_id", operation_id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + deserialized = self._deserialize('PhoneNumberOperation', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + get_operation.metadata = {'url': '/phoneNumbers/operations/{operationId}'} # type: ignore + + async def cancel_operation( + self, + operation_id: str, + **kwargs: Any + ) -> None: + """Cancels an operation by its id. + + Cancels an operation by its id. + + :param operation_id: The id of the operation. + :type operation_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None, or the result of cls(response) + :rtype: None + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.cancel_operation.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'operationId': self._serialize.url("operation_id", operation_id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.delete(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + if cls: + return cls(pipeline_response, None, {}) + + cancel_operation.metadata = {'url': '/phoneNumbers/operations/{operationId}'} # type: ignore + + async def _update_capabilities_initial( + self, + phone_number: str, + calling: Optional[Union[str, "_models.PhoneNumberCapabilityType"]] = None, + sms: Optional[Union[str, "_models.PhoneNumberCapabilityType"]] = None, + **kwargs: Any + ) -> "_models.PurchasedPhoneNumber": + cls = kwargs.pop('cls', None) # type: ClsType["_models.PurchasedPhoneNumber"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _body = _models.PhoneNumberCapabilitiesRequest(calling=calling, sms=sms) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/merge-patch+json") + accept = "application/json" + + # Construct URL + url = self._update_capabilities_initial.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + if _body is not None: + body_content = self._serialize.body(_body, 'PhoneNumberCapabilitiesRequest') + else: + body_content = None + body_content_kwargs['content'] = body_content + request = self._client.patch(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['capabilities-id']=self._deserialize('str', response.headers.get('capabilities-id')) + deserialized = self._deserialize('PurchasedPhoneNumber', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + _update_capabilities_initial.metadata = {'url': '/phoneNumbers/{phoneNumber}/capabilities'} # type: ignore + + async def begin_update_capabilities( + self, + phone_number: str, + calling: Optional[Union[str, "_models.PhoneNumberCapabilityType"]] = None, + sms: Optional[Union[str, "_models.PhoneNumberCapabilityType"]] = None, + **kwargs: Any + ) -> AsyncLROPoller["_models.PurchasedPhoneNumber"]: + """Updates the capabilities of a phone number. + + Updates the capabilities of a phone number. + + :param phone_number: The phone number id in E.164 format. The leading plus can be either + or + encoded as %2B, e.g. +11234567890. + :type phone_number: str + :param calling: Capability value for calling. + :type calling: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :param sms: Capability value for SMS. + :type sms: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be AsyncLROBasePolling. + Pass in False for this operation to not poll, or pass in your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.AsyncPollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + :return: An instance of AsyncLROPoller that returns either PurchasedPhoneNumber or the result of cls(response) + :rtype: ~azure.core.polling.AsyncLROPoller[~azure.communication.phonenumbers.models.PurchasedPhoneNumber] + :raises ~azure.core.exceptions.HttpResponseError: + """ + polling = kwargs.pop('polling', True) # type: Union[bool, AsyncPollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType["_models.PurchasedPhoneNumber"] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = await self._update_capabilities_initial( + phone_number=phone_number, + calling=calling, + sms=sms, + cls=lambda x,y,z: x, + **kwargs + ) + + kwargs.pop('error_map', None) + kwargs.pop('content_type', None) + + def get_long_running_output(pipeline_response): + response_headers = {} + response = pipeline_response.http_response + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['capabilities-id']=self._deserialize('str', response.headers.get('capabilities-id')) + deserialized = self._deserialize('PurchasedPhoneNumber', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + return deserialized + + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + + if polling is True: polling_method = AsyncLROBasePolling(lro_delay, lro_options={'final-state-via': 'location'}, path_format_arguments=path_format_arguments, **kwargs) + elif polling is False: polling_method = AsyncNoPolling() + else: polling_method = polling + if cont_token: + return AsyncLROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return AsyncLROPoller(self._client, raw_result, get_long_running_output, polling_method) + begin_update_capabilities.metadata = {'url': '/phoneNumbers/{phoneNumber}/capabilities'} # type: ignore + + async def get_by_number( + self, + phone_number: str, + **kwargs: Any + ) -> "_models.PurchasedPhoneNumber": + """Gets the details of the given purchased phone number. + + Gets the details of the given purchased phone number. + + :param phone_number: The purchased phone number whose details are to be fetched in E.164 + format, e.g. +11234567890. + :type phone_number: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: PurchasedPhoneNumber, or the result of cls(response) + :rtype: ~azure.communication.phonenumbers.models.PurchasedPhoneNumber + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.PurchasedPhoneNumber"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.get_by_number.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + deserialized = self._deserialize('PurchasedPhoneNumber', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + get_by_number.metadata = {'url': '/phoneNumbers/{phoneNumber}'} # type: ignore + + async def _release_phone_number_initial( + self, + phone_number: str, + **kwargs: Any + ) -> None: + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self._release_phone_number_initial.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.delete(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['release-id']=self._deserialize('str', response.headers.get('release-id')) + + if cls: + return cls(pipeline_response, None, response_headers) + + _release_phone_number_initial.metadata = {'url': '/phoneNumbers/{phoneNumber}'} # type: ignore + + async def begin_release_phone_number( + self, + phone_number: str, + **kwargs: Any + ) -> AsyncLROPoller[None]: + """Releases a purchased phone number. + + Releases a purchased phone number. + + :param phone_number: Phone number to be released, e.g. +11234567890. + :type phone_number: str + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be AsyncLROBasePolling. + Pass in False for this operation to not poll, or pass in your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.AsyncPollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + :return: An instance of AsyncLROPoller that returns either None or the result of cls(response) + :rtype: ~azure.core.polling.AsyncLROPoller[None] + :raises ~azure.core.exceptions.HttpResponseError: + """ + polling = kwargs.pop('polling', True) # type: Union[bool, AsyncPollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType[None] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = await self._release_phone_number_initial( + phone_number=phone_number, + cls=lambda x,y,z: x, + **kwargs + ) + + kwargs.pop('error_map', None) + kwargs.pop('content_type', None) + + def get_long_running_output(pipeline_response): + if cls: + return cls(pipeline_response, None, {}) + + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + + if polling is True: polling_method = AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) + elif polling is False: polling_method = AsyncNoPolling() + else: polling_method = polling + if cont_token: + return AsyncLROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return AsyncLROPoller(self._client, raw_result, get_long_running_output, polling_method) + begin_release_phone_number.metadata = {'url': '/phoneNumbers/{phoneNumber}'} # type: ignore + + def list_phone_numbers( + self, + skip: Optional[int] = 0, + top: Optional[int] = 100, + **kwargs: Any + ) -> AsyncIterable["_models.PurchasedPhoneNumbers"]: + """Gets the list of all purchased phone numbers. + + Gets the list of all purchased phone numbers. + + :param skip: An optional parameter for how many entries to skip, for pagination purposes. The + default value is 0. + :type skip: int + :param top: An optional parameter for how many entries to return, for pagination purposes. The + default value is 100. + :type top: int + :keyword callable cls: A custom type or function that will be passed the direct response + :return: An iterator like instance of either PurchasedPhoneNumbers or the result of cls(response) + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.communication.phonenumbers.models.PurchasedPhoneNumbers] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.PurchasedPhoneNumbers"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + def prepare_request(next_link=None): + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + if not next_link: + # Construct URL + url = self.list_phone_numbers.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + if skip is not None: + query_parameters['skip'] = self._serialize.query("skip", skip, 'int') + if top is not None: + query_parameters['top'] = self._serialize.query("top", top, 'int') + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + else: + url = next_link + query_parameters = {} # type: Dict[str, Any] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + request = self._client.get(url, query_parameters, header_parameters) + return request + + async def extract_data(pipeline_response): + deserialized = self._deserialize('PurchasedPhoneNumbers', pipeline_response) + list_of_elem = deserialized.phone_numbers + if cls: + list_of_elem = cls(list_of_elem) + return deserialized.next_link or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + request = prepare_request(next_link) + + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, model=error) + + return pipeline_response + + return AsyncItemPaged( + get_next, extract_data + ) + list_phone_numbers.metadata = {'url': '/phoneNumbers'} # type: ignore diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/__init__.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/__init__.py new file mode 100644 index 00000000000..78f604eeceb --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/__init__.py @@ -0,0 +1,61 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import CommunicationError + from ._models_py3 import CommunicationErrorResponse + from ._models_py3 import PhoneNumberCapabilities + from ._models_py3 import PhoneNumberCapabilitiesRequest + from ._models_py3 import PhoneNumberCost + from ._models_py3 import PhoneNumberOperation + from ._models_py3 import PhoneNumberPurchaseRequest + from ._models_py3 import PhoneNumberSearchRequest + from ._models_py3 import PhoneNumberSearchResult + from ._models_py3 import PurchasedPhoneNumber + from ._models_py3 import PurchasedPhoneNumbers +except (SyntaxError, ImportError): + from ._models import CommunicationError # type: ignore + from ._models import CommunicationErrorResponse # type: ignore + from ._models import PhoneNumberCapabilities # type: ignore + from ._models import PhoneNumberCapabilitiesRequest # type: ignore + from ._models import PhoneNumberCost # type: ignore + from ._models import PhoneNumberOperation # type: ignore + from ._models import PhoneNumberPurchaseRequest # type: ignore + from ._models import PhoneNumberSearchRequest # type: ignore + from ._models import PhoneNumberSearchResult # type: ignore + from ._models import PurchasedPhoneNumber # type: ignore + from ._models import PurchasedPhoneNumbers # type: ignore + +from ._phone_numbers_client_enums import ( + BillingFrequency, + PhoneNumberAssignmentType, + PhoneNumberCapabilityType, + PhoneNumberOperationStatus, + PhoneNumberOperationType, + PhoneNumberType, +) + +__all__ = [ + 'CommunicationError', + 'CommunicationErrorResponse', + 'PhoneNumberCapabilities', + 'PhoneNumberCapabilitiesRequest', + 'PhoneNumberCost', + 'PhoneNumberOperation', + 'PhoneNumberPurchaseRequest', + 'PhoneNumberSearchRequest', + 'PhoneNumberSearchResult', + 'PurchasedPhoneNumber', + 'PurchasedPhoneNumbers', + 'BillingFrequency', + 'PhoneNumberAssignmentType', + 'PhoneNumberCapabilityType', + 'PhoneNumberOperationStatus', + 'PhoneNumberOperationType', + 'PhoneNumberType', +] diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/_models.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/_models.py new file mode 100644 index 00000000000..3f2998259f2 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/_models.py @@ -0,0 +1,454 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from azure.core.exceptions import HttpResponseError +import msrest.serialization + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param code: Required. The error code. + :type code: str + :param message: Required. The error message. + :type message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.phonenumbers.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.phonenumbers.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'target': {'key': 'target', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[CommunicationError]'}, + 'inner_error': {'key': 'innererror', 'type': 'CommunicationError'}, + } + + def __init__( + self, + **kwargs + ): + super(CommunicationError, self).__init__(**kwargs) + self.code = kwargs['code'] + self.message = kwargs['message'] + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :param error: Required. The Communication Services error. + :type error: ~azure.communication.phonenumbers.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + 'error': {'key': 'error', 'type': 'CommunicationError'}, + } + + def __init__( + self, + **kwargs + ): + super(CommunicationErrorResponse, self).__init__(**kwargs) + self.error = kwargs['error'] + + +class PhoneNumberCapabilities(msrest.serialization.Model): + """Capabilities of a phone number. + + All required parameters must be populated in order to send to Azure. + + :param calling: Required. Capability value for calling. Possible values include: "none", + "inbound", "outbound", "inbound+outbound". + :type calling: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :param sms: Required. Capability value for SMS. Possible values include: "none", "inbound", + "outbound", "inbound+outbound". + :type sms: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + """ + + _validation = { + 'calling': {'required': True}, + 'sms': {'required': True}, + } + + _attribute_map = { + 'calling': {'key': 'calling', 'type': 'str'}, + 'sms': {'key': 'sms', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(PhoneNumberCapabilities, self).__init__(**kwargs) + self.calling = kwargs['calling'] + self.sms = kwargs['sms'] + + +class PhoneNumberCapabilitiesRequest(msrest.serialization.Model): + """Capabilities of a phone number. + + :param calling: Capability value for calling. Possible values include: "none", "inbound", + "outbound", "inbound+outbound". + :type calling: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :param sms: Capability value for SMS. Possible values include: "none", "inbound", "outbound", + "inbound+outbound". + :type sms: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + """ + + _attribute_map = { + 'calling': {'key': 'calling', 'type': 'str'}, + 'sms': {'key': 'sms', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(PhoneNumberCapabilitiesRequest, self).__init__(**kwargs) + self.calling = kwargs.get('calling', None) + self.sms = kwargs.get('sms', None) + + +class PhoneNumberCost(msrest.serialization.Model): + """The incurred cost for a single phone number. + + All required parameters must be populated in order to send to Azure. + + :param amount: Required. The cost amount. + :type amount: float + :param currency_code: Required. The ISO 4217 currency code for the cost amount, e.g. USD. + :type currency_code: str + :param billing_frequency: Required. The frequency with which the cost gets billed. Possible + values include: "monthly". + :type billing_frequency: str or ~azure.communication.phonenumbers.models.BillingFrequency + """ + + _validation = { + 'amount': {'required': True}, + 'currency_code': {'required': True}, + 'billing_frequency': {'required': True}, + } + + _attribute_map = { + 'amount': {'key': 'amount', 'type': 'float'}, + 'currency_code': {'key': 'currencyCode', 'type': 'str'}, + 'billing_frequency': {'key': 'billingFrequency', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(PhoneNumberCost, self).__init__(**kwargs) + self.amount = kwargs['amount'] + self.currency_code = kwargs['currency_code'] + self.billing_frequency = kwargs['billing_frequency'] + + +class PhoneNumberOperation(msrest.serialization.Model): + """Long running operation. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param status: Required. Status of operation. Possible values include: "notStarted", "running", + "succeeded", "failed". + :type status: str or ~azure.communication.phonenumbers.models.PhoneNumberOperationStatus + :param resource_location: URL for retrieving the result of the operation, if any. + :type resource_location: str + :param created_date_time: Required. The date that the operation was created. + :type created_date_time: ~datetime.datetime + :param error: The Communication Services error. + :type error: ~azure.communication.phonenumbers.models.CommunicationError + :param id: Required. Id of operation. + :type id: str + :param operation_type: Required. The type of operation, e.g. Search. Possible values include: + "purchase", "releasePhoneNumber", "search", "updatePhoneNumberCapabilities". + :type operation_type: str or ~azure.communication.phonenumbers.models.PhoneNumberOperationType + :ivar last_action_date_time: The most recent date that the operation was changed. + :vartype last_action_date_time: ~datetime.datetime + """ + + _validation = { + 'status': {'required': True}, + 'created_date_time': {'required': True}, + 'id': {'required': True}, + 'operation_type': {'required': True}, + 'last_action_date_time': {'readonly': True}, + } + + _attribute_map = { + 'status': {'key': 'status', 'type': 'str'}, + 'resource_location': {'key': 'resourceLocation', 'type': 'str'}, + 'created_date_time': {'key': 'createdDateTime', 'type': 'iso-8601'}, + 'error': {'key': 'error', 'type': 'CommunicationError'}, + 'id': {'key': 'id', 'type': 'str'}, + 'operation_type': {'key': 'operationType', 'type': 'str'}, + 'last_action_date_time': {'key': 'lastActionDateTime', 'type': 'iso-8601'}, + } + + def __init__( + self, + **kwargs + ): + super(PhoneNumberOperation, self).__init__(**kwargs) + self.status = kwargs['status'] + self.resource_location = kwargs.get('resource_location', None) + self.created_date_time = kwargs['created_date_time'] + self.error = kwargs.get('error', None) + self.id = kwargs['id'] + self.operation_type = kwargs['operation_type'] + self.last_action_date_time = None + + +class PhoneNumberPurchaseRequest(msrest.serialization.Model): + """The phone number search purchase request. + + :param search_id: The search id. + :type search_id: str + """ + + _attribute_map = { + 'search_id': {'key': 'searchId', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(PhoneNumberPurchaseRequest, self).__init__(**kwargs) + self.search_id = kwargs.get('search_id', None) + + +class PhoneNumberSearchRequest(msrest.serialization.Model): + """Represents a phone number search request to find phone numbers. Found phone numbers are temporarily held for a following purchase. + + All required parameters must be populated in order to send to Azure. + + :param phone_number_type: Required. The type of phone numbers to search for, e.g. geographic, + or tollFree. Possible values include: "geographic", "tollFree". + :type phone_number_type: str or ~azure.communication.phonenumbers.models.PhoneNumberType + :param assignment_type: Required. The assignment type of the phone numbers to search for. A + phone number can be assigned to a person, or to an application. Possible values include: + "person", "application". + :type assignment_type: str or + ~azure.communication.phonenumbers.models.PhoneNumberAssignmentType + :param capabilities: Required. Capabilities of a phone number. + :type capabilities: ~azure.communication.phonenumbers.models.PhoneNumberCapabilities + :param area_code: The area code of the desired phone number, e.g. 425. + :type area_code: str + :param quantity: The quantity of desired phone numbers. The default value is 1. + :type quantity: int + """ + + _validation = { + 'phone_number_type': {'required': True}, + 'assignment_type': {'required': True}, + 'capabilities': {'required': True}, + 'quantity': {'maximum': 2147483647, 'minimum': 1}, + } + + _attribute_map = { + 'phone_number_type': {'key': 'phoneNumberType', 'type': 'str'}, + 'assignment_type': {'key': 'assignmentType', 'type': 'str'}, + 'capabilities': {'key': 'capabilities', 'type': 'PhoneNumberCapabilities'}, + 'area_code': {'key': 'areaCode', 'type': 'str'}, + 'quantity': {'key': 'quantity', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(PhoneNumberSearchRequest, self).__init__(**kwargs) + self.phone_number_type = kwargs['phone_number_type'] + self.assignment_type = kwargs['assignment_type'] + self.capabilities = kwargs['capabilities'] + self.area_code = kwargs.get('area_code', None) + self.quantity = kwargs.get('quantity', 1) + + +class PhoneNumberSearchResult(msrest.serialization.Model): + """The result of a phone number search operation. + + All required parameters must be populated in order to send to Azure. + + :param search_id: Required. The search id. + :type search_id: str + :param phone_numbers: Required. The phone numbers that are available. Can be fewer than the + desired search quantity. + :type phone_numbers: list[str] + :param phone_number_type: Required. The phone number's type, e.g. geographic, or tollFree. + Possible values include: "geographic", "tollFree". + :type phone_number_type: str or ~azure.communication.phonenumbers.models.PhoneNumberType + :param assignment_type: Required. Phone number's assignment type. Possible values include: + "person", "application". + :type assignment_type: str or + ~azure.communication.phonenumbers.models.PhoneNumberAssignmentType + :param capabilities: Required. Capabilities of a phone number. + :type capabilities: ~azure.communication.phonenumbers.models.PhoneNumberCapabilities + :param cost: Required. The incurred cost for a single phone number. + :type cost: ~azure.communication.phonenumbers.models.PhoneNumberCost + :param search_expires_by: Required. The date that this search result expires and phone numbers + are no longer on hold. A search result expires in less than 15min, e.g. + 2020-11-19T16:31:49.048Z. + :type search_expires_by: ~datetime.datetime + """ + + _validation = { + 'search_id': {'required': True}, + 'phone_numbers': {'required': True}, + 'phone_number_type': {'required': True}, + 'assignment_type': {'required': True}, + 'capabilities': {'required': True}, + 'cost': {'required': True}, + 'search_expires_by': {'required': True}, + } + + _attribute_map = { + 'search_id': {'key': 'searchId', 'type': 'str'}, + 'phone_numbers': {'key': 'phoneNumbers', 'type': '[str]'}, + 'phone_number_type': {'key': 'phoneNumberType', 'type': 'str'}, + 'assignment_type': {'key': 'assignmentType', 'type': 'str'}, + 'capabilities': {'key': 'capabilities', 'type': 'PhoneNumberCapabilities'}, + 'cost': {'key': 'cost', 'type': 'PhoneNumberCost'}, + 'search_expires_by': {'key': 'searchExpiresBy', 'type': 'iso-8601'}, + } + + def __init__( + self, + **kwargs + ): + super(PhoneNumberSearchResult, self).__init__(**kwargs) + self.search_id = kwargs['search_id'] + self.phone_numbers = kwargs['phone_numbers'] + self.phone_number_type = kwargs['phone_number_type'] + self.assignment_type = kwargs['assignment_type'] + self.capabilities = kwargs['capabilities'] + self.cost = kwargs['cost'] + self.search_expires_by = kwargs['search_expires_by'] + + +class PurchasedPhoneNumber(msrest.serialization.Model): + """Represents a purchased phone number. + + All required parameters must be populated in order to send to Azure. + + :param id: Required. The id of the phone number, e.g. 11234567890. + :type id: str + :param phone_number: Required. String of the E.164 format of the phone number, e.g. + +11234567890. + :type phone_number: str + :param country_code: Required. The ISO 3166-2 code of the phone number's country, e.g. US. + :type country_code: str + :param phone_number_type: Required. The phone number's type, e.g. Geographic, TollFree. + Possible values include: "geographic", "tollFree". + :type phone_number_type: str or ~azure.communication.phonenumbers.models.PhoneNumberType + :param capabilities: Required. Capabilities of a phone number. + :type capabilities: ~azure.communication.phonenumbers.models.PhoneNumberCapabilities + :param assignment_type: Required. The assignment type of the phone number. A phone number can + be assigned to a person, or to an application. Possible values include: "person", + "application". + :type assignment_type: str or + ~azure.communication.phonenumbers.models.PhoneNumberAssignmentType + :param purchase_date: Required. The date and time that the phone number was purchased. + :type purchase_date: ~datetime.datetime + :param cost: Required. The incurred cost for a single phone number. + :type cost: ~azure.communication.phonenumbers.models.PhoneNumberCost + """ + + _validation = { + 'id': {'required': True}, + 'phone_number': {'required': True}, + 'country_code': {'required': True}, + 'phone_number_type': {'required': True}, + 'capabilities': {'required': True}, + 'assignment_type': {'required': True}, + 'purchase_date': {'required': True}, + 'cost': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'phone_number': {'key': 'phoneNumber', 'type': 'str'}, + 'country_code': {'key': 'countryCode', 'type': 'str'}, + 'phone_number_type': {'key': 'phoneNumberType', 'type': 'str'}, + 'capabilities': {'key': 'capabilities', 'type': 'PhoneNumberCapabilities'}, + 'assignment_type': {'key': 'assignmentType', 'type': 'str'}, + 'purchase_date': {'key': 'purchaseDate', 'type': 'iso-8601'}, + 'cost': {'key': 'cost', 'type': 'PhoneNumberCost'}, + } + + def __init__( + self, + **kwargs + ): + super(PurchasedPhoneNumber, self).__init__(**kwargs) + self.id = kwargs['id'] + self.phone_number = kwargs['phone_number'] + self.country_code = kwargs['country_code'] + self.phone_number_type = kwargs['phone_number_type'] + self.capabilities = kwargs['capabilities'] + self.assignment_type = kwargs['assignment_type'] + self.purchase_date = kwargs['purchase_date'] + self.cost = kwargs['cost'] + + +class PurchasedPhoneNumbers(msrest.serialization.Model): + """The list of purchased phone numbers. + + All required parameters must be populated in order to send to Azure. + + :param phone_numbers: Required. Represents a list of phone numbers. + :type phone_numbers: list[~azure.communication.phonenumbers.models.PurchasedPhoneNumber] + :param next_link: Represents the URL link to the next page of phone number results. + :type next_link: str + """ + + _validation = { + 'phone_numbers': {'required': True}, + } + + _attribute_map = { + 'phone_numbers': {'key': 'phoneNumbers', 'type': '[PurchasedPhoneNumber]'}, + 'next_link': {'key': 'nextLink', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(PurchasedPhoneNumbers, self).__init__(**kwargs) + self.phone_numbers = kwargs['phone_numbers'] + self.next_link = kwargs.get('next_link', None) diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/_models_py3.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/_models_py3.py new file mode 100644 index 00000000000..ba87df4d5c6 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/_models_py3.py @@ -0,0 +1,509 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +import datetime +from typing import List, Optional, Union + +from azure.core.exceptions import HttpResponseError +import msrest.serialization + +from ._phone_numbers_client_enums import * + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param code: Required. The error code. + :type code: str + :param message: Required. The error message. + :type message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.phonenumbers.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.phonenumbers.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'target': {'key': 'target', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[CommunicationError]'}, + 'inner_error': {'key': 'innererror', 'type': 'CommunicationError'}, + } + + def __init__( + self, + *, + code: str, + message: str, + **kwargs + ): + super(CommunicationError, self).__init__(**kwargs) + self.code = code + self.message = message + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :param error: Required. The Communication Services error. + :type error: ~azure.communication.phonenumbers.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + 'error': {'key': 'error', 'type': 'CommunicationError'}, + } + + def __init__( + self, + *, + error: "CommunicationError", + **kwargs + ): + super(CommunicationErrorResponse, self).__init__(**kwargs) + self.error = error + + +class PhoneNumberCapabilities(msrest.serialization.Model): + """Capabilities of a phone number. + + All required parameters must be populated in order to send to Azure. + + :param calling: Required. Capability value for calling. Possible values include: "none", + "inbound", "outbound", "inbound+outbound". + :type calling: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :param sms: Required. Capability value for SMS. Possible values include: "none", "inbound", + "outbound", "inbound+outbound". + :type sms: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + """ + + _validation = { + 'calling': {'required': True}, + 'sms': {'required': True}, + } + + _attribute_map = { + 'calling': {'key': 'calling', 'type': 'str'}, + 'sms': {'key': 'sms', 'type': 'str'}, + } + + def __init__( + self, + *, + calling: Union[str, "PhoneNumberCapabilityType"], + sms: Union[str, "PhoneNumberCapabilityType"], + **kwargs + ): + super(PhoneNumberCapabilities, self).__init__(**kwargs) + self.calling = calling + self.sms = sms + + +class PhoneNumberCapabilitiesRequest(msrest.serialization.Model): + """Capabilities of a phone number. + + :param calling: Capability value for calling. Possible values include: "none", "inbound", + "outbound", "inbound+outbound". + :type calling: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :param sms: Capability value for SMS. Possible values include: "none", "inbound", "outbound", + "inbound+outbound". + :type sms: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + """ + + _attribute_map = { + 'calling': {'key': 'calling', 'type': 'str'}, + 'sms': {'key': 'sms', 'type': 'str'}, + } + + def __init__( + self, + *, + calling: Optional[Union[str, "PhoneNumberCapabilityType"]] = None, + sms: Optional[Union[str, "PhoneNumberCapabilityType"]] = None, + **kwargs + ): + super(PhoneNumberCapabilitiesRequest, self).__init__(**kwargs) + self.calling = calling + self.sms = sms + + +class PhoneNumberCost(msrest.serialization.Model): + """The incurred cost for a single phone number. + + All required parameters must be populated in order to send to Azure. + + :param amount: Required. The cost amount. + :type amount: float + :param currency_code: Required. The ISO 4217 currency code for the cost amount, e.g. USD. + :type currency_code: str + :param billing_frequency: Required. The frequency with which the cost gets billed. Possible + values include: "monthly". + :type billing_frequency: str or ~azure.communication.phonenumbers.models.BillingFrequency + """ + + _validation = { + 'amount': {'required': True}, + 'currency_code': {'required': True}, + 'billing_frequency': {'required': True}, + } + + _attribute_map = { + 'amount': {'key': 'amount', 'type': 'float'}, + 'currency_code': {'key': 'currencyCode', 'type': 'str'}, + 'billing_frequency': {'key': 'billingFrequency', 'type': 'str'}, + } + + def __init__( + self, + *, + amount: float, + currency_code: str, + billing_frequency: Union[str, "BillingFrequency"], + **kwargs + ): + super(PhoneNumberCost, self).__init__(**kwargs) + self.amount = amount + self.currency_code = currency_code + self.billing_frequency = billing_frequency + + +class PhoneNumberOperation(msrest.serialization.Model): + """Long running operation. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param status: Required. Status of operation. Possible values include: "notStarted", "running", + "succeeded", "failed". + :type status: str or ~azure.communication.phonenumbers.models.PhoneNumberOperationStatus + :param resource_location: URL for retrieving the result of the operation, if any. + :type resource_location: str + :param created_date_time: Required. The date that the operation was created. + :type created_date_time: ~datetime.datetime + :param error: The Communication Services error. + :type error: ~azure.communication.phonenumbers.models.CommunicationError + :param id: Required. Id of operation. + :type id: str + :param operation_type: Required. The type of operation, e.g. Search. Possible values include: + "purchase", "releasePhoneNumber", "search", "updatePhoneNumberCapabilities". + :type operation_type: str or ~azure.communication.phonenumbers.models.PhoneNumberOperationType + :ivar last_action_date_time: The most recent date that the operation was changed. + :vartype last_action_date_time: ~datetime.datetime + """ + + _validation = { + 'status': {'required': True}, + 'created_date_time': {'required': True}, + 'id': {'required': True}, + 'operation_type': {'required': True}, + 'last_action_date_time': {'readonly': True}, + } + + _attribute_map = { + 'status': {'key': 'status', 'type': 'str'}, + 'resource_location': {'key': 'resourceLocation', 'type': 'str'}, + 'created_date_time': {'key': 'createdDateTime', 'type': 'iso-8601'}, + 'error': {'key': 'error', 'type': 'CommunicationError'}, + 'id': {'key': 'id', 'type': 'str'}, + 'operation_type': {'key': 'operationType', 'type': 'str'}, + 'last_action_date_time': {'key': 'lastActionDateTime', 'type': 'iso-8601'}, + } + + def __init__( + self, + *, + status: Union[str, "PhoneNumberOperationStatus"], + created_date_time: datetime.datetime, + id: str, + operation_type: Union[str, "PhoneNumberOperationType"], + resource_location: Optional[str] = None, + error: Optional["CommunicationError"] = None, + **kwargs + ): + super(PhoneNumberOperation, self).__init__(**kwargs) + self.status = status + self.resource_location = resource_location + self.created_date_time = created_date_time + self.error = error + self.id = id + self.operation_type = operation_type + self.last_action_date_time = None + + +class PhoneNumberPurchaseRequest(msrest.serialization.Model): + """The phone number search purchase request. + + :param search_id: The search id. + :type search_id: str + """ + + _attribute_map = { + 'search_id': {'key': 'searchId', 'type': 'str'}, + } + + def __init__( + self, + *, + search_id: Optional[str] = None, + **kwargs + ): + super(PhoneNumberPurchaseRequest, self).__init__(**kwargs) + self.search_id = search_id + + +class PhoneNumberSearchRequest(msrest.serialization.Model): + """Represents a phone number search request to find phone numbers. Found phone numbers are temporarily held for a following purchase. + + All required parameters must be populated in order to send to Azure. + + :param phone_number_type: Required. The type of phone numbers to search for, e.g. geographic, + or tollFree. Possible values include: "geographic", "tollFree". + :type phone_number_type: str or ~azure.communication.phonenumbers.models.PhoneNumberType + :param assignment_type: Required. The assignment type of the phone numbers to search for. A + phone number can be assigned to a person, or to an application. Possible values include: + "person", "application". + :type assignment_type: str or + ~azure.communication.phonenumbers.models.PhoneNumberAssignmentType + :param capabilities: Required. Capabilities of a phone number. + :type capabilities: ~azure.communication.phonenumbers.models.PhoneNumberCapabilities + :param area_code: The area code of the desired phone number, e.g. 425. + :type area_code: str + :param quantity: The quantity of desired phone numbers. The default value is 1. + :type quantity: int + """ + + _validation = { + 'phone_number_type': {'required': True}, + 'assignment_type': {'required': True}, + 'capabilities': {'required': True}, + 'quantity': {'maximum': 2147483647, 'minimum': 1}, + } + + _attribute_map = { + 'phone_number_type': {'key': 'phoneNumberType', 'type': 'str'}, + 'assignment_type': {'key': 'assignmentType', 'type': 'str'}, + 'capabilities': {'key': 'capabilities', 'type': 'PhoneNumberCapabilities'}, + 'area_code': {'key': 'areaCode', 'type': 'str'}, + 'quantity': {'key': 'quantity', 'type': 'int'}, + } + + def __init__( + self, + *, + phone_number_type: Union[str, "PhoneNumberType"], + assignment_type: Union[str, "PhoneNumberAssignmentType"], + capabilities: "PhoneNumberCapabilities", + area_code: Optional[str] = None, + quantity: Optional[int] = 1, + **kwargs + ): + super(PhoneNumberSearchRequest, self).__init__(**kwargs) + self.phone_number_type = phone_number_type + self.assignment_type = assignment_type + self.capabilities = capabilities + self.area_code = area_code + self.quantity = quantity + + +class PhoneNumberSearchResult(msrest.serialization.Model): + """The result of a phone number search operation. + + All required parameters must be populated in order to send to Azure. + + :param search_id: Required. The search id. + :type search_id: str + :param phone_numbers: Required. The phone numbers that are available. Can be fewer than the + desired search quantity. + :type phone_numbers: list[str] + :param phone_number_type: Required. The phone number's type, e.g. geographic, or tollFree. + Possible values include: "geographic", "tollFree". + :type phone_number_type: str or ~azure.communication.phonenumbers.models.PhoneNumberType + :param assignment_type: Required. Phone number's assignment type. Possible values include: + "person", "application". + :type assignment_type: str or + ~azure.communication.phonenumbers.models.PhoneNumberAssignmentType + :param capabilities: Required. Capabilities of a phone number. + :type capabilities: ~azure.communication.phonenumbers.models.PhoneNumberCapabilities + :param cost: Required. The incurred cost for a single phone number. + :type cost: ~azure.communication.phonenumbers.models.PhoneNumberCost + :param search_expires_by: Required. The date that this search result expires and phone numbers + are no longer on hold. A search result expires in less than 15min, e.g. + 2020-11-19T16:31:49.048Z. + :type search_expires_by: ~datetime.datetime + """ + + _validation = { + 'search_id': {'required': True}, + 'phone_numbers': {'required': True}, + 'phone_number_type': {'required': True}, + 'assignment_type': {'required': True}, + 'capabilities': {'required': True}, + 'cost': {'required': True}, + 'search_expires_by': {'required': True}, + } + + _attribute_map = { + 'search_id': {'key': 'searchId', 'type': 'str'}, + 'phone_numbers': {'key': 'phoneNumbers', 'type': '[str]'}, + 'phone_number_type': {'key': 'phoneNumberType', 'type': 'str'}, + 'assignment_type': {'key': 'assignmentType', 'type': 'str'}, + 'capabilities': {'key': 'capabilities', 'type': 'PhoneNumberCapabilities'}, + 'cost': {'key': 'cost', 'type': 'PhoneNumberCost'}, + 'search_expires_by': {'key': 'searchExpiresBy', 'type': 'iso-8601'}, + } + + def __init__( + self, + *, + search_id: str, + phone_numbers: List[str], + phone_number_type: Union[str, "PhoneNumberType"], + assignment_type: Union[str, "PhoneNumberAssignmentType"], + capabilities: "PhoneNumberCapabilities", + cost: "PhoneNumberCost", + search_expires_by: datetime.datetime, + **kwargs + ): + super(PhoneNumberSearchResult, self).__init__(**kwargs) + self.search_id = search_id + self.phone_numbers = phone_numbers + self.phone_number_type = phone_number_type + self.assignment_type = assignment_type + self.capabilities = capabilities + self.cost = cost + self.search_expires_by = search_expires_by + + +class PurchasedPhoneNumber(msrest.serialization.Model): + """Represents a purchased phone number. + + All required parameters must be populated in order to send to Azure. + + :param id: Required. The id of the phone number, e.g. 11234567890. + :type id: str + :param phone_number: Required. String of the E.164 format of the phone number, e.g. + +11234567890. + :type phone_number: str + :param country_code: Required. The ISO 3166-2 code of the phone number's country, e.g. US. + :type country_code: str + :param phone_number_type: Required. The phone number's type, e.g. Geographic, TollFree. + Possible values include: "geographic", "tollFree". + :type phone_number_type: str or ~azure.communication.phonenumbers.models.PhoneNumberType + :param capabilities: Required. Capabilities of a phone number. + :type capabilities: ~azure.communication.phonenumbers.models.PhoneNumberCapabilities + :param assignment_type: Required. The assignment type of the phone number. A phone number can + be assigned to a person, or to an application. Possible values include: "person", + "application". + :type assignment_type: str or + ~azure.communication.phonenumbers.models.PhoneNumberAssignmentType + :param purchase_date: Required. The date and time that the phone number was purchased. + :type purchase_date: ~datetime.datetime + :param cost: Required. The incurred cost for a single phone number. + :type cost: ~azure.communication.phonenumbers.models.PhoneNumberCost + """ + + _validation = { + 'id': {'required': True}, + 'phone_number': {'required': True}, + 'country_code': {'required': True}, + 'phone_number_type': {'required': True}, + 'capabilities': {'required': True}, + 'assignment_type': {'required': True}, + 'purchase_date': {'required': True}, + 'cost': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'phone_number': {'key': 'phoneNumber', 'type': 'str'}, + 'country_code': {'key': 'countryCode', 'type': 'str'}, + 'phone_number_type': {'key': 'phoneNumberType', 'type': 'str'}, + 'capabilities': {'key': 'capabilities', 'type': 'PhoneNumberCapabilities'}, + 'assignment_type': {'key': 'assignmentType', 'type': 'str'}, + 'purchase_date': {'key': 'purchaseDate', 'type': 'iso-8601'}, + 'cost': {'key': 'cost', 'type': 'PhoneNumberCost'}, + } + + def __init__( + self, + *, + id: str, + phone_number: str, + country_code: str, + phone_number_type: Union[str, "PhoneNumberType"], + capabilities: "PhoneNumberCapabilities", + assignment_type: Union[str, "PhoneNumberAssignmentType"], + purchase_date: datetime.datetime, + cost: "PhoneNumberCost", + **kwargs + ): + super(PurchasedPhoneNumber, self).__init__(**kwargs) + self.id = id + self.phone_number = phone_number + self.country_code = country_code + self.phone_number_type = phone_number_type + self.capabilities = capabilities + self.assignment_type = assignment_type + self.purchase_date = purchase_date + self.cost = cost + + +class PurchasedPhoneNumbers(msrest.serialization.Model): + """The list of purchased phone numbers. + + All required parameters must be populated in order to send to Azure. + + :param phone_numbers: Required. Represents a list of phone numbers. + :type phone_numbers: list[~azure.communication.phonenumbers.models.PurchasedPhoneNumber] + :param next_link: Represents the URL link to the next page of phone number results. + :type next_link: str + """ + + _validation = { + 'phone_numbers': {'required': True}, + } + + _attribute_map = { + 'phone_numbers': {'key': 'phoneNumbers', 'type': '[PurchasedPhoneNumber]'}, + 'next_link': {'key': 'nextLink', 'type': 'str'}, + } + + def __init__( + self, + *, + phone_numbers: List["PurchasedPhoneNumber"], + next_link: Optional[str] = None, + **kwargs + ): + super(PurchasedPhoneNumbers, self).__init__(**kwargs) + self.phone_numbers = phone_numbers + self.next_link = next_link diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/_phone_numbers_client_enums.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/_phone_numbers_client_enums.py new file mode 100644 index 00000000000..3e1c41cf562 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/models/_phone_numbers_client_enums.py @@ -0,0 +1,75 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum, EnumMeta +from six import with_metaclass + +class _CaseInsensitiveEnumMeta(EnumMeta): + def __getitem__(self, name): + return super().__getitem__(name.upper()) + + def __getattr__(cls, name): + """Return the enum member matching `name` + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + """ + try: + return cls._member_map_[name.upper()] + except KeyError: + raise AttributeError(name) + + +class BillingFrequency(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """The frequency with which the cost gets billed. + """ + + MONTHLY = "monthly" + +class PhoneNumberAssignmentType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """The assignment type of the phone numbers to search for. A phone number can be assigned to a + person, or to an application. + """ + + PERSON = "person" + APPLICATION = "application" + +class PhoneNumberCapabilityType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """Capability value for calling. + """ + + NONE = "none" + INBOUND = "inbound" + OUTBOUND = "outbound" + INBOUND_OUTBOUND = "inbound+outbound" + +class PhoneNumberOperationStatus(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """Status of operation. + """ + + NOT_STARTED = "notStarted" + RUNNING = "running" + SUCCEEDED = "succeeded" + FAILED = "failed" + +class PhoneNumberOperationType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """The type of operation, e.g. Search + """ + + PURCHASE = "purchase" + RELEASE_PHONE_NUMBER = "releasePhoneNumber" + SEARCH = "search" + UPDATE_PHONE_NUMBER_CAPABILITIES = "updatePhoneNumberCapabilities" + +class PhoneNumberType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """The type of phone numbers to search for, e.g. geographic, or tollFree. + """ + + GEOGRAPHIC = "geographic" + TOLL_FREE = "tollFree" diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/operations/__init__.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/operations/__init__.py new file mode 100644 index 00000000000..3da9bcb904f --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/operations/__init__.py @@ -0,0 +1,13 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._phone_numbers_operations import PhoneNumbersOperations + +__all__ = [ + 'PhoneNumbersOperations', +] diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/operations/_phone_numbers_operations.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/operations/_phone_numbers_operations.py new file mode 100644 index 00000000000..8ded33e4a46 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_generated/operations/_phone_numbers_operations.py @@ -0,0 +1,869 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.paging import ItemPaged +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpRequest, HttpResponse +from azure.core.polling import LROPoller, NoPolling, PollingMethod +from azure.core.polling.base_polling import LROBasePolling + +from .. import models as _models + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Generic, Iterable, Optional, TypeVar, Union + + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +class PhoneNumbersOperations(object): + """PhoneNumbersOperations operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.communication.phonenumbers.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer): + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + def _search_available_phone_numbers_initial( + self, + country_code, # type: str + body, # type: "_models.PhoneNumberSearchRequest" + **kwargs # type: Any + ): + # type: (...) -> "_models.PhoneNumberSearchResult" + cls = kwargs.pop('cls', None) # type: ClsType["_models.PhoneNumberSearchResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self._search_available_phone_numbers_initial.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'countryCode': self._serialize.url("country_code", country_code, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(body, 'PhoneNumberSearchRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['search-id']=self._deserialize('str', response.headers.get('search-id')) + deserialized = self._deserialize('PhoneNumberSearchResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + _search_available_phone_numbers_initial.metadata = {'url': '/availablePhoneNumbers/countries/{countryCode}/:search'} # type: ignore + + def begin_search_available_phone_numbers( + self, + country_code, # type: str + body, # type: "_models.PhoneNumberSearchRequest" + **kwargs # type: Any + ): + # type: (...) -> LROPoller["_models.PhoneNumberSearchResult"] + """Search for available phone numbers to purchase. + + Search for available phone numbers to purchase. + + :param country_code: The ISO 3166-2 country code, e.g. US. + :type country_code: str + :param body: The phone number search request. + :type body: ~azure.communication.phonenumbers.models.PhoneNumberSearchRequest + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be LROBasePolling. + Pass in False for this operation to not poll, or pass in your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + :return: An instance of LROPoller that returns either PhoneNumberSearchResult or the result of cls(response) + :rtype: ~azure.core.polling.LROPoller[~azure.communication.phonenumbers.models.PhoneNumberSearchResult] + :raises ~azure.core.exceptions.HttpResponseError: + """ + polling = kwargs.pop('polling', True) # type: Union[bool, PollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType["_models.PhoneNumberSearchResult"] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = self._search_available_phone_numbers_initial( + country_code=country_code, + body=body, + cls=lambda x,y,z: x, + **kwargs + ) + + kwargs.pop('error_map', None) + kwargs.pop('content_type', None) + + def get_long_running_output(pipeline_response): + response_headers = {} + response = pipeline_response.http_response + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['search-id']=self._deserialize('str', response.headers.get('search-id')) + deserialized = self._deserialize('PhoneNumberSearchResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + return deserialized + + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'countryCode': self._serialize.url("country_code", country_code, 'str'), + } + + if polling is True: polling_method = LROBasePolling(lro_delay, lro_options={'final-state-via': 'location'}, path_format_arguments=path_format_arguments, **kwargs) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + if cont_token: + return LROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + begin_search_available_phone_numbers.metadata = {'url': '/availablePhoneNumbers/countries/{countryCode}/:search'} # type: ignore + + def get_search_result( + self, + search_id, # type: str + **kwargs # type: Any + ): + # type: (...) -> "_models.PhoneNumberSearchResult" + """Gets a phone number search result by search id. + + Gets a phone number search result by search id. + + :param search_id: The search Id. + :type search_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: PhoneNumberSearchResult, or the result of cls(response) + :rtype: ~azure.communication.phonenumbers.models.PhoneNumberSearchResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.PhoneNumberSearchResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.get_search_result.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'searchId': self._serialize.url("search_id", search_id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + deserialized = self._deserialize('PhoneNumberSearchResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + get_search_result.metadata = {'url': '/availablePhoneNumbers/searchResults/{searchId}'} # type: ignore + + def _purchase_phone_numbers_initial( + self, + search_id=None, # type: Optional[str] + **kwargs # type: Any + ): + # type: (...) -> None + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _body = _models.PhoneNumberPurchaseRequest(search_id=search_id) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self._purchase_phone_numbers_initial.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(_body, 'PhoneNumberPurchaseRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['purchase-id']=self._deserialize('str', response.headers.get('purchase-id')) + + if cls: + return cls(pipeline_response, None, response_headers) + + _purchase_phone_numbers_initial.metadata = {'url': '/availablePhoneNumbers/:purchase'} # type: ignore + + def begin_purchase_phone_numbers( + self, + search_id=None, # type: Optional[str] + **kwargs # type: Any + ): + # type: (...) -> LROPoller[None] + """Purchases phone numbers. + + Purchases phone numbers. + + :param search_id: The search id. + :type search_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be LROBasePolling. + Pass in False for this operation to not poll, or pass in your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + :return: An instance of LROPoller that returns either None or the result of cls(response) + :rtype: ~azure.core.polling.LROPoller[None] + :raises ~azure.core.exceptions.HttpResponseError: + """ + polling = kwargs.pop('polling', True) # type: Union[bool, PollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType[None] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = self._purchase_phone_numbers_initial( + search_id=search_id, + cls=lambda x,y,z: x, + **kwargs + ) + + kwargs.pop('error_map', None) + kwargs.pop('content_type', None) + + def get_long_running_output(pipeline_response): + if cls: + return cls(pipeline_response, None, {}) + + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + + if polling is True: polling_method = LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + if cont_token: + return LROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + begin_purchase_phone_numbers.metadata = {'url': '/availablePhoneNumbers/:purchase'} # type: ignore + + def get_operation( + self, + operation_id, # type: str + **kwargs # type: Any + ): + # type: (...) -> "_models.PhoneNumberOperation" + """Gets an operation by its id. + + Gets an operation by its id. + + :param operation_id: The id of the operation. + :type operation_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: PhoneNumberOperation, or the result of cls(response) + :rtype: ~azure.communication.phonenumbers.models.PhoneNumberOperation + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.PhoneNumberOperation"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.get_operation.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'operationId': self._serialize.url("operation_id", operation_id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + deserialized = self._deserialize('PhoneNumberOperation', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + get_operation.metadata = {'url': '/phoneNumbers/operations/{operationId}'} # type: ignore + + def cancel_operation( + self, + operation_id, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + """Cancels an operation by its id. + + Cancels an operation by its id. + + :param operation_id: The id of the operation. + :type operation_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None, or the result of cls(response) + :rtype: None + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.cancel_operation.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'operationId': self._serialize.url("operation_id", operation_id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.delete(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + if cls: + return cls(pipeline_response, None, {}) + + cancel_operation.metadata = {'url': '/phoneNumbers/operations/{operationId}'} # type: ignore + + def _update_capabilities_initial( + self, + phone_number, # type: str + calling=None, # type: Optional[Union[str, "_models.PhoneNumberCapabilityType"]] + sms=None, # type: Optional[Union[str, "_models.PhoneNumberCapabilityType"]] + **kwargs # type: Any + ): + # type: (...) -> "_models.PurchasedPhoneNumber" + cls = kwargs.pop('cls', None) # type: ClsType["_models.PurchasedPhoneNumber"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _body = _models.PhoneNumberCapabilitiesRequest(calling=calling, sms=sms) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/merge-patch+json") + accept = "application/json" + + # Construct URL + url = self._update_capabilities_initial.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + if _body is not None: + body_content = self._serialize.body(_body, 'PhoneNumberCapabilitiesRequest') + else: + body_content = None + body_content_kwargs['content'] = body_content + request = self._client.patch(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['capabilities-id']=self._deserialize('str', response.headers.get('capabilities-id')) + deserialized = self._deserialize('PurchasedPhoneNumber', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + _update_capabilities_initial.metadata = {'url': '/phoneNumbers/{phoneNumber}/capabilities'} # type: ignore + + def begin_update_capabilities( + self, + phone_number, # type: str + calling=None, # type: Optional[Union[str, "_models.PhoneNumberCapabilityType"]] + sms=None, # type: Optional[Union[str, "_models.PhoneNumberCapabilityType"]] + **kwargs # type: Any + ): + # type: (...) -> LROPoller["_models.PurchasedPhoneNumber"] + """Updates the capabilities of a phone number. + + Updates the capabilities of a phone number. + + :param phone_number: The phone number id in E.164 format. The leading plus can be either + or + encoded as %2B, e.g. +11234567890. + :type phone_number: str + :param calling: Capability value for calling. + :type calling: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :param sms: Capability value for SMS. + :type sms: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be LROBasePolling. + Pass in False for this operation to not poll, or pass in your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + :return: An instance of LROPoller that returns either PurchasedPhoneNumber or the result of cls(response) + :rtype: ~azure.core.polling.LROPoller[~azure.communication.phonenumbers.models.PurchasedPhoneNumber] + :raises ~azure.core.exceptions.HttpResponseError: + """ + polling = kwargs.pop('polling', True) # type: Union[bool, PollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType["_models.PurchasedPhoneNumber"] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = self._update_capabilities_initial( + phone_number=phone_number, + calling=calling, + sms=sms, + cls=lambda x,y,z: x, + **kwargs + ) + + kwargs.pop('error_map', None) + kwargs.pop('content_type', None) + + def get_long_running_output(pipeline_response): + response_headers = {} + response = pipeline_response.http_response + response_headers['Location']=self._deserialize('str', response.headers.get('Location')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['capabilities-id']=self._deserialize('str', response.headers.get('capabilities-id')) + deserialized = self._deserialize('PurchasedPhoneNumber', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + return deserialized + + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + + if polling is True: polling_method = LROBasePolling(lro_delay, lro_options={'final-state-via': 'location'}, path_format_arguments=path_format_arguments, **kwargs) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + if cont_token: + return LROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + begin_update_capabilities.metadata = {'url': '/phoneNumbers/{phoneNumber}/capabilities'} # type: ignore + + def get_by_number( + self, + phone_number, # type: str + **kwargs # type: Any + ): + # type: (...) -> "_models.PurchasedPhoneNumber" + """Gets the details of the given purchased phone number. + + Gets the details of the given purchased phone number. + + :param phone_number: The purchased phone number whose details are to be fetched in E.164 + format, e.g. +11234567890. + :type phone_number: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: PurchasedPhoneNumber, or the result of cls(response) + :rtype: ~azure.communication.phonenumbers.models.PurchasedPhoneNumber + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.PurchasedPhoneNumber"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self.get_by_number.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + deserialized = self._deserialize('PurchasedPhoneNumber', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + get_by_number.metadata = {'url': '/phoneNumbers/{phoneNumber}'} # type: ignore + + def _release_phone_number_initial( + self, + phone_number, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + # Construct URL + url = self._release_phone_number_initial.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.delete(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['operation-id']=self._deserialize('str', response.headers.get('operation-id')) + response_headers['release-id']=self._deserialize('str', response.headers.get('release-id')) + + if cls: + return cls(pipeline_response, None, response_headers) + + _release_phone_number_initial.metadata = {'url': '/phoneNumbers/{phoneNumber}'} # type: ignore + + def begin_release_phone_number( + self, + phone_number, # type: str + **kwargs # type: Any + ): + # type: (...) -> LROPoller[None] + """Releases a purchased phone number. + + Releases a purchased phone number. + + :param phone_number: Phone number to be released, e.g. +11234567890. + :type phone_number: str + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be LROBasePolling. + Pass in False for this operation to not poll, or pass in your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present. + :return: An instance of LROPoller that returns either None or the result of cls(response) + :rtype: ~azure.core.polling.LROPoller[None] + :raises ~azure.core.exceptions.HttpResponseError: + """ + polling = kwargs.pop('polling', True) # type: Union[bool, PollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType[None] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = self._release_phone_number_initial( + phone_number=phone_number, + cls=lambda x,y,z: x, + **kwargs + ) + + kwargs.pop('error_map', None) + kwargs.pop('content_type', None) + + def get_long_running_output(pipeline_response): + if cls: + return cls(pipeline_response, None, {}) + + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'phoneNumber': self._serialize.url("phone_number", phone_number, 'str'), + } + + if polling is True: polling_method = LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + if cont_token: + return LROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + begin_release_phone_number.metadata = {'url': '/phoneNumbers/{phoneNumber}'} # type: ignore + + def list_phone_numbers( + self, + skip=0, # type: Optional[int] + top=100, # type: Optional[int] + **kwargs # type: Any + ): + # type: (...) -> Iterable["_models.PurchasedPhoneNumbers"] + """Gets the list of all purchased phone numbers. + + Gets the list of all purchased phone numbers. + + :param skip: An optional parameter for how many entries to skip, for pagination purposes. The + default value is 0. + :type skip: int + :param top: An optional parameter for how many entries to return, for pagination purposes. The + default value is 100. + :type top: int + :keyword callable cls: A custom type or function that will be passed the direct response + :return: An iterator like instance of either PurchasedPhoneNumbers or the result of cls(response) + :rtype: ~azure.core.paging.ItemPaged[~azure.communication.phonenumbers.models.PurchasedPhoneNumbers] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.PurchasedPhoneNumbers"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + accept = "application/json" + + def prepare_request(next_link=None): + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + if not next_link: + # Construct URL + url = self.list_phone_numbers.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + if skip is not None: + query_parameters['skip'] = self._serialize.query("skip", skip, 'int') + if top is not None: + query_parameters['top'] = self._serialize.query("top", top, 'int') + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + else: + url = next_link + query_parameters = {} # type: Dict[str, Any] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + request = self._client.get(url, query_parameters, header_parameters) + return request + + def extract_data(pipeline_response): + deserialized = self._deserialize('PurchasedPhoneNumbers', pipeline_response) + list_of_elem = deserialized.phone_numbers + if cls: + list_of_elem = cls(list_of_elem) + return deserialized.next_link or None, iter(list_of_elem) + + def get_next(next_link=None): + request = prepare_request(next_link) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, response) + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, model=error) + + return pipeline_response + + return ItemPaged( + get_next, extract_data + ) + list_phone_numbers.metadata = {'url': '/phoneNumbers'} # type: ignore diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_phone_numbers_client.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_phone_numbers_client.py new file mode 100644 index 00000000000..7dd82c89e97 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_phone_numbers_client.py @@ -0,0 +1,241 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING +from azure.core.tracing.decorator import distributed_trace +from azure.core.exceptions import HttpResponseError +from ._generated._phone_numbers_client import PhoneNumbersClient as PhoneNumbersClientGen +from ._generated.models import PhoneNumberSearchRequest +from ._shared.utils import parse_connection_str, get_authentication_policy +from ._version import SDK_MONIKER + +if TYPE_CHECKING: + from typing import Any + from azure.core.credentials import TokenCredential + from azure.core.paging import ItemPaged + from azure.core.polling import LROPoller + from ._generated.models import PhoneNumberSearchResult, PurchasedPhoneNumber, PhoneNumberCapabilities + + +class PhoneNumbersClient(object): + """A client to interact with the AzureCommunicationService Phone Numbers gateway. + + This client provides operations to interact with the phone numbers service + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param TokenCredential credential: + The credentials with which to authenticate. + """ + def __init__( + self, + endpoint_, # type: str + credential, # type: TokenCredential + **kwargs # type: Any + ): + # type: (...) -> None + endpoint = str(endpoint_) + try: + if not endpoint.lower().startswith('http'): + endpoint = "https://" + endpoint + except AttributeError: + raise ValueError("Account URL must be a string.") + + if not credential: + raise ValueError( + "You need to provide account shared key to authenticate.") + + self._endpoint = endpoint + self._phone_number_client = PhoneNumbersClientGen( + self._endpoint, + authentication_policy=get_authentication_policy(endpoint, credential), + sdk_moniker=SDK_MONIKER, + **kwargs) + + @classmethod + def from_connection_string( + cls, conn_str, # type: str + **kwargs # type: Any + ): + # type: (...) -> PhoneNumbersClient + """Create PhoneNumbersClient from a Connection String. + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of PhoneNumbersClient. + :rtype: ~azure.communication.phonenumbers.PhoneNumbersClient + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) + + @distributed_trace + def begin_purchase_phone_numbers( + self, + search_id, # type: str + **kwargs # type: Any + ): + # type: (...) -> LROPoller[None] + """Purchases phone numbers. + + :param search_id: The search id. + :type search_id: str + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: Pass in True if you'd like the LROBasePolling polling method, + False for no polling, or your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls + for LRO operations if no Retry-After header is present. + :rtype: ~azure.core.polling.LROPoller[None] + """ + return self._phone_number_client.phone_numbers.begin_purchase_phone_numbers( + search_id, + **kwargs + ) + + @distributed_trace + def begin_release_phone_number( + self, + phone_number, # type: str + **kwargs # type: Any + ): + # type: (...) -> LROPoller[None] + """Releases an purchased phone number. + + :param phone_number: Phone number to be released, e.g. +55534567890. + :type phone_number: str + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: Pass in True if you'd like the LROBasePolling polling method, + False for no polling, or your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls + for LRO operations if no Retry-After header is present. + :rtype: ~azure.core.polling.LROPoller[None] + """ + return self._phone_number_client.phone_numbers.begin_release_phone_number( + phone_number, + **kwargs + ) + + @distributed_trace + def begin_search_available_phone_numbers( + self, + country_code, # type: str + phone_number_type, # type: str + assignment_type, # type: str + capabilities, # type: PhoneNumberCapabilities + **kwargs # type: Any + ): + # type: (...) -> LROPoller[PhoneNumberSearchResult] + """Search for available phone numbers to purchase. + + :param country_code: The ISO 3166-2 country code, e.g. US. + :type country_code: str + :param phone_number_type: Required. The type of phone numbers to search for, e.g. geographic, + or tollFree. Possible values include: "geographic", "tollFree". + :type phone_number_type: str or ~azure.communication.phonenumbers.models.PhoneNumberType + :param assignment_type: Required. The assignment type of the phone numbers to search for. A + phone number can be assigned to a person, or to an application. Possible values include: + "user", "application". + :type assignment_type: str or + ~azure.communication.phonenumbers.models.PhoneNumberAssignmentType + :param capabilities: Required. Capabilities of a phone number. + :type capabilities: ~azure.communication.phonenumbers.models.PhoneNumberCapabilities + :keyword str area_code: The area code of the desired phone number, e.g. 425. If not set, + any area code could be used in the final search. + :keyword int quantity: The quantity of phone numbers in the search. Default is 1. + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: Pass in True if you'd like the LROBasePolling polling method, + False for no polling, or your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls + for LRO operations if no Retry-After header is present. + :rtype: ~azure.core.polling.LROPoller[~azure.communication.phonenumbers.models.PhoneNumberSearchResult] + """ + search_request = PhoneNumberSearchRequest( + phone_number_type=phone_number_type, + assignment_type=assignment_type, + capabilities=capabilities, + quantity=kwargs.pop('quantity', None), + area_code=kwargs.pop('area_code', None) + ) + return self._phone_number_client.phone_numbers.begin_search_available_phone_numbers( + country_code, + search_request, + **kwargs + ) + @distributed_trace + def begin_update_phone_number_capabilities( + self, + phone_number, # type: str + sms=None, #type: str or PhoneNumberCapabilityType + calling=None, #type: str or PhoneNumberCapabilityType + **kwargs # type: Any + ): + # type: (...) -> LROPoller[PurchasedPhoneNumber] + """Updates the capabilities of a phone number. + + :param phone_number: The phone number id in E.164 format. The leading plus can be either + or + encoded as %2B, e.g. +55534567890. + :type phone_number: str + :param calling: Capability value for calling. + :type calling: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :param sms: Capability value for SMS. + :type sms: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: Pass in True if you'd like the LROBasePolling polling method, + False for no polling, or your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls + for LRO operations if no Retry-After header is present. + :rtype: ~azure.core.polling.LROPoller[~azure.communication.phonenumbers.models.PurchasedPhoneNumber] + """ + poller = self._phone_number_client.phone_numbers.begin_update_capabilities( + phone_number, + calling=calling, + sms=sms, + **kwargs + ) + + result_properties = poller.result().additional_properties + if 'status' in result_properties and result_properties['status'].lower() == 'failed': + raise HttpResponseError(message=result_properties['error']['message']) + + return poller + + @distributed_trace + def get_purchased_phone_number( + self, + phone_number, # type: str + **kwargs # type: Any + ): + # type: (...) -> PurchasedPhoneNumber + """Gets the details of the given purchased phone number. + + :param phone_number: The purchased phone number whose details are to be fetched in E.164 format, + e.g. +11234567890. + :type phone_number: str + :rtype: ~azure.communication.phonenumbers.models.PurchasedPhoneNumber + """ + return self._phone_number_client.phone_numbers.get_by_number( + phone_number, + **kwargs + ) + + @distributed_trace + def list_purchased_phone_numbers( + self, + **kwargs # type: Any + ): + # type: (...) -> ItemPaged[PurchasedPhoneNumber] + """Gets the list of all purchased phone numbers. + + :param skip: An optional parameter for how many entries to skip, for pagination purposes. The + default value is 0. + :type skip: int + :rtype: ~azure.core.paging.ItemPaged[~azure.communication.phonenumbers.models.PurchasedPhoneNumber] + """ + return self._phone_number_client.phone_numbers.list_phone_numbers( + **kwargs + ) diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/__init__.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/__init__.py new file mode 100644 index 00000000000..5b396cd202e --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/__init__.py @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/models.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/models.py new file mode 100644 index 00000000000..7820b25a7d1 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/models.py @@ -0,0 +1,156 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +# pylint: skip-file + +from enum import Enum, EnumMeta +from six import with_metaclass +from typing import Mapping, Optional, Union, Any +try: + from typing import Protocol, TypedDict +except ImportError: + from typing_extensions import Protocol, TypedDict + +from azure.core import CaseInsensitiveEnumMeta + + +class CommunicationIdentifierKind(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """Communication Identifier Kind.""" + + UNKNOWN = "unknown" + COMMUNICATION_USER = "communication_user" + PHONE_NUMBER = "phone_number" + MICROSOFT_TEAMS_USER = "microsoft_teams_user" + + +class CommunicationCloudEnvironment(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """The cloud enviornment that the identifier belongs to""" + + PUBLIC = "PUBLIC" + DOD = "DOD" + GCCH = "GCCH" + + +class CommunicationIdentifier(Protocol): + """Communication Identifier. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping[str, Any] properties: The properties of the identifier. + """ + raw_id = None # type: Optional[str] + kind = None # type: Optional[Union[CommunicationIdentifierKind, str]] + properties = {} # type: Mapping[str, Any] + + +CommunicationUserProperties = TypedDict( + 'CommunicationUserProperties', + id=str +) + + +class CommunicationUserIdentifier(object): + """Represents a user in Azure Communication Service. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping[str, Any] properties: The properties of the identifier. + The keys in this mapping include: + - `id`(str): ID of the Communication user as returned from Azure Communication Identity. + + :param str id: ID of the Communication user as returned from Azure Communication Identity. + """ + kind = CommunicationIdentifierKind.COMMUNICATION_USER + + def __init__(self, id, **kwargs): + # type: (str, Any) -> None + self.raw_id = kwargs.get('raw_id') + self.properties = CommunicationUserProperties(id=id) + + +PhoneNumberProperties = TypedDict( + 'PhoneNumberProperties', + value=str +) + + +class PhoneNumberIdentifier(object): + """Represents a phone number. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping properties: The properties of the identifier. + The keys in this mapping include: + - `value`(str): The phone number in E.164 format. + + :param str value: The phone number. + """ + kind = CommunicationIdentifierKind.PHONE_NUMBER + + def __init__(self, value, **kwargs): + # type: (str, Any) -> None + self.raw_id = kwargs.get('raw_id') + self.properties = PhoneNumberProperties(value=value) + + +class UnknownIdentifier(object): + """Represents an identifier of an unknown type. + + It will be encountered in communications with endpoints that are not + identifiable by this version of the SDK. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping properties: The properties of the identifier. + :param str identifier: The ID of the identifier. + """ + kind = CommunicationIdentifierKind.UNKNOWN + + def __init__(self, identifier): + # type: (str) -> None + self.raw_id = identifier + self.properties = {} + + +MicrosoftTeamsUserProperties = TypedDict( + 'MicrosoftTeamsUserProperties', + user_id=str, + is_anonymous=bool, + cloud=Union[CommunicationCloudEnvironment, str] +) + + +class MicrosoftTeamsUserIdentifier(object): + """Represents an identifier for a Microsoft Teams user. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping properties: The properties of the identifier. + The keys in this mapping include: + - `user_id`(str): The id of the Microsoft Teams user. If the user isn't anonymous, + the id is the AAD object id of the user. + - `is_anonymous` (bool): Set this to true if the user is anonymous for example when joining + a meeting with a share link. + - `cloud` (str): Cloud environment that this identifier belongs to. + + :param str user_id: Microsoft Teams user id. + :keyword bool is_anonymous: `True` if the identifier is anonymous. Default value is `False`. + :keyword cloud: Cloud environment that the user belongs to. Default value is `PUBLIC`. + :paramtype cloud: str or ~azure.communication.chat.CommunicationCloudEnvironment + """ + kind = CommunicationIdentifierKind.MICROSOFT_TEAMS_USER + + def __init__(self, user_id, **kwargs): + # type: (str, Any) -> None + self.raw_id = kwargs.get('raw_id') + self.properties = MicrosoftTeamsUserProperties( + user_id=user_id, + is_anonymous=kwargs.get('is_anonymous', False), + cloud=kwargs.get('cloud') or CommunicationCloudEnvironment.PUBLIC + ) diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/policy.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/policy.py new file mode 100644 index 00000000000..b2a0de8d423 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/policy.py @@ -0,0 +1,91 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import hashlib +import urllib +import base64 +import hmac +from azure.core.pipeline.policies import SansIOHTTPPolicy +from .utils import get_current_utc_time + +class HMACCredentialsPolicy(SansIOHTTPPolicy): + """Implementation of HMAC authentication policy. + """ + + def __init__(self, + host, # type: str + access_key, # type: str + decode_url=False # type: bool + ): + # type: (...) -> None + super(HMACCredentialsPolicy, self).__init__() + + if host.startswith("https://"): + self._host = host.replace("https://", "") + + if host.startswith("http://"): + self._host = host.replace("http://", "") + + self._access_key = access_key + self._decode_url = decode_url + + def _compute_hmac(self, + value # type: str + ): + decoded_secret = base64.b64decode(self._access_key) + digest = hmac.new( + decoded_secret, value.encode("utf-8"), hashlib.sha256 + ).digest() + + return base64.b64encode(digest).decode("utf-8") + + def _sign_request(self, request): + verb = request.http_request.method.upper() + + # Get the path and query from url, which looks like https://host/path/query + query_url = str(request.http_request.url[len(self._host) + 8:]) + + if self._decode_url: + query_url = urllib.parse.unquote(query_url) + + signed_headers = "date;host;x-ms-content-sha256" + + utc_now = get_current_utc_time() + if request.http_request.body is None: + request.http_request.body = "" + content_digest = hashlib.sha256( + (request.http_request.body.encode("utf-8")) + ).digest() + content_hash = base64.b64encode(content_digest).decode("utf-8") + + string_to_sign = ( + verb + + "\n" + + query_url + + "\n" + + utc_now + + ";" + + self._host + + ";" + + content_hash + ) + + signature = self._compute_hmac(string_to_sign) + + signature_header = { + "Date": utc_now, + "x-ms-content-sha256": content_hash, + "x-ms-return-client-request-id": "true", + "Authorization": "HMAC-SHA256 SignedHeaders=" +\ + signed_headers + "&Signature=" + signature, + } + + request.http_request.headers.update(signature_header) + + return request + + def on_request(self, request): + self._sign_request(request) diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/user_credential.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/user_credential.py new file mode 100644 index 00000000000..9b5f17dcc95 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/user_credential.py @@ -0,0 +1,86 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from threading import Lock, Condition +from datetime import timedelta +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) + +from .utils import get_current_utc_as_int +from .user_token_refresh_options import CommunicationTokenRefreshOptions + + +class CommunicationTokenCredential(object): + """Credential type used for authenticating to an Azure Communication service. + :param str token: The token used to authenticate to an Azure Communication service + :keyword token_refresher: The token refresher to provide capacity to fetch fresh token + :raises: TypeError + """ + + _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2 + + def __init__(self, + token, # type: str + **kwargs + ): + token_refresher = kwargs.pop('token_refresher', None) + communication_token_refresh_options = CommunicationTokenRefreshOptions(token=token, + token_refresher=token_refresher) + self._token = communication_token_refresh_options.get_token() + self._token_refresher = communication_token_refresh_options.get_token_refresher() + self._lock = Condition(Lock()) + self._some_thread_refreshing = False + + def get_token(self, *scopes, **kwargs): # pylint: disable=unused-argument + # type (*str, **Any) -> AccessToken + """The value of the configured token. + :rtype: ~azure.core.credentials.AccessToken + """ + + if not self._token_refresher or not self._token_expiring(): + return self._token + + should_this_thread_refresh = False + + with self._lock: + while self._token_expiring(): + if self._some_thread_refreshing: + if self._is_currenttoken_valid(): + return self._token + + self._wait_till_inprogress_thread_finish_refreshing() + else: + should_this_thread_refresh = True + self._some_thread_refreshing = True + break + + if should_this_thread_refresh: + try: + newtoken = self._token_refresher() # pylint:disable=not-callable + + with self._lock: + self._token = newtoken + self._some_thread_refreshing = False + self._lock.notify_all() + except: + with self._lock: + self._some_thread_refreshing = False + self._lock.notify_all() + + raise + return self._token + + def _wait_till_inprogress_thread_finish_refreshing(self): + self._lock.release() + self._lock.acquire() + + def _token_expiring(self): + return self._token.expires_on - get_current_utc_as_int() <\ + timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES).total_seconds() + + def _is_currenttoken_valid(self): + return get_current_utc_as_int() < self._token.expires_on diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/user_credential_async.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/user_credential_async.py new file mode 100644 index 00000000000..52a99e7a4b6 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/user_credential_async.py @@ -0,0 +1,95 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from asyncio import Condition, Lock +from datetime import timedelta +from typing import ( # pylint: disable=unused-import + cast, + Tuple, + Any +) + +from .utils import get_current_utc_as_int +from .user_token_refresh_options import CommunicationTokenRefreshOptions + + +class CommunicationTokenCredential(object): + """Credential type used for authenticating to an Azure Communication service. + :param str token: The token used to authenticate to an Azure Communication service + :keyword token_refresher: The async token refresher to provide capacity to fetch fresh token + :raises: TypeError + """ + + _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2 + + def __init__(self, token: str, **kwargs: Any): + token_refresher = kwargs.pop('token_refresher', None) + communication_token_refresh_options = CommunicationTokenRefreshOptions(token=token, + token_refresher=token_refresher) + self._token = communication_token_refresh_options.get_token() + self._token_refresher = communication_token_refresh_options.get_token_refresher() + self._lock = Condition(Lock()) + self._some_thread_refreshing = False + + async def get_token(self, *scopes, **kwargs): # pylint: disable=unused-argument + # type (*str, **Any) -> AccessToken + """The value of the configured token. + :rtype: ~azure.core.credentials.AccessToken + """ + if not self._token_refresher or not self._token_expiring(): + return self._token + + should_this_thread_refresh = False + + async with self._lock: + + while self._token_expiring(): + if self._some_thread_refreshing: + if self._is_currenttoken_valid(): + return self._token + + await self._wait_till_inprogress_thread_finish_refreshing() + else: + should_this_thread_refresh = True + self._some_thread_refreshing = True + break + + + if should_this_thread_refresh: + try: + newtoken = await self._token_refresher() # pylint:disable=not-callable + + async with self._lock: + self._token = newtoken + self._some_thread_refreshing = False + self._lock.notify_all() + except: + async with self._lock: + self._some_thread_refreshing = False + self._lock.notify_all() + + raise + + return self._token + + async def _wait_till_inprogress_thread_finish_refreshing(self): + self._lock.release() + await self._lock.acquire() + + def _token_expiring(self): + return self._token.expires_on - get_current_utc_as_int() <\ + timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES).total_seconds() + + def _is_currenttoken_valid(self): + return get_current_utc_as_int() < self._token.expires_on + + async def close(self) -> None: + pass + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + await self.close() diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/user_token_refresh_options.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/user_token_refresh_options.py new file mode 100644 index 00000000000..6bdc0d45602 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/user_token_refresh_options.py @@ -0,0 +1,36 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) +import six +from .utils import create_access_token + +class CommunicationTokenRefreshOptions(object): + """Options for refreshing CommunicationTokenCredential. + :param str token: The token used to authenticate to an Azure Communication service + :param token_refresher: The token refresher to provide capacity to fetch fresh token + :raises: TypeError + """ + + def __init__(self, + token, # type: str + token_refresher=None + ): + # type: (str) -> None + if not isinstance(token, six.string_types): + raise TypeError("token must be a string.") + self._token = token + self._token_refresher = token_refresher + + def get_token(self): + """Return the the serialized JWT token.""" + return create_access_token(self._token) + + def get_token_refresher(self): + """Return the token refresher to provide capacity to fetch fresh token.""" + return self._token_refresher diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/utils.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/utils.py new file mode 100644 index 00000000000..4da2691b9fb --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_shared/utils.py @@ -0,0 +1,121 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import base64 +import json +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) +from datetime import datetime +import calendar +from msrest.serialization import TZ_UTC +from azure.core.credentials import AccessToken + + +def _convert_datetime_to_utc_int(expires_on): + return int(calendar.timegm(expires_on.utctimetuple())) + +def parse_connection_str(conn_str): + # type: (str) -> Tuple[str, str, str, str] + if conn_str is None: + raise ValueError( + "Connection string is undefined." + ) + endpoint = None + shared_access_key = None + for element in conn_str.split(";"): + key, _, value = element.partition("=") + if key.lower() == "endpoint": + endpoint = value.rstrip("/") + elif key.lower() == "accesskey": + shared_access_key = value + if not all([endpoint, shared_access_key]): + raise ValueError( + "Invalid connection string. You can get the connection string from your resource page in the Azure Portal. " + "The format should be as follows: endpoint=https:///;accesskey=" + ) + left_slash_pos = cast(str, endpoint).find("//") + if left_slash_pos != -1: + host = cast(str, endpoint)[left_slash_pos + 2:] + else: + host = str(endpoint) + + return host, str(shared_access_key) + +def get_current_utc_time(): + # type: () -> str + return str(datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S ")) + "GMT" + + +def get_current_utc_as_int(): + # type: () -> int + current_utc_datetime = datetime.utcnow().replace(tzinfo=TZ_UTC) + return _convert_datetime_to_utc_int(current_utc_datetime) + +def create_access_token(token): + # type: (str) -> azure.core.credentials.AccessToken + """Creates an instance of azure.core.credentials.AccessToken from a + string token. The input string is jwt token in the following form: + .. + This method looks into the token_payload which is a json and extracts the expiry time + for that token and creates a tuple of type azure.core.credentials.AccessToken + (, ) + :param token: User token + :type token: str + :return: Instance of azure.core.credentials.AccessToken - token and expiry date of it + :rtype: ~azure.core.credentials.AccessToken + """ + + token_parse_err_msg = "Token is not formatted correctly" + parts = token.split(".") + + if len(parts) < 3: + raise ValueError(token_parse_err_msg) + + try: + padded_base64_payload = base64.b64decode(parts[1] + "==").decode('ascii') + payload = json.loads(padded_base64_payload) + return AccessToken(token, + _convert_datetime_to_utc_int(datetime.fromtimestamp(payload['exp']).replace(tzinfo=TZ_UTC))) + except ValueError: + raise ValueError(token_parse_err_msg) + +def get_authentication_policy( + endpoint, # type: str + credential, # type: TokenCredential or str + decode_url=False, # type: bool + is_async=False, # type: bool +): + # type: (...) -> BearerTokenCredentialPolicy or HMACCredentialPolicy + """Returns the correct authentication policy based + on which credential is being passed. + :param endpoint: The endpoint to which we are authenticating to. + :type endpoint: str + :param credential: The credential we use to authenticate to the service + :type credential: TokenCredential or str + :param isAsync: For async clients there is a need to decode the url + :type bool: isAsync or str + :rtype: ~azure.core.pipeline.policies.BearerTokenCredentialPolicy + ~HMACCredentialsPolicy + """ + + if credential is None: + raise ValueError("Parameter 'credential' must not be None.") + if hasattr(credential, "get_token"): + if is_async: + from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy + return AsyncBearerTokenCredentialPolicy( + credential, "https://communication.azure.com//.default") + from azure.core.pipeline.policies import BearerTokenCredentialPolicy + return BearerTokenCredentialPolicy( + credential, "https://communication.azure.com//.default") + if isinstance(credential, str): + from .._shared.policy import HMACCredentialsPolicy + return HMACCredentialsPolicy(endpoint, credential, decode_url=decode_url) + + raise TypeError("Unsupported credential: {}. Use an access token string to use HMACCredentialsPolicy" + "or a token credential from azure.identity".format(type(credential))) diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/_version.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/_version.py new file mode 100644 index 00000000000..9e20b3b75b7 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/_version.py @@ -0,0 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +VERSION = "1.0.1" + +SDK_MONIKER = "communication-phonenumbers/{}".format(VERSION) # type: str diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/aio/__init__.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/aio/__init__.py new file mode 100644 index 00000000000..6c133642985 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/aio/__init__.py @@ -0,0 +1,5 @@ +from ._phone_numbers_client_async import PhoneNumbersClient + +__all__ = [ + 'PhoneNumbersClient', +] diff --git a/src/communication/azext_communication/vendored_sdks/phonenumbers/aio/_phone_numbers_client_async.py b/src/communication/azext_communication/vendored_sdks/phonenumbers/aio/_phone_numbers_client_async.py new file mode 100644 index 00000000000..601a03dd610 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/phonenumbers/aio/_phone_numbers_client_async.py @@ -0,0 +1,252 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING +from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.core.paging import ItemPaged +from azure.core.polling import LROPoller +from .._generated.aio._phone_numbers_client import PhoneNumbersClient as PhoneNumbersClientGen +from .._generated.models import PhoneNumberSearchRequest +from .._shared.utils import parse_connection_str, get_authentication_policy +from .._version import SDK_MONIKER + +if TYPE_CHECKING: + from typing import Any + from azure.core.credentials_async import AsyncTokenCredential + from azure.core.async_paging import AsyncItemPaged + from azure.core.polling import AsyncLROPoller + from .._generated.models import PhoneNumberSearchResult, PurchasedPhoneNumber, PhoneNumberCapabilities + +class PhoneNumbersClient(object): + """A client to interact with the AzureCommunicationService Phone Numbers gateway. + + This client provides operations to interact with the phone numbers service + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param AsyncTokenCredential credential: + The credentials with which to authenticate. + """ + def __init__( + self, + endpoint, # type: str + credential, # type: AsyncTokenCredential + **kwargs # type: Any + ): + # type: (...) -> None + try: + if not endpoint.lower().startswith('http'): + endpoint = "https://" + endpoint + except AttributeError: + raise ValueError("Account URL must be a string.") + + if not credential: + raise ValueError( + "You need to provide account shared key to authenticate.") + + self._endpoint = endpoint + self._phone_number_client = PhoneNumbersClientGen( + self._endpoint, + authentication_policy=get_authentication_policy(endpoint, credential, is_async=True), + sdk_moniker=SDK_MONIKER, + **kwargs) + + @classmethod + def from_connection_string( + cls, conn_str, # type: str + **kwargs # type: Any + ): + # type: (...) -> PhoneNumbersClient + """Create PhoneNumbersClient from a Connection String. + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of PhoneNumbersClient. + :rtype: ~azure.communication.phonenumbers.aio.PhoneNumbersClient + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) + + @distributed_trace_async + async def begin_purchase_phone_numbers( + self, + search_id, # type: str + **kwargs # type: Any + ): + # type: (...) -> AsyncLROPoller[None] + """Purchases phone numbers. + + :param search_id: The search id. + :type search_id: str + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: Pass in True if you'd like the LROBasePolling polling method, + False for no polling, or your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls + for LRO operations if no Retry-After header is present. + :rtype: ~azure.core.polling.AsyncLROPoller[None] + """ + return await self._phone_number_client.phone_numbers.begin_purchase_phone_numbers( + search_id, + **kwargs + ) + + @distributed_trace_async + async def begin_release_phone_number( + self, + phone_number, # type: str + **kwargs # type: Any + ): + # type: (...) -> AsyncLROPoller[None] + """Releases an purchased phone number. + + :param phone_number: Phone number to be released, e.g. +11234567890. + :type phone_number: str + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: Pass in True if you'd like the LROBasePolling polling method, + False for no polling, or your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls + for LRO operations if no Retry-After header is present. + :rtype: ~azure.core.polling.AsyncLROPoller[None] + """ + return await self._phone_number_client.phone_numbers.begin_release_phone_number( + phone_number, + **kwargs + ) + + @distributed_trace_async + async def begin_search_available_phone_numbers( + self, + country_code, # type: str + phone_number_type, # type: str + assignment_type, # type: str + capabilities, + **kwargs + ): + # type: (...) -> AsyncLROPoller[PhoneNumberSearchResult] + """Search for available phone numbers to purchase. + + :param country_code: The ISO 3166-2 country code, e.g. US. + :type country_code: str + :param phone_number_type: Required. The type of phone numbers to search for, e.g. geographic, + or tollFree. Possible values include: "geographic", "tollFree". + :type phone_number_type: str or ~azure.communication.phonenumbers.models.PhoneNumberType + :param assignment_type: Required. The assignment type of the phone numbers to search for. A + phone number can be assigned to a person, or to an application. Possible values include: + "user", "application". + :type assignment_type: str or + ~azure.communication.phonenumbers.models.PhoneNumberAssignmentType + :param capabilities: Required. Capabilities of a phone number. + :type capabilities: ~azure.communication.phonenumbers.models.PhoneNumberCapabilities + :keyword str area_code: The area code of the desired phone number, e.g. 425. If not set, + any area code could be used in the final search. + :keyword int quantity: The quantity of phone numbers in the search. Default is 1. + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: Pass in True if you'd like the LROBasePolling polling method, + False for no polling, or your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls + for LRO operations if no Retry-After header is present. + :rtype: ~azure.core.polling.AsyncLROPoller[~azure.communication.phonenumbers.models.PhoneNumberSearchResult] + """ + search_request = PhoneNumberSearchRequest( + phone_number_type=phone_number_type, + assignment_type=assignment_type, + capabilities=capabilities, + quantity=kwargs.pop('quantity', None), + area_code=kwargs.pop('area_code', None) + ) + return await self._phone_number_client.phone_numbers.begin_search_available_phone_numbers( + country_code, + search_request, + **kwargs + ) + + @distributed_trace_async + async def begin_update_phone_number_capabilities( + self, + phone_number, # type: str + sms=None, # type: str + calling=None, # type: str + **kwargs # type: Any + ): + # type: (...) -> AsyncLROPoller[PurchasedPhoneNumber] + """Updates the capabilities of a phone number. + + :param phone_number: The phone number id in E.164 format. The leading plus can be either + or + encoded as %2B, e.g. +11234567890. + :type phone_number: str + :param calling: Capability value for calling. + :type calling: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :param sms: Capability value for SMS. + :type sms: str or ~azure.communication.phonenumbers.models.PhoneNumberCapabilityType + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: Pass in True if you'd like the LROBasePolling polling method, + False for no polling, or your own initialized polling object for a personal polling strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls + for LRO operations if no Retry-After header is present. + :rtype: ~azure.core.polling.AsyncLROPoller[~azure.communication.phonenumbers.models.PurchasedPhoneNumber] + """ + return await self._phone_number_client.phone_numbers.begin_update_capabilities( + phone_number, + calling, + sms, + **kwargs + ) + + @distributed_trace_async + async def get_purchased_phone_number( + self, + phone_number, # type: str + **kwargs # type: Any + ): + # type: (...) -> PurchasedPhoneNumber + """Gets the details of the given purchased phone number. + + :param phone_number: The purchased phone number whose details are to be fetched in E.164 format, + e.g. +11234567890. + :type phone_number: str + :rtype: ~azure.communication.phonenumbers.models.PurchasedPhoneNumber + """ + return await self._phone_number_client.phone_numbers.get_by_number( + phone_number, + **kwargs + ) + + @distributed_trace + def list_purchased_phone_numbers( + self, + **kwargs # type: Any + ): + # type: (...) -> AsyncItemPaged[PurchasedPhoneNumber] + """Gets the list of all purchased phone numbers. + + :param skip: An optional parameter for how many entries to skip, for pagination purposes. The + default value is 0. + :type skip: int + :param top: An optional parameter for how many entries to return, for pagination purposes. The + default value is 100. + :type top: int + :rtype: ~azure.core.paging.AsyncItemPaged[~azure.communication.phonenumbers.models.PurchasedPhoneNumber] + """ + return self._phone_number_client.phone_numbers.list_phone_numbers( + **kwargs + ) + + async def __aenter__(self) -> "PhoneNumbersClient": + await self._phone_number_client.__aenter__() + return self + + async def __aexit__(self, *args: "Any") -> None: + await self.close() + + async def close(self) -> None: + """Close the :class: + `~azure.communication.phonenumbers.aio.PhoneNumbersClient` session. + """ + await self._phone_number_client.__aexit__() diff --git a/src/communication/azext_communication/vendored_sdks/sms/__init__.py b/src/communication/azext_communication/vendored_sdks/sms/__init__.py new file mode 100644 index 00000000000..f1e383aa337 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/__init__.py @@ -0,0 +1,8 @@ +from ._sms_client import SmsClient + +from ._models import SmsSendResult + +__all__ = [ + 'SmsClient', + 'SmsSendResult', +] diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/__init__.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/__init__.py new file mode 100644 index 00000000000..9b98a0a107d --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/__init__.py @@ -0,0 +1,16 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._azure_communication_sms_service import AzureCommunicationSMSService +__all__ = ['AzureCommunicationSMSService'] + +try: + from ._patch import patch_sdk # type: ignore + patch_sdk() +except ImportError: + pass diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/_azure_communication_sms_service.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/_azure_communication_sms_service.py new file mode 100644 index 00000000000..b6e74c04a54 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/_azure_communication_sms_service.py @@ -0,0 +1,82 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core import PipelineClient +from msrest import Deserializer, Serializer + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + from azure.core.pipeline.transport import HttpRequest, HttpResponse + +from ._configuration import AzureCommunicationSMSServiceConfiguration +from .operations import SmsOperations +from . import models + + +class AzureCommunicationSMSService(object): + """Azure Communication SMS Service. + + :ivar sms: SmsOperations operations + :vartype sms: azure.communication.sms.operations.SmsOperations + :param endpoint: The communication resource, for example https://my-resource.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint, # type: str + base_url=None, + **kwargs # type: Any + ): + # type: (...) -> None + base_url = '{endpoint}' + self._config = AzureCommunicationSMSServiceConfiguration(endpoint, **kwargs) + self._client = PipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._serialize.client_side_validation = False + self._deserialize = Deserializer(client_models) + + self.sms = SmsOperations( + self._client, self._config, self._serialize, self._deserialize) + + def _send_request(self, http_request, **kwargs): + # type: (HttpRequest, Any) -> HttpResponse + """Runs the network request through the client's chained policies. + + :param http_request: The network request you want to make. Required. + :type http_request: ~azure.core.pipeline.transport.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to True. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.pipeline.transport.HttpResponse + """ + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + http_request.url = self._client.format_url(http_request.url, **path_format_arguments) + stream = kwargs.pop("stream", True) + pipeline_response = self._client._pipeline.run(http_request, stream=stream, **kwargs) + return pipeline_response.http_response + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> AzureCommunicationSMSService + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/_configuration.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/_configuration.py new file mode 100644 index 00000000000..9d3154a1fa3 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/_configuration.py @@ -0,0 +1,58 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + +VERSION = "unknown" + +class AzureCommunicationSMSServiceConfiguration(Configuration): + """Configuration for AzureCommunicationSMSService. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example https://my-resource.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + super(AzureCommunicationSMSServiceConfiguration, self).__init__(**kwargs) + + self.endpoint = endpoint + self.api_version = "2021-03-07" + kwargs.setdefault('sdk_moniker', 'azurecommunicationsmsservice/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs # type: Any + ): + # type: (...) -> None + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/__init__.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/__init__.py new file mode 100644 index 00000000000..1149dd8315f --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/__init__.py @@ -0,0 +1,10 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._azure_communication_sms_service import AzureCommunicationSMSService +__all__ = ['AzureCommunicationSMSService'] diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/_azure_communication_sms_service.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/_azure_communication_sms_service.py new file mode 100644 index 00000000000..9df4a63dd70 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/_azure_communication_sms_service.py @@ -0,0 +1,71 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core import AsyncPipelineClient +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest +from msrest import Deserializer, Serializer + +from ._configuration import AzureCommunicationSMSServiceConfiguration +from .operations import SmsOperations +from .. import models + + +class AzureCommunicationSMSService(object): + """Azure Communication SMS Service. + + :ivar sms: SmsOperations operations + :vartype sms: azure.communication.sms.aio.operations.SmsOperations + :param endpoint: The communication resource, for example https://my-resource.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + base_url = '{endpoint}' + self._config = AzureCommunicationSMSServiceConfiguration(endpoint, **kwargs) + self._client = AsyncPipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._serialize.client_side_validation = False + self._deserialize = Deserializer(client_models) + + self.sms = SmsOperations( + self._client, self._config, self._serialize, self._deserialize) + + async def _send_request(self, http_request: HttpRequest, **kwargs: Any) -> AsyncHttpResponse: + """Runs the network request through the client's chained policies. + + :param http_request: The network request you want to make. Required. + :type http_request: ~azure.core.pipeline.transport.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to True. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.pipeline.transport.AsyncHttpResponse + """ + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + http_request.url = self._client.format_url(http_request.url, **path_format_arguments) + stream = kwargs.pop("stream", True) + pipeline_response = await self._client._pipeline.run(http_request, stream=stream, **kwargs) + return pipeline_response.http_response + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "AzureCommunicationSMSService": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/_configuration.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/_configuration.py new file mode 100644 index 00000000000..77033099353 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/_configuration.py @@ -0,0 +1,52 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +VERSION = "unknown" + +class AzureCommunicationSMSServiceConfiguration(Configuration): + """Configuration for AzureCommunicationSMSService. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example https://my-resource.communication.azure.com. + :type endpoint: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + super(AzureCommunicationSMSServiceConfiguration, self).__init__(**kwargs) + + self.endpoint = endpoint + self.api_version = "2021-03-07" + kwargs.setdefault('sdk_moniker', 'azurecommunicationsmsservice/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs: Any + ) -> None: + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/operations/__init__.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/operations/__init__.py new file mode 100644 index 00000000000..f9ce203e288 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/operations/__init__.py @@ -0,0 +1,13 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._sms_operations import SmsOperations + +__all__ = [ + 'SmsOperations', +] diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/operations/_sms_operations.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/operations/_sms_operations.py new file mode 100644 index 00000000000..7ec83da71f2 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/aio/operations/_sms_operations.py @@ -0,0 +1,100 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, Callable, Dict, Generic, Optional, TypeVar +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest + +from ... import models as _models + +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class SmsOperations: + """SmsOperations async operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.communication.sms.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer) -> None: + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + async def send( + self, + send_message_request: "_models.SendMessageRequest", + **kwargs: Any + ) -> "_models.SmsSendResponse": + """Sends a SMS message from a phone number that belongs to the authenticated account. + + Sends a SMS message from a phone number that belongs to the authenticated account. + + :param send_message_request: Represents the body of the send message request. + :type send_message_request: ~azure.communication.sms.models.SendMessageRequest + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SmsSendResponse, or the result of cls(response) + :rtype: ~azure.communication.sms.models.SmsSendResponse + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.SmsSendResponse"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.send.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(send_message_request, 'SendMessageRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + deserialized = self._deserialize('SmsSendResponse', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + send.metadata = {'url': '/sms'} # type: ignore diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/models/__init__.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/models/__init__.py new file mode 100644 index 00000000000..74b2447dbed --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/models/__init__.py @@ -0,0 +1,33 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import SendMessageRequest + from ._models_py3 import SmsRecipient + from ._models_py3 import SmsSendOptions + from ._models_py3 import SmsSendResponse + from ._models_py3 import SmsSendResponseItem +except (SyntaxError, ImportError): + from ._models import SendMessageRequest # type: ignore + from ._models import SmsRecipient # type: ignore + from ._models import SmsSendOptions # type: ignore + from ._models import SmsSendResponse # type: ignore + from ._models import SmsSendResponseItem # type: ignore + +from ._azure_communication_sms_service_enums import ( + SmsSendResponseItemRepeatabilityResult, +) + +__all__ = [ + 'SendMessageRequest', + 'SmsRecipient', + 'SmsSendOptions', + 'SmsSendResponse', + 'SmsSendResponseItem', + 'SmsSendResponseItemRepeatabilityResult', +] diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/models/_azure_communication_sms_service_enums.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/models/_azure_communication_sms_service_enums.py new file mode 100644 index 00000000000..635ce86194d --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/models/_azure_communication_sms_service_enums.py @@ -0,0 +1,35 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum, EnumMeta +from six import with_metaclass + +class _CaseInsensitiveEnumMeta(EnumMeta): + def __getitem__(self, name): + return super().__getitem__(name.upper()) + + def __getattr__(cls, name): + """Return the enum member matching `name` + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + """ + try: + return cls._member_map_[name.upper()] + except KeyError: + raise AttributeError(name) + + +class SmsSendResponseItemRepeatabilityResult(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """The result of a repeatable request with one of the case-insensitive values accepted or + rejected. + """ + + ACCEPTED = "accepted" + REJECTED = "rejected" diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/models/_models.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/models/_models.py new file mode 100644 index 00000000000..3dd8dab3bef --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/models/_models.py @@ -0,0 +1,197 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +import msrest.serialization + + +class SendMessageRequest(msrest.serialization.Model): + """Represents the properties of a send message request. + + All required parameters must be populated in order to send to Azure. + + :param from_property: Required. The sender's phone number in E.164 format that is owned by the + authenticated account. + :type from_property: str + :param sms_recipients: Required. The recipient's phone number in E.164 format. In this version, + a minimum of 1 and upto 100 recipients in the list are supported. + :type sms_recipients: list[~azure.communication.sms.models.SmsRecipient] + :param message: Required. The contents of the message that will be sent to the recipient. The + allowable content is defined by RFC 5724. + :type message: str + :param sms_send_options: Optional configuration for sending SMS messages. + :type sms_send_options: ~azure.communication.sms.models.SmsSendOptions + """ + + _validation = { + 'from_property': {'required': True}, + 'sms_recipients': {'required': True}, + 'message': {'required': True, 'max_length': 2048, 'min_length': 0}, + } + + _attribute_map = { + 'from_property': {'key': 'from', 'type': 'str'}, + 'sms_recipients': {'key': 'smsRecipients', 'type': '[SmsRecipient]'}, + 'message': {'key': 'message', 'type': 'str'}, + 'sms_send_options': {'key': 'smsSendOptions', 'type': 'SmsSendOptions'}, + } + + def __init__( + self, + **kwargs + ): + super(SendMessageRequest, self).__init__(**kwargs) + self.from_property = kwargs['from_property'] + self.sms_recipients = kwargs['sms_recipients'] + self.message = kwargs['message'] + self.sms_send_options = kwargs.get('sms_send_options', None) + + +class SmsRecipient(msrest.serialization.Model): + """Recipient details for sending SMS messages. + + All required parameters must be populated in order to send to Azure. + + :param to: Required. The recipient's phone number in E.164 format. + :type to: str + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, the client can make the request multiple times with the same + Repeatability-Request-ID and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-ID is an opaque string + representing a client-generated, 36-character hexadecimal case-insensitive encoding of a UUID + (GUID), identifier for the request. + :type repeatability_request_id: str + :param repeatability_first_sent: MUST be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created.eg- Tue, 26 Mar 2019 16:06:51 GMT. + :type repeatability_first_sent: str + """ + + _validation = { + 'to': {'required': True}, + } + + _attribute_map = { + 'to': {'key': 'to', 'type': 'str'}, + 'repeatability_request_id': {'key': 'repeatabilityRequestId', 'type': 'str'}, + 'repeatability_first_sent': {'key': 'repeatabilityFirstSent', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(SmsRecipient, self).__init__(**kwargs) + self.to = kwargs['to'] + self.repeatability_request_id = kwargs.get('repeatability_request_id', None) + self.repeatability_first_sent = kwargs.get('repeatability_first_sent', None) + + +class SmsSendOptions(msrest.serialization.Model): + """Optional configuration for sending SMS messages. + + All required parameters must be populated in order to send to Azure. + + :param enable_delivery_report: Required. Enable this flag to receive a delivery report for this + message on the Azure Resource EventGrid. + :type enable_delivery_report: bool + :param tag: Use this field to provide metadata that will then be sent back in the corresponding + Delivery Report. + :type tag: str + """ + + _validation = { + 'enable_delivery_report': {'required': True}, + } + + _attribute_map = { + 'enable_delivery_report': {'key': 'enableDeliveryReport', 'type': 'bool'}, + 'tag': {'key': 'tag', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(SmsSendOptions, self).__init__(**kwargs) + self.enable_delivery_report = kwargs['enable_delivery_report'] + self.tag = kwargs.get('tag', None) + + +class SmsSendResponse(msrest.serialization.Model): + """Response for a successful or multi status send Sms request. + + All required parameters must be populated in order to send to Azure. + + :param value: Required. + :type value: list[~azure.communication.sms.models.SmsSendResponseItem] + """ + + _validation = { + 'value': {'required': True}, + } + + _attribute_map = { + 'value': {'key': 'value', 'type': '[SmsSendResponseItem]'}, + } + + def __init__( + self, + **kwargs + ): + super(SmsSendResponse, self).__init__(**kwargs) + self.value = kwargs['value'] + + +class SmsSendResponseItem(msrest.serialization.Model): + """Response for a single recipient. + + All required parameters must be populated in order to send to Azure. + + :param to: Required. The recipient's phone number in E.164 format. + :type to: str + :param message_id: The identifier of the outgoing Sms message. Only present if message + processed. + :type message_id: str + :param http_status_code: Required. HTTP Status code. + :type http_status_code: int + :param repeatability_result: The result of a repeatable request with one of the + case-insensitive values accepted or rejected. Possible values include: "accepted", "rejected". + :type repeatability_result: str or + ~azure.communication.sms.models.SmsSendResponseItemRepeatabilityResult + :param successful: Required. Indicates if the message is processed successfully or not. + :type successful: bool + :param error_message: Optional error message in case of 4xx/5xx/repeatable errors. + :type error_message: str + """ + + _validation = { + 'to': {'required': True}, + 'http_status_code': {'required': True}, + 'successful': {'required': True}, + } + + _attribute_map = { + 'to': {'key': 'to', 'type': 'str'}, + 'message_id': {'key': 'messageId', 'type': 'str'}, + 'http_status_code': {'key': 'httpStatusCode', 'type': 'int'}, + 'repeatability_result': {'key': 'repeatabilityResult', 'type': 'str'}, + 'successful': {'key': 'successful', 'type': 'bool'}, + 'error_message': {'key': 'errorMessage', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(SmsSendResponseItem, self).__init__(**kwargs) + self.to = kwargs['to'] + self.message_id = kwargs.get('message_id', None) + self.http_status_code = kwargs['http_status_code'] + self.repeatability_result = kwargs.get('repeatability_result', None) + self.successful = kwargs['successful'] + self.error_message = kwargs.get('error_message', None) diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/models/_models_py3.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/models/_models_py3.py new file mode 100644 index 00000000000..7934352a770 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/models/_models_py3.py @@ -0,0 +1,222 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import List, Optional, Union + +import msrest.serialization + +from ._azure_communication_sms_service_enums import * + + +class SendMessageRequest(msrest.serialization.Model): + """Represents the properties of a send message request. + + All required parameters must be populated in order to send to Azure. + + :param from_property: Required. The sender's phone number in E.164 format that is owned by the + authenticated account. + :type from_property: str + :param sms_recipients: Required. The recipient's phone number in E.164 format. In this version, + a minimum of 1 and upto 100 recipients in the list are supported. + :type sms_recipients: list[~azure.communication.sms.models.SmsRecipient] + :param message: Required. The contents of the message that will be sent to the recipient. The + allowable content is defined by RFC 5724. + :type message: str + :param sms_send_options: Optional configuration for sending SMS messages. + :type sms_send_options: ~azure.communication.sms.models.SmsSendOptions + """ + + _validation = { + 'from_property': {'required': True}, + 'sms_recipients': {'required': True}, + 'message': {'required': True, 'max_length': 2048, 'min_length': 0}, + } + + _attribute_map = { + 'from_property': {'key': 'from', 'type': 'str'}, + 'sms_recipients': {'key': 'smsRecipients', 'type': '[SmsRecipient]'}, + 'message': {'key': 'message', 'type': 'str'}, + 'sms_send_options': {'key': 'smsSendOptions', 'type': 'SmsSendOptions'}, + } + + def __init__( + self, + *, + from_property: str, + sms_recipients: List["SmsRecipient"], + message: str, + sms_send_options: Optional["SmsSendOptions"] = None, + **kwargs + ): + super(SendMessageRequest, self).__init__(**kwargs) + self.from_property = from_property + self.sms_recipients = sms_recipients + self.message = message + self.sms_send_options = sms_send_options + + +class SmsRecipient(msrest.serialization.Model): + """Recipient details for sending SMS messages. + + All required parameters must be populated in order to send to Azure. + + :param to: Required. The recipient's phone number in E.164 format. + :type to: str + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, the client can make the request multiple times with the same + Repeatability-Request-ID and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-ID is an opaque string + representing a client-generated, 36-character hexadecimal case-insensitive encoding of a UUID + (GUID), identifier for the request. + :type repeatability_request_id: str + :param repeatability_first_sent: MUST be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created.eg- Tue, 26 Mar 2019 16:06:51 GMT. + :type repeatability_first_sent: str + """ + + _validation = { + 'to': {'required': True}, + } + + _attribute_map = { + 'to': {'key': 'to', 'type': 'str'}, + 'repeatability_request_id': {'key': 'repeatabilityRequestId', 'type': 'str'}, + 'repeatability_first_sent': {'key': 'repeatabilityFirstSent', 'type': 'str'}, + } + + def __init__( + self, + *, + to: str, + repeatability_request_id: Optional[str] = None, + repeatability_first_sent: Optional[str] = None, + **kwargs + ): + super(SmsRecipient, self).__init__(**kwargs) + self.to = to + self.repeatability_request_id = repeatability_request_id + self.repeatability_first_sent = repeatability_first_sent + + +class SmsSendOptions(msrest.serialization.Model): + """Optional configuration for sending SMS messages. + + All required parameters must be populated in order to send to Azure. + + :param enable_delivery_report: Required. Enable this flag to receive a delivery report for this + message on the Azure Resource EventGrid. + :type enable_delivery_report: bool + :param tag: Use this field to provide metadata that will then be sent back in the corresponding + Delivery Report. + :type tag: str + """ + + _validation = { + 'enable_delivery_report': {'required': True}, + } + + _attribute_map = { + 'enable_delivery_report': {'key': 'enableDeliveryReport', 'type': 'bool'}, + 'tag': {'key': 'tag', 'type': 'str'}, + } + + def __init__( + self, + *, + enable_delivery_report: bool, + tag: Optional[str] = None, + **kwargs + ): + super(SmsSendOptions, self).__init__(**kwargs) + self.enable_delivery_report = enable_delivery_report + self.tag = tag + + +class SmsSendResponse(msrest.serialization.Model): + """Response for a successful or multi status send Sms request. + + All required parameters must be populated in order to send to Azure. + + :param value: Required. + :type value: list[~azure.communication.sms.models.SmsSendResponseItem] + """ + + _validation = { + 'value': {'required': True}, + } + + _attribute_map = { + 'value': {'key': 'value', 'type': '[SmsSendResponseItem]'}, + } + + def __init__( + self, + *, + value: List["SmsSendResponseItem"], + **kwargs + ): + super(SmsSendResponse, self).__init__(**kwargs) + self.value = value + + +class SmsSendResponseItem(msrest.serialization.Model): + """Response for a single recipient. + + All required parameters must be populated in order to send to Azure. + + :param to: Required. The recipient's phone number in E.164 format. + :type to: str + :param message_id: The identifier of the outgoing Sms message. Only present if message + processed. + :type message_id: str + :param http_status_code: Required. HTTP Status code. + :type http_status_code: int + :param repeatability_result: The result of a repeatable request with one of the + case-insensitive values accepted or rejected. Possible values include: "accepted", "rejected". + :type repeatability_result: str or + ~azure.communication.sms.models.SmsSendResponseItemRepeatabilityResult + :param successful: Required. Indicates if the message is processed successfully or not. + :type successful: bool + :param error_message: Optional error message in case of 4xx/5xx/repeatable errors. + :type error_message: str + """ + + _validation = { + 'to': {'required': True}, + 'http_status_code': {'required': True}, + 'successful': {'required': True}, + } + + _attribute_map = { + 'to': {'key': 'to', 'type': 'str'}, + 'message_id': {'key': 'messageId', 'type': 'str'}, + 'http_status_code': {'key': 'httpStatusCode', 'type': 'int'}, + 'repeatability_result': {'key': 'repeatabilityResult', 'type': 'str'}, + 'successful': {'key': 'successful', 'type': 'bool'}, + 'error_message': {'key': 'errorMessage', 'type': 'str'}, + } + + def __init__( + self, + *, + to: str, + http_status_code: int, + successful: bool, + message_id: Optional[str] = None, + repeatability_result: Optional[Union[str, "SmsSendResponseItemRepeatabilityResult"]] = None, + error_message: Optional[str] = None, + **kwargs + ): + super(SmsSendResponseItem, self).__init__(**kwargs) + self.to = to + self.message_id = message_id + self.http_status_code = http_status_code + self.repeatability_result = repeatability_result + self.successful = successful + self.error_message = error_message diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/operations/__init__.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/operations/__init__.py new file mode 100644 index 00000000000..f9ce203e288 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/operations/__init__.py @@ -0,0 +1,13 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._sms_operations import SmsOperations + +__all__ = [ + 'SmsOperations', +] diff --git a/src/communication/azext_communication/vendored_sdks/sms/_generated/operations/_sms_operations.py b/src/communication/azext_communication/vendored_sdks/sms/_generated/operations/_sms_operations.py new file mode 100644 index 00000000000..1f0d0640e12 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_generated/operations/_sms_operations.py @@ -0,0 +1,105 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpRequest, HttpResponse + +from .. import models as _models + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Generic, Optional, TypeVar + + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +class SmsOperations(object): + """SmsOperations operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.communication.sms.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer): + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + def send( + self, + send_message_request, # type: "_models.SendMessageRequest" + **kwargs # type: Any + ): + # type: (...) -> "_models.SmsSendResponse" + """Sends a SMS message from a phone number that belongs to the authenticated account. + + Sends a SMS message from a phone number that belongs to the authenticated account. + + :param send_message_request: Represents the body of the send message request. + :type send_message_request: ~azure.communication.sms.models.SendMessageRequest + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SmsSendResponse, or the result of cls(response) + :rtype: ~azure.communication.sms.models.SmsSendResponse + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.SmsSendResponse"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-07" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.send.metadata['url'] # type: ignore + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(send_message_request, 'SendMessageRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + deserialized = self._deserialize('SmsSendResponse', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + send.metadata = {'url': '/sms'} # type: ignore diff --git a/src/communication/azext_communication/vendored_sdks/sms/_models/__init__.py b/src/communication/azext_communication/vendored_sdks/sms/_models/__init__.py new file mode 100644 index 00000000000..96cd579bc6e --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_models/__init__.py @@ -0,0 +1,16 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import SmsSendResult +except (SyntaxError, ImportError): + from ._models import SmsSendResult # type: ignore + +__all__ = [ + 'SmsSendResult' +] diff --git a/src/communication/azext_communication/vendored_sdks/sms/_models/_models.py b/src/communication/azext_communication/vendored_sdks/sms/_models/_models.py new file mode 100644 index 00000000000..c6b703966ef --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_models/_models.py @@ -0,0 +1,53 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +import msrest.serialization + + +class SmsSendResult(msrest.serialization.Model): + """Response for a single recipient. + + All required parameters must be populated in order to send to Azure. + + :param to: Required. The recipient's phone number in E.164 format. + :type to: str + :param message_id: The identifier of the outgoing Sms message. Only present if message + processed. + :type message_id: str + :param http_status_code: Required. HTTP Status code. + :type http_status_code: int + :param successful: Required. Indicates if the message is processed successfully or not. + :type successful: bool + :param error_message: Optional error message in case of 4xx/5xx/repeatable errors. + :type error_message: str + """ + + _validation = { + 'to': {'required': True}, + 'http_status_code': {'required': True}, + 'successful': {'required': True}, + } + + _attribute_map = { + 'to': {'key': 'to', 'type': 'str'}, + 'message_id': {'key': 'messageId', 'type': 'str'}, + 'http_status_code': {'key': 'httpStatusCode', 'type': 'int'}, + 'successful': {'key': 'successful', 'type': 'bool'}, + 'error_message': {'key': 'errorMessage', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(SmsSendResult, self).__init__(**kwargs) + self.to = kwargs['to'] + self.message_id = kwargs.get('message_id', None) + self.http_status_code = kwargs['http_status_code'] + self.successful = kwargs['successful'] + self.error_message = kwargs.get('error_message', None) diff --git a/src/communication/azext_communication/vendored_sdks/sms/_models/_models_py3.py b/src/communication/azext_communication/vendored_sdks/sms/_models/_models_py3.py new file mode 100644 index 00000000000..cd3d27e5354 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_models/_models_py3.py @@ -0,0 +1,61 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Optional + +import msrest.serialization + + +class SmsSendResult(msrest.serialization.Model): + """Response for a single recipient. + + All required parameters must be populated in order to send to Azure. + + :param to: Required. The recipient's phone number in E.164 format. + :type to: str + :param message_id: The identifier of the outgoing Sms message. Only present if message + processed. + :type message_id: str + :param http_status_code: Required. HTTP Status code. + :type http_status_code: int + :param successful: Required. Indicates if the message is processed successfully or not. + :type successful: bool + :param error_message: Optional error message in case of 4xx/5xx/repeatable errors. + :type error_message: str + """ + + _validation = { + 'to': {'required': True}, + 'http_status_code': {'required': True}, + 'successful': {'required': True}, + } + + _attribute_map = { + 'to': {'key': 'to', 'type': 'str'}, + 'message_id': {'key': 'messageId', 'type': 'str'}, + 'http_status_code': {'key': 'httpStatusCode', 'type': 'int'}, + 'successful': {'key': 'successful', 'type': 'bool'}, + 'error_message': {'key': 'errorMessage', 'type': 'str'}, + } + + def __init__( + self, + *, + to: str, + http_status_code: int, + successful: bool, + message_id: Optional[str] = None, + error_message: Optional[str] = None, + **kwargs + ): + super(SmsSendResult, self).__init__(**kwargs) + self.to = to + self.message_id = message_id + self.http_status_code = http_status_code + self.successful = successful + self.error_message = error_message diff --git a/src/communication/azext_communication/vendored_sdks/sms/_shared/__init__.py b/src/communication/azext_communication/vendored_sdks/sms/_shared/__init__.py new file mode 100644 index 00000000000..5b396cd202e --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_shared/__init__.py @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- diff --git a/src/communication/azext_communication/vendored_sdks/sms/_shared/models.py b/src/communication/azext_communication/vendored_sdks/sms/_shared/models.py new file mode 100644 index 00000000000..7820b25a7d1 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_shared/models.py @@ -0,0 +1,156 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +# pylint: skip-file + +from enum import Enum, EnumMeta +from six import with_metaclass +from typing import Mapping, Optional, Union, Any +try: + from typing import Protocol, TypedDict +except ImportError: + from typing_extensions import Protocol, TypedDict + +from azure.core import CaseInsensitiveEnumMeta + + +class CommunicationIdentifierKind(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """Communication Identifier Kind.""" + + UNKNOWN = "unknown" + COMMUNICATION_USER = "communication_user" + PHONE_NUMBER = "phone_number" + MICROSOFT_TEAMS_USER = "microsoft_teams_user" + + +class CommunicationCloudEnvironment(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """The cloud enviornment that the identifier belongs to""" + + PUBLIC = "PUBLIC" + DOD = "DOD" + GCCH = "GCCH" + + +class CommunicationIdentifier(Protocol): + """Communication Identifier. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping[str, Any] properties: The properties of the identifier. + """ + raw_id = None # type: Optional[str] + kind = None # type: Optional[Union[CommunicationIdentifierKind, str]] + properties = {} # type: Mapping[str, Any] + + +CommunicationUserProperties = TypedDict( + 'CommunicationUserProperties', + id=str +) + + +class CommunicationUserIdentifier(object): + """Represents a user in Azure Communication Service. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping[str, Any] properties: The properties of the identifier. + The keys in this mapping include: + - `id`(str): ID of the Communication user as returned from Azure Communication Identity. + + :param str id: ID of the Communication user as returned from Azure Communication Identity. + """ + kind = CommunicationIdentifierKind.COMMUNICATION_USER + + def __init__(self, id, **kwargs): + # type: (str, Any) -> None + self.raw_id = kwargs.get('raw_id') + self.properties = CommunicationUserProperties(id=id) + + +PhoneNumberProperties = TypedDict( + 'PhoneNumberProperties', + value=str +) + + +class PhoneNumberIdentifier(object): + """Represents a phone number. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping properties: The properties of the identifier. + The keys in this mapping include: + - `value`(str): The phone number in E.164 format. + + :param str value: The phone number. + """ + kind = CommunicationIdentifierKind.PHONE_NUMBER + + def __init__(self, value, **kwargs): + # type: (str, Any) -> None + self.raw_id = kwargs.get('raw_id') + self.properties = PhoneNumberProperties(value=value) + + +class UnknownIdentifier(object): + """Represents an identifier of an unknown type. + + It will be encountered in communications with endpoints that are not + identifiable by this version of the SDK. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping properties: The properties of the identifier. + :param str identifier: The ID of the identifier. + """ + kind = CommunicationIdentifierKind.UNKNOWN + + def __init__(self, identifier): + # type: (str) -> None + self.raw_id = identifier + self.properties = {} + + +MicrosoftTeamsUserProperties = TypedDict( + 'MicrosoftTeamsUserProperties', + user_id=str, + is_anonymous=bool, + cloud=Union[CommunicationCloudEnvironment, str] +) + + +class MicrosoftTeamsUserIdentifier(object): + """Represents an identifier for a Microsoft Teams user. + + :ivar str raw_id: Optional raw ID of the identifier. + :ivar kind: The type of identifier. + :vartype kind: str or CommunicationIdentifierKind + :ivar Mapping properties: The properties of the identifier. + The keys in this mapping include: + - `user_id`(str): The id of the Microsoft Teams user. If the user isn't anonymous, + the id is the AAD object id of the user. + - `is_anonymous` (bool): Set this to true if the user is anonymous for example when joining + a meeting with a share link. + - `cloud` (str): Cloud environment that this identifier belongs to. + + :param str user_id: Microsoft Teams user id. + :keyword bool is_anonymous: `True` if the identifier is anonymous. Default value is `False`. + :keyword cloud: Cloud environment that the user belongs to. Default value is `PUBLIC`. + :paramtype cloud: str or ~azure.communication.chat.CommunicationCloudEnvironment + """ + kind = CommunicationIdentifierKind.MICROSOFT_TEAMS_USER + + def __init__(self, user_id, **kwargs): + # type: (str, Any) -> None + self.raw_id = kwargs.get('raw_id') + self.properties = MicrosoftTeamsUserProperties( + user_id=user_id, + is_anonymous=kwargs.get('is_anonymous', False), + cloud=kwargs.get('cloud') or CommunicationCloudEnvironment.PUBLIC + ) diff --git a/src/communication/azext_communication/vendored_sdks/sms/_shared/policy.py b/src/communication/azext_communication/vendored_sdks/sms/_shared/policy.py new file mode 100644 index 00000000000..b2a0de8d423 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_shared/policy.py @@ -0,0 +1,91 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import hashlib +import urllib +import base64 +import hmac +from azure.core.pipeline.policies import SansIOHTTPPolicy +from .utils import get_current_utc_time + +class HMACCredentialsPolicy(SansIOHTTPPolicy): + """Implementation of HMAC authentication policy. + """ + + def __init__(self, + host, # type: str + access_key, # type: str + decode_url=False # type: bool + ): + # type: (...) -> None + super(HMACCredentialsPolicy, self).__init__() + + if host.startswith("https://"): + self._host = host.replace("https://", "") + + if host.startswith("http://"): + self._host = host.replace("http://", "") + + self._access_key = access_key + self._decode_url = decode_url + + def _compute_hmac(self, + value # type: str + ): + decoded_secret = base64.b64decode(self._access_key) + digest = hmac.new( + decoded_secret, value.encode("utf-8"), hashlib.sha256 + ).digest() + + return base64.b64encode(digest).decode("utf-8") + + def _sign_request(self, request): + verb = request.http_request.method.upper() + + # Get the path and query from url, which looks like https://host/path/query + query_url = str(request.http_request.url[len(self._host) + 8:]) + + if self._decode_url: + query_url = urllib.parse.unquote(query_url) + + signed_headers = "date;host;x-ms-content-sha256" + + utc_now = get_current_utc_time() + if request.http_request.body is None: + request.http_request.body = "" + content_digest = hashlib.sha256( + (request.http_request.body.encode("utf-8")) + ).digest() + content_hash = base64.b64encode(content_digest).decode("utf-8") + + string_to_sign = ( + verb + + "\n" + + query_url + + "\n" + + utc_now + + ";" + + self._host + + ";" + + content_hash + ) + + signature = self._compute_hmac(string_to_sign) + + signature_header = { + "Date": utc_now, + "x-ms-content-sha256": content_hash, + "x-ms-return-client-request-id": "true", + "Authorization": "HMAC-SHA256 SignedHeaders=" +\ + signed_headers + "&Signature=" + signature, + } + + request.http_request.headers.update(signature_header) + + return request + + def on_request(self, request): + self._sign_request(request) diff --git a/src/communication/azext_communication/vendored_sdks/sms/_shared/user_credential.py b/src/communication/azext_communication/vendored_sdks/sms/_shared/user_credential.py new file mode 100644 index 00000000000..9b5f17dcc95 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_shared/user_credential.py @@ -0,0 +1,86 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from threading import Lock, Condition +from datetime import timedelta +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) + +from .utils import get_current_utc_as_int +from .user_token_refresh_options import CommunicationTokenRefreshOptions + + +class CommunicationTokenCredential(object): + """Credential type used for authenticating to an Azure Communication service. + :param str token: The token used to authenticate to an Azure Communication service + :keyword token_refresher: The token refresher to provide capacity to fetch fresh token + :raises: TypeError + """ + + _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2 + + def __init__(self, + token, # type: str + **kwargs + ): + token_refresher = kwargs.pop('token_refresher', None) + communication_token_refresh_options = CommunicationTokenRefreshOptions(token=token, + token_refresher=token_refresher) + self._token = communication_token_refresh_options.get_token() + self._token_refresher = communication_token_refresh_options.get_token_refresher() + self._lock = Condition(Lock()) + self._some_thread_refreshing = False + + def get_token(self, *scopes, **kwargs): # pylint: disable=unused-argument + # type (*str, **Any) -> AccessToken + """The value of the configured token. + :rtype: ~azure.core.credentials.AccessToken + """ + + if not self._token_refresher or not self._token_expiring(): + return self._token + + should_this_thread_refresh = False + + with self._lock: + while self._token_expiring(): + if self._some_thread_refreshing: + if self._is_currenttoken_valid(): + return self._token + + self._wait_till_inprogress_thread_finish_refreshing() + else: + should_this_thread_refresh = True + self._some_thread_refreshing = True + break + + if should_this_thread_refresh: + try: + newtoken = self._token_refresher() # pylint:disable=not-callable + + with self._lock: + self._token = newtoken + self._some_thread_refreshing = False + self._lock.notify_all() + except: + with self._lock: + self._some_thread_refreshing = False + self._lock.notify_all() + + raise + return self._token + + def _wait_till_inprogress_thread_finish_refreshing(self): + self._lock.release() + self._lock.acquire() + + def _token_expiring(self): + return self._token.expires_on - get_current_utc_as_int() <\ + timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES).total_seconds() + + def _is_currenttoken_valid(self): + return get_current_utc_as_int() < self._token.expires_on diff --git a/src/communication/azext_communication/vendored_sdks/sms/_shared/user_credential_async.py b/src/communication/azext_communication/vendored_sdks/sms/_shared/user_credential_async.py new file mode 100644 index 00000000000..52a99e7a4b6 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_shared/user_credential_async.py @@ -0,0 +1,95 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from asyncio import Condition, Lock +from datetime import timedelta +from typing import ( # pylint: disable=unused-import + cast, + Tuple, + Any +) + +from .utils import get_current_utc_as_int +from .user_token_refresh_options import CommunicationTokenRefreshOptions + + +class CommunicationTokenCredential(object): + """Credential type used for authenticating to an Azure Communication service. + :param str token: The token used to authenticate to an Azure Communication service + :keyword token_refresher: The async token refresher to provide capacity to fetch fresh token + :raises: TypeError + """ + + _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2 + + def __init__(self, token: str, **kwargs: Any): + token_refresher = kwargs.pop('token_refresher', None) + communication_token_refresh_options = CommunicationTokenRefreshOptions(token=token, + token_refresher=token_refresher) + self._token = communication_token_refresh_options.get_token() + self._token_refresher = communication_token_refresh_options.get_token_refresher() + self._lock = Condition(Lock()) + self._some_thread_refreshing = False + + async def get_token(self, *scopes, **kwargs): # pylint: disable=unused-argument + # type (*str, **Any) -> AccessToken + """The value of the configured token. + :rtype: ~azure.core.credentials.AccessToken + """ + if not self._token_refresher or not self._token_expiring(): + return self._token + + should_this_thread_refresh = False + + async with self._lock: + + while self._token_expiring(): + if self._some_thread_refreshing: + if self._is_currenttoken_valid(): + return self._token + + await self._wait_till_inprogress_thread_finish_refreshing() + else: + should_this_thread_refresh = True + self._some_thread_refreshing = True + break + + + if should_this_thread_refresh: + try: + newtoken = await self._token_refresher() # pylint:disable=not-callable + + async with self._lock: + self._token = newtoken + self._some_thread_refreshing = False + self._lock.notify_all() + except: + async with self._lock: + self._some_thread_refreshing = False + self._lock.notify_all() + + raise + + return self._token + + async def _wait_till_inprogress_thread_finish_refreshing(self): + self._lock.release() + await self._lock.acquire() + + def _token_expiring(self): + return self._token.expires_on - get_current_utc_as_int() <\ + timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES).total_seconds() + + def _is_currenttoken_valid(self): + return get_current_utc_as_int() < self._token.expires_on + + async def close(self) -> None: + pass + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + await self.close() diff --git a/src/communication/azext_communication/vendored_sdks/sms/_shared/user_token_refresh_options.py b/src/communication/azext_communication/vendored_sdks/sms/_shared/user_token_refresh_options.py new file mode 100644 index 00000000000..6bdc0d45602 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_shared/user_token_refresh_options.py @@ -0,0 +1,36 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) +import six +from .utils import create_access_token + +class CommunicationTokenRefreshOptions(object): + """Options for refreshing CommunicationTokenCredential. + :param str token: The token used to authenticate to an Azure Communication service + :param token_refresher: The token refresher to provide capacity to fetch fresh token + :raises: TypeError + """ + + def __init__(self, + token, # type: str + token_refresher=None + ): + # type: (str) -> None + if not isinstance(token, six.string_types): + raise TypeError("token must be a string.") + self._token = token + self._token_refresher = token_refresher + + def get_token(self): + """Return the the serialized JWT token.""" + return create_access_token(self._token) + + def get_token_refresher(self): + """Return the token refresher to provide capacity to fetch fresh token.""" + return self._token_refresher diff --git a/src/communication/azext_communication/vendored_sdks/sms/_shared/utils.py b/src/communication/azext_communication/vendored_sdks/sms/_shared/utils.py new file mode 100644 index 00000000000..4da2691b9fb --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_shared/utils.py @@ -0,0 +1,121 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import base64 +import json +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) +from datetime import datetime +import calendar +from msrest.serialization import TZ_UTC +from azure.core.credentials import AccessToken + + +def _convert_datetime_to_utc_int(expires_on): + return int(calendar.timegm(expires_on.utctimetuple())) + +def parse_connection_str(conn_str): + # type: (str) -> Tuple[str, str, str, str] + if conn_str is None: + raise ValueError( + "Connection string is undefined." + ) + endpoint = None + shared_access_key = None + for element in conn_str.split(";"): + key, _, value = element.partition("=") + if key.lower() == "endpoint": + endpoint = value.rstrip("/") + elif key.lower() == "accesskey": + shared_access_key = value + if not all([endpoint, shared_access_key]): + raise ValueError( + "Invalid connection string. You can get the connection string from your resource page in the Azure Portal. " + "The format should be as follows: endpoint=https:///;accesskey=" + ) + left_slash_pos = cast(str, endpoint).find("//") + if left_slash_pos != -1: + host = cast(str, endpoint)[left_slash_pos + 2:] + else: + host = str(endpoint) + + return host, str(shared_access_key) + +def get_current_utc_time(): + # type: () -> str + return str(datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S ")) + "GMT" + + +def get_current_utc_as_int(): + # type: () -> int + current_utc_datetime = datetime.utcnow().replace(tzinfo=TZ_UTC) + return _convert_datetime_to_utc_int(current_utc_datetime) + +def create_access_token(token): + # type: (str) -> azure.core.credentials.AccessToken + """Creates an instance of azure.core.credentials.AccessToken from a + string token. The input string is jwt token in the following form: + .. + This method looks into the token_payload which is a json and extracts the expiry time + for that token and creates a tuple of type azure.core.credentials.AccessToken + (, ) + :param token: User token + :type token: str + :return: Instance of azure.core.credentials.AccessToken - token and expiry date of it + :rtype: ~azure.core.credentials.AccessToken + """ + + token_parse_err_msg = "Token is not formatted correctly" + parts = token.split(".") + + if len(parts) < 3: + raise ValueError(token_parse_err_msg) + + try: + padded_base64_payload = base64.b64decode(parts[1] + "==").decode('ascii') + payload = json.loads(padded_base64_payload) + return AccessToken(token, + _convert_datetime_to_utc_int(datetime.fromtimestamp(payload['exp']).replace(tzinfo=TZ_UTC))) + except ValueError: + raise ValueError(token_parse_err_msg) + +def get_authentication_policy( + endpoint, # type: str + credential, # type: TokenCredential or str + decode_url=False, # type: bool + is_async=False, # type: bool +): + # type: (...) -> BearerTokenCredentialPolicy or HMACCredentialPolicy + """Returns the correct authentication policy based + on which credential is being passed. + :param endpoint: The endpoint to which we are authenticating to. + :type endpoint: str + :param credential: The credential we use to authenticate to the service + :type credential: TokenCredential or str + :param isAsync: For async clients there is a need to decode the url + :type bool: isAsync or str + :rtype: ~azure.core.pipeline.policies.BearerTokenCredentialPolicy + ~HMACCredentialsPolicy + """ + + if credential is None: + raise ValueError("Parameter 'credential' must not be None.") + if hasattr(credential, "get_token"): + if is_async: + from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy + return AsyncBearerTokenCredentialPolicy( + credential, "https://communication.azure.com//.default") + from azure.core.pipeline.policies import BearerTokenCredentialPolicy + return BearerTokenCredentialPolicy( + credential, "https://communication.azure.com//.default") + if isinstance(credential, str): + from .._shared.policy import HMACCredentialsPolicy + return HMACCredentialsPolicy(endpoint, credential, decode_url=decode_url) + + raise TypeError("Unsupported credential: {}. Use an access token string to use HMACCredentialsPolicy" + "or a token credential from azure.identity".format(type(credential))) diff --git a/src/communication/azext_communication/vendored_sdks/sms/_sms_client.py b/src/communication/azext_communication/vendored_sdks/sms/_sms_client.py new file mode 100644 index 00000000000..1f34ebec70a --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_sms_client.py @@ -0,0 +1,134 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from uuid import uuid4 +from azure.core.tracing.decorator import distributed_trace +from azext_communication.vendored_sdks.sms._generated.models import ( + SendMessageRequest, + SmsRecipient, + SmsSendOptions, +) +from azext_communication.vendored_sdks.sms._models import SmsSendResult + +from ._generated._azure_communication_sms_service import AzureCommunicationSMSService +from ._shared.utils import parse_connection_str, get_authentication_policy, get_current_utc_time +from ._version import SDK_MONIKER + +class SmsClient(object): + """A client to interact with the AzureCommunicationService Sms gateway. + + This client provides operations to send an SMS via a phone number. + + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param TokenCredential credential: + The TokenCredential we use to authenticate against the service. + """ + def __init__( + self, endpoint_, # type: str + credential, # type: TokenCredential + **kwargs # type: Any + ): + # type: (...) -> None + endpoint = str(endpoint_) + try: + if not endpoint.lower().startswith('http'): + endpoint = "https://" + endpoint + except AttributeError: + raise ValueError("Account URL must be a string.") + + if not credential: + raise ValueError( + "invalid credential from connection string.") + + self._endpoint = endpoint + self._authentication_policy = get_authentication_policy(endpoint, credential) + self._sms_service_client = AzureCommunicationSMSService( + self._endpoint, + authentication_policy=self._authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs) + + @classmethod + def from_connection_string(cls, conn_str, # type: str + **kwargs # type: Any + ): # type: (...) -> SmsClient + """Create SmsClient from a Connection String. + + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of SmsClient. + :rtype: ~azure.communication.SmsClient + + .. admonition:: Example: + + .. literalinclude:: ../samples/sms_sample.py + :start-after: [START auth_from_connection_string] + :end-before: [END auth_from_connection_string] + :language: python + :dedent: 8 + :caption: Creating the SmsClient from a connection string. + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) + + @distributed_trace + def send(self, from_, # type: str + to, # type: Union[str, List[str]] + message, # type: str + **kwargs #type: Any + ): # type: (...) -> [SmsSendResult] + """Sends SMSs to phone numbers. + + :param str from_: The sender of the SMS. + :param to: The single recipient or the list of recipients of the SMS. + :type to: Union[str, List[str]] + :param str message: The message in the SMS + :keyword bool enable_delivery_report: Enable this flag to receive a delivery report for this + message on the Azure Resource EventGrid. + :keyword str tag: Use this field to provide metadata that will then be sent back in the corresponding + Delivery Report. + :return: A list of SmsSendResult. + :rtype: [~azure.communication.sms.models.SmsSendResult] + """ + + if isinstance(to, str): + to = [to] + + enable_delivery_report = kwargs.pop('enable_delivery_report', False) + tag = kwargs.pop('tag', None) + + sms_send_options = SmsSendOptions( + enable_delivery_report=enable_delivery_report, + tag=tag + ) + + request = SendMessageRequest( + from_property=from_, + sms_recipients=[ + SmsRecipient( + to=p, + repeatability_request_id=str(uuid4()), + repeatability_first_sent=get_current_utc_time() + ) for p in to + ], + message=message, + sms_send_options=sms_send_options, + **kwargs) + + return self._sms_service_client.sms.send( + request, + cls=lambda pr, r, e: [ + SmsSendResult( + to=item.to, + message_id=item.message_id, + http_status_code=item.http_status_code, + successful=item.successful, + error_message=item.error_message + ) for item in r.value + ], + **kwargs) diff --git a/src/communication/azext_communication/vendored_sdks/sms/_version.py b/src/communication/azext_communication/vendored_sdks/sms/_version.py new file mode 100644 index 00000000000..87c59b87eb3 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/_version.py @@ -0,0 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +VERSION = "1.0.1" + +SDK_MONIKER = "communication-sms/{}".format(VERSION) # type: str diff --git a/src/communication/azext_communication/vendored_sdks/sms/aio/__init__.py b/src/communication/azext_communication/vendored_sdks/sms/aio/__init__.py new file mode 100644 index 00000000000..247030c2657 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/aio/__init__.py @@ -0,0 +1,5 @@ +from ._sms_client_async import SmsClient + +__all__ = [ + 'SmsClient', +] diff --git a/src/communication/azext_communication/vendored_sdks/sms/aio/_sms_client_async.py b/src/communication/azext_communication/vendored_sdks/sms/aio/_sms_client_async.py new file mode 100644 index 00000000000..3d473c9b823 --- /dev/null +++ b/src/communication/azext_communication/vendored_sdks/sms/aio/_sms_client_async.py @@ -0,0 +1,147 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from uuid import uuid4 +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.communication.sms._generated.models import ( + SendMessageRequest, + SmsRecipient, + SmsSendOptions, +) +from azure.communication.sms._models import SmsSendResult + +from .._generated.aio._azure_communication_sms_service import AzureCommunicationSMSService +from .._shared.utils import parse_connection_str, get_authentication_policy, get_current_utc_time +from .._version import SDK_MONIKER + +class SmsClient(object): + """A client to interact with the AzureCommunicationService Sms gateway asynchronously. + + This client provides operations to send an SMS via a phone number. + + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param AsyncTokenCredential credential: + The AsyncTokenCredential we use to authenticate against the service. + """ + def __init__( + self, endpoint, # type: str + credential, # type: AsyncTokenCredential + **kwargs # type: Any + ): + # type: (...) -> None + try: + if not endpoint.lower().startswith('http'): + endpoint = "https://" + endpoint + except AttributeError: + raise ValueError("Account URL must be a string.") + + if not credential: + raise ValueError( + "invalid credential from connection string.") + + self._endpoint = endpoint + self._authentication_policy = get_authentication_policy(endpoint, credential, decode_url=True, is_async=True) + + self._sms_service_client = AzureCommunicationSMSService( + self._endpoint, + authentication_policy=self._authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs) + + @classmethod + def from_connection_string(cls, conn_str, # type: str + **kwargs # type: Any + ): # type: (...) -> SmsClient + """Create SmsClient from a Connection String. + + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of SmsClient. + :rtype: ~azure.communication.SmsClient + + .. admonition:: Example: + + .. literalinclude:: ../samples/sms_sample.py + :start-after: [START auth_from_connection_string] + :end-before: [END auth_from_connection_string] + :language: python + :dedent: 8 + :caption: Creating the SmsClient from a connection string. + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) + + @distributed_trace_async() + async def send(self, from_, # type: str + to, # type: Union[str, List[str]] + message, # type: str + **kwargs # type: Any + ): # type: (...) -> [SmsSendResult] + """Sends SMSs to phone numbers. + + :param str from_: The sender of the SMS. + :param to: The single recipient or the list of recipients of the SMS. + :type to: Union[str, List[str]] + :param str message: The message in the SMS + :keyword bool enable_delivery_report: Enable this flag to receive a delivery report for this + message on the Azure Resource EventGrid. + :keyword str tag: Use this field to provide metadata that will then be sent back in the corresponding + Delivery Report. + :return: A list of SmsSendResult. + :rtype: [~azure.communication.sms.models.SmsSendResult] + """ + + if isinstance(to, str): + to = [to] + + enable_delivery_report = kwargs.pop('enable_delivery_report', False) + tag = kwargs.pop('tag', None) + + sms_send_options = SmsSendOptions( + enable_delivery_report=enable_delivery_report, + tag=tag + ) + + request = SendMessageRequest( + from_property=from_, + sms_recipients=[ + SmsRecipient( + to=p, + repeatability_request_id=str(uuid4()), + repeatability_first_sent=get_current_utc_time() + ) for p in to + ], + message=message, + sms_send_options=sms_send_options, + **kwargs) + + return await self._sms_service_client.sms.send( + request, + cls=lambda pr, r, e: [ + SmsSendResult( + to=item.to, + message_id=item.message_id, + http_status_code=item.http_status_code, + successful=item.successful, + error_message=item.error_message + ) for item in r.value + ], + **kwargs) + + async def __aenter__(self) -> "SMSClient": + await self._sms_service_client.__aenter__() + return self + + async def __aexit__(self, *args: "Any") -> None: + await self.close() + + async def close(self) -> None: + """Close the :class: + `~azure.communication.sms.aio.SmsClient` session. + """ + await self._sms_service_client.__aexit__()