Skip to content

Commit

Permalink
Merge pull request #1626 from awslabs/develop
Browse files Browse the repository at this point in the history
chore: Release 0.35.0
  • Loading branch information
jfuss authored Dec 2, 2019
2 parents 39f1915 + 24bdc50 commit 69daba3
Show file tree
Hide file tree
Showing 39 changed files with 3,011 additions and 87 deletions.
6 changes: 3 additions & 3 deletions requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
chevron~=0.12
click~=7.0
Flask~=1.0.2
boto3~=1.9, >=1.9.56
boto3~=1.10, >=1.10.29
jmespath~=0.9.4
PyYAML~=5.1
cookiecutter~=1.6.0
aws-sam-translator==1.16.0
aws-sam-translator==1.17.0
docker~=4.0
dateparser~=0.7
python-dateutil~=2.6, <2.8.1
Expand All @@ -14,4 +14,4 @@ serverlessrepo==0.1.9
aws_lambda_builders==0.6.0
# https://github.com/mhammond/pywin32/issues/1439
pywin32 < 226; sys_platform == 'win32'
tomlkit==0.5.8
tomlkit==0.5.8
2 changes: 1 addition & 1 deletion samcli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
SAM CLI version
"""

__version__ = "0.34.0"
__version__ = "0.35.0"
16 changes: 8 additions & 8 deletions samcli/commands/deploy/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
required=False,
is_flag=True,
help="Indicates whether to override existing files in the S3 bucket. "
"Specify this flag to upload artifacts even if they"
"Specify this flag to upload artifacts even if they "
"match existing artifacts in the S3 bucket.",
)
@click.option(
Expand All @@ -99,26 +99,26 @@
required=False,
is_flag=True,
help="Indicates whether to execute the"
"change set. Specify this flag if you want to view your stack changes"
"before executing the change set. The command creates an AWS CloudForma-"
"tion change set and then exits without executing the change set. if "
"change set. Specify this flag if you want to view your stack changes "
"before executing the change set. The command creates an AWS CloudFormation "
"change set and then exits without executing the change set. if "
"the changeset looks satisfactory, the stack changes can be made by "
"running the same command without specifying `--no-execute-changeset`",
)
@click.option(
"--role-arn",
required=False,
help="The Amazon Resource Name (ARN) of an AWS Identity"
"and Access Management (IAM) role that AWS CloudFormation assumes when"
help="The Amazon Resource Name (ARN) of an AWS Identity "
"and Access Management (IAM) role that AWS CloudFormation assumes when "
"executing the change set.",
)
@click.option(
"--fail-on-empty-changeset/--no-fail-on-empty-changeset",
default=True,
required=False,
is_flag=True,
help="Specify if the CLI should return a non-zero exit code if there are no"
"changes to be made to the stack. The default behavior is to return a"
help="Specify if the CLI should return a non-zero exit code if there are no "
"changes to be made to the stack. The default behavior is to return a "
"non-zero exit code.",
)
@click.option(
Expand Down
6 changes: 6 additions & 0 deletions samcli/commands/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ class CredentialsError(UserException):
"""


class SchemasApiException(UserException):
"""
Exception class to wrap all Schemas APIs exceptions.
"""


