From 7b2489a349e1806461712fef305a3b9d3f64f2fd Mon Sep 17 00:00:00 2001 From: Mayssam Mohammadi Nevisi Date: Thu, 19 Jan 2023 12:30:57 -0800 Subject: [PATCH 1/4] [Communication] update communication module version to 1.4.1, remove redundant version in setup --- src/communication/HISTORY.rst | 6 ++++++ src/communication/azext_communication/version.py | 2 +- src/communication/setup.py | 1 - 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/communication/HISTORY.rst b/src/communication/HISTORY.rst index 670a8b2081e..be4478084e5 100644 --- a/src/communication/HISTORY.rst +++ b/src/communication/HISTORY.rst @@ -3,6 +3,12 @@ Release History =============== +1.4.1 +++++++ + * Update version missed in previous release + * Remove redundant version definition in setup.py + + 1.4.0 ++++++ * Add communication rooms command group in preview mode diff --git a/src/communication/azext_communication/version.py b/src/communication/azext_communication/version.py index ddbdf2a43cb..1aca1eb313f 100644 --- a/src/communication/azext_communication/version.py +++ b/src/communication/azext_communication/version.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- -VERSION = '1.3.0' +VERSION = '1.4.1' def cli_application_id(): diff --git a/src/communication/setup.py b/src/communication/setup.py index 16ff4c23d9e..8aacfaf082f 100644 --- a/src/communication/setup.py +++ b/src/communication/setup.py @@ -10,7 +10,6 @@ from setuptools import setup, find_packages # HISTORY.rst entry. -VERSION = '1.4.0' try: from azext_communication.version import VERSION except ImportError: From 8a3c972550433f043fd3bc0efdb8bbfddf919768 Mon Sep 17 00:00:00 2001 From: Mayssam Mohammadi Nevisi Date: Wed, 1 Feb 2023 00:19:23 -0800 Subject: [PATCH 2/4] [Communication] add email command group in preview mode --- src/communication/HISTORY.rst | 5 ++ src/communication/README.md | 8 ++ .../manual/_client_factory.py | 12 +++ .../azext_communication/manual/_help.py | 18 ++++ .../azext_communication/manual/_params.py | 32 +++++++ .../azext_communication/manual/commands.py | 12 +++ .../azext_communication/manual/custom.py | 87 +++++++++++++++++++ .../azext_communication/version.py | 2 +- src/communication/setup.py | 1 + 9 files changed, 176 insertions(+), 1 deletion(-) diff --git a/src/communication/HISTORY.rst b/src/communication/HISTORY.rst index be4478084e5..3cdb7f8aa89 100644 --- a/src/communication/HISTORY.rst +++ b/src/communication/HISTORY.rst @@ -3,6 +3,11 @@ Release History =============== +1.5.0 +++++++ + * Add communication email command group in preview mode + + 1.4.1 ++++++ * Update version missed in previous release diff --git a/src/communication/README.md b/src/communication/README.md index 6566255259c..84e7ab91c4d 100644 --- a/src/communication/README.md +++ b/src/communication/README.md @@ -178,3 +178,11 @@ az communication rooms participant update --room "roomId" --presenter-participan ``` az communication rooms participant remove --room "roomId" --participants "8:acs:xxxxxx" "8:acs:xxxxxx" "8:acs:xxxxxx" "8:acs:xxxxxx" ``` +##### Send-Email ##### +``` +az communication email send --sender "NoReply@contoso.com" --subject "Contoso Update" --to "user1@user1-domain.com" "user2@user2-domain.com" --text "Hello valued client. There is an update." +``` +##### Get-Email-Status ##### +``` +az communication email status get --message-id "01234567-89ab-cdef-0123-012345678901" +``` diff --git a/src/communication/azext_communication/manual/_client_factory.py b/src/communication/azext_communication/manual/_client_factory.py index 49b873e6de8..72d24735ffe 100644 --- a/src/communication/azext_communication/manual/_client_factory.py +++ b/src/communication/azext_communication/manual/_client_factory.py @@ -78,3 +78,15 @@ def cf_communication_rooms(cli_ctx, kwargs): client = RoomsClient.from_connection_string(connection_string) return client + + +def cf_communication_email(cli_ctx, kwargs): + from azure.communication.email import EmailClient + + connection_string = kwargs.pop('connection_string', None) + if connection_string is None: + error_msg = 'Please specify --connection-string, or set AZURE_COMMUNICATION_CONNECTION_STRING.' + raise RequiredArgumentMissingError(error_msg) + + client = EmailClient.from_connection_string(connection_string) + return client diff --git a/src/communication/azext_communication/manual/_help.py b/src/communication/azext_communication/manual/_help.py index 947d984b82b..5f94322f0f4 100644 --- a/src/communication/azext_communication/manual/_help.py +++ b/src/communication/azext_communication/manual/_help.py @@ -504,3 +504,21 @@ text: |- az communication rooms participant remove --room "12345678901234567" --participants "8:acs:xxxxxx" "8:acs:xxxxxx" "8:acs:xxxxxx" """ + +helps['communication email send'] = """ + type: command + short-summary: "Send an email." + examples: + - name: Send an email from an existing domain + text: |- + az communication email send --sender "NoReply@contoso.com" --subject "Contoso Update" --to "user1@user1-domain.com" "user2@user2-domain.com" --text "Hello valued client. There is an update." +""" + +helps['communication email status get'] = """ + type: command + short-summary: "Get status of an email previously sent." + examples: + - name: Get status of an email + text: |- + az communication email status get --message-id "01234567-89ab-cdef-0123-012345678901" +""" diff --git a/src/communication/azext_communication/manual/_params.py b/src/communication/azext_communication/manual/_params.py index e8286701c24..8fafbfb1b3c 100644 --- a/src/communication/azext_communication/manual/_params.py +++ b/src/communication/azext_communication/manual/_params.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +from azure.cli.core.commands.parameters import get_enum_type, get_three_state_flag + def load_arguments(self, _): with self.argument_context('communication update') as c: @@ -13,6 +15,7 @@ def load_arguments(self, _): _load_phonenumber_arguments(self) _load_chat_arguments(self) _load_rooms_arguments(self) + _load_email_arguments(self) def _load_identity_arguments(self): @@ -242,3 +245,32 @@ def _load_rooms_arguments(self): type=str, help='Room Id') c.argument('participants', options_list=['--participants'], nargs='+', help='Collection of identities that will be removed from the room.') + + +def _load_email_arguments(self): + with self.argument_context('communication email send') as c: + c.argument('sender', options_list=['--sender'], type=str, help='Sender email address from a verified domain.') + c.argument('subject', options_list=['--subject'], type=str, help='Subject of the email message.') + c.argument('text', options_list=['--text'], type=str, help='Plain text version of the email message. Optional.') + c.argument('html', options_list=['--html'], type=str, help='Html version of the email message. Optional.') + c.argument('importance', options_list=['--importance'], arg_type=get_enum_type(['normal', 'low', 'high']), + help='The importance type for the email. Known values are: high,' + ' normal, and low. Default is normal. Optional') + c.argument('recipients_to', options_list=['--to'], nargs='+', help='Recepients email addresses.') + c.argument('recipients_cc', options_list=['--cc'], nargs='+', help='Carbon copy email addresses.') + c.argument('recipients_bcc', options_list=['--bcc'], nargs='+', help='Blind carbon copy email addresses.') + c.argument('reply_to', options_list=['--reply-to'], type=str, help='Reply-to email address. Optional.') + c.argument('disable_tracking', options_list=['--disable-tracking'], arg_type=get_three_state_flag(), + help='Indicates whether user engagement tracking should be disabled for this request if' + 'the resource-level user engagement tracking setting was already enabled. Optional.') + c.argument('attachments', options_list=['--attachments'], nargs='+', + help='List of email attachments. Optional.') + c.argument('attachment_types', options_list=['--attachment-types'], nargs='+', + help='List of email attachment types, in the same order of attachments.' + ' Required for each attachment. 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') + + with self.argument_context('communication email send') as c: + c.argument('message_id', options_list=['--message-id'], type=str, + help='System generated message id (GUID) returned from a previous call to send email') diff --git a/src/communication/azext_communication/manual/commands.py b/src/communication/azext_communication/manual/commands.py index c2ae1d74c0b..098f21b45e7 100644 --- a/src/communication/azext_communication/manual/commands.py +++ b/src/communication/azext_communication/manual/commands.py @@ -9,6 +9,7 @@ from azext_communication.manual._client_factory import cf_communication_phonenumbers from azext_communication.manual._client_factory import cf_communication_chat from azext_communication.manual._client_factory import cf_communication_rooms +from azext_communication.manual._client_factory import cf_communication_email def load_command_table(self, _): @@ -17,6 +18,7 @@ def load_command_table(self, _): _load_phonenumber_command_table(self) _load_chat_command_table(self) _load_rooms_command_table(self) + _load_email_command_table(self) def _load_identity_command_table(self): @@ -102,3 +104,13 @@ def _load_rooms_command_table(self): g.communication_custom_command('add', 'communication_rooms_add_participants', rooms_arguments) g.communication_custom_command('update', 'communication_rooms_update_participants', rooms_arguments) g.communication_custom_command('remove', 'communication_rooms_remove_participants', rooms_arguments, confirmation=True) + + +def _load_email_command_table(self): + rooms_arguments = ['connection_string'] + self.command_group('communication email', is_preview=True) + + with self.command_group('communication email', client_factory=cf_communication_email, is_preview=True) as g: + g.communication_custom_command('send', 'communication_email_send', rooms_arguments) + with self.command_group('communication email', client_factory=cf_communication_email, is_preview=True) as g: + g.communication_custom_command('status get', 'communication_email_get_status', rooms_arguments) diff --git a/src/communication/azext_communication/manual/custom.py b/src/communication/azext_communication/manual/custom.py index 97b0eb794b9..cbbce5e2eef 100644 --- a/src/communication/azext_communication/manual/custom.py +++ b/src/communication/azext_communication/manual/custom.py @@ -293,3 +293,90 @@ def communication_rooms_remove_participants(client, room_id, participants): raise except Exception as ex: sys.exit(str(ex)) + + +def __get_attachment_content(filename, filetype): + import base64 + import os + from azure.communication.email import EmailAttachment + + _, tail = os.path.split(filename) + with open(filename, "r", encoding="utf-8") as file: + file_content = file.read() + + base64_content = base64.b64encode(bytes(file_content, 'utf-8')) + + return EmailAttachment( + name=tail, + attachment_type=filetype, + content_bytes_base64=base64_content.decode(), + ) + + +def communication_email_send(client, + subject, + sender, + recipients_to, + disable_tracking=False, + text=None, + html=None, + importance='normal', + recipients_cc=None, + recipients_bcc=None, + reply_to=None, + attachments=None, + attachment_types=None): + + from azure.communication.email import EmailContent, EmailAddress, EmailMessage, EmailRecipients + from knack.util import CLIError + + try: + email_content = EmailContent( + subject=subject, + plain_text=text, + html=html, + ) + + to_address = [EmailAddress(email=r) for r in recipients_to] + + reply_to_address = None if reply_to is None else [EmailAddress(email=reply_to)] + + if attachments is None and attachment_types is None: + attachments_list = None + elif attachments is None or attachment_types is None: + raise CLIError('Number of attachments and attachment-types should match.') + elif len(attachments) != len(attachment_types): + raise CLIError('Number of attachments and attachment-types should match.') + else: + attachments_list = [ + __get_attachment_content(attachments[i], attachment_types[i]) + for i in range(len(attachments)) + ] + + message = EmailMessage( + sender=sender, + content=email_content, + recipients=EmailRecipients( + to=to_address, + cc=[] if recipients_cc is None else [EmailAddress(email=r) for r in recipients_cc], + bcc=[] if recipients_bcc is None else [EmailAddress(email=r) for r in recipients_bcc]), + importance=importance, + reply_to=reply_to_address, + disable_user_engagement_tracking=disable_tracking, + attachments=attachments_list, + ) + + return client.send(message) + except HttpResponseError: + raise + except Exception as ex: + sys.exit(str(ex)) + + +def communication_email_get_status(client, message_id): + try: + return client.get_send_status(message_id) + except HttpResponseError: + raise + except Exception as ex: + sys.exit(str(ex)) diff --git a/src/communication/azext_communication/version.py b/src/communication/azext_communication/version.py index 1aca1eb313f..6095a817d78 100644 --- a/src/communication/azext_communication/version.py +++ b/src/communication/azext_communication/version.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- -VERSION = '1.4.1' +VERSION = '1.5.0' def cli_application_id(): diff --git a/src/communication/setup.py b/src/communication/setup.py index 8aacfaf082f..c9125a03679 100644 --- a/src/communication/setup.py +++ b/src/communication/setup.py @@ -34,6 +34,7 @@ 'azure-communication-sms', 'azure-communication-chat', 'azure-communication-rooms', + 'azure-communication-email >= 1.0.0b1', ] try: From 1ea485a81acf691b53e5f443c906c83e00ba4395 Mon Sep 17 00:00:00 2001 From: Mayssam Mohammadi Nevisi Date: Wed, 1 Feb 2023 10:54:49 -0800 Subject: [PATCH 3/4] fix typo in email status get parameter setup --- src/communication/azext_communication/manual/_params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/communication/azext_communication/manual/_params.py b/src/communication/azext_communication/manual/_params.py index 8fafbfb1b3c..01f43fc2e24 100644 --- a/src/communication/azext_communication/manual/_params.py +++ b/src/communication/azext_communication/manual/_params.py @@ -271,6 +271,6 @@ def _load_email_arguments(self): ' 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') - with self.argument_context('communication email send') as c: + with self.argument_context('communication email status get') as c: c.argument('message_id', options_list=['--message-id'], type=str, help='System generated message id (GUID) returned from a previous call to send email') From 5312dc3f19062afb98b7f1db5778b850b07e035d Mon Sep 17 00:00:00 2001 From: Mayssam Mohammadi Nevisi Date: Wed, 1 Feb 2023 13:04:44 -0800 Subject: [PATCH 4/4] Add more help for email commands. --- src/communication/azext_communication/manual/_help.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/communication/azext_communication/manual/_help.py b/src/communication/azext_communication/manual/_help.py index 5f94322f0f4..ea70452da9b 100644 --- a/src/communication/azext_communication/manual/_help.py +++ b/src/communication/azext_communication/manual/_help.py @@ -505,6 +505,16 @@ az communication rooms participant remove --room "12345678901234567" --participants "8:acs:xxxxxx" "8:acs:xxxxxx" "8:acs:xxxxxx" """ +helps['communication email'] = """ + type: group + short-summary: Commands to send emails and get the status of emails previously sent using Azure Communication Services Email service. +""" + +helps['communication email status'] = """ + type: group + short-summary: Commands to get the status of emails previously sent using Azure Communication Services Email service. +""" + helps['communication email send'] = """ type: command short-summary: "Send an email."