syndicate/core/groups/appsync.py (202 lines of code) (raw):
import os
from functools import partial
import click
from syndicate.commons.log_helper import get_user_logger
from syndicate.core.constants import APPSYNC_TYPE, APPSYNC_DATA_SOURCE_TYPES, \
APPSYNC_AUTHENTICATION_TYPES, APPSYNC_AUTHORIZATION_TYPES, \
APPSYNC_RESOLVER_RUNTIMES, APPSYNC_RESOLVER_KINDS, OK_RETURN_CODE, \
FAILED_RETURN_CODE
from syndicate.core.decorators import return_code_manager
from syndicate.core.generators.appsync import generate_appsync
from syndicate.core.generators.deployment_resources import \
BaseConfigurationGenerator
from syndicate.core.generators.deployment_resources.appsync_generator import \
AppSyncDataSourceGenerator, AppSyncAuthorizationGenerator, \
AppSyncResolverGenerator, AppSyncFunctionGenerator
from syndicate.core.generators.lambda_function import PROJECT_PATH_PARAM
from syndicate.core.helper import OrderedGroup, resolve_project_path, \
DictParamType, check_tags, verbose_option, timeit, OptionRequiredIf, \
ValidRegionParamType, validate_incompatible_options
USER_LOG = get_user_logger()
@click.group(name=APPSYNC_TYPE, cls=OrderedGroup)
@return_code_manager
@click.option('--project_path', nargs=1,
help="Path to the project folder. Default value: the one "
"from the current config if it exists. "
"Otherwise - the current working directory",
callback=resolve_project_path)
@click.pass_context
def appsync(ctx, project_path):
"""Generates AppSync env and resource meta"""
if not os.access(project_path, os.F_OK):
USER_LOG.error(f"The provided path {project_path} doesn't exist")
return FAILED_RETURN_CODE
elif not os.access(project_path, os.W_OK) or not os.access(project_path,
os.X_OK):
USER_LOG.error(f"Incorrect permissions for the provided path "
f"'{project_path}'")
return FAILED_RETURN_CODE
ctx.ensure_object(dict)
ctx.obj[PROJECT_PATH_PARAM] = project_path
return OK_RETURN_CODE
@appsync.command(name='api')
@return_code_manager
@click.option('--name', required=True, type=str,
help="AppSync API name")
@click.option('--project_path', nargs=1,
help="Path to the project folder. Default value: the one "
"from the current config if it exists. "
"Otherwise - the current working directory",
callback=resolve_project_path)
@click.option('--tags', type=DictParamType(), callback=check_tags,
help='The resource tags')
@verbose_option
@timeit()
def api(name, project_path, tags):
"""
Generates required environment for AppSync API
"""
if not os.access(project_path, os.F_OK):
USER_LOG.error(f"The provided path {project_path} doesn't exist")
return FAILED_RETURN_CODE
elif not os.access(project_path, os.W_OK) or not os.access(project_path,
os.X_OK):
USER_LOG.error(f"Incorrect permissions for the provided path "
f"'{project_path}'")
return FAILED_RETURN_CODE
generate_appsync(name=name,
project_path=project_path,
tags=tags)
return OK_RETURN_CODE
@appsync.command(name='data_source')
@return_code_manager
@click.option('--api_name', required=True, type=str,
help="AppSync API name to add data source to")
@click.option('--name', required=True, type=str,
help="Data source name")
@click.option('--description', type=str,
help="Data source description")
@click.option('--type', type=click.Choice(APPSYNC_DATA_SOURCE_TYPES),
default='NONE', help="Data source type")
@click.option('--resource_name', type=str, cls=OptionRequiredIf,
required_if_values=['AWS_LAMBDA', 'AMAZON_DYNAMODB'],
required_if='type', help="Data source resource name")
@click.option('--region', type=ValidRegionParamType(),
help="The region where the resource is located. If not "
"specified, sets the default value from syndicate config")
@click.option('--service_role_name', type=str, cls=OptionRequiredIf,
required_if='type',
required_if_values=['AWS_LAMBDA', 'AMAZON_DYNAMODB'],
help="The name of the role to access the data source resource")
@verbose_option
@click.pass_context
@timeit()
def data_source(ctx, **kwargs):
"""Adds data source to an existing SyncApp API"""
kwargs[PROJECT_PATH_PARAM] = ctx.obj[PROJECT_PATH_PARAM]
try:
generator = AppSyncDataSourceGenerator(**kwargs)
except ValueError as e:
raise click.BadParameter(e)
_generate(generator)
USER_LOG.info(f"Data source '{kwargs['name']}' was added to AppSync API "
f"'{kwargs['api_name']}' successfully")
return OK_RETURN_CODE
@appsync.command(name='function')
@return_code_manager
@click.option('--api_name', required=True, type=str,
help="AppSync API name to add function to")
@click.option('--name', required=True, type=str,
help="Function name")
@click.option('--description', type=str,
help="Function description")
@click.option('--data_source_name', required=True, type=str,
help="The name of the data source to associate the function "
"with")
@click.option('--runtime', type=click.Choice(APPSYNC_RESOLVER_RUNTIMES),
required=True, help="Function runtime")
@verbose_option
@click.pass_context
@timeit()
def function(ctx, **kwargs):
"""Adds function to an existing SyncApp API"""
kwargs[PROJECT_PATH_PARAM] = ctx.obj[PROJECT_PATH_PARAM]
try:
generator = AppSyncFunctionGenerator(**kwargs)
except ValueError as e:
raise click.BadParameter(e)
_generate(generator)
USER_LOG.info(f"The function '{kwargs['name']}' was added to AppSync API "
f"'{kwargs['api_name']}' successfully")
return OK_RETURN_CODE
@appsync.command(name='resolver')
@return_code_manager
@click.option('--api_name', required=True, type=str,
help="AppSync API name to add resolver to")
@click.option('--kind', type=click.Choice(APPSYNC_RESOLVER_KINDS),
required=True, default='UNIT', is_eager=True,
help="The kind of resolver.")
@click.option('--type_name', required=True, type=str,
help="The name of the type defined in the API schema")
@click.option('--field_name', required=True, type=str,
help="The name of the field defined in the API schema to attach "
"the resolver to")
@click.option('--data_source_name', type=str, cls=OptionRequiredIf,
required_if='kind', required_if_values=['UNIT'],
help="The name of the data source to associate the resolver "
"with")
@click.option('--function_name', type=str, multiple=True,
callback=partial(validate_incompatible_options,
incompatible_options=['data_source_name']),
help="The name of the function to add to the resolver")
@click.option('--runtime', type=click.Choice(APPSYNC_RESOLVER_RUNTIMES),
required=True, help="Resolver runtime")
@verbose_option
@click.pass_context
@timeit()
def resolver(ctx, **kwargs):
"""Adds resolver to an existing SyncApp API"""
kwargs[PROJECT_PATH_PARAM] = ctx.obj[PROJECT_PATH_PARAM]
try:
generator = AppSyncResolverGenerator(**kwargs)
except ValueError as e:
raise click.BadParameter(e)
_generate(generator)
USER_LOG.info(f"The resolver of the type '{kwargs['type_name']}' for the "
f"field '{kwargs['field_name']}' was added to AppSync API "
f"'{kwargs['api_name']}' successfully")
return OK_RETURN_CODE
@appsync.command(name='authorization')
@return_code_manager
@click.option('--api_name', required=True, type=str,
help="AppSync API name to add authorization to")
@click.option('--type', required=True,
type=click.Choice(APPSYNC_AUTHORIZATION_TYPES),
help="The authorization type")
@click.option('--auth_type', required=True,
type=click.Choice(APPSYNC_AUTHENTICATION_TYPES),
help="The authentication type")
@click.option('--resource_name', type=str, cls=OptionRequiredIf,
required_if_values=['AWS_LAMBDA', 'AMAZON_COGNITO_USER_POOLS'],
required_if='auth_type',
help="Authentication provider resource name")
@click.option('--region', type=ValidRegionParamType(),
help="The region where the authentication provider resource is "
"located. If not specified, sets the default value from "
"syndicate config")
@verbose_option
@click.pass_context
@timeit()
def authorization(ctx, **kwargs):
"""Adds authorization to an existing SyncApp API"""
kwargs[PROJECT_PATH_PARAM] = ctx.obj[PROJECT_PATH_PARAM]
try:
generator = AppSyncAuthorizationGenerator(**kwargs)
except ValueError as e:
raise click.BadParameter(e)
_generate(generator)
USER_LOG.info(f"The '{kwargs['type']}' authorization of type "
f"'{kwargs['auth_type']}' was added to AppSync API "
f"'{kwargs['api_name']}' successfully")
return OK_RETURN_CODE
def _generate(generator: BaseConfigurationGenerator):
"""Just some common actions for this module are gathered in here"""
try:
generator.write()
except ValueError as e:
raise click.BadParameter(e)
except RuntimeError as e:
raise click.Abort(e)
except Exception as e:
raise Exception(f"An unexpected error occurred: {e}")