diff --git a/lambda-events/Cargo.toml b/lambda-events/Cargo.toml index 28df6b4a..27b577cb 100644 --- a/lambda-events/Cargo.toml +++ b/lambda-events/Cargo.toml @@ -30,7 +30,7 @@ chrono = { version = "0.4.23", default-features = false, features = [ "serde", "std", ], optional = true } -query_map = { version = "^0.6", features = ["serde", "url-query"], optional = true } +query_map = { version = "^0.7", features = ["serde", "url-query"], optional = true } flate2 = { version = "1.0.24", optional = true } [features] diff --git a/lambda-events/src/event/apigw/mod.rs b/lambda-events/src/event/apigw/mod.rs index b595d825..9119bdc7 100644 --- a/lambda-events/src/event/apigw/mod.rs +++ b/lambda-events/src/event/apigw/mod.rs @@ -34,6 +34,7 @@ where #[serde(serialize_with = "serialize_multi_value_headers")] pub multi_value_headers: HeaderMap, #[serde(default, deserialize_with = "query_map::serde::standard::deserialize_empty")] + #[serde(serialize_with = "query_map::serde::aws_api_gateway_v1::serialize_query_string_parameters")] pub query_string_parameters: QueryMap, #[serde(default, deserialize_with = "query_map::serde::standard::deserialize_empty")] pub multi_value_query_string_parameters: QueryMap, @@ -151,6 +152,7 @@ pub struct ApiGatewayV2httpRequest { deserialize_with = "query_map::serde::aws_api_gateway_v2::deserialize_empty" )] #[serde(skip_serializing_if = "QueryMap::is_empty")] + #[serde(serialize_with = "query_map::serde::aws_api_gateway_v2::serialize_query_string_parameters")] pub query_string_parameters: QueryMap, #[serde(deserialize_with = "deserialize_lambda_map")] #[serde(default)] @@ -832,6 +834,21 @@ mod test { assert_eq!(parsed, reparsed); } + #[test] + #[cfg(feature = "apigw")] + fn example_apigw_request_multi_value_parameters() { + let data = include_bytes!("../../fixtures/example-apigw-request-multi-value-parameters.json"); + let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap(); + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: ApiGatewayProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!(parsed, reparsed); + + assert!(output.contains(r#""multiValueQueryStringParameters":{"name":["me","me2"]}"#)); + assert!(output.contains(r#""queryStringParameters":{"name":"me"}"#)); + assert!(output.contains(r#""headername":["headerValue","headerValue2"]"#)); + assert!(output.contains(r#""headername":"headerValue2""#)); + } + #[test] #[cfg(feature = "apigw")] fn example_apigw_restapi_openapi_request() { @@ -872,6 +889,19 @@ mod test { assert_eq!(parsed, reparsed); } + #[test] + #[cfg(feature = "apigw")] + fn example_apigw_v2_request_multi_value_parameters() { + let data = include_bytes!("../../fixtures/example-apigw-v2-request-multi-value-parameters.json"); + let parsed: ApiGatewayV2httpRequest = serde_json::from_slice(data).unwrap(); + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: ApiGatewayV2httpRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!(parsed, reparsed); + + assert!(output.contains(r#""header2":"value1,value2""#)); + assert!(output.contains(r#""queryStringParameters":{"Parameter1":"value1,value2"}"#)); + } + #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_request_no_authorizer() { diff --git a/lambda-events/src/fixtures/example-apigw-request-multi-value-parameters.json b/lambda-events/src/fixtures/example-apigw-request-multi-value-parameters.json new file mode 100644 index 00000000..340b1df1 --- /dev/null +++ b/lambda-events/src/fixtures/example-apigw-request-multi-value-parameters.json @@ -0,0 +1,136 @@ +{ + "resource": "/{proxy+}", + "path": "/hello/world", + "httpMethod": "POST", + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "cache-control": "no-cache", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Content-Type": "application/json", + "headerName": "headerValue2", + "Host": "gy415nuibc.execute-api.us-east-1.amazonaws.com", + "Postman-Token": "9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f", + "User-Agent": "PostmanRuntime/2.4.5", + "Via": "1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A==", + "X-Forwarded-For": "54.240.196.186, 54.182.214.83", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "cache-control": [ + "no-cache" + ], + "CloudFront-Forwarded-Proto": [ + "https" + ], + "CloudFront-Is-Desktop-Viewer": [ + "true" + ], + "CloudFront-Is-Mobile-Viewer": [ + "false" + ], + "CloudFront-Is-SmartTV-Viewer": [ + "false" + ], + "CloudFront-Is-Tablet-Viewer": [ + "false" + ], + "CloudFront-Viewer-Country": [ + "US" + ], + "Content-Type": [ + "application/json" + ], + "headerName": [ + "headerValue", + "headerValue2" + ], + "Host": [ + "gy415nuibc.execute-api.us-east-1.amazonaws.com" + ], + "Postman-Token": [ + "9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f" + ], + "User-Agent": [ + "PostmanRuntime/2.4.5" + ], + "Via": [ + "1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)" + ], + "X-Amz-Cf-Id": [ + "pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A==" + ], + "X-Forwarded-For": [ + "54.240.196.186, 54.182.214.83" + ], + "X-Forwarded-Port": [ + "443" + ], + "X-Forwarded-Proto": [ + "https" + ] + }, + "queryStringParameters": { + "name": "me" + }, + "multiValueQueryStringParameters": { + "name": [ + "me", "me2" + ] + }, + "pathParameters": { + "proxy": "hello/world" + }, + "stageVariables": { + "stageVariableName": "stageVariableValue" + }, + "requestContext": { + "accountId": "12345678912", + "resourceId": "roq9wj", + "path": "/hello/world", + "stage": "testStage", + "domainName": "gy415nuibc.execute-api.us-east-2.amazonaws.com", + "domainPrefix": "y0ne18dixk", + "requestId": "deef4878-7910-11e6-8f14-25afc3e9ae33", + "protocol": "HTTP/1.1", + "identity": { + "cognitoIdentityPoolId": "theCognitoIdentityPoolId", + "accountId": "theAccountId", + "cognitoIdentityId": "theCognitoIdentityId", + "caller": "theCaller", + "apiKey": "theApiKey", + "apiKeyId": "theApiKeyId", + "accessKey": "ANEXAMPLEOFACCESSKEY", + "sourceIp": "192.168.196.186", + "cognitoAuthenticationType": "theCognitoAuthenticationType", + "cognitoAuthenticationProvider": "theCognitoAuthenticationProvider", + "userArn": "theUserArn", + "userAgent": "PostmanRuntime/2.4.5", + "user": "theUser" + }, + "authorizer": { + "principalId": "admin", + "clientId": 1, + "clientName": "Exata" + }, + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "requestTime": "15/May/2020:06:01:09 +0000", + "requestTimeEpoch": 1589522469693, + "apiId": "gy415nuibc" + }, + "body": "{\r\n\t\"a\": 1\r\n}" +} \ No newline at end of file diff --git a/lambda-events/src/fixtures/example-apigw-v2-request-multi-value-parameters.json b/lambda-events/src/fixtures/example-apigw-v2-request-multi-value-parameters.json new file mode 100644 index 00000000..5094d6c2 --- /dev/null +++ b/lambda-events/src/fixtures/example-apigw-v2-request-multi-value-parameters.json @@ -0,0 +1,61 @@ +{ + "version": "2.0", + "routeKey": "$default", + "rawPath": "/my/path", + "rawQueryString": "Parameter1=value1&Parameter1=value2", + "cookies": [ + "cookie1", + "cookie2" + ], + "headers": { + "Header2": "value1,value2" + }, + "queryStringParameters": { + "Parameter1": "value1,value2" + }, + "pathParameters": { + "proxy": "hello/world" + }, + "requestContext": { + "routeKey": "$default", + "accountId": "123456789012", + "stage": "$default", + "requestId": "id", + "authorizer": { + "lambda": { + "key": "value" + } + }, + "apiId": "api-id", + "authentication": { + "clientCert": { + "clientCertPem": "-----BEGIN CERTIFICATE-----\nMIIEZTCCAk0CAQEwDQ...", + "issuerDN": "C=US,ST=Washington,L=Seattle,O=Amazon Web Services,OU=Security,CN=My Private CA", + "serialNumber": "1", + "subjectDN": "C=US,ST=Washington,L=Seattle,O=Amazon Web Services,OU=Security,CN=My Client", + "validity": { + "notAfter": "Aug 5 00:28:21 2120 GMT", + "notBefore": "Aug 29 00:28:21 2020 GMT" + } + } + }, + "domainName": "id.execute-api.us-east-1.amazonaws.com", + "domainPrefix": "id", + "time": "12/Mar/2020:19:03:58+0000", + "timeEpoch": 1583348638390, + "http": { + "method": "GET", + "path": "/my/path", + "protocol": "HTTP/1.1", + "sourceIp": "IP", + "userAgent": "agent" + } + }, + "stageVariables": { + "stageVariable1": "value1", + "stageVariable2": "value2" + }, + "body": "{\r\n\t\"a\": 1\r\n}", + "isBase64Encoded": false +} +