Metadata-Version: 2.1
Name: aws-lambda-decorators
Version: 0.36
Summary: A set of python decorators to simplify aws python lambda development
Home-page: https://github.com/gridsmartercities/aws-lambda-decorators
Author: Grid Smarter Cities
Author-email: open-source@gridsmartercities.com
License: MIT
Description: [<img align="right" alt="Grid Smarter Cities" src="https://s3.eu-west-2.amazonaws.com/open-source-resources/grid_smarter_cities_small.png">](https://www.gridsmartercities.com/)
        
        ![Build Status](https://codebuild.eu-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiSTZNdEsxUHdnWWdRMGwrS3FuaUxSb0g5c2hNdWdSNE94Y1RFRGNrdk96Zm9LWlZWWmpEK1FTWmcraGRnMEdzbmRjakF5SDVQUVBzcVpNL3hLSGw3TnpNPSIsIml2UGFyYW1ldGVyU3BlYyI6ImZsbHEwcUJGOFV2VXNpWHoiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master)
        [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
        ![Github Release](https://img.shields.io/github/release/gridsmartercities/aws-lambda-decorators.svg?style=flat)
        \
        \
        ![Python Versions](https://img.shields.io/pypi/pyversions/aws-lambda-decorators.svg?style=flat)
        ![PyPi Version](https://img.shields.io/pypi/v/aws-lambda-decorators.svg?style=flat)
        ![PyPi Status](https://img.shields.io/pypi/status/aws-lambda-decorators.svg?style=flat)
        ![Pypi Downloads](https://img.shields.io/pypi/dm/aws-lambda-decorators.svg?style=flat&logo=pypi)
        
        # aws-lambda-decorators
        
        A set of Python decorators to ease the development of AWS lambda functions.
        
        ## Installation
        
        The easiest way to use these AWS Lambda Decorators is to install them through Pip:
        
        `pip install aws-lambda-decorators`
        
        ## Logging
        
        The Logging level of the decorators can be controlled by setting a LOG_LEVEL environment variable. In python:
        
        `os.environ["LOG_LEVEL"] = "INFO"`
        
        The default value is "INFO"
        
        ## Package Contents
        
        ### [Decorators](https://github.com/gridsmartercities/aws-lambda-decorators/blob/master/aws_lambda_decorators/decorators.py)
        
        The current list of AWS Lambda Python Decorators includes:
        
        * [__extract__](#extract): a decorator to extract and validate specific keys of a dictionary parameter passed to a AWS Lambda function.
        * [__extract_from_event__](#extract_from_event): a facade of [__extract__](#extract) to extract and validate keys from an AWS API Gateway lambda function _event_ parameter.
        * [__extract_from_context__](#extract_from_context): a facade of [__extract__](#extract) to extract and validate keys from an AWS API Gateway lambda function _context_ parameter.
        * [__extract_from_ssm__](#extract_from_ssm): a decorator to extract from AWS SSM the values of a set of parameter keys.
        * [__validate__](#validate): a decorator to validate a list of function parameters.
        * [__log__](#log): a decorator to log the parameters passed to the lambda function and/or the response of the lambda function.
        * [__handle_exceptions__](#handle_exceptions): a decorator to handle any type of declared exception generated by the lambda function. 
        * [__response_body_as_json__](#response_body_as_json): a decorator to transform a response dictionary body to a json string.
        * [__handle_all_exceptions__](#handle_all_exceptions): a decorator to handle all exceptions thrown by the lambda function.
        * [__cors__](#cors): a decorator to add cors headers to a lambda function.
        
        
        ### [Validators](https://github.com/gridsmartercities/aws-lambda-decorators/blob/master/aws_lambda_decorators/validators.py)
        
        Currently, the package offers 5 validators:
        
        * __Mandatory__: Checks if a parameter has a value.
        * __RegexValidator__: Checks a parameter against a regular expression.
        * __SchemaValidator__: Checks if an object adheres to the schema. Uses [schema](https://github.com/keleshev/schema) library.
        * __Minimum__: Checks if an optional numerical value is greater than a minimum value.
        * __Maximum__: Checks if an optional numerical value is less than a maximum value.
        * __MinLength__: Checks if an optional string value is longer than a minimum length.
        * __MaxLength__: Checks if an optional string value is shorter than a maximum length.
        
        ### [Decoders](https://github.com/gridsmartercities/aws-lambda-decorators/blob/master/aws_lambda_decorators/decoders.py)
        
        The package offers functions to decode from JSON and JWT. 
        
        * __decode_json__: decodes/converts a json string to a python dictionary
        * __decode_jwt__: decodes/converts a JWT string to a python dictionary
        
        ## Examples
        
        ### extract
        
        This decorator extracts and validates values from dictionary parameters passed to a Lambda Function.
        
        * The decorator takes a list of __Parameter__ objects.
        * Each __Parameter__ object requires a non-empty path to the parameter in the dictionary, and the name of the dictionary (func_param_name)
        * The parameter value is extracted and added as a kwarg to the lambda handler (or any other decorated function/method).
        * You can add the parameter to the handler signature, or access it in the handler through kwargs.
        * The name of the extracted parameter is defaulted to the last element of the path name, but can be changed by passing a (valid pythonic variable name) var_name
        * You can define a default value for the parameter in the __Parameter__ or in the lambda handler itself.
        * A 400 exception is raised when the parameter cannot be extracted or when it does not validate.
        * A variable path (e.g. '/headers/Authorization[jwt]/sub') can be annotated to specify a decoding. In the example, Authorization might contain a JWT, which needs to be decoded before accessing the "sub" element.
        
        Example:
        ```python
        @extract(parameters=[
            Parameter(path='/parent/my_param', func_param_name='a_dictionary'),  # extracts a non mandatory my_param from a_dictionary
            Parameter(path='/parent/missing_non_mandatory', func_param_name='a_dictionary', default='I am missing'),  # extracts a non mandatory missing_non_mandatory from a_dictionary
            Parameter(path='/parent/missing_mandatory', func_param_name='a_dictionary'),  # does not fail as the parameter is not validated as mandatory
            Parameter(path='/parent/child/id', validators=[Mandatory], var_name='user_id', func_param_name='another_dictionary')  # extracts a mandatory id as "user_id" from another_dictionary
        ])
        def extract_example(a_dictionary, another_dictionary, my_param='aDefaultValue', missing_non_mandatory='I am missing', missing_mandatory=None, user_id=None):
            """
                Given these two dictionaries:
                
                a_dictionary = { 
                    'parent': { 
                        'my_param': 'Hello!' 
                    }, 
                    'other': 'other value' 
                }
                
                another_dictionary = { 
                    'parent': { 
                        'child': { 
                            'id': '123' 
                        } 
                    } 
                }
            
                you can now access the extracted parameters directly: 
            """
            return my_param, missing_non_mandatory, missing_mandatory, user_id
        ```
        
        Or you can use kwargs instead of specific parameter names:
        
        Example:
        ```python
        @extract(parameters=[
            Parameter(path='/parent/my_param', func_param_name='a_dictionary')  # extracts a non mandatory my_param from a_dictionary
        ])
        def extract_to_kwargs_example(a_dictionary, **kwargs):
            """
                a_dictionary = { 
                    'parent': { 
                        'my_param': 'Hello!' 
                    }, 
                    'other': 'other value' 
                }
            """
            return kwargs['my_param']  # returns 'Hello!'
        ```
        
        A missing mandatory parameter, or a parameter that fails validation, will raise an exception:
        
        Example:
        ```python
        @extract(parameters=[
            Parameter(path='/parent/mandatory_param', func_param_name='a_dictionary', validators=[Mandatory])  # extracts a mandatory mandatory_param from a_dictionary
        ])
        def extract_mandatory_param_example(a_dictionary, mandatory_param=None):
            return 'Here!'  # this part will never be reached, if the mandatory_param is missing
            
        response = extract_mandatory_param_example({'parent': {'my_param': 'Hello!'}, 'other': 'other value'} )
        
        print(response)  # prints { 'statusCode': 400, 'body': '{"message": [{"mandatory_param": ["Missing mandatory value"]}]}' } and logs a more detailed error
        
        ```
        
        You can add custom error messages to all validators, and incorporate to those error messages the validated value and the validation condition:
        
        Example:
        ```python
        @extract(parameters=[
            Parameter(path='/parent/an_int', func_param_name='a_dictionary', validators=[Minimum(100, 'Bad value {value}: should be at least {condition}')])  # extracts a mandatory mandatory_param from a_dictionary
        ])
        def extract_minimum_param_with_custom_error_example(a_dictionary, mandatory_param=None):
            return 'Here!'  # this part will never be reached, if the an_int param is less than 100
            
        response = extract_minimum_param_with_custom_error_example({'parent': {'an_int': 10}})
        
        print(response)  # prints { 'statusCode': 400, 'body': '{"message": [{"an_int": ["Bad value 10: should be at least 100"]}]}' } and logs a more detailed error
        
        ```
        
        You can group the validation errors together (instead of exiting on first error).
        
        Example:
        ```python
        @extract(parameters=[
            Parameter(path='/parent/mandatory_param', func_param_name='a_dictionary', validators=[Mandatory]),  # extracts two mandatory parameters from a_dictionary
            Parameter(path='/parent/another_mandatory_param', func_param_name='a_dictionary', validators=[Mandatory]),
            Parameter(path='/parent/an_int', func_param_name='a_dictionary', validators=[Maximum(10), Minimum(5)])
        ], group_errors=True)  # groups both errors together
        def extract_multiple_param_example(a_dictionary, mandatory_param=None, another_mandatory_param=None, an_int=0):
            return 'Here!'  # this part will never be reached, if the mandatory_param is missing
            
        response = extract_multiple_param_example({'parent': {'my_param': 'Hello!', 'an_int': 20}, 'other': 'other value'})
        
        print(response)  # prints {'statusCode': 400, 'body': '{"message": [{"mandatory_param": ["Missing mandatory value"]}, {"another_mandatory_param": ["Missing mandatory value"]}, {"an_int": ["\'20\' is greater than maximum value \'10\'"]}]}'}
        
        ```
        
        You can decode any part of the parameter path from json or any other existing annotation.
        
        Example:
        ```python
        @extract(parameters=[
            Parameter(path='/parent[json]/my_param', func_param_name='a_dictionary')  # extracts a non mandatory my_param from a_dictionary
        ])
        def extract_from_json_example(a_dictionary, my_param=None):
            """
                a_dictionary = { 
                    'parent': '{"my_param": "Hello!" }', 
                    'other': 'other value' 
                }
            """
            return my_param  # returns 'Hello!'
        
        ```
        
        You can also use an integer annotation to access an specific list element by index.
        
        Example:
        ```python
        @extract(parameters=[
            Parameter(path='/parent[1]/my_param', func_param_name='a_dictionary')  # extracts a non mandatory my_param from a_dictionary
        ])
        def extract_from_list_example(a_dictionary, my_param=None):
            """
                a_dictionary = { 
                    'parent': [
                        {'my_param': 'Hello!'},
                        {'my_param': 'Bye!'}
                    ]
                }
            """
            return my_param  # returns 'Bye!'
        
        ```
        
        You can extract all parameters into a dictionary
        
        Example:
        ```python
        @extract(parameters=[
            Parameter(path='/params/my_param_1', func_param_name='a_dictionary'),  # extracts a non mandatory my_param_1 from a_dictionary
            Parameter(path='/params/my_param_2', func_param_name='a_dictionary')  # extracts a non mandatory my_param_2 from a_dictionary
        ])
        def extract_dictionary_example(a_dictionary, **kwargs):
            """
                a_dictionary = { 
                    'params': {
                        'my_param_1': 'Hello!',
                        'my_param_2': 'Bye!'
                    }
                }
            """
            return kwargs  # returns {'my_param_1': 'Hello!', 'my_param_2': 'Bye!'}
        
        ```
        
        ### extract_from_event
        
        This decorator is just a facade to the [extract](#extract) method to be used in AWS Api Gateway Lambdas. It automatically extracts from the event lambda parameter.
        
        Example:
        ```python
        @extract_from_event(parameters=[
            Parameter(path='/body[json]/my_param', validators=[Mandatory]),  # extracts a mandatory my_param from the json body of the event
            Parameter(path='/headers/Authorization[jwt]/sub', validators=[Mandatory], var_name='user_id')  # extract the mandatory sub value as user_id from the authorization JWT
        ])
        def extract_from_event_example(event, context, my_param=None, user_id=None):
            """
                event = { 
                    'body': '{"my_param": "Hello!"}', 
                    'headers': { 
                        'Authorization': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' 
                    } 
                }
            """
            return my_param, user_id  # returns ('Hello!', '1234567890')
        ```
        
        ### extract_from_context
        
        This decorator is just a facade to the [extract](#extract) method to be used in AWS Api Gateway Lambdas. It automatically extracts from the context lambda parameter.
        
        Example:
        ```python
        @extract_from_context(parameters=[
            Parameter(path='/parent/my_param', validators=[Mandatory])  # extracts a mandatory my_param from the parent element in context
        ])
        def extract_from_context_example(event, context, my_param=None):
            """
                context = {
                    'parent': {
                        'my_param': 'Hello!'
                    }
                }
            """    
            return my_param  # returns 'Hello!'
        ```
        
        ### extract_from_ssm
        
        This decorator extracts a parameter from AWS SSM and passes the parameter down to your function as a kwarg.
        
        * The decorator takes a list of __SSMParameter__ objects.
        * Each __SSMParameter__ object requires the name of the SSM parameter (ssm_name)
        * If no var_name is passed in, the extracted value is passed to the function with the ssm_name name
        
        Example:
        ```python
        @extract_from_ssm(ssm_parameters=[
            SSMParameter(ssm_name='one_key'),  # extracts the value of one_key from SSM as a kwarg named "one_key"
            SSMParameter(ssm_name='another_key', var_name="another")  # extracts another_key as a kwarg named "another"
        ])
        def extract_from_ssm_example(your_func_params, one_key=None, another=None):
            return your_func_params, one_key, another
        ```
        
        ### validate
        
        This decorator validates a list of non dictionary parameters from your lambda function.
        
        * The decorator takes a list of __ValidatedParameter__ objects.
        * Each parameter object needs the name of the lambda function parameter that it is going to be validated, and the list of rules to validate.
        * A 400 exception is raised when the parameter does not validate.
        
        Example:
        ```python
        @validate(parameters=[
            ValidatedParameter(func_param_name='a_param', validators=[Mandatory]),  # validates a_param as mandatory
            ValidatedParameter(func_param_name='another_param', validators=[Mandatory, RegexValidator(r'\d+')])  # validates another_param as mandatory and containing only digits
            ValidatedParameter(func_param_name='param_with_schema', validators=[SchemaValidator(Schema({'a': Or(str, dict)}))])  # validates param_with_schema as an object with specified schema
        ])
        def validate_example(a_param, another_param, param_with_schema):
            return a_param, another_param, param_with_schema  # returns 'Hello!', '123456', {'a': {'b': 'c'}}
            
        validate_example('Hello!', '123456', {'a': {'b': 'c'}})
        ```
        
        Given the same function `validate_example`, a 400 exception is returned if at least one parameter does not validate (as per the [extract](#extract) decorator, you can group errors with the group_errors flag):
        
        ```python
        validate_example('Hello!', 'ABCD')  # returns a 400 status code and an error message
        ```
        
        ### log
        
        This decorator allows for logging the function arguments and/or the response.
        
        Example:
        ```python
        @log(parameters=True, response=True)
        def log_example(parameters): 
            return 'Done!'
            
        log_example('Hello!')  # logs 'Hello!' and 'Done!'
        ```
        
        ### handle_exceptions
        
        This decorator handles a list of exceptions, returning a 400 response containing the specified friendly message to the caller.
        
        * The decorator takes a list of __ExceptionHandler__ objects.
        * Each __ExceptionHandler__ requires the type of exception to check, and an optional friendly message to return to the caller.
        
        Example:
        ```python
        @handle_exceptions(handlers=[
            ExceptionHandler(ClientError, "Your message when a client error happens.")
        ])
        def handle_exceptions_example():
            dynamodb = boto3.resource('dynamodb')
            table = dynamodb.Table('non_existing_table')
            table.query(KeyConditionExpression=Key('user_id').eq(user_id))
            # ...
            
        handle_exceptions_example()  # returns {'body': '{"message": "Your message when a client error happens."}', 'statusCode': 400}
        ```
        
        ### handle_all_exceptions
        
        This decorator handles all exceptions thrown by a lambda, returning a 400 response and the exception's message.
        
        Example:
        ```python
        @handle_all_exceptions()
        def handle_exceptions_example():
            test_list = [1, 2, 3]
            invalid_value = test_list[5]
            # ...    
        
        handle_all_exceptions_example()  # returns {'body': '{"message": "list index out of range"}, 'statusCode': 400}
        ```
        
        ### response_body_as_json
        
        This decorator ensures that, if the response contains a body, the body is dumped as json.
        
        * Returns a 500 error if the response body cannot be dumped as json.
        
        Example:
        ```python
        @response_body_as_json
        def response_body_as_json_example():
            return {'statusCode': 400, 'body': {'param': 'hello!'}}
            
        response_body_as_json_example()  # returns { 'statusCode': 400, 'body': "{'param': 'hello!'}" }
        ```
        
        ### cors
        
        This decorator adds your defined CORS headers to the decorated function response.
        
        * Returns a 500 error if one or more of the CORS headers have an invalid type
        
        Example:
        ```python
        @cors(allow_origin='*', allow_methods='POST', allow_headers='Content-Type', max_age=86400)
        def cors_example():
            return {'statusCode': 200}
            
        cors_example()  # returns {'statusCode': 200, 'headers': {'access-control-allow-origin': '*', 'access-control-allow-methods': 'POST', 'access-control-allow-headers': 'Content-Type', 'access-control-max-age': 86400}}
        ```
        
        ## Documentation
        
        You can get the docstring help by running:  
        
        ```bash
        >>> from aws_lambda_decorators.decorators import extract
        >>> help(extract)
        ```
        
        ## Links
        
        * [PyPi](https://pypi.org/project/aws-lambda-decorators/)
        * [Test PyPi](https://test.pypi.org/project/aws-lambda-decorators/)
        * [Github](https://github.com/gridsmartercities/aws-lambda-decorators)
        
Keywords: aws lambda decorator
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Natural Language :: English
Description-Content-Type: text/markdown
