Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/aws/chalice into upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
kapilt committed Jun 6, 2019
2 parents 4f8ddd0 + 31acf9e commit 07ca537
Show file tree
Hide file tree
Showing 13 changed files with 1,689 additions and 61 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ Next Release (TBD)
* Raise TypeError when trying to serialize an unserializable
type
(`#1100 <https://github.com/aws/chalice/issues/1100>`__)
* Update ``policies.json`` file
(`#1110 <https://github.com/aws/chalice/issues/1110>`__)
* Support repeating values in the query string
(`#1131 <https://github.com/aws/chalice/issues/1131>`__)
* Add layer support to chalice package
(`#1130 <https://github.com/aws/chalice/issues/1130>`__)


1.8.0
Expand Down
58 changes: 49 additions & 9 deletions chalice/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ def handle_extra_types(obj):
# to support that as well.
if isinstance(obj, decimal.Decimal):
return float(obj)
# This is added for backwards compatibility.
# It will keep only the last value for every key as it used to.
if isinstance(obj, MultiDict):
return dict(obj)
raise TypeError('Object of type %s is not JSON serializable'
% obj.__class__.__name__)

Expand Down Expand Up @@ -146,6 +150,37 @@ class TooManyRequestsError(ChaliceViewError):
TooManyRequestsError]


class MultiDict(Mapping):
"""A read only mapping of key to list of values.
Accessing it in the usual way will return the last value in the list.
Calling getlist will return a list of values with the same key.
"""

def __init__(self, mapping):
if mapping is None:
mapping = {}

self._dict = mapping

def __getitem__(self, k):
values_list = self._dict[k]

try:
return values_list[-1]
except IndexError:
raise KeyError(k)

def getlist(self, k):
return list(self._dict.get(k, []))

def __len__(self):
return len(self._dict)

def __iter__(self):
return iter(self._dict)


class CaseInsensitiveMapping(Mapping):
"""Case insensitive and read-only mapping."""

Expand Down Expand Up @@ -302,7 +337,8 @@ class Request(object):

def __init__(self, query_params, headers, uri_params, method, body,
context, stage_vars, is_base64_encoded):
self.query_params = query_params
self.query_params = None if query_params is None \
else MultiDict(query_params)
self.headers = CaseInsensitiveMapping(headers)
self.uri_params = uri_params
self.method = method
Expand Down Expand Up @@ -350,6 +386,8 @@ def to_dict(self):
# We want the output of `to_dict()` to be
# JSON serializable, so we need to remove the CaseInsensitive dict.
copied['headers'] = dict(copied['headers'])
if copied['query_params'] is not None:
copied['query_params'] = dict(copied['query_params'])
return copied


Expand Down Expand Up @@ -787,14 +825,16 @@ def __call__(self, event, context):
function_args = {name: event['pathParameters'][name]
for name in route_entry.view_args}
self.lambda_context = context
self.current_request = Request(event['queryStringParameters'],
event['headers'],
event['pathParameters'],
event['requestContext']['httpMethod'],
event['body'],
event['requestContext'],
event['stageVariables'],
event.get('isBase64Encoded', False))
self.current_request = Request(
event['multiValueQueryStringParameters'],
event['headers'],
event['pathParameters'],
event['requestContext']['httpMethod'],
event['body'],
event['requestContext'],
event['stageVariables'],
event.get('isBase64Encoded', False)
)
# We're getting the CORS headers before validation to be able to
# output desired headers with
cors_headers = None
Expand Down
21 changes: 15 additions & 6 deletions chalice/invoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,21 @@ def _format_stacktrace(self, formatted, stack_trace):
self._format_frame(formatted, frame)

def _format_frame(self, formatted, frame):
# type: (StringIO, List[Union[str, int]]) -> None
path, lineno, function, code = frame
formatted.write(
' File "{}", line {}, in {}\n'.format(path, lineno, function))
formatted.write(
' {}\n'.format(code))
# type: (StringIO, Union[str, List[Union[str, int]]]) -> None
if isinstance(frame, list):
# If the output is a list, it came from a 4-tuple as a result of
# an extract_tb call. This is the behavior up to and including
# python 3.6.
path, lineno, function, code = frame
formatted.write(
' File "{}", line {}, in {}\n'.format(path, lineno, function))
formatted.write(
' {}\n'.format(code))
else:
# If it is not a list, its a string. This is because the 4-tuple
# was replaced with a FrameSummary object which is serialized as
# a string by Lambda. In this case we can just print it directly.
formatted.write(frame)

def _format_success(self, formatted, payload):
# type: (StringIO, bytes) -> None
Expand Down
7 changes: 3 additions & 4 deletions chalice/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ def match_route(self, url):
"""
# Otherwise we need to check for param substitution
parsed_url = urlparse(url)
parsed_qs = parse_qs(parsed_url.query, keep_blank_values=True)
query_params = {k: v[-1] for k, v in parsed_qs.items()}
query_params = parse_qs(parsed_url.query, keep_blank_values=True)
path = parsed_url.path
# API Gateway removes the trailing slash if the route is not the root
# path. We do the same here so our route matching works the same way.
Expand Down Expand Up @@ -180,11 +179,11 @@ def create_lambda_event(self, method, path, headers, body=None):
'stageVariables': {},
}
if view_route.query_params:
event['queryStringParameters'] = view_route.query_params
event['multiValueQueryStringParameters'] = view_route.query_params
else:
# If no query parameters are provided, API gateway maps
# this to None so we're doing this for parity.
event['queryStringParameters'] = None
event['multiValueQueryStringParameters'] = None
if self._is_binary(headers) and body is not None:
event['body'] = base64.b64encode(body).decode('ascii')
event['isBase64Encoded'] = True
Expand Down
6 changes: 6 additions & 0 deletions chalice/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ def _generate_lambdafunction(self, resource, template):
}
lambdafunction_definition['Properties'].update(
reserved_concurrency_config)
if resource.layers:
layers_config = {
'Layers': resource.layers
} # type: Dict[str, List[str]]
lambdafunction_definition['Properties'].update(layers_config)

resources[cfn_name] = lambdafunction_definition
self._add_iam_role(resource, resources[cfn_name])

Expand Down
Loading

0 comments on commit 07ca537

Please sign in to comment.