From 31a174d56d8a8bc115ae28a4b1e83faa5f1b52f8 Mon Sep 17 00:00:00 2001 From: Nicolas Moutschen Date: Tue, 16 Nov 2021 14:21:05 +0100 Subject: [PATCH 1/2] fix: null deserialization of stageVariables for sam local --- lambda-http/src/request.rs | 39 +++++++++++++++- .../tests/data/apigw_v2_sam_local.json | 46 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 lambda-http/tests/data/apigw_v2_sam_local.json diff --git a/lambda-http/src/request.rs b/lambda-http/src/request.rs index 5fa5a2b7..fbb17b3e 100644 --- a/lambda-http/src/request.rs +++ b/lambda-http/src/request.rs @@ -37,7 +37,7 @@ pub enum LambdaRequest<'a> { query_string_parameters: StrMap, #[serde(default)] path_parameters: StrMap, - #[serde(default)] + #[serde(default, deserialize_with = "nullable_default")] stage_variables: StrMap, body: Option>, #[serde(default)] @@ -736,6 +736,29 @@ mod tests { ); } + #[test] + fn deserialize_apigw_v2_sam_local() { + // manually generated from AWS SAM CLI + // Steps to recreate: + // * sam init + // * Use, Zip Python 3.9, and Hello World example + // * Change the template to use HttpApi instead of Api + // * Change the function code to return the Lambda event serialized + // * sam local start-api + // * Invoke the API + let input = include_str!("../tests/data/apigw_v2_sam_local.json"); + let result = from_str(input); + assert!( + result.is_ok(), + "event was not parsed as expected {:?} given {}", + result, + input + ); + let req = result.expect("failed to parse request"); + assert_eq!(req.method(), "GET"); + assert_eq!(req.uri(), "http://127.0.0.1:3000/hello"); + } + #[test] fn deserialize_with_null() { #[derive(Debug, PartialEq, Deserialize)] @@ -749,4 +772,18 @@ mod tests { Test { foo: HashMap::new() } ) } + + #[test] + fn deserialize_with_missing() { + #[derive(Debug, PartialEq, Deserialize)] + struct Test { + #[serde(default, deserialize_with = "nullable_default")] + foo: HashMap, + } + + assert_eq!( + serde_json::from_str::(r#"{}"#).expect("failed to deserialize"), + Test { foo: HashMap::new() } + ) + } } diff --git a/lambda-http/tests/data/apigw_v2_sam_local.json b/lambda-http/tests/data/apigw_v2_sam_local.json new file mode 100644 index 00000000..4043b7a1 --- /dev/null +++ b/lambda-http/tests/data/apigw_v2_sam_local.json @@ -0,0 +1,46 @@ +{ + "version": "2.0", + "routeKey": "GET /hello", + "rawPath": "/hello", + "rawQueryString": "", + "cookies": [], + "headers": { + "Host": "127.0.0.1:3000", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", + "Cache-Control": "max-age=0", + "X-Forwarded-Proto": "http", + "X-Forwarded-Port": "3000" + }, + "queryStringParameters": {}, + "requestContext": { + "accountId": "123456789012", + "apiId": "1234567890", + "http": { + "method": "GET", + "path": "/hello", + "protocol": "HTTP/1.1", + "sourceIp": "127.0.0.1", + "userAgent": "Custom User Agent String" + }, + "requestId": "1ac06eee-f687-44ec-9036-dfd49d0be0a3", + "routeKey": "GET /hello", + "stage": "$default", + "time": "16/Nov/2021:11:54:33 +0000", + "timeEpoch": 1637063673, + "domainName": "localhost", + "domainPrefix": "localhost" + }, + "body": "", + "pathParameters": {}, + "stageVariables": null, + "isBase64Encoded": false +} \ No newline at end of file From 5e890ae38fb8890e7f67e2d7d52b4bf7dcb1610d Mon Sep 17 00:00:00 2001 From: Nicolas Moutschen Date: Wed, 17 Nov 2021 12:59:49 +0100 Subject: [PATCH 2/2] feat: make all StrMap fields in LambdaRequest deserializable with null or default value --- lambda-http/src/request.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lambda-http/src/request.rs b/lambda-http/src/request.rs index fbb17b3e..6bef39ce 100644 --- a/lambda-http/src/request.rs +++ b/lambda-http/src/request.rs @@ -33,9 +33,9 @@ pub enum LambdaRequest<'a> { cookies: Option>>, #[serde(deserialize_with = "deserialize_headers")] headers: http::HeaderMap, - #[serde(default)] + #[serde(default, deserialize_with = "nullable_default")] query_string_parameters: StrMap, - #[serde(default)] + #[serde(default, deserialize_with = "nullable_default")] path_parameters: StrMap, #[serde(default, deserialize_with = "nullable_default")] stage_variables: StrMap, @@ -55,7 +55,7 @@ pub enum LambdaRequest<'a> { /// the `lambda.multi_value_headers.enabled` target group setting turned on #[serde(default, deserialize_with = "deserialize_multi_value_headers")] multi_value_headers: http::HeaderMap, - #[serde(deserialize_with = "nullable_default")] + #[serde(default, deserialize_with = "nullable_default")] query_string_parameters: StrMap, /// For alb events these are only present when /// the `lambda.multi_value_headers.enabled` target group setting turned on @@ -75,7 +75,7 @@ pub enum LambdaRequest<'a> { headers: http::HeaderMap, #[serde(default, deserialize_with = "deserialize_multi_value_headers")] multi_value_headers: http::HeaderMap, - #[serde(deserialize_with = "nullable_default")] + #[serde(default, deserialize_with = "nullable_default")] query_string_parameters: StrMap, #[serde(default, deserialize_with = "nullable_default")] multi_value_query_string_parameters: StrMap,