Skip to content

Feature request: Support types annotated with Field for event_handler validation #5953

Open
@rtandy

Description

@rtandy

Use case

Hi, thank you for working on Powertools.

My Pydantic models are shared between multiple applications. For example, a client application builds an instance of a model and submits it to the API, which validates the request against the same model.

A few types I want to accept as API payloads or parameters are annotated with Field, mainly tagged unions.

I am aware of the Powertools OpenAPI Param annotations, but they have downsides:

  • having to import Powertools into non-Lambda projects;
  • being stuck with a single Param type (i.e. Body, Path, etc) per annotated type, or having to duplicate the arguments for different types

With Powertools 3.5.0, the example code below fails:

  • with def create_action(action: Action): AssertionError: Param: action can only be a request body, use Body()
  • with def create_action(action: Annotated[Action, Body()]): AssertionError: Only one FieldInfo can be used per parameter

It would be nice if event_handler would allow nested Field annotations, so that I can apply Annotated[some_type, Body()] to an existing annotated type such as a tagged union.

Thank you for considering.

Solution/User Experience

class FooAction(BaseModel):
    action: Literal['foo']
    foo: int 

class BarAction(BaseModel):
    action: Literal['bar']
    bar: int 

Action = Annotated[FooAction | BarAction, Field(discriminator='action')]

app = APIGatewayHttpResolver(enable_validation=True)

@app.post('/actions')
def create_action(action: Action): # or Annotated[Action, Body()]
    ...

def lambda_handler(event, context):
    return app.resolve(event, context)

Alternative solutions

  1. use event_handler OpenAPI Param annotations exclusively
Action = Annotated[FooAction | BarAction, Body(discriminator='action')]
  1. use TypeAdapter manually
Action = Annotated[FooAction | BarAction, Field(discriminator='action')]
action_adapter = TypeAdapter(Action)

@app.post('/actions')
def create_action():
    try:
        action = action_adapter.validate_json(app.current_event.body)
    except ValidationError as e:
        raise RequestValidationError(e.errors())

Acknowledgment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Ideas

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions