diff --git a/lambda-events/src/event/cloudformation/mod.rs b/lambda-events/src/event/cloudformation/mod.rs index 3123d8af..8dbf8b5e 100644 --- a/lambda-events/src/event/cloudformation/mod.rs +++ b/lambda-events/src/event/cloudformation/mod.rs @@ -2,6 +2,8 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; +pub mod provider; + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(tag = "RequestType")] pub enum CloudFormationCustomResourceRequest diff --git a/lambda-events/src/event/cloudformation/provider.rs b/lambda-events/src/event/cloudformation/provider.rs new file mode 100644 index 00000000..e3ba5bea --- /dev/null +++ b/lambda-events/src/event/cloudformation/provider.rs @@ -0,0 +1,159 @@ +//! These events are to be used with the CDK custom resource provider framework. +//! +//! Note that they are similar (but not the same) as the events in the `super` module. +//! +//! See https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.custom_resources-readme.html for details. + +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Value; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(tag = "RequestType")] +pub enum CloudFormationCustomResourceRequest +where + P1: DeserializeOwned + Serialize, + P2: DeserializeOwned + Serialize, +{ + #[serde(bound = "")] + Create(CreateRequest), + #[serde(bound = "")] + Update(UpdateRequest), + #[serde(bound = "")] + Delete(DeleteRequest), +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct CreateRequest +where + P2: DeserializeOwned + Serialize, +{ + #[serde(flatten, bound = "")] + pub common: CommonRequestParams, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct UpdateRequest +where + P1: DeserializeOwned + Serialize, + P2: DeserializeOwned + Serialize, +{ + pub physical_resource_id: String, + + #[serde(bound = "")] + pub old_resource_properties: P1, + + #[serde(flatten, bound = "")] + pub common: CommonRequestParams, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct DeleteRequest +where + P2: DeserializeOwned + Serialize, +{ + pub physical_resource_id: String, + + #[serde(flatten, bound = "")] + pub common: CommonRequestParams, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct CommonRequestParams +where + P2: DeserializeOwned + Serialize, +{ + pub logical_resource_id: String, + #[serde(bound = "")] + pub resource_properties: P2, + pub resource_type: String, + pub request_id: String, + pub stack_id: String, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, Default)] +#[serde(rename_all = "PascalCase")] +pub struct CloudFormationCustomResourceResponse +where + D: DeserializeOwned + Serialize, +{ + pub physical_resource_id: Option, + #[serde(bound = "")] + pub data: D, + pub no_echo: bool, +} + +#[cfg(test)] +mod test { + use std::collections::HashMap; + + use super::CloudFormationCustomResourceRequest::*; + use super::*; + + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] + #[serde(rename_all = "PascalCase")] + struct TestProperties { + key_1: String, + key_2: Vec, + key_3: HashMap, + } + + type TestRequest = CloudFormationCustomResourceRequest; + + #[test] + fn example_create_request() { + let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-create-request.json"); + let parsed: TestRequest = serde_json::from_slice(data).unwrap(); + + match parsed { + Create(_) => (), + _ => panic!("expected Create request"), + } + + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!(parsed, reparsed); + } + + #[test] + fn example_update_request() { + let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-update-request.json"); + let parsed: TestRequest = serde_json::from_slice(data).unwrap(); + + match parsed { + Update(_) => (), + _ => panic!("expected Update request"), + } + + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!(parsed, reparsed); + } + + #[test] + fn example_delete_request() { + let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-delete-request.json"); + let parsed: TestRequest = serde_json::from_slice(data).unwrap(); + + match parsed { + Delete(_) => (), + _ => panic!("expected Delete request"), + } + + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!(parsed, reparsed); + } + + #[test] + fn example_response() { + let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-response.json"); + let parsed: CloudFormationCustomResourceResponse = serde_json::from_slice(data).unwrap(); + let output: String = serde_json::to_string(&parsed).unwrap(); + let reparsed: CloudFormationCustomResourceResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + assert_eq!(parsed, reparsed); + } +} diff --git a/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-create-request.json b/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-create-request.json new file mode 100644 index 00000000..074a3712 --- /dev/null +++ b/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-create-request.json @@ -0,0 +1,12 @@ +{ + "RequestType" : "Create", + "RequestId" : "82304eb2-bdda-469f-a33b-a3f1406d0a52", + "StackId" : "arn:aws:cloudformation:us-east-1:123456789012:stack/stack-name/16580499-7622-4a9c-b32f-4eba35da93da", + "ResourceType" : "Custom::MyCustomResourceType", + "LogicalResourceId" : "CustomResource", + "ResourceProperties" : { + "Key1" : "string", + "Key2" : [ "list" ], + "Key3" : { "Key4" : "map" } + } +} diff --git a/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-delete-request.json b/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-delete-request.json new file mode 100644 index 00000000..3fb58391 --- /dev/null +++ b/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-delete-request.json @@ -0,0 +1,13 @@ +{ + "RequestType" : "Delete", + "RequestId" : "ef70561d-d4ba-42a4-801b-33ad88dafc37", + "StackId" : "arn:aws:cloudformation:us-east-1:123456789012:stack/stack-name/16580499-7622-4a9c-b32f-4eba35da93da", + "ResourceType" : "Custom::MyCustomResourceType", + "LogicalResourceId" : "CustomResource", + "PhysicalResourceId" : "custom-resource-f4bd5382-3de3-4caf-b7ad-1be06b899647", + "ResourceProperties" : { + "Key1" : "string", + "Key2" : [ "list" ], + "Key3" : { "Key4" : "map" } + } +} diff --git a/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-response.json b/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-response.json new file mode 100644 index 00000000..cff06191 --- /dev/null +++ b/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-response.json @@ -0,0 +1,9 @@ +{ + "PhysicalResourceId": "custom-resource-f4bd5382-3de3-4caf-b7ad-1be06b899647", + "NoEcho": false, + "Data": { + "Key1": "a", + "Key2": "b", + "Key3": "c" + } +} \ No newline at end of file diff --git a/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-update-request.json b/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-update-request.json new file mode 100644 index 00000000..90280f72 --- /dev/null +++ b/lambda-events/src/fixtures/example-cloudformation-custom-resource-provider-update-request.json @@ -0,0 +1,18 @@ +{ + "RequestType" : "Update", + "RequestId" : "49347ca5-c603-44e5-a34b-10cf1854a887", + "StackId" : "arn:aws:cloudformation:us-east-1:123456789012:stack/stack-name/16580499-7622-4a9c-b32f-4eba35da93da", + "ResourceType" : "Custom::MyCustomResourceType", + "LogicalResourceId" : "CustomResource", + "PhysicalResourceId" : "custom-resource-f4bd5382-3de3-4caf-b7ad-1be06b899647", + "ResourceProperties" : { + "Key1" : "new-string", + "Key2" : [ "new-list" ], + "Key3" : { "Key4" : "new-map" } + }, + "OldResourceProperties" : { + "Key1" : "string", + "Key2" : [ "list" ], + "Key3" : { "Key4" : "map" } + } +}