Skip to content
This repository was archived by the owner on May 16, 2023. It is now read-only.

Commit 858ee85

Browse files
authored
Fix serialization issues with the AWS Console test requests. (#69)
Test requests serialize null headers and queries, instead of omitting the fields. These changes make the deserialization work by creating default structs, so we don't have to add more optionals. Signed-off-by: David Calavera <david.calavera@gmail.com>
1 parent 7da3573 commit 858ee85

File tree

8 files changed

+491
-302
lines changed

8 files changed

+491
-302
lines changed

aws_lambda_events/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ serde_derive = "^1"
2626
serde_json = "^1"
2727
bytes = { version = "1", features = ["serde"] }
2828
chrono = { version = "^0.4.4", features = ["serde"] }
29-
query_map = { version = "^0.3", features = ["serde"] }
29+
query_map = { version = "^0.4", features = ["serde"] }
3030

3131
[dev-dependencies]
3232
pretty_assertions = "0.7"

aws_lambda_events/src/apigw/mod.rs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ where
2525
pub path: Option<String>,
2626
#[serde(with = "http_method")]
2727
pub http_method: Method,
28-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
28+
#[serde(deserialize_with = "deserialize_headers", default)]
2929
#[serde(serialize_with = "serialize_headers")]
3030
pub headers: HeaderMap,
31-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
31+
#[serde(deserialize_with = "deserialize_headers", default)]
3232
#[serde(serialize_with = "serialize_multi_value_headers")]
3333
pub multi_value_headers: HeaderMap,
34-
#[serde(default)]
34+
#[serde(default, deserialize_with = "query_map::deserialize_empty")]
3535
pub query_string_parameters: QueryMap,
36-
#[serde(default)]
36+
#[serde(default, deserialize_with = "query_map::deserialize_empty")]
3737
pub multi_value_query_string_parameters: QueryMap,
3838
#[serde(deserialize_with = "deserialize_lambda_map")]
3939
#[serde(default)]
@@ -56,10 +56,10 @@ where
5656
#[serde(rename_all = "camelCase")]
5757
pub struct ApiGatewayProxyResponse {
5858
pub status_code: i64,
59-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
59+
#[serde(deserialize_with = "deserialize_headers", default)]
6060
#[serde(serialize_with = "serialize_headers")]
6161
pub headers: HeaderMap,
62-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
62+
#[serde(deserialize_with = "deserialize_headers", default)]
6363
#[serde(serialize_with = "serialize_multi_value_headers")]
6464
pub multi_value_headers: HeaderMap,
6565
#[serde(skip_serializing_if = "Option::is_none")]
@@ -138,10 +138,10 @@ pub struct ApiGatewayV2httpRequest {
138138
#[serde(default)]
139139
pub raw_query_string: Option<String>,
140140
pub cookies: Option<Vec<String>>,
141-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
141+
#[serde(deserialize_with = "deserialize_headers", default)]
142142
#[serde(serialize_with = "serialize_headers")]
143143
pub headers: HeaderMap,
144-
#[serde(default)]
144+
#[serde(default, deserialize_with = "query_map::deserialize_empty")]
145145
pub query_string_parameters: QueryMap,
146146
#[serde(deserialize_with = "deserialize_lambda_map")]
147147
#[serde(default)]
@@ -284,10 +284,10 @@ pub struct ApiGatewayV2httpRequestContextHttpDescription {
284284
#[serde(rename_all = "camelCase")]
285285
pub struct ApiGatewayV2httpResponse {
286286
pub status_code: i64,
287-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
287+
#[serde(deserialize_with = "deserialize_headers", default)]
288288
#[serde(serialize_with = "serialize_headers")]
289289
pub headers: HeaderMap,
290-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
290+
#[serde(deserialize_with = "deserialize_headers", default)]
291291
#[serde(serialize_with = "serialize_multi_value_headers")]
292292
pub multi_value_headers: HeaderMap,
293293
#[serde(skip_serializing_if = "Option::is_none")]
@@ -365,15 +365,15 @@ where
365365
#[serde(serialize_with = "http_method::serialize_optional")]
366366
#[serde(skip_serializing_if = "Option::is_none")]
367367
pub http_method: Option<Method>,
368-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
368+
#[serde(deserialize_with = "deserialize_headers", default)]
369369
#[serde(serialize_with = "serialize_headers")]
370370
pub headers: HeaderMap,
371-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
371+
#[serde(deserialize_with = "deserialize_headers", default)]
372372
#[serde(serialize_with = "serialize_multi_value_headers")]
373373
pub multi_value_headers: HeaderMap,
374-
#[serde(default)]
374+
#[serde(default, deserialize_with = "query_map::deserialize_empty")]
375375
pub query_string_parameters: QueryMap,
376-
#[serde(default)]
376+
#[serde(default, deserialize_with = "query_map::deserialize_empty")]
377377
pub multi_value_query_string_parameters: QueryMap,
378378
#[serde(deserialize_with = "deserialize_lambda_map")]
379379
#[serde(default)]
@@ -630,15 +630,15 @@ pub struct ApiGatewayCustomAuthorizerRequestTypeRequest {
630630
pub path: Option<String>,
631631
#[serde(with = "http_method")]
632632
pub http_method: Method,
633-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
633+
#[serde(deserialize_with = "deserialize_headers", default)]
634634
#[serde(serialize_with = "serialize_headers")]
635635
pub headers: HeaderMap,
636-
#[serde(deserialize_with = "http_serde::header_map::deserialize", default)]
636+
#[serde(deserialize_with = "deserialize_headers", default)]
637637
#[serde(serialize_with = "serialize_multi_value_headers")]
638638
pub multi_value_headers: HeaderMap,
639-
#[serde(default)]
639+
#[serde(default, deserialize_with = "query_map::deserialize_empty")]
640640
pub query_string_parameters: QueryMap,
641-
#[serde(default)]
641+
#[serde(default, deserialize_with = "query_map::deserialize_empty")]
642642
pub multi_value_query_string_parameters: QueryMap,
643643
#[serde(deserialize_with = "deserialize_lambda_map")]
644644
#[serde(default)]
@@ -831,4 +831,14 @@ mod test {
831831
serde_json::from_slice(output.as_bytes()).unwrap();
832832
assert_eq!(parsed, reparsed);
833833
}
834+
835+
#[test]
836+
#[cfg(feature = "apigw")]
837+
fn example_apigw_console_test_request() {
838+
let data = include_bytes!("../generated/fixtures/example-apigw-console-test-request.json");
839+
let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap();
840+
let output: String = serde_json::to_string(&parsed).unwrap();
841+
let reparsed: ApiGatewayProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap();
842+
assert_eq!(parsed, reparsed);
843+
}
834844
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use chrono::{DateTime, TimeZone, Utc};
2+
use serde::de::{Deserialize, Deserializer, Error as DeError, Visitor};
3+
use serde::ser::Serializer;
4+
use std::fmt;
5+
6+
// Jan 2, 2006 3:04:05 PM
7+
const CODEBUILD_TIME_FORMAT: &str = "%b %e, %Y %l:%M:%S %p";
8+
9+
struct TimeVisitor;
10+
impl<'de> Visitor<'de> for TimeVisitor {
11+
type Value = DateTime<Utc>;
12+
13+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
14+
write!(formatter, "valid codebuild time: {}", CODEBUILD_TIME_FORMAT)
15+
}
16+
17+
fn visit_str<E: DeError>(self, val: &str) -> Result<Self::Value, E> {
18+
Utc.datetime_from_str(val, CODEBUILD_TIME_FORMAT)
19+
.map_err(|e| DeError::custom(format!("Parse error {} for {}", e, val)))
20+
}
21+
}
22+
23+
pub(crate) mod str_time {
24+
use super::*;
25+
26+
pub(crate) fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
27+
where
28+
D: Deserializer<'de>,
29+
{
30+
d.deserialize_str(TimeVisitor)
31+
}
32+
33+
pub fn serialize<S: Serializer>(date: &DateTime<Utc>, ser: S) -> Result<S::Ok, S::Error> {
34+
let s = format!("{}", date.format(CODEBUILD_TIME_FORMAT));
35+
ser.serialize_str(&s)
36+
}
37+
}
38+
39+
pub(crate) mod optional_time {
40+
use super::*;
41+
42+
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
43+
where
44+
D: Deserializer<'de>,
45+
{
46+
let s: Option<String> = Option::deserialize(deserializer)?;
47+
if let Some(val) = s {
48+
let visitor = TimeVisitor {};
49+
return visitor.visit_str(&val).map(Some);
50+
}
51+
52+
Ok(None)
53+
}
54+
55+
pub fn serialize<S: Serializer>(
56+
date: &Option<DateTime<Utc>>,
57+
ser: S,
58+
) -> Result<S::Ok, S::Error> {
59+
if let Some(date) = date {
60+
return str_time::serialize(date, ser);
61+
}
62+
63+
ser.serialize_none()
64+
}
65+
}
66+
67+
#[cfg(test)]
68+
mod tests {
69+
use super::*;
70+
71+
type TestTime = DateTime<Utc>;
72+
73+
#[test]
74+
fn test_deserialize_codebuild_time() {
75+
#[derive(Deserialize)]
76+
struct Test {
77+
#[serde(with = "str_time")]
78+
pub date: TestTime,
79+
}
80+
let data = json!({
81+
"date": "Sep 1, 2017 4:12:29 PM"
82+
});
83+
84+
let expected = Utc
85+
.datetime_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT)
86+
.unwrap();
87+
let decoded: Test = serde_json::from_value(data).unwrap();
88+
assert_eq!(expected, decoded.date);
89+
}
90+
91+
#[test]
92+
fn test_deserialize_codebuild_optional_time() {
93+
#[derive(Deserialize)]
94+
struct Test {
95+
#[serde(with = "optional_time")]
96+
pub date: Option<TestTime>,
97+
}
98+
let data = json!({
99+
"date": "Sep 1, 2017 4:12:29 PM"
100+
});
101+
102+
let expected = Utc
103+
.datetime_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT)
104+
.unwrap();
105+
let decoded: Test = serde_json::from_value(data).unwrap();
106+
assert_eq!(Some(expected), decoded.date);
107+
}
108+
}

0 commit comments

Comments
 (0)