Skip to content

Commit

Permalink
Improved download and create connector experience (#38)
Browse files Browse the repository at this point in the history
- Specified directory during download is created when doesn't exists, but no extra sub-drectory with connector_id is created.
- A sub-drectory with connector_id is still created when destination parameter isn't specified during download.
- A --overwrite option is added during download. When this is parameter specified ALL destination file will be overwritten. Customer will be prompted for file replacement when this parameter is not specified.
- A settings file is now written after the create operation.
- A --overwriter-setting option is added for create. When this is parameter specified the settings file will be overwritten. Customer will be prompted for settings file replacement when this parameter is not specified.
  • Loading branch information
mamurshe committed Jun 25, 2019
1 parent c5e413c commit ba2cbef
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 37 deletions.
2 changes: 1 addition & 1 deletion tools/paconn-cli/paconn.pyproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<Compile Include="paconn\apimanager\apimanager.py" />
<Compile Include="paconn\commands\download.py" />
<Compile Include="paconn\commands\login.py" />
<Compile Include="paconn\operations\create_update.py" />
<Compile Include="paconn\operations\upsert.py" />
<Compile Include="paconn\settings\settings.py" />
<Compile Include="paconn\apimanager\flowrp.py" />
<Compile Include="paconn\apimanager\iconuploader.py" />
Expand Down
10 changes: 5 additions & 5 deletions tools/paconn-cli/paconn/authentication/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from msrestazure.azure_active_directory import AADTokenCredentials

CLIENT_ID = '04b07795-8ddb-461a-bbee-02f9e1bf7b46'
AUTHORITY_URL = 'https://login.microsoftonline.com/{tenant}'
TENANT = 'common'
AUTHORITY_URL = 'https://login.microsoftonline.com/{}'
RESOURCE = 'https://management.core.windows.net/'


Expand All @@ -26,17 +27,16 @@ def __init__(self, resource=RESOURCE, authority_url=AUTHORITY_URL):
self.authority_url = authority_url

def _get_authentication_context(self, tenant):
auth_url = self.authority_url.format(
tenant=tenant)
auth_url = self.authority_url.format(tenant)
return adal.AuthenticationContext(
authority=auth_url,
api_version=None)

def authenticate_device_code(self, client_id=CLIENT_ID):
def authenticate_device_code(self, tenant=TENANT, client_id=CLIENT_ID):
"""
Authenticate the end-user using device auth.
"""
context = self._get_authentication_context('common')
context = self._get_authentication_context(tenant)

code = context.acquire_user_code(
resource=self.resource,
Expand Down
10 changes: 6 additions & 4 deletions tools/paconn-cli/paconn/commands/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from paconn import _CREATE
from paconn.common.util import display
from paconn.settings.util import load_settings_and_powerapps_rp
from paconn.operations.create_update import create_update
from paconn.operations.upsert import upsert


# pylint: disable=too-many-arguments
Expand All @@ -23,7 +23,8 @@ def create(
powerapps_url,
powerapps_version,
client_secret,
settings_file):
settings_file,
overwrite_settings):
"""
Create command.
"""
Expand All @@ -38,10 +39,11 @@ def create(
powerapps_version=powerapps_version,
command_context=_CREATE)

connector_id = create_update(
connector_id = upsert(
powerapps_rp=powerapps_rp,
settings=settings,
client_secret=client_secret,
is_update=False)
is_update=False,
overwrite_settings=overwrite_settings)

display('{} created successfully.'.format(connector_id))
12 changes: 10 additions & 2 deletions tools/paconn-cli/paconn/commands/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@


# pylint: disable=too-many-arguments
def download(environment, connector_id, destination, powerapps_url, powerapps_version, settings_file):
def download(
environment,
connector_id,
destination,
powerapps_url,
powerapps_version,
settings_file,
overwrite):
"""
Download command.
"""
Expand All @@ -34,6 +41,7 @@ def download(environment, connector_id, destination, powerapps_url, powerapps_ve
directory = paconn.operations.download.download(
powerapps_rp=powerapps_rp,
settings=settings,
destination=destination)
destination=destination,
overwrite=overwrite)

display('The connector is downloaded to {}.'.format(directory))
34 changes: 30 additions & 4 deletions tools/paconn-cli/paconn/commands/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

DESTINATION = 'destination'
DESTINATION_OPTIONS = ['--dest', '-d']
DESTINATION_HELP = 'Destination directory.'
DESTINATION_HELP = 'Destination directory. Non-existent directories will be created.'

POWERAPPS_URL = 'powerapps_url'
POWERAPPS_URL_OPTIONS = ['--pau', '-u']
Expand All @@ -49,17 +49,25 @@
SETTINGS_HELP = 'A settings file containing required parameters. When a settings file is specified some commandline parameters are ignored.' # noqa: E501

API_PROPERTIES = 'api_properties'
API_PROPERTIES_OPTIONS = ['--api-prop']
API_PROPERTIES_OPTIONS = ['--api-prop', '-p']
API_PROPERTIES_HELP = 'Location of the API properties JSON document.'

API_DEFINITION = 'api_definition'
API_DEFINITION_OPTIONS = ['--api-def']
API_DEFINITION_OPTIONS = ['--api-def', '-d']
API_DEFINITION_HELP = 'Location of the Open API definition JSON document.'

ICON = 'icon'
ICON_OPTIONS = ['--icon']
ICON_OPTIONS = ['--icon', '-i']
ICON_HELP = 'Location for the icon file.'

OVERWRITE = 'overwrite'
OVERWRITE_OPTIONS = ['--overwrite']
OVERWRITE_HELP = 'Overwrite all the existing connector and settings files.'

OVERWRITE_SETTINGS = 'overwrite_settings'
OVERWRITE_SETTINGS_OPTIONS = ['--overwrite-settings']
OVERWRITE_SETTINGS_HELP = 'Overwrite the existing settings file.'


# pylint: disable=unused-argument
def load_arguments(self, command):
Expand Down Expand Up @@ -106,6 +114,15 @@ def load_arguments(self, command):
type=str,
required=False,
help=SETTINGS_HELP)
arg_context.argument(
OVERWRITE,
options_list=OVERWRITE_OPTIONS,
type=bool,
required=False,
nargs='?',
default=False,
const=True,
help=OVERWRITE_HELP)

