Skip to content

Commit 6c3272f

Browse files
authored
Serialize APIGW queryStringParameters properly (#676)
Reference: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html 'queryStringParameters' were serialized incorrectly to format like queryStringParameters:{"key":["value"]} when it should be like queryStringParameters:{"key":"value"}
1 parent 33cce77 commit 6c3272f

File tree

4 files changed

+228
-1
lines changed

4 files changed

+228
-1
lines changed

lambda-events/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ chrono = { version = "0.4.23", default-features = false, features = [
3030
"serde",
3131
"std",
3232
], optional = true }
33-
query_map = { version = "^0.6", features = ["serde", "url-query"], optional = true }
33+
query_map = { version = "^0.7", features = ["serde", "url-query"], optional = true }
3434
flate2 = { version = "1.0.24", optional = true }
3535

3636
[features]

lambda-events/src/event/apigw/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ where
3434
#[serde(serialize_with = "serialize_multi_value_headers")]
3535
pub multi_value_headers: HeaderMap,
3636
#[serde(default, deserialize_with = "query_map::serde::standard::deserialize_empty")]
37+
#[serde(serialize_with = "query_map::serde::aws_api_gateway_v1::serialize_query_string_parameters")]
3738
pub query_string_parameters: QueryMap,
3839
#[serde(default, deserialize_with = "query_map::serde::standard::deserialize_empty")]
3940
pub multi_value_query_string_parameters: QueryMap,
@@ -151,6 +152,7 @@ pub struct ApiGatewayV2httpRequest {
151152
deserialize_with = "query_map::serde::aws_api_gateway_v2::deserialize_empty"
152153
)]
153154
#[serde(skip_serializing_if = "QueryMap::is_empty")]
155+
#[serde(serialize_with = "query_map::serde::aws_api_gateway_v2::serialize_query_string_parameters")]
154156
pub query_string_parameters: QueryMap,
155157
#[serde(deserialize_with = "deserialize_lambda_map")]
156158
#[serde(default)]
@@ -832,6 +834,21 @@ mod test {
832834
assert_eq!(parsed, reparsed);
833835
}
834836

