Skip to content

Commit

Permalink
Azure CLI commands for communication (#4206)
Browse files Browse the repository at this point in the history
* Azure CLI commands for communication

* Resolving lint and style errors

* Lint errors fix

* More descriptive about the communication commands

* Adding tests for communication extension

* Made changes for sanatization

* Incorporated review comments

* Azure CLI commands for communication

* Resolving lint and style errors

* Lint errors fix

* More descriptive about the communication commands

* Adding tests for communication extension

* Made changes for sanatization

* Incorporated review comments

* Removing hard coded ACS resource id

* Made changes to run the sample in playback mode

* Change azure_devtools with azure.cli.testsdk imports, as per latest azure-cli changes

* Add communication resource preparer in communication extension

* Add license information in preparers.py

* Updated the version and added rease history.
Updated the Readme file.

Co-authored-by: maulinasharma <[email protected]>
Co-authored-by: Mohammad Irfan <[email protected]>
Co-authored-by: maulinasharma <[email protected]>
Co-authored-by: Mohammad Irfan <[email protected]>
  • Loading branch information
5 people authored Jan 7, 2022
1 parent 6ac7b05 commit 72de9b7
Show file tree
Hide file tree
Showing 21 changed files with 1,332 additions and 4 deletions.
7 changes: 7 additions & 0 deletions src/communication/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
Release History
===============

1.1.0
++++++
* Add communication identity command group.
* Add communication sms command group.
* Add communication phonenumbers command group.


1.0.0
++++++
* GA release.
Expand Down
19 changes: 19 additions & 0 deletions src/communication/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,22 @@ az communication regenerate-key --name "MyCommunicationResource" --key-type "Pri
```
az communication delete --name "MyCommunicationResource" --resource-group "MyResourceGroup"
```
##### Issue-Access-Token #####
```
az communication identity issue-access-token --scope chat
az communication identity issue-access-token --scope chat voip --userid "8:acs:xxxxxx"
```
##### Send-SMS #####
```
az communication sms send-sms --sender "+1833xxxxxxx" \
--recipient "+1425xxxxxxx" --message "Hello there!!"
```
##### List-Phonenumbers #####
```
az communication phonenumbers list-phonenumbers
```
##### Show-Phonenumber #####
```
az communication phonenumbers show-phonenumber --phonenumber "+1833xxxxxxx"
```
28 changes: 26 additions & 2 deletions src/communication/azext_communication/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# --------------------------------------------------------------------------

from azure.cli.core import AzCommandsLoader
from azure.cli.core.commands import AzCommandGroup
from azext_communication.generated._help import helps # pylint: disable=unused-import
try:
from azext_communication.manual._help import helps # pylint: disable=reimported
Expand All @@ -24,8 +25,9 @@ def __init__(self, cli_ctx=None):
communication_custom = CliCommandType(
operations_tmpl='azext_communication.custom#{}',
client_factory=cf_communication_cl)
parent = super(CommunicationServiceManagementClientCommandsLoader, self)
parent.__init__(cli_ctx=cli_ctx, custom_command_type=communication_custom)
parent = super()
parent.__init__(cli_ctx=cli_ctx, custom_command_type=communication_custom,
command_group_cls=CommunicationCommandGroup)

def load_command_table(self, args):
from azext_communication.generated.commands import load_command_table
Expand All @@ -47,4 +49,26 @@ def load_arguments(self, command):
pass


class CommunicationCommandGroup(AzCommandGroup):

def communication_custom_command(self, name, method_name, **kwargs):
command_name = self.custom_command(name, method_name, **kwargs)
self._register_data_plane_account_arguments(command_name)

def _register_data_plane_account_arguments(self, command_name):
""" Add parameters required to create a communication client """
from .manual._validators import validate_client_parameters

command = self.command_loader.command_table.get(command_name, None)

if not command:
return

group_name = 'communication'
command.add_argument('connection_string', '--connection-string', required=False, default=None,
validator=validate_client_parameters, arg_group=group_name,
help='Communication connection string. Environment variable: '
'AZURE_COMMUNICATION_CONNECTION_STRING')


COMMAND_LOADER_CLS = CommunicationServiceManagementClientCommandsLoader
26 changes: 26 additions & 0 deletions src/communication/azext_communication/manual/_client_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------


# pylint: disable=unused-argument
def cf_communication_identity(cli_ctx, kwargs):
from azure.communication.identity import CommunicationIdentityClient
connection_string = kwargs.pop('connection_string', None)
client = CommunicationIdentityClient.from_connection_string(connection_string)
return client


def cf_communication_sms(cli_ctx, kwargs):
from azure.communication.sms import SmsClient
connection_string = kwargs.pop('connection_string', None)
client = SmsClient.from_connection_string(connection_string)
return client


def cf_communication_phonenumbers(cli_ctx, kwargs):
from azure.communication.phonenumbers import PhoneNumbersClient
connection_string = kwargs.pop('connection_string', None)
client = PhoneNumbersClient.from_connection_string(connection_string)
return client
55 changes: 55 additions & 0 deletions src/communication/azext_communication/manual/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,58 @@
text: |-
az communication wait --name "MyCommunicationResource" --resource-group "MyResourceGroup" --deleted
"""

helps['communication identity'] = """
type: group
short-summary: Commands to manage User Identity for a CommunicationService resource.
"""

helps['communication identity issue-access-token'] = """
type: command
short-summary: "Issues a new access token with the specified scopes for a given User Identity. If no User Identity is specified, creates a new User Identity as well."
examples:
- name: issue-access-token
text: |-
az communication identity issue-access-token --scope chat
- name: issue-access-token with multiple scopes and userid
text: |-
az communication identity issue-access-token --scope chat voip --userid "8:acs:xxxxxx"
"""

helps['communication sms'] = """
type: group
short-summary: Commands to manage SMS for a CommunicationService resource.
"""

helps['communication sms send-sms'] = """
type: command
short-summary: "Sends an SMS from the sender phone number to the recipient phone number."
examples:
- name: send sms
text: |-
az communication sms send-sms --sender "+1833xxxxxxx" \
--recipient "+1425xxxxxxx" --message "Hello there!!"
"""

helps['communication phonenumbers'] = """
type: group
short-summary: Commands to manage phone numbers for a CommunicationService resource.
"""

helps['communication phonenumbers list-phonenumbers'] = """
type: command
short-summary: "Lists all phone numbers associated with the CommunicationService resource."
examples:
- name: list phonenumbers
text: |-
az communication phonenumbers list-phonenumbers
"""

helps['communication phonenumbers show-phonenumber'] = """
type: command
short-summary: "Shows the details for a phone number associated with the CommunicationService resource."
examples:
- name: show phonenumber
text: |-
az communication phonenumbers show-phonenumber --phonenumber "+1833xxxxxxx"
"""
14 changes: 14 additions & 0 deletions src/communication/azext_communication/manual/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,17 @@
def load_arguments(self, _):
with self.argument_context('communication update') as c:
c.argument('location', validator=None)

with self.argument_context('communication identity issue-access-token') as c:
c.argument('userid', options_list=['--userid', '-u'], type=str, help='ACS identifier')
c.argument('scopes', options_list=[
'--scope', '-s'], nargs='+', help='list of scopes for an access token ex: chat/voip')

with self.argument_context('communication sms send-sms') as c:
c.argument('sender', options_list=['--sender', '-s'], type=str, help='The sender of the SMS')
c.argument('recipient', options_list=['--recipient', '-r'], type=str, help='The recipient of the SMS')
c.argument('message', options_list=['--message', '-m'], type=str, help='The message in the SMS')

with self.argument_context('communication phonenumbers show-phonenumber') as c:
c.argument('phonenumber', options_list=[
'--phonenumber', '-p'], type=str, help='Phone number to get information about')
17 changes: 17 additions & 0 deletions src/communication/azext_communication/manual/_validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------


def get_config_value(cmd, section, key, default):
return cmd.cli_ctx.config.get(section, key, default)


def validate_client_parameters(cmd, namespace):
""" Retrieves communication connection parameters from environment variables
and parses out connection string into account name and key """
n = namespace

if not n.connection_string:
n.connection_string = get_config_value(cmd, 'communication', 'connection_string', None)
22 changes: 22 additions & 0 deletions src/communication/azext_communication/manual/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=line-too-long
from azext_communication.manual._client_factory import cf_communication_identity
from azext_communication.manual._client_factory import cf_communication_sms
from azext_communication.manual._client_factory import cf_communication_phonenumbers


def load_command_table(self, _):

with self.command_group('communication identity', client_factory=cf_communication_identity) as g:
g.communication_custom_command('issue-access-token', "issue_access_token", client_factory=cf_communication_identity)

with self.command_group('communication sms', client_factory=cf_communication_sms) as g:
g.communication_custom_command('send-sms', 'communication_send_sms')

with self.command_group('communication phonenumbers', client_factory=cf_communication_phonenumbers) as g:
g.communication_custom_command('list-phonenumbers', 'communication_list_phonenumbers')
g.communication_custom_command('show-phonenumber', 'communication_show_phonenumber')
36 changes: 36 additions & 0 deletions src/communication/azext_communication/manual/custom.py
Original file line number Diff line number Diff line change
@@ -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 azure.communication.identity import CommunicationUserIdentifier


def issue_access_token(client, scopes, userid=None):
user_token_data = {"user_id": userid, "token": "", "expires_on": ""}
if userid is not None:
user = CommunicationUserIdentifier(userid)
token_data = client.get_token(user, scopes)
user_token_data["token"] = token_data.token
user_token_data["expires_on"] = str(token_data.expires_on)
else:
identity_token_result = client.create_user_and_token(scopes)
if len(identity_token_result) >= 2:
user_token_data["user_id"] = identity_token_result[0].properties['id']
user_token_data["token"] = identity_token_result[1].token
user_token_data["expires_on"] = str(identity_token_result[1].expires_on)

return user_token_data


def communication_send_sms(client, sender, recipient, message):
return client.send(from_=sender, to=recipient, message=message)


def communication_list_phonenumbers(client):
return client.list_purchased_phone_numbers()


def communication_show_phonenumber(client, phonenumber):
return client.get_purchased_phone_number(phonenumber)
66 changes: 66 additions & 0 deletions src/communication/azext_communication/tests/latest/preparers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# --------------------------------------------------------------------------
# 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.cli.testsdk.scenario_tests import SingleValueReplacer
from azure.cli.testsdk.preparers import NoTrafficRecordingPreparer, ResourceGroupPreparer
from azure.cli.testsdk.exceptions import CliTestError
from azure.cli.testsdk.reverse_dependency import get_dummy_cli
import os

# Communication resource Preparer and its shorthand decorator
# pylint: disable=too-many-instance-attributes
class CommunicationResourcePreparer(NoTrafficRecordingPreparer, SingleValueReplacer):
def __init__(self, name_prefix='clitest', location='Global', data_location='United States', length=24,
parameter_name='communication_resource', resource_group_parameter_name='resource_group', skip_delete=True,
dev_setting_name='AZURE_CLI_TEST_DEV_COMMUNICATION_RESOURCE_NAME', key='cr'):
super(CommunicationResourcePreparer, self).__init__(name_prefix, length)
self.cli_ctx = get_dummy_cli()
self.location = location
self.data_location = data_location
self.resource_group_parameter_name = resource_group_parameter_name
self.skip_delete = skip_delete
self.parameter_name = parameter_name
self.key = key
self.dev_setting_name = os.environ.get(dev_setting_name, None)

def create_resource(self, name, **kwargs):
group = self._get_resource_group(**kwargs)

if not self.dev_setting_name:

template = 'az communication create --name {} --location {} --data-location "{}" --resource-group {} '
self.live_only_execute(self.cli_ctx, template.format(
name, self.location, self.data_location, group))
else:
name = self.dev_setting_name

try:
account_key = self.live_only_execute(self.cli_ctx,
'az communication list-key --name {} --resource-group {} --query "primaryConnectionString" -otsv'
.format(name, group)).output.strip()
except AttributeError: # live only execute returns None if playing from record
account_key = None

self.test_class_instance.kwargs[self.key] = name
return {self.parameter_name: name,
self.parameter_name + '_info': (name, account_key or 'endpoint=https://sanitized.communication.azure.com/;accesskey=fake===')}

def remove_resource(self, name, **kwargs):
if not self.skip_delete and not self.dev_setting_name:
group = self._get_resource_group(**kwargs)
self.live_only_execute(self.cli_ctx, 'az communication delete --name {} --resource-group {} --yes'.format(name, group))

def _get_resource_group(self, **kwargs):
try:
return kwargs.get(self.resource_group_parameter_name)
except KeyError:
template = 'To create a communication resource a resource group is required. Please add ' \
'decorator @{} in front of this communication resource preparer.'
raise CliTestError(template.format(ResourceGroupPreparer.__name__))
Loading

0 comments on commit 72de9b7

Please sign in to comment.