Skip to content

feat(event_handler): add File parameter support for multipart/form-data uploads in OpenAPI utility #7132

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

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from

Conversation

oyiz-michael
Copy link
Contributor

Issue number: #7124

Summary

This PR adds comprehensive File parameter support for handling file uploads in multipart/form-data requests within the AWS Lambda Powertools Python Event Handler with OpenAPI validation.

Changes

  • Added File class in aws_lambda_powertools/event_handler/openapi/params.py

    • New parameter type specifically for file uploads
    • Inherits from Form with format: binary in OpenAPI schema
    • Supports validation constraints (max_length, etc.)
  • Enhanced multipart parsing in aws_lambda_powertools/event_handler/middlewares/openapi_validation.py

    • Added _parse_multipart_data method for parsing multipart/form-data
    • WebKit boundary support for Safari/Chrome compatibility
    • Base64 decoding support for Lambda event handling
    • Distinguishes between file fields and form fields
  • Comprehensive test suite with 13 test scenarios covering:

    • Basic file uploads and multiple file handling
    • File + form data combinations
    • WebKit boundary parsing and base64 encoded content
    • Validation constraints and error handling
    • Optional file parameters
  • Complete usage example in examples/event_handler_rest/src/file_parameter_example.py

User experience

Before: Users could not handle file uploads in multipart/form-data requests with OpenAPI validation. They had to manually parse request bodies or disable validation entirely.

After: Users can now use type-annotated File parameters that automatically:

  • Parse multipart/form-data file uploads
  • Generate proper OpenAPI schema with format: binary
  • Apply validation constraints
  • Work seamlessly with existing form parameters
from typing import Annotated
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.event_handler.openapi.params import File, Form

app = APIGatewayRestResolver(enable_validation=True)

@app.post("/upload")
def upload_file(
    file: Annotated[bytes, File(description="File to upload", max_length=1000000)],
    title: Annotated[str, Form(description="File title")]
):
    return {"file_size": len(file), "title": title, "status": "uploaded"}

Checklist

If your change doesn't seem to apply, please leave them unchecked.

Is this a breaking change? RFC issue number: N/A

This is not a breaking change - it's a new feature addition that doesn't modify existing functionality.

Checklist:

  • Migration process documented
  • Implement warnings (if it can live side by side)

Acknowledgment

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

…ploads

- Add public File parameter class extending _File
- Support multipart/form-data parsing with WebKit boundary compatibility
- OpenAPI schema generation with format: binary for file uploads
- Enhanced dependant logic to handle File + Form parameter combinations
- Clean implementation based on upstream develop branch

Changes:
- params.py: Add File(_File) public class with proper documentation
- dependant.py: Add File parameter support in body field info logic
- openapi_validation.py: Add multipart parsing with boundary detection
- test_file_form_validation.py: Basic test coverage for File parameters

This provides customers with File parameter support using the same
pattern as Query, Path, Header parameters with Annotated types.
- Add File parameter class in openapi/params.py with binary format schema
- Implement comprehensive multipart/form-data parsing in openapi_validation.py
  * Support for WebKit and standard boundary formats
  * Base64-encoded request handling for AWS Lambda
  * Mixed file and form data parsing
- Update dependant.py to handle File parameters in body field resolution
- Add comprehensive test suite (13 tests) covering:
  * Basic file upload parsing and validation
  * WebKit boundary format support
  * Base64-encoded multipart data
  * Multiple file uploads
  * File size constraints validation
  * Optional file parameters
  * Error handling for invalid boundaries and missing files
- Add file_parameter_example.py demonstrating various File parameter use cases
- Clean up unnecessary imports and pragma comments

Resolves file upload functionality with full OpenAPI schema generation and validation support.
@oyiz-michael oyiz-michael requested a review from a team as a code owner August 6, 2025 22:12
@pull-request-size pull-request-size bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Aug 6, 2025

Not all issues are linked correctly.

Please link each issue to the PR either manually or using a closing keyword in the format fixes #<issue-number> format.

If mentioning more than one issue, separate them with commas: i.e. fixes #<issue-number-1>, closes #<issue-number-2>.

- Break down _parse_multipart_data method into smaller helper methods
- Reduce cognitive complexity from 43 to under 15 per SonarCloud requirement
- Improve code readability and maintainability
- All existing tests continue to pass

Helper methods created:
- _decode_request_body: Handle base64 decoding
- _extract_boundary_bytes: Extract multipart boundary
- _parse_multipart_sections: Parse sections into data dict
- _parse_multipart_section: Handle individual section parsing
- _split_section_headers_and_content: Split headers/content
- _decode_form_field_content: Decode form field as string

Addresses SonarCloud cognitive complexity violation while maintaining
all existing functionality for File parameter multipart parsing.
@oyiz-michael oyiz-michael changed the title Feature/file parameter clean feat: add File parameter support for multipart/form-data uploads Aug 6, 2025
@github-actions github-actions bot added the feature New feature or functionality label Aug 6, 2025
@oyiz-michael oyiz-michael changed the title feat: add File parameter support for multipart/form-data uploads feat(event_handler): add File parameter support for multipart/form-data uploads in OpenAPI utility Aug 6, 2025
- Add missing __future__ annotations imports
- Remove unused pytest imports from test files
- Remove unused json import from example
- Fix line length violations in test files
- All File parameter tests continue to pass (13/13)

Addresses ruff linting violations:
- FA102: Missing future annotations for PEP 604 unions
- F401: Unused imports
- E501: Line too long violations
@leandrodamascena
Copy link
Contributor

Hi @oyiz-michael, I see you are working on this PR and please let me know when you need a first round of review or any help.

@leandrodamascena leandrodamascena linked an issue Aug 7, 2025 that may be closed by this pull request
2 tasks
- Replace bytes | None with Union[bytes, None] for broader compatibility
- Replace str | None with Union[str, None] in examples
- Add noqa: UP007 comments to suppress linter preference for newer syntax
- Ensures compatibility with Python environments that don't support PEP 604 unions
- Fixes test failure: 'Unable to evaluate type annotation bytes | None'

All File parameter tests continue to pass (13/13) across Python versions.
Copy link

codecov bot commented Aug 7, 2025

Codecov Report

❌ Patch coverage is 72.83951% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.21%. Comparing base (ddd9b14) to head (54b3723).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
...ls/event_handler/middlewares/openapi_validation.py 76.11% 10 Missing and 6 partials ⚠️
...mbda_powertools/event_handler/openapi/dependant.py 50.00% 3 Missing and 1 partial ⚠️
..._lambda_powertools/event_handler/openapi/params.py 66.66% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #7132      +/-   ##
===========================================
- Coverage    96.35%   96.21%   -0.14%     
===========================================
  Files          275      275              
  Lines        12980    13058      +78     
  Branches       965      980      +15     
===========================================
+ Hits         12507    12564      +57     
- Misses         366      379      +13     
- Partials       107      115       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@oyiz-michael
Copy link
Contributor Author

Hi @oyiz-michael, I see you are working on this PR and please let me know when you need a first round of review or any help.

@leandrodamascena fixing some failing test and should be ready for a review and feed back

@pull-request-size pull-request-size bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Aug 7, 2025
@leandrodamascena
Copy link
Contributor

Hi @oyiz-michael, a quick tip: run make pr in your local environment and then you can catch errors before committing and pushing the files.

Copy link
Contributor

@leandrodamascena leandrodamascena left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey hey @oyiz-michael this is a super nice PR! I was playing with this code with a real Lambda and is working as expect, but I think we need to improve when customers are uploading files..

I left a comment to investigate. Lets see if we can improve it without breaking the current implementation.

headers_part = section[:header_end].decode("utf-8", errors="ignore")
return headers_part, content

def _decode_form_field_content(self, content: bytes) -> str | bytes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm I guess this method returns the decode content of the file and while this is nice, I think developers must also have access to filename, headers, content-type to reconstruct the file in the Lambda..

I'm talking about something like FastAPI is doing with UploadFile class - https://fastapi.tiangolo.com/reference/uploadfile/#fastapi.UploadFile.file.

Can you investigate this, please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will look in to it and see how we can have that part of the implementation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leandrodamascena! Great feedback! I've implemented exactly what you requested - a FastAPI-inspired UploadFile class that provides developers with complete access to filename, headers, content-type, and all metadata needed to reconstruct files in Lambda functions.

UploadFile Response:
{
"filename": "important-document.pdf",
"content_type": "application/pdf",
"size": 52,
"headers": {
"Content-Type": "application/pdf",
"X-Upload-ID": "12345",
"X-File-Hash": "abc123def456"
},
"content_preview": "PDF file content with metadata for reconstruction.",
"can_reconstruct_file": true
}

Backward Compatibility Response:
{
"message": "Existing code works!",
"size": 27
}

- Add FastAPI-inspired UploadFile class with filename, content_type, size, headers properties
- Enhance multipart parser to extract and preserve file metadata from Content-Disposition headers
- Implement automatic type resolution for backward compatibility with existing bytes-based File parameters
- Add comprehensive Pydantic schema validation for UploadFile class
- Include 6 comprehensive test cases covering metadata access, backward compatibility, and file reconstruction scenarios
- Update official example to showcase both new UploadFile and legacy bytes approaches
- Maintain 100% backward compatibility - existing bytes code works unchanged
- Address @leandrodamascena feedback about file reconstruction capabilities in Lambda environments

Fixes: File parameter enhancement for metadata access in AWS Lambda file uploads
…_to_args

- Extract helper functions to reduce cognitive complexity from 24 to under 15
- _get_field_location: Extract field location logic
- _get_field_value: Extract value retrieval logic with error handling
- _resolve_field_type: Extract Union type resolution logic
- _convert_value_type: Extract UploadFile/bytes conversion logic
- Maintain all existing functionality and test coverage
- Improve code readability and maintainability
Copy link

sonarqubecloud bot commented Aug 7, 2025

@leandrodamascena
Copy link
Contributor

Hi @oyiz-michael I'll take a look at the changes on Monday. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
event_handlers feature New feature or functionality size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. tests
Projects
None yet
2 participants