def rest_api( self, encoding: Encoding = JsonEncoding(), validator: Validator = NoneValidator() ) -> Callable: """ Decorator for API Gateway event. Automatically parse string if the 'body' can be parsed as Dictionary. Automatically returns 500 error if unexpected error happens. Parameters ---------- response_headers: dict Response headers when 500 error get_app. Usage:: >>> from jeffy.framework import setup >>> from jeffy.encoding.json import JsonEncoding >>> app = get_app() >>> @app.handlers.rest_api(encoding=JsonEncoding()) ... def handler(event, context): ... return event['body']['foo'] """ def _rest_api(func: Callable): # type: ignore @functools.wraps(func) def wrapper(event, context): # type: ignore try: self.capture_correlation_id(event) if event.get('body') is not None: try: event['body'] = encoding.decode( event.get('body', '').encode('utf-8')) validator.validate(event['body']) except (DecodeError, ValidationError) as e: self.app.logger.exception(e) return { 'statusCode': 400, 'headers': { self.app.correlation_id_header: self.app.correlation_id }, 'body': json.dumps({'error_message': str(e)}) } self.app.logger.info(event) ret = func(event, context) if ret.get('headers') is not None: ret['headers'].update({ self.app.correlation_id_header: self.app.correlation_id }) self.app.logger.info(ret) return ret except Exception as e: self.app.logger.exception(e) raise e return wrapper return _rest_api
def common(self, validator: Validator = NoneValidator()) -> Callable: """ Decorator for common event. Automatically logs payload of events and errors with the correlation ID. Usage:: >>> from jeffy.framework import get_app >>> app = get_app() >>> @app.handlers.event_logging ... def handler(event, context): ... return event['body']['foo'] """ def _common(func: Callable): # type: ignore @functools.wraps(func) def wrapper(event, context): # type: ignore self.capture_correlation_id(event) try: self.app.logger.info(event) ret = func(event, context) self.app.logger.info(ret) return ret except Exception as e: self.app.logger.exception(e) raise e return wrapper return _common
def schedule(self, validator: Validator = NoneValidator()) -> Callable: """ Decorator for scheduled events. Usage:: >>> from jeffy.framework import get_app >>> app = get_app() >>> @app.handlers.schedule ... def handler(event, context): ... return event['body']['foo'] """ def _schedule(func: Callable) -> Callable: # type: ignore @functools.wraps(func) def wrapper(event, context): # type: ignore validator.validate(event) self.capture_correlation_id(event) try: self.app.logger.info(event) ret = func(event, context) self.app.logger.info(ret) return ret except Exception as e: raise e return wrapper return _schedule
def dynamodb_streams( self, validator: Validator = NoneValidator()) -> Callable: """ Decorator for Dynamodb streams event. Automatically divide 'Records' for making it easy to treat it inside main process of Lambda. Usage:: >>> from jeffy.framework import get_app >>> app = get_app() >>> @app.handlers.dynamodb_streams ... def handler(event, context): ... return event['body']['foo'] """ def _dynamodb_streams(func: Callable) -> Callable: # type: ignore @functools.wraps(func) def wrapper(event, context): # type: ignore ret = [] for record in event['Records']: message = record['body'] validator.validate(message) self.capture_correlation_id(message) try: ret.append(func(message, context)) except Exception as e: self.app.logger.exception(e) raise e return ret return wrapper return _dynamodb_streams
def s3( self, encoding: Encoding = BytesEncoding(), validator: Validator = NoneValidator() ) -> Callable: """ Decorator for S3 event. Automatically parse object body stream to Lambda. Usage:: >>> from jeffy.framework import setup >>> from jeffy.encoding.bytes import BytesEncoding >>> app = get_app() >>> @app.handlers.s3(encoding=BytesEncoding()) ... def handler(event, context): ... return event['body'] """ def _s3(func: Callable) -> Callable: # type: ignore @functools.wraps(func) def wrapper(event, context): # type: ignore from jeffy.sdk.s3 import S3 s3 = S3() ret = [] for record in event['Records']: bucket = record['s3']['bucket']['name'] key = urllib.parse.unquote_plus( record['s3']['object']['key']) try: response = s3.get_resource().get_object(Bucket=bucket, Key=key) self.capture_correlation_id( response.get('Metadata', {})) body = encoding.decode(response['Body'].read()) validator.validate(body) self.app.logger.info({ 'key': key, 'bucket_name': bucket, 'metadata': response['Metadata'] }) result = func( { 'key': key, 'bucket_name': bucket, 'body': body, 'metadata': response['Metadata'] }, context) self.app.logger.info(result) ret.append(result) except Exception as e: self.app.logger.exception(e) raise e return ret return wrapper return _s3
def kinesis_streams( self, encoding: Encoding = JsonEncoding(), validator: Validator = NoneValidator() ) -> Callable: """ Decorator for Kinesis stream event. Automatically divide 'Records' for making it easy to treat it inside main process of Lambda. Usage:: >>> from jeffy.framework import get_app >>> from jeffy.encoding.json import JsonEncoding >>> app = get_app() >>> @app.handlers.kinesis_streams(encoding=JsonEncoding()) ... def handler(event, context): ... return event['body']['foo'] """ def _kinesis_streams(func: Callable) -> Callable: # type: ignore @functools.wraps(func) def wrapper(event, context): # type: ignore ret = [] for record in event['Records']: message = encoding.decode( base64.b64decode(record['kinesis']['data'])) validator.validate(message) self.capture_correlation_id(message) try: self.app.logger.info(message) result = func(message, context) self.app.logger.info(result) ret.append(result) except Exception as e: self.app.logger.exception(e) raise e return ret return wrapper return _kinesis_streams
def sqs_raw( self, encoding: Encoding = JsonEncoding(), validator: Validator = NoneValidator() ) -> Callable: """ Decorator for sqs raw events (with all metadatas). Automatically divide 'Records' and pass the record to main process of Lambda. Usage:: >>> from jeffy.framework import get_app >>> app = get_app() >>> @app.handlers.sqs_raw() ... def handler(event, context): ... return event['body'] """ def _sqs_raw(func: Callable): # type: ignore @functools.wraps(func) def wrapper(event, context): # type: ignore ret = [] for record in event['Records']: message = encoding.decode(record['body'].encode('utf-8')) validator.validate(message) self.capture_correlation_id(message) record['body'] = message try: self.app.logger.info(message) result = func(record, context) self.app.logger.info(event) ret.append(result) except Exception as e: self.app.logger.exception(e) raise e return ret return wrapper return _sqs_raw