Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'package' command and SAM support #258

Merged
merged 3 commits into from
Mar 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -911,19 +911,25 @@ API Key

Only requests sent with a valid `X-Api-Key` header will be accepted.

Custom Auth Handler
-------------------
Using Amazon Cognito User Pools
-------------------------------

A custom Authorizer is required for this to work, details can be found here;
http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html
To integrate with cognito user pools, you can use the ``define_authorizer`` method
on the ``app`` object.

.. code-block:: python

@app.route('/authenticated', methods=['GET'], authorization_type='CUSTOM', authorizer_id='ab12cd')
@app.route('/user-pools', methods=['GET'], authorizer_name='MyPool')
def authenticated():
return {"secure": True}

Only requests sent with a valid `X-Api-Key` header will be accepted.
app.define_authorizer(
name='MyPool',
header='Authorization',
auth_type='cognito_user_pools',
provider_arns=['arn:aws:cognito:...:userpool/name']
)


Tutorial: Local Mode
====================
Expand Down
26 changes: 19 additions & 7 deletions chalice/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,14 @@ def to_dict(self):
class RouteEntry(object):

def __init__(self, view_function, view_name, path, methods,
authorization_type=None, authorizer_id=None,
authorizer_name=None,
api_key_required=None, content_types=None,
cors=False):
self.view_function = view_function
self.view_name = view_name
self.uri_pattern = path
self.methods = methods
self.authorization_type = authorization_type
self.authorizer_id = authorizer_id
self.authorizer_name = authorizer_name
self.api_key_required = api_key_required
#: A list of names to extract from path:
#: e.g, '/foo/{bar}/{baz}/qux -> ['bar', 'baz']
Expand Down Expand Up @@ -192,6 +191,7 @@ def __init__(self, app_name, configure_logs=True):
self.debug = False
self.configure_logs = configure_logs
self.log = logging.getLogger(self.app_name)
self._authorizers = {}
if self.configure_logs:
self._configure_logging()

Expand Down Expand Up @@ -221,6 +221,19 @@ def _already_configured(self, log):
return True
return False

@property
def authorizers(self):
return self._authorizers.copy()

def define_authorizer(self, name, header, auth_type, provider_arns=None):
# TODO: double check remaining authorizers. This only handles
# cognito_user_pools.
self._authorizers[name] = {
'header': header,
'auth_type': auth_type,
'provider_arns': provider_arns
}

