diff --git a/sdk/communication/azure-communication-messages/_meta.json b/sdk/communication/azure-communication-messages/_meta.json new file mode 100644 index 000000000000..7af754dff745 --- /dev/null +++ b/sdk/communication/azure-communication-messages/_meta.json @@ -0,0 +1,6 @@ +{ + "commit": "26af8eff710ea634da1b33fed369967d1c39b689", + "repository_url": "https://github.com/Azure/azure-rest-api-specs", + "typespec_src": "specification/communication/Communication.Messages", + "@azure-tools/typespec-python": "0.36.1" +} \ No newline at end of file diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/__init__.py b/sdk/communication/azure-communication-messages/azure/communication/messages/__init__.py index 3ce4f906d3df..d37d6656e82a 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/__init__.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/__init__.py @@ -5,16 +5,22 @@ # Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._client import NotificationMessagesClient -from ._client import MessageTemplateClient +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._client import NotificationMessagesClient # type: ignore +from ._client import MessageTemplateClient # type: ignore from ._version import VERSION __version__ = VERSION try: from ._patch import __all__ as _patch_all - from ._patch import * # pylint: disable=unused-wildcard-import + from ._patch import * except ImportError: _patch_all = [] from ._patch import patch_sdk as _patch_sdk @@ -23,6 +29,6 @@ "NotificationMessagesClient", "MessageTemplateClient", ] -__all__.extend([p for p in _patch_all if p not in __all__]) +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_api_versions.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_api_versions.py deleted file mode 100644 index dd1c75a2a5a8..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_api_versions.py +++ /dev/null @@ -1,16 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -from enum import Enum -from azure.core import CaseInsensitiveEnumMeta - - -class ApiVersion(str, Enum, metaclass=CaseInsensitiveEnumMeta): - V2023_08_24_PREVIEW = "2023-08-24-preview" - V2024_02_01 = "2024-02-01" - V2024_08_30 = "2024-08-30" - - -DEFAULT_VERSION = ApiVersion.V2024_08_30.value diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_client.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_client.py index d86dd1c37d16..2d02b1571b02 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_client.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/_client.py @@ -20,13 +20,10 @@ from ._serialization import Deserializer, Serializer if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports from azure.core.credentials import TokenCredential -class NotificationMessagesClient( - NotificationMessagesClientOperationsMixin -): # pylint: disable=client-accepts-api-version-keyword +class NotificationMessagesClient(NotificationMessagesClientOperationsMixin): """NotificationMessagesClient. :param endpoint: The communication resource, for example @@ -104,7 +101,7 @@ def __exit__(self, *exc_details: Any) -> None: self._client.__exit__(*exc_details) -class MessageTemplateClient(MessageTemplateClientOperationsMixin): # pylint: disable=client-accepts-api-version-keyword +class MessageTemplateClient(MessageTemplateClientOperationsMixin): """MessageTemplateClient. :param endpoint: The communication resource, for example diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_configuration.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_configuration.py index 068efb0f2106..d4547bdeaeaf 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_configuration.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/_configuration.py @@ -14,11 +14,10 @@ from ._version import VERSION if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports from azure.core.credentials import TokenCredential -class NotificationMessagesClientConfiguration: # pylint: disable=too-many-instance-attributes,name-too-long +class NotificationMessagesClientConfiguration: # pylint: disable=too-many-instance-attributes """Configuration for NotificationMessagesClient. Note that all parameters used to create this instance are saved as instance @@ -73,7 +72,7 @@ def _configure(self, **kwargs: Any) -> None: self.authentication_policy = self._infer_policy(**kwargs) -class MessageTemplateClientConfiguration: # pylint: disable=too-many-instance-attributes,name-too-long +class MessageTemplateClientConfiguration: # pylint: disable=too-many-instance-attributes """Configuration for MessageTemplateClient. Note that all parameters used to create this instance are saved as instance diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_model_base.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_model_base.py index 12ad7f29c71e..e6a2730f9276 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_model_base.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/_model_base.py @@ -1,10 +1,11 @@ +# pylint: disable=too-many-lines # 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. # -------------------------------------------------------------------------- -# pylint: disable=protected-access, arguments-differ, signature-differs, broad-except, too-many-lines +# pylint: disable=protected-access, broad-except import copy import calendar @@ -573,7 +574,7 @@ def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None: def copy(self) -> "Model": return Model(self.__dict__) - def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self: # pylint: disable=unused-argument + def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self: if f"{cls.__module__}.{cls.__qualname__}" not in cls._calculated: # we know the last nine classes in mro are going to be 'Model', '_MyMutableMapping', 'MutableMapping', # 'Mapping', 'Collection', 'Sized', 'Iterable', 'Container' and 'object' @@ -584,8 +585,8 @@ def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self: # pylint: di annotations = { k: v for mro_class in mros - if hasattr(mro_class, "__annotations__") # pylint: disable=no-member - for k, v in mro_class.__annotations__.items() # pylint: disable=no-member + if hasattr(mro_class, "__annotations__") + for k, v in mro_class.__annotations__.items() } for attr, rf in attr_to_rest_field.items(): rf._module = cls.__module__ @@ -600,8 +601,8 @@ def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self: # pylint: di def __init_subclass__(cls, discriminator: typing.Optional[str] = None) -> None: for base in cls.__bases__: - if hasattr(base, "__mapping__"): # pylint: disable=no-member - base.__mapping__[discriminator or cls.__name__] = cls # type: ignore # pylint: disable=no-member + if hasattr(base, "__mapping__"): + base.__mapping__[discriminator or cls.__name__] = cls # type: ignore @classmethod def _get_discriminator(cls, exist_discriminators) -> typing.Optional["_RestField"]: @@ -612,7 +613,7 @@ def _get_discriminator(cls, exist_discriminators) -> typing.Optional["_RestField @classmethod def _deserialize(cls, data, exist_discriminators): - if not hasattr(cls, "__mapping__"): # pylint: disable=no-member + if not hasattr(cls, "__mapping__"): return cls(data) discriminator = cls._get_discriminator(exist_discriminators) if discriminator is None: @@ -632,11 +633,11 @@ def _deserialize(cls, data, exist_discriminators): discriminator_value = data.find(xml_name).text # pyright: ignore else: discriminator_value = data.get(discriminator._rest_name) - mapped_cls = cls.__mapping__.get(discriminator_value, cls) # pyright: ignore # pylint: disable=no-member + mapped_cls = cls.__mapping__.get(discriminator_value, cls) # pyright: ignore return mapped_cls._deserialize(data, exist_discriminators) def as_dict(self, *, exclude_readonly: bool = False) -> typing.Dict[str, typing.Any]: - """Return a dict that can be JSONify using json.dump. + """Return a dict that can be turned into json using json.dump. :keyword bool exclude_readonly: Whether to remove the readonly properties. :returns: A dict JSON compatible object @@ -733,7 +734,7 @@ def _sorted_annotations(types: typing.List[typing.Any]) -> typing.List[typing.An ) -def _get_deserialize_callable_from_annotation( # pylint: disable=R0911, R0915, R0912 +def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-branches annotation: typing.Any, module: typing.Optional[str], rf: typing.Optional["_RestField"] = None, diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_operations/__init__.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_operations/__init__.py index 5a3a47e6ff3a..5e78e7b2ee7c 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_operations/__init__.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/_operations/__init__.py @@ -5,17 +5,23 @@ # Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._operations import NotificationMessagesClientOperationsMixin -from ._operations import MessageTemplateClientOperationsMixin +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._operations import NotificationMessagesClientOperationsMixin # type: ignore +from ._operations import MessageTemplateClientOperationsMixin # type: ignore from ._patch import __all__ as _patch_all -from ._patch import * # pylint: disable=unused-wildcard-import +from ._patch import * from ._patch import patch_sdk as _patch_sdk __all__ = [ "NotificationMessagesClientOperationsMixin", "MessageTemplateClientOperationsMixin", ] -__all__.extend([p for p in _patch_all if p not in __all__]) +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_operations/_operations.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_operations/_operations.py index 981550e5cdd0..e01c9610cac1 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_operations/_operations.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/_operations/_operations.py @@ -1,4 +1,3 @@ -# pylint: disable=too-many-lines,too-many-statements # coding=utf-8 # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. @@ -10,7 +9,7 @@ from io import IOBase import json import sys -from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Optional, Type, TypeVar, Union, overload +from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Optional, TypeVar, Union, overload import urllib.parse import uuid @@ -38,7 +37,7 @@ if sys.version_info >= (3, 9): from collections.abc import MutableMapping else: - from typing import MutableMapping # type: ignore # pylint: disable=ungrouped-imports + from typing import MutableMapping # type: ignore JSON = MutableMapping[str, Any] # pylint: disable=unsubscriptable-object T = TypeVar("T") ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] @@ -190,7 +189,7 @@ def send( :rtype: ~azure.communication.messages.models.SendMessageResult :raises ~azure.core.exceptions.HttpResponseError: """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { # pylint: disable=unsubscriptable-object + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, @@ -267,7 +266,7 @@ def download_media(self, id: str, **kwargs: Any) -> Iterator[bytes]: :rtype: Iterator[bytes] :raises ~azure.core.exceptions.HttpResponseError: """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { # pylint: disable=unsubscriptable-object + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, @@ -339,7 +338,7 @@ def list_templates(self, channel_id: str, **kwargs: Any) -> Iterable["_models.Me maxpagesize = kwargs.pop("maxpagesize", None) cls: ClsType[List[_models.MessageTemplateItem]] = kwargs.pop("cls", None) - error_map: MutableMapping[int, Type[HttpResponseError]] = { # pylint: disable=unsubscriptable-object + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_patch.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_patch.py index eb3162ba82cb..f7dd32510333 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_patch.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/_patch.py @@ -6,132 +6,9 @@ Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize """ -from typing import List, Any, Union -from urllib.parse import urlparse -from azure.core.credentials import TokenCredential, AzureKeyCredential -from ._shared.utils import parse_connection_str -from ._client import ( - NotificationMessagesClient as NotificationMessagesClientGenerated, - MessageTemplateClient as MessageTemplateClientGenerated, -) -from ._shared.auth_policy_utils import get_authentication_policy -from ._api_versions import DEFAULT_VERSION +from typing import List - -class NotificationMessagesClient(NotificationMessagesClientGenerated): - """A client to interact with the AzureCommunicationService Messaging service. - - This client provides operations to create and update jobs, policies and workers. - - :param str endpoint: - The endpoint of the Azure Communication resource. - :param Union[TokenCredential, AsyncTokenCredential] credential: - The credentials with which to authenticate - - :keyword api_version: Azure Communication Messaging API version. - Default value is "2023-11-01". - Note that overriding this default value may result in unsupported behavior. - """ - - def __init__(self, endpoint: str, credential: Union[TokenCredential, AzureKeyCredential], **kwargs: Any) -> None: - if not credential: - raise ValueError("credential can not be None") - - try: - if not endpoint.lower().startswith("http"): - endpoint = "https://" + endpoint - except AttributeError: - raise ValueError("Host URL must be a string") # pylint:disable=raise-missing-from - - parsed_url = urlparse(endpoint.rstrip("/")) - if not parsed_url.netloc: - raise ValueError("Invalid URL: {}".format(endpoint)) - - self._endpoint = endpoint - self._api_version = kwargs.pop("api_version", DEFAULT_VERSION) - self._authentication_policy = get_authentication_policy(endpoint, credential) - self._credential = credential - super().__init__( - self._endpoint, - self._credential, - authentication_policy=self._authentication_policy, - api_version=self._api_version, - **kwargs - ) - - @classmethod - def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "NotificationMessagesClient": - """Create NotificationMessagesClient from a Connection String. - - :param conn_str: Azure communication service connection string. Required. - :type conn_str: str - :return: instance of NotificationMessagesClient - :rtype: - ~azure.communication.messages.NotificationMessagesClient - """ - endpoint, access_key = parse_connection_str(conn_str) - return cls(endpoint, AzureKeyCredential(access_key), **kwargs) - - -class MessageTemplateClient(MessageTemplateClientGenerated): - """A client to interact with the AzureCommunicationService Messaging service. - - This client provides Advanced Messaging. - - :param str endpoint: - The endpoint of the Azure Communication resource. - :param Union[TokenCredential, AsyncTokenCredential] credential: - The credentials with which to authenticate - - :keyword api_version: Azure Communication Messages API version. Default value is "2024-02-01". - Note that overriding this default value may result in unsupported behavior. - :paramtype api_version: str - """ - - def __init__(self, endpoint: str, credential: Union[TokenCredential, AzureKeyCredential], **kwargs: Any) -> "None": - if not credential: - raise ValueError("credential can not be None") - - try: - if not endpoint.lower().startswith("http"): - endpoint = "https://" + endpoint - except AttributeError: - raise ValueError("Host URL must be a string") # pylint: disable=raise-missing-from - - parsed_url = urlparse(endpoint.rstrip("/")) - if not parsed_url.netloc: - raise ValueError("Invalid URL: {}".format(endpoint)) - - self._endpoint = endpoint - self._api_version = kwargs.pop("api_version", DEFAULT_VERSION) - self._authentication_policy = get_authentication_policy(endpoint, credential) - self._credential = credential - super().__init__( - self._endpoint, - self._credential, - authentication_policy=self._authentication_policy, - api_version=self._api_version, - **kwargs - ) - - @classmethod - def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "MessageTemplateClient": - """Create MessageTemplateClient from a Connection String. - - :param conn_str: Azure communication service connection string. Required. - :type conn_str: str - :return: instance of MessageTemplateClient - :rtype: - ~azure.communication.messages.MessageTemplateClient - """ - endpoint, access_key = parse_connection_str(conn_str) - return cls(endpoint, AzureKeyCredential(access_key), **kwargs) - - -__all__: List[str] = [ - "NotificationMessagesClient", - "MessageTemplateClient", -] # Add all objects you want publicly available to users at this package level +__all__: List[str] = [] # Add all objects you want publicly available to users at this package level def patch_sdk(): diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_serialization.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_serialization.py index 01a226bd7f14..ce17d1798ce7 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_serialization.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/_serialization.py @@ -227,7 +227,7 @@ class _FixedOffset(datetime.tzinfo): # type: ignore :param datetime.timedelta offset: offset in timedelta format """ - def __init__(self, offset): + def __init__(self, offset) -> None: self.__offset = offset def utcoffset(self, dt): @@ -507,7 +507,6 @@ def _flatten_subtype(cls, key, objects): def _classify(cls, response, objects): """Check the class _subtype_map for any child classes. We want to ignore any inherited _subtype_maps. - Remove the polymorphic key from the initial data. :param dict response: The initial data :param dict objects: The class objects @@ -519,7 +518,7 @@ def _classify(cls, response, objects): if not isinstance(response, ET.Element): rest_api_response_key = cls._get_rest_key_parts(subtype_key)[-1] - subtype_value = response.pop(rest_api_response_key, None) or response.pop(subtype_key, None) + subtype_value = response.get(rest_api_response_key, None) or response.get(subtype_key, None) else: subtype_value = xml_key_extractor(subtype_key, cls._attribute_map[subtype_key], response) if subtype_value: @@ -599,7 +598,7 @@ class Serializer(object): # pylint: disable=too-many-public-methods "multiple": lambda x, y: x % y != 0, } - def __init__(self, classes: Optional[Mapping[str, type]] = None): + def __init__(self, classes: Optional[Mapping[str, type]] = None) -> None: self.serialize_type = { "iso-8601": Serializer.serialize_iso, "rfc-1123": Serializer.serialize_rfc, @@ -1453,7 +1452,7 @@ class Deserializer(object): valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") - def __init__(self, classes: Optional[Mapping[str, type]] = None): + def __init__(self, classes: Optional[Mapping[str, type]] = None) -> None: self.deserialize_type = { "iso-8601": Deserializer.deserialize_iso, "rfc-1123": Deserializer.deserialize_rfc, diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/__init__.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/__init__.py deleted file mode 100644 index 5b396cd202e8..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# ------------------------------------------------------------------------- -# 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/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/auth_policy_utils.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/auth_policy_utils.py deleted file mode 100644 index c5172309c831..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/auth_policy_utils.py +++ /dev/null @@ -1,53 +0,0 @@ -# ------------------------------------------------------------------------ -# 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 Union -from azure.core.credentials import TokenCredential, AzureKeyCredential -from azure.core.credentials_async import AsyncTokenCredential -from azure.core.pipeline.policies import ( - AsyncBearerTokenCredentialPolicy, - BearerTokenCredentialPolicy, -) -from .._shared.policy import HMACCredentialsPolicy - - -def get_authentication_policy( - endpoint: str, - credential: Union[TokenCredential, AsyncTokenCredential, AzureKeyCredential, str], - decode_url: bool = False, - is_async: bool = False, -): - # type: (...) -> Union[AsyncBearerTokenCredentialPolicy, BearerTokenCredentialPolicy, HMACCredentialsPolicy] - """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: Union[TokenCredential, AsyncTokenCredential, AzureKeyCredential, str] - :param bool decode_url: `True` if there is a need to decode the url. Default value is `False` - :param bool is_async: For async clients there is a need to decode the url - - :return: Either AsyncBearerTokenCredentialPolicy or BearerTokenCredentialPolicy or HMACCredentialsPolicy - :rtype: ~azure.core.pipeline.policies.AsyncBearerTokenCredentialPolicy or - ~azure.core.pipeline.policies.BearerTokenCredentialPolicy or - ~azure.communication.messages.shared.policy.HMACCredentialsPolicy - """ - - if credential is None: - raise ValueError("Parameter 'credential' must not be None.") - if hasattr(credential, "get_token"): - if is_async: - return AsyncBearerTokenCredentialPolicy( - credential, "https://communication.azure.com//.default" # type: ignore - ) - return BearerTokenCredentialPolicy(credential, "https://communication.azure.com//.default") # type: ignore - if isinstance(credential, (AzureKeyCredential, str)): - return HMACCredentialsPolicy(endpoint, credential, decode_url=decode_url) - - raise TypeError( - f"Unsupported credential: {format(type(credential))}. Use an access token string to use HMACCredentialsPolicy" - "or a token credential from azure.identity" - ) diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/models.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/models.py deleted file mode 100644 index 43875e1197e5..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/models.py +++ /dev/null @@ -1,417 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -from enum import Enum -import warnings -from typing import Mapping, Optional, Union, Any, cast -from typing_extensions import Literal, TypedDict, Protocol, runtime_checkable - -from azure.core import CaseInsensitiveEnumMeta - - -class DeprecatedEnumMeta(CaseInsensitiveEnumMeta): - - def __getattribute__(cls, item): - if item.upper() == "MICROSOFT_BOT": - warnings.warn( - "MICROSOFT_BOT is deprecated and has been replaced by \ - MICROSOFT_TEAMS_APP identifier.", - DeprecationWarning, - ) - item = "MICROSOFT_TEAMS_APP" - return super().__getattribute__(item) - - -class CommunicationIdentifierKind(str, Enum, metaclass=DeprecatedEnumMeta): - """Communication Identifier Kind. - - For checking yet unknown identifiers it is better to rely on the presence of the `raw_id` property, - as new or existing distinct type identifiers always contain the `raw_id` property. - It is not advisable to rely on the `kind` property with a value `unknown`, - as it could become a new or existing distinct type in the future. - """ - - UNKNOWN = "unknown" - COMMUNICATION_USER = "communication_user" - PHONE_NUMBER = "phone_number" - MICROSOFT_TEAMS_USER = "microsoft_teams_user" - MICROSOFT_TEAMS_APP = "microsoft_teams_app" - - -class CommunicationCloudEnvironment(str, Enum, metaclass=CaseInsensitiveEnumMeta): - """The cloud environment that the identifier belongs to""" - - PUBLIC = "PUBLIC" - DOD = "DOD" - GCCH = "GCCH" - - -@runtime_checkable -class CommunicationIdentifier(Protocol): - """Communication Identifier.""" - - @property - def raw_id(self) -> str: - """The raw ID of the identifier.""" - ... - - @property - def kind(self) -> CommunicationIdentifierKind: - """The type of identifier.""" - ... - - @property - def properties(self) -> Mapping[str, Any]: - """The properties of the identifier.""" - ... - - -PHONE_NUMBER_PREFIX = "4:" -BOT_PREFIX = "28:" -BOT_PUBLIC_CLOUD_PREFIX = "28:orgid:" -BOT_DOD_CLOUD_PREFIX = "28:dod:" -BOT_DOD_CLOUD_GLOBAL_PREFIX = "28:dod-global:" -BOT_GCCH_CLOUD_PREFIX = "28:gcch:" -BOT_GCCH_CLOUD_GLOBAL_PREFIX = "28:gcch-global:" -TEAMS_APP_PUBLIC_CLOUD_PREFIX = "28:orgid:" -TEAMS_APP_DOD_CLOUD_PREFIX = "28:dod:" -TEAMS_APP_GCCH_CLOUD_PREFIX = "28:gcch:" -TEAMS_USER_ANONYMOUS_PREFIX = "8:teamsvisitor:" -TEAMS_USER_PUBLIC_CLOUD_PREFIX = "8:orgid:" -TEAMS_USER_DOD_CLOUD_PREFIX = "8:dod:" -TEAMS_USER_GCCH_CLOUD_PREFIX = "8:gcch:" -ACS_USER_PREFIX = "8:acs:" -ACS_USER_DOD_CLOUD_PREFIX = "8:dod-acs:" -ACS_USER_GCCH_CLOUD_PREFIX = "8:gcch-acs:" -SPOOL_USER_PREFIX = "8:spool:" - - -class CommunicationUserProperties(TypedDict): - """Dictionary of properties for a CommunicationUserIdentifier.""" - - id: str - """ID of the Communication user as returned from Azure Communication Identity.""" - - -class CommunicationUserIdentifier: - """Represents a user in Azure Communication Service.""" - - kind: Literal[CommunicationIdentifierKind.COMMUNICATION_USER] = CommunicationIdentifierKind.COMMUNICATION_USER - """The type of identifier.""" - properties: CommunicationUserProperties - """The properties of the identifier.""" - raw_id: str - """The raw ID of the identifier.""" - - def __init__(self, id: str, **kwargs: Any) -> None: - """ - :param str id: ID of the Communication user as returned from Azure Communication Identity. - :keyword str raw_id: The raw ID of the identifier. If not specified, the 'id' value will be used. - """ - self.properties = CommunicationUserProperties(id=id) - raw_id: Optional[str] = kwargs.get("raw_id") - self.raw_id = raw_id if raw_id is not None else id - - def __eq__(self, other): - try: - if other.raw_id: - return self.raw_id == other.raw_id - return self.raw_id == other.properties["id"] - except Exception: # pylint: disable=broad-except - return False - - -class PhoneNumberProperties(TypedDict): - """Dictionary of properties for a PhoneNumberIdentifier.""" - - value: str - """The phone number in E.164 format.""" - - -class PhoneNumberIdentifier: - """Represents a phone number.""" - - kind: Literal[CommunicationIdentifierKind.PHONE_NUMBER] = CommunicationIdentifierKind.PHONE_NUMBER - """The type of identifier.""" - properties: PhoneNumberProperties - """The properties of the identifier.""" - raw_id: str - """The raw ID of the identifier.""" - - def __init__(self, value: str, **kwargs: Any) -> None: - """ - :param str value: The phone number. - :keyword str raw_id: The raw ID of the identifier. If not specified, this will be constructed from - the 'value' parameter. - """ - self.properties = PhoneNumberProperties(value=value) - raw_id: Optional[str] = kwargs.get("raw_id") - self.raw_id = raw_id if raw_id is not None else self._format_raw_id(self.properties) - - def __eq__(self, other): - try: - if other.raw_id: - return self.raw_id == other.raw_id - return self.raw_id == self._format_raw_id(other.properties) - except Exception: # pylint:disable=broad-except - return False - - def _format_raw_id(self, properties: PhoneNumberProperties) -> str: - # We just assume correct E.164 format here because - # validation should only happen server-side, not client-side. - value = properties["value"] - return f"{PHONE_NUMBER_PREFIX}{value}" - - -class UnknownIdentifier: - """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. - - For checking yet unknown identifiers it is better to rely on the presence of the `raw_id` property, - as new or existing distinct type identifiers always contain the `raw_id` property. - It is not advisable to rely on the `kind` property with a value `unknown`, - as it could become a new or existing distinct type in the future. - """ - - kind: Literal[CommunicationIdentifierKind.UNKNOWN] = CommunicationIdentifierKind.UNKNOWN - """The type of identifier.""" - properties: Mapping[str, Any] - """The properties of the identifier.""" - raw_id: str - """The raw ID of the identifier.""" - - def __init__(self, identifier: str) -> None: - """ - :param str identifier: The ID of the identifier. - """ - self.raw_id = identifier - self.properties = {} - - def __eq__(self, other): - try: - return self.raw_id == other.raw_id - except AttributeError: - return False - - -class MicrosoftTeamsUserProperties(TypedDict): - """Dictionary of properties for a MicrosoftTeamsUserIdentifier.""" - - 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: Union[CommunicationCloudEnvironment, str] - """Cloud environment that this identifier belongs to.""" - - -class MicrosoftTeamsUserIdentifier: - """Represents an identifier for a Microsoft Teams user.""" - - kind: Literal[CommunicationIdentifierKind.MICROSOFT_TEAMS_USER] = CommunicationIdentifierKind.MICROSOFT_TEAMS_USER - """The type of identifier.""" - properties: MicrosoftTeamsUserProperties - """The properties of the identifier.""" - raw_id: str - """The raw ID of the identifier.""" - - def __init__(self, user_id: str, **kwargs: Any) -> None: - """ - :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 - :keyword str raw_id: The raw ID of the identifier. If not specified, this value will be constructed from - the other properties. - """ - self.properties = MicrosoftTeamsUserProperties( - user_id=user_id, - is_anonymous=kwargs.get("is_anonymous", False), - cloud=kwargs.get("cloud") or CommunicationCloudEnvironment.PUBLIC, - ) - raw_id: Optional[str] = kwargs.get("raw_id") - self.raw_id = raw_id if raw_id is not None else self._format_raw_id(self.properties) - - def __eq__(self, other): - try: - if other.raw_id: - return self.raw_id == other.raw_id - return self.raw_id == self._format_raw_id(other.properties) - except Exception: # pylint: disable=broad-except - return False - - def _format_raw_id(self, properties: MicrosoftTeamsUserProperties) -> str: - user_id = properties["user_id"] - if properties["is_anonymous"]: - return f"{TEAMS_USER_ANONYMOUS_PREFIX}{user_id}" - cloud = properties["cloud"] - if cloud == CommunicationCloudEnvironment.DOD: - return f"{TEAMS_USER_DOD_CLOUD_PREFIX}{user_id}" - if cloud == CommunicationCloudEnvironment.GCCH: - return f"{TEAMS_USER_GCCH_CLOUD_PREFIX}{user_id}" - if cloud == CommunicationCloudEnvironment.PUBLIC: - return f"{TEAMS_USER_PUBLIC_CLOUD_PREFIX}{user_id}" - return f"{TEAMS_USER_PUBLIC_CLOUD_PREFIX}{user_id}" - - -class MicrosoftTeamsAppProperties(TypedDict): - """Dictionary of properties for a MicrosoftTeamsAppIdentifier.""" - - app_id: str - """The id of the Microsoft Teams application.""" - cloud: Union[CommunicationCloudEnvironment, str] - """Cloud environment that this identifier belongs to.""" - - -class _botbackcompatdict(dict): - """Backwards compatible properties.""" - - def __getitem__(self, __key: Any) -> Any: - try: - return super().__getitem__(__key) - except KeyError: - if __key == "bot_id": - return super().__getitem__("app_id") - if __key == "is_resource_account_configured": - return True - raise - - -class MicrosoftTeamsAppIdentifier: - """Represents an identifier for a Microsoft Teams application.""" - - kind: Literal[CommunicationIdentifierKind.MICROSOFT_TEAMS_APP] = CommunicationIdentifierKind.MICROSOFT_TEAMS_APP - """The type of identifier.""" - properties: MicrosoftTeamsAppProperties - """The properties of the identifier.""" - raw_id: str - """The raw ID of the identifier.""" - - def __init__(self, app_id: str, **kwargs: Any) -> None: - """ - :param str app_id: Microsoft Teams application id. - :keyword cloud: Cloud environment that the application belongs to. Default value is `PUBLIC`. - :paramtype cloud: str or ~azure.communication.chat.CommunicationCloudEnvironment - :keyword str raw_id: The raw ID of the identifier. If not specified, this value will be constructed - from the other properties. - """ - self.properties = cast( - MicrosoftTeamsAppProperties, - _botbackcompatdict( - app_id=app_id, - cloud=kwargs.get("cloud") or CommunicationCloudEnvironment.PUBLIC, - ), - ) - raw_id: Optional[str] = kwargs.get("raw_id") - self.raw_id = raw_id if raw_id is not None else self._format_raw_id(self.properties) - - def __eq__(self, other): - try: - if other.raw_id: - return self.raw_id == other.raw_id - return self.raw_id == self._format_raw_id(other.properties) - except Exception: # pylint: disable=broad-except - return False - - def _format_raw_id(self, properties: MicrosoftTeamsAppProperties) -> str: - app_id = properties["app_id"] - cloud = properties["cloud"] - if cloud == CommunicationCloudEnvironment.DOD: - return f"{TEAMS_APP_DOD_CLOUD_PREFIX}{app_id}" - if cloud == CommunicationCloudEnvironment.GCCH: - return f"{TEAMS_APP_GCCH_CLOUD_PREFIX}{app_id}" - return f"{TEAMS_APP_PUBLIC_CLOUD_PREFIX}{app_id}" - - -class _MicrosoftBotIdentifier(MicrosoftTeamsAppIdentifier): - """Represents an identifier for a Microsoft bot. - - DEPRECATED. Only used in cases of backwards compatibility. - """ - - def __init__(self, bot_id, **kwargs): - """ - :param str bot_id: Microsoft bot id. - :keyword bool is_resource_account_configured: `False` if the identifier is global. - Default value is `True` for tennantzed bots. - :keyword cloud: Cloud environment that the bot belongs to. Default value is `PUBLIC`. - :paramtype cloud: str or ~azure.communication.chat.CommunicationCloudEnvironment - """ - warnings.warn( - "The MicrosoftBotIdentifier is deprecated and has been replaced by MicrosoftTeamsAppIdentifier.", - DeprecationWarning, - ) - super().__init__(bot_id, **kwargs) - - -def identifier_from_raw_id(raw_id: str) -> CommunicationIdentifier: # pylint: disable=too-many-return-statements - """ - Creates a CommunicationIdentifier from a given raw ID. - - When storing raw IDs use this function to restore the identifier that was encoded in the raw ID. - - :param str raw_id: A raw ID to construct the CommunicationIdentifier from. - :return: The CommunicationIdentifier parsed from the raw_id. - :rtype: CommunicationIdentifier - """ - if raw_id.startswith(PHONE_NUMBER_PREFIX): - return PhoneNumberIdentifier(value=raw_id[len(PHONE_NUMBER_PREFIX) :], raw_id=raw_id) - - segments = raw_id.split(":", maxsplit=2) - if len(segments) < 3: - return UnknownIdentifier(identifier=raw_id) - - prefix = f"{segments[0]}:{segments[1]}:" - suffix = segments[2] - if prefix == TEAMS_USER_ANONYMOUS_PREFIX: - return MicrosoftTeamsUserIdentifier(user_id=suffix, is_anonymous=True, raw_id=raw_id) - if prefix == TEAMS_USER_PUBLIC_CLOUD_PREFIX: - return MicrosoftTeamsUserIdentifier( - user_id=suffix, - is_anonymous=False, - cloud=CommunicationCloudEnvironment.PUBLIC, - raw_id=raw_id, - ) - if prefix == TEAMS_USER_DOD_CLOUD_PREFIX: - return MicrosoftTeamsUserIdentifier( - user_id=suffix, - is_anonymous=False, - cloud=CommunicationCloudEnvironment.DOD, - raw_id=raw_id, - ) - if prefix == TEAMS_USER_GCCH_CLOUD_PREFIX: - return MicrosoftTeamsUserIdentifier( - user_id=suffix, - is_anonymous=False, - cloud=CommunicationCloudEnvironment.GCCH, - raw_id=raw_id, - ) - if prefix == TEAMS_APP_PUBLIC_CLOUD_PREFIX: - return MicrosoftTeamsAppIdentifier( - app_id=suffix, - cloud=CommunicationCloudEnvironment.PUBLIC, - raw_id=raw_id, - ) - if prefix == TEAMS_APP_DOD_CLOUD_PREFIX: - return MicrosoftTeamsAppIdentifier( - app_id=suffix, - cloud=CommunicationCloudEnvironment.DOD, - raw_id=raw_id, - ) - if prefix == TEAMS_APP_GCCH_CLOUD_PREFIX: - return MicrosoftTeamsAppIdentifier( - app_id=suffix, - cloud=CommunicationCloudEnvironment.GCCH, - raw_id=raw_id, - ) - if prefix in [ - ACS_USER_PREFIX, - ACS_USER_DOD_CLOUD_PREFIX, - ACS_USER_GCCH_CLOUD_PREFIX, - SPOOL_USER_PREFIX, - ]: - return CommunicationUserIdentifier(id=raw_id, raw_id=raw_id) - return UnknownIdentifier(identifier=raw_id) diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/policy.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/policy.py deleted file mode 100644 index 79aa48136fd3..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/policy.py +++ /dev/null @@ -1,121 +0,0 @@ -# ------------------------------------------------------------------------ -# 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 urllib.parse import ParseResult, urlparse -from typing import Union -from azure.core.credentials import AzureKeyCredential -from azure.core.pipeline.policies import SansIOHTTPPolicy -from .utils import get_current_utc_time - - -class HMACCredentialsPolicy(SansIOHTTPPolicy): - """Implementation of HMAC authentication policy. - - :param str host: The host of the endpoint url for Azure Communication Service resource - :param access_key: The access key we use to authenticate to the service - :type access_key: str or AzureKeyCredential - :param bool decode_url: `True` if there is a need to decode the url. Default value is `False` - """ - - def __init__( - self, - host, # type: str - access_key, # type: Union[str, AzureKeyCredential] - 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 - ): - if isinstance(self._access_key, AzureKeyCredential): - decoded_secret = base64.b64decode(self._access_key.key) - else: - 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 - parsed_url: ParseResult = urlparse(request.http_request.url) - query_url = parsed_url.path - - if parsed_url.query: - query_url += "?" + parsed_url.query - - # Need URL() to get a correct encoded key value, from "%3A" to ":", when transport is in type AioHttpTransport. - # There's a similar scenario in azure-storage-blob and azure-appconfiguration, the check logic is from there. - try: - from yarl import URL - from azure.core.pipeline.transport import ( # pylint:disable=non-abstract-transport-import - AioHttpTransport, - ) - - if ( - isinstance(request.context.transport, AioHttpTransport) - or isinstance( - getattr(request.context.transport, "_transport", None), - AioHttpTransport, - ) - or isinstance( - getattr( - getattr(request.context.transport, "_transport", None), - "_transport", - None, - ), - AioHttpTransport, - ) - ): - query_url = str(URL(query_url)) - except (ImportError, TypeError): - pass - - if self._decode_url: - query_url = urllib.parse.unquote(query_url) - - signed_headers = "x-ms-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 = { - "x-ms-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/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/user_credential.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/user_credential.py deleted file mode 100644 index 54603e7b32ce..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/user_credential.py +++ /dev/null @@ -1,143 +0,0 @@ -# ------------------------------------------------------------------------- -# 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, Timer, TIMEOUT_MAX, Event -from datetime import timedelta -from typing import Any - -from .utils import get_current_utc_as_int -from .utils import create_access_token - - -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 sync token refresher to provide capacity to fetch a fresh token. - The returned token must be valid (expiration date must be in the future). - :paramtype token_refresher: Callable[[], AccessToken] - :keyword bool proactive_refresh: Whether to refresh the token proactively or not. - If the proactive refreshing is enabled ('proactive_refresh' is true), the credential will use - a background thread to attempt to refresh the token within 10 minutes before the cached token expires, - the proactive refresh will request a new token by calling the 'token_refresher' callback. - When 'proactive_refresh' is enabled, the Credential object must be either run within a context manager - or the 'close' method must be called once the object usage has been finished. - :raises: TypeError if paramater 'token' is not a string - :raises: ValueError if the 'proactive_refresh' is enabled without providing the 'token_refresher' callable. - """ - - _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2 - _DEFAULT_AUTOREFRESH_INTERVAL_MINUTES = 10 - - def __init__(self, token: str, **kwargs: Any): - if not isinstance(token, str): - raise TypeError("Token must be a string.") - self._token = create_access_token(token) - self._token_refresher = kwargs.pop("token_refresher", None) - self._proactive_refresh = kwargs.pop("proactive_refresh", False) - if self._proactive_refresh and self._token_refresher is None: - raise ValueError("When 'proactive_refresh' is True, 'token_refresher' must not be None.") - self._timer = None - self._lock = Condition(Lock()) - self._some_thread_refreshing = False - self._is_closed = Event() - - def get_token(self, *scopes, **kwargs): # pylint: disable=unused-argument - # type (*str, **Any) -> AccessToken - """The value of the configured token. - :param any scopes: Scopes to be added to the token. - :return: AccessToken - :rtype: ~azure.core.credentials.AccessToken - """ - if self._proactive_refresh and self._is_closed.is_set(): - raise RuntimeError("An instance of CommunicationTokenCredential cannot be reused once it has been closed.") - - if not self._token_refresher or not self._is_token_expiring_soon(self._token): - return self._token - self._update_token_and_reschedule() - return self._token - - def _update_token_and_reschedule(self): - should_this_thread_refresh = False - with self._lock: - while self._is_token_expiring_soon(self._token): - if self._some_thread_refreshing: - if self._is_token_valid(self._token): - return self._token - self._wait_till_lock_owner_finishes_refreshing() - else: - should_this_thread_refresh = True - self._some_thread_refreshing = True - break - - if should_this_thread_refresh: - try: - new_token = self._token_refresher() - if not self._is_token_valid(new_token): - raise ValueError("The token returned from the token_refresher is expired.") - with self._lock: - self._token = new_token - self._some_thread_refreshing = False - self._lock.notify_all() - except: - with self._lock: - self._some_thread_refreshing = False - self._lock.notify_all() - raise - if self._proactive_refresh: - self._schedule_refresh() - return self._token - - def _schedule_refresh(self): - if self._is_closed.is_set(): - return - if self._timer is not None: - self._timer.cancel() - - token_ttl = self._token.expires_on - get_current_utc_as_int() - - if self._is_token_expiring_soon(self._token): - # Schedule the next refresh for when it reaches a certain percentage of the remaining lifetime. - timespan = token_ttl // 2 - else: - # Schedule the next refresh for when it gets in to the soon-to-expire window. - timespan = token_ttl - timedelta(minutes=self._DEFAULT_AUTOREFRESH_INTERVAL_MINUTES).total_seconds() - if timespan <= TIMEOUT_MAX: - self._timer = Timer(timespan, self._update_token_and_reschedule) - self._timer.daemon = True - self._timer.start() - - def _wait_till_lock_owner_finishes_refreshing(self): - self._lock.release() - self._lock.acquire() - - def _is_token_expiring_soon(self, token): - if self._proactive_refresh: - interval = timedelta(minutes=self._DEFAULT_AUTOREFRESH_INTERVAL_MINUTES) - else: - interval = timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES) - return (token.expires_on - get_current_utc_as_int()) < interval.total_seconds() - - @classmethod - def _is_token_valid(cls, token): - return get_current_utc_as_int() < token.expires_on - - def __enter__(self): - if self._proactive_refresh: - if self._is_closed.is_set(): - raise RuntimeError( - "An instance of CommunicationTokenCredential cannot be reused once it has been closed." - ) - self._schedule_refresh() - return self - - def __exit__(self, *args): - self.close() - - def close(self) -> None: - if self._timer is not None: - self._timer.cancel() - self._timer = None - self._is_closed.set() diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/user_credential_async.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/user_credential_async.py deleted file mode 100644 index 15ad17da1a8c..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/user_credential_async.py +++ /dev/null @@ -1,148 +0,0 @@ -# ------------------------------------------------------------------------- -# 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, Event -from datetime import timedelta -from typing import Any -import sys - -from .utils import get_current_utc_as_int -from .utils import create_access_token -from .utils_async import AsyncTimer - - -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 a fresh token. - The returned token must be valid (expiration date must be in the future). - :paramtype token_refresher: Callable[[], Awaitable[AccessToken]] - :keyword bool proactive_refresh: Whether to refresh the token proactively or not. - If the proactive refreshing is enabled ('proactive_refresh' is true), the credential will use - a background thread to attempt to refresh the token within 10 minutes before the cached token expires, - the proactive refresh will request a new token by calling the 'token_refresher' callback. - When 'proactive_refresh is enabled', the Credential object must be either run within a context manager - or the 'close' method must be called once the object usage has been finished. - :raises: TypeError if paramater 'token' is not a string - :raises: ValueError if the 'proactive_refresh' is enabled without providing the 'token_refresher' function. - """ - - _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2 - _DEFAULT_AUTOREFRESH_INTERVAL_MINUTES = 10 - - def __init__(self, token: str, **kwargs: Any): - if not isinstance(token, str): - raise TypeError("Token must be a string.") - self._token = create_access_token(token) - self._token_refresher = kwargs.pop("token_refresher", None) - self._proactive_refresh = kwargs.pop("proactive_refresh", False) - if self._proactive_refresh and self._token_refresher is None: - raise ValueError("When 'proactive_refresh' is True, 'token_refresher' must not be None.") - self._timer = None - self._async_mutex = Lock() - if sys.version_info[:3] == (3, 10, 0): - # Workaround for Python 3.10 bug(https://bugs.python.org/issue45416): - getattr(self._async_mutex, "_get_loop", lambda: None)() - self._lock = Condition(self._async_mutex) - self._some_thread_refreshing = False - self._is_closed = Event() - - async def get_token(self, *scopes, **kwargs): # pylint: disable=unused-argument - # type (*str, **Any) -> AccessToken - """The value of the configured token. - :param any scopes: Scopes to be added to the token. - :return: AccessToken - :rtype: ~azure.core.credentials.AccessToken - """ - if self._proactive_refresh and self._is_closed.is_set(): - raise RuntimeError("An instance of CommunicationTokenCredential cannot be reused once it has been closed.") - - if not self._token_refresher or not self._is_token_expiring_soon(self._token): - return self._token - await self._update_token_and_reschedule() - return self._token - - async def _update_token_and_reschedule(self): - should_this_thread_refresh = False - async with self._lock: - while self._is_token_expiring_soon(self._token): - if self._some_thread_refreshing: - if self._is_token_valid(self._token): - return self._token - await self._wait_till_lock_owner_finishes_refreshing() - else: - should_this_thread_refresh = True - self._some_thread_refreshing = True - break - - if should_this_thread_refresh: - try: - new_token = await self._token_refresher() - if not self._is_token_valid(new_token): - raise ValueError("The token returned from the token_refresher is expired.") - async with self._lock: - self._token = new_token - self._some_thread_refreshing = False - self._lock.notify_all() - except: - async with self._lock: - self._some_thread_refreshing = False - self._lock.notify_all() - raise - if self._proactive_refresh: - self._schedule_refresh() - return self._token - - def _schedule_refresh(self): - if self._is_closed.is_set(): - return - if self._timer is not None: - self._timer.cancel() - - token_ttl = self._token.expires_on - get_current_utc_as_int() - - if self._is_token_expiring_soon(self._token): - # Schedule the next refresh for when it reaches a certain percentage of the remaining lifetime. - timespan = token_ttl // 2 - else: - # Schedule the next refresh for when it gets in to the soon-to-expire window. - timespan = token_ttl - timedelta(minutes=self._DEFAULT_AUTOREFRESH_INTERVAL_MINUTES).total_seconds() - - self._timer = AsyncTimer(timespan, self._update_token_and_reschedule) - self._timer.start() - - async def _wait_till_lock_owner_finishes_refreshing(self): - self._lock.release() - await self._lock.acquire() - - def _is_token_expiring_soon(self, token): - if self._proactive_refresh: - interval = timedelta(minutes=self._DEFAULT_AUTOREFRESH_INTERVAL_MINUTES) - else: - interval = timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES) - return (token.expires_on - get_current_utc_as_int()) < interval.total_seconds() - - @classmethod - def _is_token_valid(cls, token): - return get_current_utc_as_int() < token.expires_on - - async def __aenter__(self): - if self._proactive_refresh: - if self._is_closed.is_set(): - raise RuntimeError( - "An instance of CommunicationTokenCredential cannot be reused once it has been closed." - ) - self._schedule_refresh() - return self - - async def __aexit__(self, *args): - await self.close() - - async def close(self) -> None: - if self._timer is not None: - self._timer.cancel() - self._timer = None - self._is_closed.set() diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/utils.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/utils.py deleted file mode 100644 index 8576c31ddc56..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# ------------------------------------------------------------------------ -# 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 calendar -from typing import cast, Tuple, Optional -from datetime import datetime -from azure.core.serialization import TZ_UTC -from azure.core.credentials import AccessToken - - -def _convert_datetime_to_utc_int(input_datetime) -> int: - """ - Converts DateTime in local time to the Epoch in UTC in second. - - :param input_datetime: Input datetime - :type input_datetime: datetime - :return: Integer - :rtype: int - """ - return int(calendar.timegm(input_datetime.utctimetuple())) - - -def parse_connection_str(conn_str): - # type: (Optional[str]) -> Tuple[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.now(tz=TZ_UTC).strftime("%a, %d %b %Y %H:%M:%S ")) + "GMT" - - -def get_current_utc_as_int(): - # type: () -> int - current_utc_datetime = datetime.utcnow() - return _convert_datetime_to_utc_int(current_utc_datetime) - - -def create_access_token(token): - # type: (str) -> 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"], TZ_UTC)), - ) - except ValueError as val_error: - raise ValueError(token_parse_err_msg) from val_error diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/utils_async.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/utils_async.py deleted file mode 100644 index 86e0e04d273c..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_shared/utils_async.py +++ /dev/null @@ -1,31 +0,0 @@ -# ------------------------------------------------------------------------ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# ------------------------------------------------------------------------- - -import asyncio - - -class AsyncTimer: - """A non-blocking timer, that calls a function after a specified number of seconds: - :param int interval: time interval in seconds - :param callable callback: function to be called after the interval has elapsed - """ - - def __init__(self, interval, callback): - self._interval = interval - self._callback = callback - self._task = None - - def start(self): - self._task = asyncio.ensure_future(self._job()) - - async def _job(self): - await asyncio.sleep(self._interval) - await self._callback() - - def cancel(self): - if self._task is not None: - self._task.cancel() - self._task = None diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_types.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_types.py deleted file mode 100644 index 1fe86657cb0f..000000000000 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_types.py +++ /dev/null @@ -1,12 +0,0 @@ -# coding=utf-8 -# pylint: disable=too-many-lines -# -------------------------------------------------------------------------- -# 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) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from typing import Literal - -RepeatabilityResult = Literal["accepted", "rejected"] diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_vendor.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_vendor.py index ad8505048a22..877478019c76 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_vendor.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/_vendor.py @@ -11,7 +11,6 @@ from ._configuration import MessageTemplateClientConfiguration, NotificationMessagesClientConfiguration if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports from azure.core import PipelineClient from ._serialization import Deserializer, Serializer diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/_version.py b/sdk/communication/azure-communication-messages/azure/communication/messages/_version.py index ed0855dea5e6..0ec13ea52bbf 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/_version.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/_version.py @@ -6,4 +6,4 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -VERSION = "1.1.0" +VERSION = "1.0.0" diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/__init__.py b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/__init__.py index f6f164b43755..b23d0eda98d1 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/__init__.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/__init__.py @@ -5,13 +5,19 @@ # Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._client import NotificationMessagesClient -from ._client import MessageTemplateClient +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._client import NotificationMessagesClient # type: ignore +from ._client import MessageTemplateClient # type: ignore try: from ._patch import __all__ as _patch_all - from ._patch import * # pylint: disable=unused-wildcard-import + from ._patch import * except ImportError: _patch_all = [] from ._patch import patch_sdk as _patch_sdk @@ -20,6 +26,6 @@ "NotificationMessagesClient", "MessageTemplateClient", ] -__all__.extend([p for p in _patch_all if p not in __all__]) +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_client.py b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_client.py index 4aa7832ff659..b57ae4545761 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_client.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_client.py @@ -20,13 +20,10 @@ from ._operations import MessageTemplateClientOperationsMixin, NotificationMessagesClientOperationsMixin if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports from azure.core.credentials_async import AsyncTokenCredential -class NotificationMessagesClient( - NotificationMessagesClientOperationsMixin -): # pylint: disable=client-accepts-api-version-keyword +class NotificationMessagesClient(NotificationMessagesClientOperationsMixin): """NotificationMessagesClient. :param endpoint: The communication resource, for example @@ -108,7 +105,7 @@ async def __aexit__(self, *exc_details: Any) -> None: await self._client.__aexit__(*exc_details) -class MessageTemplateClient(MessageTemplateClientOperationsMixin): # pylint: disable=client-accepts-api-version-keyword +class MessageTemplateClient(MessageTemplateClientOperationsMixin): """MessageTemplateClient. :param endpoint: The communication resource, for example diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_configuration.py b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_configuration.py index 2e15e3c0079d..4b8a082fea10 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_configuration.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_configuration.py @@ -14,11 +14,10 @@ from .._version import VERSION if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports from azure.core.credentials_async import AsyncTokenCredential -class NotificationMessagesClientConfiguration: # pylint: disable=too-many-instance-attributes,name-too-long +class NotificationMessagesClientConfiguration: # pylint: disable=too-many-instance-attributes """Configuration for NotificationMessagesClient. Note that all parameters used to create this instance are saved as instance @@ -75,7 +74,7 @@ def _configure(self, **kwargs: Any) -> None: self.authentication_policy = self._infer_policy(**kwargs) -class MessageTemplateClientConfiguration: # pylint: disable=too-many-instance-attributes,name-too-long +class MessageTemplateClientConfiguration: # pylint: disable=too-many-instance-attributes """Configuration for MessageTemplateClient. Note that all parameters used to create this instance are saved as instance diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_operations/__init__.py b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_operations/__init__.py index 5a3a47e6ff3a..5e78e7b2ee7c 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_operations/__init__.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_operations/__init__.py @@ -5,17 +5,23 @@ # Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._operations import NotificationMessagesClientOperationsMixin -from ._operations import MessageTemplateClientOperationsMixin +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._operations import NotificationMessagesClientOperationsMixin # type: ignore +from ._operations import MessageTemplateClientOperationsMixin # type: ignore from ._patch import __all__ as _patch_all -from ._patch import * # pylint: disable=unused-wildcard-import +from ._patch import * from ._patch import patch_sdk as _patch_sdk __all__ = [ "NotificationMessagesClientOperationsMixin", "MessageTemplateClientOperationsMixin", ] -__all__.extend([p for p in _patch_all if p not in __all__]) +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_operations/_operations.py b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_operations/_operations.py index 3ddcda2c51ed..574698b25cdb 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_operations/_operations.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_operations/_operations.py @@ -1,4 +1,3 @@ -# pylint: disable=too-many-lines,too-many-statements # coding=utf-8 # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. @@ -9,7 +8,7 @@ from io import IOBase import json import sys -from typing import Any, AsyncIterable, AsyncIterator, Callable, Dict, IO, List, Optional, Type, TypeVar, Union, overload +from typing import Any, AsyncIterable, AsyncIterator, Callable, Dict, IO, List, Optional, TypeVar, Union, overload import urllib.parse from azure.core.async_paging import AsyncItemPaged, AsyncList @@ -41,7 +40,7 @@ if sys.version_info >= (3, 9): from collections.abc import MutableMapping else: - from typing import MutableMapping # type: ignore # pylint: disable=ungrouped-imports + from typing import MutableMapping # type: ignore JSON = MutableMapping[str, Any] # pylint: disable=unsubscriptable-object T = TypeVar("T") ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] @@ -110,7 +109,7 @@ async def send( :rtype: ~azure.communication.messages.models.SendMessageResult :raises ~azure.core.exceptions.HttpResponseError: """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { # pylint: disable=unsubscriptable-object + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, @@ -187,7 +186,7 @@ async def download_media(self, id: str, **kwargs: Any) -> AsyncIterator[bytes]: :rtype: AsyncIterator[bytes] :raises ~azure.core.exceptions.HttpResponseError: """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { # pylint: disable=unsubscriptable-object + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, @@ -260,7 +259,7 @@ def list_templates(self, channel_id: str, **kwargs: Any) -> AsyncIterable["_mode maxpagesize = kwargs.pop("maxpagesize", None) cls: ClsType[List[_models.MessageTemplateItem]] = kwargs.pop("cls", None) - error_map: MutableMapping[int, Type[HttpResponseError]] = { # pylint: disable=unsubscriptable-object + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_patch.py b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_patch.py index d69ae3068ceb..f7dd32510333 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_patch.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_patch.py @@ -6,139 +6,9 @@ Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize """ -from typing import ( - List, - Any, - Union, -) -from urllib.parse import urlparse -from azure.core.credentials import AzureKeyCredential -from azure.core.credentials_async import AsyncTokenCredential -from .._shared.utils import parse_connection_str -from ._client import ( - NotificationMessagesClient as NotificationMessagesClientGenerated, - MessageTemplateClient as MessageTemplateClientGenerated, -) -from .._api_versions import DEFAULT_VERSION -from .._shared.auth_policy_utils import get_authentication_policy +from typing import List - -class NotificationMessagesClient(NotificationMessagesClientGenerated): - """A client to interact with the AzureCommunicationService Messaging service. - - This client allows you to send and receive WhatsApp template messages using the - Azure Communication Services Messaging SDK. - - :param str endpoint: - The endpoint of the Azure Communication resource. - :param ~azure.core.credentials.AzureKeyCredential credential: - The credentials with which to authenticate - :keyword api_version: Azure Communication Messaging API version. - Default value is "2024-02-01". - Note that overriding this default value may result in unsupported behavior. - """ - - def __init__( - self, endpoint: str, credential: Union[AsyncTokenCredential, AzureKeyCredential], **kwargs: Any - ) -> None: - if not credential: - raise ValueError("credential can not be None") - - try: - if not endpoint.lower().startswith("http"): - endpoint = "https://" + endpoint - except AttributeError: - raise ValueError("Host URL must be a string") # pylint:disable=raise-missing-from - - parsed_url = urlparse(endpoint.rstrip("/")) - if not parsed_url.netloc: - raise ValueError("Invalid URL: {}".format(endpoint)) - - self._endpoint = endpoint - self._credential = credential - self._authentication_policy = get_authentication_policy(endpoint, credential) - self._api_version = kwargs.pop("api_version", DEFAULT_VERSION) - super().__init__( - self._endpoint, - self._credential, - authentication_policy=self._authentication_policy, - api_version=self._api_version, - **kwargs - ) - - @classmethod - def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "NotificationMessagesClient": - """Create NotificationMessagesClient from a Connection String. - - :param conn_str: Azure Communication Service endpoint. Required. - :type conn_str: str - :return: instance of NotificationMessagesClient. - :rtype: ~azure.communication.message.NotificationMessagesClient - """ - endpoint, access_key = parse_connection_str(conn_str) - return cls(endpoint, AzureKeyCredential(key=access_key), **kwargs) - - -class MessageTemplateClient(MessageTemplateClientGenerated): - """A client to interact with the AzureCommunicationService Messaging service. - - This client allows you to send and receive WhatsApp template - messages using the Azure Communication Services Messaging SDK. - - :param str endpoint: - The endpoint of the Azure Communication resource. - :param ~azure.core.credentials.AzureKeyCredential credential: - The credentials with which to authenticate - :keyword api_version: Azure Communication Messaging API version. Default value is "2024-02-01". - Note that overriding this default value may result in unsupported behavior. - :paramtype api_version: str - """ - - def __init__( - self, endpoint: str, credential: Union[AsyncTokenCredential, AzureKeyCredential], **kwargs: Any - ) -> "None": - if not credential: - raise ValueError("credential can not be None") - - try: - if not endpoint.lower().startswith("http"): - endpoint = "https://" + endpoint - except AttributeError: - raise ValueError("Host URL must be a string") # pylint: disable=raise-missing-from - - parsed_url = urlparse(endpoint.rstrip("/")) - if not parsed_url.netloc: - raise ValueError("Invalid URL: {}".format(endpoint)) - - self._endpoint = endpoint - self._credential = credential - self._authentication_policy = get_authentication_policy(endpoint, credential) - self._api_version = kwargs.pop("api_version", DEFAULT_VERSION) - super().__init__( - self._endpoint, - self._credential, - authentication_policy=self._authentication_policy, - api_version=self._api_version, - **kwargs - ) - - @classmethod - def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "MessageTemplateClient": - """Create MessageTemplateClient from a Connection String. - - :param conn_str: Azure Communication Service endpoint. Required. - :type conn_str: str - :return: instance of MessageTemplateClient. - :rtype: ~azure.communication.message.MessageTemplateClient - """ - endpoint, access_key = parse_connection_str(conn_str) - return cls(endpoint, AzureKeyCredential(access_key), **kwargs) - - -__all__: List[str] = [ - "NotificationMessagesClient", - "MessageTemplateClient", -] # Add all objects you want publicly available to users at this package level +__all__: List[str] = [] # Add all objects you want publicly available to users at this package level def patch_sdk(): diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_vendor.py b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_vendor.py index e5c42048e8a2..1ef61ca00c30 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_vendor.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/aio/_vendor.py @@ -11,7 +11,6 @@ from ._configuration import MessageTemplateClientConfiguration, NotificationMessagesClientConfiguration if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports from azure.core import AsyncPipelineClient from .._serialization import Deserializer, Serializer diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/models/__init__.py b/sdk/communication/azure-communication-messages/azure/communication/messages/models/__init__.py index 570a3c9a5efe..7d16e2be1174 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/models/__init__.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/models/__init__.py @@ -5,46 +5,59 @@ # Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._models import AudioNotificationContent -from ._models import DocumentNotificationContent -from ._models import ImageNotificationContent -from ._models import MessageReceipt -from ._models import MessageTemplate -from ._models import MessageTemplateBindings -from ._models import MessageTemplateDocument -from ._models import MessageTemplateImage -from ._models import MessageTemplateItem -from ._models import MessageTemplateLocation -from ._models import MessageTemplateQuickAction -from ._models import MessageTemplateText -from ._models import MessageTemplateValue -from ._models import MessageTemplateVideo -from ._models import NotificationContent -from ._models import SendMessageResult -from ._models import TemplateNotificationContent -from ._models import TextNotificationContent -from ._models import VideoNotificationContent -from ._models import WhatsAppMessageTemplateBindings -from ._models import WhatsAppMessageTemplateBindingsButton -from ._models import WhatsAppMessageTemplateBindingsComponent -from ._models import WhatsAppMessageTemplateItem +from typing import TYPE_CHECKING -from ._enums import CommunicationMessageKind -from ._enums import CommunicationMessagesChannel -from ._enums import MessageTemplateBindingsKind -from ._enums import MessageTemplateStatus -from ._enums import MessageTemplateValueKind -from ._enums import RepeatabilityResult -from ._enums import WhatsAppMessageButtonSubType +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + + +from ._models import ( # type: ignore + AudioNotificationContent, + DocumentNotificationContent, + ImageNotificationContent, + MediaNotificationContent, + MessageReceipt, + MessageTemplate, + MessageTemplateBindings, + MessageTemplateDocument, + MessageTemplateImage, + MessageTemplateItem, + MessageTemplateLocation, + MessageTemplateQuickAction, + MessageTemplateText, + MessageTemplateValue, + MessageTemplateVideo, + NotificationContent, + SendMessageResult, + TemplateNotificationContent, + TextNotificationContent, + VideoNotificationContent, + WhatsAppMessageTemplateBindings, + WhatsAppMessageTemplateBindingsButton, + WhatsAppMessageTemplateBindingsComponent, + WhatsAppMessageTemplateItem, +) + +from ._enums import ( # type: ignore + CommunicationMessageKind, + CommunicationMessagesChannel, + MessageTemplateBindingsKind, + MessageTemplateStatus, + MessageTemplateValueKind, + RepeatabilityResult, + WhatsAppMessageButtonSubType, +) from ._patch import __all__ as _patch_all -from ._patch import * # pylint: disable=unused-wildcard-import +from ._patch import * from ._patch import patch_sdk as _patch_sdk __all__ = [ "AudioNotificationContent", "DocumentNotificationContent", "ImageNotificationContent", + "MediaNotificationContent", "MessageReceipt", "MessageTemplate", "MessageTemplateBindings", @@ -73,5 +86,5 @@ "RepeatabilityResult", "WhatsAppMessageButtonSubType", ] -__all__.extend([p for p in _patch_all if p not in __all__]) +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/models/_enums.py b/sdk/communication/azure-communication-messages/azure/communication/messages/models/_enums.py index bec601169dbf..791a113fbe35 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/models/_enums.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/models/_enums.py @@ -33,14 +33,14 @@ class CommunicationMessageKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): class CommunicationMessagesChannel(str, Enum, metaclass=CaseInsensitiveEnumMeta): """The type of the communication messages channel.""" - WHATSAPP = "whatsApp" + WHATS_APP = "whatsApp" """The WhatsApp communication messages channel type.""" class MessageTemplateBindingsKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): """The type of the message template.""" - WHATSAPP = "whatsApp" + WHATS_APP = "whatsApp" """The WhatsApp template type.""" diff --git a/sdk/communication/azure-communication-messages/azure/communication/messages/models/_models.py b/sdk/communication/azure-communication-messages/azure/communication/messages/models/_models.py index 4e99c2b006fd..18e3a73e6323 100644 --- a/sdk/communication/azure-communication-messages/azure/communication/messages/models/_models.py +++ b/sdk/communication/azure-communication-messages/azure/communication/messages/models/_models.py @@ -1,11 +1,12 @@ -# coding=utf-8 # pylint: disable=too-many-lines +# 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) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=useless-super-delegation from typing import Any, Dict, List, Literal, Mapping, Optional, TYPE_CHECKING, Union, overload @@ -19,7 +20,6 @@ ) if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports from .. import models as _models @@ -59,16 +59,16 @@ def __init__( channel_registration_id: str, to: List[str], kind: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -102,16 +102,16 @@ def __init__( channel_registration_id: str, to: List[str], media_uri: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=CommunicationMessageKind.AUDIO, **kwargs) @@ -155,16 +155,16 @@ def __init__( media_uri: str, caption: Optional[str] = None, file_name: Optional[str] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=CommunicationMessageKind.DOCUMENT, **kwargs) @@ -203,16 +203,16 @@ def __init__( to: List[str], media_uri: str, content: Optional[str] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=CommunicationMessageKind.IMAGE, **kwargs) @@ -253,16 +253,16 @@ def __init__( to: List[str], media_uri: str, content: Optional[str] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=CommunicationMessageKind.IMAGE_V0, **kwargs) @@ -287,16 +287,16 @@ def __init__( *, message_id: str, to: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -334,16 +334,16 @@ def __init__( language: str, template_values: Optional[List["_models.MessageTemplateValue"]] = None, bindings: Optional["_models.MessageTemplateBindings"] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -368,16 +368,16 @@ def __init__( self, *, kind: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -410,16 +410,16 @@ def __init__( *, name: str, kind: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -457,16 +457,16 @@ def __init__( url: str, caption: Optional[str] = None, file_name: Optional[str] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=MessageTemplateValueKind.DOCUMENT, **kwargs) @@ -504,16 +504,16 @@ def __init__( url: str, caption: Optional[str] = None, file_name: Optional[str] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=MessageTemplateValueKind.IMAGE, **kwargs) @@ -557,16 +557,16 @@ def __init__( language: str, status: Union[str, "_models.MessageTemplateStatus"], kind: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -609,16 +609,16 @@ def __init__( longitude: float, location_name: Optional[str] = None, address: Optional[str] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=MessageTemplateValueKind.LOCATION, **kwargs) @@ -652,16 +652,16 @@ def __init__( name: str, text: Optional[str] = None, payload: Optional[str] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=MessageTemplateValueKind.QUICK_ACTION, **kwargs) @@ -689,16 +689,16 @@ def __init__( *, name: str, text: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=MessageTemplateValueKind.TEXT, **kwargs) @@ -736,16 +736,16 @@ def __init__( url: str, caption: Optional[str] = None, file_name: Optional[str] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=MessageTemplateValueKind.VIDEO, **kwargs) @@ -765,16 +765,16 @@ def __init__( self, *, receipts: List["_models.MessageReceipt"], - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -806,16 +806,16 @@ def __init__( channel_registration_id: str, to: List[str], template: "_models.MessageTemplate", - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=CommunicationMessageKind.TEMPLATE, **kwargs) @@ -847,16 +847,16 @@ def __init__( channel_registration_id: str, to: List[str], content: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=CommunicationMessageKind.TEXT, **kwargs) @@ -895,16 +895,16 @@ def __init__( to: List[str], media_uri: str, caption: Optional[str] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, kind=CommunicationMessageKind.VIDEO, **kwargs) @@ -914,7 +914,7 @@ class WhatsAppMessageTemplateBindings(MessageTemplateBindings, discriminator="wh All required parameters must be populated in order to send to server. :ivar kind: MessageTemplateBindings is whatsApp. Required. The WhatsApp template type. - :vartype kind: str or ~azure.communication.messages.models.WHATSAPP + :vartype kind: str or ~azure.communication.messages.models.WHATS_APP :ivar header: The header template bindings. :vartype header: list[~azure.communication.messages.models.WhatsAppMessageTemplateBindingsComponent] @@ -929,7 +929,7 @@ class WhatsAppMessageTemplateBindings(MessageTemplateBindings, discriminator="wh list[~azure.communication.messages.models.WhatsAppMessageTemplateBindingsButton] """ - kind: Literal[MessageTemplateBindingsKind.WHATSAPP] = rest_discriminator(name="kind") # type: ignore + kind: Literal[MessageTemplateBindingsKind.WHATS_APP] = rest_discriminator(name="kind") # type: ignore """MessageTemplateBindings is whatsApp. Required. The WhatsApp template type.""" header: Optional[List["_models.WhatsAppMessageTemplateBindingsComponent"]] = rest_field() """The header template bindings.""" @@ -948,17 +948,17 @@ def __init__( body: Optional[List["_models.WhatsAppMessageTemplateBindingsComponent"]] = None, footer: Optional[List["_models.WhatsAppMessageTemplateBindingsComponent"]] = None, buttons: Optional[List["_models.WhatsAppMessageTemplateBindingsButton"]] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation - super().__init__(*args, kind=MessageTemplateBindingsKind.WHATSAPP, **kwargs) + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, kind=MessageTemplateBindingsKind.WHATS_APP, **kwargs) class WhatsAppMessageTemplateBindingsButton(_model_base.Model): @@ -984,16 +984,16 @@ def __init__( *, sub_type: Union[str, "_models.WhatsAppMessageButtonSubType"], ref_value: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -1014,16 +1014,16 @@ def __init__( self, *, ref_value: str, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -1051,7 +1051,7 @@ class WhatsAppMessageTemplateItem(MessageTemplateItem, discriminator="whatsApp") content: Optional[Any] = rest_field() """WhatsApp platform's template content. This is the payload returned from WhatsApp API.""" - kind: Literal[CommunicationMessagesChannel.WHATSAPP] = rest_discriminator(name="kind") # type: ignore + kind: Literal[CommunicationMessagesChannel.WHATS_APP] = rest_discriminator(name="kind") # type: ignore """Message template response type is whatsApp. Required. The WhatsApp communication messages channel type.""" @@ -1062,14 +1062,14 @@ def __init__( language: str, status: Union[str, "_models.MessageTemplateStatus"], content: Optional[Any] = None, - ): ... + ) -> None: ... @overload - def __init__(self, mapping: Mapping[str, Any]): + def __init__(self, mapping: Mapping[str, Any]) -> None: """ :param mapping: raw JSON to initialize the model. :type mapping: Mapping[str, Any] """ - def __init__(self, *args: Any, **kwargs: Any) -> None: # pylint: disable=useless-super-delegation - super().__init__(*args, kind=CommunicationMessagesChannel.WHATSAPP, **kwargs) + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, kind=CommunicationMessagesChannel.WHATS_APP, **kwargs) diff --git a/sdk/communication/azure-communication-messages/generated_samples/stream_get_media.py b/sdk/communication/azure-communication-messages/generated_samples/stream_get_media.py new file mode 100644 index 000000000000..ee26f73be88d --- /dev/null +++ b/sdk/communication/azure-communication-messages/generated_samples/stream_get_media.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) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from azure.communication.messages import NotificationMessagesClient + +""" +# PREREQUISITES + pip install azure-communication-messages +# USAGE + python stream_get_media.py +""" + + +def main(): + client = NotificationMessagesClient( + endpoint="https://my-resource.communication.azure.com", + credential="CREDENTIAL", + ) + + response = client.download_media( + id="d19e68ec-bdd6-4a50-8dfb-cbb1642df6ab", + ) + print(response) + + +# x-ms-original-file: 2024-08-30/Stream_GetMedia.json +if __name__ == "__main__": + main() diff --git a/sdk/communication/azure-communication-messages/generated_tests/conftest.py b/sdk/communication/azure-communication-messages/generated_tests/conftest.py new file mode 100644 index 000000000000..381df64efbf4 --- /dev/null +++ b/sdk/communication/azure-communication-messages/generated_tests/conftest.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) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +import os +import pytest +from dotenv import load_dotenv +from devtools_testutils import ( + test_proxy, + add_general_regex_sanitizer, + add_body_key_sanitizer, + add_header_regex_sanitizer, +) + +load_dotenv() + + +# For security, please avoid record sensitive identity information in recordings +@pytest.fixture(scope="session", autouse=True) +def add_sanitizers(test_proxy): + notificationmessages_subscription_id = os.environ.get( + "NOTIFICATIONMESSAGES_SUBSCRIPTION_ID", "00000000-0000-0000-0000-000000000000" + ) + notificationmessages_tenant_id = os.environ.get( + "NOTIFICATIONMESSAGES_TENANT_ID", "00000000-0000-0000-0000-000000000000" + ) + notificationmessages_client_id = os.environ.get( + "NOTIFICATIONMESSAGES_CLIENT_ID", "00000000-0000-0000-0000-000000000000" + ) + notificationmessages_client_secret = os.environ.get( + "NOTIFICATIONMESSAGES_CLIENT_SECRET", "00000000-0000-0000-0000-000000000000" + ) + add_general_regex_sanitizer( + regex=notificationmessages_subscription_id, value="00000000-0000-0000-0000-000000000000" + ) + add_general_regex_sanitizer(regex=notificationmessages_tenant_id, value="00000000-0000-0000-0000-000000000000") + add_general_regex_sanitizer(regex=notificationmessages_client_id, value="00000000-0000-0000-0000-000000000000") + add_general_regex_sanitizer(regex=notificationmessages_client_secret, value="00000000-0000-0000-0000-000000000000") + + messagetemplate_subscription_id = os.environ.get( + "MESSAGETEMPLATE_SUBSCRIPTION_ID", "00000000-0000-0000-0000-000000000000" + ) + messagetemplate_tenant_id = os.environ.get("MESSAGETEMPLATE_TENANT_ID", "00000000-0000-0000-0000-000000000000") + messagetemplate_client_id = os.environ.get("MESSAGETEMPLATE_CLIENT_ID", "00000000-0000-0000-0000-000000000000") + messagetemplate_client_secret = os.environ.get( + "MESSAGETEMPLATE_CLIENT_SECRET", "00000000-0000-0000-0000-000000000000" + ) + add_general_regex_sanitizer(regex=messagetemplate_subscription_id, value="00000000-0000-0000-0000-000000000000") + add_general_regex_sanitizer(regex=messagetemplate_tenant_id, value="00000000-0000-0000-0000-000000000000") + add_general_regex_sanitizer(regex=messagetemplate_client_id, value="00000000-0000-0000-0000-000000000000") + add_general_regex_sanitizer(regex=messagetemplate_client_secret, value="00000000-0000-0000-0000-000000000000") + + add_header_regex_sanitizer(key="Set-Cookie", value="[set-cookie;]") + add_header_regex_sanitizer(key="Cookie", value="cookie;") + add_body_key_sanitizer(json_path="$..access_token", value="access_token") diff --git a/sdk/communication/azure-communication-messages/generated_tests/test_message_template.py b/sdk/communication/azure-communication-messages/generated_tests/test_message_template.py new file mode 100644 index 000000000000..dabf704a6444 --- /dev/null +++ b/sdk/communication/azure-communication-messages/generated_tests/test_message_template.py @@ -0,0 +1,24 @@ +# 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) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +import pytest +from devtools_testutils import recorded_by_proxy +from testpreparer import MessageTemplateClientTestBase, MessageTemplatePreparer + + +@pytest.mark.skip("you may need to update the auto-generated test case before run it") +class TestMessageTemplate(MessageTemplateClientTestBase): + @MessageTemplatePreparer() + @recorded_by_proxy + def test_list_templates(self, messagetemplate_endpoint): + client = self.create_client(endpoint=messagetemplate_endpoint) + response = client.list_templates( + channel_id="str", + ) + result = [r for r in response] + # please add some check logic here by yourself + # ... diff --git a/sdk/communication/azure-communication-messages/generated_tests/test_message_template_async.py b/sdk/communication/azure-communication-messages/generated_tests/test_message_template_async.py new file mode 100644 index 000000000000..17dc315792dd --- /dev/null +++ b/sdk/communication/azure-communication-messages/generated_tests/test_message_template_async.py @@ -0,0 +1,25 @@ +# 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) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +import pytest +from devtools_testutils.aio import recorded_by_proxy_async +from testpreparer import MessageTemplatePreparer +from testpreparer_async import MessageTemplateClientTestBaseAsync + + +@pytest.mark.skip("you may need to update the auto-generated test case before run it") +class TestMessageTemplateAsync(MessageTemplateClientTestBaseAsync): + @MessageTemplatePreparer() + @recorded_by_proxy_async + async def test_list_templates(self, messagetemplate_endpoint): + client = self.create_async_client(endpoint=messagetemplate_endpoint) + response = client.list_templates( + channel_id="str", + ) + result = [r async for r in response] + # please add some check logic here by yourself + # ... diff --git a/sdk/communication/azure-communication-messages/generated_tests/test_notification_messages.py b/sdk/communication/azure-communication-messages/generated_tests/test_notification_messages.py new file mode 100644 index 000000000000..23071ca986e6 --- /dev/null +++ b/sdk/communication/azure-communication-messages/generated_tests/test_notification_messages.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) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +import pytest +from devtools_testutils import recorded_by_proxy +from testpreparer import NotificationMessagesClientTestBase, NotificationMessagesPreparer + + +@pytest.mark.skip("you may need to update the auto-generated test case before run it") +class TestNotificationMessages(NotificationMessagesClientTestBase): + @NotificationMessagesPreparer() + @recorded_by_proxy + def test_send(self, notificationmessages_endpoint): + client = self.create_client(endpoint=notificationmessages_endpoint) + response = client.send( + body={"channelRegistrationId": "str", "kind": "audio", "mediaUri": "str", "to": ["str"]}, + ) + + # please add some check logic here by yourself + # ... + + @NotificationMessagesPreparer() + @recorded_by_proxy + def test_download_media(self, notificationmessages_endpoint): + client = self.create_client(endpoint=notificationmessages_endpoint) + response = client.download_media( + id="str", + ) + + # please add some check logic here by yourself + # ... diff --git a/sdk/communication/azure-communication-messages/generated_tests/test_notification_messages_async.py b/sdk/communication/azure-communication-messages/generated_tests/test_notification_messages_async.py new file mode 100644 index 000000000000..27188dc4eb4d --- /dev/null +++ b/sdk/communication/azure-communication-messages/generated_tests/test_notification_messages_async.py @@ -0,0 +1,36 @@ +# 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) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +import pytest +from devtools_testutils.aio import recorded_by_proxy_async +from testpreparer import NotificationMessagesPreparer +from testpreparer_async import NotificationMessagesClientTestBaseAsync + + +@pytest.mark.skip("you may need to update the auto-generated test case before run it") +class TestNotificationMessagesAsync(NotificationMessagesClientTestBaseAsync): + @NotificationMessagesPreparer() + @recorded_by_proxy_async + async def test_send(self, notificationmessages_endpoint): + client = self.create_async_client(endpoint=notificationmessages_endpoint) + response = await client.send( + body={"channelRegistrationId": "str", "kind": "audio", "mediaUri": "str", "to": ["str"]}, + ) + + # please add some check logic here by yourself + # ... + + @NotificationMessagesPreparer() + @recorded_by_proxy_async + async def test_download_media(self, notificationmessages_endpoint): + client = self.create_async_client(endpoint=notificationmessages_endpoint) + response = await client.download_media( + id="str", + ) + + # please add some check logic here by yourself + # ... diff --git a/sdk/communication/azure-communication-messages/generated_tests/testpreparer.py b/sdk/communication/azure-communication-messages/generated_tests/testpreparer.py new file mode 100644 index 000000000000..b7641dca06e4 --- /dev/null +++ b/sdk/communication/azure-communication-messages/generated_tests/testpreparer.py @@ -0,0 +1,44 @@ +# 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) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from azure.communication.messages import MessageTemplateClient, NotificationMessagesClient +from devtools_testutils import AzureRecordedTestCase, PowerShellPreparer +import functools + + +class NotificationMessagesClientTestBase(AzureRecordedTestCase): + + def create_client(self, endpoint): + credential = self.get_credential(NotificationMessagesClient) + return self.create_client_from_credential( + NotificationMessagesClient, + credential=credential, + endpoint=endpoint, + ) + + +NotificationMessagesPreparer = functools.partial( + PowerShellPreparer, + "notificationmessages", + notificationmessages_endpoint="https://fake_notificationmessages_endpoint.com", +) + + +class MessageTemplateClientTestBase(AzureRecordedTestCase): + + def create_client(self, endpoint): + credential = self.get_credential(MessageTemplateClient) + return self.create_client_from_credential( + MessageTemplateClient, + credential=credential, + endpoint=endpoint, + ) + + +MessageTemplatePreparer = functools.partial( + PowerShellPreparer, "messagetemplate", messagetemplate_endpoint="https://fake_messagetemplate_endpoint.com" +) diff --git a/sdk/communication/azure-communication-messages/generated_tests/testpreparer_async.py b/sdk/communication/azure-communication-messages/generated_tests/testpreparer_async.py new file mode 100644 index 000000000000..ffa7ee49f930 --- /dev/null +++ b/sdk/communication/azure-communication-messages/generated_tests/testpreparer_async.py @@ -0,0 +1,31 @@ +# 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) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from azure.communication.messages.aio import MessageTemplateClient, NotificationMessagesClient +from devtools_testutils import AzureRecordedTestCase + + +class NotificationMessagesClientTestBaseAsync(AzureRecordedTestCase): + + def create_async_client(self, endpoint): + credential = self.get_credential(NotificationMessagesClient, is_async=True) + return self.create_client_from_credential( + NotificationMessagesClient, + credential=credential, + endpoint=endpoint, + ) + + +class MessageTemplateClientTestBaseAsync(AzureRecordedTestCase): + + def create_async_client(self, endpoint): + credential = self.get_credential(MessageTemplateClient, is_async=True) + return self.create_client_from_credential( + MessageTemplateClient, + credential=credential, + endpoint=endpoint, + ) diff --git a/sdk/communication/azure-communication-messages/tsp-location.yaml b/sdk/communication/azure-communication-messages/tsp-location.yaml index 4292cd594e07..fb517c0c09a2 100644 --- a/sdk/communication/azure-communication-messages/tsp-location.yaml +++ b/sdk/communication/azure-communication-messages/tsp-location.yaml @@ -1,4 +1,4 @@ directory: specification/communication/Communication.Messages -commit: abe3209e7c6924a58ab560ebab2349bc8fde6aa7 +commit: 26af8eff710ea634da1b33fed369967d1c39b689 repo: Azure/azure-rest-api-specs -additionalDirectories: [] +additionalDirectories: