-
Notifications
You must be signed in to change notification settings - Fork 380
Add builder pattern support for event response types #1090
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
FullyTyped
wants to merge
3
commits into
main
Choose a base branch
from
derive-builders
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| // Example demonstrating builder pattern usage for AWS Lambda events | ||
| #[cfg(feature = "builders")] | ||
| use aws_lambda_events::event::{ | ||
| dynamodb::EventBuilder as DynamoDbEventBuilder, kinesis::KinesisEventBuilder, s3::S3EventBuilder, | ||
| secretsmanager::SecretsManagerSecretRotationEventBuilder, sns::SnsEventBuilder, sqs::SqsEventBuilder, | ||
| }; | ||
|
|
||
| #[cfg(feature = "builders")] | ||
| fn main() { | ||
| // S3 Event - Object storage notifications | ||
| let s3_event = S3EventBuilder::default().records(vec![]).build().unwrap(); | ||
|
|
||
| // Kinesis Event - Stream processing | ||
| let kinesis_event = KinesisEventBuilder::default().records(vec![]).build().unwrap(); | ||
|
|
||
| // DynamoDB Event - Database change streams | ||
| let dynamodb_event = DynamoDbEventBuilder::default().records(vec![]).build().unwrap(); | ||
|
|
||
| // SNS Event - Pub/sub messaging | ||
| let sns_event = SnsEventBuilder::default().records(vec![]).build().unwrap(); | ||
|
|
||
| // SQS Event - Queue messaging | ||
| let sqs_event = SqsEventBuilder::default().records(vec![]).build().unwrap(); | ||
|
|
||
| // Secrets Manager Event - Secret rotation | ||
| let secrets_event = SecretsManagerSecretRotationEventBuilder::default() | ||
| .step("createSecret") | ||
| .secret_id("test-secret") | ||
| .client_request_token("token-123") | ||
| .build() | ||
| .unwrap(); | ||
| } | ||
|
|
||
| #[cfg(not(feature = "builders"))] | ||
| fn main() { | ||
| println!("This example requires the 'builders' feature to be enabled."); | ||
| println!("Run with: cargo run --example comprehensive-builders --all-features"); | ||
| } |
148 changes: 148 additions & 0 deletions
148
lambda-events/examples/lambda-runtime-authorizer-builder.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| // Example showing how builders solve the Default trait requirement problem | ||
| // when using lambda_runtime with API Gateway custom authorizers | ||
| // | ||
| // ❌ OLD WAY (with Default requirement): | ||
| // #[derive(Default)] | ||
| // struct MyContext { | ||
| // // Had to use Option just for Default | ||
| // some_thing: Option<ThirdPartyThing>, | ||
| // } | ||
| // | ||
| // let mut output = Response::default(); | ||
| // output.is_authorized = true; | ||
| // output.context = MyContext { | ||
| // some_thing: Some(thing), // ❌ Unnecessary Some() | ||
| // }; | ||
| // | ||
| // ✅ NEW WAY (with Builder pattern): | ||
| // struct MyContext { | ||
| // // No Option needed! | ||
| // some_thing: ThirdPartyThing, | ||
| // } | ||
| // | ||
| // let output = ResponseBuilder::default() | ||
| // .is_authorized(true) | ||
| // .context(context) | ||
| // .build()?; | ||
| // | ||
| // Benefits: | ||
| // • No Option<T> wrapper for fields that always exist | ||
| // • Type-safe construction | ||
| // • Works seamlessly with lambda_runtime::LambdaEvent | ||
| // • Cleaner, more idiomatic Rust code | ||
|
|
||
| #[cfg(feature = "builders")] | ||
| use aws_lambda_events::event::apigw::{ | ||
| ApiGatewayV2CustomAuthorizerSimpleResponse, ApiGatewayV2CustomAuthorizerSimpleResponseBuilder, | ||
| ApiGatewayV2CustomAuthorizerV2Request, | ||
| }; | ||
| #[cfg(feature = "builders")] | ||
| use lambda_runtime::{Error, LambdaEvent}; | ||
| #[cfg(feature = "builders")] | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| #[cfg(feature = "builders")] | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct SomeThirdPartyThingWithoutDefaultValue { | ||
| pub api_key: String, | ||
| pub endpoint: String, | ||
| pub timeout_ms: u64, | ||
| } | ||
|
|
||
| // ❌ OLD WAY: Had to use Option to satisfy Default requirement | ||
| #[cfg(feature = "builders")] | ||
| #[derive(Debug, Default, Serialize, Deserialize)] | ||
| pub struct MyContextOldWay { | ||
| // NOT IDEAL: Need to wrap with Option just for Default | ||
| some_thing_always_exists: Option<SomeThirdPartyThingWithoutDefaultValue>, | ||
| } | ||
|
|
||
| // ✅ NEW WAY: No Option needed with builder pattern! | ||
| #[cfg(feature = "builders")] | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct MyContext { | ||
| // IDEAL: Can use the actual type directly! | ||
| some_thing_always_exists: SomeThirdPartyThingWithoutDefaultValue, | ||
| user_id: String, | ||
| permissions: Vec<String>, | ||
| } | ||
|
|
||
| // ❌ OLD IMPLEMENTATION: Using Default | ||
| #[cfg(feature = "builders")] | ||
| pub async fn function_handler_old_way( | ||
| _event: LambdaEvent<ApiGatewayV2CustomAuthorizerV2Request>, | ||
| ) -> Result<ApiGatewayV2CustomAuthorizerSimpleResponse<MyContextOldWay>, Error> { | ||
| let mut output: ApiGatewayV2CustomAuthorizerSimpleResponse<MyContextOldWay> = | ||
| ApiGatewayV2CustomAuthorizerSimpleResponse::default(); | ||
|
|
||
| output.is_authorized = true; | ||
| output.context = MyContextOldWay { | ||
| // ❌ Had to wrap in Some() even though it always exists | ||
| some_thing_always_exists: Some(SomeThirdPartyThingWithoutDefaultValue { | ||
| api_key: "secret-key-123".to_string(), | ||
| endpoint: "https://api.example.com".to_string(), | ||
| timeout_ms: 5000, | ||
| }), | ||
| }; | ||
|
|
||
| Ok(output) | ||
| } | ||
|
|
||
| // ✅ NEW IMPLEMENTATION: Using Builder | ||
| #[cfg(feature = "builders")] | ||
| pub async fn function_handler( | ||
| _event: LambdaEvent<ApiGatewayV2CustomAuthorizerV2Request>, | ||
| ) -> Result<ApiGatewayV2CustomAuthorizerSimpleResponse<MyContext>, Error> { | ||
| let context = MyContext { | ||
| // ✅ No Option wrapper needed! | ||
| some_thing_always_exists: SomeThirdPartyThingWithoutDefaultValue { | ||
| api_key: "secret-key-123".to_string(), | ||
| endpoint: "https://api.example.com".to_string(), | ||
| timeout_ms: 5000, | ||
| }, | ||
| user_id: "user-123".to_string(), | ||
| permissions: vec!["read".to_string(), "write".to_string()], | ||
| }; | ||
|
|
||
| // ✅ Clean builder pattern - no Default required! | ||
| let output = ApiGatewayV2CustomAuthorizerSimpleResponseBuilder::default() | ||
| .is_authorized(true) | ||
| .context(context) | ||
| .build() | ||
| .map_err(|e| format!("Failed to build response: {}", e))?; | ||
|
|
||
| Ok(output) | ||
| } | ||
|
|
||
| #[cfg(feature = "builders")] | ||
| fn main() { | ||
| let context = MyContext { | ||
| some_thing_always_exists: SomeThirdPartyThingWithoutDefaultValue { | ||
| api_key: "secret-key-123".to_string(), | ||
| endpoint: "https://api.example.com".to_string(), | ||
| timeout_ms: 5000, | ||
| }, | ||
| user_id: "user-123".to_string(), | ||
| permissions: vec!["read".to_string(), "write".to_string()], | ||
| }; | ||
|
|
||
| let response = ApiGatewayV2CustomAuthorizerSimpleResponseBuilder::<MyContext>::default() | ||
| .is_authorized(true) | ||
| .context(context) | ||
| .build() | ||
| .unwrap(); | ||
|
|
||
| println!("✅ Built authorizer response for user: {}", response.context.user_id); | ||
| println!(" Authorized: {}", response.is_authorized); | ||
| println!(" Permissions: {:?}", response.context.permissions); | ||
| println!( | ||
| " Third-party endpoint: {}", | ||
| response.context.some_thing_always_exists.endpoint | ||
| ); | ||
| } | ||
|
|
||
| #[cfg(not(feature = "builders"))] | ||
| fn main() { | ||
| println!("This example requires the 'builders' feature to be enabled."); | ||
| println!("Run with: cargo run --example lambda-runtime-authorizer-builder --features builders"); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would you feel about using
boninstead? The big reason would be that it uses typestate for compile time verification, which is pretty nice for ergonomics.The better refactoring support probably doesn't affect us since we are anyway all pub so would break semver either way on internal changes.
The function builders we don't need yet, though I wouldn't be surprised if it is useful later. For instance, we have split into separate APIs for the concurrent mode. Future features like that would be nice to add as optional parameters via a builder.