Skip to content

Feature request: Support Field annotation for event_handler parameter validation #8304

Description

@rtandy

Use case

Hi, this is a follow-up to #5953.

@dap0am kindly implemented the example use case I showed there (thanks!). However, it looks like they actually only special-cased the specific example (discriminated union in the body). General support for Field annotations with different parameter types (path/query/header/body) is still missing.

This issue is to express more explicitly the originally intended request: I wish to be able to use any Field annotation in parameter validation, same as if the field were used in a model, with any kind of parameter.

Thank you for maintaining Powertools!

Solution/User Experience

from http import HTTPStatus
from typing import Annotated

from aws_lambda_powertools.event_handler import APIGatewayHttpResolver
from aws_lambda_powertools.event_handler.openapi.params import Body, Header, Path, Query

from pydantic.fields import Field

# may be defined elsewhere; used in models as well as parameters; etc
StrField = Annotated[str, Field()]

app = APIGatewayHttpResolver(enable_validation=True)

@app.get('/header')
def get_header(h: Annotated[StrField, Header]) -> None:
    ...

@app.get('/path/<p>')
def get_path(p: Annotated[StrField, Path]) -> None:
    ...

@app.get('/query')
def get_query(q: Annotated[StrField, Query]) -> None:
    ...

@app.post('/body')
def post_body(b: Annotated[StrField, Body]) -> None:
    ...

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

def test_get_header():
    event = {
        'headers': {'h': 'test'},
        'rawPath': '/header',
        'requestContext': {
            'http': { 'method': 'GET' },
            'stage': '$default'
        }
    }

    response = lambda_handler(event, None)
    assert HTTPStatus.OK == response['statusCode']

def test_get_path():
    event = {
        'rawPath': '/path/test',
        'requestContext': {
            'http': { 'method': 'GET' },
            'stage': '$default'
        }
    }

    response = lambda_handler(event, None)
    assert HTTPStatus.OK == response['statusCode']

def test_get_query():
    event = {
        'rawPath': '/query',
        'requestContext': {
            'http': { 'method': 'GET' },
            'stage': '$default'
        },
        'queryStringParameters': {'q': 'test'}
    }

    response = lambda_handler(event, None)
    assert HTTPStatus.OK == response['statusCode']

def test_post_body():
    event = {
        'body': '"test"',
        'rawPath': '/body',
        'requestContext': {
            'http': { 'method': 'POST' },
            'stage': '$default'
        },
    }

    response = lambda_handler(event, None)
    assert HTTPStatus.OK == response['statusCode']

Alternative solutions

Manual validation using TypeAdapter inside each route.

Acknowledgment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-requestfeature requesttriagePending triage from maintainers

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions