Skip to content

Commit e2d51ad

Browse files
chris-leachChris Leach
and
Chris Leach
authored
Add event definitions for CloudFormation custom resources (#695)
* Add event definitions for CloudFormation custom resources * Remove let else statements * Fix codebuild_time for chrono ^0.4.29 by parsing via NaiveDateTime --------- Co-authored-by: Chris Leach <[email protected]>
1 parent d1d5823 commit e2d51ad

8 files changed

+212
-8
lines changed

lambda-events/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ default = [
4242
"autoscaling",
4343
"chime_bot",
4444
"clientvpn",
45+
"cloudformation",
4546
"cloudwatch_events",
4647
"cloudwatch_logs",
4748
"code_commit",
@@ -81,6 +82,7 @@ appsync = []
8182
autoscaling = ["chrono"]
8283
chime_bot = ["chrono"]
8384
clientvpn = []
85+
cloudformation = []
8486
cloudwatch_events = ["chrono"]
8587
cloudwatch_logs = ["flate2"]
8688
code_commit = ["chrono"]

lambda-events/src/custom_serde/codebuild_time.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use chrono::{DateTime, TimeZone, Utc};
1+
use chrono::{DateTime, NaiveDateTime, Utc};
22
use serde::ser::Serializer;
33
use serde::{
44
de::{Deserializer, Error as DeError, Visitor},
@@ -18,7 +18,8 @@ impl<'de> Visitor<'de> for TimeVisitor {
1818
}
1919

2020
fn visit_str<E: DeError>(self, val: &str) -> Result<Self::Value, E> {
21-
Utc.datetime_from_str(val, CODEBUILD_TIME_FORMAT)
21+
NaiveDateTime::parse_from_str(val, CODEBUILD_TIME_FORMAT)
22+
.map(|naive| naive.and_utc())
2223
.map_err(|e| DeError::custom(format!("Parse error {} for {}", e, val)))
2324
}
2425
}
@@ -81,9 +82,9 @@ mod tests {
8182
"date": "Sep 1, 2017 4:12:29 PM"
8283
});
8384

84-
let expected = Utc
85-
.datetime_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT)
86-
.unwrap();
85+
let expected = NaiveDateTime::parse_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT)
86+
.unwrap()
87+
.and_utc();
8788
let decoded: Test = serde_json::from_value(data).unwrap();
8889
assert_eq!(expected, decoded.date);
8990
}
@@ -99,9 +100,9 @@ mod tests {
99100
"date": "Sep 1, 2017 4:12:29 PM"
100101
});
101102

102-
let expected = Utc
103-
.datetime_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT)
104-
.unwrap();
103+
let expected = NaiveDateTime::parse_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT)
104+
.unwrap()
105+
.and_utc();
105106
let decoded: Test = serde_json::from_value(data).unwrap();
106107
assert_eq!(Some(expected), decoded.date);
107108
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use serde::{de::DeserializeOwned, Deserialize, Serialize};
2+
use serde_json::Value;
3+
4+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
5+
#[serde(tag = "RequestType")]
6+
pub enum CloudFormationCustomResourceRequest<P1 = Value, P2 = Value>
7+
where
8+
P1: DeserializeOwned + Serialize,
9+
P2: DeserializeOwned + Serialize,
10+
{
11+
#[serde(bound = "")]
12+
Create(CreateRequest<P2>),
13+
#[serde(bound = "")]
14+
Update(UpdateRequest<P1, P2>),
15+
#[serde(bound = "")]
16+
Delete(DeleteRequest<P2>),
17+
}
18+
19+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
20+
#[serde(rename_all = "PascalCase")]
21+
#[serde(deny_unknown_fields)]
22+
pub struct CreateRequest<P2 = Value>
23+
where
24+
P2: DeserializeOwned + Serialize,
25+
{
26+
#[serde(default)]
27+
pub service_token: Option<String>,
28+
pub request_id: String,
29+
#[serde(rename = "ResponseURL")]
30+
pub response_url: String,
31+
pub stack_id: String,
32+
pub resource_type: String,
33+
pub logical_resource_id: String,
34+
#[serde(bound = "")]
35+
pub resource_properties: P2,
36+
}
37+
38+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
39+
#[serde(rename_all = "PascalCase")]
40+
#[serde(deny_unknown_fields)]
41+
pub struct UpdateRequest<P1 = Value, P2 = Value>
42+
where
43+
P1: DeserializeOwned + Serialize,
44+
P2: DeserializeOwned + Serialize,
45+
{
46+
#[serde(default)]
47+
pub service_token: Option<String>,
48+
pub request_id: String,
49+
#[serde(rename = "ResponseURL")]
50+
pub response_url: String,
51+
pub stack_id: String,
52+
pub resource_type: String,
53+
pub logical_resource_id: String,
54+
pub physical_resource_id: String,
55+
#[serde(bound = "")]
56+
pub resource_properties: P2,
57+
#[serde(bound = "")]
58+
pub old_resource_properties: P1,
59+
}
60+
61+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
62+
#[serde(rename_all = "PascalCase")]
63+
#[serde(deny_unknown_fields)]
64+
pub struct DeleteRequest<P2 = Value>
65+
where
66+
P2: DeserializeOwned + Serialize,
67+
{
68+
#[serde(default)]
69+
pub service_token: Option<String>,
70+
pub request_id: String,
71+
#[serde(rename = "ResponseURL")]
72+
pub response_url: String,
73+
pub stack_id: String,
74+
pub resource_type: String,
75+
pub logical_resource_id: String,
76+
pub physical_resource_id: String,
77+
#[serde(bound = "")]
78+
pub resource_properties: P2,
79+
}
80+
81+
#[cfg(test)]
82+
mod test {
83+
use std::collections::HashMap;
84+
85+
use super::CloudFormationCustomResourceRequest::*;
86+
use super::*;
87+
88+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
89+
#[serde(rename_all = "PascalCase")]
90+
#[serde(deny_unknown_fields)]
91+
struct TestProperties {
92+
key_1: String,
93+
key_2: Vec<String>,
94+
key_3: HashMap<String, String>,
95+
}
96+
97+
type TestRequest = CloudFormationCustomResourceRequest<TestProperties, TestProperties>;
98+
99+
#[test]
100+
fn example_cloudformation_custom_resource_create_request() {
101+
let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-create-request.json");
102+
let parsed: TestRequest = serde_json::from_slice(data).unwrap();
103+
104+
match parsed {
105+
Create(_) => (),
106+
_ => panic!("expected Create request"),
107+
}
108+
109+
let output: String = serde_json::to_string(&parsed).unwrap();
110+
let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap();
111+
assert_eq!(parsed, reparsed);
112+
}
113+
114+
#[test]
115+
fn example_cloudformation_custom_resource_update_request() {
116+
let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-update-request.json");
117+
let parsed: TestRequest = serde_json::from_slice(data).unwrap();
118+
119+
match parsed {
120+
Update(_) => (),
121+
_ => panic!("expected Update request"),
122+
}
123+
124+
let output: String = serde_json::to_string(&parsed).unwrap();
125+
let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap();
126+
assert_eq!(parsed, reparsed);
127+
}
128+
129+
#[test]
130+
fn example_cloudformation_custom_resource_delete_request() {
131+
let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-delete-request.json");
132+
let parsed: TestRequest = serde_json::from_slice(data).unwrap();
133+
134+
match parsed {
135+
Delete(_) => (),
136+
_ => panic!("expected Delete request"),
137+
}
138+
139+
let output: String = serde_json::to_string(&parsed).unwrap();
140+
let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap();
141+
assert_eq!(parsed, reparsed);
142+
}
143+
}

lambda-events/src/event/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ pub mod chime_bot;
2525
#[cfg(feature = "clientvpn")]
2626
pub mod clientvpn;
2727

