From 24c73420a6a335ec07b6d93ef6d484ec97b2f2e6 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 17 Jun 2022 11:14:18 -0700 Subject: [PATCH 01/30] Adding python SDK --- .../azure-communication-email/CHANGELOG.md | 9 + .../azure-communication-email/LICENSE | 21 + .../azure-communication-email/MANIFEST.in | 7 + .../azure-communication-email/README.md | 1 + .../azure/__init__.py | 1 + .../azure/communication/__init__.py | 1 + .../azure/communication/email/__init__.py | 30 ++ .../communication/email/_email_client.py | 72 +++ .../email/_generated/__init__.py | 23 + .../_azure_communication_email_service.py | 100 ++++ .../email/_generated/_configuration.py | 65 +++ .../communication/email/_generated/_patch.py | 23 + .../communication/email/_generated/_vendor.py | 27 ++ .../email/_generated/_version.py | 11 + .../email/_generated/aio/__init__.py | 20 + .../aio/_azure_communication_email_service.py | 90 ++++ .../email/_generated/aio/_configuration.py | 59 +++ .../email/_generated/aio/_patch.py | 23 + .../_generated/aio/operations/__init__.py | 18 + .../aio/operations/_email_operations.py | 284 +++++++++++ .../email/_generated/aio/operations/_patch.py | 69 +++ .../email/_generated/models/__init__.py | 51 ++ ...azure_communication_email_service_enums.py | 63 +++ .../email/_generated/models/_models.py | 411 ++++++++++++++++ .../email/_generated/models/_models_py3.py | 452 ++++++++++++++++++ .../email/_generated/models/_patch.py | 47 ++ .../email/_generated/operations/__init__.py | 18 + .../operations/_email_operations.py | 361 ++++++++++++++ .../email/_generated/operations/_patch.py | 69 +++ .../communication/email/_generated/py.typed | 1 + .../communication/email/_shared/__init__.py | 5 + .../communication/email}/_shared/policy.py | 0 .../communication/email/_shared/utils.py | 37 ++ .../azure/communication/email/_version.py | 11 + .../azure/communication/email/aio/__init__.py | 5 + .../email/aio/_email_client_async.py | 82 ++++ .../azure/communication/email/py.typed | 0 .../dev_requirement.txt | 7 + .../samples/check_message_status_sample.py | 72 +++ .../check_message_status_sample_async.py | 82 ++++ ...end_email_to_multiple_recipients_sample.py | 72 +++ ...ail_to_multiple_recipients_sample_async.py | 82 ++++ .../send_email_to_single_recipient_sample.py | 67 +++ ..._email_to_single_recipient_sample_async.py | 77 +++ .../send_email_with_attachments_sample.py | 75 +++ ...end_email_with_attachments_sample_async.py | 85 ++++ .../azure-communication-email/setup.py | 71 +++ .../swagger/SWAGGER.md | 42 ++ .../tests/_shared/testcase.py | 102 ++++ ...ail_client_e2e.test_send_email_single.yaml | 53 ++ .../tests/test_email_client.py | 69 +++ .../tests/test_email_client_e2e.py | 36 ++ .../tests/unittest_helpers.py | 20 + 53 files changed, 3579 insertions(+) create mode 100644 sdk/communication/azure-communication-email/CHANGELOG.md create mode 100644 sdk/communication/azure-communication-email/LICENSE create mode 100644 sdk/communication/azure-communication-email/MANIFEST.in create mode 100644 sdk/communication/azure-communication-email/README.md create mode 100644 sdk/communication/azure-communication-email/azure/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_email_client.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py rename sdk/communication/{azure-communication-sms/azure/communication/sms => azure-communication-email/azure/communication/email}/_shared/policy.py (100%) create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_version.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/py.typed create mode 100644 sdk/communication/azure-communication-email/dev_requirement.txt create mode 100644 sdk/communication/azure-communication-email/samples/check_message_status_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py create mode 100644 sdk/communication/azure-communication-email/setup.py create mode 100644 sdk/communication/azure-communication-email/swagger/SWAGGER.md create mode 100644 sdk/communication/azure-communication-email/tests/_shared/testcase.py create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml create mode 100644 sdk/communication/azure-communication-email/tests/test_email_client.py create mode 100644 sdk/communication/azure-communication-email/tests/test_email_client_e2e.py create mode 100644 sdk/communication/azure-communication-email/tests/unittest_helpers.py diff --git a/sdk/communication/azure-communication-email/CHANGELOG.md b/sdk/communication/azure-communication-email/CHANGELOG.md new file mode 100644 index 000000000000..1cf845b24478 --- /dev/null +++ b/sdk/communication/azure-communication-email/CHANGELOG.md @@ -0,0 +1,9 @@ +# Release History + +## 1.0.0b1 (TODO: UPDATE WITH RELEASE DATE) + +The first preview of the Azure Communication Email Client has the following features: + +- send emails to multiple recipients with attachments +- get the status of a sent message + diff --git a/sdk/communication/azure-communication-email/LICENSE b/sdk/communication/azure-communication-email/LICENSE new file mode 100644 index 000000000000..63447fd8bbbf --- /dev/null +++ b/sdk/communication/azure-communication-email/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) Microsoft Corporation. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/MANIFEST.in b/sdk/communication/azure-communication-email/MANIFEST.in new file mode 100644 index 000000000000..4f582a7c8d7b --- /dev/null +++ b/sdk/communication/azure-communication-email/MANIFEST.in @@ -0,0 +1,7 @@ +include *.md +include azure/__init__.py +include azure/communication/__init__.py +include LICENSE +recursive-include tests *.py +recursive-include samples *.py *.md +include azure/communication/email/py.typed \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md new file mode 100644 index 000000000000..38f1d4c0c53d --- /dev/null +++ b/sdk/communication/azure-communication-email/README.md @@ -0,0 +1 @@ +# TODO: Populate this README \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/__init__.py b/sdk/communication/azure-communication-email/azure/__init__.py new file mode 100644 index 000000000000..69e3be50dac4 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/sdk/communication/azure-communication-email/azure/communication/__init__.py b/sdk/communication/azure-communication-email/azure/communication/__init__.py new file mode 100644 index 000000000000..69e3be50dac4 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py new file mode 100644 index 000000000000..f7befc593db1 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py @@ -0,0 +1,30 @@ +from ._email_client import EmailClient + +from ._generated.models import ( + EmailMessage, + EmailCustomHeader, + EmailContent, + EmailImportance, + EmailRecipients, + EmailAddress, + EmailAttachment, + EmailAttachmentType, + SendEmailResult, + SendStatus, + SendStatusResult +) + +__all__ = [ + 'EmailClient', + 'EmailMessage', + 'EmailCustomHeader', + 'EmailContent', + 'EmailImportance', + 'EmailRecipients', + 'EmailAddress', + 'EmailAttachment', + 'EmailAttachmentType', + 'SendEmailResult', + 'SendStatus', + 'SendStatusResult', +] \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py new file mode 100644 index 000000000000..c87a337f562b --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -0,0 +1,72 @@ +from uuid import uuid4 +from azure.core.tracing.decorator import distributed_trace +from ._shared.utils import parse_connection_str, get_current_utc_time +from ._shared.policy import HMACCredentialsPolicy +from ._generated._azure_communication_email_service import AzureCommunicationEmailService +from ._version import SDK_MONIKER +from ._generated.models import SendEmailResult, SendStatusResult, EmailMessage + +class EmailClient(object): + """A client to interact with the AzureCommunicationService Email gateway. + + This client provides operations to send an email and monitor its status. + + :param str conn_string: + The connection string to connect to an Azure Communication Service resource. + Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + """ + def __init__( + self, + conn_str, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + endpoint, access_key = parse_connection_str(conn_str) + authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + + self._generated_client = AzureCommunicationEmailService( + endpoint, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs + ) + + @distributed_trace + def send( + self, + email_message, # type: EmailMessage + **kwargs # type: Any + ): # type: (...) -> SendEmailResult + """Queues an email message to be sent to one or more recipients. + + :param email_message: The message payload for sending an email. + :type email_message: ~azure.communication.email.models.EmailMessage + :return: SendEmailResult + :rtype: ~azure.communication.email.models.SendEmailResult + """ + + return self._generated_client.email.send( + repeatability_request_id=uuid4(), + repeatability_first_sent=get_current_utc_time(), + email_message=email_message, + **kwargs + ) + + @distributed_trace + def get_send_status( + self, + message_id, #type: str + **kwargs # type: Any + ): # type: (...) -> SendStatusResult + """Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send email + :type message_id: str + :return: SendStatusResult + :rtype: ~azure.communication.email.models.SendStatusResult + """ + + return self._generated_client.email.get_send_status( + message_id=message_id, + **kwargs + ) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py new file mode 100644 index 000000000000..a5e340739e13 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py @@ -0,0 +1,23 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._azure_communication_email_service import AzureCommunicationEmailService +from ._version import VERSION + +__version__ = VERSION + +try: + from ._patch import __all__ as _patch_all + from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +except ImportError: + _patch_all = [] +from ._patch import patch_sdk as _patch_sdk +__all__ = ['AzureCommunicationEmailService'] +__all__.extend([p for p in _patch_all if p not in __all__]) + +_patch_sdk() diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py new file mode 100644 index 000000000000..3cfacefc146f --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py @@ -0,0 +1,100 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from copy import deepcopy +from typing import TYPE_CHECKING + +from msrest import Deserializer, Serializer + +from azure.core import PipelineClient + +from . import models +from ._configuration import AzureCommunicationEmailServiceConfiguration +from .operations import EmailOperations + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + from azure.core.rest import HttpRequest, HttpResponse + +class AzureCommunicationEmailService(object): # pylint: disable=client-accepts-api-version-keyword + """Azure Communication Email Service. + + :ivar email: EmailOperations operations + :vartype email: azure.communication.email.operations.EmailOperations + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + _endpoint = '{endpoint}' + self._config = AzureCommunicationEmailServiceConfiguration(endpoint=endpoint, **kwargs) + self._client = PipelineClient(base_url=_endpoint, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + self._serialize.client_side_validation = False + self.email = EmailOperations( + self._client, self._config, self._serialize, self._deserialize + ) + + + def _send_request( + self, + request, # type: HttpRequest + **kwargs # type: Any + ): + # type: (...) -> HttpResponse + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = client._send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/python/protocol/quickstart + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.HttpResponse + """ + + request_copy = deepcopy(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + + request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) + return self._client.send_request(request_copy, **kwargs) + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> AzureCommunicationEmailService + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py new file mode 100644 index 000000000000..a3fb353d8a75 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py @@ -0,0 +1,65 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +from ._version import VERSION + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + +class AzureCommunicationEmailServiceConfiguration(Configuration): # pylint: disable=too-many-instance-attributes + """Configuration for AzureCommunicationEmailService. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + super(AzureCommunicationEmailServiceConfiguration, self).__init__(**kwargs) + api_version = kwargs.pop('api_version', "2021-10-01-preview") # type: str + + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + + self.endpoint = endpoint + self.api_version = api_version + kwargs.setdefault('sdk_moniker', 'azurecommunicationemailservice/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs # type: Any + ): + # type: (...) -> None + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py new file mode 100644 index 000000000000..8a35ddb87c7e --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py @@ -0,0 +1,23 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import List + +__all__ = [] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py new file mode 100644 index 000000000000..138f663c53a4 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py @@ -0,0 +1,27 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from azure.core.pipeline.transport import HttpRequest + +def _convert_request(request, files=None): + data = request.content if not files else None + request = HttpRequest(method=request.method, url=request.url, headers=request.headers, data=data) + if files: + request.set_formdata_body(files) + return request + +def _format_url_section(template, **kwargs): + components = template.split("/") + while components: + try: + return template.format(**kwargs) + except KeyError as key: + formatted_components = template.split("/") + components = [ + c for c in formatted_components if "{}".format(key.args[0]) not in c + ] + template = "/".join(components) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py new file mode 100644 index 000000000000..41f0bacc9706 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py @@ -0,0 +1,11 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +VERSION = "1.0.0b1" + +SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py new file mode 100644 index 000000000000..3926c45d3176 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py @@ -0,0 +1,20 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._azure_communication_email_service import AzureCommunicationEmailService + +try: + from ._patch import __all__ as _patch_all + from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +except ImportError: + _patch_all = [] +from ._patch import patch_sdk as _patch_sdk +__all__ = ['AzureCommunicationEmailService'] +__all__.extend([p for p in _patch_all if p not in __all__]) + +_patch_sdk() diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py new file mode 100644 index 000000000000..f505db9f0d17 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py @@ -0,0 +1,90 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from copy import deepcopy +from typing import Any, Awaitable + +from msrest import Deserializer, Serializer + +from azure.core import AsyncPipelineClient +from azure.core.rest import AsyncHttpResponse, HttpRequest + +from .. import models +from ._configuration import AzureCommunicationEmailServiceConfiguration +from .operations import EmailOperations + +class AzureCommunicationEmailService: # pylint: disable=client-accepts-api-version-keyword + """Azure Communication Email Service. + + :ivar email: EmailOperations operations + :vartype email: azure.communication.email.aio.operations.EmailOperations + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + _endpoint = '{endpoint}' + self._config = AzureCommunicationEmailServiceConfiguration(endpoint=endpoint, **kwargs) + self._client = AsyncPipelineClient(base_url=_endpoint, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + self._serialize.client_side_validation = False + self.email = EmailOperations( + self._client, self._config, self._serialize, self._deserialize + ) + + + def _send_request( + self, + request: HttpRequest, + **kwargs: Any + ) -> Awaitable[AsyncHttpResponse]: + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = await client._send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/python/protocol/quickstart + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.AsyncHttpResponse + """ + + request_copy = deepcopy(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + + request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) + return self._client.send_request(request_copy, **kwargs) + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "AzureCommunicationEmailService": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py new file mode 100644 index 000000000000..2c74b995d496 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py @@ -0,0 +1,59 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +from .._version import VERSION + + +class AzureCommunicationEmailServiceConfiguration(Configuration): # pylint: disable=too-many-instance-attributes + """Configuration for AzureCommunicationEmailService. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + super(AzureCommunicationEmailServiceConfiguration, self).__init__(**kwargs) + api_version = kwargs.pop('api_version', "2021-10-01-preview") # type: str + + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + + self.endpoint = endpoint + self.api_version = api_version + kwargs.setdefault('sdk_moniker', 'azurecommunicationemailservice/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs: Any + ) -> None: + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py new file mode 100644 index 000000000000..8a35ddb87c7e --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py @@ -0,0 +1,23 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import List + +__all__ = [] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py new file mode 100644 index 000000000000..98c27c3620bc --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._email_operations import EmailOperations + +from ._patch import __all__ as _patch_all +from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +from ._patch import patch_sdk as _patch_sdk +__all__ = [ + 'EmailOperations', +] +__all__.extend([p for p in _patch_all if p not in __all__]) +_patch_sdk() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py new file mode 100644 index 000000000000..8b538c791667 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py @@ -0,0 +1,284 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, Callable, Dict, IO, Optional, TypeVar, Union, overload + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.core.utils import case_insensitive_dict + +from ... import models as _models +from ..._vendor import _convert_request +from ...operations._email_operations import build_get_send_status_request, build_send_request +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class EmailOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.communication.email.aio.AzureCommunicationEmailService`'s + :attr:`email` attribute. + """ + + models = _models + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + + @distributed_trace_async + async def get_send_status( + self, + message_id: str, + **kwargs: Any + ) -> _models.SendStatusResult: + """Gets the status of a message sent previously. + + Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send + email. Required. + :type message_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SendStatusResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendStatusResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + cls = kwargs.pop('cls', None) # type: ClsType[_models.SendStatusResult] + + + request = build_get_send_status_request( + message_id=message_id, + api_version=api_version, + template_url=self.get_send_status.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + + deserialized = self._deserialize('SendStatusResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + + get_send_status.metadata = {'url': "/emails/{messageId}/status"} # type: ignore + + + @overload + async def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: _models.EmailMessage, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> None: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: ~azure.communication.email.models.EmailMessage + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: IO, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> None: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + + @distributed_trace_async + async def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: Union[_models.EmailMessage, IO], + **kwargs: Any + ) -> None: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Is either a model type or a IO + type. Required. + :type email_message: ~azure.communication.email.models.EmailMessage or IO + :keyword content_type: Body Parameter content-type. Known values are: 'application/json'. + Default value is None. + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + content_type = kwargs.pop('content_type', _headers.pop('Content-Type', None)) # type: Optional[str] + cls = kwargs.pop('cls', None) # type: ClsType[None] + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(email_message, (IO, bytes)): + _content = email_message + else: + _json = self._serialize.body(email_message, 'EmailMessage') + + request = build_send_request( + repeatability_request_id=repeatability_request_id, + repeatability_first_sent=repeatability_first_sent, + api_version=api_version, + content_type=content_type, + json=_json, + content=_content, + template_url=self.send.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Repeatability-Result']=self._deserialize('str', response.headers.get('Repeatability-Result')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + response_headers['x-ms-request-id']=self._deserialize('str', response.headers.get('x-ms-request-id')) + + + if cls: + return cls(pipeline_response, None, response_headers) + + send.metadata = {'url': "/emails:send"} # type: ignore + diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py new file mode 100644 index 000000000000..6eecde32b570 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py @@ -0,0 +1,69 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import Any, IO, Union +from ._email_operations import EmailOperations as EmailOperationsGenerated +from ... import models as _models + +class EmailOperations(EmailOperationsGenerated): + + def __return_message_id(self, pipeline_response, _, response_headers): + return response_headers['x-ms-request-id'] + + async def send( + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: Union[_models.EmailMessage, IO], + **kwargs: Any + ) -> _models.SendEmailResult: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: SendEmailResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendEmailResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + message_id = await super().send( + repeatability_request_id, + repeatability_first_sent, + email_message, + **dict(kwargs, cls=self.__return_message_id) + ) + + return _models.SendEmailResult(message_id=message_id) + + send.metadata = {'url': "/emails:send"} # type: ignore + +__all__ = ["EmailOperations"] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py new file mode 100644 index 000000000000..0f7f73e35d03 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py @@ -0,0 +1,51 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import CommunicationError + from ._models_py3 import CommunicationErrorResponse + from ._models_py3 import EmailAddress + from ._models_py3 import EmailAttachment + from ._models_py3 import EmailContent + from ._models_py3 import EmailCustomHeader + from ._models_py3 import EmailMessage + from ._models_py3 import EmailRecipients + from ._models_py3 import SendStatusResult +except (SyntaxError, ImportError): + from ._models import CommunicationError # type: ignore + from ._models import CommunicationErrorResponse # type: ignore + from ._models import EmailAddress # type: ignore + from ._models import EmailAttachment # type: ignore + from ._models import EmailContent # type: ignore + from ._models import EmailCustomHeader # type: ignore + from ._models import EmailMessage # type: ignore + from ._models import EmailRecipients # type: ignore + from ._models import SendStatusResult # type: ignore + +from ._azure_communication_email_service_enums import EmailAttachmentType +from ._azure_communication_email_service_enums import EmailImportance +from ._azure_communication_email_service_enums import SendStatus +from ._patch import __all__ as _patch_all +from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +from ._patch import patch_sdk as _patch_sdk +__all__ = [ + 'CommunicationError', + 'CommunicationErrorResponse', + 'EmailAddress', + 'EmailAttachment', + 'EmailContent', + 'EmailCustomHeader', + 'EmailMessage', + 'EmailRecipients', + 'SendStatusResult', + 'EmailAttachmentType', + 'EmailImportance', + 'SendStatus', +] +__all__.extend([p for p in _patch_all if p not in __all__]) +_patch_sdk() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py new file mode 100644 index 000000000000..8743cbf94a15 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py @@ -0,0 +1,63 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum +from azure.core import CaseInsensitiveEnumMeta + + +class EmailAttachmentType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The type of attachment file. + """ + + AVI = "avi" + BMP = "bmp" + DOC = "doc" + DOCM = "docm" + DOCX = "docx" + GIF = "gif" + JPEG = "jpeg" + MP3 = "mp3" + ONE = "one" + PDF = "pdf" + PNG = "png" + PPSM = "ppsm" + PPSX = "ppsx" + PPT = "ppt" + PPTM = "pptm" + PPTX = "pptx" + PUB = "pub" + RPMSG = "rpmsg" + RTF = "rtf" + TIF = "tif" + TXT = "txt" + VSD = "vsd" + WAV = "wav" + WMA = "wma" + XLS = "xls" + XLSB = "xlsb" + XLSM = "xlsm" + XLSX = "xlsx" + +class EmailImportance(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The importance type for the email. + """ + + HIGH = "high" + NORMAL = "normal" + LOW = "low" + +class SendStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The type indicating the status of a request. + """ + + #: The message has passed basic validations and has been queued to be processed further. + QUEUED = "queued" + #: The message has been processed and is now out for delivery. + OUT_FOR_DELIVERY = "outForDelivery" + #: The message could not be processed and was dropped. + DROPPED = "dropped" diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py new file mode 100644 index 000000000000..6b0e0134ca31 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py @@ -0,0 +1,411 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +import msrest.serialization + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar code: The error code. Required. + :vartype code: str + :ivar message: The error message. Required. + :vartype message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.email.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + "code": {"key": "code", "type": "str"}, + "message": {"key": "message", "type": "str"}, + "target": {"key": "target", "type": "str"}, + "details": {"key": "details", "type": "[CommunicationError]"}, + "inner_error": {"key": "innererror", "type": "CommunicationError"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword code: The error code. Required. + :paramtype code: str + :keyword message: The error message. Required. + :paramtype message: str + """ + super(CommunicationError, self).__init__(**kwargs) + self.code = kwargs['code'] + self.message = kwargs['message'] + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :ivar error: The Communication Services error. Required. + :vartype error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + "error": {"key": "error", "type": "CommunicationError"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword error: The Communication Services error. Required. + :paramtype error: ~azure.communication.email.models.CommunicationError + """ + super(CommunicationErrorResponse, self).__init__(**kwargs) + self.error = kwargs['error'] + + +class EmailAddress(msrest.serialization.Model): + """An object representing the email address and its display name. + + All required parameters must be populated in order to send to Azure. + + :ivar email: Email address. Required. + :vartype email: str + :ivar display_name: Email display name. + :vartype display_name: str + """ + + _validation = { + 'email': {'required': True}, + } + + _attribute_map = { + "email": {"key": "email", "type": "str"}, + "display_name": {"key": "displayName", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword email: Email address. Required. + :paramtype email: str + :keyword display_name: Email display name. + :paramtype display_name: str + """ + super(EmailAddress, self).__init__(**kwargs) + self.email = kwargs['email'] + self.display_name = kwargs.get('display_name', None) + + +class EmailAttachment(msrest.serialization.Model): + """Attachment to the email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Name of the attachment. Required. + :vartype name: str + :ivar attachment_type: The type of attachment file. Required. Known values are: "avi", "bmp", + "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :vartype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :ivar content_bytes_base64: Base64 encoded contents of the attachment. Required. + :vartype content_bytes_base64: str + """ + + _validation = { + 'name': {'required': True}, + 'attachment_type': {'required': True}, + 'content_bytes_base64': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "attachment_type": {"key": "attachmentType", "type": "str"}, + "content_bytes_base64": {"key": "contentBytesBase64", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword name: Name of the attachment. Required. + :paramtype name: str + :keyword attachment_type: The type of attachment file. Required. Known values are: "avi", + "bmp", "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :paramtype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :keyword content_bytes_base64: Base64 encoded contents of the attachment. Required. + :paramtype content_bytes_base64: str + """ + super(EmailAttachment, self).__init__(**kwargs) + self.name = kwargs['name'] + self.attachment_type = kwargs['attachment_type'] + self.content_bytes_base64 = kwargs['content_bytes_base64'] + + +class EmailContent(msrest.serialization.Model): + """Content of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar subject: Subject of the email message. Required. + :vartype subject: str + :ivar plain_text: Plain text version of the email message. + :vartype plain_text: str + :ivar html: Html version of the email message. + :vartype html: str + """ + + _validation = { + 'subject': {'required': True}, + } + + _attribute_map = { + "subject": {"key": "subject", "type": "str"}, + "plain_text": {"key": "plainText", "type": "str"}, + "html": {"key": "html", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword subject: Subject of the email message. Required. + :paramtype subject: str + :keyword plain_text: Plain text version of the email message. + :paramtype plain_text: str + :keyword html: Html version of the email message. + :paramtype html: str + """ + super(EmailContent, self).__init__(**kwargs) + self.subject = kwargs['subject'] + self.plain_text = kwargs.get('plain_text', None) + self.html = kwargs.get('html', None) + + +class EmailCustomHeader(msrest.serialization.Model): + """Custom header for email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Header name. Required. + :vartype name: str + :ivar value: Header value. Required. + :vartype value: str + """ + + _validation = { + 'name': {'required': True}, + 'value': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "value": {"key": "value", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword name: Header name. Required. + :paramtype name: str + :keyword value: Header value. Required. + :paramtype value: str + """ + super(EmailCustomHeader, self).__init__(**kwargs) + self.name = kwargs['name'] + self.value = kwargs['value'] + + +class EmailMessage(msrest.serialization.Model): + """Message payload for sending an email. + + All required parameters must be populated in order to send to Azure. + + :ivar custom_headers: Custom email headers to be passed. + :vartype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :ivar sender: Sender email address from a verified domain. Required. + :vartype sender: str + :ivar content: Email content to be sent. Required. + :vartype content: ~azure.communication.email.models.EmailContent + :ivar importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :vartype importance: str or ~azure.communication.email.models.EmailImportance + :ivar recipients: Recipients for the email. Required. + :vartype recipients: ~azure.communication.email.models.EmailRecipients + :ivar attachments: list of attachments. + :vartype attachments: list[~azure.communication.email.models.EmailAttachment] + :ivar reply_to: Email addresses where recipients' replies will be sent to. + :vartype reply_to: list[~azure.communication.email.models.EmailAddress] + :ivar disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :vartype disable_user_engagement_tracking: bool + """ + + _validation = { + 'sender': {'required': True}, + 'content': {'required': True}, + 'recipients': {'required': True}, + } + + _attribute_map = { + "custom_headers": {"key": "headers", "type": "[EmailCustomHeader]"}, + "sender": {"key": "sender", "type": "str"}, + "content": {"key": "content", "type": "EmailContent"}, + "importance": {"key": "importance", "type": "str"}, + "recipients": {"key": "recipients", "type": "EmailRecipients"}, + "attachments": {"key": "attachments", "type": "[EmailAttachment]"}, + "reply_to": {"key": "replyTo", "type": "[EmailAddress]"}, + "disable_user_engagement_tracking": {"key": "disableUserEngagementTracking", "type": "bool"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword custom_headers: Custom email headers to be passed. + :paramtype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :keyword sender: Sender email address from a verified domain. Required. + :paramtype sender: str + :keyword content: Email content to be sent. Required. + :paramtype content: ~azure.communication.email.models.EmailContent + :keyword importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :paramtype importance: str or ~azure.communication.email.models.EmailImportance + :keyword recipients: Recipients for the email. Required. + :paramtype recipients: ~azure.communication.email.models.EmailRecipients + :keyword attachments: list of attachments. + :paramtype attachments: list[~azure.communication.email.models.EmailAttachment] + :keyword reply_to: Email addresses where recipients' replies will be sent to. + :paramtype reply_to: list[~azure.communication.email.models.EmailAddress] + :keyword disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :paramtype disable_user_engagement_tracking: bool + """ + super(EmailMessage, self).__init__(**kwargs) + self.custom_headers = kwargs.get('custom_headers', None) + self.sender = kwargs['sender'] + self.content = kwargs['content'] + self.importance = kwargs.get('importance', "normal") + self.recipients = kwargs['recipients'] + self.attachments = kwargs.get('attachments', None) + self.reply_to = kwargs.get('reply_to', None) + self.disable_user_engagement_tracking = kwargs.get('disable_user_engagement_tracking', None) + + +class EmailRecipients(msrest.serialization.Model): + """Recipients of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar to: Email To recipients. Required. + :vartype to: list[~azure.communication.email.models.EmailAddress] + :ivar cc: Email CC recipients. + :vartype cc: list[~azure.communication.email.models.EmailAddress] + :ivar bcc: Email BCC recipients. + :vartype bcc: list[~azure.communication.email.models.EmailAddress] + """ + + _validation = { + 'to': {'required': True}, + } + + _attribute_map = { + "to": {"key": "to", "type": "[EmailAddress]"}, + "cc": {"key": "CC", "type": "[EmailAddress]"}, + "bcc": {"key": "bCC", "type": "[EmailAddress]"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword to: Email To recipients. Required. + :paramtype to: list[~azure.communication.email.models.EmailAddress] + :keyword cc: Email CC recipients. + :paramtype cc: list[~azure.communication.email.models.EmailAddress] + :keyword bcc: Email BCC recipients. + :paramtype bcc: list[~azure.communication.email.models.EmailAddress] + """ + super(EmailRecipients, self).__init__(**kwargs) + self.to = kwargs['to'] + self.cc = kwargs.get('cc', None) + self.bcc = kwargs.get('bcc', None) + + +class SendStatusResult(msrest.serialization.Model): + """Status of an email message that was sent previously. + + All required parameters must be populated in order to send to Azure. + + :ivar message_id: System generated id of an email message sent. Required. + :vartype message_id: str + :ivar status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :vartype status: str or ~azure.communication.email.models.SendStatus + """ + + _validation = { + 'message_id': {'required': True}, + 'status': {'required': True}, + } + + _attribute_map = { + "message_id": {"key": "messageId", "type": "str"}, + "status": {"key": "status", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword message_id: System generated id of an email message sent. Required. + :paramtype message_id: str + :keyword status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :paramtype status: str or ~azure.communication.email.models.SendStatus + """ + super(SendStatusResult, self).__init__(**kwargs) + self.message_id = kwargs['message_id'] + self.status = kwargs['status'] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py new file mode 100644 index 000000000000..0e85199772e6 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py @@ -0,0 +1,452 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import List, Optional, TYPE_CHECKING, Union + +import msrest.serialization + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from .. import models as _models + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar code: The error code. Required. + :vartype code: str + :ivar message: The error message. Required. + :vartype message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.email.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + "code": {"key": "code", "type": "str"}, + "message": {"key": "message", "type": "str"}, + "target": {"key": "target", "type": "str"}, + "details": {"key": "details", "type": "[CommunicationError]"}, + "inner_error": {"key": "innererror", "type": "CommunicationError"}, + } + + def __init__( + self, + *, + code: str, + message: str, + **kwargs + ): + """ + :keyword code: The error code. Required. + :paramtype code: str + :keyword message: The error message. Required. + :paramtype message: str + """ + super().__init__(**kwargs) + self.code = code + self.message = message + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :ivar error: The Communication Services error. Required. + :vartype error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + "error": {"key": "error", "type": "CommunicationError"}, + } + + def __init__( + self, + *, + error: "_models.CommunicationError", + **kwargs + ): + """ + :keyword error: The Communication Services error. Required. + :paramtype error: ~azure.communication.email.models.CommunicationError + """ + super().__init__(**kwargs) + self.error = error + + +class EmailAddress(msrest.serialization.Model): + """An object representing the email address and its display name. + + All required parameters must be populated in order to send to Azure. + + :ivar email: Email address. Required. + :vartype email: str + :ivar display_name: Email display name. + :vartype display_name: str + """ + + _validation = { + 'email': {'required': True}, + } + + _attribute_map = { + "email": {"key": "email", "type": "str"}, + "display_name": {"key": "displayName", "type": "str"}, + } + + def __init__( + self, + *, + email: str, + display_name: Optional[str] = None, + **kwargs + ): + """ + :keyword email: Email address. Required. + :paramtype email: str + :keyword display_name: Email display name. + :paramtype display_name: str + """ + super().__init__(**kwargs) + self.email = email + self.display_name = display_name + + +class EmailAttachment(msrest.serialization.Model): + """Attachment to the email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Name of the attachment. Required. + :vartype name: str + :ivar attachment_type: The type of attachment file. Required. Known values are: "avi", "bmp", + "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :vartype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :ivar content_bytes_base64: Base64 encoded contents of the attachment. Required. + :vartype content_bytes_base64: str + """ + + _validation = { + 'name': {'required': True}, + 'attachment_type': {'required': True}, + 'content_bytes_base64': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "attachment_type": {"key": "attachmentType", "type": "str"}, + "content_bytes_base64": {"key": "contentBytesBase64", "type": "str"}, + } + + def __init__( + self, + *, + name: str, + attachment_type: Union[str, "_models.EmailAttachmentType"], + content_bytes_base64: str, + **kwargs + ): + """ + :keyword name: Name of the attachment. Required. + :paramtype name: str + :keyword attachment_type: The type of attachment file. Required. Known values are: "avi", + "bmp", "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :paramtype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :keyword content_bytes_base64: Base64 encoded contents of the attachment. Required. + :paramtype content_bytes_base64: str + """ + super().__init__(**kwargs) + self.name = name + self.attachment_type = attachment_type + self.content_bytes_base64 = content_bytes_base64 + + +class EmailContent(msrest.serialization.Model): + """Content of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar subject: Subject of the email message. Required. + :vartype subject: str + :ivar plain_text: Plain text version of the email message. + :vartype plain_text: str + :ivar html: Html version of the email message. + :vartype html: str + """ + + _validation = { + 'subject': {'required': True}, + } + + _attribute_map = { + "subject": {"key": "subject", "type": "str"}, + "plain_text": {"key": "plainText", "type": "str"}, + "html": {"key": "html", "type": "str"}, + } + + def __init__( + self, + *, + subject: str, + plain_text: Optional[str] = None, + html: Optional[str] = None, + **kwargs + ): + """ + :keyword subject: Subject of the email message. Required. + :paramtype subject: str + :keyword plain_text: Plain text version of the email message. + :paramtype plain_text: str + :keyword html: Html version of the email message. + :paramtype html: str + """ + super().__init__(**kwargs) + self.subject = subject + self.plain_text = plain_text + self.html = html + + +class EmailCustomHeader(msrest.serialization.Model): + """Custom header for email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Header name. Required. + :vartype name: str + :ivar value: Header value. Required. + :vartype value: str + """ + + _validation = { + 'name': {'required': True}, + 'value': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "value": {"key": "value", "type": "str"}, + } + + def __init__( + self, + *, + name: str, + value: str, + **kwargs + ): + """ + :keyword name: Header name. Required. + :paramtype name: str + :keyword value: Header value. Required. + :paramtype value: str + """ + super().__init__(**kwargs) + self.name = name + self.value = value + + +class EmailMessage(msrest.serialization.Model): + """Message payload for sending an email. + + All required parameters must be populated in order to send to Azure. + + :ivar custom_headers: Custom email headers to be passed. + :vartype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :ivar sender: Sender email address from a verified domain. Required. + :vartype sender: str + :ivar content: Email content to be sent. Required. + :vartype content: ~azure.communication.email.models.EmailContent + :ivar importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :vartype importance: str or ~azure.communication.email.models.EmailImportance + :ivar recipients: Recipients for the email. Required. + :vartype recipients: ~azure.communication.email.models.EmailRecipients + :ivar attachments: list of attachments. + :vartype attachments: list[~azure.communication.email.models.EmailAttachment] + :ivar reply_to: Email addresses where recipients' replies will be sent to. + :vartype reply_to: list[~azure.communication.email.models.EmailAddress] + :ivar disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :vartype disable_user_engagement_tracking: bool + """ + + _validation = { + 'sender': {'required': True}, + 'content': {'required': True}, + 'recipients': {'required': True}, + } + + _attribute_map = { + "custom_headers": {"key": "headers", "type": "[EmailCustomHeader]"}, + "sender": {"key": "sender", "type": "str"}, + "content": {"key": "content", "type": "EmailContent"}, + "importance": {"key": "importance", "type": "str"}, + "recipients": {"key": "recipients", "type": "EmailRecipients"}, + "attachments": {"key": "attachments", "type": "[EmailAttachment]"}, + "reply_to": {"key": "replyTo", "type": "[EmailAddress]"}, + "disable_user_engagement_tracking": {"key": "disableUserEngagementTracking", "type": "bool"}, + } + + def __init__( + self, + *, + sender: str, + content: "_models.EmailContent", + recipients: "_models.EmailRecipients", + custom_headers: Optional[List["_models.EmailCustomHeader"]] = None, + importance: Union[str, "_models.EmailImportance"] = "normal", + attachments: Optional[List["_models.EmailAttachment"]] = None, + reply_to: Optional[List["_models.EmailAddress"]] = None, + disable_user_engagement_tracking: Optional[bool] = None, + **kwargs + ): + """ + :keyword custom_headers: Custom email headers to be passed. + :paramtype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :keyword sender: Sender email address from a verified domain. Required. + :paramtype sender: str + :keyword content: Email content to be sent. Required. + :paramtype content: ~azure.communication.email.models.EmailContent + :keyword importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :paramtype importance: str or ~azure.communication.email.models.EmailImportance + :keyword recipients: Recipients for the email. Required. + :paramtype recipients: ~azure.communication.email.models.EmailRecipients + :keyword attachments: list of attachments. + :paramtype attachments: list[~azure.communication.email.models.EmailAttachment] + :keyword reply_to: Email addresses where recipients' replies will be sent to. + :paramtype reply_to: list[~azure.communication.email.models.EmailAddress] + :keyword disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :paramtype disable_user_engagement_tracking: bool + """ + super().__init__(**kwargs) + self.custom_headers = custom_headers + self.sender = sender + self.content = content + self.importance = importance + self.recipients = recipients + self.attachments = attachments + self.reply_to = reply_to + self.disable_user_engagement_tracking = disable_user_engagement_tracking + + +class EmailRecipients(msrest.serialization.Model): + """Recipients of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar to: Email To recipients. Required. + :vartype to: list[~azure.communication.email.models.EmailAddress] + :ivar cc: Email CC recipients. + :vartype cc: list[~azure.communication.email.models.EmailAddress] + :ivar bcc: Email BCC recipients. + :vartype bcc: list[~azure.communication.email.models.EmailAddress] + """ + + _validation = { + 'to': {'required': True}, + } + + _attribute_map = { + "to": {"key": "to", "type": "[EmailAddress]"}, + "cc": {"key": "CC", "type": "[EmailAddress]"}, + "bcc": {"key": "bCC", "type": "[EmailAddress]"}, + } + + def __init__( + self, + *, + to: List["_models.EmailAddress"], + cc: Optional[List["_models.EmailAddress"]] = None, + bcc: Optional[List["_models.EmailAddress"]] = None, + **kwargs + ): + """ + :keyword to: Email To recipients. Required. + :paramtype to: list[~azure.communication.email.models.EmailAddress] + :keyword cc: Email CC recipients. + :paramtype cc: list[~azure.communication.email.models.EmailAddress] + :keyword bcc: Email BCC recipients. + :paramtype bcc: list[~azure.communication.email.models.EmailAddress] + """ + super().__init__(**kwargs) + self.to = to + self.cc = cc + self.bcc = bcc + + +class SendStatusResult(msrest.serialization.Model): + """Status of an email message that was sent previously. + + All required parameters must be populated in order to send to Azure. + + :ivar message_id: System generated id of an email message sent. Required. + :vartype message_id: str + :ivar status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :vartype status: str or ~azure.communication.email.models.SendStatus + """ + + _validation = { + 'message_id': {'required': True}, + 'status': {'required': True}, + } + + _attribute_map = { + "message_id": {"key": "messageId", "type": "str"}, + "status": {"key": "status", "type": "str"}, + } + + def __init__( + self, + *, + message_id: str, + status: Union[str, "_models.SendStatus"], + **kwargs + ): + """ + :keyword message_id: System generated id of an email message sent. Required. + :paramtype message_id: str + :keyword status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :paramtype status: str or ~azure.communication.email.models.SendStatus + """ + super().__init__(**kwargs) + self.message_id = message_id + self.status = status diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py new file mode 100644 index 000000000000..c19a69940543 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py @@ -0,0 +1,47 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +import msrest.serialization + +class SendEmailResult(msrest.serialization.Model): + """Results of a sent email. + + All required parameters must be populated in order to send to Azure. + + :ivar message_id: System generated id of an email message sent. Required. + :vartype message_id: str + """ + + _validation = { + 'message_id': {'required': True}, + } + + _attribute_map = { + "message_id": {"key": "messageId", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword message_id: System generated id of an email message sent. Required. + :paramtype message_id: str + """ + super(SendEmailResult, self).__init__(**kwargs) + self.message_id = kwargs['message_id'] + +__all__ = ["SendEmailResult"] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py new file mode 100644 index 000000000000..98c27c3620bc --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._email_operations import EmailOperations + +from ._patch import __all__ as _patch_all +from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +from ._patch import patch_sdk as _patch_sdk +__all__ = [ + 'EmailOperations', +] +__all__.extend([p for p in _patch_all if p not in __all__]) +_patch_sdk() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py new file mode 100644 index 000000000000..90294c12462f --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py @@ -0,0 +1,361 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import IO, Optional, TYPE_CHECKING, Union, overload + +from msrest import Serializer + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpResponse +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator import distributed_trace +from azure.core.utils import case_insensitive_dict + +from .. import models as _models +from .._vendor import _convert_request, _format_url_section + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Optional, TypeVar + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +_SERIALIZER = Serializer() +_SERIALIZER.client_side_validation = False +# fmt: off + +def build_get_send_status_request( + message_id, # type: str + **kwargs # type: Any +): + # type: (...) -> HttpRequest + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', "2021-10-01-preview")) # type: str + accept = _headers.pop('Accept', "application/json") + + # Construct URL + _url = kwargs.pop("template_url", "/emails/{messageId}/status") + path_format_arguments = { + "messageId": _SERIALIZER.url("message_id", message_id, 'str'), + } + + _url = _format_url_section(_url, **path_format_arguments) + + # Construct parameters + _params['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + _headers['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs + ) + + +def build_send_request( + **kwargs # type: Any +): + # type: (...) -> HttpRequest + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', "2021-10-01-preview")) # type: str + content_type = kwargs.pop('content_type', _headers.pop('Content-Type', None)) # type: Optional[str] + repeatability_request_id = kwargs.pop('repeatability_request_id') # type: str + repeatability_first_sent = kwargs.pop('repeatability_first_sent') # type: str + accept = _headers.pop('Accept', "application/json") + + # Construct URL + _url = kwargs.pop("template_url", "/emails:send") + + # Construct parameters + _params['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + _headers['repeatability-request-id'] = _SERIALIZER.header("repeatability_request_id", repeatability_request_id, 'str') + _headers['repeatability-first-sent'] = _SERIALIZER.header("repeatability_first_sent", repeatability_first_sent, 'str') + if content_type is not None: + _headers['Content-Type'] = _SERIALIZER.header("content_type", content_type, 'str') + _headers['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="POST", + url=_url, + params=_params, + headers=_headers, + **kwargs + ) + +# fmt: on +class EmailOperations(object): + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.communication.email.AzureCommunicationEmailService`'s + :attr:`email` attribute. + """ + + models = _models + + def __init__(self, *args, **kwargs): + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + + @distributed_trace + def get_send_status( + self, + message_id, # type: str + **kwargs # type: Any + ): + # type: (...) -> _models.SendStatusResult + """Gets the status of a message sent previously. + + Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send + email. Required. + :type message_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SendStatusResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendStatusResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + cls = kwargs.pop('cls', None) # type: ClsType[_models.SendStatusResult] + + + request = build_get_send_status_request( + message_id=message_id, + api_version=api_version, + template_url=self.get_send_status.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + + deserialized = self._deserialize('SendStatusResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + + get_send_status.metadata = {'url': "/emails/{messageId}/status"} # type: ignore + + + @overload + def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: _models.EmailMessage + **kwargs # type: Any + ): + # type: (...) -> None + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: ~azure.communication.email.models.EmailMessage + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: IO + **kwargs # type: Any + ): + # type: (...) -> None + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + + @distributed_trace + def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: Union[_models.EmailMessage, IO] + **kwargs # type: Any + ): + # type: (...) -> None + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Is either a model type or a IO + type. Required. + :type email_message: ~azure.communication.email.models.EmailMessage or IO + :keyword content_type: Body Parameter content-type. Known values are: 'application/json'. + Default value is None. + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + content_type = kwargs.pop('content_type', _headers.pop('Content-Type', None)) # type: Optional[str] + cls = kwargs.pop('cls', None) # type: ClsType[None] + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(email_message, (IO, bytes)): + _content = email_message + else: + _json = self._serialize.body(email_message, 'EmailMessage') + + request = build_send_request( + repeatability_request_id=repeatability_request_id, + repeatability_first_sent=repeatability_first_sent, + api_version=api_version, + content_type=content_type, + json=_json, + content=_content, + template_url=self.send.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Repeatability-Result']=self._deserialize('str', response.headers.get('Repeatability-Result')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + response_headers['x-ms-request-id']=self._deserialize('str', response.headers.get('x-ms-request-id')) + + if cls: + return cls(pipeline_response, None, response_headers) + + send.metadata = {'url': "/emails:send"} # type: ignore + diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py new file mode 100644 index 000000000000..69156a77b9a3 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py @@ -0,0 +1,69 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import Any, IO, Union +from ._email_operations import EmailOperations as EmailOperationsGenerated +from ..models import _models, SendEmailResult + +class EmailOperations(EmailOperationsGenerated): + + def __return_message_id(self, pipeline_response, _, response_headers): + return response_headers['x-ms-request-id'] + + def send( + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: Union[_models.EmailMessage, IO] + **kwargs # type: Any + ): + # type: (...) -> SendEmailResult + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: SendEmailResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendEmailResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + message_id = super().send( + repeatability_request_id, + repeatability_first_sent, + email_message, + **dict(kwargs, cls=self.__return_message_id) + ) + return SendEmailResult(message_id=message_id) + + send.metadata = {'url': "/emails:send"} # type: ignore + +__all__ = ["EmailOperations"] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed b/sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed new file mode 100644 index 000000000000..e5aff4f83af8 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py new file mode 100644 index 000000000000..5b396cd202e8 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- diff --git a/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py similarity index 100% rename from sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py rename to sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py new file mode 100644 index 000000000000..ab028c385334 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py @@ -0,0 +1,37 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) +from datetime import datetime + +def get_current_utc_time(): + # type: () -> str + return str(datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S ")) + "GMT" + +def parse_connection_str(conn_str): + # type: (str) -> Tuple[str, str, str, str] + if conn_str is None: + raise ValueError( + "Connection string is undefined." + ) + endpoint = None + shared_access_key = None + for element in conn_str.split(";"): + key, _, value = element.partition("=") + if key.lower() == "endpoint": + endpoint = value.rstrip("/") + elif key.lower() == "accesskey": + shared_access_key = value + if not all([endpoint, shared_access_key]): + raise ValueError( + "Invalid connection string. You can get the connection string from your resource page in the Azure Portal. " + "The format should be as follows: endpoint=https:///;accesskey=" + ) + + return str(endpoint), str(shared_access_key) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_version.py b/sdk/communication/azure-communication-email/azure/communication/email/_version.py new file mode 100644 index 000000000000..41f0bacc9706 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_version.py @@ -0,0 +1,11 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +VERSION = "1.0.0b1" + +SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py new file mode 100644 index 000000000000..aa02483033ff --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py @@ -0,0 +1,5 @@ +from ._email_client_async import EmailClient + +__all__ = [ + 'EmailClient', +] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py new file mode 100644 index 000000000000..d89b789dedf1 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -0,0 +1,82 @@ +from uuid import uuid4 +from azure.core.tracing.decorator_async import distributed_trace_async +from .._shared.utils import parse_connection_str, get_current_utc_time +from .._shared.policy import HMACCredentialsPolicy +from .._generated.aio._azure_communication_email_service import AzureCommunicationEmailService +from .._version import SDK_MONIKER +from .._generated.models import SendEmailResult, SendStatusResult, EmailMessage + +class EmailClient(object): + """A client to interact with the AzureCommunicationService Email gateway asynchronously. + + This client provides operations to send an email and monitor its status. + + :param str conn_string: + The connection string to connect to an Azure Communication Service resource. + Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + """ + def __init__( + self, + conn_str, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + endpoint, access_key = parse_connection_str(conn_str) + authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + + self._generated_client = AzureCommunicationEmailService( + endpoint, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs + ) + + @distributed_trace_async + async def send( + self, + email_message, # type: EmailMessage + **kwargs # type: Any + ): # type: (...) -> SendEmailResult + """Queues an email message to be sent to one or more recipients. + + :param email_message: The message payload for sending an email. + :type email_message: ~azure.communication.email.models.EmailMessage + :return: SendEmailResult + :rtype: ~azure.communication.email.models.SendEmailResult + """ + + return await self._generated_client.email.send( + repeatability_request_id=uuid4(), + repeatability_first_sent=get_current_utc_time(), + email_message=email_message, + **kwargs + ) + + @distributed_trace_async + async def get_send_status( + self, + message_id, #type: str + **kwargs # type: Any + ): # type: (...) -> SendStatusResult + """Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send email + :type message_id: str + :return: SendStatusResult + :rtype: ~azure.communication.email.models.SendStatusResult + """ + + return await self._generated_client.email.get_send_status( + message_id=message_id, + **kwargs + ) + + async def __aenter__(self) -> "EmailClient": + await self._generated_client.__aenter__() + return self + + async def __aexit__(self, *args) -> None: + await self._generated_client.__aexit__(*args) + + async def close(self) -> None: + await self._generated_client.close() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/py.typed b/sdk/communication/azure-communication-email/azure/communication/email/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirement.txt new file mode 100644 index 000000000000..b8884941f2bd --- /dev/null +++ b/sdk/communication/azure-communication-email/dev_requirement.txt @@ -0,0 +1,7 @@ +-e ../../../tools/azure-sdk-tools +-e ../../../tools/azure-devtools +-e ../../identity/azure-identity +../../core/azure-core +aiohttp>=3.0 +aiounittest>=1.4 +pytest==7.1.2 \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py new file mode 100644 index 000000000000..4d161eea14ab --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py @@ -0,0 +1,72 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: check_message_status.py +DESCRIPTION: + This sample demonstrates checking the status of a sent email. The Email client is + authenticated using a connection string. +USAGE: + python check_message_status.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailCheckMessageStatusSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def check_message_status(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + # sending the email message + response = email_client.send(message) + + # using the message id to get the status of the email + message_id = response.message_id + message_status = email_client.get_send_status(message_id) + + print("Message Status: " + message_status.status) + +if __name__ == '__main__': + sample = EmailCheckMessageStatusSample() + sample.check_message_status() diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py new file mode 100644 index 000000000000..d294ab6d52e5 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py @@ -0,0 +1,82 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: check_message_status_async.py +DESCRIPTION: + This sample demonstrates checking the status of a sent email. The Email client is + authenticated using a connection string. +USAGE: + python check_message_status_async.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailCheckMessageStatusSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + async def check_message_status_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + + # using the message id to get the status of the email + message_id = response.message_id + message_status = await email_client.get_send_status(message_id) + + print("Message Status: " + message_status.status) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailCheckMessageStatusSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.check_message_status_async()) \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py new file mode 100644 index 000000000000..4c709356b27e --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py @@ -0,0 +1,72 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_multiple_recipient_sample.py +DESCRIPTION: + This sample demonstrates sending an email to multiple recipients. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailMultipleRecipientSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + second_recipient_address = os.getenv("SECOND_RECIPIENT_ADDRESS") + + def send_email_to_multiple_recipients(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + +if __name__ == '__main__': + sample = EmailMultipleRecipientSample() + sample.send_email_to_multiple_recipients() diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py new file mode 100644 index 000000000000..e77525bd3736 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -0,0 +1,82 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_multiple_recipient_sample_async.py +DESCRIPTION: + This sample demonstrates sending an email to multiple recipients. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailMultipleRecipientSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + second_recipient_address = os.getenv("SECOND_RECIPIENT_ADDRESS") + + async def send_email_to_multiple_recipients_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + print("Message ID: " + response.message_id) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailMultipleRecipientSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.send_email_to_multiple_recipients_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py new file mode 100644 index 000000000000..d7c58d33cd74 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py @@ -0,0 +1,67 @@ +# 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. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_single_recipient_sample.py +DESCRIPTION: + This sample demonstrates sending an email to a single recipient. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailSingleRecipientSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def send_email_to_single_recipient(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + +if __name__ == '__main__': + sample = EmailSingleRecipientSample() + sample.send_email_to_single_recipient() diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py new file mode 100644 index 000000000000..be15a68610f5 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py @@ -0,0 +1,77 @@ +# 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. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_single_recipient_sample_async.py +DESCRIPTION: + This sample demonstrates sending an email to a single recipient. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample_async.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailSingleRecipientSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + async def send_email_to_single_recipient_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + print("Message ID: " + response.message_id) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailSingleRecipientSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.send_email_to_single_recipient_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py new file mode 100644 index 000000000000..7dc5c180866c --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -0,0 +1,75 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_with_attachments_sample.py +DESCRIPTION: + This sample demonstrates sending an email with an attachment. The Email client is + authenticated using a connection string. +USAGE: + python send_email_with_attachment.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment, + EmailMessage +) + +sys.path.append("..") + +class EmailWithAttachmentSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def send_email_with_attachment(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients, + attachments=[attachment] + ) + + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + +if __name__ == '__main__': + sample = EmailWithAttachmentSample() + sample.send_email_with_attachment() diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py new file mode 100644 index 000000000000..ad6e14209064 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -0,0 +1,85 @@ +# 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. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_with_attachments_sample_async.py +DESCRIPTION: + This sample demonstrates sending an email with an attachment. The Email client is + authenticated using a connection string. +USAGE: + python send_email_with_attachment_async.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment, + EmailMessage +) + +sys.path.append("..") + +class EmailWithAttachmentSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + async def send_email_with_attachment_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients, + attachments=[attachment] + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + print("Message ID: " + response.message_id) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailWithAttachmentSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.send_email_with_attachment_async()) diff --git a/sdk/communication/azure-communication-email/setup.py b/sdk/communication/azure-communication-email/setup.py new file mode 100644 index 000000000000..b08b8ab01133 --- /dev/null +++ b/sdk/communication/azure-communication-email/setup.py @@ -0,0 +1,71 @@ +from setuptools import setup, find_packages +import os +from io import open +import re + +# example setup.py Feel free to copy the entire "azure-template" folder into a package folder named +# with "azure-". Ensure that the below arguments to setup() are updated to reflect +# your package. + +# this setup.py is set up in a specific way to keep the azure* and azure-mgmt-* namespaces WORKING all the way +# up from python 3.6. Reference here: https://github.com/Azure/azure-sdk-for-python/wiki/Azure-packaging + +PACKAGE_NAME = "azure-communication-email" +PACKAGE_PPRINT_NAME = "Communication Email" + +# a-b-c => a/b/c +package_folder_path = PACKAGE_NAME.replace('-', '/') +# a-b-c => a.b.c +namespace_name = PACKAGE_NAME.replace('-', '.') + +# Version extraction inspired from 'requests' +with open(os.path.join(package_folder_path, '_version.py'), 'r') as fd: + version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', + fd.read(), re.MULTILINE).group(1) +if not version: + raise RuntimeError('Cannot find version information') + +with open('README.md', encoding='utf-8') as f: + long_description = f.read() + +setup( + name=PACKAGE_NAME, + version=version, + description='Microsoft Azure {} Client Library for Python'.format(PACKAGE_PPRINT_NAME), + long_description=long_description, + long_description_content_type='text/markdown', + license='MIT License', + author='Microsoft Corporation', + author_email='azpysdkhelp@microsoft.com', + url='https://github.com/Azure/azure-sdk-for-python', + classifiers=[ + "Development Status :: 5 - Production/Stable", + 'Programming Language :: Python', + "Programming Language :: Python :: 3 :: Only", + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'License :: OSI Approved :: MIT License', + ], + zip_safe=False, + packages=find_packages(exclude=[ + 'tests', + # Exclude packages that will be covered by PEP420 or nspkg + 'azure', + 'azure.communication' + ]), + python_requires=">=3.6", + include_package_data=True, + package_data={ + 'pytyped': ['py.typed'], + }, + install_requires=[ + 'azure-core<2.0.0,>=1.15.0', + 'msrest>=0.6.21', + 'six>=1.11.0', + ], + extras_require={ + ":python_version<'3.8'": ["typing-extensions"] + } +) diff --git a/sdk/communication/azure-communication-email/swagger/SWAGGER.md b/sdk/communication/azure-communication-email/swagger/SWAGGER.md new file mode 100644 index 000000000000..28efe4a31e3c --- /dev/null +++ b/sdk/communication/azure-communication-email/swagger/SWAGGER.md @@ -0,0 +1,42 @@ +# Azure Communication Services Email REST API Client + +> see https://aka.ms/autorest + +### Setup +```ps +npm install -g autorest +``` + +### Generation +```ps +cd +autorest SWAGGER.md +``` + +### Settings +``` yaml +package-version: 1.0.0b1 +tag: package-2021-10-01-preview +require: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/communication/data-plane/Email/readme.md +output-folder: ../azure/communication/email/_generated +namespace: azure.communication.email +no-namespace-folders: true +license-header: MICROSOFT_MIT_NO_VERSION +enable-xml: true +clear-output-folder: true +python: true +v3: true +no-async: false +add-credential: false +security: Anonymous +title: Azure Communication Email Service +``` + +### Change the bCC property to bcc +```yaml +directive: + - from: swagger-document + where: $.definitions.EmailRecipients.properties.bCC + transform: > + $["x-ms-client-name"] = "bcc" +``` diff --git a/sdk/communication/azure-communication-email/tests/_shared/testcase.py b/sdk/communication/azure-communication-email/tests/_shared/testcase.py new file mode 100644 index 000000000000..cf7fb7d2e14d --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/_shared/testcase.py @@ -0,0 +1,102 @@ + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import os +import re +from devtools_testutils import AzureTestCase +from azure.communication.email._shared.utils import parse_connection_str +from azure_devtools.scenario_tests import RecordingProcessor, ReplayableTest +from azure_devtools.scenario_tests.utilities import is_text_payload + +class ResponseReplacerProcessor(RecordingProcessor): + def __init__(self, keys=None, replacement="sanitized"): + self._keys = keys if keys else [] + self._replacement = replacement + + def process_response(self, response): + def sanitize_dict(dictionary): + for key in dictionary: + value = dictionary[key] + if isinstance(value, str): + dictionary[key] = re.sub( + r"("+'|'.join(self._keys)+r")", + self._replacement, + dictionary[key]) + elif isinstance(value, dict): + sanitize_dict(value) + + sanitize_dict(response) + + return response + +class BodyReplacerProcessor(RecordingProcessor): + """Sanitize the sensitive info inside request or response bodies""" + + def __init__(self, keys=None, replacement="sanitized"): + self._replacement = replacement + self._keys = keys if keys else [] + + def process_request(self, request): + if is_text_payload(request) and request.body: + request.body = self._replace_keys(request.body.decode()).encode() + + return request + + def process_response(self, response): + if is_text_payload(response) and response['body']['string']: + response['body']['string'] = self._replace_keys(response['body']['string']) + + return response + + def _replace_keys(self, body): + def _replace_recursively(obj): + if isinstance(obj, dict): + for key in obj: + if key in self._keys: + obj[key] = self._replacement + else: + _replace_recursively(obj[key]) + elif isinstance(obj, list): + for i in obj: + _replace_recursively(i) + + import json + try: + body = json.loads(body) + _replace_recursively(body) + + except (KeyError, ValueError): + return body + + return json.dumps(body) + +class CommunicationTestCase(AzureTestCase): + # FILTER_HEADERS = ReplayableTest.FILTER_HEADERS + [ + # 'x-azure-ref', + # 'x-ms-content-sha256', + # 'location', + # # 'x-ms-date', + # # 'repeatability-first-sent', + # # 'repeatability-request-id', + # # 'operation-location', + # # 'date' + # ] + + def __init__(self, method_name, *args, **kwargs): + super(CommunicationTestCase, self).__init__(method_name, *args, **kwargs) + + def setUp(self): + super(CommunicationTestCase, self).setUp() + + # if self.is_playback(): + # self.connection_str = "endpoint=https://sanitized.communication.azure.com/;accesskey=fake===" + # else: + # self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') + # endpoint, _ = parse_connection_str(self.connection_str) + # self._resource_name = endpoint.split(".")[0] + # self.scrubber.register_name_pair(self._resource_name, "sanitized") + + self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml new file mode 100644 index 000000000000..8e9ff2be0c4d --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml @@ -0,0 +1,53 @@ +interactions: +- request: + body: '{"sender": "DoNotReply@266db372-a95a-494f-88b5-81ffd9e866af.azurecomm.net", + "content": {"subject": "This is the subject", "plainText": "This is the body"}, + "importance": "normal", "recipients": {"to": [{"email": "acseaastesting@gmail.com", + "displayName": "Customer Name"}]}}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '274' + Content-Type: + - application/json + User-Agent: + - azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0) + repeatability-first-sent: + - Thu, 16 Jun 2022 23:57:39 GMT + repeatability-request-id: + - e19daf10-3c5c-4b5a-84a2-6dcd31946e9a + x-ms-content-sha256: + - 5Efo+JDWFExoHqUXqWOtGVGR8b9UFPpSZvfMSo/0XCQ= + x-ms-date: + - Thu, 16 Jun 2022 23:57:39 GMT + x-ms-return-client-request-id: + - 'true' + method: POST + uri: https://email-js-sdk-recording-comm-2.communication.azure.com/emails:send?api-version=2021-10-01-preview + response: + body: + string: '' + headers: + api-supported-versions: + - 2021-10-01-preview + content-length: + - '0' + date: + - Thu, 16 Jun 2022 23:57:40 GMT + operation-location: + - https://email-js-sdk-recording-comm-2.communication.azure.com/emails/0ba65ac2-55da-4178-85b4-1d4f44c6fa84/status + repeatability-result: + - accepted + x-azure-ref: + - 0dMOrYgAAAAAaqONyzFwZSo1BsrwLQtafV1NURURHRTA4MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx + x-cache: + - CONFIG_NOCACHE + status: + code: 202 + message: Accepted +version: 1 diff --git a/sdk/communication/azure-communication-email/tests/test_email_client.py b/sdk/communication/azure-communication-email/tests/test_email_client.py new file mode 100644 index 000000000000..de4ba84d2835 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client.py @@ -0,0 +1,69 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import unittest +from unittest.mock import Mock + +from unittest_helpers import mock_response +from azure.communication.email import ( + EmailClient, + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress +) + + +class TestEmailClient(unittest.TestCase): + def test_send(self): + + message = EmailMessage( + sender="someSender@contoso.com", + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email="someRecipient@domain.com", display_name="Customer Name")] + ) + ) + + def mock_send(*_, **__): + return mock_response(status_code=202, headers={ + 'x-ms-request-id': "testMessageId" + }) + + email_client = EmailClient( + conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", + transport = Mock(send=mock_send) + ) + + response = None + raised = False + try: + response = email_client.send(message) + except: + raised = True + raise + + self.assertFalse(raised, 'Expected is no exception raised') + self.assertIsNotNone(response.message_id) + + def test_get_send_status(self): + + def mock_get_send_status(*_, **__): + return mock_response(status_code=200, json_payload={"test": "test"}) + + email_client = EmailClient( + conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", + transport = Mock(send=mock_get_send_status) + ) + response = None + raised = False + try: + response = email_client.get_send_status("testMessageId") + except: + raised = True + raise + + self.assertFalse(raised, 'Expected is no exception raised') + self.assertIsNotNone(response) \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py new file mode 100644 index 000000000000..4f6a2fe7cd59 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -0,0 +1,36 @@ +import os +from azure.communication.email import ( + EmailClient, + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress +) +from _shared.testcase import ( + CommunicationTestCase, +) + +class EmailClientTest(CommunicationTestCase): + def __init__(self, method_name): + super(EmailClientTest, self).__init__(method_name) + + def setUp(self): + super(EmailClientTest, self).setUp() + + self.sender_address = os.getenv("SENDER_ADDRESS") + self.recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def test_send_email_single(self): + email_client = EmailClient(self.connection_str) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) + + response = email_client.send(message) + print(response) + assert response is None \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/unittest_helpers.py b/sdk/communication/azure-communication-email/tests/unittest_helpers.py new file mode 100644 index 000000000000..9d24a0aa86eb --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/unittest_helpers.py @@ -0,0 +1,20 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import json + +from unittest import mock + +def mock_response(status_code=200, headers=None, json_payload=None): + response = mock.Mock(status_code=status_code, headers=headers or {}) + if json_payload is not None: + response.text = lambda encoding=None: json.dumps(json_payload) + response.headers["content-type"] = "application/json" + response.content_type = "application/json" + else: + response.text = lambda encoding=None: "" + response.headers["content-type"] = "text/plain" + response.content_type = "text/plain" + return response From 484a2336809a4624f008276f6b3865d9cea07912 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Wed, 22 Jun 2022 18:00:29 -0700 Subject: [PATCH 02/30] Updating sdk tests --- .../azure-communication-email/README.md | 159 +++++++++++++++++- .../dev_requirement.txt | 3 +- .../tests/_shared/testcase.py | 102 ----------- .../tests/async_preparers.py | 36 ++++ .../tests/conftest.py | 49 ++++++ .../tests/preparers.py | 17 ++ ...EmailClienttest_send_email_attachment.json | 56 ++++++ ...nttest_send_email_multiple_recipients.json | 53 ++++++ ...lienttest_send_email_single_recipient.json | 49 ++++++ ...ail_client_e2e.test_send_email_single.yaml | 53 ------ ...EmailClienttest_send_email_attachment.json | 55 ++++++ ...nttest_send_email_multiple_recipients.json | 52 ++++++ ...lienttest_send_email_single_recipient.json | 48 ++++++ .../tests/test_email_client.py | 69 -------- .../tests/test_email_client_e2e.py | 92 ++++++++-- .../tests/test_email_client_e2e_async.py | 99 +++++++++++ .../tests/unittest_helpers.py | 20 --- sdk/communication/ci.yml | 2 + 18 files changed, 750 insertions(+), 264 deletions(-) delete mode 100644 sdk/communication/azure-communication-email/tests/_shared/testcase.py create mode 100644 sdk/communication/azure-communication-email/tests/async_preparers.py create mode 100644 sdk/communication/azure-communication-email/tests/conftest.py create mode 100644 sdk/communication/azure-communication-email/tests/preparers.py create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json delete mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json delete mode 100644 sdk/communication/azure-communication-email/tests/test_email_client.py create mode 100644 sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py delete mode 100644 sdk/communication/azure-communication-email/tests/unittest_helpers.py diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index 38f1d4c0c53d..150594e21494 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -1 +1,158 @@ -# TODO: Populate this README \ No newline at end of file +# Azure Communication Email client library for Python + +This package contains a Python SDK for Azure Communication Services for Email. + +## Getting started + +### Prerequisites + +You need an [Azure subscription][azure_sub], a [Communication Service Resource][communication_resource_docs], and an [Email Communication Resource][email_resource_docs] with an active [Domain][domain_overview]. + +To create these resource, you can use the [Azure Portal][communication_resource_create_portal], the [Azure PowerShell][communication_resource_create_power_shell], or the [.NET management client library][communication_resource_create_net]. + +### Installing + +Install the Azure Communication Email client library for Python with [pip](https://pypi.org/project/pip/): + +```bash +pip install azure-communication-email +``` + +## Examples + +`EmailClient` provides the functionality to send email messages . + +## Authentication + +Email clients can be authenticated using the connection string acquired from an Azure Communication Resource in the [Azure Portal][azure_portal]. + +```python +from azure.communication.email import EmailClient + +connection_string = "endpoint=https://.communication.azure.com/;accessKey=" +client = EmailClient(connectionString); +``` + +### Send an Email Message + +To send an email message, call the `send` function from the `EmailClient`. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]) + ) + +response = client.send(message) +``` + +### Send an Email Message to Multiple Recipients + +To send an email message to multiple recipients, add a object for each recipient type and an object for each recipient. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +recipients = EmailRecipients( + to=[ + EmailAddress(email="customer@domain.com", display_name="Customer Name"), + EmailAddress(email="customer2@domain.com", display_name="Customer Name 2"), + ], + cc=[ + EmailAddress(email="ccCustomer@domain.com", display_name="CC Customer Name"), + EmailAddress(email="ccCustomer2@domain.com", display_name="CC Customer Name 2"), + ], + bcc=[ + EmailAddress(email="bccCustomer@domain.com", display_name="BCC Customer Name"), + EmailAddress(email="bccCustomer2@domain.com", display_name="BCC Customer Name 2"), + ] + ) + +message = EmailMessage(sender="sender@contoso.com", content=content, recipients=recipients) +response = client.send(message) +``` + +### Send Email with Attachments + +Azure Communication Services support sending email with attachments. + +```python +file = open("C://readme.txt", "r") +file_contents = file.read() +file.close() + +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64=base64.b64encode(file_contents) +) + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]), + attachments=[attachment] + ) + +response = client.send(message) +``` + +### Get Email Message Status + +The result from the `send` call contains a `message_id` which can be used to query the status of the email. + +```python +response = client.send(message) +status = client.get_sent_status(message_id) +``` + +## Next steps + +- [Read more about Email in Azure Communication Services][nextsteps] + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. + + + +[azure_sub]: https://azure.microsoft.com/free/dotnet/ +[azure_portal]: https://portal.azure.com +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com +[communication_resource_docs]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[email_resource_docs]: https://aka.ms/acsemail/createemailresource +[communication_resource_create_portal]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[communication_resource_create_power_shell]: https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice +[communication_resource_create_net]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-net +[package]: https://www.nuget.org/packages/Azure.Communication.Common/ +[product_docs]: https://aka.ms/acsemail/overview +[nextsteps]: https://aka.ms/acsemail/overview +[nuget]: https://www.nuget.org/ +[source]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/communication +[domain_overview]: https://aka.ms/acsemail/domainsoverview \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirement.txt index b8884941f2bd..8fd523934a46 100644 --- a/sdk/communication/azure-communication-email/dev_requirement.txt +++ b/sdk/communication/azure-communication-email/dev_requirement.txt @@ -4,4 +4,5 @@ ../../core/azure-core aiohttp>=3.0 aiounittest>=1.4 -pytest==7.1.2 \ No newline at end of file +pytest==7.1.2 +pytest-tornasync==0.6.0.post2 \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/_shared/testcase.py b/sdk/communication/azure-communication-email/tests/_shared/testcase.py deleted file mode 100644 index cf7fb7d2e14d..000000000000 --- a/sdk/communication/azure-communication-email/tests/_shared/testcase.py +++ /dev/null @@ -1,102 +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 os -import re -from devtools_testutils import AzureTestCase -from azure.communication.email._shared.utils import parse_connection_str -from azure_devtools.scenario_tests import RecordingProcessor, ReplayableTest -from azure_devtools.scenario_tests.utilities import is_text_payload - -class ResponseReplacerProcessor(RecordingProcessor): - def __init__(self, keys=None, replacement="sanitized"): - self._keys = keys if keys else [] - self._replacement = replacement - - def process_response(self, response): - def sanitize_dict(dictionary): - for key in dictionary: - value = dictionary[key] - if isinstance(value, str): - dictionary[key] = re.sub( - r"("+'|'.join(self._keys)+r")", - self._replacement, - dictionary[key]) - elif isinstance(value, dict): - sanitize_dict(value) - - sanitize_dict(response) - - return response - -class BodyReplacerProcessor(RecordingProcessor): - """Sanitize the sensitive info inside request or response bodies""" - - def __init__(self, keys=None, replacement="sanitized"): - self._replacement = replacement - self._keys = keys if keys else [] - - def process_request(self, request): - if is_text_payload(request) and request.body: - request.body = self._replace_keys(request.body.decode()).encode() - - return request - - def process_response(self, response): - if is_text_payload(response) and response['body']['string']: - response['body']['string'] = self._replace_keys(response['body']['string']) - - return response - - def _replace_keys(self, body): - def _replace_recursively(obj): - if isinstance(obj, dict): - for key in obj: - if key in self._keys: - obj[key] = self._replacement - else: - _replace_recursively(obj[key]) - elif isinstance(obj, list): - for i in obj: - _replace_recursively(i) - - import json - try: - body = json.loads(body) - _replace_recursively(body) - - except (KeyError, ValueError): - return body - - return json.dumps(body) - -class CommunicationTestCase(AzureTestCase): - # FILTER_HEADERS = ReplayableTest.FILTER_HEADERS + [ - # 'x-azure-ref', - # 'x-ms-content-sha256', - # 'location', - # # 'x-ms-date', - # # 'repeatability-first-sent', - # # 'repeatability-request-id', - # # 'operation-location', - # # 'date' - # ] - - def __init__(self, method_name, *args, **kwargs): - super(CommunicationTestCase, self).__init__(method_name, *args, **kwargs) - - def setUp(self): - super(CommunicationTestCase, self).setUp() - - # if self.is_playback(): - # self.connection_str = "endpoint=https://sanitized.communication.azure.com/;accesskey=fake===" - # else: - # self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') - # endpoint, _ = parse_connection_str(self.connection_str) - # self._resource_name = endpoint.split(".")[0] - # self.scrubber.register_name_pair(self._resource_name, "sanitized") - - self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') diff --git a/sdk/communication/azure-communication-email/tests/async_preparers.py b/sdk/communication/azure-communication-email/tests/async_preparers.py new file mode 100644 index 000000000000..aec31c005621 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/async_preparers.py @@ -0,0 +1,36 @@ +import os +from devtools_testutils import is_live + +def email_decorator_async(func, **kwargs): + async def wrapper(self, *args, **kwargs): + if is_live(): + self.communication_connection_string = os.environ["COMMUNICATION_CONNECTION_STRING"] + self.sender_address = os.environ["SENDER_ADDRESS"] + self.recipient_address = os.environ["RECIPIENT_ADDRESS"] + else: + self.communication_connection_string = "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==" + self.sender_address = "someSender@contoso.com" + self.recipient_address = "someRecipient@domain.com" + + EXPONENTIAL_BACKOFF = 1.5 + RETRY_COUNT = 0 + + try: + return await func(self, *args, **kwargs) + except HttpResponseError as exc: + if exc.status_code != 429: + raise + print("Retrying: {} {}".format(RETRY_COUNT, EXPONENTIAL_BACKOFF)) + while RETRY_COUNT < 6: + if is_live(): + time.sleep(EXPONENTIAL_BACKOFF) + try: + return await func(self, *args, **kwargs) + except HttpResponseError as exc: + print("Retrying: {} {}".format(RETRY_COUNT, EXPONENTIAL_BACKOFF)) + EXPONENTIAL_BACKOFF **= 2 + RETRY_COUNT += 1 + if exc.status_code != 429 or RETRY_COUNT >= 6: + raise + + return wrapper diff --git a/sdk/communication/azure-communication-email/tests/conftest.py b/sdk/communication/azure-communication-email/tests/conftest.py new file mode 100644 index 000000000000..c122d990f7b2 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/conftest.py @@ -0,0 +1,49 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import pytest +import os +from devtools_testutils import test_proxy, add_general_regex_sanitizer, add_header_regex_sanitizer, add_body_regex_sanitizer +from azure.communication.email._shared.utils import parse_connection_str + +@pytest.fixture(scope="session", autouse=True) +def add_sanitizers(test_proxy): + communication_connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING", "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==") + sender_address = os.getenv("SENDER_ADDRESS", "someSender@contoso.com") + recipient_address = os.getenv("RECIPIENT_ADDRESS", "someRecipient@domain.com") + + add_general_regex_sanitizer(regex=communication_connection_string, value="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==") + add_general_regex_sanitizer(regex=sender_address, value="someSender@contoso.com") + add_general_regex_sanitizer(regex=recipient_address, value="someRecipient@domain.com") + + endpoint, _ = parse_connection_str(communication_connection_string) + add_general_regex_sanitizer(regex=endpoint, value="https://someEndpoint") + + add_header_regex_sanitizer(key="repeatability-first-sent", value="sanitized") + add_header_regex_sanitizer(key="repeatability-request-id", value="sanitized") + add_header_regex_sanitizer(key="x-ms-content-sha256", value="sanitized") + add_header_regex_sanitizer(key="Operation-Location", value="https://someEndpoint/emails/someMessageId/status") + add_header_regex_sanitizer(key="Date", value="sanitized") + add_header_regex_sanitizer(key="x-azure-ref", value="sanitized") diff --git a/sdk/communication/azure-communication-email/tests/preparers.py b/sdk/communication/azure-communication-email/tests/preparers.py new file mode 100644 index 000000000000..e8bb262257bc --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/preparers.py @@ -0,0 +1,17 @@ +import os +from devtools_testutils import is_live + +def email_decorator(func, **kwargs): + def wrapper(self, *args, **kwargs): + if is_live(): + self.communication_connection_string = os.environ["COMMUNICATION_CONNECTION_STRING"] + self.sender_address = os.environ["SENDER_ADDRESS"] + self.recipient_address = os.environ["RECIPIENT_ADDRESS"] + else: + self.communication_connection_string = "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==" + self.sender_address = "someSender@contoso.com" + self.recipient_address = "someRecipient@domain.com" + + func(self, *args, **kwargs) + + return wrapper diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json new file mode 100644 index 000000000000..539de711bc8c --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json @@ -0,0 +1,56 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "355", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + }, + "attachments": [ + { + "name": "readme.txt", + "attachmentType": "txt", + "contentBytesBase64": "ZW1haWwgdGVzdCBhdHRhY2htZW50" + } + ] + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json new file mode 100644 index 000000000000..4fdfeab89f35 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -0,0 +1,53 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "308", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:47 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + }, + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name 2" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json new file mode 100644 index 000000000000..be357f4913bd --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json @@ -0,0 +1,49 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "235", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:46 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml deleted file mode 100644 index 8e9ff2be0c4d..000000000000 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml +++ /dev/null @@ -1,53 +0,0 @@ -interactions: -- request: - body: '{"sender": "DoNotReply@266db372-a95a-494f-88b5-81ffd9e866af.azurecomm.net", - "content": {"subject": "This is the subject", "plainText": "This is the body"}, - "importance": "normal", "recipients": {"to": [{"email": "acseaastesting@gmail.com", - "displayName": "Customer Name"}]}}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '274' - Content-Type: - - application/json - User-Agent: - - azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0) - repeatability-first-sent: - - Thu, 16 Jun 2022 23:57:39 GMT - repeatability-request-id: - - e19daf10-3c5c-4b5a-84a2-6dcd31946e9a - x-ms-content-sha256: - - 5Efo+JDWFExoHqUXqWOtGVGR8b9UFPpSZvfMSo/0XCQ= - x-ms-date: - - Thu, 16 Jun 2022 23:57:39 GMT - x-ms-return-client-request-id: - - 'true' - method: POST - uri: https://email-js-sdk-recording-comm-2.communication.azure.com/emails:send?api-version=2021-10-01-preview - response: - body: - string: '' - headers: - api-supported-versions: - - 2021-10-01-preview - content-length: - - '0' - date: - - Thu, 16 Jun 2022 23:57:40 GMT - operation-location: - - https://email-js-sdk-recording-comm-2.communication.azure.com/emails/0ba65ac2-55da-4178-85b4-1d4f44c6fa84/status - repeatability-result: - - accepted - x-azure-ref: - - 0dMOrYgAAAAAaqONyzFwZSo1BsrwLQtafV1NURURHRTA4MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx - x-cache: - - CONFIG_NOCACHE - status: - code: 202 - message: Accepted -version: 1 diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json new file mode 100644 index 000000000000..1077749edad0 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json @@ -0,0 +1,55 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "355", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + }, + "attachments": [ + { + "name": "readme.txt", + "attachmentType": "txt", + "contentBytesBase64": "ZW1haWwgdGVzdCBhdHRhY2htZW50" + } + ] + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json new file mode 100644 index 000000000000..9c8535417424 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -0,0 +1,52 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "308", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + }, + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name 2" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json new file mode 100644 index 000000000000..972c5dae3f3d --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json @@ -0,0 +1,48 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "235", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/test_email_client.py b/sdk/communication/azure-communication-email/tests/test_email_client.py deleted file mode 100644 index de4ba84d2835..000000000000 --- a/sdk/communication/azure-communication-email/tests/test_email_client.py +++ /dev/null @@ -1,69 +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 unittest -from unittest.mock import Mock - -from unittest_helpers import mock_response -from azure.communication.email import ( - EmailClient, - EmailMessage, - EmailContent, - EmailRecipients, - EmailAddress -) - - -class TestEmailClient(unittest.TestCase): - def test_send(self): - - message = EmailMessage( - sender="someSender@contoso.com", - content=EmailContent(subject="This is the subject", plain_text="This is the body"), - recipients=EmailRecipients( - to=[EmailAddress(email="someRecipient@domain.com", display_name="Customer Name")] - ) - ) - - def mock_send(*_, **__): - return mock_response(status_code=202, headers={ - 'x-ms-request-id': "testMessageId" - }) - - email_client = EmailClient( - conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", - transport = Mock(send=mock_send) - ) - - response = None - raised = False - try: - response = email_client.send(message) - except: - raised = True - raise - - self.assertFalse(raised, 'Expected is no exception raised') - self.assertIsNotNone(response.message_id) - - def test_get_send_status(self): - - def mock_get_send_status(*_, **__): - return mock_response(status_code=200, json_payload={"test": "test"}) - - email_client = EmailClient( - conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", - transport = Mock(send=mock_get_send_status) - ) - response = None - raised = False - try: - response = email_client.get_send_status("testMessageId") - except: - raised = True - raise - - self.assertFalse(raised, 'Expected is no exception raised') - self.assertIsNotNone(response) \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py index 4f6a2fe7cd59..a8c317edf2a8 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -1,36 +1,92 @@ -import os from azure.communication.email import ( EmailClient, EmailMessage, EmailContent, EmailRecipients, - EmailAddress -) -from _shared.testcase import ( - CommunicationTestCase, + EmailAddress, + EmailAttachment ) +from devtools_testutils import AzureRecordedTestCase, recorded_by_proxy +from preparers import email_decorator + +class TestEmailClient(AzureRecordedTestCase): + # TODO: Change the assert statements once x-ms-request-id change is merged in + @email_decorator + @recorded_by_proxy + def test_send_email_single_recipient(self): + email_client = EmailClient(self.communication_connection_string) -class EmailClientTest(CommunicationTestCase): - def __init__(self, method_name): - super(EmailClientTest, self).__init__(method_name) + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) - def setUp(self): - super(EmailClientTest, self).setUp() + response = email_client.send(message) + assert response is not None - self.sender_address = os.getenv("SENDER_ADDRESS") - self.recipient_address = os.getenv("RECIPIENT_ADDRESS") + @email_decorator + @recorded_by_proxy + def test_send_email_multiple_recipients(self): + email_client = EmailClient(self.communication_connection_string) - def test_send_email_single(self): - email_client = EmailClient(self.connection_str) - message = EmailMessage( sender=self.sender_address, content=EmailContent(subject="This is the subject", plain_text="This is the body"), recipients=EmailRecipients( - to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.recipient_address, display_name="Customer Name 2"), + ] ) ) response = email_client.send(message) - print(response) - assert response is None \ No newline at end of file + assert response is not None + + @email_decorator + @recorded_by_proxy + def test_send_email_attachment(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ), + attachments=[ + EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + ] + ) + + response = email_client.send(message) + assert response is not None + + # TODO: Comment back in once the x-ms-request-id change is merged in + # @email_decorator + # @recorded_by_proxy + # def test_check_message_status(self): + # email_client = EmailClient(self.communication_connection_string) + + # message = EmailMessage( + # sender=self.sender_address, + # content=EmailContent(subject="This is the subject", plain_text="This is the body"), + # recipients=EmailRecipients( + # to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + # ) + # ) + + # response = email_client.send(message) + # message_id = response.message_id + # if message_id is not None: + # message_status_response = email_client.get_send_status(message_id) + # assert message_status_response.status is not None + # else: + # assert False diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py new file mode 100644 index 000000000000..718a90fb6465 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py @@ -0,0 +1,99 @@ +import pytest + +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment +) +from devtools_testutils import AzureRecordedTestCase +from devtools_testutils.aio import recorded_by_proxy_async +from async_preparers import email_decorator_async + +class TestEmailClient(AzureRecordedTestCase): + # TODO: Change the assert statements once x-ms-request-id change is merged in + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_single_recipient(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) + + async with email_client: + response = await email_client.send(message) + assert response is not None + + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_multiple_recipients(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.recipient_address, display_name="Customer Name 2"), + ] + ) + ) + + async with email_client: + response = await email_client.send(message) + assert response is not None + + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_attachment(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ), + attachments=[ + EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + ] + ) + + async with email_client: + response = await email_client.send(message) + assert response is not None + + # TODO: Comment back in once the x-ms-request-id change is merged in + # @email_decorator_async + # @recorded_by_proxy_async + # async def test_check_message_status(self): + # email_client = EmailClient(self.communication_connection_string) + + # message = EmailMessage( + # sender=self.sender_address, + # content=EmailContent(subject="This is the subject", plain_text="This is the body"), + # recipients=EmailRecipients( + # to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + # ) + # ) + + # async with email_client: + # response = await email_client.send(message) + # message_id = response.message_id + # if message_id is not None: + # message_status_response = await email_client.get_send_status(message_id) + # assert message_status_response.status is not None + # else: + # assert False diff --git a/sdk/communication/azure-communication-email/tests/unittest_helpers.py b/sdk/communication/azure-communication-email/tests/unittest_helpers.py deleted file mode 100644 index 9d24a0aa86eb..000000000000 --- a/sdk/communication/azure-communication-email/tests/unittest_helpers.py +++ /dev/null @@ -1,20 +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 json - -from unittest import mock - -def mock_response(status_code=200, headers=None, json_payload=None): - response = mock.Mock(status_code=status_code, headers=headers or {}) - if json_payload is not None: - response.text = lambda encoding=None: json.dumps(json_payload) - response.headers["content-type"] = "application/json" - response.content_type = "application/json" - else: - response.text = lambda encoding=None: "" - response.headers["content-type"] = "text/plain" - response.content_type = "text/plain" - return response diff --git a/sdk/communication/ci.yml b/sdk/communication/ci.yml index 40619c505859..fee3c672cb36 100644 --- a/sdk/communication/ci.yml +++ b/sdk/communication/ci.yml @@ -35,6 +35,8 @@ extends: safeName: azurecommunicationidentity - name: azure-communication-chat safeName: azurecommunicationchat + - name: azure-communication-email + safeName: azurecommunicationemail - name: azure-mgmt-communication safeName: azuremgmtcommunication - name: azure-communication-sms From abe693c43e658afa296ab32f94bb169ba0ce3271 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Thu, 23 Jun 2022 11:37:20 -0700 Subject: [PATCH 03/30] Updating constructor and fixing linting errors --- .../azure-communication-email/README.md | 20 ++++++++- .../azure/communication/email/__init__.py | 2 +- .../communication/email/_email_client.py | 40 ++++++++++++++---- .../azure/communication/email/_version.py | 2 +- .../email/aio/_email_client_async.py | 42 +++++++++++++++---- ...v_requirement.txt => dev_requirements.txt} | 0 .../samples/check_message_status_sample.py | 2 +- .../check_message_status_sample_async.py | 4 +- ...end_email_to_multiple_recipients_sample.py | 2 +- ...ail_to_multiple_recipients_sample_async.py | 2 +- .../send_email_to_single_recipient_sample.py | 2 +- ..._email_to_single_recipient_sample_async.py | 2 +- .../send_email_with_attachments_sample.py | 2 +- ...end_email_with_attachments_sample_async.py | 2 +- .../azure-communication-email/setup.py | 2 +- ...EmailClienttest_send_email_attachment.json | 2 +- ...nttest_send_email_multiple_recipients.json | 2 +- ...lienttest_send_email_single_recipient.json | 2 +- ...EmailClienttest_send_email_attachment.json | 2 +- ...nttest_send_email_multiple_recipients.json | 2 +- ...lienttest_send_email_single_recipient.json | 2 +- .../tests/test_email_client_e2e.py | 8 ++-- .../tests/test_email_client_e2e_async.py | 8 ++-- sdk/communication/ci.yml | 1 + 24 files changed, 111 insertions(+), 44 deletions(-) rename sdk/communication/azure-communication-email/{dev_requirement.txt => dev_requirements.txt} (100%) diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index 150594e21494..bd952e31803f 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -2,6 +2,12 @@ This package contains a Python SDK for Azure Communication Services for Email. +## Key concepts + +The Azure Communication Email package is used to do following: +- Send emails to multiple types of recipients +- Query the status of a sent email message + ## Getting started ### Prerequisites @@ -30,7 +36,7 @@ Email clients can be authenticated using the connection string acquired from an from azure.communication.email import EmailClient connection_string = "endpoint=https://.communication.azure.com/;accessKey=" -client = EmailClient(connectionString); +client = EmailClient.from_connection_string(connection_string); ``` ### Send an Email Message @@ -127,6 +133,18 @@ response = client.send(message) status = client.get_sent_status(message_id) ``` +## Troubleshooting + +Email operations will throw an exception if the request to the server fails. The Email client will raise exceptions defined in [Azure Core](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/README.md). + +```Python +try: + response = email_client.send(message) +except Exception as ex: + print('Exception:') + print(ex) +``` + ## Next steps - [Read more about Email in Azure Communication Services][nextsteps] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py index f7befc593db1..9c22b889a185 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py @@ -27,4 +27,4 @@ 'SendEmailResult', 'SendStatus', 'SendStatusResult', -] \ No newline at end of file +] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py index c87a337f562b..44751fd3cd9a 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -1,3 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + from uuid import uuid4 from azure.core.tracing.decorator import distributed_trace from ._shared.utils import parse_connection_str, get_current_utc_time @@ -6,23 +12,24 @@ from ._version import SDK_MONIKER from ._generated.models import SendEmailResult, SendStatusResult, EmailMessage -class EmailClient(object): +class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword """A client to interact with the AzureCommunicationService Email gateway. This client provides operations to send an email and monitor its status. - :param str conn_string: - The connection string to connect to an Azure Communication Service resource. - Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param TokenCredential credential: + The TokenCredential we use to authenticate against the service. """ def __init__( self, - conn_str, # type: str + endpoint, # type: str + credential, # type: str **kwargs # type: Any ): # type: (...) -> None - endpoint, access_key = parse_connection_str(conn_str) - authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + authentication_policy = HMACCredentialsPolicy(endpoint, credential) self._generated_client = AzureCommunicationEmailService( endpoint, @@ -30,6 +37,23 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) + + @classmethod + def from_connection_string( + cls, + conn_str, # type: str + **kwargs # type: Any + ): # type: (...) -> EmailClient + """Create EmailClient from a Connection String. + + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of EmailClient. + :rtype: ~azure.communication.EmailClient + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) @distributed_trace def send( @@ -51,7 +75,7 @@ def send( email_message=email_message, **kwargs ) - + @distributed_trace def get_send_status( self, diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_version.py b/sdk/communication/azure-communication-email/azure/communication/email/_version.py index 41f0bacc9706..eadf444aa551 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_version.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_version.py @@ -8,4 +8,4 @@ VERSION = "1.0.0b1" -SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str \ No newline at end of file +SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py index d89b789dedf1..acc414758195 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -1,3 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + from uuid import uuid4 from azure.core.tracing.decorator_async import distributed_trace_async from .._shared.utils import parse_connection_str, get_current_utc_time @@ -6,23 +12,24 @@ from .._version import SDK_MONIKER from .._generated.models import SendEmailResult, SendStatusResult, EmailMessage -class EmailClient(object): +class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword """A client to interact with the AzureCommunicationService Email gateway asynchronously. This client provides operations to send an email and monitor its status. - :param str conn_string: - The connection string to connect to an Azure Communication Service resource. - Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param TokenCredential credential: + The TokenCredential we use to authenticate against the service. """ def __init__( self, - conn_str, # type: str + endpoint, # type: str + credential, # type: str **kwargs # type: Any ): # type: (...) -> None - endpoint, access_key = parse_connection_str(conn_str) - authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + authentication_policy = HMACCredentialsPolicy(endpoint, credential) self._generated_client = AzureCommunicationEmailService( endpoint, @@ -30,6 +37,23 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) + + @classmethod + def from_connection_string( + cls, + conn_str, # type: str + **kwargs # type: Any + ): # type: (...) -> EmailClient + """Create EmailClient from a Connection String. + + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of EmailClient. + :rtype: ~azure.communication.EmailClient + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) @distributed_trace_async async def send( @@ -51,7 +75,7 @@ async def send( email_message=email_message, **kwargs ) - + @distributed_trace_async async def get_send_status( self, @@ -79,4 +103,4 @@ async def __aexit__(self, *args) -> None: await self._generated_client.__aexit__(*args) async def close(self) -> None: - await self._generated_client.close() \ No newline at end of file + await self._generated_client.close() diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirements.txt similarity index 100% rename from sdk/communication/azure-communication-email/dev_requirement.txt rename to sdk/communication/azure-communication-email/dev_requirements.txt diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py index 4d161eea14ab..d249d646cedc 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py @@ -39,7 +39,7 @@ class EmailCheckMessageStatusSample(object): def check_message_status(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py index d294ab6d52e5..d0a6b6278e3a 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py @@ -40,7 +40,7 @@ class EmailCheckMessageStatusSampleAsync(object): async def check_message_status_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( @@ -79,4 +79,4 @@ async def check_message_status_async(self): # Comment in this line if you are running this sample on Windows # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - asyncio.run(sample.check_message_status_async()) \ No newline at end of file + asyncio.run(sample.check_message_status_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py index 4c709356b27e..2f365d626228 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py @@ -41,7 +41,7 @@ class EmailMultipleRecipientSample(object): def send_email_to_multiple_recipients(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py index e77525bd3736..0b43bd6689a3 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -42,7 +42,7 @@ class EmailMultipleRecipientSampleAsync(object): async def send_email_to_multiple_recipients_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py index d7c58d33cd74..0dc9f3112415 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py @@ -39,7 +39,7 @@ class EmailSingleRecipientSample(object): def send_email_to_single_recipient(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py index be15a68610f5..56dc2ca69966 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py @@ -40,7 +40,7 @@ class EmailSingleRecipientSampleAsync(object): async def send_email_to_single_recipient_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py index 7dc5c180866c..4994666f6b92 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -40,7 +40,7 @@ class EmailWithAttachmentSample(object): def send_email_with_attachment(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py index ad6e14209064..2654a67fd0d0 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -41,7 +41,7 @@ class EmailWithAttachmentSampleAsync(object): async def send_email_with_attachment_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/setup.py b/sdk/communication/azure-communication-email/setup.py index b08b8ab01133..d8092dd74f16 100644 --- a/sdk/communication/azure-communication-email/setup.py +++ b/sdk/communication/azure-communication-email/setup.py @@ -61,7 +61,7 @@ 'pytyped': ['py.typed'], }, install_requires=[ - 'azure-core<2.0.0,>=1.15.0', + 'azure-core<2.0.0,>=1.2.2', 'msrest>=0.6.21', 'six>=1.11.0', ], diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json index 539de711bc8c..cd2e134a56b4 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json @@ -13,7 +13,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json index 4fdfeab89f35..02d02db09d8d 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -13,7 +13,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:47 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:47 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json index be357f4913bd..359dd7f050a7 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json @@ -13,7 +13,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:46 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:47 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json index 1077749edad0..a725781e858c 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json @@ -12,7 +12,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json index 9c8535417424..3f18dae2f504 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -12,7 +12,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json index 972c5dae3f3d..37459430bf1c 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json @@ -12,7 +12,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py index a8c317edf2a8..98d136c719ca 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -14,7 +14,7 @@ class TestEmailClient(AzureRecordedTestCase): @email_decorator @recorded_by_proxy def test_send_email_single_recipient(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -30,7 +30,7 @@ def test_send_email_single_recipient(self): @email_decorator @recorded_by_proxy def test_send_email_multiple_recipients(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -49,7 +49,7 @@ def test_send_email_multiple_recipients(self): @email_decorator @recorded_by_proxy def test_send_email_attachment(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -73,7 +73,7 @@ def test_send_email_attachment(self): # @email_decorator # @recorded_by_proxy # def test_check_message_status(self): - # email_client = EmailClient(self.communication_connection_string) + # email_client = EmailClient.from_connection_string(self.communication_connection_string) # message = EmailMessage( # sender=self.sender_address, diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py index 718a90fb6465..da4dcfd90d60 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py @@ -17,7 +17,7 @@ class TestEmailClient(AzureRecordedTestCase): @email_decorator_async @recorded_by_proxy_async async def test_send_email_single_recipient(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -34,7 +34,7 @@ async def test_send_email_single_recipient(self): @email_decorator_async @recorded_by_proxy_async async def test_send_email_multiple_recipients(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -54,7 +54,7 @@ async def test_send_email_multiple_recipients(self): @email_decorator_async @recorded_by_proxy_async async def test_send_email_attachment(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -79,7 +79,7 @@ async def test_send_email_attachment(self): # @email_decorator_async # @recorded_by_proxy_async # async def test_check_message_status(self): - # email_client = EmailClient(self.communication_connection_string) + # email_client = EmailClient.from_connection_string(self.communication_connection_string) # message = EmailMessage( # sender=self.sender_address, diff --git a/sdk/communication/ci.yml b/sdk/communication/ci.yml index fee3c672cb36..59ea57e5cbe9 100644 --- a/sdk/communication/ci.yml +++ b/sdk/communication/ci.yml @@ -30,6 +30,7 @@ extends: template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml parameters: ServiceDirectory: communication + TestProxy: true Artifacts: - name: azure-communication-identity safeName: azurecommunicationidentity From 3fbdcdf6091962dd76e50bd3da335d886311b902 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Thu, 23 Jun 2022 13:19:50 -0700 Subject: [PATCH 04/30] Adding python SDK --- .../azure-communication-email/CHANGELOG.md | 9 + .../azure-communication-email/LICENSE | 21 + .../azure-communication-email/MANIFEST.in | 7 + .../azure-communication-email/README.md | 1 + .../azure/__init__.py | 1 + .../azure/communication/__init__.py | 1 + .../azure/communication/email/__init__.py | 30 ++ .../communication/email/_email_client.py | 72 +++ .../email/_generated/__init__.py | 23 + .../_azure_communication_email_service.py | 100 ++++ .../email/_generated/_configuration.py | 65 +++ .../communication/email/_generated/_patch.py | 23 + .../communication/email/_generated/_vendor.py | 27 ++ .../email/_generated/_version.py | 11 + .../email/_generated/aio/__init__.py | 20 + .../aio/_azure_communication_email_service.py | 90 ++++ .../email/_generated/aio/_configuration.py | 59 +++ .../email/_generated/aio/_patch.py | 23 + .../_generated/aio/operations/__init__.py | 18 + .../aio/operations/_email_operations.py | 284 +++++++++++ .../email/_generated/aio/operations/_patch.py | 69 +++ .../email/_generated/models/__init__.py | 51 ++ ...azure_communication_email_service_enums.py | 63 +++ .../email/_generated/models/_models.py | 411 ++++++++++++++++ .../email/_generated/models/_models_py3.py | 452 ++++++++++++++++++ .../email/_generated/models/_patch.py | 47 ++ .../email/_generated/operations/__init__.py | 18 + .../operations/_email_operations.py | 361 ++++++++++++++ .../email/_generated/operations/_patch.py | 69 +++ .../communication/email/_generated/py.typed | 1 + .../communication/email/_shared/__init__.py | 5 + .../communication/email}/_shared/policy.py | 0 .../communication/email/_shared/utils.py | 37 ++ .../azure/communication/email/_version.py | 11 + .../azure/communication/email/aio/__init__.py | 5 + .../email/aio/_email_client_async.py | 82 ++++ .../azure/communication/email/py.typed | 0 .../dev_requirement.txt | 7 + .../samples/check_message_status_sample.py | 72 +++ .../check_message_status_sample_async.py | 82 ++++ ...end_email_to_multiple_recipients_sample.py | 72 +++ ...ail_to_multiple_recipients_sample_async.py | 82 ++++ .../send_email_to_single_recipient_sample.py | 67 +++ ..._email_to_single_recipient_sample_async.py | 77 +++ .../send_email_with_attachments_sample.py | 75 +++ ...end_email_with_attachments_sample_async.py | 85 ++++ .../azure-communication-email/setup.py | 71 +++ .../swagger/SWAGGER.md | 42 ++ .../tests/_shared/testcase.py | 102 ++++ ...ail_client_e2e.test_send_email_single.yaml | 53 ++ .../tests/test_email_client.py | 69 +++ .../tests/test_email_client_e2e.py | 36 ++ .../tests/unittest_helpers.py | 20 + 53 files changed, 3579 insertions(+) create mode 100644 sdk/communication/azure-communication-email/CHANGELOG.md create mode 100644 sdk/communication/azure-communication-email/LICENSE create mode 100644 sdk/communication/azure-communication-email/MANIFEST.in create mode 100644 sdk/communication/azure-communication-email/README.md create mode 100644 sdk/communication/azure-communication-email/azure/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_email_client.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py rename sdk/communication/{azure-communication-sms/azure/communication/sms => azure-communication-email/azure/communication/email}/_shared/policy.py (100%) create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_version.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/py.typed create mode 100644 sdk/communication/azure-communication-email/dev_requirement.txt create mode 100644 sdk/communication/azure-communication-email/samples/check_message_status_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py create mode 100644 sdk/communication/azure-communication-email/setup.py create mode 100644 sdk/communication/azure-communication-email/swagger/SWAGGER.md create mode 100644 sdk/communication/azure-communication-email/tests/_shared/testcase.py create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml create mode 100644 sdk/communication/azure-communication-email/tests/test_email_client.py create mode 100644 sdk/communication/azure-communication-email/tests/test_email_client_e2e.py create mode 100644 sdk/communication/azure-communication-email/tests/unittest_helpers.py diff --git a/sdk/communication/azure-communication-email/CHANGELOG.md b/sdk/communication/azure-communication-email/CHANGELOG.md new file mode 100644 index 000000000000..1cf845b24478 --- /dev/null +++ b/sdk/communication/azure-communication-email/CHANGELOG.md @@ -0,0 +1,9 @@ +# Release History + +## 1.0.0b1 (TODO: UPDATE WITH RELEASE DATE) + +The first preview of the Azure Communication Email Client has the following features: + +- send emails to multiple recipients with attachments +- get the status of a sent message + diff --git a/sdk/communication/azure-communication-email/LICENSE b/sdk/communication/azure-communication-email/LICENSE new file mode 100644 index 000000000000..63447fd8bbbf --- /dev/null +++ b/sdk/communication/azure-communication-email/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) Microsoft Corporation. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/MANIFEST.in b/sdk/communication/azure-communication-email/MANIFEST.in new file mode 100644 index 000000000000..4f582a7c8d7b --- /dev/null +++ b/sdk/communication/azure-communication-email/MANIFEST.in @@ -0,0 +1,7 @@ +include *.md +include azure/__init__.py +include azure/communication/__init__.py +include LICENSE +recursive-include tests *.py +recursive-include samples *.py *.md +include azure/communication/email/py.typed \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md new file mode 100644 index 000000000000..38f1d4c0c53d --- /dev/null +++ b/sdk/communication/azure-communication-email/README.md @@ -0,0 +1 @@ +# TODO: Populate this README \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/__init__.py b/sdk/communication/azure-communication-email/azure/__init__.py new file mode 100644 index 000000000000..69e3be50dac4 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/sdk/communication/azure-communication-email/azure/communication/__init__.py b/sdk/communication/azure-communication-email/azure/communication/__init__.py new file mode 100644 index 000000000000..69e3be50dac4 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py new file mode 100644 index 000000000000..f7befc593db1 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py @@ -0,0 +1,30 @@ +from ._email_client import EmailClient + +from ._generated.models import ( + EmailMessage, + EmailCustomHeader, + EmailContent, + EmailImportance, + EmailRecipients, + EmailAddress, + EmailAttachment, + EmailAttachmentType, + SendEmailResult, + SendStatus, + SendStatusResult +) + +__all__ = [ + 'EmailClient', + 'EmailMessage', + 'EmailCustomHeader', + 'EmailContent', + 'EmailImportance', + 'EmailRecipients', + 'EmailAddress', + 'EmailAttachment', + 'EmailAttachmentType', + 'SendEmailResult', + 'SendStatus', + 'SendStatusResult', +] \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py new file mode 100644 index 000000000000..c87a337f562b --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -0,0 +1,72 @@ +from uuid import uuid4 +from azure.core.tracing.decorator import distributed_trace +from ._shared.utils import parse_connection_str, get_current_utc_time +from ._shared.policy import HMACCredentialsPolicy +from ._generated._azure_communication_email_service import AzureCommunicationEmailService +from ._version import SDK_MONIKER +from ._generated.models import SendEmailResult, SendStatusResult, EmailMessage + +class EmailClient(object): + """A client to interact with the AzureCommunicationService Email gateway. + + This client provides operations to send an email and monitor its status. + + :param str conn_string: + The connection string to connect to an Azure Communication Service resource. + Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + """ + def __init__( + self, + conn_str, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + endpoint, access_key = parse_connection_str(conn_str) + authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + + self._generated_client = AzureCommunicationEmailService( + endpoint, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs + ) + + @distributed_trace + def send( + self, + email_message, # type: EmailMessage + **kwargs # type: Any + ): # type: (...) -> SendEmailResult + """Queues an email message to be sent to one or more recipients. + + :param email_message: The message payload for sending an email. + :type email_message: ~azure.communication.email.models.EmailMessage + :return: SendEmailResult + :rtype: ~azure.communication.email.models.SendEmailResult + """ + + return self._generated_client.email.send( + repeatability_request_id=uuid4(), + repeatability_first_sent=get_current_utc_time(), + email_message=email_message, + **kwargs + ) + + @distributed_trace + def get_send_status( + self, + message_id, #type: str + **kwargs # type: Any + ): # type: (...) -> SendStatusResult + """Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send email + :type message_id: str + :return: SendStatusResult + :rtype: ~azure.communication.email.models.SendStatusResult + """ + + return self._generated_client.email.get_send_status( + message_id=message_id, + **kwargs + ) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py new file mode 100644 index 000000000000..a5e340739e13 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py @@ -0,0 +1,23 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._azure_communication_email_service import AzureCommunicationEmailService +from ._version import VERSION + +__version__ = VERSION + +try: + from ._patch import __all__ as _patch_all + from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +except ImportError: + _patch_all = [] +from ._patch import patch_sdk as _patch_sdk +__all__ = ['AzureCommunicationEmailService'] +__all__.extend([p for p in _patch_all if p not in __all__]) + +_patch_sdk() diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py new file mode 100644 index 000000000000..3cfacefc146f --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py @@ -0,0 +1,100 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from copy import deepcopy +from typing import TYPE_CHECKING + +from msrest import Deserializer, Serializer + +from azure.core import PipelineClient + +from . import models +from ._configuration import AzureCommunicationEmailServiceConfiguration +from .operations import EmailOperations + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + from azure.core.rest import HttpRequest, HttpResponse + +class AzureCommunicationEmailService(object): # pylint: disable=client-accepts-api-version-keyword + """Azure Communication Email Service. + + :ivar email: EmailOperations operations + :vartype email: azure.communication.email.operations.EmailOperations + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + _endpoint = '{endpoint}' + self._config = AzureCommunicationEmailServiceConfiguration(endpoint=endpoint, **kwargs) + self._client = PipelineClient(base_url=_endpoint, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + self._serialize.client_side_validation = False + self.email = EmailOperations( + self._client, self._config, self._serialize, self._deserialize + ) + + + def _send_request( + self, + request, # type: HttpRequest + **kwargs # type: Any + ): + # type: (...) -> HttpResponse + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = client._send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/python/protocol/quickstart + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.HttpResponse + """ + + request_copy = deepcopy(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + + request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) + return self._client.send_request(request_copy, **kwargs) + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> AzureCommunicationEmailService + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py new file mode 100644 index 000000000000..a3fb353d8a75 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py @@ -0,0 +1,65 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +from ._version import VERSION + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + +class AzureCommunicationEmailServiceConfiguration(Configuration): # pylint: disable=too-many-instance-attributes + """Configuration for AzureCommunicationEmailService. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + super(AzureCommunicationEmailServiceConfiguration, self).__init__(**kwargs) + api_version = kwargs.pop('api_version', "2021-10-01-preview") # type: str + + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + + self.endpoint = endpoint + self.api_version = api_version + kwargs.setdefault('sdk_moniker', 'azurecommunicationemailservice/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs # type: Any + ): + # type: (...) -> None + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py new file mode 100644 index 000000000000..8a35ddb87c7e --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py @@ -0,0 +1,23 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import List + +__all__ = [] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py new file mode 100644 index 000000000000..138f663c53a4 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py @@ -0,0 +1,27 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from azure.core.pipeline.transport import HttpRequest + +def _convert_request(request, files=None): + data = request.content if not files else None + request = HttpRequest(method=request.method, url=request.url, headers=request.headers, data=data) + if files: + request.set_formdata_body(files) + return request + +def _format_url_section(template, **kwargs): + components = template.split("/") + while components: + try: + return template.format(**kwargs) + except KeyError as key: + formatted_components = template.split("/") + components = [ + c for c in formatted_components if "{}".format(key.args[0]) not in c + ] + template = "/".join(components) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py new file mode 100644 index 000000000000..41f0bacc9706 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py @@ -0,0 +1,11 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +VERSION = "1.0.0b1" + +SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py new file mode 100644 index 000000000000..3926c45d3176 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py @@ -0,0 +1,20 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._azure_communication_email_service import AzureCommunicationEmailService + +try: + from ._patch import __all__ as _patch_all + from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +except ImportError: + _patch_all = [] +from ._patch import patch_sdk as _patch_sdk +__all__ = ['AzureCommunicationEmailService'] +__all__.extend([p for p in _patch_all if p not in __all__]) + +_patch_sdk() diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py new file mode 100644 index 000000000000..f505db9f0d17 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py @@ -0,0 +1,90 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from copy import deepcopy +from typing import Any, Awaitable + +from msrest import Deserializer, Serializer + +from azure.core import AsyncPipelineClient +from azure.core.rest import AsyncHttpResponse, HttpRequest + +from .. import models +from ._configuration import AzureCommunicationEmailServiceConfiguration +from .operations import EmailOperations + +class AzureCommunicationEmailService: # pylint: disable=client-accepts-api-version-keyword + """Azure Communication Email Service. + + :ivar email: EmailOperations operations + :vartype email: azure.communication.email.aio.operations.EmailOperations + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + _endpoint = '{endpoint}' + self._config = AzureCommunicationEmailServiceConfiguration(endpoint=endpoint, **kwargs) + self._client = AsyncPipelineClient(base_url=_endpoint, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + self._serialize.client_side_validation = False + self.email = EmailOperations( + self._client, self._config, self._serialize, self._deserialize + ) + + + def _send_request( + self, + request: HttpRequest, + **kwargs: Any + ) -> Awaitable[AsyncHttpResponse]: + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = await client._send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/python/protocol/quickstart + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.AsyncHttpResponse + """ + + request_copy = deepcopy(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + + request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) + return self._client.send_request(request_copy, **kwargs) + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "AzureCommunicationEmailService": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py new file mode 100644 index 000000000000..2c74b995d496 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py @@ -0,0 +1,59 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +from .._version import VERSION + + +class AzureCommunicationEmailServiceConfiguration(Configuration): # pylint: disable=too-many-instance-attributes + """Configuration for AzureCommunicationEmailService. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + super(AzureCommunicationEmailServiceConfiguration, self).__init__(**kwargs) + api_version = kwargs.pop('api_version', "2021-10-01-preview") # type: str + + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + + self.endpoint = endpoint + self.api_version = api_version + kwargs.setdefault('sdk_moniker', 'azurecommunicationemailservice/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs: Any + ) -> None: + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py new file mode 100644 index 000000000000..8a35ddb87c7e --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py @@ -0,0 +1,23 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import List + +__all__ = [] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py new file mode 100644 index 000000000000..98c27c3620bc --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._email_operations import EmailOperations + +from ._patch import __all__ as _patch_all +from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +from ._patch import patch_sdk as _patch_sdk +__all__ = [ + 'EmailOperations', +] +__all__.extend([p for p in _patch_all if p not in __all__]) +_patch_sdk() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py new file mode 100644 index 000000000000..8b538c791667 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py @@ -0,0 +1,284 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, Callable, Dict, IO, Optional, TypeVar, Union, overload + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.core.utils import case_insensitive_dict + +from ... import models as _models +from ..._vendor import _convert_request +from ...operations._email_operations import build_get_send_status_request, build_send_request +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class EmailOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.communication.email.aio.AzureCommunicationEmailService`'s + :attr:`email` attribute. + """ + + models = _models + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + + @distributed_trace_async + async def get_send_status( + self, + message_id: str, + **kwargs: Any + ) -> _models.SendStatusResult: + """Gets the status of a message sent previously. + + Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send + email. Required. + :type message_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SendStatusResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendStatusResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + cls = kwargs.pop('cls', None) # type: ClsType[_models.SendStatusResult] + + + request = build_get_send_status_request( + message_id=message_id, + api_version=api_version, + template_url=self.get_send_status.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + + deserialized = self._deserialize('SendStatusResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + + get_send_status.metadata = {'url': "/emails/{messageId}/status"} # type: ignore + + + @overload + async def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: _models.EmailMessage, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> None: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: ~azure.communication.email.models.EmailMessage + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: IO, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> None: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + + @distributed_trace_async + async def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: Union[_models.EmailMessage, IO], + **kwargs: Any + ) -> None: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Is either a model type or a IO + type. Required. + :type email_message: ~azure.communication.email.models.EmailMessage or IO + :keyword content_type: Body Parameter content-type. Known values are: 'application/json'. + Default value is None. + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + content_type = kwargs.pop('content_type', _headers.pop('Content-Type', None)) # type: Optional[str] + cls = kwargs.pop('cls', None) # type: ClsType[None] + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(email_message, (IO, bytes)): + _content = email_message + else: + _json = self._serialize.body(email_message, 'EmailMessage') + + request = build_send_request( + repeatability_request_id=repeatability_request_id, + repeatability_first_sent=repeatability_first_sent, + api_version=api_version, + content_type=content_type, + json=_json, + content=_content, + template_url=self.send.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Repeatability-Result']=self._deserialize('str', response.headers.get('Repeatability-Result')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + response_headers['x-ms-request-id']=self._deserialize('str', response.headers.get('x-ms-request-id')) + + + if cls: + return cls(pipeline_response, None, response_headers) + + send.metadata = {'url': "/emails:send"} # type: ignore + diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py new file mode 100644 index 000000000000..6eecde32b570 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py @@ -0,0 +1,69 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import Any, IO, Union +from ._email_operations import EmailOperations as EmailOperationsGenerated +from ... import models as _models + +class EmailOperations(EmailOperationsGenerated): + + def __return_message_id(self, pipeline_response, _, response_headers): + return response_headers['x-ms-request-id'] + + async def send( + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: Union[_models.EmailMessage, IO], + **kwargs: Any + ) -> _models.SendEmailResult: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: SendEmailResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendEmailResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + message_id = await super().send( + repeatability_request_id, + repeatability_first_sent, + email_message, + **dict(kwargs, cls=self.__return_message_id) + ) + + return _models.SendEmailResult(message_id=message_id) + + send.metadata = {'url': "/emails:send"} # type: ignore + +__all__ = ["EmailOperations"] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py new file mode 100644 index 000000000000..0f7f73e35d03 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py @@ -0,0 +1,51 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import CommunicationError + from ._models_py3 import CommunicationErrorResponse + from ._models_py3 import EmailAddress + from ._models_py3 import EmailAttachment + from ._models_py3 import EmailContent + from ._models_py3 import EmailCustomHeader + from ._models_py3 import EmailMessage + from ._models_py3 import EmailRecipients + from ._models_py3 import SendStatusResult +except (SyntaxError, ImportError): + from ._models import CommunicationError # type: ignore + from ._models import CommunicationErrorResponse # type: ignore + from ._models import EmailAddress # type: ignore + from ._models import EmailAttachment # type: ignore + from ._models import EmailContent # type: ignore + from ._models import EmailCustomHeader # type: ignore + from ._models import EmailMessage # type: ignore + from ._models import EmailRecipients # type: ignore + from ._models import SendStatusResult # type: ignore + +from ._azure_communication_email_service_enums import EmailAttachmentType +from ._azure_communication_email_service_enums import EmailImportance +from ._azure_communication_email_service_enums import SendStatus +from ._patch import __all__ as _patch_all +from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +from ._patch import patch_sdk as _patch_sdk +__all__ = [ + 'CommunicationError', + 'CommunicationErrorResponse', + 'EmailAddress', + 'EmailAttachment', + 'EmailContent', + 'EmailCustomHeader', + 'EmailMessage', + 'EmailRecipients', + 'SendStatusResult', + 'EmailAttachmentType', + 'EmailImportance', + 'SendStatus', +] +__all__.extend([p for p in _patch_all if p not in __all__]) +_patch_sdk() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py new file mode 100644 index 000000000000..8743cbf94a15 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py @@ -0,0 +1,63 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum +from azure.core import CaseInsensitiveEnumMeta + + +class EmailAttachmentType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The type of attachment file. + """ + + AVI = "avi" + BMP = "bmp" + DOC = "doc" + DOCM = "docm" + DOCX = "docx" + GIF = "gif" + JPEG = "jpeg" + MP3 = "mp3" + ONE = "one" + PDF = "pdf" + PNG = "png" + PPSM = "ppsm" + PPSX = "ppsx" + PPT = "ppt" + PPTM = "pptm" + PPTX = "pptx" + PUB = "pub" + RPMSG = "rpmsg" + RTF = "rtf" + TIF = "tif" + TXT = "txt" + VSD = "vsd" + WAV = "wav" + WMA = "wma" + XLS = "xls" + XLSB = "xlsb" + XLSM = "xlsm" + XLSX = "xlsx" + +class EmailImportance(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The importance type for the email. + """ + + HIGH = "high" + NORMAL = "normal" + LOW = "low" + +class SendStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The type indicating the status of a request. + """ + + #: The message has passed basic validations and has been queued to be processed further. + QUEUED = "queued" + #: The message has been processed and is now out for delivery. + OUT_FOR_DELIVERY = "outForDelivery" + #: The message could not be processed and was dropped. + DROPPED = "dropped" diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py new file mode 100644 index 000000000000..6b0e0134ca31 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py @@ -0,0 +1,411 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +import msrest.serialization + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar code: The error code. Required. + :vartype code: str + :ivar message: The error message. Required. + :vartype message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.email.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + "code": {"key": "code", "type": "str"}, + "message": {"key": "message", "type": "str"}, + "target": {"key": "target", "type": "str"}, + "details": {"key": "details", "type": "[CommunicationError]"}, + "inner_error": {"key": "innererror", "type": "CommunicationError"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword code: The error code. Required. + :paramtype code: str + :keyword message: The error message. Required. + :paramtype message: str + """ + super(CommunicationError, self).__init__(**kwargs) + self.code = kwargs['code'] + self.message = kwargs['message'] + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :ivar error: The Communication Services error. Required. + :vartype error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + "error": {"key": "error", "type": "CommunicationError"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword error: The Communication Services error. Required. + :paramtype error: ~azure.communication.email.models.CommunicationError + """ + super(CommunicationErrorResponse, self).__init__(**kwargs) + self.error = kwargs['error'] + + +class EmailAddress(msrest.serialization.Model): + """An object representing the email address and its display name. + + All required parameters must be populated in order to send to Azure. + + :ivar email: Email address. Required. + :vartype email: str + :ivar display_name: Email display name. + :vartype display_name: str + """ + + _validation = { + 'email': {'required': True}, + } + + _attribute_map = { + "email": {"key": "email", "type": "str"}, + "display_name": {"key": "displayName", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword email: Email address. Required. + :paramtype email: str + :keyword display_name: Email display name. + :paramtype display_name: str + """ + super(EmailAddress, self).__init__(**kwargs) + self.email = kwargs['email'] + self.display_name = kwargs.get('display_name', None) + + +class EmailAttachment(msrest.serialization.Model): + """Attachment to the email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Name of the attachment. Required. + :vartype name: str + :ivar attachment_type: The type of attachment file. Required. Known values are: "avi", "bmp", + "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :vartype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :ivar content_bytes_base64: Base64 encoded contents of the attachment. Required. + :vartype content_bytes_base64: str + """ + + _validation = { + 'name': {'required': True}, + 'attachment_type': {'required': True}, + 'content_bytes_base64': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "attachment_type": {"key": "attachmentType", "type": "str"}, + "content_bytes_base64": {"key": "contentBytesBase64", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword name: Name of the attachment. Required. + :paramtype name: str + :keyword attachment_type: The type of attachment file. Required. Known values are: "avi", + "bmp", "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :paramtype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :keyword content_bytes_base64: Base64 encoded contents of the attachment. Required. + :paramtype content_bytes_base64: str + """ + super(EmailAttachment, self).__init__(**kwargs) + self.name = kwargs['name'] + self.attachment_type = kwargs['attachment_type'] + self.content_bytes_base64 = kwargs['content_bytes_base64'] + + +class EmailContent(msrest.serialization.Model): + """Content of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar subject: Subject of the email message. Required. + :vartype subject: str + :ivar plain_text: Plain text version of the email message. + :vartype plain_text: str + :ivar html: Html version of the email message. + :vartype html: str + """ + + _validation = { + 'subject': {'required': True}, + } + + _attribute_map = { + "subject": {"key": "subject", "type": "str"}, + "plain_text": {"key": "plainText", "type": "str"}, + "html": {"key": "html", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword subject: Subject of the email message. Required. + :paramtype subject: str + :keyword plain_text: Plain text version of the email message. + :paramtype plain_text: str + :keyword html: Html version of the email message. + :paramtype html: str + """ + super(EmailContent, self).__init__(**kwargs) + self.subject = kwargs['subject'] + self.plain_text = kwargs.get('plain_text', None) + self.html = kwargs.get('html', None) + + +class EmailCustomHeader(msrest.serialization.Model): + """Custom header for email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Header name. Required. + :vartype name: str + :ivar value: Header value. Required. + :vartype value: str + """ + + _validation = { + 'name': {'required': True}, + 'value': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "value": {"key": "value", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword name: Header name. Required. + :paramtype name: str + :keyword value: Header value. Required. + :paramtype value: str + """ + super(EmailCustomHeader, self).__init__(**kwargs) + self.name = kwargs['name'] + self.value = kwargs['value'] + + +class EmailMessage(msrest.serialization.Model): + """Message payload for sending an email. + + All required parameters must be populated in order to send to Azure. + + :ivar custom_headers: Custom email headers to be passed. + :vartype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :ivar sender: Sender email address from a verified domain. Required. + :vartype sender: str + :ivar content: Email content to be sent. Required. + :vartype content: ~azure.communication.email.models.EmailContent + :ivar importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :vartype importance: str or ~azure.communication.email.models.EmailImportance + :ivar recipients: Recipients for the email. Required. + :vartype recipients: ~azure.communication.email.models.EmailRecipients + :ivar attachments: list of attachments. + :vartype attachments: list[~azure.communication.email.models.EmailAttachment] + :ivar reply_to: Email addresses where recipients' replies will be sent to. + :vartype reply_to: list[~azure.communication.email.models.EmailAddress] + :ivar disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :vartype disable_user_engagement_tracking: bool + """ + + _validation = { + 'sender': {'required': True}, + 'content': {'required': True}, + 'recipients': {'required': True}, + } + + _attribute_map = { + "custom_headers": {"key": "headers", "type": "[EmailCustomHeader]"}, + "sender": {"key": "sender", "type": "str"}, + "content": {"key": "content", "type": "EmailContent"}, + "importance": {"key": "importance", "type": "str"}, + "recipients": {"key": "recipients", "type": "EmailRecipients"}, + "attachments": {"key": "attachments", "type": "[EmailAttachment]"}, + "reply_to": {"key": "replyTo", "type": "[EmailAddress]"}, + "disable_user_engagement_tracking": {"key": "disableUserEngagementTracking", "type": "bool"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword custom_headers: Custom email headers to be passed. + :paramtype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :keyword sender: Sender email address from a verified domain. Required. + :paramtype sender: str + :keyword content: Email content to be sent. Required. + :paramtype content: ~azure.communication.email.models.EmailContent + :keyword importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :paramtype importance: str or ~azure.communication.email.models.EmailImportance + :keyword recipients: Recipients for the email. Required. + :paramtype recipients: ~azure.communication.email.models.EmailRecipients + :keyword attachments: list of attachments. + :paramtype attachments: list[~azure.communication.email.models.EmailAttachment] + :keyword reply_to: Email addresses where recipients' replies will be sent to. + :paramtype reply_to: list[~azure.communication.email.models.EmailAddress] + :keyword disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :paramtype disable_user_engagement_tracking: bool + """ + super(EmailMessage, self).__init__(**kwargs) + self.custom_headers = kwargs.get('custom_headers', None) + self.sender = kwargs['sender'] + self.content = kwargs['content'] + self.importance = kwargs.get('importance', "normal") + self.recipients = kwargs['recipients'] + self.attachments = kwargs.get('attachments', None) + self.reply_to = kwargs.get('reply_to', None) + self.disable_user_engagement_tracking = kwargs.get('disable_user_engagement_tracking', None) + + +class EmailRecipients(msrest.serialization.Model): + """Recipients of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar to: Email To recipients. Required. + :vartype to: list[~azure.communication.email.models.EmailAddress] + :ivar cc: Email CC recipients. + :vartype cc: list[~azure.communication.email.models.EmailAddress] + :ivar bcc: Email BCC recipients. + :vartype bcc: list[~azure.communication.email.models.EmailAddress] + """ + + _validation = { + 'to': {'required': True}, + } + + _attribute_map = { + "to": {"key": "to", "type": "[EmailAddress]"}, + "cc": {"key": "CC", "type": "[EmailAddress]"}, + "bcc": {"key": "bCC", "type": "[EmailAddress]"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword to: Email To recipients. Required. + :paramtype to: list[~azure.communication.email.models.EmailAddress] + :keyword cc: Email CC recipients. + :paramtype cc: list[~azure.communication.email.models.EmailAddress] + :keyword bcc: Email BCC recipients. + :paramtype bcc: list[~azure.communication.email.models.EmailAddress] + """ + super(EmailRecipients, self).__init__(**kwargs) + self.to = kwargs['to'] + self.cc = kwargs.get('cc', None) + self.bcc = kwargs.get('bcc', None) + + +class SendStatusResult(msrest.serialization.Model): + """Status of an email message that was sent previously. + + All required parameters must be populated in order to send to Azure. + + :ivar message_id: System generated id of an email message sent. Required. + :vartype message_id: str + :ivar status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :vartype status: str or ~azure.communication.email.models.SendStatus + """ + + _validation = { + 'message_id': {'required': True}, + 'status': {'required': True}, + } + + _attribute_map = { + "message_id": {"key": "messageId", "type": "str"}, + "status": {"key": "status", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword message_id: System generated id of an email message sent. Required. + :paramtype message_id: str + :keyword status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :paramtype status: str or ~azure.communication.email.models.SendStatus + """ + super(SendStatusResult, self).__init__(**kwargs) + self.message_id = kwargs['message_id'] + self.status = kwargs['status'] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py new file mode 100644 index 000000000000..0e85199772e6 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py @@ -0,0 +1,452 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import List, Optional, TYPE_CHECKING, Union + +import msrest.serialization + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from .. import models as _models + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar code: The error code. Required. + :vartype code: str + :ivar message: The error message. Required. + :vartype message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.email.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + "code": {"key": "code", "type": "str"}, + "message": {"key": "message", "type": "str"}, + "target": {"key": "target", "type": "str"}, + "details": {"key": "details", "type": "[CommunicationError]"}, + "inner_error": {"key": "innererror", "type": "CommunicationError"}, + } + + def __init__( + self, + *, + code: str, + message: str, + **kwargs + ): + """ + :keyword code: The error code. Required. + :paramtype code: str + :keyword message: The error message. Required. + :paramtype message: str + """ + super().__init__(**kwargs) + self.code = code + self.message = message + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :ivar error: The Communication Services error. Required. + :vartype error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + "error": {"key": "error", "type": "CommunicationError"}, + } + + def __init__( + self, + *, + error: "_models.CommunicationError", + **kwargs + ): + """ + :keyword error: The Communication Services error. Required. + :paramtype error: ~azure.communication.email.models.CommunicationError + """ + super().__init__(**kwargs) + self.error = error + + +class EmailAddress(msrest.serialization.Model): + """An object representing the email address and its display name. + + All required parameters must be populated in order to send to Azure. + + :ivar email: Email address. Required. + :vartype email: str + :ivar display_name: Email display name. + :vartype display_name: str + """ + + _validation = { + 'email': {'required': True}, + } + + _attribute_map = { + "email": {"key": "email", "type": "str"}, + "display_name": {"key": "displayName", "type": "str"}, + } + + def __init__( + self, + *, + email: str, + display_name: Optional[str] = None, + **kwargs + ): + """ + :keyword email: Email address. Required. + :paramtype email: str + :keyword display_name: Email display name. + :paramtype display_name: str + """ + super().__init__(**kwargs) + self.email = email + self.display_name = display_name + + +class EmailAttachment(msrest.serialization.Model): + """Attachment to the email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Name of the attachment. Required. + :vartype name: str + :ivar attachment_type: The type of attachment file. Required. Known values are: "avi", "bmp", + "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :vartype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :ivar content_bytes_base64: Base64 encoded contents of the attachment. Required. + :vartype content_bytes_base64: str + """ + + _validation = { + 'name': {'required': True}, + 'attachment_type': {'required': True}, + 'content_bytes_base64': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "attachment_type": {"key": "attachmentType", "type": "str"}, + "content_bytes_base64": {"key": "contentBytesBase64", "type": "str"}, + } + + def __init__( + self, + *, + name: str, + attachment_type: Union[str, "_models.EmailAttachmentType"], + content_bytes_base64: str, + **kwargs + ): + """ + :keyword name: Name of the attachment. Required. + :paramtype name: str + :keyword attachment_type: The type of attachment file. Required. Known values are: "avi", + "bmp", "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :paramtype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :keyword content_bytes_base64: Base64 encoded contents of the attachment. Required. + :paramtype content_bytes_base64: str + """ + super().__init__(**kwargs) + self.name = name + self.attachment_type = attachment_type + self.content_bytes_base64 = content_bytes_base64 + + +class EmailContent(msrest.serialization.Model): + """Content of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar subject: Subject of the email message. Required. + :vartype subject: str + :ivar plain_text: Plain text version of the email message. + :vartype plain_text: str + :ivar html: Html version of the email message. + :vartype html: str + """ + + _validation = { + 'subject': {'required': True}, + } + + _attribute_map = { + "subject": {"key": "subject", "type": "str"}, + "plain_text": {"key": "plainText", "type": "str"}, + "html": {"key": "html", "type": "str"}, + } + + def __init__( + self, + *, + subject: str, + plain_text: Optional[str] = None, + html: Optional[str] = None, + **kwargs + ): + """ + :keyword subject: Subject of the email message. Required. + :paramtype subject: str + :keyword plain_text: Plain text version of the email message. + :paramtype plain_text: str + :keyword html: Html version of the email message. + :paramtype html: str + """ + super().__init__(**kwargs) + self.subject = subject + self.plain_text = plain_text + self.html = html + + +class EmailCustomHeader(msrest.serialization.Model): + """Custom header for email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Header name. Required. + :vartype name: str + :ivar value: Header value. Required. + :vartype value: str + """ + + _validation = { + 'name': {'required': True}, + 'value': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "value": {"key": "value", "type": "str"}, + } + + def __init__( + self, + *, + name: str, + value: str, + **kwargs + ): + """ + :keyword name: Header name. Required. + :paramtype name: str + :keyword value: Header value. Required. + :paramtype value: str + """ + super().__init__(**kwargs) + self.name = name + self.value = value + + +class EmailMessage(msrest.serialization.Model): + """Message payload for sending an email. + + All required parameters must be populated in order to send to Azure. + + :ivar custom_headers: Custom email headers to be passed. + :vartype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :ivar sender: Sender email address from a verified domain. Required. + :vartype sender: str + :ivar content: Email content to be sent. Required. + :vartype content: ~azure.communication.email.models.EmailContent + :ivar importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :vartype importance: str or ~azure.communication.email.models.EmailImportance + :ivar recipients: Recipients for the email. Required. + :vartype recipients: ~azure.communication.email.models.EmailRecipients + :ivar attachments: list of attachments. + :vartype attachments: list[~azure.communication.email.models.EmailAttachment] + :ivar reply_to: Email addresses where recipients' replies will be sent to. + :vartype reply_to: list[~azure.communication.email.models.EmailAddress] + :ivar disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :vartype disable_user_engagement_tracking: bool + """ + + _validation = { + 'sender': {'required': True}, + 'content': {'required': True}, + 'recipients': {'required': True}, + } + + _attribute_map = { + "custom_headers": {"key": "headers", "type": "[EmailCustomHeader]"}, + "sender": {"key": "sender", "type": "str"}, + "content": {"key": "content", "type": "EmailContent"}, + "importance": {"key": "importance", "type": "str"}, + "recipients": {"key": "recipients", "type": "EmailRecipients"}, + "attachments": {"key": "attachments", "type": "[EmailAttachment]"}, + "reply_to": {"key": "replyTo", "type": "[EmailAddress]"}, + "disable_user_engagement_tracking": {"key": "disableUserEngagementTracking", "type": "bool"}, + } + + def __init__( + self, + *, + sender: str, + content: "_models.EmailContent", + recipients: "_models.EmailRecipients", + custom_headers: Optional[List["_models.EmailCustomHeader"]] = None, + importance: Union[str, "_models.EmailImportance"] = "normal", + attachments: Optional[List["_models.EmailAttachment"]] = None, + reply_to: Optional[List["_models.EmailAddress"]] = None, + disable_user_engagement_tracking: Optional[bool] = None, + **kwargs + ): + """ + :keyword custom_headers: Custom email headers to be passed. + :paramtype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :keyword sender: Sender email address from a verified domain. Required. + :paramtype sender: str + :keyword content: Email content to be sent. Required. + :paramtype content: ~azure.communication.email.models.EmailContent + :keyword importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :paramtype importance: str or ~azure.communication.email.models.EmailImportance + :keyword recipients: Recipients for the email. Required. + :paramtype recipients: ~azure.communication.email.models.EmailRecipients + :keyword attachments: list of attachments. + :paramtype attachments: list[~azure.communication.email.models.EmailAttachment] + :keyword reply_to: Email addresses where recipients' replies will be sent to. + :paramtype reply_to: list[~azure.communication.email.models.EmailAddress] + :keyword disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :paramtype disable_user_engagement_tracking: bool + """ + super().__init__(**kwargs) + self.custom_headers = custom_headers + self.sender = sender + self.content = content + self.importance = importance + self.recipients = recipients + self.attachments = attachments + self.reply_to = reply_to + self.disable_user_engagement_tracking = disable_user_engagement_tracking + + +class EmailRecipients(msrest.serialization.Model): + """Recipients of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar to: Email To recipients. Required. + :vartype to: list[~azure.communication.email.models.EmailAddress] + :ivar cc: Email CC recipients. + :vartype cc: list[~azure.communication.email.models.EmailAddress] + :ivar bcc: Email BCC recipients. + :vartype bcc: list[~azure.communication.email.models.EmailAddress] + """ + + _validation = { + 'to': {'required': True}, + } + + _attribute_map = { + "to": {"key": "to", "type": "[EmailAddress]"}, + "cc": {"key": "CC", "type": "[EmailAddress]"}, + "bcc": {"key": "bCC", "type": "[EmailAddress]"}, + } + + def __init__( + self, + *, + to: List["_models.EmailAddress"], + cc: Optional[List["_models.EmailAddress"]] = None, + bcc: Optional[List["_models.EmailAddress"]] = None, + **kwargs + ): + """ + :keyword to: Email To recipients. Required. + :paramtype to: list[~azure.communication.email.models.EmailAddress] + :keyword cc: Email CC recipients. + :paramtype cc: list[~azure.communication.email.models.EmailAddress] + :keyword bcc: Email BCC recipients. + :paramtype bcc: list[~azure.communication.email.models.EmailAddress] + """ + super().__init__(**kwargs) + self.to = to + self.cc = cc + self.bcc = bcc + + +class SendStatusResult(msrest.serialization.Model): + """Status of an email message that was sent previously. + + All required parameters must be populated in order to send to Azure. + + :ivar message_id: System generated id of an email message sent. Required. + :vartype message_id: str + :ivar status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :vartype status: str or ~azure.communication.email.models.SendStatus + """ + + _validation = { + 'message_id': {'required': True}, + 'status': {'required': True}, + } + + _attribute_map = { + "message_id": {"key": "messageId", "type": "str"}, + "status": {"key": "status", "type": "str"}, + } + + def __init__( + self, + *, + message_id: str, + status: Union[str, "_models.SendStatus"], + **kwargs + ): + """ + :keyword message_id: System generated id of an email message sent. Required. + :paramtype message_id: str + :keyword status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :paramtype status: str or ~azure.communication.email.models.SendStatus + """ + super().__init__(**kwargs) + self.message_id = message_id + self.status = status diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py new file mode 100644 index 000000000000..c19a69940543 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py @@ -0,0 +1,47 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +import msrest.serialization + +class SendEmailResult(msrest.serialization.Model): + """Results of a sent email. + + All required parameters must be populated in order to send to Azure. + + :ivar message_id: System generated id of an email message sent. Required. + :vartype message_id: str + """ + + _validation = { + 'message_id': {'required': True}, + } + + _attribute_map = { + "message_id": {"key": "messageId", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword message_id: System generated id of an email message sent. Required. + :paramtype message_id: str + """ + super(SendEmailResult, self).__init__(**kwargs) + self.message_id = kwargs['message_id'] + +__all__ = ["SendEmailResult"] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py new file mode 100644 index 000000000000..98c27c3620bc --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._email_operations import EmailOperations + +from ._patch import __all__ as _patch_all +from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +from ._patch import patch_sdk as _patch_sdk +__all__ = [ + 'EmailOperations', +] +__all__.extend([p for p in _patch_all if p not in __all__]) +_patch_sdk() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py new file mode 100644 index 000000000000..90294c12462f --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py @@ -0,0 +1,361 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import IO, Optional, TYPE_CHECKING, Union, overload + +from msrest import Serializer + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpResponse +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator import distributed_trace +from azure.core.utils import case_insensitive_dict + +from .. import models as _models +from .._vendor import _convert_request, _format_url_section + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Optional, TypeVar + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +_SERIALIZER = Serializer() +_SERIALIZER.client_side_validation = False +# fmt: off + +def build_get_send_status_request( + message_id, # type: str + **kwargs # type: Any +): + # type: (...) -> HttpRequest + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', "2021-10-01-preview")) # type: str + accept = _headers.pop('Accept', "application/json") + + # Construct URL + _url = kwargs.pop("template_url", "/emails/{messageId}/status") + path_format_arguments = { + "messageId": _SERIALIZER.url("message_id", message_id, 'str'), + } + + _url = _format_url_section(_url, **path_format_arguments) + + # Construct parameters + _params['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + _headers['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs + ) + + +def build_send_request( + **kwargs # type: Any +): + # type: (...) -> HttpRequest + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', "2021-10-01-preview")) # type: str + content_type = kwargs.pop('content_type', _headers.pop('Content-Type', None)) # type: Optional[str] + repeatability_request_id = kwargs.pop('repeatability_request_id') # type: str + repeatability_first_sent = kwargs.pop('repeatability_first_sent') # type: str + accept = _headers.pop('Accept', "application/json") + + # Construct URL + _url = kwargs.pop("template_url", "/emails:send") + + # Construct parameters + _params['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + _headers['repeatability-request-id'] = _SERIALIZER.header("repeatability_request_id", repeatability_request_id, 'str') + _headers['repeatability-first-sent'] = _SERIALIZER.header("repeatability_first_sent", repeatability_first_sent, 'str') + if content_type is not None: + _headers['Content-Type'] = _SERIALIZER.header("content_type", content_type, 'str') + _headers['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="POST", + url=_url, + params=_params, + headers=_headers, + **kwargs + ) + +# fmt: on +class EmailOperations(object): + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.communication.email.AzureCommunicationEmailService`'s + :attr:`email` attribute. + """ + + models = _models + + def __init__(self, *args, **kwargs): + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + + @distributed_trace + def get_send_status( + self, + message_id, # type: str + **kwargs # type: Any + ): + # type: (...) -> _models.SendStatusResult + """Gets the status of a message sent previously. + + Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send + email. Required. + :type message_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SendStatusResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendStatusResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + cls = kwargs.pop('cls', None) # type: ClsType[_models.SendStatusResult] + + + request = build_get_send_status_request( + message_id=message_id, + api_version=api_version, + template_url=self.get_send_status.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + + deserialized = self._deserialize('SendStatusResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + + get_send_status.metadata = {'url': "/emails/{messageId}/status"} # type: ignore + + + @overload + def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: _models.EmailMessage + **kwargs # type: Any + ): + # type: (...) -> None + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: ~azure.communication.email.models.EmailMessage + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: IO + **kwargs # type: Any + ): + # type: (...) -> None + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + + @distributed_trace + def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: Union[_models.EmailMessage, IO] + **kwargs # type: Any + ): + # type: (...) -> None + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Is either a model type or a IO + type. Required. + :type email_message: ~azure.communication.email.models.EmailMessage or IO + :keyword content_type: Body Parameter content-type. Known values are: 'application/json'. + Default value is None. + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + content_type = kwargs.pop('content_type', _headers.pop('Content-Type', None)) # type: Optional[str] + cls = kwargs.pop('cls', None) # type: ClsType[None] + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(email_message, (IO, bytes)): + _content = email_message + else: + _json = self._serialize.body(email_message, 'EmailMessage') + + request = build_send_request( + repeatability_request_id=repeatability_request_id, + repeatability_first_sent=repeatability_first_sent, + api_version=api_version, + content_type=content_type, + json=_json, + content=_content, + template_url=self.send.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Repeatability-Result']=self._deserialize('str', response.headers.get('Repeatability-Result')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + response_headers['x-ms-request-id']=self._deserialize('str', response.headers.get('x-ms-request-id')) + + if cls: + return cls(pipeline_response, None, response_headers) + + send.metadata = {'url': "/emails:send"} # type: ignore + diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py new file mode 100644 index 000000000000..69156a77b9a3 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py @@ -0,0 +1,69 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import Any, IO, Union +from ._email_operations import EmailOperations as EmailOperationsGenerated +from ..models import _models, SendEmailResult + +class EmailOperations(EmailOperationsGenerated): + + def __return_message_id(self, pipeline_response, _, response_headers): + return response_headers['x-ms-request-id'] + + def send( + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: Union[_models.EmailMessage, IO] + **kwargs # type: Any + ): + # type: (...) -> SendEmailResult + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: SendEmailResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendEmailResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + message_id = super().send( + repeatability_request_id, + repeatability_first_sent, + email_message, + **dict(kwargs, cls=self.__return_message_id) + ) + return SendEmailResult(message_id=message_id) + + send.metadata = {'url': "/emails:send"} # type: ignore + +__all__ = ["EmailOperations"] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed b/sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed new file mode 100644 index 000000000000..e5aff4f83af8 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py new file mode 100644 index 000000000000..5b396cd202e8 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- diff --git a/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py similarity index 100% rename from sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py rename to sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py new file mode 100644 index 000000000000..ab028c385334 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py @@ -0,0 +1,37 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) +from datetime import datetime + +def get_current_utc_time(): + # type: () -> str + return str(datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S ")) + "GMT" + +def parse_connection_str(conn_str): + # type: (str) -> Tuple[str, str, str, str] + if conn_str is None: + raise ValueError( + "Connection string is undefined." + ) + endpoint = None + shared_access_key = None + for element in conn_str.split(";"): + key, _, value = element.partition("=") + if key.lower() == "endpoint": + endpoint = value.rstrip("/") + elif key.lower() == "accesskey": + shared_access_key = value + if not all([endpoint, shared_access_key]): + raise ValueError( + "Invalid connection string. You can get the connection string from your resource page in the Azure Portal. " + "The format should be as follows: endpoint=https:///;accesskey=" + ) + + return str(endpoint), str(shared_access_key) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_version.py b/sdk/communication/azure-communication-email/azure/communication/email/_version.py new file mode 100644 index 000000000000..41f0bacc9706 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_version.py @@ -0,0 +1,11 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +VERSION = "1.0.0b1" + +SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py new file mode 100644 index 000000000000..aa02483033ff --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py @@ -0,0 +1,5 @@ +from ._email_client_async import EmailClient + +__all__ = [ + 'EmailClient', +] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py new file mode 100644 index 000000000000..d89b789dedf1 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -0,0 +1,82 @@ +from uuid import uuid4 +from azure.core.tracing.decorator_async import distributed_trace_async +from .._shared.utils import parse_connection_str, get_current_utc_time +from .._shared.policy import HMACCredentialsPolicy +from .._generated.aio._azure_communication_email_service import AzureCommunicationEmailService +from .._version import SDK_MONIKER +from .._generated.models import SendEmailResult, SendStatusResult, EmailMessage + +class EmailClient(object): + """A client to interact with the AzureCommunicationService Email gateway asynchronously. + + This client provides operations to send an email and monitor its status. + + :param str conn_string: + The connection string to connect to an Azure Communication Service resource. + Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + """ + def __init__( + self, + conn_str, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + endpoint, access_key = parse_connection_str(conn_str) + authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + + self._generated_client = AzureCommunicationEmailService( + endpoint, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs + ) + + @distributed_trace_async + async def send( + self, + email_message, # type: EmailMessage + **kwargs # type: Any + ): # type: (...) -> SendEmailResult + """Queues an email message to be sent to one or more recipients. + + :param email_message: The message payload for sending an email. + :type email_message: ~azure.communication.email.models.EmailMessage + :return: SendEmailResult + :rtype: ~azure.communication.email.models.SendEmailResult + """ + + return await self._generated_client.email.send( + repeatability_request_id=uuid4(), + repeatability_first_sent=get_current_utc_time(), + email_message=email_message, + **kwargs + ) + + @distributed_trace_async + async def get_send_status( + self, + message_id, #type: str + **kwargs # type: Any + ): # type: (...) -> SendStatusResult + """Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send email + :type message_id: str + :return: SendStatusResult + :rtype: ~azure.communication.email.models.SendStatusResult + """ + + return await self._generated_client.email.get_send_status( + message_id=message_id, + **kwargs + ) + + async def __aenter__(self) -> "EmailClient": + await self._generated_client.__aenter__() + return self + + async def __aexit__(self, *args) -> None: + await self._generated_client.__aexit__(*args) + + async def close(self) -> None: + await self._generated_client.close() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/py.typed b/sdk/communication/azure-communication-email/azure/communication/email/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirement.txt new file mode 100644 index 000000000000..b8884941f2bd --- /dev/null +++ b/sdk/communication/azure-communication-email/dev_requirement.txt @@ -0,0 +1,7 @@ +-e ../../../tools/azure-sdk-tools +-e ../../../tools/azure-devtools +-e ../../identity/azure-identity +../../core/azure-core +aiohttp>=3.0 +aiounittest>=1.4 +pytest==7.1.2 \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py new file mode 100644 index 000000000000..4d161eea14ab --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py @@ -0,0 +1,72 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: check_message_status.py +DESCRIPTION: + This sample demonstrates checking the status of a sent email. The Email client is + authenticated using a connection string. +USAGE: + python check_message_status.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailCheckMessageStatusSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def check_message_status(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + # sending the email message + response = email_client.send(message) + + # using the message id to get the status of the email + message_id = response.message_id + message_status = email_client.get_send_status(message_id) + + print("Message Status: " + message_status.status) + +if __name__ == '__main__': + sample = EmailCheckMessageStatusSample() + sample.check_message_status() diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py new file mode 100644 index 000000000000..d294ab6d52e5 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py @@ -0,0 +1,82 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: check_message_status_async.py +DESCRIPTION: + This sample demonstrates checking the status of a sent email. The Email client is + authenticated using a connection string. +USAGE: + python check_message_status_async.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailCheckMessageStatusSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + async def check_message_status_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + + # using the message id to get the status of the email + message_id = response.message_id + message_status = await email_client.get_send_status(message_id) + + print("Message Status: " + message_status.status) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailCheckMessageStatusSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.check_message_status_async()) \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py new file mode 100644 index 000000000000..4c709356b27e --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py @@ -0,0 +1,72 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_multiple_recipient_sample.py +DESCRIPTION: + This sample demonstrates sending an email to multiple recipients. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailMultipleRecipientSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + second_recipient_address = os.getenv("SECOND_RECIPIENT_ADDRESS") + + def send_email_to_multiple_recipients(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + +if __name__ == '__main__': + sample = EmailMultipleRecipientSample() + sample.send_email_to_multiple_recipients() diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py new file mode 100644 index 000000000000..e77525bd3736 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -0,0 +1,82 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_multiple_recipient_sample_async.py +DESCRIPTION: + This sample demonstrates sending an email to multiple recipients. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailMultipleRecipientSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + second_recipient_address = os.getenv("SECOND_RECIPIENT_ADDRESS") + + async def send_email_to_multiple_recipients_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + print("Message ID: " + response.message_id) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailMultipleRecipientSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.send_email_to_multiple_recipients_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py new file mode 100644 index 000000000000..d7c58d33cd74 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py @@ -0,0 +1,67 @@ +# 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. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_single_recipient_sample.py +DESCRIPTION: + This sample demonstrates sending an email to a single recipient. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailSingleRecipientSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def send_email_to_single_recipient(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + +if __name__ == '__main__': + sample = EmailSingleRecipientSample() + sample.send_email_to_single_recipient() diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py new file mode 100644 index 000000000000..be15a68610f5 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py @@ -0,0 +1,77 @@ +# 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. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_single_recipient_sample_async.py +DESCRIPTION: + This sample demonstrates sending an email to a single recipient. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample_async.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailSingleRecipientSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + async def send_email_to_single_recipient_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + print("Message ID: " + response.message_id) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailSingleRecipientSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.send_email_to_single_recipient_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py new file mode 100644 index 000000000000..7dc5c180866c --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -0,0 +1,75 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_with_attachments_sample.py +DESCRIPTION: + This sample demonstrates sending an email with an attachment. The Email client is + authenticated using a connection string. +USAGE: + python send_email_with_attachment.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment, + EmailMessage +) + +sys.path.append("..") + +class EmailWithAttachmentSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def send_email_with_attachment(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients, + attachments=[attachment] + ) + + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + +if __name__ == '__main__': + sample = EmailWithAttachmentSample() + sample.send_email_with_attachment() diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py new file mode 100644 index 000000000000..ad6e14209064 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -0,0 +1,85 @@ +# 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. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_with_attachments_sample_async.py +DESCRIPTION: + This sample demonstrates sending an email with an attachment. The Email client is + authenticated using a connection string. +USAGE: + python send_email_with_attachment_async.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment, + EmailMessage +) + +sys.path.append("..") + +class EmailWithAttachmentSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + async def send_email_with_attachment_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients, + attachments=[attachment] + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + print("Message ID: " + response.message_id) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailWithAttachmentSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.send_email_with_attachment_async()) diff --git a/sdk/communication/azure-communication-email/setup.py b/sdk/communication/azure-communication-email/setup.py new file mode 100644 index 000000000000..b08b8ab01133 --- /dev/null +++ b/sdk/communication/azure-communication-email/setup.py @@ -0,0 +1,71 @@ +from setuptools import setup, find_packages +import os +from io import open +import re + +# example setup.py Feel free to copy the entire "azure-template" folder into a package folder named +# with "azure-". Ensure that the below arguments to setup() are updated to reflect +# your package. + +# this setup.py is set up in a specific way to keep the azure* and azure-mgmt-* namespaces WORKING all the way +# up from python 3.6. Reference here: https://github.com/Azure/azure-sdk-for-python/wiki/Azure-packaging + +PACKAGE_NAME = "azure-communication-email" +PACKAGE_PPRINT_NAME = "Communication Email" + +# a-b-c => a/b/c +package_folder_path = PACKAGE_NAME.replace('-', '/') +# a-b-c => a.b.c +namespace_name = PACKAGE_NAME.replace('-', '.') + +# Version extraction inspired from 'requests' +with open(os.path.join(package_folder_path, '_version.py'), 'r') as fd: + version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', + fd.read(), re.MULTILINE).group(1) +if not version: + raise RuntimeError('Cannot find version information') + +with open('README.md', encoding='utf-8') as f: + long_description = f.read() + +setup( + name=PACKAGE_NAME, + version=version, + description='Microsoft Azure {} Client Library for Python'.format(PACKAGE_PPRINT_NAME), + long_description=long_description, + long_description_content_type='text/markdown', + license='MIT License', + author='Microsoft Corporation', + author_email='azpysdkhelp@microsoft.com', + url='https://github.com/Azure/azure-sdk-for-python', + classifiers=[ + "Development Status :: 5 - Production/Stable", + 'Programming Language :: Python', + "Programming Language :: Python :: 3 :: Only", + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'License :: OSI Approved :: MIT License', + ], + zip_safe=False, + packages=find_packages(exclude=[ + 'tests', + # Exclude packages that will be covered by PEP420 or nspkg + 'azure', + 'azure.communication' + ]), + python_requires=">=3.6", + include_package_data=True, + package_data={ + 'pytyped': ['py.typed'], + }, + install_requires=[ + 'azure-core<2.0.0,>=1.15.0', + 'msrest>=0.6.21', + 'six>=1.11.0', + ], + extras_require={ + ":python_version<'3.8'": ["typing-extensions"] + } +) diff --git a/sdk/communication/azure-communication-email/swagger/SWAGGER.md b/sdk/communication/azure-communication-email/swagger/SWAGGER.md new file mode 100644 index 000000000000..28efe4a31e3c --- /dev/null +++ b/sdk/communication/azure-communication-email/swagger/SWAGGER.md @@ -0,0 +1,42 @@ +# Azure Communication Services Email REST API Client + +> see https://aka.ms/autorest + +### Setup +```ps +npm install -g autorest +``` + +### Generation +```ps +cd +autorest SWAGGER.md +``` + +### Settings +``` yaml +package-version: 1.0.0b1 +tag: package-2021-10-01-preview +require: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/communication/data-plane/Email/readme.md +output-folder: ../azure/communication/email/_generated +namespace: azure.communication.email +no-namespace-folders: true +license-header: MICROSOFT_MIT_NO_VERSION +enable-xml: true +clear-output-folder: true +python: true +v3: true +no-async: false +add-credential: false +security: Anonymous +title: Azure Communication Email Service +``` + +### Change the bCC property to bcc +```yaml +directive: + - from: swagger-document + where: $.definitions.EmailRecipients.properties.bCC + transform: > + $["x-ms-client-name"] = "bcc" +``` diff --git a/sdk/communication/azure-communication-email/tests/_shared/testcase.py b/sdk/communication/azure-communication-email/tests/_shared/testcase.py new file mode 100644 index 000000000000..cf7fb7d2e14d --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/_shared/testcase.py @@ -0,0 +1,102 @@ + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import os +import re +from devtools_testutils import AzureTestCase +from azure.communication.email._shared.utils import parse_connection_str +from azure_devtools.scenario_tests import RecordingProcessor, ReplayableTest +from azure_devtools.scenario_tests.utilities import is_text_payload + +class ResponseReplacerProcessor(RecordingProcessor): + def __init__(self, keys=None, replacement="sanitized"): + self._keys = keys if keys else [] + self._replacement = replacement + + def process_response(self, response): + def sanitize_dict(dictionary): + for key in dictionary: + value = dictionary[key] + if isinstance(value, str): + dictionary[key] = re.sub( + r"("+'|'.join(self._keys)+r")", + self._replacement, + dictionary[key]) + elif isinstance(value, dict): + sanitize_dict(value) + + sanitize_dict(response) + + return response + +class BodyReplacerProcessor(RecordingProcessor): + """Sanitize the sensitive info inside request or response bodies""" + + def __init__(self, keys=None, replacement="sanitized"): + self._replacement = replacement + self._keys = keys if keys else [] + + def process_request(self, request): + if is_text_payload(request) and request.body: + request.body = self._replace_keys(request.body.decode()).encode() + + return request + + def process_response(self, response): + if is_text_payload(response) and response['body']['string']: + response['body']['string'] = self._replace_keys(response['body']['string']) + + return response + + def _replace_keys(self, body): + def _replace_recursively(obj): + if isinstance(obj, dict): + for key in obj: + if key in self._keys: + obj[key] = self._replacement + else: + _replace_recursively(obj[key]) + elif isinstance(obj, list): + for i in obj: + _replace_recursively(i) + + import json + try: + body = json.loads(body) + _replace_recursively(body) + + except (KeyError, ValueError): + return body + + return json.dumps(body) + +class CommunicationTestCase(AzureTestCase): + # FILTER_HEADERS = ReplayableTest.FILTER_HEADERS + [ + # 'x-azure-ref', + # 'x-ms-content-sha256', + # 'location', + # # 'x-ms-date', + # # 'repeatability-first-sent', + # # 'repeatability-request-id', + # # 'operation-location', + # # 'date' + # ] + + def __init__(self, method_name, *args, **kwargs): + super(CommunicationTestCase, self).__init__(method_name, *args, **kwargs) + + def setUp(self): + super(CommunicationTestCase, self).setUp() + + # if self.is_playback(): + # self.connection_str = "endpoint=https://sanitized.communication.azure.com/;accesskey=fake===" + # else: + # self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') + # endpoint, _ = parse_connection_str(self.connection_str) + # self._resource_name = endpoint.split(".")[0] + # self.scrubber.register_name_pair(self._resource_name, "sanitized") + + self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml new file mode 100644 index 000000000000..8e9ff2be0c4d --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml @@ -0,0 +1,53 @@ +interactions: +- request: + body: '{"sender": "DoNotReply@266db372-a95a-494f-88b5-81ffd9e866af.azurecomm.net", + "content": {"subject": "This is the subject", "plainText": "This is the body"}, + "importance": "normal", "recipients": {"to": [{"email": "acseaastesting@gmail.com", + "displayName": "Customer Name"}]}}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '274' + Content-Type: + - application/json + User-Agent: + - azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0) + repeatability-first-sent: + - Thu, 16 Jun 2022 23:57:39 GMT + repeatability-request-id: + - e19daf10-3c5c-4b5a-84a2-6dcd31946e9a + x-ms-content-sha256: + - 5Efo+JDWFExoHqUXqWOtGVGR8b9UFPpSZvfMSo/0XCQ= + x-ms-date: + - Thu, 16 Jun 2022 23:57:39 GMT + x-ms-return-client-request-id: + - 'true' + method: POST + uri: https://email-js-sdk-recording-comm-2.communication.azure.com/emails:send?api-version=2021-10-01-preview + response: + body: + string: '' + headers: + api-supported-versions: + - 2021-10-01-preview + content-length: + - '0' + date: + - Thu, 16 Jun 2022 23:57:40 GMT + operation-location: + - https://email-js-sdk-recording-comm-2.communication.azure.com/emails/0ba65ac2-55da-4178-85b4-1d4f44c6fa84/status + repeatability-result: + - accepted + x-azure-ref: + - 0dMOrYgAAAAAaqONyzFwZSo1BsrwLQtafV1NURURHRTA4MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx + x-cache: + - CONFIG_NOCACHE + status: + code: 202 + message: Accepted +version: 1 diff --git a/sdk/communication/azure-communication-email/tests/test_email_client.py b/sdk/communication/azure-communication-email/tests/test_email_client.py new file mode 100644 index 000000000000..de4ba84d2835 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client.py @@ -0,0 +1,69 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import unittest +from unittest.mock import Mock + +from unittest_helpers import mock_response +from azure.communication.email import ( + EmailClient, + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress +) + + +class TestEmailClient(unittest.TestCase): + def test_send(self): + + message = EmailMessage( + sender="someSender@contoso.com", + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email="someRecipient@domain.com", display_name="Customer Name")] + ) + ) + + def mock_send(*_, **__): + return mock_response(status_code=202, headers={ + 'x-ms-request-id': "testMessageId" + }) + + email_client = EmailClient( + conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", + transport = Mock(send=mock_send) + ) + + response = None + raised = False + try: + response = email_client.send(message) + except: + raised = True + raise + + self.assertFalse(raised, 'Expected is no exception raised') + self.assertIsNotNone(response.message_id) + + def test_get_send_status(self): + + def mock_get_send_status(*_, **__): + return mock_response(status_code=200, json_payload={"test": "test"}) + + email_client = EmailClient( + conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", + transport = Mock(send=mock_get_send_status) + ) + response = None + raised = False + try: + response = email_client.get_send_status("testMessageId") + except: + raised = True + raise + + self.assertFalse(raised, 'Expected is no exception raised') + self.assertIsNotNone(response) \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py new file mode 100644 index 000000000000..4f6a2fe7cd59 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -0,0 +1,36 @@ +import os +from azure.communication.email import ( + EmailClient, + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress +) +from _shared.testcase import ( + CommunicationTestCase, +) + +class EmailClientTest(CommunicationTestCase): + def __init__(self, method_name): + super(EmailClientTest, self).__init__(method_name) + + def setUp(self): + super(EmailClientTest, self).setUp() + + self.sender_address = os.getenv("SENDER_ADDRESS") + self.recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def test_send_email_single(self): + email_client = EmailClient(self.connection_str) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) + + response = email_client.send(message) + print(response) + assert response is None \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/unittest_helpers.py b/sdk/communication/azure-communication-email/tests/unittest_helpers.py new file mode 100644 index 000000000000..9d24a0aa86eb --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/unittest_helpers.py @@ -0,0 +1,20 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import json + +from unittest import mock + +def mock_response(status_code=200, headers=None, json_payload=None): + response = mock.Mock(status_code=status_code, headers=headers or {}) + if json_payload is not None: + response.text = lambda encoding=None: json.dumps(json_payload) + response.headers["content-type"] = "application/json" + response.content_type = "application/json" + else: + response.text = lambda encoding=None: "" + response.headers["content-type"] = "text/plain" + response.content_type = "text/plain" + return response From 248980f2e6344da05b796b1c60de40ab5c4e7cbc Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Thu, 23 Jun 2022 13:19:50 -0700 Subject: [PATCH 05/30] Updating sdk tests --- .../azure-communication-email/README.md | 159 +++++++++++++++++- .../dev_requirement.txt | 3 +- .../tests/_shared/testcase.py | 102 ----------- .../tests/async_preparers.py | 36 ++++ .../tests/conftest.py | 49 ++++++ .../tests/preparers.py | 17 ++ ...EmailClienttest_send_email_attachment.json | 56 ++++++ ...nttest_send_email_multiple_recipients.json | 53 ++++++ ...lienttest_send_email_single_recipient.json | 49 ++++++ ...ail_client_e2e.test_send_email_single.yaml | 53 ------ ...EmailClienttest_send_email_attachment.json | 55 ++++++ ...nttest_send_email_multiple_recipients.json | 52 ++++++ ...lienttest_send_email_single_recipient.json | 48 ++++++ .../tests/test_email_client.py | 69 -------- .../tests/test_email_client_e2e.py | 92 ++++++++-- .../tests/test_email_client_e2e_async.py | 99 +++++++++++ .../tests/unittest_helpers.py | 20 --- sdk/communication/ci.yml | 2 + 18 files changed, 750 insertions(+), 264 deletions(-) delete mode 100644 sdk/communication/azure-communication-email/tests/_shared/testcase.py create mode 100644 sdk/communication/azure-communication-email/tests/async_preparers.py create mode 100644 sdk/communication/azure-communication-email/tests/conftest.py create mode 100644 sdk/communication/azure-communication-email/tests/preparers.py create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json delete mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json delete mode 100644 sdk/communication/azure-communication-email/tests/test_email_client.py create mode 100644 sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py delete mode 100644 sdk/communication/azure-communication-email/tests/unittest_helpers.py diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index 38f1d4c0c53d..150594e21494 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -1 +1,158 @@ -# TODO: Populate this README \ No newline at end of file +# Azure Communication Email client library for Python + +This package contains a Python SDK for Azure Communication Services for Email. + +## Getting started + +### Prerequisites + +You need an [Azure subscription][azure_sub], a [Communication Service Resource][communication_resource_docs], and an [Email Communication Resource][email_resource_docs] with an active [Domain][domain_overview]. + +To create these resource, you can use the [Azure Portal][communication_resource_create_portal], the [Azure PowerShell][communication_resource_create_power_shell], or the [.NET management client library][communication_resource_create_net]. + +### Installing + +Install the Azure Communication Email client library for Python with [pip](https://pypi.org/project/pip/): + +```bash +pip install azure-communication-email +``` + +## Examples + +`EmailClient` provides the functionality to send email messages . + +## Authentication + +Email clients can be authenticated using the connection string acquired from an Azure Communication Resource in the [Azure Portal][azure_portal]. + +```python +from azure.communication.email import EmailClient + +connection_string = "endpoint=https://.communication.azure.com/;accessKey=" +client = EmailClient(connectionString); +``` + +### Send an Email Message + +To send an email message, call the `send` function from the `EmailClient`. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]) + ) + +response = client.send(message) +``` + +### Send an Email Message to Multiple Recipients + +To send an email message to multiple recipients, add a object for each recipient type and an object for each recipient. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +recipients = EmailRecipients( + to=[ + EmailAddress(email="customer@domain.com", display_name="Customer Name"), + EmailAddress(email="customer2@domain.com", display_name="Customer Name 2"), + ], + cc=[ + EmailAddress(email="ccCustomer@domain.com", display_name="CC Customer Name"), + EmailAddress(email="ccCustomer2@domain.com", display_name="CC Customer Name 2"), + ], + bcc=[ + EmailAddress(email="bccCustomer@domain.com", display_name="BCC Customer Name"), + EmailAddress(email="bccCustomer2@domain.com", display_name="BCC Customer Name 2"), + ] + ) + +message = EmailMessage(sender="sender@contoso.com", content=content, recipients=recipients) +response = client.send(message) +``` + +### Send Email with Attachments + +Azure Communication Services support sending email with attachments. + +```python +file = open("C://readme.txt", "r") +file_contents = file.read() +file.close() + +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64=base64.b64encode(file_contents) +) + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]), + attachments=[attachment] + ) + +response = client.send(message) +``` + +### Get Email Message Status + +The result from the `send` call contains a `message_id` which can be used to query the status of the email. + +```python +response = client.send(message) +status = client.get_sent_status(message_id) +``` + +## Next steps + +- [Read more about Email in Azure Communication Services][nextsteps] + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. + + + +[azure_sub]: https://azure.microsoft.com/free/dotnet/ +[azure_portal]: https://portal.azure.com +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com +[communication_resource_docs]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[email_resource_docs]: https://aka.ms/acsemail/createemailresource +[communication_resource_create_portal]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[communication_resource_create_power_shell]: https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice +[communication_resource_create_net]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-net +[package]: https://www.nuget.org/packages/Azure.Communication.Common/ +[product_docs]: https://aka.ms/acsemail/overview +[nextsteps]: https://aka.ms/acsemail/overview +[nuget]: https://www.nuget.org/ +[source]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/communication +[domain_overview]: https://aka.ms/acsemail/domainsoverview \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirement.txt index b8884941f2bd..8fd523934a46 100644 --- a/sdk/communication/azure-communication-email/dev_requirement.txt +++ b/sdk/communication/azure-communication-email/dev_requirement.txt @@ -4,4 +4,5 @@ ../../core/azure-core aiohttp>=3.0 aiounittest>=1.4 -pytest==7.1.2 \ No newline at end of file +pytest==7.1.2 +pytest-tornasync==0.6.0.post2 \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/_shared/testcase.py b/sdk/communication/azure-communication-email/tests/_shared/testcase.py deleted file mode 100644 index cf7fb7d2e14d..000000000000 --- a/sdk/communication/azure-communication-email/tests/_shared/testcase.py +++ /dev/null @@ -1,102 +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 os -import re -from devtools_testutils import AzureTestCase -from azure.communication.email._shared.utils import parse_connection_str -from azure_devtools.scenario_tests import RecordingProcessor, ReplayableTest -from azure_devtools.scenario_tests.utilities import is_text_payload - -class ResponseReplacerProcessor(RecordingProcessor): - def __init__(self, keys=None, replacement="sanitized"): - self._keys = keys if keys else [] - self._replacement = replacement - - def process_response(self, response): - def sanitize_dict(dictionary): - for key in dictionary: - value = dictionary[key] - if isinstance(value, str): - dictionary[key] = re.sub( - r"("+'|'.join(self._keys)+r")", - self._replacement, - dictionary[key]) - elif isinstance(value, dict): - sanitize_dict(value) - - sanitize_dict(response) - - return response - -class BodyReplacerProcessor(RecordingProcessor): - """Sanitize the sensitive info inside request or response bodies""" - - def __init__(self, keys=None, replacement="sanitized"): - self._replacement = replacement - self._keys = keys if keys else [] - - def process_request(self, request): - if is_text_payload(request) and request.body: - request.body = self._replace_keys(request.body.decode()).encode() - - return request - - def process_response(self, response): - if is_text_payload(response) and response['body']['string']: - response['body']['string'] = self._replace_keys(response['body']['string']) - - return response - - def _replace_keys(self, body): - def _replace_recursively(obj): - if isinstance(obj, dict): - for key in obj: - if key in self._keys: - obj[key] = self._replacement - else: - _replace_recursively(obj[key]) - elif isinstance(obj, list): - for i in obj: - _replace_recursively(i) - - import json - try: - body = json.loads(body) - _replace_recursively(body) - - except (KeyError, ValueError): - return body - - return json.dumps(body) - -class CommunicationTestCase(AzureTestCase): - # FILTER_HEADERS = ReplayableTest.FILTER_HEADERS + [ - # 'x-azure-ref', - # 'x-ms-content-sha256', - # 'location', - # # 'x-ms-date', - # # 'repeatability-first-sent', - # # 'repeatability-request-id', - # # 'operation-location', - # # 'date' - # ] - - def __init__(self, method_name, *args, **kwargs): - super(CommunicationTestCase, self).__init__(method_name, *args, **kwargs) - - def setUp(self): - super(CommunicationTestCase, self).setUp() - - # if self.is_playback(): - # self.connection_str = "endpoint=https://sanitized.communication.azure.com/;accesskey=fake===" - # else: - # self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') - # endpoint, _ = parse_connection_str(self.connection_str) - # self._resource_name = endpoint.split(".")[0] - # self.scrubber.register_name_pair(self._resource_name, "sanitized") - - self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') diff --git a/sdk/communication/azure-communication-email/tests/async_preparers.py b/sdk/communication/azure-communication-email/tests/async_preparers.py new file mode 100644 index 000000000000..aec31c005621 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/async_preparers.py @@ -0,0 +1,36 @@ +import os +from devtools_testutils import is_live + +def email_decorator_async(func, **kwargs): + async def wrapper(self, *args, **kwargs): + if is_live(): + self.communication_connection_string = os.environ["COMMUNICATION_CONNECTION_STRING"] + self.sender_address = os.environ["SENDER_ADDRESS"] + self.recipient_address = os.environ["RECIPIENT_ADDRESS"] + else: + self.communication_connection_string = "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==" + self.sender_address = "someSender@contoso.com" + self.recipient_address = "someRecipient@domain.com" + + EXPONENTIAL_BACKOFF = 1.5 + RETRY_COUNT = 0 + + try: + return await func(self, *args, **kwargs) + except HttpResponseError as exc: + if exc.status_code != 429: + raise + print("Retrying: {} {}".format(RETRY_COUNT, EXPONENTIAL_BACKOFF)) + while RETRY_COUNT < 6: + if is_live(): + time.sleep(EXPONENTIAL_BACKOFF) + try: + return await func(self, *args, **kwargs) + except HttpResponseError as exc: + print("Retrying: {} {}".format(RETRY_COUNT, EXPONENTIAL_BACKOFF)) + EXPONENTIAL_BACKOFF **= 2 + RETRY_COUNT += 1 + if exc.status_code != 429 or RETRY_COUNT >= 6: + raise + + return wrapper diff --git a/sdk/communication/azure-communication-email/tests/conftest.py b/sdk/communication/azure-communication-email/tests/conftest.py new file mode 100644 index 000000000000..c122d990f7b2 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/conftest.py @@ -0,0 +1,49 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import pytest +import os +from devtools_testutils import test_proxy, add_general_regex_sanitizer, add_header_regex_sanitizer, add_body_regex_sanitizer +from azure.communication.email._shared.utils import parse_connection_str + +@pytest.fixture(scope="session", autouse=True) +def add_sanitizers(test_proxy): + communication_connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING", "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==") + sender_address = os.getenv("SENDER_ADDRESS", "someSender@contoso.com") + recipient_address = os.getenv("RECIPIENT_ADDRESS", "someRecipient@domain.com") + + add_general_regex_sanitizer(regex=communication_connection_string, value="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==") + add_general_regex_sanitizer(regex=sender_address, value="someSender@contoso.com") + add_general_regex_sanitizer(regex=recipient_address, value="someRecipient@domain.com") + + endpoint, _ = parse_connection_str(communication_connection_string) + add_general_regex_sanitizer(regex=endpoint, value="https://someEndpoint") + + add_header_regex_sanitizer(key="repeatability-first-sent", value="sanitized") + add_header_regex_sanitizer(key="repeatability-request-id", value="sanitized") + add_header_regex_sanitizer(key="x-ms-content-sha256", value="sanitized") + add_header_regex_sanitizer(key="Operation-Location", value="https://someEndpoint/emails/someMessageId/status") + add_header_regex_sanitizer(key="Date", value="sanitized") + add_header_regex_sanitizer(key="x-azure-ref", value="sanitized") diff --git a/sdk/communication/azure-communication-email/tests/preparers.py b/sdk/communication/azure-communication-email/tests/preparers.py new file mode 100644 index 000000000000..e8bb262257bc --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/preparers.py @@ -0,0 +1,17 @@ +import os +from devtools_testutils import is_live + +def email_decorator(func, **kwargs): + def wrapper(self, *args, **kwargs): + if is_live(): + self.communication_connection_string = os.environ["COMMUNICATION_CONNECTION_STRING"] + self.sender_address = os.environ["SENDER_ADDRESS"] + self.recipient_address = os.environ["RECIPIENT_ADDRESS"] + else: + self.communication_connection_string = "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==" + self.sender_address = "someSender@contoso.com" + self.recipient_address = "someRecipient@domain.com" + + func(self, *args, **kwargs) + + return wrapper diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json new file mode 100644 index 000000000000..539de711bc8c --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json @@ -0,0 +1,56 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "355", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + }, + "attachments": [ + { + "name": "readme.txt", + "attachmentType": "txt", + "contentBytesBase64": "ZW1haWwgdGVzdCBhdHRhY2htZW50" + } + ] + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json new file mode 100644 index 000000000000..4fdfeab89f35 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -0,0 +1,53 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "308", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:47 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + }, + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name 2" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json new file mode 100644 index 000000000000..be357f4913bd --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json @@ -0,0 +1,49 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "235", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:46 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml deleted file mode 100644 index 8e9ff2be0c4d..000000000000 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml +++ /dev/null @@ -1,53 +0,0 @@ -interactions: -- request: - body: '{"sender": "DoNotReply@266db372-a95a-494f-88b5-81ffd9e866af.azurecomm.net", - "content": {"subject": "This is the subject", "plainText": "This is the body"}, - "importance": "normal", "recipients": {"to": [{"email": "acseaastesting@gmail.com", - "displayName": "Customer Name"}]}}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '274' - Content-Type: - - application/json - User-Agent: - - azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0) - repeatability-first-sent: - - Thu, 16 Jun 2022 23:57:39 GMT - repeatability-request-id: - - e19daf10-3c5c-4b5a-84a2-6dcd31946e9a - x-ms-content-sha256: - - 5Efo+JDWFExoHqUXqWOtGVGR8b9UFPpSZvfMSo/0XCQ= - x-ms-date: - - Thu, 16 Jun 2022 23:57:39 GMT - x-ms-return-client-request-id: - - 'true' - method: POST - uri: https://email-js-sdk-recording-comm-2.communication.azure.com/emails:send?api-version=2021-10-01-preview - response: - body: - string: '' - headers: - api-supported-versions: - - 2021-10-01-preview - content-length: - - '0' - date: - - Thu, 16 Jun 2022 23:57:40 GMT - operation-location: - - https://email-js-sdk-recording-comm-2.communication.azure.com/emails/0ba65ac2-55da-4178-85b4-1d4f44c6fa84/status - repeatability-result: - - accepted - x-azure-ref: - - 0dMOrYgAAAAAaqONyzFwZSo1BsrwLQtafV1NURURHRTA4MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx - x-cache: - - CONFIG_NOCACHE - status: - code: 202 - message: Accepted -version: 1 diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json new file mode 100644 index 000000000000..1077749edad0 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json @@ -0,0 +1,55 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "355", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + }, + "attachments": [ + { + "name": "readme.txt", + "attachmentType": "txt", + "contentBytesBase64": "ZW1haWwgdGVzdCBhdHRhY2htZW50" + } + ] + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json new file mode 100644 index 000000000000..9c8535417424 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -0,0 +1,52 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "308", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + }, + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name 2" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json new file mode 100644 index 000000000000..972c5dae3f3d --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json @@ -0,0 +1,48 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "235", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/test_email_client.py b/sdk/communication/azure-communication-email/tests/test_email_client.py deleted file mode 100644 index de4ba84d2835..000000000000 --- a/sdk/communication/azure-communication-email/tests/test_email_client.py +++ /dev/null @@ -1,69 +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 unittest -from unittest.mock import Mock - -from unittest_helpers import mock_response -from azure.communication.email import ( - EmailClient, - EmailMessage, - EmailContent, - EmailRecipients, - EmailAddress -) - - -class TestEmailClient(unittest.TestCase): - def test_send(self): - - message = EmailMessage( - sender="someSender@contoso.com", - content=EmailContent(subject="This is the subject", plain_text="This is the body"), - recipients=EmailRecipients( - to=[EmailAddress(email="someRecipient@domain.com", display_name="Customer Name")] - ) - ) - - def mock_send(*_, **__): - return mock_response(status_code=202, headers={ - 'x-ms-request-id': "testMessageId" - }) - - email_client = EmailClient( - conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", - transport = Mock(send=mock_send) - ) - - response = None - raised = False - try: - response = email_client.send(message) - except: - raised = True - raise - - self.assertFalse(raised, 'Expected is no exception raised') - self.assertIsNotNone(response.message_id) - - def test_get_send_status(self): - - def mock_get_send_status(*_, **__): - return mock_response(status_code=200, json_payload={"test": "test"}) - - email_client = EmailClient( - conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", - transport = Mock(send=mock_get_send_status) - ) - response = None - raised = False - try: - response = email_client.get_send_status("testMessageId") - except: - raised = True - raise - - self.assertFalse(raised, 'Expected is no exception raised') - self.assertIsNotNone(response) \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py index 4f6a2fe7cd59..a8c317edf2a8 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -1,36 +1,92 @@ -import os from azure.communication.email import ( EmailClient, EmailMessage, EmailContent, EmailRecipients, - EmailAddress -) -from _shared.testcase import ( - CommunicationTestCase, + EmailAddress, + EmailAttachment ) +from devtools_testutils import AzureRecordedTestCase, recorded_by_proxy +from preparers import email_decorator + +class TestEmailClient(AzureRecordedTestCase): + # TODO: Change the assert statements once x-ms-request-id change is merged in + @email_decorator + @recorded_by_proxy + def test_send_email_single_recipient(self): + email_client = EmailClient(self.communication_connection_string) -class EmailClientTest(CommunicationTestCase): - def __init__(self, method_name): - super(EmailClientTest, self).__init__(method_name) + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) - def setUp(self): - super(EmailClientTest, self).setUp() + response = email_client.send(message) + assert response is not None - self.sender_address = os.getenv("SENDER_ADDRESS") - self.recipient_address = os.getenv("RECIPIENT_ADDRESS") + @email_decorator + @recorded_by_proxy + def test_send_email_multiple_recipients(self): + email_client = EmailClient(self.communication_connection_string) - def test_send_email_single(self): - email_client = EmailClient(self.connection_str) - message = EmailMessage( sender=self.sender_address, content=EmailContent(subject="This is the subject", plain_text="This is the body"), recipients=EmailRecipients( - to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.recipient_address, display_name="Customer Name 2"), + ] ) ) response = email_client.send(message) - print(response) - assert response is None \ No newline at end of file + assert response is not None + + @email_decorator + @recorded_by_proxy + def test_send_email_attachment(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ), + attachments=[ + EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + ] + ) + + response = email_client.send(message) + assert response is not None + + # TODO: Comment back in once the x-ms-request-id change is merged in + # @email_decorator + # @recorded_by_proxy + # def test_check_message_status(self): + # email_client = EmailClient(self.communication_connection_string) + + # message = EmailMessage( + # sender=self.sender_address, + # content=EmailContent(subject="This is the subject", plain_text="This is the body"), + # recipients=EmailRecipients( + # to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + # ) + # ) + + # response = email_client.send(message) + # message_id = response.message_id + # if message_id is not None: + # message_status_response = email_client.get_send_status(message_id) + # assert message_status_response.status is not None + # else: + # assert False diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py new file mode 100644 index 000000000000..718a90fb6465 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py @@ -0,0 +1,99 @@ +import pytest + +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment +) +from devtools_testutils import AzureRecordedTestCase +from devtools_testutils.aio import recorded_by_proxy_async +from async_preparers import email_decorator_async + +class TestEmailClient(AzureRecordedTestCase): + # TODO: Change the assert statements once x-ms-request-id change is merged in + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_single_recipient(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) + + async with email_client: + response = await email_client.send(message) + assert response is not None + + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_multiple_recipients(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.recipient_address, display_name="Customer Name 2"), + ] + ) + ) + + async with email_client: + response = await email_client.send(message) + assert response is not None + + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_attachment(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ), + attachments=[ + EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + ] + ) + + async with email_client: + response = await email_client.send(message) + assert response is not None + + # TODO: Comment back in once the x-ms-request-id change is merged in + # @email_decorator_async + # @recorded_by_proxy_async + # async def test_check_message_status(self): + # email_client = EmailClient(self.communication_connection_string) + + # message = EmailMessage( + # sender=self.sender_address, + # content=EmailContent(subject="This is the subject", plain_text="This is the body"), + # recipients=EmailRecipients( + # to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + # ) + # ) + + # async with email_client: + # response = await email_client.send(message) + # message_id = response.message_id + # if message_id is not None: + # message_status_response = await email_client.get_send_status(message_id) + # assert message_status_response.status is not None + # else: + # assert False diff --git a/sdk/communication/azure-communication-email/tests/unittest_helpers.py b/sdk/communication/azure-communication-email/tests/unittest_helpers.py deleted file mode 100644 index 9d24a0aa86eb..000000000000 --- a/sdk/communication/azure-communication-email/tests/unittest_helpers.py +++ /dev/null @@ -1,20 +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 json - -from unittest import mock - -def mock_response(status_code=200, headers=None, json_payload=None): - response = mock.Mock(status_code=status_code, headers=headers or {}) - if json_payload is not None: - response.text = lambda encoding=None: json.dumps(json_payload) - response.headers["content-type"] = "application/json" - response.content_type = "application/json" - else: - response.text = lambda encoding=None: "" - response.headers["content-type"] = "text/plain" - response.content_type = "text/plain" - return response diff --git a/sdk/communication/ci.yml b/sdk/communication/ci.yml index 40619c505859..fee3c672cb36 100644 --- a/sdk/communication/ci.yml +++ b/sdk/communication/ci.yml @@ -35,6 +35,8 @@ extends: safeName: azurecommunicationidentity - name: azure-communication-chat safeName: azurecommunicationchat + - name: azure-communication-email + safeName: azurecommunicationemail - name: azure-mgmt-communication safeName: azuremgmtcommunication - name: azure-communication-sms From d91f79522ad029c2ed84953bd03a665eb03784fd Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Thu, 23 Jun 2022 13:19:50 -0700 Subject: [PATCH 06/30] Updating constructor and fixing linting errors --- .../azure-communication-email/README.md | 20 ++++++++- .../azure/communication/email/__init__.py | 2 +- .../communication/email/_email_client.py | 40 ++++++++++++++---- .../azure/communication/email/_version.py | 2 +- .../email/aio/_email_client_async.py | 42 +++++++++++++++---- ...v_requirement.txt => dev_requirements.txt} | 0 .../samples/check_message_status_sample.py | 2 +- .../check_message_status_sample_async.py | 4 +- ...end_email_to_multiple_recipients_sample.py | 2 +- ...ail_to_multiple_recipients_sample_async.py | 2 +- .../send_email_to_single_recipient_sample.py | 2 +- ..._email_to_single_recipient_sample_async.py | 2 +- .../send_email_with_attachments_sample.py | 2 +- ...end_email_with_attachments_sample_async.py | 2 +- .../azure-communication-email/setup.py | 2 +- ...EmailClienttest_send_email_attachment.json | 2 +- ...nttest_send_email_multiple_recipients.json | 2 +- ...lienttest_send_email_single_recipient.json | 2 +- ...EmailClienttest_send_email_attachment.json | 2 +- ...nttest_send_email_multiple_recipients.json | 2 +- ...lienttest_send_email_single_recipient.json | 2 +- .../tests/test_email_client_e2e.py | 8 ++-- .../tests/test_email_client_e2e_async.py | 8 ++-- sdk/communication/ci.yml | 1 + 24 files changed, 111 insertions(+), 44 deletions(-) rename sdk/communication/azure-communication-email/{dev_requirement.txt => dev_requirements.txt} (100%) diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index 150594e21494..bd952e31803f 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -2,6 +2,12 @@ This package contains a Python SDK for Azure Communication Services for Email. +## Key concepts + +The Azure Communication Email package is used to do following: +- Send emails to multiple types of recipients +- Query the status of a sent email message + ## Getting started ### Prerequisites @@ -30,7 +36,7 @@ Email clients can be authenticated using the connection string acquired from an from azure.communication.email import EmailClient connection_string = "endpoint=https://.communication.azure.com/;accessKey=" -client = EmailClient(connectionString); +client = EmailClient.from_connection_string(connection_string); ``` ### Send an Email Message @@ -127,6 +133,18 @@ response = client.send(message) status = client.get_sent_status(message_id) ``` +## Troubleshooting + +Email operations will throw an exception if the request to the server fails. The Email client will raise exceptions defined in [Azure Core](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/README.md). + +```Python +try: + response = email_client.send(message) +except Exception as ex: + print('Exception:') + print(ex) +``` + ## Next steps - [Read more about Email in Azure Communication Services][nextsteps] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py index f7befc593db1..9c22b889a185 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py @@ -27,4 +27,4 @@ 'SendEmailResult', 'SendStatus', 'SendStatusResult', -] \ No newline at end of file +] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py index c87a337f562b..44751fd3cd9a 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -1,3 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + from uuid import uuid4 from azure.core.tracing.decorator import distributed_trace from ._shared.utils import parse_connection_str, get_current_utc_time @@ -6,23 +12,24 @@ from ._version import SDK_MONIKER from ._generated.models import SendEmailResult, SendStatusResult, EmailMessage -class EmailClient(object): +class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword """A client to interact with the AzureCommunicationService Email gateway. This client provides operations to send an email and monitor its status. - :param str conn_string: - The connection string to connect to an Azure Communication Service resource. - Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param TokenCredential credential: + The TokenCredential we use to authenticate against the service. """ def __init__( self, - conn_str, # type: str + endpoint, # type: str + credential, # type: str **kwargs # type: Any ): # type: (...) -> None - endpoint, access_key = parse_connection_str(conn_str) - authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + authentication_policy = HMACCredentialsPolicy(endpoint, credential) self._generated_client = AzureCommunicationEmailService( endpoint, @@ -30,6 +37,23 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) + + @classmethod + def from_connection_string( + cls, + conn_str, # type: str + **kwargs # type: Any + ): # type: (...) -> EmailClient + """Create EmailClient from a Connection String. + + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of EmailClient. + :rtype: ~azure.communication.EmailClient + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) @distributed_trace def send( @@ -51,7 +75,7 @@ def send( email_message=email_message, **kwargs ) - + @distributed_trace def get_send_status( self, diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_version.py b/sdk/communication/azure-communication-email/azure/communication/email/_version.py index 41f0bacc9706..eadf444aa551 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_version.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_version.py @@ -8,4 +8,4 @@ VERSION = "1.0.0b1" -SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str \ No newline at end of file +SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py index d89b789dedf1..acc414758195 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -1,3 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + from uuid import uuid4 from azure.core.tracing.decorator_async import distributed_trace_async from .._shared.utils import parse_connection_str, get_current_utc_time @@ -6,23 +12,24 @@ from .._version import SDK_MONIKER from .._generated.models import SendEmailResult, SendStatusResult, EmailMessage -class EmailClient(object): +class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword """A client to interact with the AzureCommunicationService Email gateway asynchronously. This client provides operations to send an email and monitor its status. - :param str conn_string: - The connection string to connect to an Azure Communication Service resource. - Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param TokenCredential credential: + The TokenCredential we use to authenticate against the service. """ def __init__( self, - conn_str, # type: str + endpoint, # type: str + credential, # type: str **kwargs # type: Any ): # type: (...) -> None - endpoint, access_key = parse_connection_str(conn_str) - authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + authentication_policy = HMACCredentialsPolicy(endpoint, credential) self._generated_client = AzureCommunicationEmailService( endpoint, @@ -30,6 +37,23 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) + + @classmethod + def from_connection_string( + cls, + conn_str, # type: str + **kwargs # type: Any + ): # type: (...) -> EmailClient + """Create EmailClient from a Connection String. + + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of EmailClient. + :rtype: ~azure.communication.EmailClient + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) @distributed_trace_async async def send( @@ -51,7 +75,7 @@ async def send( email_message=email_message, **kwargs ) - + @distributed_trace_async async def get_send_status( self, @@ -79,4 +103,4 @@ async def __aexit__(self, *args) -> None: await self._generated_client.__aexit__(*args) async def close(self) -> None: - await self._generated_client.close() \ No newline at end of file + await self._generated_client.close() diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirements.txt similarity index 100% rename from sdk/communication/azure-communication-email/dev_requirement.txt rename to sdk/communication/azure-communication-email/dev_requirements.txt diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py index 4d161eea14ab..d249d646cedc 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py @@ -39,7 +39,7 @@ class EmailCheckMessageStatusSample(object): def check_message_status(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py index d294ab6d52e5..d0a6b6278e3a 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py @@ -40,7 +40,7 @@ class EmailCheckMessageStatusSampleAsync(object): async def check_message_status_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( @@ -79,4 +79,4 @@ async def check_message_status_async(self): # Comment in this line if you are running this sample on Windows # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - asyncio.run(sample.check_message_status_async()) \ No newline at end of file + asyncio.run(sample.check_message_status_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py index 4c709356b27e..2f365d626228 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py @@ -41,7 +41,7 @@ class EmailMultipleRecipientSample(object): def send_email_to_multiple_recipients(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py index e77525bd3736..0b43bd6689a3 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -42,7 +42,7 @@ class EmailMultipleRecipientSampleAsync(object): async def send_email_to_multiple_recipients_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py index d7c58d33cd74..0dc9f3112415 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py @@ -39,7 +39,7 @@ class EmailSingleRecipientSample(object): def send_email_to_single_recipient(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py index be15a68610f5..56dc2ca69966 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py @@ -40,7 +40,7 @@ class EmailSingleRecipientSampleAsync(object): async def send_email_to_single_recipient_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py index 7dc5c180866c..4994666f6b92 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -40,7 +40,7 @@ class EmailWithAttachmentSample(object): def send_email_with_attachment(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py index ad6e14209064..2654a67fd0d0 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -41,7 +41,7 @@ class EmailWithAttachmentSampleAsync(object): async def send_email_with_attachment_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/setup.py b/sdk/communication/azure-communication-email/setup.py index b08b8ab01133..d8092dd74f16 100644 --- a/sdk/communication/azure-communication-email/setup.py +++ b/sdk/communication/azure-communication-email/setup.py @@ -61,7 +61,7 @@ 'pytyped': ['py.typed'], }, install_requires=[ - 'azure-core<2.0.0,>=1.15.0', + 'azure-core<2.0.0,>=1.2.2', 'msrest>=0.6.21', 'six>=1.11.0', ], diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json index 539de711bc8c..cd2e134a56b4 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json @@ -13,7 +13,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json index 4fdfeab89f35..02d02db09d8d 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -13,7 +13,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:47 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:47 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json index be357f4913bd..359dd7f050a7 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json @@ -13,7 +13,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:46 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:47 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json index 1077749edad0..a725781e858c 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json @@ -12,7 +12,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json index 9c8535417424..3f18dae2f504 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -12,7 +12,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json index 972c5dae3f3d..37459430bf1c 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json @@ -12,7 +12,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py index a8c317edf2a8..98d136c719ca 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -14,7 +14,7 @@ class TestEmailClient(AzureRecordedTestCase): @email_decorator @recorded_by_proxy def test_send_email_single_recipient(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -30,7 +30,7 @@ def test_send_email_single_recipient(self): @email_decorator @recorded_by_proxy def test_send_email_multiple_recipients(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -49,7 +49,7 @@ def test_send_email_multiple_recipients(self): @email_decorator @recorded_by_proxy def test_send_email_attachment(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -73,7 +73,7 @@ def test_send_email_attachment(self): # @email_decorator # @recorded_by_proxy # def test_check_message_status(self): - # email_client = EmailClient(self.communication_connection_string) + # email_client = EmailClient.from_connection_string(self.communication_connection_string) # message = EmailMessage( # sender=self.sender_address, diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py index 718a90fb6465..da4dcfd90d60 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py @@ -17,7 +17,7 @@ class TestEmailClient(AzureRecordedTestCase): @email_decorator_async @recorded_by_proxy_async async def test_send_email_single_recipient(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -34,7 +34,7 @@ async def test_send_email_single_recipient(self): @email_decorator_async @recorded_by_proxy_async async def test_send_email_multiple_recipients(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -54,7 +54,7 @@ async def test_send_email_multiple_recipients(self): @email_decorator_async @recorded_by_proxy_async async def test_send_email_attachment(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -79,7 +79,7 @@ async def test_send_email_attachment(self): # @email_decorator_async # @recorded_by_proxy_async # async def test_check_message_status(self): - # email_client = EmailClient(self.communication_connection_string) + # email_client = EmailClient.from_connection_string(self.communication_connection_string) # message = EmailMessage( # sender=self.sender_address, diff --git a/sdk/communication/ci.yml b/sdk/communication/ci.yml index fee3c672cb36..59ea57e5cbe9 100644 --- a/sdk/communication/ci.yml +++ b/sdk/communication/ci.yml @@ -30,6 +30,7 @@ extends: template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml parameters: ServiceDirectory: communication + TestProxy: true Artifacts: - name: azure-communication-identity safeName: azurecommunicationidentity From d40c8fca89d9f16f24ec39715ee3c617da7522bf Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Thu, 23 Jun 2022 14:45:56 -0700 Subject: [PATCH 07/30] Removing pytest from dev_requirements.txt --- sdk/communication/azure-communication-email/dev_requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/communication/azure-communication-email/dev_requirements.txt b/sdk/communication/azure-communication-email/dev_requirements.txt index 8fd523934a46..733dcf452e64 100644 --- a/sdk/communication/azure-communication-email/dev_requirements.txt +++ b/sdk/communication/azure-communication-email/dev_requirements.txt @@ -4,5 +4,4 @@ ../../core/azure-core aiohttp>=3.0 aiounittest>=1.4 -pytest==7.1.2 pytest-tornasync==0.6.0.post2 \ No newline at end of file From 47913ed9624b6ae0aa01215a0d5ae1f97a5823b2 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Thu, 23 Jun 2022 17:01:28 -0700 Subject: [PATCH 08/30] Updating azure core dependency --- .../azure/communication/email/_email_client.py | 2 +- .../azure/communication/email/aio/_email_client_async.py | 2 +- sdk/communication/azure-communication-email/setup.py | 2 +- shared_requirements.txt | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py index 44751fd3cd9a..ae2e9ee8baec 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -37,7 +37,7 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) - + @classmethod def from_connection_string( cls, diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py index acc414758195..0ed6c8c94d5d 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -37,7 +37,7 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) - + @classmethod def from_connection_string( cls, diff --git a/sdk/communication/azure-communication-email/setup.py b/sdk/communication/azure-communication-email/setup.py index d8092dd74f16..797663b6c6fd 100644 --- a/sdk/communication/azure-communication-email/setup.py +++ b/sdk/communication/azure-communication-email/setup.py @@ -61,7 +61,7 @@ 'pytyped': ['py.typed'], }, install_requires=[ - 'azure-core<2.0.0,>=1.2.2', + 'azure-core<2.0.0,>=1.20.0', 'msrest>=0.6.21', 'six>=1.11.0', ], diff --git a/shared_requirements.txt b/shared_requirements.txt index 0d4da86646b4..e7db990dead2 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -196,6 +196,7 @@ opentelemetry-sdk<2.0.0,>=1.5.0,!=1.10a0 #override azure-communication-phonenumbers azure-core<2.0.0,>=1.15.0 #override azure-communication-identity azure-core<2.0.0,>=1.19.1 #override azure-communication-networktraversal azure-core<2.0.0,>=1.19.1 +#override azure-communication-email azure-core<2.0.0,>=1.20.0 #override azure-mgmt-communication azure-core<2.0.0,>=1.9.0 #override azure-ai-metricsadvisor azure-core<2.0.0,>=1.23.0 #override azure-ai-translation-document azure-core<2.0.0,>=1.14.0 From db859f13652dc64472b9b314f3c6812f5a9aa4f5 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Thu, 23 Jun 2022 17:09:07 -0700 Subject: [PATCH 09/30] Updating azure core version --- sdk/communication/azure-communication-email/setup.py | 2 +- shared_requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/communication/azure-communication-email/setup.py b/sdk/communication/azure-communication-email/setup.py index 797663b6c6fd..e16f16f68b8c 100644 --- a/sdk/communication/azure-communication-email/setup.py +++ b/sdk/communication/azure-communication-email/setup.py @@ -61,7 +61,7 @@ 'pytyped': ['py.typed'], }, install_requires=[ - 'azure-core<2.0.0,>=1.20.0', + 'azure-core<2.0.0,>=1.23.0', 'msrest>=0.6.21', 'six>=1.11.0', ], diff --git a/shared_requirements.txt b/shared_requirements.txt index e7db990dead2..cb94f76bf812 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -196,7 +196,7 @@ opentelemetry-sdk<2.0.0,>=1.5.0,!=1.10a0 #override azure-communication-phonenumbers azure-core<2.0.0,>=1.15.0 #override azure-communication-identity azure-core<2.0.0,>=1.19.1 #override azure-communication-networktraversal azure-core<2.0.0,>=1.19.1 -#override azure-communication-email azure-core<2.0.0,>=1.20.0 +#override azure-communication-email azure-core<2.0.0,>=1.23.0 #override azure-mgmt-communication azure-core<2.0.0,>=1.9.0 #override azure-ai-metricsadvisor azure-core<2.0.0,>=1.23.0 #override azure-ai-translation-document azure-core<2.0.0,>=1.14.0 From a0f8f5246bfe35cce976b1d2f4343b86934b94d0 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Thu, 23 Jun 2022 18:08:31 -0700 Subject: [PATCH 10/30] Adding policy file back into sms module --- .../azure/communication/sms/_shared/policy.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py diff --git a/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py b/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py new file mode 100644 index 000000000000..c38a8ed92f3e --- /dev/null +++ b/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py @@ -0,0 +1,91 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import hashlib +import urllib +import base64 +import hmac +from azure.core.pipeline.policies import SansIOHTTPPolicy +from .utils import get_current_utc_time + +class HMACCredentialsPolicy(SansIOHTTPPolicy): + """Implementation of HMAC authentication policy. + """ + + def __init__(self, + host, # type: str + access_key, # type: str + decode_url=False # type: bool + ): + # type: (...) -> None + super(HMACCredentialsPolicy, self).__init__() + + if host.startswith("https://"): + self._host = host.replace("https://", "") + + if host.startswith("http://"): + self._host = host.replace("http://", "") + + self._access_key = access_key + self._decode_url = decode_url + + def _compute_hmac(self, + value # type: str + ): + decoded_secret = base64.b64decode(self._access_key) + digest = hmac.new( + decoded_secret, value.encode("utf-8"), hashlib.sha256 + ).digest() + + return base64.b64encode(digest).decode("utf-8") + + def _sign_request(self, request): + verb = request.http_request.method.upper() + + # Get the path and query from url, which looks like https://host/path/query + query_url = str(request.http_request.url[len(self._host) + 8:]) + + if self._decode_url: + query_url = urllib.parse.unquote(query_url) + + signed_headers = "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) \ No newline at end of file From 6eb6c465ebaf7203cb8d41dd60d4c7e7eee570b6 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 24 Jun 2022 08:10:48 -0700 Subject: [PATCH 11/30] Adding newline to policy file --- .../azure/communication/sms/_shared/policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py b/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py index c38a8ed92f3e..d4197ede0e38 100644 --- a/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py +++ b/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py @@ -88,4 +88,4 @@ def _sign_request(self, request): return request def on_request(self, request): - self._sign_request(request) \ No newline at end of file + self._sign_request(request) From 1fc0256f880042be13131dc6ea08e74ccddffdb2 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 24 Jun 2022 09:23:16 -0700 Subject: [PATCH 12/30] Adding python SDK --- .../azure-communication-email/CHANGELOG.md | 9 + .../azure-communication-email/LICENSE | 21 + .../azure-communication-email/MANIFEST.in | 7 + .../azure-communication-email/README.md | 1 + .../azure/__init__.py | 1 + .../azure/communication/__init__.py | 1 + .../azure/communication/email/__init__.py | 30 ++ .../communication/email/_email_client.py | 72 +++ .../email/_generated/__init__.py | 23 + .../_azure_communication_email_service.py | 100 ++++ .../email/_generated/_configuration.py | 65 +++ .../communication/email/_generated/_patch.py | 23 + .../communication/email/_generated/_vendor.py | 27 ++ .../email/_generated/_version.py | 11 + .../email/_generated/aio/__init__.py | 20 + .../aio/_azure_communication_email_service.py | 90 ++++ .../email/_generated/aio/_configuration.py | 59 +++ .../email/_generated/aio/_patch.py | 23 + .../_generated/aio/operations/__init__.py | 18 + .../aio/operations/_email_operations.py | 284 +++++++++++ .../email/_generated/aio/operations/_patch.py | 69 +++ .../email/_generated/models/__init__.py | 51 ++ ...azure_communication_email_service_enums.py | 63 +++ .../email/_generated/models/_models.py | 411 ++++++++++++++++ .../email/_generated/models/_models_py3.py | 452 ++++++++++++++++++ .../email/_generated/models/_patch.py | 47 ++ .../email/_generated/operations/__init__.py | 18 + .../operations/_email_operations.py | 361 ++++++++++++++ .../email/_generated/operations/_patch.py | 69 +++ .../communication/email/_generated/py.typed | 1 + .../communication/email/_shared/__init__.py | 5 + .../communication/email}/_shared/policy.py | 0 .../communication/email/_shared/utils.py | 37 ++ .../azure/communication/email/_version.py | 11 + .../azure/communication/email/aio/__init__.py | 5 + .../email/aio/_email_client_async.py | 82 ++++ .../azure/communication/email/py.typed | 0 .../dev_requirement.txt | 7 + .../samples/check_message_status_sample.py | 72 +++ .../check_message_status_sample_async.py | 82 ++++ ...end_email_to_multiple_recipients_sample.py | 72 +++ ...ail_to_multiple_recipients_sample_async.py | 82 ++++ .../send_email_to_single_recipient_sample.py | 67 +++ ..._email_to_single_recipient_sample_async.py | 77 +++ .../send_email_with_attachments_sample.py | 75 +++ ...end_email_with_attachments_sample_async.py | 85 ++++ .../azure-communication-email/setup.py | 71 +++ .../swagger/SWAGGER.md | 42 ++ .../tests/_shared/testcase.py | 102 ++++ ...ail_client_e2e.test_send_email_single.yaml | 53 ++ .../tests/test_email_client.py | 69 +++ .../tests/test_email_client_e2e.py | 36 ++ .../tests/unittest_helpers.py | 20 + 53 files changed, 3579 insertions(+) create mode 100644 sdk/communication/azure-communication-email/CHANGELOG.md create mode 100644 sdk/communication/azure-communication-email/LICENSE create mode 100644 sdk/communication/azure-communication-email/MANIFEST.in create mode 100644 sdk/communication/azure-communication-email/README.md create mode 100644 sdk/communication/azure-communication-email/azure/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_email_client.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py rename sdk/communication/{azure-communication-sms/azure/communication/sms => azure-communication-email/azure/communication/email}/_shared/policy.py (100%) create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/_version.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py create mode 100644 sdk/communication/azure-communication-email/azure/communication/email/py.typed create mode 100644 sdk/communication/azure-communication-email/dev_requirement.txt create mode 100644 sdk/communication/azure-communication-email/samples/check_message_status_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py create mode 100644 sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py create mode 100644 sdk/communication/azure-communication-email/setup.py create mode 100644 sdk/communication/azure-communication-email/swagger/SWAGGER.md create mode 100644 sdk/communication/azure-communication-email/tests/_shared/testcase.py create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml create mode 100644 sdk/communication/azure-communication-email/tests/test_email_client.py create mode 100644 sdk/communication/azure-communication-email/tests/test_email_client_e2e.py create mode 100644 sdk/communication/azure-communication-email/tests/unittest_helpers.py diff --git a/sdk/communication/azure-communication-email/CHANGELOG.md b/sdk/communication/azure-communication-email/CHANGELOG.md new file mode 100644 index 000000000000..1cf845b24478 --- /dev/null +++ b/sdk/communication/azure-communication-email/CHANGELOG.md @@ -0,0 +1,9 @@ +# Release History + +## 1.0.0b1 (TODO: UPDATE WITH RELEASE DATE) + +The first preview of the Azure Communication Email Client has the following features: + +- send emails to multiple recipients with attachments +- get the status of a sent message + diff --git a/sdk/communication/azure-communication-email/LICENSE b/sdk/communication/azure-communication-email/LICENSE new file mode 100644 index 000000000000..63447fd8bbbf --- /dev/null +++ b/sdk/communication/azure-communication-email/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) Microsoft Corporation. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/MANIFEST.in b/sdk/communication/azure-communication-email/MANIFEST.in new file mode 100644 index 000000000000..4f582a7c8d7b --- /dev/null +++ b/sdk/communication/azure-communication-email/MANIFEST.in @@ -0,0 +1,7 @@ +include *.md +include azure/__init__.py +include azure/communication/__init__.py +include LICENSE +recursive-include tests *.py +recursive-include samples *.py *.md +include azure/communication/email/py.typed \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md new file mode 100644 index 000000000000..38f1d4c0c53d --- /dev/null +++ b/sdk/communication/azure-communication-email/README.md @@ -0,0 +1 @@ +# TODO: Populate this README \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/__init__.py b/sdk/communication/azure-communication-email/azure/__init__.py new file mode 100644 index 000000000000..69e3be50dac4 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/sdk/communication/azure-communication-email/azure/communication/__init__.py b/sdk/communication/azure-communication-email/azure/communication/__init__.py new file mode 100644 index 000000000000..69e3be50dac4 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py new file mode 100644 index 000000000000..f7befc593db1 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py @@ -0,0 +1,30 @@ +from ._email_client import EmailClient + +from ._generated.models import ( + EmailMessage, + EmailCustomHeader, + EmailContent, + EmailImportance, + EmailRecipients, + EmailAddress, + EmailAttachment, + EmailAttachmentType, + SendEmailResult, + SendStatus, + SendStatusResult +) + +__all__ = [ + 'EmailClient', + 'EmailMessage', + 'EmailCustomHeader', + 'EmailContent', + 'EmailImportance', + 'EmailRecipients', + 'EmailAddress', + 'EmailAttachment', + 'EmailAttachmentType', + 'SendEmailResult', + 'SendStatus', + 'SendStatusResult', +] \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py new file mode 100644 index 000000000000..c87a337f562b --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -0,0 +1,72 @@ +from uuid import uuid4 +from azure.core.tracing.decorator import distributed_trace +from ._shared.utils import parse_connection_str, get_current_utc_time +from ._shared.policy import HMACCredentialsPolicy +from ._generated._azure_communication_email_service import AzureCommunicationEmailService +from ._version import SDK_MONIKER +from ._generated.models import SendEmailResult, SendStatusResult, EmailMessage + +class EmailClient(object): + """A client to interact with the AzureCommunicationService Email gateway. + + This client provides operations to send an email and monitor its status. + + :param str conn_string: + The connection string to connect to an Azure Communication Service resource. + Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + """ + def __init__( + self, + conn_str, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + endpoint, access_key = parse_connection_str(conn_str) + authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + + self._generated_client = AzureCommunicationEmailService( + endpoint, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs + ) + + @distributed_trace + def send( + self, + email_message, # type: EmailMessage + **kwargs # type: Any + ): # type: (...) -> SendEmailResult + """Queues an email message to be sent to one or more recipients. + + :param email_message: The message payload for sending an email. + :type email_message: ~azure.communication.email.models.EmailMessage + :return: SendEmailResult + :rtype: ~azure.communication.email.models.SendEmailResult + """ + + return self._generated_client.email.send( + repeatability_request_id=uuid4(), + repeatability_first_sent=get_current_utc_time(), + email_message=email_message, + **kwargs + ) + + @distributed_trace + def get_send_status( + self, + message_id, #type: str + **kwargs # type: Any + ): # type: (...) -> SendStatusResult + """Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send email + :type message_id: str + :return: SendStatusResult + :rtype: ~azure.communication.email.models.SendStatusResult + """ + + return self._generated_client.email.get_send_status( + message_id=message_id, + **kwargs + ) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py new file mode 100644 index 000000000000..a5e340739e13 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/__init__.py @@ -0,0 +1,23 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._azure_communication_email_service import AzureCommunicationEmailService +from ._version import VERSION + +__version__ = VERSION + +try: + from ._patch import __all__ as _patch_all + from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +except ImportError: + _patch_all = [] +from ._patch import patch_sdk as _patch_sdk +__all__ = ['AzureCommunicationEmailService'] +__all__.extend([p for p in _patch_all if p not in __all__]) + +_patch_sdk() diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py new file mode 100644 index 000000000000..3cfacefc146f --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_azure_communication_email_service.py @@ -0,0 +1,100 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from copy import deepcopy +from typing import TYPE_CHECKING + +from msrest import Deserializer, Serializer + +from azure.core import PipelineClient + +from . import models +from ._configuration import AzureCommunicationEmailServiceConfiguration +from .operations import EmailOperations + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + from azure.core.rest import HttpRequest, HttpResponse + +class AzureCommunicationEmailService(object): # pylint: disable=client-accepts-api-version-keyword + """Azure Communication Email Service. + + :ivar email: EmailOperations operations + :vartype email: azure.communication.email.operations.EmailOperations + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + _endpoint = '{endpoint}' + self._config = AzureCommunicationEmailServiceConfiguration(endpoint=endpoint, **kwargs) + self._client = PipelineClient(base_url=_endpoint, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + self._serialize.client_side_validation = False + self.email = EmailOperations( + self._client, self._config, self._serialize, self._deserialize + ) + + + def _send_request( + self, + request, # type: HttpRequest + **kwargs # type: Any + ): + # type: (...) -> HttpResponse + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = client._send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/python/protocol/quickstart + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.HttpResponse + """ + + request_copy = deepcopy(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + + request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) + return self._client.send_request(request_copy, **kwargs) + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> AzureCommunicationEmailService + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py new file mode 100644 index 000000000000..a3fb353d8a75 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_configuration.py @@ -0,0 +1,65 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +from ._version import VERSION + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + +class AzureCommunicationEmailServiceConfiguration(Configuration): # pylint: disable=too-many-instance-attributes + """Configuration for AzureCommunicationEmailService. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + super(AzureCommunicationEmailServiceConfiguration, self).__init__(**kwargs) + api_version = kwargs.pop('api_version', "2021-10-01-preview") # type: str + + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + + self.endpoint = endpoint + self.api_version = api_version + kwargs.setdefault('sdk_moniker', 'azurecommunicationemailservice/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs # type: Any + ): + # type: (...) -> None + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py new file mode 100644 index 000000000000..8a35ddb87c7e --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_patch.py @@ -0,0 +1,23 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import List + +__all__ = [] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py new file mode 100644 index 000000000000..138f663c53a4 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_vendor.py @@ -0,0 +1,27 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from azure.core.pipeline.transport import HttpRequest + +def _convert_request(request, files=None): + data = request.content if not files else None + request = HttpRequest(method=request.method, url=request.url, headers=request.headers, data=data) + if files: + request.set_formdata_body(files) + return request + +def _format_url_section(template, **kwargs): + components = template.split("/") + while components: + try: + return template.format(**kwargs) + except KeyError as key: + formatted_components = template.split("/") + components = [ + c for c in formatted_components if "{}".format(key.args[0]) not in c + ] + template = "/".join(components) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py new file mode 100644 index 000000000000..41f0bacc9706 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/_version.py @@ -0,0 +1,11 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +VERSION = "1.0.0b1" + +SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py new file mode 100644 index 000000000000..3926c45d3176 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/__init__.py @@ -0,0 +1,20 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._azure_communication_email_service import AzureCommunicationEmailService + +try: + from ._patch import __all__ as _patch_all + from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +except ImportError: + _patch_all = [] +from ._patch import patch_sdk as _patch_sdk +__all__ = ['AzureCommunicationEmailService'] +__all__.extend([p for p in _patch_all if p not in __all__]) + +_patch_sdk() diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py new file mode 100644 index 000000000000..f505db9f0d17 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_azure_communication_email_service.py @@ -0,0 +1,90 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from copy import deepcopy +from typing import Any, Awaitable + +from msrest import Deserializer, Serializer + +from azure.core import AsyncPipelineClient +from azure.core.rest import AsyncHttpResponse, HttpRequest + +from .. import models +from ._configuration import AzureCommunicationEmailServiceConfiguration +from .operations import EmailOperations + +class AzureCommunicationEmailService: # pylint: disable=client-accepts-api-version-keyword + """Azure Communication Email Service. + + :ivar email: EmailOperations operations + :vartype email: azure.communication.email.aio.operations.EmailOperations + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + _endpoint = '{endpoint}' + self._config = AzureCommunicationEmailServiceConfiguration(endpoint=endpoint, **kwargs) + self._client = AsyncPipelineClient(base_url=_endpoint, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + self._serialize.client_side_validation = False + self.email = EmailOperations( + self._client, self._config, self._serialize, self._deserialize + ) + + + def _send_request( + self, + request: HttpRequest, + **kwargs: Any + ) -> Awaitable[AsyncHttpResponse]: + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = await client._send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/python/protocol/quickstart + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.AsyncHttpResponse + """ + + request_copy = deepcopy(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + + request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) + return self._client.send_request(request_copy, **kwargs) + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "AzureCommunicationEmailService": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py new file mode 100644 index 000000000000..2c74b995d496 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_configuration.py @@ -0,0 +1,59 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +from .._version import VERSION + + +class AzureCommunicationEmailServiceConfiguration(Configuration): # pylint: disable=too-many-instance-attributes + """Configuration for AzureCommunicationEmailService. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The communication resource, for example + https://my-resource.communication.azure.com. Required. + :type endpoint: str + :keyword api_version: Api Version. Default value is "2021-10-01-preview". Note that overriding + this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__( + self, + endpoint: str, + **kwargs: Any + ) -> None: + super(AzureCommunicationEmailServiceConfiguration, self).__init__(**kwargs) + api_version = kwargs.pop('api_version', "2021-10-01-preview") # type: str + + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + + self.endpoint = endpoint + self.api_version = api_version + kwargs.setdefault('sdk_moniker', 'azurecommunicationemailservice/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs: Any + ) -> None: + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py new file mode 100644 index 000000000000..8a35ddb87c7e --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/_patch.py @@ -0,0 +1,23 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import List + +__all__ = [] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py new file mode 100644 index 000000000000..98c27c3620bc --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/__init__.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._email_operations import EmailOperations + +from ._patch import __all__ as _patch_all +from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +from ._patch import patch_sdk as _patch_sdk +__all__ = [ + 'EmailOperations', +] +__all__.extend([p for p in _patch_all if p not in __all__]) +_patch_sdk() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py new file mode 100644 index 000000000000..8b538c791667 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_email_operations.py @@ -0,0 +1,284 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, Callable, Dict, IO, Optional, TypeVar, Union, overload + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.core.utils import case_insensitive_dict + +from ... import models as _models +from ..._vendor import _convert_request +from ...operations._email_operations import build_get_send_status_request, build_send_request +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class EmailOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.communication.email.aio.AzureCommunicationEmailService`'s + :attr:`email` attribute. + """ + + models = _models + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + + @distributed_trace_async + async def get_send_status( + self, + message_id: str, + **kwargs: Any + ) -> _models.SendStatusResult: + """Gets the status of a message sent previously. + + Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send + email. Required. + :type message_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SendStatusResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendStatusResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + cls = kwargs.pop('cls', None) # type: ClsType[_models.SendStatusResult] + + + request = build_get_send_status_request( + message_id=message_id, + api_version=api_version, + template_url=self.get_send_status.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + + deserialized = self._deserialize('SendStatusResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + + get_send_status.metadata = {'url': "/emails/{messageId}/status"} # type: ignore + + + @overload + async def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: _models.EmailMessage, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> None: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: ~azure.communication.email.models.EmailMessage + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: IO, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> None: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + + @distributed_trace_async + async def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: Union[_models.EmailMessage, IO], + **kwargs: Any + ) -> None: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Is either a model type or a IO + type. Required. + :type email_message: ~azure.communication.email.models.EmailMessage or IO + :keyword content_type: Body Parameter content-type. Known values are: 'application/json'. + Default value is None. + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + content_type = kwargs.pop('content_type', _headers.pop('Content-Type', None)) # type: Optional[str] + cls = kwargs.pop('cls', None) # type: ClsType[None] + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(email_message, (IO, bytes)): + _content = email_message + else: + _json = self._serialize.body(email_message, 'EmailMessage') + + request = build_send_request( + repeatability_request_id=repeatability_request_id, + repeatability_first_sent=repeatability_first_sent, + api_version=api_version, + content_type=content_type, + json=_json, + content=_content, + template_url=self.send.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Repeatability-Result']=self._deserialize('str', response.headers.get('Repeatability-Result')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + response_headers['x-ms-request-id']=self._deserialize('str', response.headers.get('x-ms-request-id')) + + + if cls: + return cls(pipeline_response, None, response_headers) + + send.metadata = {'url': "/emails:send"} # type: ignore + diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py new file mode 100644 index 000000000000..6eecde32b570 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/aio/operations/_patch.py @@ -0,0 +1,69 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import Any, IO, Union +from ._email_operations import EmailOperations as EmailOperationsGenerated +from ... import models as _models + +class EmailOperations(EmailOperationsGenerated): + + def __return_message_id(self, pipeline_response, _, response_headers): + return response_headers['x-ms-request-id'] + + async def send( + self, + repeatability_request_id: str, + repeatability_first_sent: str, + email_message: Union[_models.EmailMessage, IO], + **kwargs: Any + ) -> _models.SendEmailResult: + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: SendEmailResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendEmailResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + message_id = await super().send( + repeatability_request_id, + repeatability_first_sent, + email_message, + **dict(kwargs, cls=self.__return_message_id) + ) + + return _models.SendEmailResult(message_id=message_id) + + send.metadata = {'url': "/emails:send"} # type: ignore + +__all__ = ["EmailOperations"] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py new file mode 100644 index 000000000000..0f7f73e35d03 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/__init__.py @@ -0,0 +1,51 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import CommunicationError + from ._models_py3 import CommunicationErrorResponse + from ._models_py3 import EmailAddress + from ._models_py3 import EmailAttachment + from ._models_py3 import EmailContent + from ._models_py3 import EmailCustomHeader + from ._models_py3 import EmailMessage + from ._models_py3 import EmailRecipients + from ._models_py3 import SendStatusResult +except (SyntaxError, ImportError): + from ._models import CommunicationError # type: ignore + from ._models import CommunicationErrorResponse # type: ignore + from ._models import EmailAddress # type: ignore + from ._models import EmailAttachment # type: ignore + from ._models import EmailContent # type: ignore + from ._models import EmailCustomHeader # type: ignore + from ._models import EmailMessage # type: ignore + from ._models import EmailRecipients # type: ignore + from ._models import SendStatusResult # type: ignore + +from ._azure_communication_email_service_enums import EmailAttachmentType +from ._azure_communication_email_service_enums import EmailImportance +from ._azure_communication_email_service_enums import SendStatus +from ._patch import __all__ as _patch_all +from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +from ._patch import patch_sdk as _patch_sdk +__all__ = [ + 'CommunicationError', + 'CommunicationErrorResponse', + 'EmailAddress', + 'EmailAttachment', + 'EmailContent', + 'EmailCustomHeader', + 'EmailMessage', + 'EmailRecipients', + 'SendStatusResult', + 'EmailAttachmentType', + 'EmailImportance', + 'SendStatus', +] +__all__.extend([p for p in _patch_all if p not in __all__]) +_patch_sdk() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py new file mode 100644 index 000000000000..8743cbf94a15 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_azure_communication_email_service_enums.py @@ -0,0 +1,63 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum +from azure.core import CaseInsensitiveEnumMeta + + +class EmailAttachmentType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The type of attachment file. + """ + + AVI = "avi" + BMP = "bmp" + DOC = "doc" + DOCM = "docm" + DOCX = "docx" + GIF = "gif" + JPEG = "jpeg" + MP3 = "mp3" + ONE = "one" + PDF = "pdf" + PNG = "png" + PPSM = "ppsm" + PPSX = "ppsx" + PPT = "ppt" + PPTM = "pptm" + PPTX = "pptx" + PUB = "pub" + RPMSG = "rpmsg" + RTF = "rtf" + TIF = "tif" + TXT = "txt" + VSD = "vsd" + WAV = "wav" + WMA = "wma" + XLS = "xls" + XLSB = "xlsb" + XLSM = "xlsm" + XLSX = "xlsx" + +class EmailImportance(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The importance type for the email. + """ + + HIGH = "high" + NORMAL = "normal" + LOW = "low" + +class SendStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The type indicating the status of a request. + """ + + #: The message has passed basic validations and has been queued to be processed further. + QUEUED = "queued" + #: The message has been processed and is now out for delivery. + OUT_FOR_DELIVERY = "outForDelivery" + #: The message could not be processed and was dropped. + DROPPED = "dropped" diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py new file mode 100644 index 000000000000..6b0e0134ca31 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models.py @@ -0,0 +1,411 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +import msrest.serialization + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar code: The error code. Required. + :vartype code: str + :ivar message: The error message. Required. + :vartype message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.email.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + "code": {"key": "code", "type": "str"}, + "message": {"key": "message", "type": "str"}, + "target": {"key": "target", "type": "str"}, + "details": {"key": "details", "type": "[CommunicationError]"}, + "inner_error": {"key": "innererror", "type": "CommunicationError"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword code: The error code. Required. + :paramtype code: str + :keyword message: The error message. Required. + :paramtype message: str + """ + super(CommunicationError, self).__init__(**kwargs) + self.code = kwargs['code'] + self.message = kwargs['message'] + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :ivar error: The Communication Services error. Required. + :vartype error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + "error": {"key": "error", "type": "CommunicationError"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword error: The Communication Services error. Required. + :paramtype error: ~azure.communication.email.models.CommunicationError + """ + super(CommunicationErrorResponse, self).__init__(**kwargs) + self.error = kwargs['error'] + + +class EmailAddress(msrest.serialization.Model): + """An object representing the email address and its display name. + + All required parameters must be populated in order to send to Azure. + + :ivar email: Email address. Required. + :vartype email: str + :ivar display_name: Email display name. + :vartype display_name: str + """ + + _validation = { + 'email': {'required': True}, + } + + _attribute_map = { + "email": {"key": "email", "type": "str"}, + "display_name": {"key": "displayName", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword email: Email address. Required. + :paramtype email: str + :keyword display_name: Email display name. + :paramtype display_name: str + """ + super(EmailAddress, self).__init__(**kwargs) + self.email = kwargs['email'] + self.display_name = kwargs.get('display_name', None) + + +class EmailAttachment(msrest.serialization.Model): + """Attachment to the email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Name of the attachment. Required. + :vartype name: str + :ivar attachment_type: The type of attachment file. Required. Known values are: "avi", "bmp", + "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :vartype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :ivar content_bytes_base64: Base64 encoded contents of the attachment. Required. + :vartype content_bytes_base64: str + """ + + _validation = { + 'name': {'required': True}, + 'attachment_type': {'required': True}, + 'content_bytes_base64': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "attachment_type": {"key": "attachmentType", "type": "str"}, + "content_bytes_base64": {"key": "contentBytesBase64", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword name: Name of the attachment. Required. + :paramtype name: str + :keyword attachment_type: The type of attachment file. Required. Known values are: "avi", + "bmp", "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :paramtype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :keyword content_bytes_base64: Base64 encoded contents of the attachment. Required. + :paramtype content_bytes_base64: str + """ + super(EmailAttachment, self).__init__(**kwargs) + self.name = kwargs['name'] + self.attachment_type = kwargs['attachment_type'] + self.content_bytes_base64 = kwargs['content_bytes_base64'] + + +class EmailContent(msrest.serialization.Model): + """Content of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar subject: Subject of the email message. Required. + :vartype subject: str + :ivar plain_text: Plain text version of the email message. + :vartype plain_text: str + :ivar html: Html version of the email message. + :vartype html: str + """ + + _validation = { + 'subject': {'required': True}, + } + + _attribute_map = { + "subject": {"key": "subject", "type": "str"}, + "plain_text": {"key": "plainText", "type": "str"}, + "html": {"key": "html", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword subject: Subject of the email message. Required. + :paramtype subject: str + :keyword plain_text: Plain text version of the email message. + :paramtype plain_text: str + :keyword html: Html version of the email message. + :paramtype html: str + """ + super(EmailContent, self).__init__(**kwargs) + self.subject = kwargs['subject'] + self.plain_text = kwargs.get('plain_text', None) + self.html = kwargs.get('html', None) + + +class EmailCustomHeader(msrest.serialization.Model): + """Custom header for email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Header name. Required. + :vartype name: str + :ivar value: Header value. Required. + :vartype value: str + """ + + _validation = { + 'name': {'required': True}, + 'value': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "value": {"key": "value", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword name: Header name. Required. + :paramtype name: str + :keyword value: Header value. Required. + :paramtype value: str + """ + super(EmailCustomHeader, self).__init__(**kwargs) + self.name = kwargs['name'] + self.value = kwargs['value'] + + +class EmailMessage(msrest.serialization.Model): + """Message payload for sending an email. + + All required parameters must be populated in order to send to Azure. + + :ivar custom_headers: Custom email headers to be passed. + :vartype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :ivar sender: Sender email address from a verified domain. Required. + :vartype sender: str + :ivar content: Email content to be sent. Required. + :vartype content: ~azure.communication.email.models.EmailContent + :ivar importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :vartype importance: str or ~azure.communication.email.models.EmailImportance + :ivar recipients: Recipients for the email. Required. + :vartype recipients: ~azure.communication.email.models.EmailRecipients + :ivar attachments: list of attachments. + :vartype attachments: list[~azure.communication.email.models.EmailAttachment] + :ivar reply_to: Email addresses where recipients' replies will be sent to. + :vartype reply_to: list[~azure.communication.email.models.EmailAddress] + :ivar disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :vartype disable_user_engagement_tracking: bool + """ + + _validation = { + 'sender': {'required': True}, + 'content': {'required': True}, + 'recipients': {'required': True}, + } + + _attribute_map = { + "custom_headers": {"key": "headers", "type": "[EmailCustomHeader]"}, + "sender": {"key": "sender", "type": "str"}, + "content": {"key": "content", "type": "EmailContent"}, + "importance": {"key": "importance", "type": "str"}, + "recipients": {"key": "recipients", "type": "EmailRecipients"}, + "attachments": {"key": "attachments", "type": "[EmailAttachment]"}, + "reply_to": {"key": "replyTo", "type": "[EmailAddress]"}, + "disable_user_engagement_tracking": {"key": "disableUserEngagementTracking", "type": "bool"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword custom_headers: Custom email headers to be passed. + :paramtype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :keyword sender: Sender email address from a verified domain. Required. + :paramtype sender: str + :keyword content: Email content to be sent. Required. + :paramtype content: ~azure.communication.email.models.EmailContent + :keyword importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :paramtype importance: str or ~azure.communication.email.models.EmailImportance + :keyword recipients: Recipients for the email. Required. + :paramtype recipients: ~azure.communication.email.models.EmailRecipients + :keyword attachments: list of attachments. + :paramtype attachments: list[~azure.communication.email.models.EmailAttachment] + :keyword reply_to: Email addresses where recipients' replies will be sent to. + :paramtype reply_to: list[~azure.communication.email.models.EmailAddress] + :keyword disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :paramtype disable_user_engagement_tracking: bool + """ + super(EmailMessage, self).__init__(**kwargs) + self.custom_headers = kwargs.get('custom_headers', None) + self.sender = kwargs['sender'] + self.content = kwargs['content'] + self.importance = kwargs.get('importance', "normal") + self.recipients = kwargs['recipients'] + self.attachments = kwargs.get('attachments', None) + self.reply_to = kwargs.get('reply_to', None) + self.disable_user_engagement_tracking = kwargs.get('disable_user_engagement_tracking', None) + + +class EmailRecipients(msrest.serialization.Model): + """Recipients of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar to: Email To recipients. Required. + :vartype to: list[~azure.communication.email.models.EmailAddress] + :ivar cc: Email CC recipients. + :vartype cc: list[~azure.communication.email.models.EmailAddress] + :ivar bcc: Email BCC recipients. + :vartype bcc: list[~azure.communication.email.models.EmailAddress] + """ + + _validation = { + 'to': {'required': True}, + } + + _attribute_map = { + "to": {"key": "to", "type": "[EmailAddress]"}, + "cc": {"key": "CC", "type": "[EmailAddress]"}, + "bcc": {"key": "bCC", "type": "[EmailAddress]"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword to: Email To recipients. Required. + :paramtype to: list[~azure.communication.email.models.EmailAddress] + :keyword cc: Email CC recipients. + :paramtype cc: list[~azure.communication.email.models.EmailAddress] + :keyword bcc: Email BCC recipients. + :paramtype bcc: list[~azure.communication.email.models.EmailAddress] + """ + super(EmailRecipients, self).__init__(**kwargs) + self.to = kwargs['to'] + self.cc = kwargs.get('cc', None) + self.bcc = kwargs.get('bcc', None) + + +class SendStatusResult(msrest.serialization.Model): + """Status of an email message that was sent previously. + + All required parameters must be populated in order to send to Azure. + + :ivar message_id: System generated id of an email message sent. Required. + :vartype message_id: str + :ivar status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :vartype status: str or ~azure.communication.email.models.SendStatus + """ + + _validation = { + 'message_id': {'required': True}, + 'status': {'required': True}, + } + + _attribute_map = { + "message_id": {"key": "messageId", "type": "str"}, + "status": {"key": "status", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword message_id: System generated id of an email message sent. Required. + :paramtype message_id: str + :keyword status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :paramtype status: str or ~azure.communication.email.models.SendStatus + """ + super(SendStatusResult, self).__init__(**kwargs) + self.message_id = kwargs['message_id'] + self.status = kwargs['status'] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py new file mode 100644 index 000000000000..0e85199772e6 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_models_py3.py @@ -0,0 +1,452 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import List, Optional, TYPE_CHECKING, Union + +import msrest.serialization + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from .. import models as _models + + +class CommunicationError(msrest.serialization.Model): + """The Communication Services error. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar code: The error code. Required. + :vartype code: str + :ivar message: The error message. Required. + :vartype message: str + :ivar target: The error target. + :vartype target: str + :ivar details: Further details about specific errors that led to this error. + :vartype details: list[~azure.communication.email.models.CommunicationError] + :ivar inner_error: The inner error if any. + :vartype inner_error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'inner_error': {'readonly': True}, + } + + _attribute_map = { + "code": {"key": "code", "type": "str"}, + "message": {"key": "message", "type": "str"}, + "target": {"key": "target", "type": "str"}, + "details": {"key": "details", "type": "[CommunicationError]"}, + "inner_error": {"key": "innererror", "type": "CommunicationError"}, + } + + def __init__( + self, + *, + code: str, + message: str, + **kwargs + ): + """ + :keyword code: The error code. Required. + :paramtype code: str + :keyword message: The error message. Required. + :paramtype message: str + """ + super().__init__(**kwargs) + self.code = code + self.message = message + self.target = None + self.details = None + self.inner_error = None + + +class CommunicationErrorResponse(msrest.serialization.Model): + """The Communication Services error. + + All required parameters must be populated in order to send to Azure. + + :ivar error: The Communication Services error. Required. + :vartype error: ~azure.communication.email.models.CommunicationError + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + "error": {"key": "error", "type": "CommunicationError"}, + } + + def __init__( + self, + *, + error: "_models.CommunicationError", + **kwargs + ): + """ + :keyword error: The Communication Services error. Required. + :paramtype error: ~azure.communication.email.models.CommunicationError + """ + super().__init__(**kwargs) + self.error = error + + +class EmailAddress(msrest.serialization.Model): + """An object representing the email address and its display name. + + All required parameters must be populated in order to send to Azure. + + :ivar email: Email address. Required. + :vartype email: str + :ivar display_name: Email display name. + :vartype display_name: str + """ + + _validation = { + 'email': {'required': True}, + } + + _attribute_map = { + "email": {"key": "email", "type": "str"}, + "display_name": {"key": "displayName", "type": "str"}, + } + + def __init__( + self, + *, + email: str, + display_name: Optional[str] = None, + **kwargs + ): + """ + :keyword email: Email address. Required. + :paramtype email: str + :keyword display_name: Email display name. + :paramtype display_name: str + """ + super().__init__(**kwargs) + self.email = email + self.display_name = display_name + + +class EmailAttachment(msrest.serialization.Model): + """Attachment to the email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Name of the attachment. Required. + :vartype name: str + :ivar attachment_type: The type of attachment file. Required. Known values are: "avi", "bmp", + "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :vartype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :ivar content_bytes_base64: Base64 encoded contents of the attachment. Required. + :vartype content_bytes_base64: str + """ + + _validation = { + 'name': {'required': True}, + 'attachment_type': {'required': True}, + 'content_bytes_base64': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "attachment_type": {"key": "attachmentType", "type": "str"}, + "content_bytes_base64": {"key": "contentBytesBase64", "type": "str"}, + } + + def __init__( + self, + *, + name: str, + attachment_type: Union[str, "_models.EmailAttachmentType"], + content_bytes_base64: str, + **kwargs + ): + """ + :keyword name: Name of the attachment. Required. + :paramtype name: str + :keyword attachment_type: The type of attachment file. Required. Known values are: "avi", + "bmp", "doc", "docm", "docx", "gif", "jpeg", "mp3", "one", "pdf", "png", "ppsm", "ppsx", "ppt", + "pptm", "pptx", "pub", "rpmsg", "rtf", "tif", "txt", "vsd", "wav", "wma", "xls", "xlsb", + "xlsm", and "xlsx". + :paramtype attachment_type: str or ~azure.communication.email.models.EmailAttachmentType + :keyword content_bytes_base64: Base64 encoded contents of the attachment. Required. + :paramtype content_bytes_base64: str + """ + super().__init__(**kwargs) + self.name = name + self.attachment_type = attachment_type + self.content_bytes_base64 = content_bytes_base64 + + +class EmailContent(msrest.serialization.Model): + """Content of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar subject: Subject of the email message. Required. + :vartype subject: str + :ivar plain_text: Plain text version of the email message. + :vartype plain_text: str + :ivar html: Html version of the email message. + :vartype html: str + """ + + _validation = { + 'subject': {'required': True}, + } + + _attribute_map = { + "subject": {"key": "subject", "type": "str"}, + "plain_text": {"key": "plainText", "type": "str"}, + "html": {"key": "html", "type": "str"}, + } + + def __init__( + self, + *, + subject: str, + plain_text: Optional[str] = None, + html: Optional[str] = None, + **kwargs + ): + """ + :keyword subject: Subject of the email message. Required. + :paramtype subject: str + :keyword plain_text: Plain text version of the email message. + :paramtype plain_text: str + :keyword html: Html version of the email message. + :paramtype html: str + """ + super().__init__(**kwargs) + self.subject = subject + self.plain_text = plain_text + self.html = html + + +class EmailCustomHeader(msrest.serialization.Model): + """Custom header for email. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Header name. Required. + :vartype name: str + :ivar value: Header value. Required. + :vartype value: str + """ + + _validation = { + 'name': {'required': True}, + 'value': {'required': True}, + } + + _attribute_map = { + "name": {"key": "name", "type": "str"}, + "value": {"key": "value", "type": "str"}, + } + + def __init__( + self, + *, + name: str, + value: str, + **kwargs + ): + """ + :keyword name: Header name. Required. + :paramtype name: str + :keyword value: Header value. Required. + :paramtype value: str + """ + super().__init__(**kwargs) + self.name = name + self.value = value + + +class EmailMessage(msrest.serialization.Model): + """Message payload for sending an email. + + All required parameters must be populated in order to send to Azure. + + :ivar custom_headers: Custom email headers to be passed. + :vartype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :ivar sender: Sender email address from a verified domain. Required. + :vartype sender: str + :ivar content: Email content to be sent. Required. + :vartype content: ~azure.communication.email.models.EmailContent + :ivar importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :vartype importance: str or ~azure.communication.email.models.EmailImportance + :ivar recipients: Recipients for the email. Required. + :vartype recipients: ~azure.communication.email.models.EmailRecipients + :ivar attachments: list of attachments. + :vartype attachments: list[~azure.communication.email.models.EmailAttachment] + :ivar reply_to: Email addresses where recipients' replies will be sent to. + :vartype reply_to: list[~azure.communication.email.models.EmailAddress] + :ivar disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :vartype disable_user_engagement_tracking: bool + """ + + _validation = { + 'sender': {'required': True}, + 'content': {'required': True}, + 'recipients': {'required': True}, + } + + _attribute_map = { + "custom_headers": {"key": "headers", "type": "[EmailCustomHeader]"}, + "sender": {"key": "sender", "type": "str"}, + "content": {"key": "content", "type": "EmailContent"}, + "importance": {"key": "importance", "type": "str"}, + "recipients": {"key": "recipients", "type": "EmailRecipients"}, + "attachments": {"key": "attachments", "type": "[EmailAttachment]"}, + "reply_to": {"key": "replyTo", "type": "[EmailAddress]"}, + "disable_user_engagement_tracking": {"key": "disableUserEngagementTracking", "type": "bool"}, + } + + def __init__( + self, + *, + sender: str, + content: "_models.EmailContent", + recipients: "_models.EmailRecipients", + custom_headers: Optional[List["_models.EmailCustomHeader"]] = None, + importance: Union[str, "_models.EmailImportance"] = "normal", + attachments: Optional[List["_models.EmailAttachment"]] = None, + reply_to: Optional[List["_models.EmailAddress"]] = None, + disable_user_engagement_tracking: Optional[bool] = None, + **kwargs + ): + """ + :keyword custom_headers: Custom email headers to be passed. + :paramtype custom_headers: list[~azure.communication.email.models.EmailCustomHeader] + :keyword sender: Sender email address from a verified domain. Required. + :paramtype sender: str + :keyword content: Email content to be sent. Required. + :paramtype content: ~azure.communication.email.models.EmailContent + :keyword importance: The importance type for the email. Known values are: "high", "normal", and + "low". + :paramtype importance: str or ~azure.communication.email.models.EmailImportance + :keyword recipients: Recipients for the email. Required. + :paramtype recipients: ~azure.communication.email.models.EmailRecipients + :keyword attachments: list of attachments. + :paramtype attachments: list[~azure.communication.email.models.EmailAttachment] + :keyword reply_to: Email addresses where recipients' replies will be sent to. + :paramtype reply_to: list[~azure.communication.email.models.EmailAddress] + :keyword disable_user_engagement_tracking: Indicates whether user engagement tracking should be + disabled for this request if the resource-level user engagement tracking setting was already + enabled in the control plane. + :paramtype disable_user_engagement_tracking: bool + """ + super().__init__(**kwargs) + self.custom_headers = custom_headers + self.sender = sender + self.content = content + self.importance = importance + self.recipients = recipients + self.attachments = attachments + self.reply_to = reply_to + self.disable_user_engagement_tracking = disable_user_engagement_tracking + + +class EmailRecipients(msrest.serialization.Model): + """Recipients of the email. + + All required parameters must be populated in order to send to Azure. + + :ivar to: Email To recipients. Required. + :vartype to: list[~azure.communication.email.models.EmailAddress] + :ivar cc: Email CC recipients. + :vartype cc: list[~azure.communication.email.models.EmailAddress] + :ivar bcc: Email BCC recipients. + :vartype bcc: list[~azure.communication.email.models.EmailAddress] + """ + + _validation = { + 'to': {'required': True}, + } + + _attribute_map = { + "to": {"key": "to", "type": "[EmailAddress]"}, + "cc": {"key": "CC", "type": "[EmailAddress]"}, + "bcc": {"key": "bCC", "type": "[EmailAddress]"}, + } + + def __init__( + self, + *, + to: List["_models.EmailAddress"], + cc: Optional[List["_models.EmailAddress"]] = None, + bcc: Optional[List["_models.EmailAddress"]] = None, + **kwargs + ): + """ + :keyword to: Email To recipients. Required. + :paramtype to: list[~azure.communication.email.models.EmailAddress] + :keyword cc: Email CC recipients. + :paramtype cc: list[~azure.communication.email.models.EmailAddress] + :keyword bcc: Email BCC recipients. + :paramtype bcc: list[~azure.communication.email.models.EmailAddress] + """ + super().__init__(**kwargs) + self.to = to + self.cc = cc + self.bcc = bcc + + +class SendStatusResult(msrest.serialization.Model): + """Status of an email message that was sent previously. + + All required parameters must be populated in order to send to Azure. + + :ivar message_id: System generated id of an email message sent. Required. + :vartype message_id: str + :ivar status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :vartype status: str or ~azure.communication.email.models.SendStatus + """ + + _validation = { + 'message_id': {'required': True}, + 'status': {'required': True}, + } + + _attribute_map = { + "message_id": {"key": "messageId", "type": "str"}, + "status": {"key": "status", "type": "str"}, + } + + def __init__( + self, + *, + message_id: str, + status: Union[str, "_models.SendStatus"], + **kwargs + ): + """ + :keyword message_id: System generated id of an email message sent. Required. + :paramtype message_id: str + :keyword status: The type indicating the status of a request. Required. Known values are: + "queued", "outForDelivery", and "dropped". + :paramtype status: str or ~azure.communication.email.models.SendStatus + """ + super().__init__(**kwargs) + self.message_id = message_id + self.status = status diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py new file mode 100644 index 000000000000..c19a69940543 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/models/_patch.py @@ -0,0 +1,47 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +import msrest.serialization + +class SendEmailResult(msrest.serialization.Model): + """Results of a sent email. + + All required parameters must be populated in order to send to Azure. + + :ivar message_id: System generated id of an email message sent. Required. + :vartype message_id: str + """ + + _validation = { + 'message_id': {'required': True}, + } + + _attribute_map = { + "message_id": {"key": "messageId", "type": "str"}, + } + + def __init__( + self, + **kwargs + ): + """ + :keyword message_id: System generated id of an email message sent. Required. + :paramtype message_id: str + """ + super(SendEmailResult, self).__init__(**kwargs) + self.message_id = kwargs['message_id'] + +__all__ = ["SendEmailResult"] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py new file mode 100644 index 000000000000..98c27c3620bc --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/__init__.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._email_operations import EmailOperations + +from ._patch import __all__ as _patch_all +from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import +from ._patch import patch_sdk as _patch_sdk +__all__ = [ + 'EmailOperations', +] +__all__.extend([p for p in _patch_all if p not in __all__]) +_patch_sdk() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py new file mode 100644 index 000000000000..90294c12462f --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_email_operations.py @@ -0,0 +1,361 @@ +# 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) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import IO, Optional, TYPE_CHECKING, Union, overload + +from msrest import Serializer + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpResponse +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator import distributed_trace +from azure.core.utils import case_insensitive_dict + +from .. import models as _models +from .._vendor import _convert_request, _format_url_section + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Optional, TypeVar + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +_SERIALIZER = Serializer() +_SERIALIZER.client_side_validation = False +# fmt: off + +def build_get_send_status_request( + message_id, # type: str + **kwargs # type: Any +): + # type: (...) -> HttpRequest + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', "2021-10-01-preview")) # type: str + accept = _headers.pop('Accept', "application/json") + + # Construct URL + _url = kwargs.pop("template_url", "/emails/{messageId}/status") + path_format_arguments = { + "messageId": _SERIALIZER.url("message_id", message_id, 'str'), + } + + _url = _format_url_section(_url, **path_format_arguments) + + # Construct parameters + _params['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + _headers['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs + ) + + +def build_send_request( + **kwargs # type: Any +): + # type: (...) -> HttpRequest + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', "2021-10-01-preview")) # type: str + content_type = kwargs.pop('content_type', _headers.pop('Content-Type', None)) # type: Optional[str] + repeatability_request_id = kwargs.pop('repeatability_request_id') # type: str + repeatability_first_sent = kwargs.pop('repeatability_first_sent') # type: str + accept = _headers.pop('Accept', "application/json") + + # Construct URL + _url = kwargs.pop("template_url", "/emails:send") + + # Construct parameters + _params['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + _headers['repeatability-request-id'] = _SERIALIZER.header("repeatability_request_id", repeatability_request_id, 'str') + _headers['repeatability-first-sent'] = _SERIALIZER.header("repeatability_first_sent", repeatability_first_sent, 'str') + if content_type is not None: + _headers['Content-Type'] = _SERIALIZER.header("content_type", content_type, 'str') + _headers['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="POST", + url=_url, + params=_params, + headers=_headers, + **kwargs + ) + +# fmt: on +class EmailOperations(object): + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.communication.email.AzureCommunicationEmailService`'s + :attr:`email` attribute. + """ + + models = _models + + def __init__(self, *args, **kwargs): + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = input_args.pop(0) if input_args else kwargs.pop("deserializer") + + + @distributed_trace + def get_send_status( + self, + message_id, # type: str + **kwargs # type: Any + ): + # type: (...) -> _models.SendStatusResult + """Gets the status of a message sent previously. + + Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send + email. Required. + :type message_id: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SendStatusResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendStatusResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + cls = kwargs.pop('cls', None) # type: ClsType[_models.SendStatusResult] + + + request = build_get_send_status_request( + message_id=message_id, + api_version=api_version, + template_url=self.get_send_status.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + + deserialized = self._deserialize('SendStatusResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + + get_send_status.metadata = {'url': "/emails/{messageId}/status"} # type: ignore + + + @overload + def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: _models.EmailMessage + **kwargs # type: Any + ): + # type: (...) -> None + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: ~azure.communication.email.models.EmailMessage + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: IO + **kwargs # type: Any + ): + # type: (...) -> None + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + + @distributed_trace + def send( # pylint: disable=inconsistent-return-statements + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: Union[_models.EmailMessage, IO] + **kwargs # type: Any + ): + # type: (...) -> None + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Is either a model type or a IO + type. Required. + :type email_message: ~azure.communication.email.models.EmailMessage or IO + :keyword content_type: Body Parameter content-type. Known values are: 'application/json'. + Default value is None. + :paramtype content_type: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None or the result of cls(response) + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version = kwargs.pop('api_version', _params.pop('api-version', self._config.api_version)) # type: str + content_type = kwargs.pop('content_type', _headers.pop('Content-Type', None)) # type: Optional[str] + cls = kwargs.pop('cls', None) # type: ClsType[None] + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(email_message, (IO, bytes)): + _content = email_message + else: + _json = self._serialize.body(email_message, 'EmailMessage') + + request = build_send_request( + repeatability_request_id=repeatability_request_id, + repeatability_first_sent=repeatability_first_sent, + api_version=api_version, + content_type=content_type, + json=_json, + content=_content, + template_url=self.send.metadata['url'], + headers=_headers, + params=_params, + ) + request = _convert_request(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + } + request.url = self._client.format_url(request.url, **path_format_arguments) # type: ignore + + pipeline_response = self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + request, + stream=False, + **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [202]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.CommunicationErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers['Repeatability-Result']=self._deserialize('str', response.headers.get('Repeatability-Result')) + response_headers['Operation-Location']=self._deserialize('str', response.headers.get('Operation-Location')) + response_headers['Retry-After']=self._deserialize('int', response.headers.get('Retry-After')) + response_headers['x-ms-request-id']=self._deserialize('str', response.headers.get('x-ms-request-id')) + + if cls: + return cls(pipeline_response, None, response_headers) + + send.metadata = {'url': "/emails:send"} # type: ignore + diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py new file mode 100644 index 000000000000..69156a77b9a3 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/operations/_patch.py @@ -0,0 +1,69 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import Any, IO, Union +from ._email_operations import EmailOperations as EmailOperationsGenerated +from ..models import _models, SendEmailResult + +class EmailOperations(EmailOperationsGenerated): + + def __return_message_id(self, pipeline_response, _, response_headers): + return response_headers['x-ms-request-id'] + + def send( + self, + repeatability_request_id, # type: str + repeatability_first_sent, # type: str + email_message, # type: Union[_models.EmailMessage, IO] + **kwargs # type: Any + ): + # type: (...) -> SendEmailResult + """Queues an email message to be sent to one or more recipients. + + Queues an email message to be sent to one or more recipients. + + :param repeatability_request_id: If specified, the client directs that the request is + repeatable; that is, that the client can make the request multiple times with the same + Repeatability-Request-Id and get back an appropriate response without the server executing the + request multiple times. The value of the Repeatability-Request-Id is an opaque string + representing a client-generated, globally unique for all time, identifier for the request. It + is recommended to use version 4 (random) UUIDs. Required. + :type repeatability_request_id: str + :param repeatability_first_sent: Must be sent by clients to specify that a request is + repeatable. Repeatability-First-Sent is used to specify the date and time at which the request + was first created in the IMF-fix date form of HTTP-date as defined in RFC7231. eg- Tue, 26 Mar + 2019 16:06:51 GMT. Required. + :type repeatability_first_sent: str + :param email_message: Message payload for sending an email. Required. + :type email_message: IO + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: SendEmailResult or the result of cls(response) + :rtype: ~azure.communication.email.models.SendEmailResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + message_id = super().send( + repeatability_request_id, + repeatability_first_sent, + email_message, + **dict(kwargs, cls=self.__return_message_id) + ) + return SendEmailResult(message_id=message_id) + + send.metadata = {'url': "/emails:send"} # type: ignore + +__all__ = ["EmailOperations"] # type: List[str] # Add all objects you want publicly available to users at this package level + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed b/sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed new file mode 100644 index 000000000000..e5aff4f83af8 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_generated/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py new file mode 100644 index 000000000000..5b396cd202e8 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_shared/__init__.py @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- diff --git a/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py similarity index 100% rename from sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py rename to sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py new file mode 100644 index 000000000000..ab028c385334 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_shared/utils.py @@ -0,0 +1,37 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +from typing import ( # pylint: disable=unused-import + cast, + Tuple, +) +from datetime import datetime + +def get_current_utc_time(): + # type: () -> str + return str(datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S ")) + "GMT" + +def parse_connection_str(conn_str): + # type: (str) -> Tuple[str, str, str, str] + if conn_str is None: + raise ValueError( + "Connection string is undefined." + ) + endpoint = None + shared_access_key = None + for element in conn_str.split(";"): + key, _, value = element.partition("=") + if key.lower() == "endpoint": + endpoint = value.rstrip("/") + elif key.lower() == "accesskey": + shared_access_key = value + if not all([endpoint, shared_access_key]): + raise ValueError( + "Invalid connection string. You can get the connection string from your resource page in the Azure Portal. " + "The format should be as follows: endpoint=https:///;accesskey=" + ) + + return str(endpoint), str(shared_access_key) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_version.py b/sdk/communication/azure-communication-email/azure/communication/email/_version.py new file mode 100644 index 000000000000..41f0bacc9706 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_version.py @@ -0,0 +1,11 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +VERSION = "1.0.0b1" + +SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py new file mode 100644 index 000000000000..aa02483033ff --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/__init__.py @@ -0,0 +1,5 @@ +from ._email_client_async import EmailClient + +__all__ = [ + 'EmailClient', +] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py new file mode 100644 index 000000000000..d89b789dedf1 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -0,0 +1,82 @@ +from uuid import uuid4 +from azure.core.tracing.decorator_async import distributed_trace_async +from .._shared.utils import parse_connection_str, get_current_utc_time +from .._shared.policy import HMACCredentialsPolicy +from .._generated.aio._azure_communication_email_service import AzureCommunicationEmailService +from .._version import SDK_MONIKER +from .._generated.models import SendEmailResult, SendStatusResult, EmailMessage + +class EmailClient(object): + """A client to interact with the AzureCommunicationService Email gateway asynchronously. + + This client provides operations to send an email and monitor its status. + + :param str conn_string: + The connection string to connect to an Azure Communication Service resource. + Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + """ + def __init__( + self, + conn_str, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + endpoint, access_key = parse_connection_str(conn_str) + authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + + self._generated_client = AzureCommunicationEmailService( + endpoint, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs + ) + + @distributed_trace_async + async def send( + self, + email_message, # type: EmailMessage + **kwargs # type: Any + ): # type: (...) -> SendEmailResult + """Queues an email message to be sent to one or more recipients. + + :param email_message: The message payload for sending an email. + :type email_message: ~azure.communication.email.models.EmailMessage + :return: SendEmailResult + :rtype: ~azure.communication.email.models.SendEmailResult + """ + + return await self._generated_client.email.send( + repeatability_request_id=uuid4(), + repeatability_first_sent=get_current_utc_time(), + email_message=email_message, + **kwargs + ) + + @distributed_trace_async + async def get_send_status( + self, + message_id, #type: str + **kwargs # type: Any + ): # type: (...) -> SendStatusResult + """Gets the status of a message sent previously. + + :param message_id: System generated message id (GUID) returned from a previous call to send email + :type message_id: str + :return: SendStatusResult + :rtype: ~azure.communication.email.models.SendStatusResult + """ + + return await self._generated_client.email.get_send_status( + message_id=message_id, + **kwargs + ) + + async def __aenter__(self) -> "EmailClient": + await self._generated_client.__aenter__() + return self + + async def __aexit__(self, *args) -> None: + await self._generated_client.__aexit__(*args) + + async def close(self) -> None: + await self._generated_client.close() \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/azure/communication/email/py.typed b/sdk/communication/azure-communication-email/azure/communication/email/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirement.txt new file mode 100644 index 000000000000..b8884941f2bd --- /dev/null +++ b/sdk/communication/azure-communication-email/dev_requirement.txt @@ -0,0 +1,7 @@ +-e ../../../tools/azure-sdk-tools +-e ../../../tools/azure-devtools +-e ../../identity/azure-identity +../../core/azure-core +aiohttp>=3.0 +aiounittest>=1.4 +pytest==7.1.2 \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py new file mode 100644 index 000000000000..4d161eea14ab --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py @@ -0,0 +1,72 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: check_message_status.py +DESCRIPTION: + This sample demonstrates checking the status of a sent email. The Email client is + authenticated using a connection string. +USAGE: + python check_message_status.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailCheckMessageStatusSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def check_message_status(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + # sending the email message + response = email_client.send(message) + + # using the message id to get the status of the email + message_id = response.message_id + message_status = email_client.get_send_status(message_id) + + print("Message Status: " + message_status.status) + +if __name__ == '__main__': + sample = EmailCheckMessageStatusSample() + sample.check_message_status() diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py new file mode 100644 index 000000000000..d294ab6d52e5 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py @@ -0,0 +1,82 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: check_message_status_async.py +DESCRIPTION: + This sample demonstrates checking the status of a sent email. The Email client is + authenticated using a connection string. +USAGE: + python check_message_status_async.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailCheckMessageStatusSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + async def check_message_status_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + + # using the message id to get the status of the email + message_id = response.message_id + message_status = await email_client.get_send_status(message_id) + + print("Message Status: " + message_status.status) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailCheckMessageStatusSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.check_message_status_async()) \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py new file mode 100644 index 000000000000..4c709356b27e --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py @@ -0,0 +1,72 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_multiple_recipient_sample.py +DESCRIPTION: + This sample demonstrates sending an email to multiple recipients. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailMultipleRecipientSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + second_recipient_address = os.getenv("SECOND_RECIPIENT_ADDRESS") + + def send_email_to_multiple_recipients(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + +if __name__ == '__main__': + sample = EmailMultipleRecipientSample() + sample.send_email_to_multiple_recipients() diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py new file mode 100644 index 000000000000..e77525bd3736 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -0,0 +1,82 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_multiple_recipient_sample_async.py +DESCRIPTION: + This sample demonstrates sending an email to multiple recipients. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailMultipleRecipientSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + second_recipient_address = os.getenv("SECOND_RECIPIENT_ADDRESS") + + async def send_email_to_multiple_recipients_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + print("Message ID: " + response.message_id) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailMultipleRecipientSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.send_email_to_multiple_recipients_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py new file mode 100644 index 000000000000..d7c58d33cd74 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py @@ -0,0 +1,67 @@ +# 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. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_single_recipient_sample.py +DESCRIPTION: + This sample demonstrates sending an email to a single recipient. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailSingleRecipientSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def send_email_to_single_recipient(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + +if __name__ == '__main__': + sample = EmailSingleRecipientSample() + sample.send_email_to_single_recipient() diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py new file mode 100644 index 000000000000..be15a68610f5 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py @@ -0,0 +1,77 @@ +# 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. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_to_single_recipient_sample_async.py +DESCRIPTION: + This sample demonstrates sending an email to a single recipient. The Email client is + authenticated using a connection string. +USAGE: + python send_email_to_single_recipient_sample_async.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailMessage +) + +sys.path.append("..") + +class EmailSingleRecipientSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + async def send_email_to_single_recipient_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + print("Message ID: " + response.message_id) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailSingleRecipientSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.send_email_to_single_recipient_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py new file mode 100644 index 000000000000..7dc5c180866c --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -0,0 +1,75 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_with_attachments_sample.py +DESCRIPTION: + This sample demonstrates sending an email with an attachment. The Email client is + authenticated using a connection string. +USAGE: + python send_email_with_attachment.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +from azure.communication.email import ( + EmailClient, + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment, + EmailMessage +) + +sys.path.append("..") + +class EmailWithAttachmentSample(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def send_email_with_attachment(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients, + attachments=[attachment] + ) + + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + +if __name__ == '__main__': + sample = EmailWithAttachmentSample() + sample.send_email_with_attachment() diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py new file mode 100644 index 000000000000..ad6e14209064 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -0,0 +1,85 @@ +# 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. +# -------------------------------------------------------------------------- + +""" +FILE: send_email_with_attachments_sample_async.py +DESCRIPTION: + This sample demonstrates sending an email with an attachment. The Email client is + authenticated using a connection string. +USAGE: + python send_email_with_attachment_async.py + Set the environment variable with your own value before running the sample: + 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource + 2) SENDER_ADDRESS - the address found in the linked domain that will send the email + 3) RECIPIENT_ADDRESS - the address that will recieve the email +""" + +import os +import sys +import asyncio +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment, + EmailMessage +) + +sys.path.append("..") + +class EmailWithAttachmentSampleAsync(object): + + connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING") + sender_address = os.getenv("SENDER_ADDRESS") + recipient_address = os.getenv("RECIPIENT_ADDRESS") + + async def send_email_with_attachment_async(self): + # creating the email client + email_client = EmailClient(self.connection_string) + + # creating the email message + content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", + ) + + recipients = EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + + attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients, + attachments=[attachment] + ) + + async with email_client: + try: + # sending the email message + response = await email_client.send(message) + print("Message ID: " + response.message_id) + except Exception: + print(Exception) + pass + +if __name__ == '__main__': + sample = EmailWithAttachmentSampleAsync() + + # Comment in this line if you are running this sample on Windows + # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + asyncio.run(sample.send_email_with_attachment_async()) diff --git a/sdk/communication/azure-communication-email/setup.py b/sdk/communication/azure-communication-email/setup.py new file mode 100644 index 000000000000..b08b8ab01133 --- /dev/null +++ b/sdk/communication/azure-communication-email/setup.py @@ -0,0 +1,71 @@ +from setuptools import setup, find_packages +import os +from io import open +import re + +# example setup.py Feel free to copy the entire "azure-template" folder into a package folder named +# with "azure-". Ensure that the below arguments to setup() are updated to reflect +# your package. + +# this setup.py is set up in a specific way to keep the azure* and azure-mgmt-* namespaces WORKING all the way +# up from python 3.6. Reference here: https://github.com/Azure/azure-sdk-for-python/wiki/Azure-packaging + +PACKAGE_NAME = "azure-communication-email" +PACKAGE_PPRINT_NAME = "Communication Email" + +# a-b-c => a/b/c +package_folder_path = PACKAGE_NAME.replace('-', '/') +# a-b-c => a.b.c +namespace_name = PACKAGE_NAME.replace('-', '.') + +# Version extraction inspired from 'requests' +with open(os.path.join(package_folder_path, '_version.py'), 'r') as fd: + version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', + fd.read(), re.MULTILINE).group(1) +if not version: + raise RuntimeError('Cannot find version information') + +with open('README.md', encoding='utf-8') as f: + long_description = f.read() + +setup( + name=PACKAGE_NAME, + version=version, + description='Microsoft Azure {} Client Library for Python'.format(PACKAGE_PPRINT_NAME), + long_description=long_description, + long_description_content_type='text/markdown', + license='MIT License', + author='Microsoft Corporation', + author_email='azpysdkhelp@microsoft.com', + url='https://github.com/Azure/azure-sdk-for-python', + classifiers=[ + "Development Status :: 5 - Production/Stable", + 'Programming Language :: Python', + "Programming Language :: Python :: 3 :: Only", + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'License :: OSI Approved :: MIT License', + ], + zip_safe=False, + packages=find_packages(exclude=[ + 'tests', + # Exclude packages that will be covered by PEP420 or nspkg + 'azure', + 'azure.communication' + ]), + python_requires=">=3.6", + include_package_data=True, + package_data={ + 'pytyped': ['py.typed'], + }, + install_requires=[ + 'azure-core<2.0.0,>=1.15.0', + 'msrest>=0.6.21', + 'six>=1.11.0', + ], + extras_require={ + ":python_version<'3.8'": ["typing-extensions"] + } +) diff --git a/sdk/communication/azure-communication-email/swagger/SWAGGER.md b/sdk/communication/azure-communication-email/swagger/SWAGGER.md new file mode 100644 index 000000000000..28efe4a31e3c --- /dev/null +++ b/sdk/communication/azure-communication-email/swagger/SWAGGER.md @@ -0,0 +1,42 @@ +# Azure Communication Services Email REST API Client + +> see https://aka.ms/autorest + +### Setup +```ps +npm install -g autorest +``` + +### Generation +```ps +cd +autorest SWAGGER.md +``` + +### Settings +``` yaml +package-version: 1.0.0b1 +tag: package-2021-10-01-preview +require: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/communication/data-plane/Email/readme.md +output-folder: ../azure/communication/email/_generated +namespace: azure.communication.email +no-namespace-folders: true +license-header: MICROSOFT_MIT_NO_VERSION +enable-xml: true +clear-output-folder: true +python: true +v3: true +no-async: false +add-credential: false +security: Anonymous +title: Azure Communication Email Service +``` + +### Change the bCC property to bcc +```yaml +directive: + - from: swagger-document + where: $.definitions.EmailRecipients.properties.bCC + transform: > + $["x-ms-client-name"] = "bcc" +``` diff --git a/sdk/communication/azure-communication-email/tests/_shared/testcase.py b/sdk/communication/azure-communication-email/tests/_shared/testcase.py new file mode 100644 index 000000000000..cf7fb7d2e14d --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/_shared/testcase.py @@ -0,0 +1,102 @@ + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import os +import re +from devtools_testutils import AzureTestCase +from azure.communication.email._shared.utils import parse_connection_str +from azure_devtools.scenario_tests import RecordingProcessor, ReplayableTest +from azure_devtools.scenario_tests.utilities import is_text_payload + +class ResponseReplacerProcessor(RecordingProcessor): + def __init__(self, keys=None, replacement="sanitized"): + self._keys = keys if keys else [] + self._replacement = replacement + + def process_response(self, response): + def sanitize_dict(dictionary): + for key in dictionary: + value = dictionary[key] + if isinstance(value, str): + dictionary[key] = re.sub( + r"("+'|'.join(self._keys)+r")", + self._replacement, + dictionary[key]) + elif isinstance(value, dict): + sanitize_dict(value) + + sanitize_dict(response) + + return response + +class BodyReplacerProcessor(RecordingProcessor): + """Sanitize the sensitive info inside request or response bodies""" + + def __init__(self, keys=None, replacement="sanitized"): + self._replacement = replacement + self._keys = keys if keys else [] + + def process_request(self, request): + if is_text_payload(request) and request.body: + request.body = self._replace_keys(request.body.decode()).encode() + + return request + + def process_response(self, response): + if is_text_payload(response) and response['body']['string']: + response['body']['string'] = self._replace_keys(response['body']['string']) + + return response + + def _replace_keys(self, body): + def _replace_recursively(obj): + if isinstance(obj, dict): + for key in obj: + if key in self._keys: + obj[key] = self._replacement + else: + _replace_recursively(obj[key]) + elif isinstance(obj, list): + for i in obj: + _replace_recursively(i) + + import json + try: + body = json.loads(body) + _replace_recursively(body) + + except (KeyError, ValueError): + return body + + return json.dumps(body) + +class CommunicationTestCase(AzureTestCase): + # FILTER_HEADERS = ReplayableTest.FILTER_HEADERS + [ + # 'x-azure-ref', + # 'x-ms-content-sha256', + # 'location', + # # 'x-ms-date', + # # 'repeatability-first-sent', + # # 'repeatability-request-id', + # # 'operation-location', + # # 'date' + # ] + + def __init__(self, method_name, *args, **kwargs): + super(CommunicationTestCase, self).__init__(method_name, *args, **kwargs) + + def setUp(self): + super(CommunicationTestCase, self).setUp() + + # if self.is_playback(): + # self.connection_str = "endpoint=https://sanitized.communication.azure.com/;accesskey=fake===" + # else: + # self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') + # endpoint, _ = parse_connection_str(self.connection_str) + # self._resource_name = endpoint.split(".")[0] + # self.scrubber.register_name_pair(self._resource_name, "sanitized") + + self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml new file mode 100644 index 000000000000..8e9ff2be0c4d --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml @@ -0,0 +1,53 @@ +interactions: +- request: + body: '{"sender": "DoNotReply@266db372-a95a-494f-88b5-81ffd9e866af.azurecomm.net", + "content": {"subject": "This is the subject", "plainText": "This is the body"}, + "importance": "normal", "recipients": {"to": [{"email": "acseaastesting@gmail.com", + "displayName": "Customer Name"}]}}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '274' + Content-Type: + - application/json + User-Agent: + - azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0) + repeatability-first-sent: + - Thu, 16 Jun 2022 23:57:39 GMT + repeatability-request-id: + - e19daf10-3c5c-4b5a-84a2-6dcd31946e9a + x-ms-content-sha256: + - 5Efo+JDWFExoHqUXqWOtGVGR8b9UFPpSZvfMSo/0XCQ= + x-ms-date: + - Thu, 16 Jun 2022 23:57:39 GMT + x-ms-return-client-request-id: + - 'true' + method: POST + uri: https://email-js-sdk-recording-comm-2.communication.azure.com/emails:send?api-version=2021-10-01-preview + response: + body: + string: '' + headers: + api-supported-versions: + - 2021-10-01-preview + content-length: + - '0' + date: + - Thu, 16 Jun 2022 23:57:40 GMT + operation-location: + - https://email-js-sdk-recording-comm-2.communication.azure.com/emails/0ba65ac2-55da-4178-85b4-1d4f44c6fa84/status + repeatability-result: + - accepted + x-azure-ref: + - 0dMOrYgAAAAAaqONyzFwZSo1BsrwLQtafV1NURURHRTA4MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx + x-cache: + - CONFIG_NOCACHE + status: + code: 202 + message: Accepted +version: 1 diff --git a/sdk/communication/azure-communication-email/tests/test_email_client.py b/sdk/communication/azure-communication-email/tests/test_email_client.py new file mode 100644 index 000000000000..de4ba84d2835 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client.py @@ -0,0 +1,69 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import unittest +from unittest.mock import Mock + +from unittest_helpers import mock_response +from azure.communication.email import ( + EmailClient, + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress +) + + +class TestEmailClient(unittest.TestCase): + def test_send(self): + + message = EmailMessage( + sender="someSender@contoso.com", + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email="someRecipient@domain.com", display_name="Customer Name")] + ) + ) + + def mock_send(*_, **__): + return mock_response(status_code=202, headers={ + 'x-ms-request-id': "testMessageId" + }) + + email_client = EmailClient( + conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", + transport = Mock(send=mock_send) + ) + + response = None + raised = False + try: + response = email_client.send(message) + except: + raised = True + raise + + self.assertFalse(raised, 'Expected is no exception raised') + self.assertIsNotNone(response.message_id) + + def test_get_send_status(self): + + def mock_get_send_status(*_, **__): + return mock_response(status_code=200, json_payload={"test": "test"}) + + email_client = EmailClient( + conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", + transport = Mock(send=mock_get_send_status) + ) + response = None + raised = False + try: + response = email_client.get_send_status("testMessageId") + except: + raised = True + raise + + self.assertFalse(raised, 'Expected is no exception raised') + self.assertIsNotNone(response) \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py new file mode 100644 index 000000000000..4f6a2fe7cd59 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -0,0 +1,36 @@ +import os +from azure.communication.email import ( + EmailClient, + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress +) +from _shared.testcase import ( + CommunicationTestCase, +) + +class EmailClientTest(CommunicationTestCase): + def __init__(self, method_name): + super(EmailClientTest, self).__init__(method_name) + + def setUp(self): + super(EmailClientTest, self).setUp() + + self.sender_address = os.getenv("SENDER_ADDRESS") + self.recipient_address = os.getenv("RECIPIENT_ADDRESS") + + def test_send_email_single(self): + email_client = EmailClient(self.connection_str) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) + + response = email_client.send(message) + print(response) + assert response is None \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/unittest_helpers.py b/sdk/communication/azure-communication-email/tests/unittest_helpers.py new file mode 100644 index 000000000000..9d24a0aa86eb --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/unittest_helpers.py @@ -0,0 +1,20 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import json + +from unittest import mock + +def mock_response(status_code=200, headers=None, json_payload=None): + response = mock.Mock(status_code=status_code, headers=headers or {}) + if json_payload is not None: + response.text = lambda encoding=None: json.dumps(json_payload) + response.headers["content-type"] = "application/json" + response.content_type = "application/json" + else: + response.text = lambda encoding=None: "" + response.headers["content-type"] = "text/plain" + response.content_type = "text/plain" + return response From cae0ce6b9844e35c8d2e0ebb53f811e7f208700a Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 24 Jun 2022 09:23:16 -0700 Subject: [PATCH 13/30] Updating sdk tests --- .../azure-communication-email/README.md | 159 +++++++++++++++++- .../dev_requirement.txt | 3 +- .../tests/_shared/testcase.py | 102 ----------- .../tests/async_preparers.py | 36 ++++ .../tests/conftest.py | 49 ++++++ .../tests/preparers.py | 17 ++ ...EmailClienttest_send_email_attachment.json | 56 ++++++ ...nttest_send_email_multiple_recipients.json | 53 ++++++ ...lienttest_send_email_single_recipient.json | 49 ++++++ ...ail_client_e2e.test_send_email_single.yaml | 53 ------ ...EmailClienttest_send_email_attachment.json | 55 ++++++ ...nttest_send_email_multiple_recipients.json | 52 ++++++ ...lienttest_send_email_single_recipient.json | 48 ++++++ .../tests/test_email_client.py | 69 -------- .../tests/test_email_client_e2e.py | 92 ++++++++-- .../tests/test_email_client_e2e_async.py | 99 +++++++++++ .../tests/unittest_helpers.py | 20 --- sdk/communication/ci.yml | 2 + 18 files changed, 750 insertions(+), 264 deletions(-) delete mode 100644 sdk/communication/azure-communication-email/tests/_shared/testcase.py create mode 100644 sdk/communication/azure-communication-email/tests/async_preparers.py create mode 100644 sdk/communication/azure-communication-email/tests/conftest.py create mode 100644 sdk/communication/azure-communication-email/tests/preparers.py create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json delete mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json delete mode 100644 sdk/communication/azure-communication-email/tests/test_email_client.py create mode 100644 sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py delete mode 100644 sdk/communication/azure-communication-email/tests/unittest_helpers.py diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index 38f1d4c0c53d..150594e21494 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -1 +1,158 @@ -# TODO: Populate this README \ No newline at end of file +# Azure Communication Email client library for Python + +This package contains a Python SDK for Azure Communication Services for Email. + +## Getting started + +### Prerequisites + +You need an [Azure subscription][azure_sub], a [Communication Service Resource][communication_resource_docs], and an [Email Communication Resource][email_resource_docs] with an active [Domain][domain_overview]. + +To create these resource, you can use the [Azure Portal][communication_resource_create_portal], the [Azure PowerShell][communication_resource_create_power_shell], or the [.NET management client library][communication_resource_create_net]. + +### Installing + +Install the Azure Communication Email client library for Python with [pip](https://pypi.org/project/pip/): + +```bash +pip install azure-communication-email +``` + +## Examples + +`EmailClient` provides the functionality to send email messages . + +## Authentication + +Email clients can be authenticated using the connection string acquired from an Azure Communication Resource in the [Azure Portal][azure_portal]. + +```python +from azure.communication.email import EmailClient + +connection_string = "endpoint=https://.communication.azure.com/;accessKey=" +client = EmailClient(connectionString); +``` + +### Send an Email Message + +To send an email message, call the `send` function from the `EmailClient`. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]) + ) + +response = client.send(message) +``` + +### Send an Email Message to Multiple Recipients + +To send an email message to multiple recipients, add a object for each recipient type and an object for each recipient. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +recipients = EmailRecipients( + to=[ + EmailAddress(email="customer@domain.com", display_name="Customer Name"), + EmailAddress(email="customer2@domain.com", display_name="Customer Name 2"), + ], + cc=[ + EmailAddress(email="ccCustomer@domain.com", display_name="CC Customer Name"), + EmailAddress(email="ccCustomer2@domain.com", display_name="CC Customer Name 2"), + ], + bcc=[ + EmailAddress(email="bccCustomer@domain.com", display_name="BCC Customer Name"), + EmailAddress(email="bccCustomer2@domain.com", display_name="BCC Customer Name 2"), + ] + ) + +message = EmailMessage(sender="sender@contoso.com", content=content, recipients=recipients) +response = client.send(message) +``` + +### Send Email with Attachments + +Azure Communication Services support sending email with attachments. + +```python +file = open("C://readme.txt", "r") +file_contents = file.read() +file.close() + +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64=base64.b64encode(file_contents) +) + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]), + attachments=[attachment] + ) + +response = client.send(message) +``` + +### Get Email Message Status + +The result from the `send` call contains a `message_id` which can be used to query the status of the email. + +```python +response = client.send(message) +status = client.get_sent_status(message_id) +``` + +## Next steps + +- [Read more about Email in Azure Communication Services][nextsteps] + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. + + + +[azure_sub]: https://azure.microsoft.com/free/dotnet/ +[azure_portal]: https://portal.azure.com +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com +[communication_resource_docs]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[email_resource_docs]: https://aka.ms/acsemail/createemailresource +[communication_resource_create_portal]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[communication_resource_create_power_shell]: https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice +[communication_resource_create_net]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-net +[package]: https://www.nuget.org/packages/Azure.Communication.Common/ +[product_docs]: https://aka.ms/acsemail/overview +[nextsteps]: https://aka.ms/acsemail/overview +[nuget]: https://www.nuget.org/ +[source]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/communication +[domain_overview]: https://aka.ms/acsemail/domainsoverview \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirement.txt index b8884941f2bd..8fd523934a46 100644 --- a/sdk/communication/azure-communication-email/dev_requirement.txt +++ b/sdk/communication/azure-communication-email/dev_requirement.txt @@ -4,4 +4,5 @@ ../../core/azure-core aiohttp>=3.0 aiounittest>=1.4 -pytest==7.1.2 \ No newline at end of file +pytest==7.1.2 +pytest-tornasync==0.6.0.post2 \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/_shared/testcase.py b/sdk/communication/azure-communication-email/tests/_shared/testcase.py deleted file mode 100644 index cf7fb7d2e14d..000000000000 --- a/sdk/communication/azure-communication-email/tests/_shared/testcase.py +++ /dev/null @@ -1,102 +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 os -import re -from devtools_testutils import AzureTestCase -from azure.communication.email._shared.utils import parse_connection_str -from azure_devtools.scenario_tests import RecordingProcessor, ReplayableTest -from azure_devtools.scenario_tests.utilities import is_text_payload - -class ResponseReplacerProcessor(RecordingProcessor): - def __init__(self, keys=None, replacement="sanitized"): - self._keys = keys if keys else [] - self._replacement = replacement - - def process_response(self, response): - def sanitize_dict(dictionary): - for key in dictionary: - value = dictionary[key] - if isinstance(value, str): - dictionary[key] = re.sub( - r"("+'|'.join(self._keys)+r")", - self._replacement, - dictionary[key]) - elif isinstance(value, dict): - sanitize_dict(value) - - sanitize_dict(response) - - return response - -class BodyReplacerProcessor(RecordingProcessor): - """Sanitize the sensitive info inside request or response bodies""" - - def __init__(self, keys=None, replacement="sanitized"): - self._replacement = replacement - self._keys = keys if keys else [] - - def process_request(self, request): - if is_text_payload(request) and request.body: - request.body = self._replace_keys(request.body.decode()).encode() - - return request - - def process_response(self, response): - if is_text_payload(response) and response['body']['string']: - response['body']['string'] = self._replace_keys(response['body']['string']) - - return response - - def _replace_keys(self, body): - def _replace_recursively(obj): - if isinstance(obj, dict): - for key in obj: - if key in self._keys: - obj[key] = self._replacement - else: - _replace_recursively(obj[key]) - elif isinstance(obj, list): - for i in obj: - _replace_recursively(i) - - import json - try: - body = json.loads(body) - _replace_recursively(body) - - except (KeyError, ValueError): - return body - - return json.dumps(body) - -class CommunicationTestCase(AzureTestCase): - # FILTER_HEADERS = ReplayableTest.FILTER_HEADERS + [ - # 'x-azure-ref', - # 'x-ms-content-sha256', - # 'location', - # # 'x-ms-date', - # # 'repeatability-first-sent', - # # 'repeatability-request-id', - # # 'operation-location', - # # 'date' - # ] - - def __init__(self, method_name, *args, **kwargs): - super(CommunicationTestCase, self).__init__(method_name, *args, **kwargs) - - def setUp(self): - super(CommunicationTestCase, self).setUp() - - # if self.is_playback(): - # self.connection_str = "endpoint=https://sanitized.communication.azure.com/;accesskey=fake===" - # else: - # self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') - # endpoint, _ = parse_connection_str(self.connection_str) - # self._resource_name = endpoint.split(".")[0] - # self.scrubber.register_name_pair(self._resource_name, "sanitized") - - self.connection_str = os.getenv('COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING') diff --git a/sdk/communication/azure-communication-email/tests/async_preparers.py b/sdk/communication/azure-communication-email/tests/async_preparers.py new file mode 100644 index 000000000000..aec31c005621 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/async_preparers.py @@ -0,0 +1,36 @@ +import os +from devtools_testutils import is_live + +def email_decorator_async(func, **kwargs): + async def wrapper(self, *args, **kwargs): + if is_live(): + self.communication_connection_string = os.environ["COMMUNICATION_CONNECTION_STRING"] + self.sender_address = os.environ["SENDER_ADDRESS"] + self.recipient_address = os.environ["RECIPIENT_ADDRESS"] + else: + self.communication_connection_string = "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==" + self.sender_address = "someSender@contoso.com" + self.recipient_address = "someRecipient@domain.com" + + EXPONENTIAL_BACKOFF = 1.5 + RETRY_COUNT = 0 + + try: + return await func(self, *args, **kwargs) + except HttpResponseError as exc: + if exc.status_code != 429: + raise + print("Retrying: {} {}".format(RETRY_COUNT, EXPONENTIAL_BACKOFF)) + while RETRY_COUNT < 6: + if is_live(): + time.sleep(EXPONENTIAL_BACKOFF) + try: + return await func(self, *args, **kwargs) + except HttpResponseError as exc: + print("Retrying: {} {}".format(RETRY_COUNT, EXPONENTIAL_BACKOFF)) + EXPONENTIAL_BACKOFF **= 2 + RETRY_COUNT += 1 + if exc.status_code != 429 or RETRY_COUNT >= 6: + raise + + return wrapper diff --git a/sdk/communication/azure-communication-email/tests/conftest.py b/sdk/communication/azure-communication-email/tests/conftest.py new file mode 100644 index 000000000000..c122d990f7b2 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/conftest.py @@ -0,0 +1,49 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import pytest +import os +from devtools_testutils import test_proxy, add_general_regex_sanitizer, add_header_regex_sanitizer, add_body_regex_sanitizer +from azure.communication.email._shared.utils import parse_connection_str + +@pytest.fixture(scope="session", autouse=True) +def add_sanitizers(test_proxy): + communication_connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING", "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==") + sender_address = os.getenv("SENDER_ADDRESS", "someSender@contoso.com") + recipient_address = os.getenv("RECIPIENT_ADDRESS", "someRecipient@domain.com") + + add_general_regex_sanitizer(regex=communication_connection_string, value="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==") + add_general_regex_sanitizer(regex=sender_address, value="someSender@contoso.com") + add_general_regex_sanitizer(regex=recipient_address, value="someRecipient@domain.com") + + endpoint, _ = parse_connection_str(communication_connection_string) + add_general_regex_sanitizer(regex=endpoint, value="https://someEndpoint") + + add_header_regex_sanitizer(key="repeatability-first-sent", value="sanitized") + add_header_regex_sanitizer(key="repeatability-request-id", value="sanitized") + add_header_regex_sanitizer(key="x-ms-content-sha256", value="sanitized") + add_header_regex_sanitizer(key="Operation-Location", value="https://someEndpoint/emails/someMessageId/status") + add_header_regex_sanitizer(key="Date", value="sanitized") + add_header_regex_sanitizer(key="x-azure-ref", value="sanitized") diff --git a/sdk/communication/azure-communication-email/tests/preparers.py b/sdk/communication/azure-communication-email/tests/preparers.py new file mode 100644 index 000000000000..e8bb262257bc --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/preparers.py @@ -0,0 +1,17 @@ +import os +from devtools_testutils import is_live + +def email_decorator(func, **kwargs): + def wrapper(self, *args, **kwargs): + if is_live(): + self.communication_connection_string = os.environ["COMMUNICATION_CONNECTION_STRING"] + self.sender_address = os.environ["SENDER_ADDRESS"] + self.recipient_address = os.environ["RECIPIENT_ADDRESS"] + else: + self.communication_connection_string = "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==" + self.sender_address = "someSender@contoso.com" + self.recipient_address = "someRecipient@domain.com" + + func(self, *args, **kwargs) + + return wrapper diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json new file mode 100644 index 000000000000..539de711bc8c --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json @@ -0,0 +1,56 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "355", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + }, + "attachments": [ + { + "name": "readme.txt", + "attachmentType": "txt", + "contentBytesBase64": "ZW1haWwgdGVzdCBhdHRhY2htZW50" + } + ] + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json new file mode 100644 index 000000000000..4fdfeab89f35 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -0,0 +1,53 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "308", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:47 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + }, + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name 2" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json new file mode 100644 index 000000000000..be357f4913bd --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json @@ -0,0 +1,49 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "235", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:46 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml deleted file mode 100644 index 8e9ff2be0c4d..000000000000 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.test_send_email_single.yaml +++ /dev/null @@ -1,53 +0,0 @@ -interactions: -- request: - body: '{"sender": "DoNotReply@266db372-a95a-494f-88b5-81ffd9e866af.azurecomm.net", - "content": {"subject": "This is the subject", "plainText": "This is the body"}, - "importance": "normal", "recipients": {"to": [{"email": "acseaastesting@gmail.com", - "displayName": "Customer Name"}]}}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '274' - Content-Type: - - application/json - User-Agent: - - azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0) - repeatability-first-sent: - - Thu, 16 Jun 2022 23:57:39 GMT - repeatability-request-id: - - e19daf10-3c5c-4b5a-84a2-6dcd31946e9a - x-ms-content-sha256: - - 5Efo+JDWFExoHqUXqWOtGVGR8b9UFPpSZvfMSo/0XCQ= - x-ms-date: - - Thu, 16 Jun 2022 23:57:39 GMT - x-ms-return-client-request-id: - - 'true' - method: POST - uri: https://email-js-sdk-recording-comm-2.communication.azure.com/emails:send?api-version=2021-10-01-preview - response: - body: - string: '' - headers: - api-supported-versions: - - 2021-10-01-preview - content-length: - - '0' - date: - - Thu, 16 Jun 2022 23:57:40 GMT - operation-location: - - https://email-js-sdk-recording-comm-2.communication.azure.com/emails/0ba65ac2-55da-4178-85b4-1d4f44c6fa84/status - repeatability-result: - - accepted - x-azure-ref: - - 0dMOrYgAAAAAaqONyzFwZSo1BsrwLQtafV1NURURHRTA4MTEAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx - x-cache: - - CONFIG_NOCACHE - status: - code: 202 - message: Accepted -version: 1 diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json new file mode 100644 index 000000000000..1077749edad0 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json @@ -0,0 +1,55 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "355", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + }, + "attachments": [ + { + "name": "readme.txt", + "attachmentType": "txt", + "contentBytesBase64": "ZW1haWwgdGVzdCBhdHRhY2htZW50" + } + ] + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json new file mode 100644 index 000000000000..9c8535417424 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -0,0 +1,52 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "308", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + }, + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name 2" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json new file mode 100644 index 000000000000..972c5dae3f3d --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json @@ -0,0 +1,48 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "235", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": null + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/test_email_client.py b/sdk/communication/azure-communication-email/tests/test_email_client.py deleted file mode 100644 index de4ba84d2835..000000000000 --- a/sdk/communication/azure-communication-email/tests/test_email_client.py +++ /dev/null @@ -1,69 +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 unittest -from unittest.mock import Mock - -from unittest_helpers import mock_response -from azure.communication.email import ( - EmailClient, - EmailMessage, - EmailContent, - EmailRecipients, - EmailAddress -) - - -class TestEmailClient(unittest.TestCase): - def test_send(self): - - message = EmailMessage( - sender="someSender@contoso.com", - content=EmailContent(subject="This is the subject", plain_text="This is the body"), - recipients=EmailRecipients( - to=[EmailAddress(email="someRecipient@domain.com", display_name="Customer Name")] - ) - ) - - def mock_send(*_, **__): - return mock_response(status_code=202, headers={ - 'x-ms-request-id': "testMessageId" - }) - - email_client = EmailClient( - conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", - transport = Mock(send=mock_send) - ) - - response = None - raised = False - try: - response = email_client.send(message) - except: - raised = True - raise - - self.assertFalse(raised, 'Expected is no exception raised') - self.assertIsNotNone(response.message_id) - - def test_get_send_status(self): - - def mock_get_send_status(*_, **__): - return mock_response(status_code=200, json_payload={"test": "test"}) - - email_client = EmailClient( - conn_str="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==", - transport = Mock(send=mock_get_send_status) - ) - response = None - raised = False - try: - response = email_client.get_send_status("testMessageId") - except: - raised = True - raise - - self.assertFalse(raised, 'Expected is no exception raised') - self.assertIsNotNone(response) \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py index 4f6a2fe7cd59..a8c317edf2a8 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -1,36 +1,92 @@ -import os from azure.communication.email import ( EmailClient, EmailMessage, EmailContent, EmailRecipients, - EmailAddress -) -from _shared.testcase import ( - CommunicationTestCase, + EmailAddress, + EmailAttachment ) +from devtools_testutils import AzureRecordedTestCase, recorded_by_proxy +from preparers import email_decorator + +class TestEmailClient(AzureRecordedTestCase): + # TODO: Change the assert statements once x-ms-request-id change is merged in + @email_decorator + @recorded_by_proxy + def test_send_email_single_recipient(self): + email_client = EmailClient(self.communication_connection_string) -class EmailClientTest(CommunicationTestCase): - def __init__(self, method_name): - super(EmailClientTest, self).__init__(method_name) + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) - def setUp(self): - super(EmailClientTest, self).setUp() + response = email_client.send(message) + assert response is not None - self.sender_address = os.getenv("SENDER_ADDRESS") - self.recipient_address = os.getenv("RECIPIENT_ADDRESS") + @email_decorator + @recorded_by_proxy + def test_send_email_multiple_recipients(self): + email_client = EmailClient(self.communication_connection_string) - def test_send_email_single(self): - email_client = EmailClient(self.connection_str) - message = EmailMessage( sender=self.sender_address, content=EmailContent(subject="This is the subject", plain_text="This is the body"), recipients=EmailRecipients( - to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.recipient_address, display_name="Customer Name 2"), + ] ) ) response = email_client.send(message) - print(response) - assert response is None \ No newline at end of file + assert response is not None + + @email_decorator + @recorded_by_proxy + def test_send_email_attachment(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ), + attachments=[ + EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + ] + ) + + response = email_client.send(message) + assert response is not None + + # TODO: Comment back in once the x-ms-request-id change is merged in + # @email_decorator + # @recorded_by_proxy + # def test_check_message_status(self): + # email_client = EmailClient(self.communication_connection_string) + + # message = EmailMessage( + # sender=self.sender_address, + # content=EmailContent(subject="This is the subject", plain_text="This is the body"), + # recipients=EmailRecipients( + # to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + # ) + # ) + + # response = email_client.send(message) + # message_id = response.message_id + # if message_id is not None: + # message_status_response = email_client.get_send_status(message_id) + # assert message_status_response.status is not None + # else: + # assert False diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py new file mode 100644 index 000000000000..718a90fb6465 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py @@ -0,0 +1,99 @@ +import pytest + +from azure.communication.email.aio import EmailClient +from azure.communication.email import ( + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment +) +from devtools_testutils import AzureRecordedTestCase +from devtools_testutils.aio import recorded_by_proxy_async +from async_preparers import email_decorator_async + +class TestEmailClient(AzureRecordedTestCase): + # TODO: Change the assert statements once x-ms-request-id change is merged in + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_single_recipient(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) + + async with email_client: + response = await email_client.send(message) + assert response is not None + + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_multiple_recipients(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.recipient_address, display_name="Customer Name 2"), + ] + ) + ) + + async with email_client: + response = await email_client.send(message) + assert response is not None + + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_attachment(self): + email_client = EmailClient(self.communication_connection_string) + + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ), + attachments=[ + EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + ) + ] + ) + + async with email_client: + response = await email_client.send(message) + assert response is not None + + # TODO: Comment back in once the x-ms-request-id change is merged in + # @email_decorator_async + # @recorded_by_proxy_async + # async def test_check_message_status(self): + # email_client = EmailClient(self.communication_connection_string) + + # message = EmailMessage( + # sender=self.sender_address, + # content=EmailContent(subject="This is the subject", plain_text="This is the body"), + # recipients=EmailRecipients( + # to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + # ) + # ) + + # async with email_client: + # response = await email_client.send(message) + # message_id = response.message_id + # if message_id is not None: + # message_status_response = await email_client.get_send_status(message_id) + # assert message_status_response.status is not None + # else: + # assert False diff --git a/sdk/communication/azure-communication-email/tests/unittest_helpers.py b/sdk/communication/azure-communication-email/tests/unittest_helpers.py deleted file mode 100644 index 9d24a0aa86eb..000000000000 --- a/sdk/communication/azure-communication-email/tests/unittest_helpers.py +++ /dev/null @@ -1,20 +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 json - -from unittest import mock - -def mock_response(status_code=200, headers=None, json_payload=None): - response = mock.Mock(status_code=status_code, headers=headers or {}) - if json_payload is not None: - response.text = lambda encoding=None: json.dumps(json_payload) - response.headers["content-type"] = "application/json" - response.content_type = "application/json" - else: - response.text = lambda encoding=None: "" - response.headers["content-type"] = "text/plain" - response.content_type = "text/plain" - return response diff --git a/sdk/communication/ci.yml b/sdk/communication/ci.yml index 40619c505859..fee3c672cb36 100644 --- a/sdk/communication/ci.yml +++ b/sdk/communication/ci.yml @@ -35,6 +35,8 @@ extends: safeName: azurecommunicationidentity - name: azure-communication-chat safeName: azurecommunicationchat + - name: azure-communication-email + safeName: azurecommunicationemail - name: azure-mgmt-communication safeName: azuremgmtcommunication - name: azure-communication-sms From fb90751b788a164c6b9fbee52300fb766e91a2f5 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 24 Jun 2022 09:23:16 -0700 Subject: [PATCH 14/30] Updating constructor and fixing linting errors --- .../azure-communication-email/README.md | 22 +++++++++- .../azure/communication/email/__init__.py | 2 +- .../communication/email/_email_client.py | 38 +++++++++++++---- .../azure/communication/email/_version.py | 2 +- .../email/aio/_email_client_async.py | 42 +++++++++++++++---- .../dev_requirement.txt | 3 +- .../dev_requirements.txt | 8 ++++ .../samples/check_message_status_sample.py | 2 +- .../check_message_status_sample_async.py | 4 +- ...end_email_to_multiple_recipients_sample.py | 2 +- ...ail_to_multiple_recipients_sample_async.py | 2 +- .../send_email_to_single_recipient_sample.py | 2 +- ..._email_to_single_recipient_sample_async.py | 2 +- .../send_email_with_attachments_sample.py | 2 +- ...end_email_with_attachments_sample_async.py | 2 +- .../azure-communication-email/setup.py | 2 +- ...EmailClienttest_send_email_attachment.json | 2 +- ...nttest_send_email_multiple_recipients.json | 2 +- ...lienttest_send_email_single_recipient.json | 2 +- ...EmailClienttest_send_email_attachment.json | 2 +- ...nttest_send_email_multiple_recipients.json | 2 +- ...lienttest_send_email_single_recipient.json | 2 +- .../tests/test_email_client_e2e.py | 8 ++-- .../tests/test_email_client_e2e_async.py | 8 ++-- sdk/communication/ci.yml | 1 + shared_requirements.txt | 1 + 26 files changed, 121 insertions(+), 46 deletions(-) create mode 100644 sdk/communication/azure-communication-email/dev_requirements.txt diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index 150594e21494..39f972a197f6 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -2,6 +2,12 @@ This package contains a Python SDK for Azure Communication Services for Email. +## Key concepts + +The Azure Communication Email package is used to do following: +- Send emails to multiple types of recipients +- Query the status of a sent email message + ## Getting started ### Prerequisites @@ -30,7 +36,7 @@ Email clients can be authenticated using the connection string acquired from an from azure.communication.email import EmailClient connection_string = "endpoint=https://.communication.azure.com/;accessKey=" -client = EmailClient(connectionString); +client = EmailClient.from_connection_string(connection_string); ``` ### Send an Email Message @@ -127,6 +133,18 @@ response = client.send(message) status = client.get_sent_status(message_id) ``` +## Troubleshooting + +Email operations will throw an exception if the request to the server fails. The Email client will raise exceptions defined in [Azure Core](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/README.md). + +```Python +try: + response = email_client.send(message) +except Exception as ex: + print('Exception:') + print(ex) +``` + ## Next steps - [Read more about Email in Azure Communication Services][nextsteps] @@ -155,4 +173,4 @@ This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For m [nextsteps]: https://aka.ms/acsemail/overview [nuget]: https://www.nuget.org/ [source]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/communication -[domain_overview]: https://aka.ms/acsemail/domainsoverview \ No newline at end of file +[domain_overview]: https://aka.ms/acsemail/domainsoverview diff --git a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py index f7befc593db1..9c22b889a185 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py @@ -27,4 +27,4 @@ 'SendEmailResult', 'SendStatus', 'SendStatusResult', -] \ No newline at end of file +] diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py index c87a337f562b..71b4a9dff304 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -1,3 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + from uuid import uuid4 from azure.core.tracing.decorator import distributed_trace from ._shared.utils import parse_connection_str, get_current_utc_time @@ -6,23 +12,24 @@ from ._version import SDK_MONIKER from ._generated.models import SendEmailResult, SendStatusResult, EmailMessage -class EmailClient(object): +class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword """A client to interact with the AzureCommunicationService Email gateway. This client provides operations to send an email and monitor its status. - :param str conn_string: - The connection string to connect to an Azure Communication Service resource. - Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param TokenCredential credential: + The TokenCredential we use to authenticate against the service. """ def __init__( self, - conn_str, # type: str + endpoint, # type: str + credential, # type: str **kwargs # type: Any ): # type: (...) -> None - endpoint, access_key = parse_connection_str(conn_str) - authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + authentication_policy = HMACCredentialsPolicy(endpoint, credential) self._generated_client = AzureCommunicationEmailService( endpoint, @@ -30,6 +37,23 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) + + @classmethod + def from_connection_string( + cls, + conn_str, # type: str + **kwargs # type: Any + ): # type: (...) -> EmailClient + """Create EmailClient from a Connection String. + + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of EmailClient. + :rtype: ~azure.communication.EmailClient + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) @distributed_trace def send( diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_version.py b/sdk/communication/azure-communication-email/azure/communication/email/_version.py index 41f0bacc9706..eadf444aa551 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_version.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_version.py @@ -8,4 +8,4 @@ VERSION = "1.0.0b1" -SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str \ No newline at end of file +SDK_MONIKER = "communication-email/{}".format(VERSION) # type: str diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py index d89b789dedf1..acc414758195 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -1,3 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + from uuid import uuid4 from azure.core.tracing.decorator_async import distributed_trace_async from .._shared.utils import parse_connection_str, get_current_utc_time @@ -6,23 +12,24 @@ from .._version import SDK_MONIKER from .._generated.models import SendEmailResult, SendStatusResult, EmailMessage -class EmailClient(object): +class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword """A client to interact with the AzureCommunicationService Email gateway asynchronously. This client provides operations to send an email and monitor its status. - :param str conn_string: - The connection string to connect to an Azure Communication Service resource. - Example: "endpoint=https://contoso.eastus.communications.azure.net/;accesskey=secret"; + :param str endpoint: + The endpoint url for Azure Communication Service resource. + :param TokenCredential credential: + The TokenCredential we use to authenticate against the service. """ def __init__( self, - conn_str, # type: str + endpoint, # type: str + credential, # type: str **kwargs # type: Any ): # type: (...) -> None - endpoint, access_key = parse_connection_str(conn_str) - authentication_policy = HMACCredentialsPolicy(endpoint, access_key) + authentication_policy = HMACCredentialsPolicy(endpoint, credential) self._generated_client = AzureCommunicationEmailService( endpoint, @@ -30,6 +37,23 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) + + @classmethod + def from_connection_string( + cls, + conn_str, # type: str + **kwargs # type: Any + ): # type: (...) -> EmailClient + """Create EmailClient from a Connection String. + + :param str conn_str: + A connection string to an Azure Communication Service resource. + :returns: Instance of EmailClient. + :rtype: ~azure.communication.EmailClient + """ + endpoint, access_key = parse_connection_str(conn_str) + + return cls(endpoint, access_key, **kwargs) @distributed_trace_async async def send( @@ -51,7 +75,7 @@ async def send( email_message=email_message, **kwargs ) - + @distributed_trace_async async def get_send_status( self, @@ -79,4 +103,4 @@ async def __aexit__(self, *args) -> None: await self._generated_client.__aexit__(*args) async def close(self) -> None: - await self._generated_client.close() \ No newline at end of file + await self._generated_client.close() diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirement.txt index 8fd523934a46..b8884941f2bd 100644 --- a/sdk/communication/azure-communication-email/dev_requirement.txt +++ b/sdk/communication/azure-communication-email/dev_requirement.txt @@ -4,5 +4,4 @@ ../../core/azure-core aiohttp>=3.0 aiounittest>=1.4 -pytest==7.1.2 -pytest-tornasync==0.6.0.post2 \ No newline at end of file +pytest==7.1.2 \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/dev_requirements.txt b/sdk/communication/azure-communication-email/dev_requirements.txt new file mode 100644 index 000000000000..8fd523934a46 --- /dev/null +++ b/sdk/communication/azure-communication-email/dev_requirements.txt @@ -0,0 +1,8 @@ +-e ../../../tools/azure-sdk-tools +-e ../../../tools/azure-devtools +-e ../../identity/azure-identity +../../core/azure-core +aiohttp>=3.0 +aiounittest>=1.4 +pytest==7.1.2 +pytest-tornasync==0.6.0.post2 \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py index 4d161eea14ab..d249d646cedc 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py @@ -39,7 +39,7 @@ class EmailCheckMessageStatusSample(object): def check_message_status(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py index d294ab6d52e5..d0a6b6278e3a 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py @@ -40,7 +40,7 @@ class EmailCheckMessageStatusSampleAsync(object): async def check_message_status_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( @@ -79,4 +79,4 @@ async def check_message_status_async(self): # Comment in this line if you are running this sample on Windows # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - asyncio.run(sample.check_message_status_async()) \ No newline at end of file + asyncio.run(sample.check_message_status_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py index 4c709356b27e..2f365d626228 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py @@ -41,7 +41,7 @@ class EmailMultipleRecipientSample(object): def send_email_to_multiple_recipients(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py index e77525bd3736..0b43bd6689a3 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -42,7 +42,7 @@ class EmailMultipleRecipientSampleAsync(object): async def send_email_to_multiple_recipients_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py index d7c58d33cd74..0dc9f3112415 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py @@ -39,7 +39,7 @@ class EmailSingleRecipientSample(object): def send_email_to_single_recipient(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py index be15a68610f5..56dc2ca69966 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py @@ -40,7 +40,7 @@ class EmailSingleRecipientSampleAsync(object): async def send_email_to_single_recipient_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py index 7dc5c180866c..4994666f6b92 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -40,7 +40,7 @@ class EmailWithAttachmentSample(object): def send_email_with_attachment(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py index ad6e14209064..2654a67fd0d0 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -41,7 +41,7 @@ class EmailWithAttachmentSampleAsync(object): async def send_email_with_attachment_async(self): # creating the email client - email_client = EmailClient(self.connection_string) + email_client = EmailClient.from_connection_string(self.connection_string) # creating the email message content = EmailContent( diff --git a/sdk/communication/azure-communication-email/setup.py b/sdk/communication/azure-communication-email/setup.py index b08b8ab01133..e16f16f68b8c 100644 --- a/sdk/communication/azure-communication-email/setup.py +++ b/sdk/communication/azure-communication-email/setup.py @@ -61,7 +61,7 @@ 'pytyped': ['py.typed'], }, install_requires=[ - 'azure-core<2.0.0,>=1.15.0', + 'azure-core<2.0.0,>=1.23.0', 'msrest>=0.6.21', 'six>=1.11.0', ], diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json index 539de711bc8c..cd2e134a56b4 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json @@ -13,7 +13,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json index 4fdfeab89f35..02d02db09d8d 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -13,7 +13,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:47 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:47 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json index be357f4913bd..359dd7f050a7 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json @@ -13,7 +13,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:46 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:47 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json index 1077749edad0..a725781e858c 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json @@ -12,7 +12,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json index 9c8535417424..3f18dae2f504 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -12,7 +12,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json index 972c5dae3f3d..37459430bf1c 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json @@ -12,7 +12,7 @@ "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 00:31:48 GMT", + "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py index a8c317edf2a8..98d136c719ca 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -14,7 +14,7 @@ class TestEmailClient(AzureRecordedTestCase): @email_decorator @recorded_by_proxy def test_send_email_single_recipient(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -30,7 +30,7 @@ def test_send_email_single_recipient(self): @email_decorator @recorded_by_proxy def test_send_email_multiple_recipients(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -49,7 +49,7 @@ def test_send_email_multiple_recipients(self): @email_decorator @recorded_by_proxy def test_send_email_attachment(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -73,7 +73,7 @@ def test_send_email_attachment(self): # @email_decorator # @recorded_by_proxy # def test_check_message_status(self): - # email_client = EmailClient(self.communication_connection_string) + # email_client = EmailClient.from_connection_string(self.communication_connection_string) # message = EmailMessage( # sender=self.sender_address, diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py index 718a90fb6465..da4dcfd90d60 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py @@ -17,7 +17,7 @@ class TestEmailClient(AzureRecordedTestCase): @email_decorator_async @recorded_by_proxy_async async def test_send_email_single_recipient(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -34,7 +34,7 @@ async def test_send_email_single_recipient(self): @email_decorator_async @recorded_by_proxy_async async def test_send_email_multiple_recipients(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -54,7 +54,7 @@ async def test_send_email_multiple_recipients(self): @email_decorator_async @recorded_by_proxy_async async def test_send_email_attachment(self): - email_client = EmailClient(self.communication_connection_string) + email_client = EmailClient.from_connection_string(self.communication_connection_string) message = EmailMessage( sender=self.sender_address, @@ -79,7 +79,7 @@ async def test_send_email_attachment(self): # @email_decorator_async # @recorded_by_proxy_async # async def test_check_message_status(self): - # email_client = EmailClient(self.communication_connection_string) + # email_client = EmailClient.from_connection_string(self.communication_connection_string) # message = EmailMessage( # sender=self.sender_address, diff --git a/sdk/communication/ci.yml b/sdk/communication/ci.yml index fee3c672cb36..59ea57e5cbe9 100644 --- a/sdk/communication/ci.yml +++ b/sdk/communication/ci.yml @@ -30,6 +30,7 @@ extends: template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml parameters: ServiceDirectory: communication + TestProxy: true Artifacts: - name: azure-communication-identity safeName: azurecommunicationidentity diff --git a/shared_requirements.txt b/shared_requirements.txt index 7de2bf65a04b..fc244596f9c8 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -196,6 +196,7 @@ opentelemetry-sdk<2.0.0,>=1.5.0,!=1.10a0 #override azure-communication-phonenumbers azure-core<2.0.0,>=1.15.0 #override azure-communication-identity azure-core<2.0.0,>=1.19.1 #override azure-communication-networktraversal azure-core<2.0.0,>=1.19.1 +#override azure-communication-email azure-core<2.0.0,>=1.23.0 #override azure-mgmt-communication azure-core<2.0.0,>=1.9.0 #override azure-ai-metricsadvisor azure-core<2.0.0,>=1.23.0 #override azure-ai-translation-document azure-core<2.0.0,>=1.14.0 From cee2e085ee9b1a95f6dc26c9aa093217cd25d158 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 24 Jun 2022 09:35:55 -0700 Subject: [PATCH 15/30] Adding dev requirements --- .../azure/communication/email/_email_client.py | 2 +- .../azure-communication-email/dev_requirement.txt | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 sdk/communication/azure-communication-email/dev_requirement.txt diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py index 71b4a9dff304..44751fd3cd9a 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -75,7 +75,7 @@ def send( email_message=email_message, **kwargs ) - + @distributed_trace def get_send_status( self, diff --git a/sdk/communication/azure-communication-email/dev_requirement.txt b/sdk/communication/azure-communication-email/dev_requirement.txt deleted file mode 100644 index b8884941f2bd..000000000000 --- a/sdk/communication/azure-communication-email/dev_requirement.txt +++ /dev/null @@ -1,7 +0,0 @@ --e ../../../tools/azure-sdk-tools --e ../../../tools/azure-devtools --e ../../identity/azure-identity -../../core/azure-core -aiohttp>=3.0 -aiounittest>=1.4 -pytest==7.1.2 \ No newline at end of file From 50f955e9e7bde8ec62519d9a8838ea53f546e04c Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 24 Jun 2022 09:36:50 -0700 Subject: [PATCH 16/30] Removing pytest from dev_requirements.txt --- .../azure/communication/email/_email_client.py | 2 +- .../azure/communication/email/aio/_email_client_async.py | 2 +- .../azure-communication-email/dev_requirements.txt | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py index 44751fd3cd9a..ae2e9ee8baec 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -37,7 +37,7 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) - + @classmethod def from_connection_string( cls, diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py index acc414758195..0ed6c8c94d5d 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -37,7 +37,7 @@ def __init__( sdk_moniker=SDK_MONIKER, **kwargs ) - + @classmethod def from_connection_string( cls, diff --git a/sdk/communication/azure-communication-email/dev_requirements.txt b/sdk/communication/azure-communication-email/dev_requirements.txt index 8fd523934a46..733dcf452e64 100644 --- a/sdk/communication/azure-communication-email/dev_requirements.txt +++ b/sdk/communication/azure-communication-email/dev_requirements.txt @@ -4,5 +4,4 @@ ../../core/azure-core aiohttp>=3.0 aiounittest>=1.4 -pytest==7.1.2 pytest-tornasync==0.6.0.post2 \ No newline at end of file From e98b324f451950c6e2403e6d02baed9780f947b8 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 24 Jun 2022 09:38:18 -0700 Subject: [PATCH 17/30] Adding policy file back into sms module --- .../azure/communication/sms/_shared/policy.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py diff --git a/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py b/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py new file mode 100644 index 000000000000..c38a8ed92f3e --- /dev/null +++ b/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py @@ -0,0 +1,91 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import hashlib +import urllib +import base64 +import hmac +from azure.core.pipeline.policies import SansIOHTTPPolicy +from .utils import get_current_utc_time + +class HMACCredentialsPolicy(SansIOHTTPPolicy): + """Implementation of HMAC authentication policy. + """ + + def __init__(self, + host, # type: str + access_key, # type: str + decode_url=False # type: bool + ): + # type: (...) -> None + super(HMACCredentialsPolicy, self).__init__() + + if host.startswith("https://"): + self._host = host.replace("https://", "") + + if host.startswith("http://"): + self._host = host.replace("http://", "") + + self._access_key = access_key + self._decode_url = decode_url + + def _compute_hmac(self, + value # type: str + ): + decoded_secret = base64.b64decode(self._access_key) + digest = hmac.new( + decoded_secret, value.encode("utf-8"), hashlib.sha256 + ).digest() + + return base64.b64encode(digest).decode("utf-8") + + def _sign_request(self, request): + verb = request.http_request.method.upper() + + # Get the path and query from url, which looks like https://host/path/query + query_url = str(request.http_request.url[len(self._host) + 8:]) + + if self._decode_url: + query_url = urllib.parse.unquote(query_url) + + signed_headers = "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) \ No newline at end of file From b2989731b0ed375a39cdcd1c927e66b04f6121a8 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 24 Jun 2022 09:38:19 -0700 Subject: [PATCH 18/30] Adding newline to policy file --- .../azure/communication/sms/_shared/policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py b/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py index c38a8ed92f3e..d4197ede0e38 100644 --- a/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py +++ b/sdk/communication/azure-communication-sms/azure/communication/sms/_shared/policy.py @@ -88,4 +88,4 @@ def _sign_request(self, request): return request def on_request(self, request): - self._sign_request(request) \ No newline at end of file + self._sign_request(request) From d3af5267b96ebce6eb188862a5071e602334ce21 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Fri, 24 Jun 2022 14:22:49 -0700 Subject: [PATCH 19/30] Updating tests with message id --- .../tests/conftest.py | 9 +- ...tEmailClienttest_check_message_status.json | 82 +++++++++++++++++++ ...EmailClienttest_send_email_attachment.json | 7 +- ...nttest_send_email_multiple_recipients.json | 7 +- ...lienttest_send_email_single_recipient.json | 7 +- ...tEmailClienttest_check_message_status.json | 81 ++++++++++++++++++ ...EmailClienttest_send_email_attachment.json | 7 +- ...nttest_send_email_multiple_recipients.json | 7 +- ...lienttest_send_email_single_recipient.json | 7 +- .../tests/test_email_client_e2e.py | 44 +++++----- .../tests/test_email_client_e2e_async.py | 46 +++++------ 11 files changed, 244 insertions(+), 60 deletions(-) create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_check_message_status.json create mode 100644 sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_check_message_status.json diff --git a/sdk/communication/azure-communication-email/tests/conftest.py b/sdk/communication/azure-communication-email/tests/conftest.py index c122d990f7b2..8e8387e7fbd6 100644 --- a/sdk/communication/azure-communication-email/tests/conftest.py +++ b/sdk/communication/azure-communication-email/tests/conftest.py @@ -25,11 +25,13 @@ # -------------------------------------------------------------------------- import pytest import os -from devtools_testutils import test_proxy, add_general_regex_sanitizer, add_header_regex_sanitizer, add_body_regex_sanitizer +from devtools_testutils import test_proxy, add_general_regex_sanitizer, add_header_regex_sanitizer, set_default_settings, add_uri_regex_sanitizer from azure.communication.email._shared.utils import parse_connection_str @pytest.fixture(scope="session", autouse=True) def add_sanitizers(test_proxy): + set_default_settings() + communication_connection_string = os.getenv("COMMUNICATION_CONNECTION_STRING", "endpoint=https://someEndpoint/;accesskey=someAccessKeyw==") sender_address = os.getenv("SENDER_ADDRESS", "someSender@contoso.com") recipient_address = os.getenv("RECIPIENT_ADDRESS", "someRecipient@domain.com") @@ -37,6 +39,7 @@ def add_sanitizers(test_proxy): add_general_regex_sanitizer(regex=communication_connection_string, value="endpoint=https://someEndpoint/;accesskey=someAccessKeyw==") add_general_regex_sanitizer(regex=sender_address, value="someSender@contoso.com") add_general_regex_sanitizer(regex=recipient_address, value="someRecipient@domain.com") + add_general_regex_sanitizer(regex='"messageId":.*,', value='"messageId": "someMessageId",') endpoint, _ = parse_connection_str(communication_connection_string) add_general_regex_sanitizer(regex=endpoint, value="https://someEndpoint") @@ -46,4 +49,8 @@ def add_sanitizers(test_proxy): add_header_regex_sanitizer(key="x-ms-content-sha256", value="sanitized") add_header_regex_sanitizer(key="Operation-Location", value="https://someEndpoint/emails/someMessageId/status") add_header_regex_sanitizer(key="Date", value="sanitized") + add_header_regex_sanitizer(key="x-ms-request-id", value="someMessageId") + add_header_regex_sanitizer(key="x-ms-client-request-id", value="sanitized") add_header_regex_sanitizer(key="x-azure-ref", value="sanitized") + + add_uri_regex_sanitizer(regex="emails/.*/", value="emails/someMessageId/") diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_check_message_status.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_check_message_status.json new file mode 100644 index 000000000000..aad8bb1039b7 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_check_message_status.json @@ -0,0 +1,82 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", + "Connection": "keep-alive", + "Content-Length": "235", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Fri, 24 Jun 2022 21:20:56 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-request-id": "someMessageId" + }, + "ResponseBody": null + }, + { + "RequestUri": "https://someEndpoint/emails/someMessageId/status?api-version=2021-10-01-preview", + "RequestMethod": "GET", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", + "Connection": "keep-alive", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Fri, 24 Jun 2022 21:20:56 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "48", + "Content-Type": "application/json; charset=utf-8", + "Date": "sanitized", + "Retry-After": "60", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": { + "messageId": "someMessageId", + "status": "Queued" + } + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json index cd2e134a56b4..a9671d91d6dd 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json @@ -6,14 +6,16 @@ "RequestHeaders": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", "Connection": "keep-alive", "Content-Length": "355", "Content-Type": "application/json", "repeatability-first-sent": "sanitized", "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", + "x-ms-date": "Fri, 24 Jun 2022 21:20:56 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { @@ -47,7 +49,8 @@ "Operation-Location": "https://someEndpoint/emails/someMessageId/status", "Repeatability-Result": "accepted", "X-Azure-Ref": "sanitized", - "X-Cache": "CONFIG_NOCACHE" + "X-Cache": "CONFIG_NOCACHE", + "x-ms-request-id": "someMessageId" }, "ResponseBody": null } diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json index 02d02db09d8d..2d5a82ccc01e 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -6,14 +6,16 @@ "RequestHeaders": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", "Connection": "keep-alive", "Content-Length": "308", "Content-Type": "application/json", "repeatability-first-sent": "sanitized", "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 18:29:47 GMT", + "x-ms-date": "Fri, 24 Jun 2022 21:20:56 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { @@ -44,7 +46,8 @@ "Operation-Location": "https://someEndpoint/emails/someMessageId/status", "Repeatability-Result": "accepted", "X-Azure-Ref": "sanitized", - "X-Cache": "CONFIG_NOCACHE" + "X-Cache": "CONFIG_NOCACHE", + "x-ms-request-id": "someMessageId" }, "ResponseBody": null } diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json index 359dd7f050a7..1b20f55c0e03 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.json @@ -6,14 +6,16 @@ "RequestHeaders": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", "Connection": "keep-alive", "Content-Length": "235", "Content-Type": "application/json", "repeatability-first-sent": "sanitized", "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 18:29:47 GMT", + "x-ms-date": "Fri, 24 Jun 2022 21:20:55 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { @@ -40,7 +42,8 @@ "Operation-Location": "https://someEndpoint/emails/someMessageId/status", "Repeatability-Result": "accepted", "X-Azure-Ref": "sanitized", - "X-Cache": "CONFIG_NOCACHE" + "X-Cache": "CONFIG_NOCACHE", + "x-ms-request-id": "someMessageId" }, "ResponseBody": null } diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_check_message_status.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_check_message_status.json new file mode 100644 index 000000000000..6f5b1317694f --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_check_message_status.json @@ -0,0 +1,81 @@ +{ + "Entries": [ + { + "RequestUri": "https://someEndpoint/emails:send?api-version=2021-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", + "Content-Length": "235", + "Content-Type": "application/json", + "repeatability-first-sent": "sanitized", + "repeatability-request-id": "sanitized", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Fri, 24 Jun 2022 21:20:57 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "sender": "someSender@contoso.com", + "content": { + "subject": "This is the subject", + "plainText": "This is the body" + }, + "importance": "normal", + "recipients": { + "to": [ + { + "email": "someRecipient@domain.com", + "displayName": "Customer Name" + } + ] + } + }, + "StatusCode": 202, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "0", + "Date": "sanitized", + "Operation-Location": "https://someEndpoint/emails/someMessageId/status", + "Repeatability-Result": "accepted", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-request-id": "someMessageId" + }, + "ResponseBody": null + }, + { + "RequestUri": "https://someEndpoint/emails/someMessageId/status?api-version=2021-10-01-preview", + "RequestMethod": "GET", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", + "Content-Length": "0", + "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", + "x-ms-content-sha256": "sanitized", + "x-ms-date": "Fri, 24 Jun 2022 21:20:57 GMT", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "api-supported-versions": "2021-10-01-preview", + "Content-Length": "48", + "Content-Type": "application/json; charset=utf-8", + "Date": "sanitized", + "Retry-After": "60", + "X-Azure-Ref": "sanitized", + "X-Cache": "CONFIG_NOCACHE" + }, + "ResponseBody": { + "messageId": "someMessageId", + "status": "Queued" + } + } + ], + "Variables": {} +} diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json index a725781e858c..bc656dc8c6a7 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json @@ -6,13 +6,15 @@ "RequestHeaders": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", "Content-Length": "355", "Content-Type": "application/json", "repeatability-first-sent": "sanitized", "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", + "x-ms-date": "Fri, 24 Jun 2022 21:20:57 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { @@ -46,7 +48,8 @@ "Operation-Location": "https://someEndpoint/emails/someMessageId/status", "Repeatability-Result": "accepted", "X-Azure-Ref": "sanitized", - "X-Cache": "CONFIG_NOCACHE" + "X-Cache": "CONFIG_NOCACHE", + "x-ms-request-id": "someMessageId" }, "ResponseBody": null } diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json index 3f18dae2f504..51e3b1c904fe 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.json @@ -6,13 +6,15 @@ "RequestHeaders": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", "Content-Length": "308", "Content-Type": "application/json", "repeatability-first-sent": "sanitized", "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", + "x-ms-date": "Fri, 24 Jun 2022 21:20:56 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { @@ -43,7 +45,8 @@ "Operation-Location": "https://someEndpoint/emails/someMessageId/status", "Repeatability-Result": "accepted", "X-Azure-Ref": "sanitized", - "X-Cache": "CONFIG_NOCACHE" + "X-Cache": "CONFIG_NOCACHE", + "x-ms-request-id": "someMessageId" }, "ResponseBody": null } diff --git a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json index 37459430bf1c..8c72e1488763 100644 --- a/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json @@ -6,13 +6,15 @@ "RequestHeaders": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate", + "Authorization": "Sanitized", "Content-Length": "235", "Content-Type": "application/json", "repeatability-first-sent": "sanitized", "repeatability-request-id": "sanitized", "User-Agent": "azsdk-python-communication-email/1.0.0b1 Python/3.10.4 (Windows-10-10.0.19044-SP0)", + "x-ms-client-request-id": "sanitized", "x-ms-content-sha256": "sanitized", - "x-ms-date": "Thu, 23 Jun 2022 18:29:48 GMT", + "x-ms-date": "Fri, 24 Jun 2022 21:20:56 GMT", "x-ms-return-client-request-id": "true" }, "RequestBody": { @@ -39,7 +41,8 @@ "Operation-Location": "https://someEndpoint/emails/someMessageId/status", "Repeatability-Result": "accepted", "X-Azure-Ref": "sanitized", - "X-Cache": "CONFIG_NOCACHE" + "X-Cache": "CONFIG_NOCACHE", + "x-ms-request-id": "someMessageId" }, "ResponseBody": null } diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py index 98d136c719ca..23724b825d7c 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -10,7 +10,6 @@ from preparers import email_decorator class TestEmailClient(AzureRecordedTestCase): - # TODO: Change the assert statements once x-ms-request-id change is merged in @email_decorator @recorded_by_proxy def test_send_email_single_recipient(self): @@ -25,7 +24,7 @@ def test_send_email_single_recipient(self): ) response = email_client.send(message) - assert response is not None + assert response.message_id is not None @email_decorator @recorded_by_proxy @@ -44,7 +43,7 @@ def test_send_email_multiple_recipients(self): ) response = email_client.send(message) - assert response is not None + assert response.message_id is not None @email_decorator @recorded_by_proxy @@ -67,26 +66,25 @@ def test_send_email_attachment(self): ) response = email_client.send(message) - assert response is not None + assert response.message_id is not None - # TODO: Comment back in once the x-ms-request-id change is merged in - # @email_decorator - # @recorded_by_proxy - # def test_check_message_status(self): - # email_client = EmailClient.from_connection_string(self.communication_connection_string) + @email_decorator + @recorded_by_proxy + def test_check_message_status(self): + email_client = EmailClient.from_connection_string(self.communication_connection_string) - # message = EmailMessage( - # sender=self.sender_address, - # content=EmailContent(subject="This is the subject", plain_text="This is the body"), - # recipients=EmailRecipients( - # to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] - # ) - # ) + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) - # response = email_client.send(message) - # message_id = response.message_id - # if message_id is not None: - # message_status_response = email_client.get_send_status(message_id) - # assert message_status_response.status is not None - # else: - # assert False + response = email_client.send(message) + message_id = response.message_id + if message_id is not None: + message_status_response = email_client.get_send_status(message_id) + assert message_status_response.status is not None + else: + assert False diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py index da4dcfd90d60..ed34480a34dc 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py @@ -13,7 +13,6 @@ from async_preparers import email_decorator_async class TestEmailClient(AzureRecordedTestCase): - # TODO: Change the assert statements once x-ms-request-id change is merged in @email_decorator_async @recorded_by_proxy_async async def test_send_email_single_recipient(self): @@ -29,7 +28,7 @@ async def test_send_email_single_recipient(self): async with email_client: response = await email_client.send(message) - assert response is not None + assert response.message_id is not None @email_decorator_async @recorded_by_proxy_async @@ -49,7 +48,7 @@ async def test_send_email_multiple_recipients(self): async with email_client: response = await email_client.send(message) - assert response is not None + assert response.message_id is not None @email_decorator_async @recorded_by_proxy_async @@ -73,27 +72,26 @@ async def test_send_email_attachment(self): async with email_client: response = await email_client.send(message) - assert response is not None + assert response.message_id is not None - # TODO: Comment back in once the x-ms-request-id change is merged in - # @email_decorator_async - # @recorded_by_proxy_async - # async def test_check_message_status(self): - # email_client = EmailClient.from_connection_string(self.communication_connection_string) + @email_decorator_async + @recorded_by_proxy_async + async def test_check_message_status(self): + email_client = EmailClient.from_connection_string(self.communication_connection_string) - # message = EmailMessage( - # sender=self.sender_address, - # content=EmailContent(subject="This is the subject", plain_text="This is the body"), - # recipients=EmailRecipients( - # to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] - # ) - # ) + message = EmailMessage( + sender=self.sender_address, + content=EmailContent(subject="This is the subject", plain_text="This is the body"), + recipients=EmailRecipients( + to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] + ) + ) - # async with email_client: - # response = await email_client.send(message) - # message_id = response.message_id - # if message_id is not None: - # message_status_response = await email_client.get_send_status(message_id) - # assert message_status_response.status is not None - # else: - # assert False + async with email_client: + response = await email_client.send(message) + message_id = response.message_id + if message_id is not None: + message_status_response = await email_client.get_send_status(message_id) + assert message_status_response.status is not None + else: + assert False From 519795af906a40c16fe9833edc39fa7cbb858a67 Mon Sep 17 00:00:00 2001 From: Azure SDK Bot Date: Mon, 27 Jun 2022 16:22:34 +0000 Subject: [PATCH 20/30] Packaging update of azure-communication-email --- .../azure-communication-email/LICENSE | 2 +- .../azure-communication-email/MANIFEST.in | 6 +- .../azure-communication-email/README.md | 180 ++---------------- .../azure/__init__.py | 2 +- .../azure/communication/__init__.py | 2 +- .../sdk_packaging.toml | 9 + .../azure-communication-email/setup.py | 51 ++--- 7 files changed, 61 insertions(+), 191 deletions(-) create mode 100644 sdk/communication/azure-communication-email/sdk_packaging.toml diff --git a/sdk/communication/azure-communication-email/LICENSE b/sdk/communication/azure-communication-email/LICENSE index 63447fd8bbbf..b2f52a2bad4e 100644 --- a/sdk/communication/azure-communication-email/LICENSE +++ b/sdk/communication/azure-communication-email/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/sdk/communication/azure-communication-email/MANIFEST.in b/sdk/communication/azure-communication-email/MANIFEST.in index 4f582a7c8d7b..6602a5ae07c5 100644 --- a/sdk/communication/azure-communication-email/MANIFEST.in +++ b/sdk/communication/azure-communication-email/MANIFEST.in @@ -1,7 +1,7 @@ +include _meta.json +recursive-include tests *.py *.yaml include *.md include azure/__init__.py include azure/communication/__init__.py include LICENSE -recursive-include tests *.py -recursive-include samples *.py *.md -include azure/communication/email/py.typed \ No newline at end of file +include azure/communication/email/py.typed diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index 39f972a197f6..751f8683b66d 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -1,176 +1,30 @@ -# Azure Communication Email client library for Python +# Microsoft Azure SDK for Python -This package contains a Python SDK for Azure Communication Services for Email. +This is the Microsoft Azure MyService Management Client Library. +This package has been tested with Python 3.6+. +For a more complete view of Azure libraries, see the [azure sdk python release](https://aka.ms/azsdk/python/all). -## Key concepts +## _Disclaimer_ -The Azure Communication Email package is used to do following: -- Send emails to multiple types of recipients -- Query the status of a sent email message +_Azure SDK Python packages support for Python 2.7 has ended 01 January 2022. For more information and questions, please refer to https://github.com/Azure/azure-sdk-for-python/issues/20691_ -## Getting started +# Usage -### Prerequisites -You need an [Azure subscription][azure_sub], a [Communication Service Resource][communication_resource_docs], and an [Email Communication Resource][email_resource_docs] with an active [Domain][domain_overview]. +To learn how to use this package, see the [quickstart guide](https://aka.ms/azsdk/python/mgmt) -To create these resource, you can use the [Azure Portal][communication_resource_create_portal], the [Azure PowerShell][communication_resource_create_power_shell], or the [.NET management client library][communication_resource_create_net]. -### Installing + +For docs and references, see [Python SDK References](https://docs.microsoft.com/python/api/overview/azure/) +Code samples for this package can be found at [MyService Management](https://docs.microsoft.com/samples/browse/?languages=python&term=Getting%20started%20-%20Managing&terms=Getting%20started%20-%20Managing) on docs.microsoft.com. +Additional code samples for different Azure services are available at [Samples Repo](https://aka.ms/azsdk/python/mgmt/samples) -Install the Azure Communication Email client library for Python with [pip](https://pypi.org/project/pip/): -```bash -pip install azure-communication-email -``` +# Provide Feedback -## Examples +If you encounter any bugs or have suggestions, please file an issue in the +[Issues](https://github.com/Azure/azure-sdk-for-python/issues) +section of the project. -`EmailClient` provides the functionality to send email messages . -## Authentication - -Email clients can be authenticated using the connection string acquired from an Azure Communication Resource in the [Azure Portal][azure_portal]. - -```python -from azure.communication.email import EmailClient - -connection_string = "endpoint=https://.communication.azure.com/;accessKey=" -client = EmailClient.from_connection_string(connection_string); -``` - -### Send an Email Message - -To send an email message, call the `send` function from the `EmailClient`. - -```python -content = EmailContent( - subject="This is the subject", - plain_text="This is the body", - html= "

This is the body

", -) - -address = EmailAddress(email="customer@domain.com", display_name="Customer Name") - -message = EmailMessage( - sender="sender@contoso.com", - content=content, - recipients=EmailRecipients(to=[address]) - ) - -response = client.send(message) -``` - -### Send an Email Message to Multiple Recipients - -To send an email message to multiple recipients, add a object for each recipient type and an object for each recipient. - -```python -content = EmailContent( - subject="This is the subject", - plain_text="This is the body", - html= "

This is the body

", -) - -recipients = EmailRecipients( - to=[ - EmailAddress(email="customer@domain.com", display_name="Customer Name"), - EmailAddress(email="customer2@domain.com", display_name="Customer Name 2"), - ], - cc=[ - EmailAddress(email="ccCustomer@domain.com", display_name="CC Customer Name"), - EmailAddress(email="ccCustomer2@domain.com", display_name="CC Customer Name 2"), - ], - bcc=[ - EmailAddress(email="bccCustomer@domain.com", display_name="BCC Customer Name"), - EmailAddress(email="bccCustomer2@domain.com", display_name="BCC Customer Name 2"), - ] - ) - -message = EmailMessage(sender="sender@contoso.com", content=content, recipients=recipients) -response = client.send(message) -``` - -### Send Email with Attachments - -Azure Communication Services support sending email with attachments. - -```python -file = open("C://readme.txt", "r") -file_contents = file.read() -file.close() - -content = EmailContent( - subject="This is the subject", - plain_text="This is the body", - html= "

This is the body

", -) - -address = EmailAddress(email="customer@domain.com", display_name="Customer Name") - -attachment = EmailAttachment( - name="readme.txt", - attachment_type="txt", - content_bytes_base64=base64.b64encode(file_contents) -) - -message = EmailMessage( - sender="sender@contoso.com", - content=content, - recipients=EmailRecipients(to=[address]), - attachments=[attachment] - ) - -response = client.send(message) -``` - -### Get Email Message Status - -The result from the `send` call contains a `message_id` which can be used to query the status of the email. - -```python -response = client.send(message) -status = client.get_sent_status(message_id) -``` - -## Troubleshooting - -Email operations will throw an exception if the request to the server fails. The Email client will raise exceptions defined in [Azure Core](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/README.md). - -```Python -try: - response = email_client.send(message) -except Exception as ex: - print('Exception:') - print(ex) -``` - -## Next steps - -- [Read more about Email in Azure Communication Services][nextsteps] - -## Contributing - -This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. - -This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. - - - -[azure_sub]: https://azure.microsoft.com/free/dotnet/ -[azure_portal]: https://portal.azure.com -[cla]: https://cla.microsoft.com -[coc]: https://opensource.microsoft.com/codeofconduct/ -[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ -[coc_contact]: mailto:opencode@microsoft.com -[communication_resource_docs]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp -[email_resource_docs]: https://aka.ms/acsemail/createemailresource -[communication_resource_create_portal]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp -[communication_resource_create_power_shell]: https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice -[communication_resource_create_net]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-net -[package]: https://www.nuget.org/packages/Azure.Communication.Common/ -[product_docs]: https://aka.ms/acsemail/overview -[nextsteps]: https://aka.ms/acsemail/overview -[nuget]: https://www.nuget.org/ -[source]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/communication -[domain_overview]: https://aka.ms/acsemail/domainsoverview +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-python%2Fazure-communication-email%2FREADME.png) diff --git a/sdk/communication/azure-communication-email/azure/__init__.py b/sdk/communication/azure-communication-email/azure/__init__.py index 69e3be50dac4..8db66d3d0f0f 100644 --- a/sdk/communication/azure-communication-email/azure/__init__.py +++ b/sdk/communication/azure-communication-email/azure/__init__.py @@ -1 +1 @@ -__path__ = __import__('pkgutil').extend_path(__path__, __name__) +__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/sdk/communication/azure-communication-email/azure/communication/__init__.py b/sdk/communication/azure-communication-email/azure/communication/__init__.py index 69e3be50dac4..8db66d3d0f0f 100644 --- a/sdk/communication/azure-communication-email/azure/communication/__init__.py +++ b/sdk/communication/azure-communication-email/azure/communication/__init__.py @@ -1 +1 @@ -__path__ = __import__('pkgutil').extend_path(__path__, __name__) +__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/sdk/communication/azure-communication-email/sdk_packaging.toml b/sdk/communication/azure-communication-email/sdk_packaging.toml new file mode 100644 index 000000000000..68394c692f5b --- /dev/null +++ b/sdk/communication/azure-communication-email/sdk_packaging.toml @@ -0,0 +1,9 @@ +[packaging] +package_name = "azure-communication-email" +package_nspkg = "azure-communication-nspkg" +package_pprint_name = "MyService Management" +package_doc_id = "" +is_stable = false +is_arm = true +need_msrestazure = false +need_azuremgmtcore = true diff --git a/sdk/communication/azure-communication-email/setup.py b/sdk/communication/azure-communication-email/setup.py index e16f16f68b8c..01ae9b0708f7 100644 --- a/sdk/communication/azure-communication-email/setup.py +++ b/sdk/communication/azure-communication-email/setup.py @@ -1,17 +1,19 @@ -from setuptools import setup, find_packages -import os -from io import open -import re +#!/usr/bin/env python -# example setup.py Feel free to copy the entire "azure-template" folder into a package folder named -# with "azure-". Ensure that the below arguments to setup() are updated to reflect -# your package. +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +#-------------------------------------------------------------------------- -# this setup.py is set up in a specific way to keep the azure* and azure-mgmt-* namespaces WORKING all the way -# up from python 3.6. Reference here: https://github.com/Azure/azure-sdk-for-python/wiki/Azure-packaging +import re +import os.path +from io import open +from setuptools import find_packages, setup +# Change the PACKAGE_NAME only to change folder and different name PACKAGE_NAME = "azure-communication-email" -PACKAGE_PPRINT_NAME = "Communication Email" +PACKAGE_PPRINT_NAME = "MyService Management" # a-b-c => a/b/c package_folder_path = PACKAGE_NAME.replace('-', '/') @@ -19,33 +21,41 @@ namespace_name = PACKAGE_NAME.replace('-', '.') # Version extraction inspired from 'requests' -with open(os.path.join(package_folder_path, '_version.py'), 'r') as fd: +with open(os.path.join(package_folder_path, 'version.py') + if os.path.exists(os.path.join(package_folder_path, 'version.py')) + else os.path.join(package_folder_path, '_version.py'), 'r') as fd: version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) + if not version: raise RuntimeError('Cannot find version information') with open('README.md', encoding='utf-8') as f: - long_description = f.read() + readme = f.read() +with open('CHANGELOG.md', encoding='utf-8') as f: + changelog = f.read() setup( name=PACKAGE_NAME, version=version, description='Microsoft Azure {} Client Library for Python'.format(PACKAGE_PPRINT_NAME), - long_description=long_description, + long_description=readme + '\n\n' + changelog, long_description_content_type='text/markdown', license='MIT License', author='Microsoft Corporation', author_email='azpysdkhelp@microsoft.com', url='https://github.com/Azure/azure-sdk-for-python', + keywords="azure, azure sdk", # update with search keywords relevant to the azure service / product classifiers=[ - "Development Status :: 5 - Production/Stable", + 'Development Status :: 4 - Beta', 'Programming Language :: Python', - "Programming Language :: Python :: 3 :: Only", + 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'License :: OSI Approved :: MIT License', ], zip_safe=False, @@ -53,19 +63,16 @@ 'tests', # Exclude packages that will be covered by PEP420 or nspkg 'azure', - 'azure.communication' + 'azure.communication', ]), - python_requires=">=3.6", include_package_data=True, package_data={ 'pytyped': ['py.typed'], }, install_requires=[ - 'azure-core<2.0.0,>=1.23.0', 'msrest>=0.6.21', - 'six>=1.11.0', + 'azure-common~=1.1', + 'azure-mgmt-core>=1.3.1,<2.0.0', ], - extras_require={ - ":python_version<'3.8'": ["typing-extensions"] - } + python_requires=">=3.6" ) From dc1d09f14562c76cff7827bdefc1f789f6148d30 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Mon, 27 Jun 2022 10:34:39 -0700 Subject: [PATCH 21/30] Fixing spelling errors --- .../samples/check_message_status_sample.py | 2 +- .../samples/check_message_status_sample_async.py | 2 +- .../samples/send_email_to_multiple_recipients_sample.py | 2 +- .../samples/send_email_to_multiple_recipients_sample_async.py | 2 +- .../samples/send_email_to_single_recipient_sample.py | 2 +- .../samples/send_email_to_single_recipient_sample_async.py | 2 +- .../samples/send_email_with_attachments_sample.py | 4 ++-- .../samples/send_email_with_attachments_sample_async.py | 4 ++-- .../azure-communication-email/tests/test_email_client_e2e.py | 2 +- .../tests/test_email_client_e2e_async.py | 2 +- shared_requirements.txt | 1 + 11 files changed, 13 insertions(+), 12 deletions(-) diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py index d249d646cedc..a320f35536f3 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py @@ -16,7 +16,7 @@ Set the environment variable with your own value before running the sample: 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email - 3) RECIPIENT_ADDRESS - the address that will recieve the email + 3) RECIPIENT_ADDRESS - the address that will receive the email """ import os diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py index d0a6b6278e3a..42b7e375be5e 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py @@ -16,7 +16,7 @@ Set the environment variable with your own value before running the sample: 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email - 3) RECIPIENT_ADDRESS - the address that will recieve the email + 3) RECIPIENT_ADDRESS - the address that will receive the email """ import os diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py index 2f365d626228..340f21ba25b3 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py @@ -17,7 +17,7 @@ 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email 3) RECIPIENT_ADDRESS - the address that will recieve the email - 4) SECOND_RECIPIENT_ADDRESS - the second address that will recieve the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will receive the email """ import os diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py index 0b43bd6689a3..59f0169bb729 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -16,7 +16,7 @@ Set the environment variable with your own value before running the sample: 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email - 3) RECIPIENT_ADDRESS - the address that will recieve the email + 3) RECIPIENT_ADDRESS - the address that will receive the email 4) SECOND_RECIPIENT_ADDRESS - the second address that will recieve the email """ diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py index 0dc9f3112415..dec8b37ec01f 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py @@ -16,7 +16,7 @@ Set the environment variable with your own value before running the sample: 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email - 3) RECIPIENT_ADDRESS - the address that will recieve the email + 3) RECIPIENT_ADDRESS - the address that will receive the email """ import os diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py index 56dc2ca69966..abf21a66bd1f 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py @@ -16,7 +16,7 @@ Set the environment variable with your own value before running the sample: 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email - 3) RECIPIENT_ADDRESS - the address that will recieve the email + 3) RECIPIENT_ADDRESS - the address that will receive the email """ import os diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py index 4994666f6b92..2aa3995b6c1f 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -16,7 +16,7 @@ Set the environment variable with your own value before running the sample: 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email - 3) RECIPIENT_ADDRESS - the address that will recieve the email + 3) RECIPIENT_ADDRESS - the address that will receive the email """ import os @@ -56,7 +56,7 @@ def send_email_with_attachment(self): attachment = EmailAttachment( name="readme.txt", attachment_type="txt", - content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" #cspell:disable-line ) message = EmailMessage( diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py index 2654a67fd0d0..d345a283d147 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -16,7 +16,7 @@ Set the environment variable with your own value before running the sample: 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email - 3) RECIPIENT_ADDRESS - the address that will recieve the email + 3) RECIPIENT_ADDRESS - the address that will receive the email """ import os @@ -57,7 +57,7 @@ async def send_email_with_attachment_async(self): attachment = EmailAttachment( name="readme.txt", attachment_type="txt", - content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" #cspell:disable-line ) message = EmailMessage( diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py index 23724b825d7c..1ed8bc15feb9 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -60,7 +60,7 @@ def test_send_email_attachment(self): EmailAttachment( name="readme.txt", attachment_type="txt", - content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" #cspell:disable-line ) ] ) diff --git a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py index ed34480a34dc..ae5deb6ed7ee 100644 --- a/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py @@ -65,7 +65,7 @@ async def test_send_email_attachment(self): EmailAttachment( name="readme.txt", attachment_type="txt", - content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" #cspell:disable-line ) ] ) diff --git a/shared_requirements.txt b/shared_requirements.txt index fc244596f9c8..e369bd57759f 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -197,6 +197,7 @@ opentelemetry-sdk<2.0.0,>=1.5.0,!=1.10a0 #override azure-communication-identity azure-core<2.0.0,>=1.19.1 #override azure-communication-networktraversal azure-core<2.0.0,>=1.19.1 #override azure-communication-email azure-core<2.0.0,>=1.23.0 +#override azure-communication-email azure-mgmt-core<2.0.0,>=1.3.1 #override azure-mgmt-communication azure-core<2.0.0,>=1.9.0 #override azure-ai-metricsadvisor azure-core<2.0.0,>=1.23.0 #override azure-ai-translation-document azure-core<2.0.0,>=1.14.0 From 46ed5c238e8d7007a5eb49fb64754f0ec27cbea7 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Mon, 27 Jun 2022 11:07:36 -0700 Subject: [PATCH 22/30] Fixing spelling --- .../samples/send_email_to_multiple_recipients_sample.py | 2 +- .../samples/send_email_to_multiple_recipients_sample_async.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py index 340f21ba25b3..68d0f10df7dc 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py @@ -16,7 +16,7 @@ Set the environment variable with your own value before running the sample: 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email - 3) RECIPIENT_ADDRESS - the address that will recieve the email + 3) RECIPIENT_ADDRESS - the address that will receive the email 4) SECOND_RECIPIENT_ADDRESS - the second address that will receive the email """ diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py index 59f0169bb729..521ea3a17a7f 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -17,7 +17,7 @@ 1) COMMUNICATION_CONNECTION_STRING - the connection string in your ACS resource 2) SENDER_ADDRESS - the address found in the linked domain that will send the email 3) RECIPIENT_ADDRESS - the address that will receive the email - 4) SECOND_RECIPIENT_ADDRESS - the second address that will recieve the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will receive the email """ import os From 2abf4468f727066f5964bf49b04bb7414325db80 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Mon, 27 Jun 2022 11:43:53 -0700 Subject: [PATCH 23/30] Including samples and fixing README.md --- .../azure-communication-email/MANIFEST.in | 1 + .../azure-communication-email/README.md | 180 ++++++++++++++++-- 2 files changed, 164 insertions(+), 17 deletions(-) diff --git a/sdk/communication/azure-communication-email/MANIFEST.in b/sdk/communication/azure-communication-email/MANIFEST.in index 6602a5ae07c5..4841394c332c 100644 --- a/sdk/communication/azure-communication-email/MANIFEST.in +++ b/sdk/communication/azure-communication-email/MANIFEST.in @@ -1,5 +1,6 @@ include _meta.json recursive-include tests *.py *.yaml +recursive-include samples *.py *.md include *.md include azure/__init__.py include azure/communication/__init__.py diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index 751f8683b66d..bd952e31803f 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -1,30 +1,176 @@ -# Microsoft Azure SDK for Python +# Azure Communication Email client library for Python -This is the Microsoft Azure MyService Management Client Library. -This package has been tested with Python 3.6+. -For a more complete view of Azure libraries, see the [azure sdk python release](https://aka.ms/azsdk/python/all). +This package contains a Python SDK for Azure Communication Services for Email. -## _Disclaimer_ +## Key concepts -_Azure SDK Python packages support for Python 2.7 has ended 01 January 2022. For more information and questions, please refer to https://github.com/Azure/azure-sdk-for-python/issues/20691_ +The Azure Communication Email package is used to do following: +- Send emails to multiple types of recipients +- Query the status of a sent email message -# Usage +## Getting started +### Prerequisites -To learn how to use this package, see the [quickstart guide](https://aka.ms/azsdk/python/mgmt) +You need an [Azure subscription][azure_sub], a [Communication Service Resource][communication_resource_docs], and an [Email Communication Resource][email_resource_docs] with an active [Domain][domain_overview]. +To create these resource, you can use the [Azure Portal][communication_resource_create_portal], the [Azure PowerShell][communication_resource_create_power_shell], or the [.NET management client library][communication_resource_create_net]. - -For docs and references, see [Python SDK References](https://docs.microsoft.com/python/api/overview/azure/) -Code samples for this package can be found at [MyService Management](https://docs.microsoft.com/samples/browse/?languages=python&term=Getting%20started%20-%20Managing&terms=Getting%20started%20-%20Managing) on docs.microsoft.com. -Additional code samples for different Azure services are available at [Samples Repo](https://aka.ms/azsdk/python/mgmt/samples) +### Installing +Install the Azure Communication Email client library for Python with [pip](https://pypi.org/project/pip/): -# Provide Feedback +```bash +pip install azure-communication-email +``` -If you encounter any bugs or have suggestions, please file an issue in the -[Issues](https://github.com/Azure/azure-sdk-for-python/issues) -section of the project. +## Examples +`EmailClient` provides the functionality to send email messages . -![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-python%2Fazure-communication-email%2FREADME.png) +## Authentication + +Email clients can be authenticated using the connection string acquired from an Azure Communication Resource in the [Azure Portal][azure_portal]. + +```python +from azure.communication.email import EmailClient + +connection_string = "endpoint=https://.communication.azure.com/;accessKey=" +client = EmailClient.from_connection_string(connection_string); +``` + +### Send an Email Message + +To send an email message, call the `send` function from the `EmailClient`. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]) + ) + +response = client.send(message) +``` + +### Send an Email Message to Multiple Recipients + +To send an email message to multiple recipients, add a object for each recipient type and an object for each recipient. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +recipients = EmailRecipients( + to=[ + EmailAddress(email="customer@domain.com", display_name="Customer Name"), + EmailAddress(email="customer2@domain.com", display_name="Customer Name 2"), + ], + cc=[ + EmailAddress(email="ccCustomer@domain.com", display_name="CC Customer Name"), + EmailAddress(email="ccCustomer2@domain.com", display_name="CC Customer Name 2"), + ], + bcc=[ + EmailAddress(email="bccCustomer@domain.com", display_name="BCC Customer Name"), + EmailAddress(email="bccCustomer2@domain.com", display_name="BCC Customer Name 2"), + ] + ) + +message = EmailMessage(sender="sender@contoso.com", content=content, recipients=recipients) +response = client.send(message) +``` + +### Send Email with Attachments + +Azure Communication Services support sending email with attachments. + +```python +file = open("C://readme.txt", "r") +file_contents = file.read() +file.close() + +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64=base64.b64encode(file_contents) +) + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]), + attachments=[attachment] + ) + +response = client.send(message) +``` + +### Get Email Message Status + +The result from the `send` call contains a `message_id` which can be used to query the status of the email. + +```python +response = client.send(message) +status = client.get_sent_status(message_id) +``` + +## Troubleshooting + +Email operations will throw an exception if the request to the server fails. The Email client will raise exceptions defined in [Azure Core](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/README.md). + +```Python +try: + response = email_client.send(message) +except Exception as ex: + print('Exception:') + print(ex) +``` + +## Next steps + +- [Read more about Email in Azure Communication Services][nextsteps] + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. + + + +[azure_sub]: https://azure.microsoft.com/free/dotnet/ +[azure_portal]: https://portal.azure.com +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com +[communication_resource_docs]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[email_resource_docs]: https://aka.ms/acsemail/createemailresource +[communication_resource_create_portal]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[communication_resource_create_power_shell]: https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice +[communication_resource_create_net]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-net +[package]: https://www.nuget.org/packages/Azure.Communication.Common/ +[product_docs]: https://aka.ms/acsemail/overview +[nextsteps]: https://aka.ms/acsemail/overview +[nuget]: https://www.nuget.org/ +[source]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/communication +[domain_overview]: https://aka.ms/acsemail/domainsoverview \ No newline at end of file From 42f8da6cbfd5a3e7bdf72aac1ca05c64b155072a Mon Sep 17 00:00:00 2001 From: Azure SDK Bot Date: Mon, 27 Jun 2022 18:48:31 +0000 Subject: [PATCH 24/30] Packaging update of azure-communication-email --- .../azure-communication-email/MANIFEST.in | 1 - .../azure-communication-email/README.md | 180 ++---------------- 2 files changed, 17 insertions(+), 164 deletions(-) diff --git a/sdk/communication/azure-communication-email/MANIFEST.in b/sdk/communication/azure-communication-email/MANIFEST.in index 4841394c332c..6602a5ae07c5 100644 --- a/sdk/communication/azure-communication-email/MANIFEST.in +++ b/sdk/communication/azure-communication-email/MANIFEST.in @@ -1,6 +1,5 @@ include _meta.json recursive-include tests *.py *.yaml -recursive-include samples *.py *.md include *.md include azure/__init__.py include azure/communication/__init__.py diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index bd952e31803f..751f8683b66d 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -1,176 +1,30 @@ -# Azure Communication Email client library for Python +# Microsoft Azure SDK for Python -This package contains a Python SDK for Azure Communication Services for Email. +This is the Microsoft Azure MyService Management Client Library. +This package has been tested with Python 3.6+. +For a more complete view of Azure libraries, see the [azure sdk python release](https://aka.ms/azsdk/python/all). -## Key concepts +## _Disclaimer_ -The Azure Communication Email package is used to do following: -- Send emails to multiple types of recipients -- Query the status of a sent email message +_Azure SDK Python packages support for Python 2.7 has ended 01 January 2022. For more information and questions, please refer to https://github.com/Azure/azure-sdk-for-python/issues/20691_ -## Getting started +# Usage -### Prerequisites -You need an [Azure subscription][azure_sub], a [Communication Service Resource][communication_resource_docs], and an [Email Communication Resource][email_resource_docs] with an active [Domain][domain_overview]. +To learn how to use this package, see the [quickstart guide](https://aka.ms/azsdk/python/mgmt) -To create these resource, you can use the [Azure Portal][communication_resource_create_portal], the [Azure PowerShell][communication_resource_create_power_shell], or the [.NET management client library][communication_resource_create_net]. -### Installing + +For docs and references, see [Python SDK References](https://docs.microsoft.com/python/api/overview/azure/) +Code samples for this package can be found at [MyService Management](https://docs.microsoft.com/samples/browse/?languages=python&term=Getting%20started%20-%20Managing&terms=Getting%20started%20-%20Managing) on docs.microsoft.com. +Additional code samples for different Azure services are available at [Samples Repo](https://aka.ms/azsdk/python/mgmt/samples) -Install the Azure Communication Email client library for Python with [pip](https://pypi.org/project/pip/): -```bash -pip install azure-communication-email -``` +# Provide Feedback -## Examples +If you encounter any bugs or have suggestions, please file an issue in the +[Issues](https://github.com/Azure/azure-sdk-for-python/issues) +section of the project. -`EmailClient` provides the functionality to send email messages . -## Authentication - -Email clients can be authenticated using the connection string acquired from an Azure Communication Resource in the [Azure Portal][azure_portal]. - -```python -from azure.communication.email import EmailClient - -connection_string = "endpoint=https://.communication.azure.com/;accessKey=" -client = EmailClient.from_connection_string(connection_string); -``` - -### Send an Email Message - -To send an email message, call the `send` function from the `EmailClient`. - -```python -content = EmailContent( - subject="This is the subject", - plain_text="This is the body", - html= "

This is the body

", -) - -address = EmailAddress(email="customer@domain.com", display_name="Customer Name") - -message = EmailMessage( - sender="sender@contoso.com", - content=content, - recipients=EmailRecipients(to=[address]) - ) - -response = client.send(message) -``` - -### Send an Email Message to Multiple Recipients - -To send an email message to multiple recipients, add a object for each recipient type and an object for each recipient. - -```python -content = EmailContent( - subject="This is the subject", - plain_text="This is the body", - html= "

This is the body

", -) - -recipients = EmailRecipients( - to=[ - EmailAddress(email="customer@domain.com", display_name="Customer Name"), - EmailAddress(email="customer2@domain.com", display_name="Customer Name 2"), - ], - cc=[ - EmailAddress(email="ccCustomer@domain.com", display_name="CC Customer Name"), - EmailAddress(email="ccCustomer2@domain.com", display_name="CC Customer Name 2"), - ], - bcc=[ - EmailAddress(email="bccCustomer@domain.com", display_name="BCC Customer Name"), - EmailAddress(email="bccCustomer2@domain.com", display_name="BCC Customer Name 2"), - ] - ) - -message = EmailMessage(sender="sender@contoso.com", content=content, recipients=recipients) -response = client.send(message) -``` - -### Send Email with Attachments - -Azure Communication Services support sending email with attachments. - -```python -file = open("C://readme.txt", "r") -file_contents = file.read() -file.close() - -content = EmailContent( - subject="This is the subject", - plain_text="This is the body", - html= "

This is the body

", -) - -address = EmailAddress(email="customer@domain.com", display_name="Customer Name") - -attachment = EmailAttachment( - name="readme.txt", - attachment_type="txt", - content_bytes_base64=base64.b64encode(file_contents) -) - -message = EmailMessage( - sender="sender@contoso.com", - content=content, - recipients=EmailRecipients(to=[address]), - attachments=[attachment] - ) - -response = client.send(message) -``` - -### Get Email Message Status - -The result from the `send` call contains a `message_id` which can be used to query the status of the email. - -```python -response = client.send(message) -status = client.get_sent_status(message_id) -``` - -## Troubleshooting - -Email operations will throw an exception if the request to the server fails. The Email client will raise exceptions defined in [Azure Core](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/README.md). - -```Python -try: - response = email_client.send(message) -except Exception as ex: - print('Exception:') - print(ex) -``` - -## Next steps - -- [Read more about Email in Azure Communication Services][nextsteps] - -## Contributing - -This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. - -This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. - - - -[azure_sub]: https://azure.microsoft.com/free/dotnet/ -[azure_portal]: https://portal.azure.com -[cla]: https://cla.microsoft.com -[coc]: https://opensource.microsoft.com/codeofconduct/ -[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ -[coc_contact]: mailto:opencode@microsoft.com -[communication_resource_docs]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp -[email_resource_docs]: https://aka.ms/acsemail/createemailresource -[communication_resource_create_portal]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp -[communication_resource_create_power_shell]: https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice -[communication_resource_create_net]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-net -[package]: https://www.nuget.org/packages/Azure.Communication.Common/ -[product_docs]: https://aka.ms/acsemail/overview -[nextsteps]: https://aka.ms/acsemail/overview -[nuget]: https://www.nuget.org/ -[source]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/communication -[domain_overview]: https://aka.ms/acsemail/domainsoverview \ No newline at end of file +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-python%2Fazure-communication-email%2FREADME.png) From 235eb4fad672d4859d2f908310b41a1b6cffb17b Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Mon, 27 Jun 2022 13:19:53 -0700 Subject: [PATCH 25/30] Updating packaging file --- sdk/communication/azure-communication-email/sdk_packaging.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/communication/azure-communication-email/sdk_packaging.toml b/sdk/communication/azure-communication-email/sdk_packaging.toml index 68394c692f5b..ecfa0b440e13 100644 --- a/sdk/communication/azure-communication-email/sdk_packaging.toml +++ b/sdk/communication/azure-communication-email/sdk_packaging.toml @@ -1,4 +1,5 @@ [packaging] +auto_update = false package_name = "azure-communication-email" package_nspkg = "azure-communication-nspkg" package_pprint_name = "MyService Management" From cb14df51c6c84cec44c8834ff3ce649769e72477 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Mon, 27 Jun 2022 13:28:52 -0700 Subject: [PATCH 26/30] Update README and MANIFEST --- .../azure-communication-email/MANIFEST.in | 3 +- .../azure-communication-email/README.md | 180 ++++++++++++++++-- 2 files changed, 165 insertions(+), 18 deletions(-) diff --git a/sdk/communication/azure-communication-email/MANIFEST.in b/sdk/communication/azure-communication-email/MANIFEST.in index 6602a5ae07c5..e888b3235b57 100644 --- a/sdk/communication/azure-communication-email/MANIFEST.in +++ b/sdk/communication/azure-communication-email/MANIFEST.in @@ -1,7 +1,8 @@ include _meta.json recursive-include tests *.py *.yaml +recursive-include samples *.py *.md include *.md include azure/__init__.py include azure/communication/__init__.py include LICENSE -include azure/communication/email/py.typed +include azure/communication/email/py.typed \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index 751f8683b66d..bd952e31803f 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -1,30 +1,176 @@ -# Microsoft Azure SDK for Python +# Azure Communication Email client library for Python -This is the Microsoft Azure MyService Management Client Library. -This package has been tested with Python 3.6+. -For a more complete view of Azure libraries, see the [azure sdk python release](https://aka.ms/azsdk/python/all). +This package contains a Python SDK for Azure Communication Services for Email. -## _Disclaimer_ +## Key concepts -_Azure SDK Python packages support for Python 2.7 has ended 01 January 2022. For more information and questions, please refer to https://github.com/Azure/azure-sdk-for-python/issues/20691_ +The Azure Communication Email package is used to do following: +- Send emails to multiple types of recipients +- Query the status of a sent email message -# Usage +## Getting started +### Prerequisites -To learn how to use this package, see the [quickstart guide](https://aka.ms/azsdk/python/mgmt) +You need an [Azure subscription][azure_sub], a [Communication Service Resource][communication_resource_docs], and an [Email Communication Resource][email_resource_docs] with an active [Domain][domain_overview]. +To create these resource, you can use the [Azure Portal][communication_resource_create_portal], the [Azure PowerShell][communication_resource_create_power_shell], or the [.NET management client library][communication_resource_create_net]. - -For docs and references, see [Python SDK References](https://docs.microsoft.com/python/api/overview/azure/) -Code samples for this package can be found at [MyService Management](https://docs.microsoft.com/samples/browse/?languages=python&term=Getting%20started%20-%20Managing&terms=Getting%20started%20-%20Managing) on docs.microsoft.com. -Additional code samples for different Azure services are available at [Samples Repo](https://aka.ms/azsdk/python/mgmt/samples) +### Installing +Install the Azure Communication Email client library for Python with [pip](https://pypi.org/project/pip/): -# Provide Feedback +```bash +pip install azure-communication-email +``` -If you encounter any bugs or have suggestions, please file an issue in the -[Issues](https://github.com/Azure/azure-sdk-for-python/issues) -section of the project. +## Examples +`EmailClient` provides the functionality to send email messages . -![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-python%2Fazure-communication-email%2FREADME.png) +## Authentication + +Email clients can be authenticated using the connection string acquired from an Azure Communication Resource in the [Azure Portal][azure_portal]. + +```python +from azure.communication.email import EmailClient + +connection_string = "endpoint=https://.communication.azure.com/;accessKey=" +client = EmailClient.from_connection_string(connection_string); +``` + +### Send an Email Message + +To send an email message, call the `send` function from the `EmailClient`. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]) + ) + +response = client.send(message) +``` + +### Send an Email Message to Multiple Recipients + +To send an email message to multiple recipients, add a object for each recipient type and an object for each recipient. + +```python +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +recipients = EmailRecipients( + to=[ + EmailAddress(email="customer@domain.com", display_name="Customer Name"), + EmailAddress(email="customer2@domain.com", display_name="Customer Name 2"), + ], + cc=[ + EmailAddress(email="ccCustomer@domain.com", display_name="CC Customer Name"), + EmailAddress(email="ccCustomer2@domain.com", display_name="CC Customer Name 2"), + ], + bcc=[ + EmailAddress(email="bccCustomer@domain.com", display_name="BCC Customer Name"), + EmailAddress(email="bccCustomer2@domain.com", display_name="BCC Customer Name 2"), + ] + ) + +message = EmailMessage(sender="sender@contoso.com", content=content, recipients=recipients) +response = client.send(message) +``` + +### Send Email with Attachments + +Azure Communication Services support sending email with attachments. + +```python +file = open("C://readme.txt", "r") +file_contents = file.read() +file.close() + +content = EmailContent( + subject="This is the subject", + plain_text="This is the body", + html= "

This is the body

", +) + +address = EmailAddress(email="customer@domain.com", display_name="Customer Name") + +attachment = EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64=base64.b64encode(file_contents) +) + +message = EmailMessage( + sender="sender@contoso.com", + content=content, + recipients=EmailRecipients(to=[address]), + attachments=[attachment] + ) + +response = client.send(message) +``` + +### Get Email Message Status + +The result from the `send` call contains a `message_id` which can be used to query the status of the email. + +```python +response = client.send(message) +status = client.get_sent_status(message_id) +``` + +## Troubleshooting + +Email operations will throw an exception if the request to the server fails. The Email client will raise exceptions defined in [Azure Core](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/README.md). + +```Python +try: + response = email_client.send(message) +except Exception as ex: + print('Exception:') + print(ex) +``` + +## Next steps + +- [Read more about Email in Azure Communication Services][nextsteps] + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. + + + +[azure_sub]: https://azure.microsoft.com/free/dotnet/ +[azure_portal]: https://portal.azure.com +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com +[communication_resource_docs]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[email_resource_docs]: https://aka.ms/acsemail/createemailresource +[communication_resource_create_portal]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[communication_resource_create_power_shell]: https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice +[communication_resource_create_net]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-net +[package]: https://www.nuget.org/packages/Azure.Communication.Common/ +[product_docs]: https://aka.ms/acsemail/overview +[nextsteps]: https://aka.ms/acsemail/overview +[nuget]: https://www.nuget.org/ +[source]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/communication +[domain_overview]: https://aka.ms/acsemail/domainsoverview \ No newline at end of file From d9fca055dd83aef3bbc3d6fa42aeb62ca6fbf16a Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Thu, 7 Jul 2022 12:57:03 -0700 Subject: [PATCH 27/30] Addressing PR comments --- .../azure-communication-email/CHANGELOG.md | 3 +-- .../azure-communication-email/README.md | 19 ++++++++++------- .../azure/communication/email/__init__.py | 6 ++++++ .../communication/email/_email_client.py | 9 +++++++- .../email/aio/_email_client_async.py | 6 +++--- .../samples/attachment.txt | 1 + .../samples/check_message_status_sample.py | 18 +++++++++------- .../check_message_status_sample_async.py | 9 +++----- ...end_email_to_multiple_recipients_sample.py | 19 ++++++++++++++--- ...ail_to_multiple_recipients_sample_async.py | 17 +++++++++------ .../send_email_to_single_recipient_sample.py | 11 +++++++--- ..._email_to_single_recipient_sample_async.py | 9 +++----- .../send_email_with_attachments_sample.py | 21 ++++++++++++++----- ...end_email_with_attachments_sample_async.py | 19 ++++++++++------- 14 files changed, 110 insertions(+), 57 deletions(-) create mode 100644 sdk/communication/azure-communication-email/samples/attachment.txt diff --git a/sdk/communication/azure-communication-email/CHANGELOG.md b/sdk/communication/azure-communication-email/CHANGELOG.md index 1cf845b24478..616fb0662508 100644 --- a/sdk/communication/azure-communication-email/CHANGELOG.md +++ b/sdk/communication/azure-communication-email/CHANGELOG.md @@ -1,9 +1,8 @@ # Release History -## 1.0.0b1 (TODO: UPDATE WITH RELEASE DATE) +## 1.0.0b1 (Unreleased) The first preview of the Azure Communication Email Client has the following features: - send emails to multiple recipients with attachments - get the status of a sent message - diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index bd952e31803f..b55e0453a51a 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -96,9 +96,7 @@ response = client.send(message) Azure Communication Services support sending email with attachments. ```python -file = open("C://readme.txt", "r") -file_contents = file.read() -file.close() +import base64 content = EmailContent( subject="This is the subject", @@ -108,10 +106,15 @@ content = EmailContent( address = EmailAddress(email="customer@domain.com", display_name="Customer Name") +with open("C://readme.txt", "r") as file: + file_contents = file.read() + +file_bytes_b64 = base64.b64encode(bytes(file_contents, 'utf-8')) + attachment = EmailAttachment( - name="readme.txt", + name="attachment.txt", attachment_type="txt", - content_bytes_base64=base64.b64encode(file_contents) + content_bytes_base64=file_bytes_b64.decode() ) message = EmailMessage( @@ -130,7 +133,7 @@ The result from the `send` call contains a `message_id` which can be used to que ```python response = client.send(message) -status = client.get_sent_status(message_id) +status = client.get_sent_status(response.message_id) ``` ## Troubleshooting @@ -138,9 +141,11 @@ status = client.get_sent_status(message_id) Email operations will throw an exception if the request to the server fails. The Email client will raise exceptions defined in [Azure Core](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/README.md). ```Python +from azure.core.exceptions import HttpResponseError + try: response = email_client.send(message) -except Exception as ex: +except HttpResponseError as ex: print('Exception:') print(ex) ``` diff --git a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py index 9c22b889a185..f9531f95ac23 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/__init__.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py @@ -1,3 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + from ._email_client import EmailClient from ._generated.models import ( diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py index ae2e9ee8baec..82532de9b926 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -25,7 +25,7 @@ class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword def __init__( self, endpoint, # type: str - credential, # type: str + credential, # type: TokenCredential **kwargs # type: Any ): # type: (...) -> None @@ -94,3 +94,10 @@ def get_send_status( message_id=message_id, **kwargs ) + + async def __enter__(self) -> "EmailClient": + await self._generated_client.__enter__() + return self + + async def __exit__(self, *args) -> None: + await self._generated_client.__exit__(*args) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py index 0ed6c8c94d5d..abbdc2ee0aa0 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -19,13 +19,13 @@ class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword :param str endpoint: The endpoint url for Azure Communication Service resource. - :param TokenCredential credential: - The TokenCredential we use to authenticate against the service. + :param AsyncTokenCredential credential: + The AsyncTokenCredential we use to authenticate against the service. """ def __init__( self, endpoint, # type: str - credential, # type: str + credential, # type: AsyncTokenCredential **kwargs # type: Any ): # type: (...) -> None diff --git a/sdk/communication/azure-communication-email/samples/attachment.txt b/sdk/communication/azure-communication-email/samples/attachment.txt new file mode 100644 index 000000000000..85c78dc78ed7 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/attachment.txt @@ -0,0 +1 @@ +Test Attachment Text \ No newline at end of file diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py index a320f35536f3..01843a216ab2 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py @@ -21,6 +21,7 @@ import os import sys +from azure.core.exceptions import HttpResponseError from azure.communication.email import ( EmailClient, EmailContent, @@ -57,15 +58,18 @@ def check_message_status(self): content=content, recipients=recipients ) + try: + # sending the email message + response = email_client.send(message) - # sending the email message - response = email_client.send(message) + # using the message id to get the status of the email + message_id = response.message_id + message_status = email_client.get_send_status(message_id) - # using the message id to get the status of the email - message_id = response.message_id - message_status = email_client.get_send_status(message_id) - - print("Message Status: " + message_status.status) + print("Message Status: " + message_status.status) + except HttpResponseError as ex: + print(ex) + pass if __name__ == '__main__': sample = EmailCheckMessageStatusSample() diff --git a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py index 42b7e375be5e..9ca2596a33e6 100644 --- a/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py @@ -22,6 +22,7 @@ import os import sys import asyncio +from azure.core.exceptions import HttpResponseError from azure.communication.email.aio import EmailClient from azure.communication.email import ( EmailContent, @@ -69,14 +70,10 @@ async def check_message_status_async(self): message_status = await email_client.get_send_status(message_id) print("Message Status: " + message_status.status) - except Exception: - print(Exception) + except HttpResponseError as ex: + print(ex) pass if __name__ == '__main__': sample = EmailCheckMessageStatusSampleAsync() - - # Comment in this line if you are running this sample on Windows - # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - asyncio.run(sample.check_message_status_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py index 68d0f10df7dc..d0ac33f19b29 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.py @@ -22,6 +22,7 @@ import os import sys +from azure.core.exceptions import HttpResponseError from azure.communication.email import ( EmailClient, EmailContent, @@ -54,6 +55,14 @@ def send_email_to_multiple_recipients(self): to=[ EmailAddress(email=self.recipient_address, display_name="Customer Name"), EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ], + cc=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ], + bcc=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), ] ) @@ -63,9 +72,13 @@ def send_email_to_multiple_recipients(self): recipients=recipients ) - # sending the email message - response = email_client.send(message) - print("Message ID: " + response.message_id) + try: + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + except HttpResponseError as ex: + print(ex) + pass if __name__ == '__main__': sample = EmailMultipleRecipientSample() diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py index 521ea3a17a7f..975d49f2874d 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -23,6 +23,7 @@ import os import sys import asyncio +from azure.core.exceptions import HttpResponseError from azure.communication.email.aio import EmailClient from azure.communication.email import ( EmailContent, @@ -55,6 +56,14 @@ async def send_email_to_multiple_recipients_async(self): to=[ EmailAddress(email=self.recipient_address, display_name="Customer Name"), EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ], + cc=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), + ], + bcc=[ + EmailAddress(email=self.recipient_address, display_name="Customer Name"), + EmailAddress(email=self.second_recipient_address, display_name="Customer Name 2"), ] ) @@ -69,14 +78,10 @@ async def send_email_to_multiple_recipients_async(self): # sending the email message response = await email_client.send(message) print("Message ID: " + response.message_id) - except Exception: - print(Exception) + except HttpResponseError as ex: + print(ex) pass if __name__ == '__main__': sample = EmailMultipleRecipientSampleAsync() - - # Comment in this line if you are running this sample on Windows - # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - asyncio.run(sample.send_email_to_multiple_recipients_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py index dec8b37ec01f..a25ac406568c 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample.py @@ -21,6 +21,7 @@ import os import sys +from azure.core.exceptions import HttpResponseError from azure.communication.email import ( EmailClient, EmailContent, @@ -58,9 +59,13 @@ def send_email_to_single_recipient(self): recipients=recipients ) - # sending the email message - response = email_client.send(message) - print("Message ID: " + response.message_id) + try: + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + except HttpResponseError as ex: + print(ex) + pass if __name__ == '__main__': sample = EmailSingleRecipientSample() diff --git a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py index abf21a66bd1f..33b067503a09 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py @@ -22,6 +22,7 @@ import os import sys import asyncio +from azure.core.exceptions import HttpResponseError from azure.communication.email.aio import EmailClient from azure.communication.email import ( EmailContent, @@ -64,14 +65,10 @@ async def send_email_to_single_recipient_async(self): # sending the email message response = await email_client.send(message) print("Message ID: " + response.message_id) - except Exception: - print(Exception) + except HttpResponseError as ex: + print(ex) pass if __name__ == '__main__': sample = EmailSingleRecipientSampleAsync() - - # Comment in this line if you are running this sample on Windows - # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - asyncio.run(sample.send_email_to_single_recipient_async()) diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py index 2aa3995b6c1f..80a4892cd92b 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -19,8 +19,10 @@ 3) RECIPIENT_ADDRESS - the address that will receive the email """ +import base64 import os import sys +from azure.core.exceptions import HttpResponseError from azure.communication.email import ( EmailClient, EmailContent, @@ -53,10 +55,15 @@ def send_email_with_attachment(self): to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] ) + with open("./attachment.txt", "r") as file: + file_contents = file.read() + + file_bytes_b64 = base64.b64encode(bytes(file_contents, 'utf-8')) + attachment = EmailAttachment( - name="readme.txt", + name="attachment.txt", attachment_type="txt", - content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" #cspell:disable-line + content_bytes_base64=file_bytes_b64.decode() ) message = EmailMessage( @@ -66,9 +73,13 @@ def send_email_with_attachment(self): attachments=[attachment] ) - # sending the email message - response = email_client.send(message) - print("Message ID: " + response.message_id) + try: + # sending the email message + response = email_client.send(message) + print("Message ID: " + response.message_id) + except HttpResponseError as ex: + print(ex) + pass if __name__ == '__main__': sample = EmailWithAttachmentSample() diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py index d345a283d147..519a93aaa075 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -19,9 +19,11 @@ 3) RECIPIENT_ADDRESS - the address that will receive the email """ +import base64 import os import sys import asyncio +from azure.core.exceptions import HttpResponseError from azure.communication.email.aio import EmailClient from azure.communication.email import ( EmailContent, @@ -54,10 +56,15 @@ async def send_email_with_attachment_async(self): to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] ) + with open("./attachment.txt", "r") as file: + file_contents = file.read() + + file_bytes_b64 = base64.b64encode(bytes(file_contents, 'utf-8')) + attachment = EmailAttachment( - name="readme.txt", + name="attachment.txt", attachment_type="txt", - content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" #cspell:disable-line + content_bytes_base64=file_bytes_b64.decode() ) message = EmailMessage( @@ -72,14 +79,10 @@ async def send_email_with_attachment_async(self): # sending the email message response = await email_client.send(message) print("Message ID: " + response.message_id) - except Exception: - print(Exception) + except HttpResponseError as ex: + print(ex) pass if __name__ == '__main__': sample = EmailWithAttachmentSampleAsync() - - # Comment in this line if you are running this sample on Windows - # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - asyncio.run(sample.send_email_with_attachment_async()) From 0fae0b4a7c53854a5cd8880105ee683bd3826afb Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Mon, 11 Jul 2022 10:59:34 -0700 Subject: [PATCH 28/30] Addressing PR comments --- .../azure-communication-email/README.md | 12 +++++ .../communication/email/_email_client.py | 47 ++++++++++--------- .../communication/email/_shared/policy.py | 9 +++- .../email/aio/_email_client_async.py | 40 +++++++++------- .../send_email_with_attachments_sample.py | 6 +-- ...end_email_with_attachments_sample_async.py | 6 +-- 6 files changed, 74 insertions(+), 46 deletions(-) diff --git a/sdk/communication/azure-communication-email/README.md b/sdk/communication/azure-communication-email/README.md index b55e0453a51a..3b45b3c2bb77 100644 --- a/sdk/communication/azure-communication-email/README.md +++ b/sdk/communication/azure-communication-email/README.md @@ -39,6 +39,17 @@ connection_string = "endpoint=https://.communication.azure.com/;a client = EmailClient.from_connection_string(connection_string); ``` +Email clients can also be authenticated using an [AzureKeyCredential][azure-key-credential]. + +```python +from azure.communication.email import EmailClient +from azure.core.credentials import AzureKeyCredential + +credential = AzureKeyCredential("") +endpoint = "https://.communication.azure.com/" +client = EmailClient(endpoint, credential); +``` + ### Send an Email Message To send an email message, call the `send` function from the `EmailClient`. @@ -164,6 +175,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For m [azure_sub]: https://azure.microsoft.com/free/dotnet/ [azure_portal]: https://portal.azure.com +[azure-key-credential]: https://aka.ms/azsdk-python-core-azurekeycredential [cla]: https://cla.microsoft.com [coc]: https://opensource.microsoft.com/codeofconduct/ [coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py index 82532de9b926..05b65d351c0e 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -5,7 +5,10 @@ # -------------------------------------------------------------------------- from uuid import uuid4 +from azure.core.credentials import AzureKeyCredential +from azure.core.credentials import TokenCredential from azure.core.tracing.decorator import distributed_trace +from typing import Union from ._shared.utils import parse_connection_str, get_current_utc_time from ._shared.policy import HMACCredentialsPolicy from ._generated._azure_communication_email_service import AzureCommunicationEmailService @@ -19,16 +22,18 @@ class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword :param str endpoint: The endpoint url for Azure Communication Service resource. - :param TokenCredential credential: - The TokenCredential we use to authenticate against the service. + :param Union[TokenCredential, AzureKeyCredential] credential: + The credential we use to authenticate against the service. """ def __init__( self, - endpoint, # type: str - credential, # type: TokenCredential - **kwargs # type: Any - ): - # type: (...) -> None + endpoint: str, + credential: Union[TokenCredential, AzureKeyCredential], + **kwargs + ) -> None: + if endpoint.endswith("/"): + endpoint = endpoint[:-1] + authentication_policy = HMACCredentialsPolicy(endpoint, credential) self._generated_client = AzureCommunicationEmailService( @@ -41,9 +46,9 @@ def __init__( @classmethod def from_connection_string( cls, - conn_str, # type: str - **kwargs # type: Any - ): # type: (...) -> EmailClient + conn_str: str, + **kwargs + ) -> 'EmailClient': """Create EmailClient from a Connection String. :param str conn_str: @@ -53,14 +58,14 @@ def from_connection_string( """ endpoint, access_key = parse_connection_str(conn_str) - return cls(endpoint, access_key, **kwargs) + return cls(endpoint, AzureKeyCredential(access_key), **kwargs) @distributed_trace def send( self, - email_message, # type: EmailMessage - **kwargs # type: Any - ): # type: (...) -> SendEmailResult + email_message: EmailMessage, + **kwargs + ) -> SendEmailResult: """Queues an email message to be sent to one or more recipients. :param email_message: The message payload for sending an email. @@ -79,9 +84,9 @@ def send( @distributed_trace def get_send_status( self, - message_id, #type: str - **kwargs # type: Any - ): # type: (...) -> SendStatusResult + message_id: str, + **kwargs + ) -> SendStatusResult: """Gets the status of a message sent previously. :param message_id: System generated message id (GUID) returned from a previous call to send email @@ -95,9 +100,9 @@ def get_send_status( **kwargs ) - async def __enter__(self) -> "EmailClient": - await self._generated_client.__enter__() + def __enter__(self) -> "EmailClient": + self._generated_client.__enter__() return self - async def __exit__(self, *args) -> None: - await self._generated_client.__exit__(*args) + def __exit__(self, *args) -> None: + self._generated_client.__exit__(*args) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py index d4197ede0e38..de6ccc113f54 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py @@ -8,6 +8,7 @@ import urllib import base64 import hmac +from azure.core.credentials import AzureKeyCredential from azure.core.pipeline.policies import SansIOHTTPPolicy from .utils import get_current_utc_time @@ -17,7 +18,7 @@ class HMACCredentialsPolicy(SansIOHTTPPolicy): def __init__(self, host, # type: str - access_key, # type: str + access_key, # type: Union[str, AzureKeyCredential] decode_url=False # type: bool ): # type: (...) -> None @@ -35,7 +36,11 @@ def __init__(self, def _compute_hmac(self, value # type: str ): - decoded_secret = base64.b64decode(self._access_key) + 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() diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py index abbdc2ee0aa0..8aa1fcf6b656 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -5,7 +5,10 @@ # -------------------------------------------------------------------------- from uuid import uuid4 +from azure.core.credentials import AzureKeyCredential +from azure.core.credentials_async import AsyncTokenCredential from azure.core.tracing.decorator_async import distributed_trace_async +from typing import Union from .._shared.utils import parse_connection_str, get_current_utc_time from .._shared.policy import HMACCredentialsPolicy from .._generated.aio._azure_communication_email_service import AzureCommunicationEmailService @@ -19,16 +22,19 @@ class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword :param str endpoint: The endpoint url for Azure Communication Service resource. - :param AsyncTokenCredential credential: - The AsyncTokenCredential we use to authenticate against the service. + :param Union[AsyncTokenCredential, AzureKeyCredential] credential: + The credential we use to authenticate against the service. + """ """ def __init__( self, - endpoint, # type: str - credential, # type: AsyncTokenCredential - **kwargs # type: Any - ): - # type: (...) -> None + endpoint: str, + credential: Union[AsyncTokenCredential, AzureKeyCredential], + **kwargs + ) -> None: + if endpoint.endswith("/"): + endpoint = endpoint[:-1] + authentication_policy = HMACCredentialsPolicy(endpoint, credential) self._generated_client = AzureCommunicationEmailService( @@ -41,9 +47,9 @@ def __init__( @classmethod def from_connection_string( cls, - conn_str, # type: str - **kwargs # type: Any - ): # type: (...) -> EmailClient + conn_str: str, + **kwargs + ) -> 'EmailClient': """Create EmailClient from a Connection String. :param str conn_str: @@ -53,14 +59,14 @@ def from_connection_string( """ endpoint, access_key = parse_connection_str(conn_str) - return cls(endpoint, access_key, **kwargs) + return cls(endpoint, AzureKeyCredential(access_key), **kwargs) @distributed_trace_async async def send( self, - email_message, # type: EmailMessage - **kwargs # type: Any - ): # type: (...) -> SendEmailResult + email_message: EmailMessage, + **kwargs + ) -> SendEmailResult: """Queues an email message to be sent to one or more recipients. :param email_message: The message payload for sending an email. @@ -79,9 +85,9 @@ async def send( @distributed_trace_async async def get_send_status( self, - message_id, #type: str - **kwargs # type: Any - ): # type: (...) -> SendStatusResult + message_id: str, + **kwargs + ) -> SendStatusResult: """Gets the status of a message sent previously. :param message_id: System generated message id (GUID) returned from a previous call to send email diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py index 80a4892cd92b..a7b08a91e9c2 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -55,10 +55,10 @@ def send_email_with_attachment(self): to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] ) - with open("./attachment.txt", "r") as file: - file_contents = file.read() + with open("./attachment.txt", "rb") as file: + file_bytes = file.read() - file_bytes_b64 = base64.b64encode(bytes(file_contents, 'utf-8')) + file_bytes_b64 = base64.b64encode(file_bytes) attachment = EmailAttachment( name="attachment.txt", diff --git a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py index 519a93aaa075..207fb63cd4af 100644 --- a/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -56,10 +56,10 @@ async def send_email_with_attachment_async(self): to=[EmailAddress(email=self.recipient_address, display_name="Customer Name")] ) - with open("./attachment.txt", "r") as file: - file_contents = file.read() + with open("./attachment.txt", "rb") as file: + file_bytes = file.read() - file_bytes_b64 = base64.b64encode(bytes(file_contents, 'utf-8')) + file_bytes_b64 = base64.b64encode(file_bytes) attachment = EmailAttachment( name="attachment.txt", From e887acfa2516ee377edd9b34de224822b501abcf Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Mon, 11 Jul 2022 13:51:37 -0700 Subject: [PATCH 29/30] Fixing comment error --- .../azure/communication/email/aio/_email_client_async.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py index 8aa1fcf6b656..abdf44229567 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -25,7 +25,6 @@ class EmailClient(object): # pylint: disable=client-accepts-api-version-keyword :param Union[AsyncTokenCredential, AzureKeyCredential] credential: The credential we use to authenticate against the service. """ - """ def __init__( self, endpoint: str, From 2a70bea52e46fb5ae2938454e9064995fc9de022 Mon Sep 17 00:00:00 2001 From: Yogesh Mohanraj Date: Tue, 12 Jul 2022 10:03:10 -0700 Subject: [PATCH 30/30] Updating imports --- .../azure/communication/email/_email_client.py | 2 +- .../azure/communication/email/aio/_email_client_async.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py index 05b65d351c0e..698e061183bb 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -4,11 +4,11 @@ # license information. # -------------------------------------------------------------------------- +from typing import Union from uuid import uuid4 from azure.core.credentials import AzureKeyCredential from azure.core.credentials import TokenCredential from azure.core.tracing.decorator import distributed_trace -from typing import Union from ._shared.utils import parse_connection_str, get_current_utc_time from ._shared.policy import HMACCredentialsPolicy from ._generated._azure_communication_email_service import AzureCommunicationEmailService diff --git a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py index abdf44229567..58e940994f8d 100644 --- a/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -4,11 +4,11 @@ # license information. # -------------------------------------------------------------------------- +from typing import Union from uuid import uuid4 from azure.core.credentials import AzureKeyCredential from azure.core.credentials_async import AsyncTokenCredential from azure.core.tracing.decorator_async import distributed_trace_async -from typing import Union from .._shared.utils import parse_connection_str, get_current_utc_time from .._shared.policy import HMACCredentialsPolicy from .._generated.aio._azure_communication_email_service import AzureCommunicationEmailService