diff --git a/sdk/communication/azure-communication-email/CHANGELOG.md b/sdk/communication/azure-communication-email/CHANGELOG.md new file mode 100644 index 000000000000..616fb0662508 --- /dev/null +++ b/sdk/communication/azure-communication-email/CHANGELOG.md @@ -0,0 +1,8 @@ +# Release History + +## 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/LICENSE b/sdk/communication/azure-communication-email/LICENSE new file mode 100644 index 000000000000..b2f52a2bad4e --- /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. diff --git a/sdk/communication/azure-communication-email/MANIFEST.in b/sdk/communication/azure-communication-email/MANIFEST.in new file mode 100644 index 000000000000..e888b3235b57 --- /dev/null +++ b/sdk/communication/azure-communication-email/MANIFEST.in @@ -0,0 +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 \ 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..3b45b3c2bb77 --- /dev/null +++ b/sdk/communication/azure-communication-email/README.md @@ -0,0 +1,193 @@ +# Azure Communication Email client library for Python + +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 + +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.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`. + +```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 +import base64 + +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") + +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="attachment.txt", + attachment_type="txt", + content_bytes_base64=file_bytes_b64.decode() +) + +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(response.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 +from azure.core.exceptions import HttpResponseError + +try: + response = email_client.send(message) +except HttpResponseError 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 +[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/ +[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/azure/__init__.py b/sdk/communication/azure-communication-email/azure/__init__.py new file mode 100644 index 000000000000..8db66d3d0f0f --- /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..8db66d3d0f0f --- /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..f9531f95ac23 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/__init__.py @@ -0,0 +1,36 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from ._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', +] 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..698e061183bb --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_email_client.py @@ -0,0 +1,108 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from typing import Union +from uuid import uuid4 +from azure.core.credentials import AzureKeyCredential +from azure.core.credentials import TokenCredential +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): # 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 endpoint: + The endpoint url for Azure Communication Service resource. + :param Union[TokenCredential, AzureKeyCredential] credential: + The credential we use to authenticate against the service. + """ + def __init__( + self, + endpoint: str, + credential: Union[TokenCredential, AzureKeyCredential], + **kwargs + ) -> None: + if endpoint.endswith("/"): + endpoint = endpoint[:-1] + + authentication_policy = HMACCredentialsPolicy(endpoint, credential) + + self._generated_client = AzureCommunicationEmailService( + endpoint, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs + ) + + @classmethod + def from_connection_string( + cls, + conn_str: str, + **kwargs + ) -> '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, AzureKeyCredential(access_key), **kwargs) + + @distributed_trace + def send( + self, + 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. + :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: 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 + :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 + ) + + def __enter__(self) -> "EmailClient": + self._generated_client.__enter__() + return self + + def __exit__(self, *args) -> None: + self._generated_client.__exit__(*args) 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-email/azure/communication/email/_shared/policy.py b/sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py new file mode 100644 index 000000000000..de6ccc113f54 --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/_shared/policy.py @@ -0,0 +1,96 @@ +# ------------------------------------------------------------------------ +# 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.credentials import AzureKeyCredential +from azure.core.pipeline.policies import SansIOHTTPPolicy +from .utils import get_current_utc_time + +class HMACCredentialsPolicy(SansIOHTTPPolicy): + """Implementation of HMAC authentication policy. + """ + + def __init__(self, + host, # type: str + access_key, # type: Union[str, AzureKeyCredential] + decode_url=False # type: bool + ): + # type: (...) -> None + super(HMACCredentialsPolicy, self).__init__() + + if host.startswith("https://"): + self._host = host.replace("https://", "") + + if host.startswith("http://"): + self._host = host.replace("http://", "") + + self._access_key = access_key + self._decode_url = decode_url + + def _compute_hmac(self, + value # type: str + ): + if isinstance(self._access_key, AzureKeyCredential): + decoded_secret = base64.b64decode(self._access_key.key) + else: + decoded_secret = base64.b64decode(self._access_key) + + digest = hmac.new( + decoded_secret, value.encode("utf-8"), hashlib.sha256 + ).digest() + + return base64.b64encode(digest).decode("utf-8") + + def _sign_request(self, request): + verb = request.http_request.method.upper() + + # Get the path and query from url, which looks like https://host/path/query + 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) 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..eadf444aa551 --- /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 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..58e940994f8d --- /dev/null +++ b/sdk/communication/azure-communication-email/azure/communication/email/aio/_email_client_async.py @@ -0,0 +1,111 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from typing import Union +from 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 .._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): # 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 endpoint: + The endpoint url for Azure Communication Service resource. + :param Union[AsyncTokenCredential, AzureKeyCredential] credential: + The credential we use to authenticate against the service. + """ + def __init__( + self, + endpoint: str, + credential: Union[AsyncTokenCredential, AzureKeyCredential], + **kwargs + ) -> None: + if endpoint.endswith("/"): + endpoint = endpoint[:-1] + + authentication_policy = HMACCredentialsPolicy(endpoint, credential) + + self._generated_client = AzureCommunicationEmailService( + endpoint, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs + ) + + @classmethod + def from_connection_string( + cls, + conn_str: str, + **kwargs + ) -> '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, AzureKeyCredential(access_key), **kwargs) + + @distributed_trace_async + async def send( + self, + 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. + :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: 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 + :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() 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_requirements.txt b/sdk/communication/azure-communication-email/dev_requirements.txt new file mode 100644 index 000000000000..733dcf452e64 --- /dev/null +++ b/sdk/communication/azure-communication-email/dev_requirements.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-tornasync==0.6.0.post2 \ No newline at end of file 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 new file mode 100644 index 000000000000..01843a216ab2 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample.py @@ -0,0 +1,76 @@ +# 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 receive the email +""" + +import os +import sys +from azure.core.exceptions import HttpResponseError +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.from_connection_string(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 + ) + try: + # 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) + except HttpResponseError as ex: + print(ex) + pass + +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..9ca2596a33e6 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/check_message_status_sample_async.py @@ -0,0 +1,79 @@ +# 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 receive the email +""" + +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, + 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.from_connection_string(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 HttpResponseError as ex: + print(ex) + pass + +if __name__ == '__main__': + sample = EmailCheckMessageStatusSampleAsync() + 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 new file mode 100644 index 000000000000..d0ac33f19b29 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample.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_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 receive the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will receive the email +""" + +import os +import sys +from azure.core.exceptions import HttpResponseError +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.from_connection_string(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"), + ], + 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"), + ] + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients + ) + + 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() + 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..975d49f2874d --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_multiple_recipients_sample_async.py @@ -0,0 +1,87 @@ +# 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 receive the email + 4) SECOND_RECIPIENT_ADDRESS - the second address that will receive the email +""" + +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, + 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.from_connection_string(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"), + ], + 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"), + ] + ) + + 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 HttpResponseError as ex: + print(ex) + pass + +if __name__ == '__main__': + sample = EmailMultipleRecipientSampleAsync() + 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..a25ac406568c --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_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_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 receive the email +""" + +import os +import sys +from azure.core.exceptions import HttpResponseError +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.from_connection_string(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 + ) + + 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() + 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..33b067503a09 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_to_single_recipient_sample_async.py @@ -0,0 +1,74 @@ +# 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 receive the email +""" + +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, + 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.from_connection_string(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 HttpResponseError as ex: + print(ex) + pass + +if __name__ == '__main__': + sample = EmailSingleRecipientSampleAsync() + 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..a7b08a91e9c2 --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample.py @@ -0,0 +1,86 @@ +# 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 receive the email +""" + +import base64 +import os +import sys +from azure.core.exceptions import HttpResponseError +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.from_connection_string(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")] + ) + + with open("./attachment.txt", "rb") as file: + file_bytes = file.read() + + file_bytes_b64 = base64.b64encode(file_bytes) + + attachment = EmailAttachment( + name="attachment.txt", + attachment_type="txt", + content_bytes_base64=file_bytes_b64.decode() + ) + + message = EmailMessage( + sender=self.sender_address, + content=content, + recipients=recipients, + attachments=[attachment] + ) + + 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() + 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..207fb63cd4af --- /dev/null +++ b/sdk/communication/azure-communication-email/samples/send_email_with_attachments_sample_async.py @@ -0,0 +1,88 @@ +# 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 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, + 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.from_connection_string(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")] + ) + + with open("./attachment.txt", "rb") as file: + file_bytes = file.read() + + file_bytes_b64 = base64.b64encode(file_bytes) + + attachment = EmailAttachment( + name="attachment.txt", + attachment_type="txt", + content_bytes_base64=file_bytes_b64.decode() + ) + + 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 HttpResponseError as ex: + print(ex) + pass + +if __name__ == '__main__': + sample = EmailWithAttachmentSampleAsync() + asyncio.run(sample.send_email_with_attachment_async()) 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..ecfa0b440e13 --- /dev/null +++ b/sdk/communication/azure-communication-email/sdk_packaging.toml @@ -0,0 +1,10 @@ +[packaging] +auto_update = false +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 new file mode 100644 index 000000000000..01ae9b0708f7 --- /dev/null +++ b/sdk/communication/azure-communication-email/setup.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +#-------------------------------------------------------------------------- + +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 = "MyService Management" + +# 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') + 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: + 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=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 :: 4 - Beta', + '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', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + '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', + ]), + include_package_data=True, + package_data={ + 'pytyped': ['py.typed'], + }, + install_requires=[ + 'msrest>=0.6.21', + 'azure-common~=1.1', + 'azure-mgmt-core>=1.3.1,<2.0.0', + ], + python_requires=">=3.6" +) 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/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..8e8387e7fbd6 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/conftest.py @@ -0,0 +1,56 @@ +# -------------------------------------------------------------------------- +# +# 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, 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") + + 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") + + 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-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/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_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 new file mode 100644 index 000000000000..a9671d91d6dd --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_attachment.json @@ -0,0 +1,59 @@ +{ + "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": "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": "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" + } + ] + }, + "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", + "x-ms-request-id": "someMessageId" + }, + "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..2d5a82ccc01e --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_multiple_recipients.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", + "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": "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" + }, + { + "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", + "x-ms-request-id": "someMessageId" + }, + "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..1b20f55c0e03 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e.pyTestEmailClienttest_send_email_single_recipient.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", + "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:55 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 + } + ], + "Variables": {} +} 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 new file mode 100644 index 000000000000..bc656dc8c6a7 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_attachment.json @@ -0,0 +1,58 @@ +{ + "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": "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": "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" + } + ] + }, + "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", + "x-ms-request-id": "someMessageId" + }, + "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..51e3b1c904fe --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_multiple_recipients.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", + "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": "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" + }, + { + "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", + "x-ms-request-id": "someMessageId" + }, + "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..8c72e1488763 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/recordings/test_email_client_e2e_async.pyTestEmailClienttest_send_email_single_recipient.json @@ -0,0 +1,51 @@ +{ + "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: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 + } + ], + "Variables": {} +} 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..1ed8bc15feb9 --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e.py @@ -0,0 +1,90 @@ +from azure.communication.email import ( + EmailClient, + EmailMessage, + EmailContent, + EmailRecipients, + EmailAddress, + EmailAttachment +) +from devtools_testutils import AzureRecordedTestCase, recorded_by_proxy +from preparers import email_decorator + +class TestEmailClient(AzureRecordedTestCase): + @email_decorator + @recorded_by_proxy + def test_send_email_single_recipient(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")] + ) + ) + + response = email_client.send(message) + assert response.message_id is not None + + @email_decorator + @recorded_by_proxy + def test_send_email_multiple_recipients(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"), + EmailAddress(email=self.recipient_address, display_name="Customer Name 2"), + ] + ) + ) + + response = email_client.send(message) + assert response.message_id is not None + + @email_decorator + @recorded_by_proxy + def test_send_email_attachment(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")] + ), + attachments=[ + EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" #cspell:disable-line + ) + ] + ) + + response = email_client.send(message) + assert response.message_id is not None + + @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")] + ) + ) + + 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..ae5deb6ed7ee --- /dev/null +++ b/sdk/communication/azure-communication-email/tests/test_email_client_e2e_async.py @@ -0,0 +1,97 @@ +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): + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_single_recipient(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")] + ) + ) + + async with email_client: + response = await email_client.send(message) + assert response.message_id is not None + + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_multiple_recipients(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"), + EmailAddress(email=self.recipient_address, display_name="Customer Name 2"), + ] + ) + ) + + async with email_client: + response = await email_client.send(message) + assert response.message_id is not None + + @email_decorator_async + @recorded_by_proxy_async + async def test_send_email_attachment(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")] + ), + attachments=[ + EmailAttachment( + name="readme.txt", + attachment_type="txt", + content_bytes_base64="ZW1haWwgdGVzdCBhdHRhY2htZW50" #cspell:disable-line + ) + ] + ) + + async with email_client: + response = await email_client.send(message) + assert response.message_id is not None + + @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")] + ) + ) + + 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/ci.yml b/sdk/communication/ci.yml index 40619c505859..59ea57e5cbe9 100644 --- a/sdk/communication/ci.yml +++ b/sdk/communication/ci.yml @@ -30,11 +30,14 @@ extends: template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml parameters: ServiceDirectory: communication + TestProxy: true Artifacts: - name: azure-communication-identity safeName: azurecommunicationidentity - name: azure-communication-chat safeName: azurecommunicationchat + - name: azure-communication-email + safeName: azurecommunicationemail - name: azure-mgmt-communication safeName: azuremgmtcommunication - name: azure-communication-sms diff --git a/shared_requirements.txt b/shared_requirements.txt index 7de2bf65a04b..e369bd57759f 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -196,6 +196,8 @@ 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-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