diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a8ed8c9..0000000 --- a/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg -*.pyc - -# sphinx -_build - -# Serverless directories -.serverless - -# node js -node_modules diff --git a/README.rst b/README.rst index 2efe285..33f3f33 100644 --- a/README.rst +++ b/README.rst @@ -57,6 +57,7 @@ for common usecases when using AWS Lambda with Python. * `cors_headers `_ - automatic injection of CORS headers * `dump_json_body `_ - auto-serialization of http body to JSON * `load_json_body `_ - auto-deserialize of http body from JSON +* `load_json_queryStringParameters ` - auto-deserialize of http queryStringParameters from JSON * `json_http_resp `_ - automatic serialization of python object to HTTP JSON response * `json_schema_validator `_ - use JSONSchema to validate request&response payloads * `load_urlencoded_body `_ - auto-deserialize of http body from a querystring encoded body diff --git a/lambda_decorators.py b/lambda_decorators.py index f98a9b7..4fef047 100644 --- a/lambda_decorators.py +++ b/lambda_decorators.py @@ -58,6 +58,7 @@ def handler(event, context): * :func:`cors_headers` - automatic injection of CORS headers * :func:`dump_json_body` - auto-serialization of http body to JSON * :func:`load_json_body` - auto-deserialize of http body from JSON +* :func:`load_json_queryStringParameters` - auto-deserialize of http queryStringParameters from JSON * :func:`json_http_resp` - automatic serialization of python object to HTTP JSON response * :func:`json_schema_validator` - use JSONSchema to validate request&response payloads * :func:`load_urlencoded_body` - auto-deserialize of http body from a querystring encoded body @@ -144,6 +145,7 @@ def handler(event, context): import json import logging +import ast import boto3 from functools import wraps, update_wrapper @@ -489,6 +491,61 @@ def wrapper(event, context): return wrapper +def load_json_queryStringParameters(handler): + """ + Automatically deserialize event queryStringParameters with ast.literal_eval + + Automatically returns a 400 if there is an error while parsing. + + Usage:: + + >>> from lambda_decorators import load_json_queryStringParameters + >>> @load_json_queryStringParameters + ... def handler(event, context): + ... return event['queryStringParameters'] + >>> handler({'queryStringParameters': '{"foo": ["bar1", "None"]'}, object()) + {"foo": ["bar1", None]} + + """ + @wraps(handler) + def wrapper(event, context): + isObject = lambda x: type(x) in [list, dict, tuple] + + def evaluate_items(obj): + def seq(obj): + if isinstance(obj, dict): + keys = obj.keys() + return zip([key for key in keys], [obj[key] for key in keys]) + else: + return enumerate(obj) + + tuple_flag = isinstance(obj, tuple) + obj = list(obj) if tuple_flag else obj + for i, value in seq(obj): + try: + obj[i] = ast.literal_eval(value) + except ValueError as e: + pass + if isObject(obj[i]): + obj[i] = evaluate_items(obj[i]) + obj = tuple(obj) if tuple_flag else obj + + return obj + + if isinstance(event.get('queryStringParameters'), str): + try: + event['queryStringParameters'] = ast.literal_eval(event['queryStringParameters']) + if isObject(event['queryStringParameters']): + event['queryStringParameters'] = evaluate_items(event['queryStringParameters']) + + + except Exception as exception: + return {'statusCode': 400, 'body': str(exception) } + return handler(event, context) + + return wrapper + + def json_schema_validator(request_schema=None, response_schema=None): """ Validate your request & response payloads against a JSONSchema. diff --git a/tests/test_load_json_queryStringParameters.py b/tests/test_load_json_queryStringParameters.py new file mode 100644 index 0000000..3a2cdfa --- /dev/null +++ b/tests/test_load_json_queryStringParameters.py @@ -0,0 +1,80 @@ +from mock import MagicMock + +from lambda_decorators import load_json_queryStringParameters + + +def test_queryStringParameters_empty(): + @load_json_queryStringParameters + def hello(example, context): + return example + assert hello({}, MagicMock()) == {} + + +def test_queryStringParameters_no_parameters(): + @load_json_queryStringParameters + def hello(example, context): + return example + + query_parameters = '{}' + result = {} + assert hello({'queryStringParameters': query_parameters}, + MagicMock()) == {'queryStringParameters': result} + + +def test_queryStringParameters_1level_str(): + @load_json_queryStringParameters + def hello(example, context): + return example + + query_parameters = '{"foo": "bar"}' + result = {"foo": "bar"} + assert hello({'queryStringParameters': query_parameters}, + MagicMock()) == {'queryStringParameters': result} + + + +def test_queryStringParameters_1level_list(): + @load_json_queryStringParameters + def hello(example, context): + return example + + query_parameters = '{"foo": ' + str(["bar1", "bar2"]) + '}' + result = {"foo": ["bar1", "bar2"]} + assert hello({'queryStringParameters': query_parameters}, + MagicMock()) == {'queryStringParameters': result} + + +def test_queryStringParameters_2level_dict(): + @load_json_queryStringParameters + def hello(example, context): + return example + + query_parameters = '{"foo": {"bar1": "baz1", "bar2": "baz2"}}' + result = {"foo": {"bar1": "baz1", "bar2": "baz2"}} + assert hello({'queryStringParameters': query_parameters}, + MagicMock()) == {'queryStringParameters': result} + +def test_queryStringParameters_2level_list(): + @load_json_queryStringParameters + def hello(example, context): + return example + + query_parameters = '{"foo": {"bar1": ' + str(["(1.0)", "(1, 2)"]) + ', "bar2": "baz2"}}' + result = {"foo": {"bar1": [(1.0), (1, 2)], "bar2": "baz2"}} + assert hello({'queryStringParameters': query_parameters}, + MagicMock()) == {'queryStringParameters': result} + + +def test_queryStringParameters_2level_None_bool(): + @load_json_queryStringParameters + def hello(example, context): + return example + + query_parameters = '{"foo": {"bar1": ' + str(["(1.0)", "(1, 2)"]) + ', "bar2": ' + str(("None", "True")) + '}}' + result = {"foo": {"bar1": [(1.0), (1, 2)], "bar2": (None, True)}} + assert hello({'queryStringParameters': query_parameters}, + MagicMock()) == {'queryStringParameters': result} + + + +