837+
#[test]
838+
#[cfg(feature = "apigw")]
839+
fn example_apigw_request_multi_value_parameters() {
840+
let data = include_bytes!("../../fixtures/example-apigw-request-multi-value-parameters.json");
841+
let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap();
842+
let output: String = serde_json::to_string(&parsed).unwrap();
843+
let reparsed: ApiGatewayProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap();
844+
assert_eq!(parsed, reparsed);
845+
846+
assert!(output.contains(r#""multiValueQueryStringParameters":{"name":["me","me2"]}"#));
847+
assert!(output.contains(r#""queryStringParameters":{"name":"me"}"#));
848+
assert!(output.contains(r#""headername":["headerValue","headerValue2"]"#));
849+
assert!(output.contains(r#""headername":"headerValue2""#));
850+
}
851+
835852
#[test]
836853
#[cfg(feature = "apigw")]
837854
fn example_apigw_restapi_openapi_request() {
@@ -872,6 +889,19 @@ mod test {
872889
assert_eq!(parsed, reparsed);
873890
}
874891

892+
#[test]
893+
#[cfg(feature = "apigw")]
894+
fn example_apigw_v2_request_multi_value_parameters() {
895+
let data = include_bytes!("../../fixtures/example-apigw-v2-request-multi-value-parameters.json");
896+
let parsed: ApiGatewayV2httpRequest = serde_json::from_slice(data).unwrap();
897+
let output: String = serde_json::to_string(&parsed).unwrap();
898+
let reparsed: ApiGatewayV2httpRequest = serde_json::from_slice(output.as_bytes()).unwrap();
899+
assert_eq!(parsed, reparsed);
900+
901+
assert!(output.contains(r#""header2":"value1,value2""#));
902+
assert!(output.contains(r#""queryStringParameters":{"Parameter1":"value1,value2"}"#));
903+
}
904+
875905
#[test]
876906
#[cfg(feature = "apigw")]
877907
fn example_apigw_v2_request_no_authorizer() {
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
{
2+
"resource": "/{proxy+}",
3+
"path": "/hello/world",
4+
"httpMethod": "POST",
5+
"headers": {
6+
"Accept": "*/*",
7+
"Accept-Encoding": "gzip, deflate",
8+
"cache-control": "no-cache",
9+
"CloudFront-Forwarded-Proto": "https",
10+
"CloudFront-Is-Desktop-Viewer": "true",
11+
"CloudFront-Is-Mobile-Viewer": "false",
12+
"CloudFront-Is-SmartTV-Viewer": "false",
13+
"CloudFront-Is-Tablet-Viewer": "false",
14+
"CloudFront-Viewer-Country": "US",
15+
"Content-Type": "application/json",
16+
"headerName": "headerValue2",
17+
"Host": "gy415nuibc.execute-api.us-east-1.amazonaws.com",
18+
"Postman-Token": "9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f",
19+
"User-Agent": "PostmanRuntime/2.4.5",
20+
"Via": "1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)",
21+
"X-Amz-Cf-Id": "pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A==",
22+
"X-Forwarded-For": "54.240.196.186, 54.182.214.83",
23+
"X-Forwarded-Port": "443",
24+
"X-Forwarded-Proto": "https"
25+
},
26+
"multiValueHeaders": {
27+
"Accept": [
28+
"*/*"
29+
],
30+
"Accept-Encoding": [
31+
"gzip, deflate"
32+
],
33+
"cache-control": [
34+
"no-cache"
35+
],
36+
"CloudFront-Forwarded-Proto": [
37+
"https"
38+
],
39+
"CloudFront-Is-Desktop-Viewer": [
40+
"true"
41+
],
42+
"CloudFront-Is-Mobile-Viewer": [
43+
"false"
44+
],
45+
"CloudFront-Is-SmartTV-Viewer": [
46+
"false"
47+
],
48+
"CloudFront-Is-Tablet-Viewer": [
49+
"false"
50+
],
51+
"CloudFront-Viewer-Country": [
52+
"US"
53+
],
54+
"Content-Type": [
55+
"application/json"
56+
],
57+
"headerName": [
58+
"headerValue",
59+
"headerValue2"
60+
],
61+
"Host": [
62+
"gy415nuibc.execute-api.us-east-1.amazonaws.com"
63+
],
64+
"Postman-Token": [
65+
"9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f"
66+
],
67+
"User-Agent": [
68+
"PostmanRuntime/2.4.5"
69+
],
70+
"Via": [
71+
"1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)"
72+
],
73+
"X-Amz-Cf-Id": [
74+
"pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A=="
75+
],
76+
"X-Forwarded-For": [
77+
"54.240.196.186, 54.182.214.83"
78+
],
79+
"X-Forwarded-Port": [
80+
"443"
81+
],
82+
"X-Forwarded-Proto": [
83+
"https"
84+
]
85+
},
86+
"queryStringParameters": {
87+
"name": "me"
88+
},
89+
"multiValueQueryStringParameters": {
90+
"name": [
91+
"me", "me2"
92+
]
93+
},
94+
"pathParameters": {
95+
"proxy": "hello/world"
96+
},
97+
"stageVariables": {
98+
"stageVariableName": "stageVariableValue"
99+
},
100+
"requestContext": {
101+
"accountId": "12345678912",
102+
"resourceId": "roq9wj",
103+
"path": "/hello/world",
104+
"stage": "testStage",
105+
"domainName": "gy415nuibc.execute-api.us-east-2.amazonaws.com",
106+
"domainPrefix": "y0ne18dixk",
107+
"requestId": "deef4878-7910-11e6-8f14-25afc3e9ae33",
108+
"protocol": "HTTP/1.1",
109+
"identity": {
110+
"cognitoIdentityPoolId": "theCognitoIdentityPoolId",
111+
"accountId": "theAccountId",
112+
"cognitoIdentityId": "theCognitoIdentityId",
113+
"caller": "theCaller",
114+
"apiKey": "theApiKey",
115+
"apiKeyId": "theApiKeyId",
116+
"accessKey": "ANEXAMPLEOFACCESSKEY",
117+
"sourceIp": "192.168.196.186",
118+
"cognitoAuthenticationType": "theCognitoAuthenticationType",
119+
"cognitoAuthenticationProvider": "theCognitoAuthenticationProvider",
120+
"userArn": "theUserArn",
121+
"userAgent": "PostmanRuntime/2.4.5",
122+
"user": "theUser"
123+
},
124+
"authorizer": {
125+
"principalId": "admin",
126+
"clientId": 1,
127+
"clientName": "Exata"
128+
},
129+
"resourcePath": "/{proxy+}",
130+
"httpMethod": "POST",
131+
"requestTime": "15/May/2020:06:01:09 +0000",
132+
"requestTimeEpoch": 1589522469693,
133+
"apiId": "gy415nuibc"
134+
},
135+
"body": "{\r\n\t\"a\": 1\r\n}"
136+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"version": "2.0",
3+
"routeKey": "$default",
4+
"rawPath": "/my/path",
5+
"rawQueryString": "Parameter1=value1&Parameter1=value2",
6+
"cookies": [
7+
"cookie1",
8+
"cookie2"
9+
],
10+
"headers": {
11+
"Header2": "value1,value2"
12+
},
13+
"queryStringParameters": {
14+
"Parameter1": "value1,value2"
15+
},
16+
"pathParameters": {
17+
"proxy": "hello/world"
18+
},
19+
"requestContext": {
20+
"routeKey": "$default",
21+
"accountId": "123456789012",
22+
"stage": "$default",
23+
"requestId": "id",
24+
"authorizer": {
25+
"lambda": {
26+
"key": "value"
27+
}
28+
},
29+
"apiId": "api-id",
30+
"authentication": {
31+
"clientCert": {
32+
"clientCertPem": "-----BEGIN CERTIFICATE-----\nMIIEZTCCAk0CAQEwDQ...",
33+
"issuerDN": "C=US,ST=Washington,L=Seattle,O=Amazon Web Services,OU=Security,CN=My Private CA",
34+
"serialNumber": "1",
35+
"subjectDN": "C=US,ST=Washington,L=Seattle,O=Amazon Web Services,OU=Security,CN=My Client",
36+
"validity": {
37+
"notAfter": "Aug 5 00:28:21 2120 GMT",
38+
"notBefore": "Aug 29 00:28:21 2020 GMT"
39+
}
40+
}
41+
},
42+
"domainName": "id.execute-api.us-east-1.amazonaws.com",
43+
"domainPrefix": "id",
44+
"time": "12/Mar/2020:19:03:58+0000",
45+
"timeEpoch": 1583348638390,
46+
"http": {
47+
"method": "GET",
48+
"path": "/my/path",
49+
"protocol": "HTTP/1.1",
50+
"sourceIp": "IP",
51+
"userAgent": "agent"
52+
}
53+
},
54+
"stageVariables": {
55+
"stageVariable1": "value1",
56+
"stageVariable2": "value2"
57+
},
58+
"body": "{\r\n\t\"a\": 1\r\n}",
59+
"isBase64Encoded": false
60+
}
61+

0 commit comments

Comments
 (0)