28+
/// AWS Lambda event definitions for cloudformation.
29+
#[cfg(feature = "cloudformation")]
30+
pub mod cloudformation;
31+
2832
/// CloudWatch Events payload
2933
#[cfg(feature = "cloudwatch_events")]
3034
pub mod cloudwatch_events;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"ServiceToken": "arn:aws:lambda:eu-west-2:123456789012:function:custom-resource-handler",
3+
"RequestType" : "Create",
4+
"RequestId" : "82304eb2-bdda-469f-a33b-a3f1406d0a52",
5+
"ResponseURL": "https://cr-response-bucket.s3.us-east-1.amazonaws.com/cr-response-key?sig-params=sig-values",
6+
"StackId" : "arn:aws:cloudformation:us-east-1:123456789012:stack/stack-name/16580499-7622-4a9c-b32f-4eba35da93da",
7+
"ResourceType" : "Custom::MyCustomResourceType",
8+
"LogicalResourceId" : "CustomResource",
9+
"ResourceProperties" : {
10+
"Key1" : "string",
11+
"Key2" : [ "list" ],
12+
"Key3" : { "Key4" : "map" }
13+
}
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"ServiceToken": "arn:aws:lambda:eu-west-2:123456789012:function:custom-resource-handler",
3+
"RequestType" : "Delete",
4+
"RequestId" : "ef70561d-d4ba-42a4-801b-33ad88dafc37",
5+
"ResponseURL": "https://cr-response-bucket.s3.us-east-1.amazonaws.com/cr-response-key?sig-params=sig-values",
6+
"StackId" : "arn:aws:cloudformation:us-east-1:123456789012:stack/stack-name/16580499-7622-4a9c-b32f-4eba35da93da",
7+
"ResourceType" : "Custom::MyCustomResourceType",
8+
"LogicalResourceId" : "CustomResource",
9+
"PhysicalResourceId" : "custom-resource-f4bd5382-3de3-4caf-b7ad-1be06b899647",
10+
"ResourceProperties" : {
11+
"Key1" : "string",
12+
"Key2" : [ "list" ],
13+
"Key3" : { "Key4" : "map" }
14+
}
15+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"ServiceToken": "arn:aws:lambda:eu-west-2:123456789012:function:custom-resource-handler",
3+
"RequestType" : "Update",
4+
"RequestId" : "49347ca5-c603-44e5-a34b-10cf1854a887",
5+
"ResponseURL": "https://cr-response-bucket.s3.us-east-1.amazonaws.com/cr-response-key?sig-params=sig-values",
6+
"StackId" : "arn:aws:cloudformation:us-east-1:123456789012:stack/stack-name/16580499-7622-4a9c-b32f-4eba35da93da",
7+
"ResourceType" : "Custom::MyCustomResourceType",
8+
"LogicalResourceId" : "CustomResource",
9+
"PhysicalResourceId" : "custom-resource-f4bd5382-3de3-4caf-b7ad-1be06b899647",
10+
"ResourceProperties" : {
11+
"Key1" : "new-string",
12+
"Key2" : [ "new-list" ],
13+
"Key3" : { "Key4" : "new-map" }
14+
},
15+
"OldResourceProperties" : {
16+
"Key1" : "string",
17+
"Key2" : [ "list" ],
18+
"Key3" : { "Key4" : "map" }
19+
}
20+
}

lambda-events/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub use event::activemq;
2020
/// AWS Lambda event definitions for alb.
2121
#[cfg(feature = "alb")]
2222
pub use event::alb;
23+
2324
/// AWS Lambda event definitions for apigw.
2425
#[cfg(feature = "apigw")]
2526
pub use event::apigw;
@@ -40,6 +41,10 @@ pub use event::chime_bot;
4041
#[cfg(feature = "clientvpn")]
4142
pub use event::clientvpn;
4243

44+
/// AWS Lambda event definitions for cloudformation
45+
#[cfg(feature = "cloudformation")]
46+
pub use event::cloudformation;
47+
4348
/// CloudWatch Events payload
4449
#[cfg(feature = "cloudwatch_events")]
4550
pub use event::cloudwatch_events;

0 commit comments

Comments
 (0)