with ArgumentsContext(self, _CREATE) as arg_context:
arg_context.argument(
Expand Down Expand Up @@ -156,6 +173,15 @@ def load_arguments(self, command):
type=str,
required=False,
help=SETTINGS_HELP)
arg_context.argument(
OVERWRITE_SETTINGS,
options_list=OVERWRITE_SETTINGS_OPTIONS,
type=bool,
required=False,
nargs='?',
default=False,
const=True,
help=OVERWRITE_SETTINGS_HELP)

with ArgumentsContext(self, _UPDATE) as arg_context:
arg_context.argument(
Expand Down
7 changes: 4 additions & 3 deletions tools/paconn-cli/paconn/commands/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from paconn import _UPDATE
from paconn.common.util import display
from paconn.settings.util import load_settings_and_powerapps_rp
from paconn.operations.create_update import create_update
from paconn.operations.upsert import upsert


# pylint: disable=too-many-arguments
Expand Down Expand Up @@ -38,10 +38,11 @@ def update(
powerapps_version=powerapps_version,
command_context=_UPDATE)

connector_id = create_update(
connector_id = upsert(
powerapps_rp=powerapps_rp,
settings=settings,
client_secret=client_secret,
is_update=True)
is_update=True,
overwrite=False)

display('{} updated successfully.'.format(connector_id))
22 changes: 20 additions & 2 deletions tools/paconn-cli/paconn/common/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import json

from knack.util import CLIError
from knack.prompting import prompt_y_n


def get_config_dir():
Expand Down Expand Up @@ -46,6 +47,23 @@ def ensure_file_exists(file, file_type):
"""

if not file:
raise CLIError('{file_type} must be specified.'.format(file_type=file_type))
raise CLIError('{} must be specified.'.format(file_type))
if not os.path.exists(file):
raise CLIError('File does not exist: {file}'.format(file=file))
raise CLIError('File does not exist: {}'.format(file))


def ensure_overwrite(filename):
overwrite = True
if os.path.exists(filename):
msg = '{} file exists. Do you want to overwrite?'.format(filename)
overwrite = prompt_y_n(msg)

return overwrite


def write_with_prompt(filename, mode, content, overwrite):
if not overwrite:
overwrite = ensure_overwrite(filename)

if overwrite:
open(filename, mode=mode).write(content)
70 changes: 57 additions & 13 deletions tools/paconn-cli/paconn/operations/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
import requests

from knack.util import CLIError
from knack.prompting import prompt_y_n

from paconn.common.util import format_json
from paconn.settings.settingsserializer import SettingsSerializer
from paconn.settings.util import write_settings, SETTINGS_FILE

from paconn.operations.json_keys import (
_PROPERTIES,
Expand All @@ -32,19 +33,48 @@ def _prepare_directory(destination, connector_id):
"""
Create directory for saving a connector.
"""
if destination and os.path.isdir(destination):
os.chdir(destination)

if os.path.isdir(connector_id):
dir_error = '{} directory already exists. Please remove the directory before continuing.'
raise CLIError(dir_error.format(connector_id))
os.mkdir(connector_id)
os.chdir(connector_id)
# Use the destination directory when provided
if destination:
if not os.path.exists(destination):
# Create all sub-directories
os.makedirs(destination)
# Create a sub-directory in the current directory
# when a destination isn't provided
else:
if os.path.isdir(connector_id):
error = '{} directory already exists. Please remove the directory before continuing.'
raise CLIError(error.format(connector_id))
else:
os.mkdir(connector_id)
destination = connector_id

if os.path.isdir(destination):
os.chdir(destination)
else:
error = 'Couldn\'t download to the desination directory {}.'
raise CLIError(error.format(destination))

return os.getcwd()


def download(powerapps_rp, settings, destination):
def _ensure_overwrite(settings):
"""
Ensure the files can be overwritten, if exists
"""
overwrite = False
files = [settings.api_properties, settings.api_definition, settings.icon, SETTINGS_FILE]
existing_files = [file for file in files if os.path.exists(file)]
if len(existing_files) > 0:
msg = '{} file(s) exist. Do you want to overwrite?'.format(existing_files)
overwrite = prompt_y_n(msg)
if not overwrite:
raise CLIError('{} files not overwritten.'.format(existing_files))

return overwrite


def download(powerapps_rp, settings, destination, overwrite):
"""
Download operation.
"""
Expand All @@ -53,6 +83,10 @@ def download(powerapps_rp, settings, destination):
destination=destination,
connector_id=settings.connector_id)

# Check if files could be overwritten
if not overwrite:
overwrite = _ensure_overwrite(settings)

api_registration = powerapps_rp.get_connector(
environment=settings.environment,
connector_id=settings.connector_id)
Expand All @@ -63,7 +97,7 @@ def download(powerapps_rp, settings, destination):
api_properties = api_registration[_PROPERTIES]

# Save the settings
SettingsSerializer.to_json(settings, 'settings.json')
write_settings(settings, overwrite)

# Property whitelist
property_keys_whitelist = [
Expand All @@ -86,7 +120,11 @@ def download(powerapps_rp, settings, destination):
}

# Write the api properties
open(settings.api_properties, mode='w').write(format_json(api_properties_selected))
api_prop = format_json(api_properties_selected)
open(
file=settings.api_properties,
mode='w'
).write(api_prop)

# Write the open api definition,
# either from swagger URL when available or from swagger property.
Expand All @@ -95,12 +133,18 @@ def download(powerapps_rp, settings, destination):
response = requests.get(original_swagger_url, allow_redirects=True)
response_string = response.content.decode('utf-8-sig')
swagger = format_json(json.loads(response_string))
open(settings.api_definition, mode='w').write(swagger)
open(
file=settings.api_definition,
mode='w'
).write(swagger)

# Write the icon
if _ICON_URI in api_properties:
icon_url = api_properties[_ICON_URI]
response = requests.get(icon_url, allow_redirects=True)
open(settings.icon, mode='wb').write(response.content)
open(
file=settings.icon,
mode='wb'
).write(response.content)

return directory
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from knack.util import CLIError

from paconn.common.util import ensure_file_exists
from paconn.settings.util import write_settings
from paconn.apimanager.iconuploader import upload_icon
from paconn.operations.json_keys import (
_PROPERTIES,
Expand Down Expand Up @@ -59,7 +60,7 @@ def _create_backendservice_url(openapi_definition):
return url


def create_update(powerapps_rp, settings, client_secret, is_update):
def upsert(powerapps_rp, settings, client_secret, is_update, overwrite_settings):
"""
Method for create/update operation
"""
Expand Down Expand Up @@ -138,4 +139,8 @@ def create_update(powerapps_rp, settings, client_secret, is_update):
payload=property_definition)
connector_id = json.loads(api_registration)[_NAME]

# Save the settings
settings.connector_id = connector_id
write_settings(settings, overwrite_settings)

return connector_id
12 changes: 10 additions & 2 deletions tools/paconn-cli/paconn/settings/settingsserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,20 @@ class SettingsSerializer:
Serializes and deserializes a settings object
"""
@staticmethod
def to_json(settings, filename):
def to_json_string(settings):
"""
Serializes a settings object into the settings.json
Serializes a settings object into string
"""
settings_dict = SettingsSerializer.serialize(settings)
json_str = format_json(settings_dict)
return json_str

@staticmethod
def to_json(settings, filename):
"""
Serializes a settings object into the settings.json
"""
json_str = SettingsSerializer.to_json_string(settings)
open(filename, 'w').write(json_str)

@staticmethod
Expand Down
Loading

0 comments on commit ba2cbef

Please sign in to comment.