class RegionError(UserException):
"""
Exception class when no valid region is passed to a client.
Expand Down
34 changes: 21 additions & 13 deletions samcli/commands/init/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ def do_cli(
auto_clone=True,
):
from samcli.commands.init.init_generator import do_generate
from samcli.commands.init.init_templates import InitTemplates
from samcli.commands.init.interactive_init_flow import do_interactive
from samcli.commands.init.init_templates import InitTemplates

# check for mutually exclusive parameters
if location and app_template:
Expand All @@ -155,11 +155,8 @@ def do_cli(
templates = InitTemplates(no_interactive, auto_clone)
location = templates.location_from_app_template(runtime, dependency_manager, app_template)
no_input = True
default_context = {"project_name": name, "runtime": runtime}
if extra_context is None:
extra_context = default_context
else:
extra_context = _merge_extra_context(default_context, extra_context)
extra_context = _get_cookiecutter_template_context(name, runtime, extra_context)

if not output_dir:
output_dir = "."
do_generate(location, runtime, dependency_manager, output_dir, name, no_input, extra_context)
Expand All @@ -179,11 +176,22 @@ def do_cli(
do_interactive(location, runtime, dependency_manager, output_dir, name, app_template, no_input)


def _merge_extra_context(default_context, extra_context):
try:
extra_context_dict = json.loads(extra_context)
except JSONDecodeError:
raise UserException(
"Parse error reading the --extra-content parameter. The value of this parameter must be valid JSON."
)
def _get_cookiecutter_template_context(name, runtime, extra_context):
default_context = {}
extra_context_dict = {}

if runtime is not None:
default_context["runtime"] = runtime

if name is not None:
default_context["project_name"] = name

if extra_context is not None:
try:
extra_context_dict = json.loads(extra_context)
except JSONDecodeError:
raise UserException(
"Parse error reading the --extra-context parameter. The value of this parameter must be valid JSON."
)

return {**extra_context_dict, **default_context}
15 changes: 15 additions & 0 deletions samcli/commands/init/init_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,18 @@ def _should_clone_repo(self, expected_path):
"\nAllow SAM CLI to download AWS-provided quick start templates from Github", default=True
)
return do_clone

def is_dynamic_schemas_template(self, app_template, runtime, dependency_manager):
"""
Check if provided template is dynamic template e.g: AWS Schemas template.
Currently dynamic templates require different handling e.g: for schema download and merge schema code in sam-app.
:param app_template:
:param runtime:
:param dependency_manager:
:return:
"""
options = self.init_options(runtime, dependency_manager)
for option in options:
if option.get("appTemplate") == app_template:
return option.get("isDynamicTemplate", False)
return False
192 changes: 192 additions & 0 deletions samcli/commands/init/interactive_event_bridge_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
"""
Isolates interactive init prompt flow for event bridge.
"""

import click

from samcli.lib.schemas.schemas_cli_message_generator import (
construct_cli_display_message_for_schemas,
construct_cli_display_message_for_registries,
)
from samcli.lib.schemas.schemas_api_caller import SchemasApiCaller
from samcli.lib.schemas.schemas_aws_config import get_schemas_client, get_aws_configuration_choice
from samcli.lib.schemas.cli_paginator import do_paginate_cli
from samcli.lib.schemas.schemas_constants import (
SCHEMAS_REGISTRY,
SCHEMA_NAME,
EVENT_BRIDGE_SOURCE,
EVENT_BRIDGE_SOURCE_DETAIL_TYPE,
PAGE_LIMIT,
SCHEMA_ROOT,
)


def get_schema_template_details(schemas_api_caller):
"""
Calls schemas APIs to fetch available selection and returns schema details based on user selection.
:param schemas_api_caller:
:return:
"""
registry_name = _get_registry_cli_choice(schemas_api_caller)
schema_full_name = _get_schema_cli_choice(schemas_api_caller, registry_name)
schema_latest_version = schemas_api_caller.get_latest_schema_version(registry_name, schema_full_name)
get_schema_metadata_response = schemas_api_caller.get_schema_metadata(registry_name, schema_full_name)
return {
"registry_name": registry_name,
"schema_full_name": schema_full_name,
"schema_version": schema_latest_version,
"event_source": get_schema_metadata_response["event_source"],
"event_source_detail_type": get_schema_metadata_response["event_source_detail_type"],
"schema_root_name": get_schema_metadata_response["schema_root_name"],
"schemas_package_hierarchy": get_schema_metadata_response["schemas_package_hierarchy"],
}


def _get_registry_cli_choice(schemas_api_caller):
""" Returns registry choice if one registry is present otherwise prompt for selection """
registries = _fetch_available_registries(schemas_api_caller, dict(), None)
registry_pages = registries["registry_pages"]
# If only one registry don't prompt for choice
if len(registry_pages) == 1 and len(registry_pages.get(0)) == 1:
return registry_pages.get(0)[0]

# more than one registries
click.echo("Which Schema Registry would you like to use?")
next_token = registries.get("next_token")
is_last_page = next_token is None
return _prompt_for_registry_choice(
schemas_api_caller, registry_pages, 0, next_token, is_last_page, last_page_number=None
)


def _prompt_for_registry_choice(
schemas_api_caller, registry_pages, page_to_render, next_token, is_last_page, last_page_number
):
# construct CLI message
cli_display_message = construct_cli_display_message_for_registries(page_to_render + 1, last_page_number)

# get customer decision
cli_response = do_paginate_cli(registry_pages, page_to_render, PAGE_LIMIT, is_last_page, cli_display_message)

# user selected item
if cli_response.get("choice") is not None:
return cli_response.get("choice")

# user decided to paginate
page_to_render = cli_response.get("page_to_render")
if registry_pages.get(page_to_render) is None:
registries = _fetch_available_registries(schemas_api_caller, registry_pages, next_token)
registry_pages = registries["registry_pages"]
next_token = registries.get("next_token")
is_last_page = next_token is None
if is_last_page and last_page_number is None:
last_page_number = page_to_render + 1
return _prompt_for_registry_choice(
schemas_api_caller, registry_pages, page_to_render, next_token, is_last_page, last_page_number
)


def _get_schema_cli_choice(schemas_api_caller, registry_name):
""" Returns registry registry choice if one registry is present otherwise prompt for selection """
schemas = _fetch_available_schemas(schemas_api_caller, registry_name, dict(), None)
schema_pages = schemas["schema_pages"]
# If only one schema don't prompt for choice
if len(schema_pages) == 1 and len(schema_pages.get(0)) == 1:
return schema_pages.get(0)[0]

# more than one schema
click.echo("\nWhich Schema would you like to use?")
next_token = schemas.get("next_token")
is_last_page = next_token is None
return _prompt_for_schemas_choice(
schemas_api_caller, registry_name, schema_pages, 0, next_token, is_last_page, last_page_number=None
)


def _prompt_for_schemas_choice(
schemas_api_caller, registry_name, schema_pages, page_to_render, next_token, is_last_page, last_page_number
):
# construct CLI message
cli_display_message = construct_cli_display_message_for_schemas(page_to_render + 1, last_page_number)

# get customer decision
cli_response = do_paginate_cli(schema_pages, page_to_render, PAGE_LIMIT, is_last_page, cli_display_message)

# user selected item
if cli_response.get("choice") is not None:
return cli_response["choice"]

# user decided to paginate
page_to_render = cli_response.get("page_to_render")
if schema_pages.get(page_to_render) is None:
schemas = _fetch_available_schemas(schemas_api_caller, registry_name, schema_pages, next_token)
schema_pages = schemas["schema_pages"]
next_token = schemas.get("next_token")
is_last_page = next_token is None
if is_last_page and last_page_number is None:
last_page_number = page_to_render + 1
return _prompt_for_schemas_choice(
schemas_api_caller, registry_name, schema_pages, page_to_render, next_token, is_last_page, last_page_number
)


def _fetch_available_schemas(schemas_api_caller, registry_name, schema_pages, next_token):
""" calls schemas api fetch schemas for given registry. Two CLI pages are fetched at a time."""
list_schemas_response = schemas_api_caller.list_schemas(registry_name, next_token, PAGE_LIMIT)
schemas = list_schemas_response["schemas"]

# divided response into pages
pages = _construct_cli_page(schemas, PAGE_LIMIT)
for page in range(0, len(pages)):
schema_pages.update({len(schema_pages): pages.get(page)})
next_token = list_schemas_response.get("next_token")
return {"schema_pages": schema_pages, "next_token": next_token}


def _fetch_available_registries(schemas_api_caller, registry_pages, next_token):
""" calls schemas api to fetch registries. Two CLI pages are fetched at a time. """
list_registries_response = schemas_api_caller.list_registries(next_token, PAGE_LIMIT)
registries = list_registries_response["registries"]

# sort registries alphabetically by name
registries.sort()

# divided response into pages
pages = _construct_cli_page(registries, PAGE_LIMIT)
for page in range(0, len(pages)):
registry_pages.update({len(registry_pages): pages.get(page)})
next_token = list_registries_response.get("next_token")
return {"registry_pages": registry_pages, "next_token": next_token}


def _construct_cli_page(items, item_per_page):
""" Responsible for splitting items into CLI pages. Currently CLI pages are list of dictionary [0:{0:s1, 1:s2: 3:s3}, 1: {4:s4, 5:s5: 6:s6}]
We maintain the page detail and item index details. """
pages = [
items[i * item_per_page : (i + 1) * item_per_page]
for i in range((len(items) + item_per_page - 1) // item_per_page)
]
index = 0
schema_dict = dict()
for page in pages:
schema_dict.update({index: page})
index = index + 1

return schema_dict


def get_schemas_template_parameter(schema_template_details):
""" Schemas cookiecutter template parameter mapping """
return {
SCHEMAS_REGISTRY: schema_template_details["registry_name"],
SCHEMA_NAME: schema_template_details["schema_root_name"],
EVENT_BRIDGE_SOURCE: schema_template_details["event_source"],
EVENT_BRIDGE_SOURCE_DETAIL_TYPE: schema_template_details["event_source_detail_type"],
SCHEMA_ROOT: schema_template_details["schemas_package_hierarchy"],
}


def get_schemas_api_caller():
aws_configuration = get_aws_configuration_choice()
schemas_client = get_schemas_client(aws_configuration["profile"], aws_configuration["region"])
return SchemasApiCaller(schemas_client)
Loading

0 comments on commit 69daba3

Please sign in to comment.