def route(self, path, **kwargs):
def _register_view(view_func):
self._add_route(path, view_func, **kwargs)
Expand All @@ -230,8 +243,7 @@ def _register_view(view_func):
def _add_route(self, path, view_func, **kwargs):
name = kwargs.pop('name', view_func.__name__)
methods = kwargs.pop('methods', ['GET'])
authorization_type = kwargs.pop('authorization_type', None)
authorizer_id = kwargs.pop('authorizer_id', None)
authorizer_name = kwargs.pop('authorizer_name', None)
api_key_required = kwargs.pop('api_key_required', None)
content_types = kwargs.pop('content_types', ['application/json'])
cors = kwargs.pop('cors', False)
Expand All @@ -247,8 +259,8 @@ def _add_route(self, path, view_func, **kwargs):
raise ValueError(
"Duplicate route detected: '%s'\n"
"URL paths must be unique." % path)
entry = RouteEntry(view_func, name, path, methods, authorization_type,
authorizer_id, api_key_required,
entry = RouteEntry(view_func, name, path, methods,
authorizer_name, api_key_required,
content_types, cors)
self.routes[path] = entry

Expand Down
8 changes: 4 additions & 4 deletions chalice/app.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,15 @@ class RouteEntry(object):
view_name = ... # type: str
methods = ... # type: List[str]
uri_pattern = ... # type: str
authorization_type = ... # type: str
authorizer_id = ... # type: str
authorizer_name = ... # type: str
api_key_required = ... # type: bool
content_types = ... # type: List[str]
view_args = ... # type: List[str]
cors = ... # type: bool

def __init__(self, view_function: Callable[..., Any],
view_name: str, path: str, methods: List[str],
authorization_type: str=None,
authorizer_id: str=None,
authorizer_name: str=None,
api_key_required: bool=None,
content_types: List[str]=None,
cors: bool=False) -> None: ...
Expand All @@ -81,6 +79,8 @@ class Chalice(object):
routes = ... # type: Dict[str, RouteEntry]
current_request = ... # type: Request
debug = ... # type: bool
authorizers = ... # type: Dict[str, Dict[str, Any]]

def __init__(self, app_name: str) -> None: ...

def route(self, path: str, **kwargs: Any) -> Callable[..., Any]: ...
Expand Down
50 changes: 11 additions & 39 deletions chalice/awsclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,39 +138,21 @@ def get_rest_api_id(self, name):
return api['id']
return None

def create_rest_api(self, name):
# type: (str) -> str
response = self._client('apigateway').create_rest_api(name=name)
return response['id']

def get_root_resource_for_api(self, rest_api_id):
# type: (str) -> Dict[str, Any]
resources = [r for r in self.get_resources_for_api(rest_api_id)
if r['path'] == '/']
root_resource = resources[0]
return root_resource

def get_resources_for_api(self, rest_api_id):
# type: (str) -> List[Dict[str, Any]]
def import_rest_api(self, swagger_document):
# type: (Dict[str, Any]) -> str
client = self._client('apigateway')
paginator = client.get_paginator('get_resources')
pages = paginator.paginate(restApiId=rest_api_id)
return pages.build_full_result()['items']
response = client.import_rest_api(
body=json.dumps(swagger_document, indent=2)
)
rest_api_id = response['id']
return rest_api_id

def delete_methods_from_root_resource(self, rest_api_id, root_resource):
def update_api_from_swagger(self, rest_api_id, swagger_document):
# type: (str, Dict[str, Any]) -> None
client = self._client('apigateway')
methods = list(root_resource.get('resourceMethods', []))
for method in methods:
client.delete_method(restApiId=rest_api_id,
resourceId=root_resource['id'],
httpMethod=method)

def delete_resource_for_api(self, rest_api_id, resource_id):
# type: (str, str) -> None
client = self._client('apigateway')
client.delete_resource(restApiId=rest_api_id,
resourceId=resource_id)
client.put_rest_api(
restApiId=rest_api_id,
body=json.dumps(swagger_document, indent=2))

def deploy_rest_api(self, rest_api_id, stage_name):
# type: (str, str) -> None
Expand All @@ -180,16 +162,6 @@ def deploy_rest_api(self, rest_api_id, stage_name):
stageName=stage_name,
)

def create_rest_resource(self, rest_api_id, parent_id, path_part):
# type: (str, str, str) -> Dict[str, Any]
client = self._client('apigateway')
response = client.create_resource(
restApiId=rest_api_id,
parentId=parent_id,
pathPart=path_part,
)
return response

def add_permission_for_apigateway_if_needed(self, function_name,
region_name, account_id,
rest_api_id, random_id):
Expand Down
28 changes: 19 additions & 9 deletions chalice/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
Contains commands for deploying chalice.

"""
import os
import importlib
import json
import sys
import logging
import importlib
import os
import sys

import click
import botocore.exceptions
import click
from typing import Dict, Any # noqa

from chalice.app import Chalice # noqa
from chalice import deployer
from chalice import __version__ as chalice_version
from chalice.logs import LogRetriever
from chalice import prompts
from chalice.config import Config
from chalice.app import Chalice # noqa
from chalice.awsclient import TypedAWSClient
from chalice.cli.utils import create_botocore_session

from chalice.config import Config
from chalice.deploy import deployer
from chalice.logs import LogRetriever
from chalice.package import create_app_packager

TEMPLATE_APP = """\
from chalice import Chalice
Expand Down Expand Up @@ -282,6 +282,16 @@ def generate_sdk(ctx, sdk_type, outdir):
sdk_type=sdk_type)


@cli.command('package')
@click.argument('outdir')
@click.pass_context
def package(ctx, outdir):
# type: (click.Context, str) -> None
config = create_config_obj(ctx)
packager = create_app_packager(config)
packager.package_app(outdir)


def run_local_server(app_obj, port):
# type: (Chalice, int) -> None
from chalice.local import create_local_server
Expand Down
Empty file added chalice/deploy/__init__.py
Empty file.
Loading