Skip to content

EventParser APIGatewayProxyEventModel body cannot be none #797

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

Closed
bb-rrogers opened this issue Nov 4, 2021 · 10 comments
Closed

EventParser APIGatewayProxyEventModel body cannot be none #797

bb-rrogers opened this issue Nov 4, 2021 · 10 comments
Assignees
Labels
bug Something isn't working p1

Comments

@bb-rrogers
Copy link

First time user of AWS Lambda PowerTools and I would just like to say this looks like an amazing library.

I recently came across what I think might be a bug, but not sure. When creating a simple lambda with a APIGatewayProxyEventModel being parsed it throws an error unless I put a request body in my payload. This is due to the body object being None while the model expects a "str".

This issue affects me because it makes it a body a requirement for every request.

Expected Behavior

It should execute as expected with body being an object that can be independently parsed.

Current Behavior

When invoking the Steps to Reproduce code I get the following in CloudWatch. As such the current behavior is that if body is a None object it will fail validation.

[ERROR] ValidationError: 1 validation error for APIGatewayProxyEventModel
body
  none is not an allowed value (type=type_error.none.not_allowed)
Traceback (most recent call last):
  File "/opt/python/aws_lambda_powertools/middleware_factory/factory.py", line 133, in wrapper
    response = middleware()
  File "/var/task/get_preference.py", line 18, in api_handler
    response = handler(event, context)
  File "/opt/python/aws_lambda_powertools/middleware_factory/factory.py", line 133, in wrapper
    response = middleware()
  File "/opt/python/aws_lambda_powertools/utilities/parser/parser.py", line 84, in event_parser
    parsed_event = parse(event=event, model=model, envelope=envelope)
  File "/opt/python/aws_lambda_powertools/utilities/parser/parser.py", line 157, in parse
    return model.parse_obj(event)
  File "/opt/python/pydantic/main.py", line 578, in parse_obj
    return cls(**obj)
  File "/opt/python/pydantic/main.py", line 406, in __init__
    raise validation_error

Possible Solution

Make body an optional object.

Steps to Reproduce (for bugs)

Simple Code should help you reproduce it:

import json

from aws_lambda_powertools import Logger, Tracer, Metrics
from aws_lambda_powertools.middleware_factory import lambda_handler_decorator
from aws_lambda_powertools.utilities.parser import event_parser
from aws_lambda_powertools.utilities.parser.models import APIGatewayProxyEventModel
from aws_lambda_powertools.utilities.typing import LambdaContext

logger = Logger()
tracer = Tracer()
metrics = Metrics()


@lambda_handler_decorator
def api_handler(handler, event, context, status_code):
    response = handler(event, context)
    return {
        "statusCode": status_code,
        "body": json.dumps(response)
    }


@api_handler(status_code=200)
@event_parser(model=APIGatewayProxyEventModel)
def handler(event: APIGatewayProxyEventModel, _context=LambdaContext()):
    return event

Environment

  • Powertools version used: 1.21.1
  • Packaging format (Layers, PyPi): PyPi
  • AWS Lambda function runtime: 3.9 -> ARM
  • Debugging logs
@bb-rrogers bb-rrogers added bug Something isn't working triage Pending triage from maintainers labels Nov 4, 2021
@boring-cyborg
Copy link

boring-cyborg bot commented Nov 4, 2021

Thanks for opening your first issue here! We'll come back to you as soon as we can.

@heitorlessa
Copy link
Contributor

heitorlessa commented Nov 4, 2021

Hey @bb-rrogers, that's awesome to hear you're finding it useful!

Would you have a sample event for us to test it? IIRC, API Gateway sends an empty body string when there is none, for example a typical GET request. Nevertheless, we'll try creating a dummy infra to test it.

Because I see you're using an extra middleware to control the response + using JSON dump, I think you might prefer using our API Gateway Event Handler instead to solve this and many other common developer experience issues.

https://awslabs.github.io/aws-lambda-powertools-python/latest/core/event_handler/api_gateway/


Example

from aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver

tracer = Tracer()
logger = Logger()
app = ApiGatewayResolver()  # by default API Gateway REST API (v1)

@app.get("/hello")
@tracer.capture_method
def get_hello_universe():
    return {"message": "hello universe"}

# You can continue to use other utilities just as before
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
@tracer.capture_lambda_handler
def lambda_handler(event, context):
    return app.resolve(event, context)

@heitorlessa heitorlessa added the need-more-information Pending information to continue label Nov 4, 2021
@bb-rrogers
Copy link
Author

Hey @bb-rrogers, that's awesome to hear you're finding it useful!

Would you have a sample event for us to test it? IIRC, API Gateway sends an empty body string when there is none, for example a typical GET request. Nevertheless, we'll try creating a dummy infra to test it.

Because I see you're using an extra middleware to control the response + using JSON dump, I think you might prefer using our API Gateway Event Handler instead to solve this and many other common developer experience issues.

https://awslabs.github.io/aws-lambda-powertools-python/latest/core/event_handler/api_gateway/

Example

from aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver

tracer = Tracer()
logger = Logger()
app = ApiGatewayResolver()  # by default API Gateway REST API (v1)

@app.get("/hello")
@tracer.capture_method
def get_hello_universe():
    return {"message": "hello universe"}

# You can continue to use other utilities just as before
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
@tracer.capture_lambda_handler
def lambda_handler(event, context):
    return app.resolve(event, context)

Hello @heitorlessa,

Thanks for getting back so quickly, firstly what are you wanting in regards to a sample event. I simply set up a aws proxy lambda against an apigateway and hit it using a GET request with Postman with no request body without any modifications.

As to your suggestion regarding API Gateway Event Handler. I looked at this but it seemed to more more orientated to a simple lambda handling multiple endpoints were we actually use multiple lambdas with a common layer on top.

If you think we should go down the right of a API Event Handler then I could investigate further but I imagine this might still be the same problem.

Also I will highlight that we are looking at maybe doing body transformation with a custom lambda integration in the future but are investigating how to handle errors nicely it seems all errors need to be json encoded in the error Message which takes away the benefit of lambda not containing api related code.

Any thoughts or suggestions would be greatly appreciated.

@heitorlessa
Copy link
Contributor

I see. It definitely looks like a bug to be investigated - no need to share a sample event that your Functions received.

As for the use case, I can ask more questions on what this common top layer would look like later, then I can make suggestions on how to maybe make this easier and more easily maintainable, e.g cross-cutting concerns for before/after request and error handling?)

@bb-rrogers
Copy link
Author

I see. It definitely looks like a bug to be investigated - no need to share a sample event that your Functions received.

As for the use case, I can ask more questions on what this common top layer would look like later, then I can make suggestions on how to maybe make this easier and more easily maintainable, e.g cross-cutting concerns for before/after request and error handling?)

Hey @heitorlessa - Just to be clear is there anymore information I need to give here for this bug? I'm not quite sure.

@heitorlessa
Copy link
Contributor

No @bb-rrogers. We'll setup a sample infrastructure to test whether we can hit the same issue -- if we can't, we will ask

@heitorlessa heitorlessa self-assigned this Nov 12, 2021
@heitorlessa heitorlessa added p1 area/parser and removed triage Pending triage from maintainers need-more-information Pending information to continue labels Nov 12, 2021
@heitorlessa heitorlessa added the pending-release Fix or implementation already in dev waiting to be released label Nov 13, 2021
@heitorlessa
Copy link
Contributor

@bb-rrogers just merged the fix for API GW v1, and also found the same bug in v2, including Query Strings key which are omitted in the payload when using API GW HTTP API v2 payload.

Should be out early next week as part of the next release.

@bb-rrogers
Copy link
Author

Thanks a bunch @heitorlessa

@heitorlessa
Copy link
Contributor

hey @bb-rrogers this is now out as part of 1.22.0 release - available on PyPi, SAR, and soon Lambda Layer (version 4).

https://github.com/awslabs/aws-lambda-powertools-python/releases/tag/v1.22.0

@heitorlessa heitorlessa removed the pending-release Fix or implementation already in dev waiting to be released label Nov 17, 2021
@gomboc1985
Copy link

Hello @heitorlessa, I noticed that there might still be a bug there as discussed here.

Specifically, headers and multiValueHeaders should be optional as it might happen when testing the lambda straight from the API Gatewat

Is it really a bug or am using the parser in the wrong way?
API Gateway Console - Copy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working p1
Projects
None yet
Development

No branches or pull requests

3 participants