diff --git a/lambda-events/Cargo.toml b/lambda-events/Cargo.toml index d9774104..f6ecd3a6 100644 --- a/lambda-events/Cargo.toml +++ b/lambda-events/Cargo.toml @@ -38,6 +38,7 @@ default = [ "alb", "apigw", "appsync", + "appsync_events", "autoscaling", "bedrock_agent_runtime", "chime_bot", @@ -83,6 +84,7 @@ activemq = [] alb = ["bytes", "http", "http-body", "http-serde", "query_map"] apigw = ["bytes", "http", "http-body", "http-serde", "iam", "query_map"] appsync = [] +appsync_events = ["bytes", "http", "http-body"] autoscaling = ["chrono"] bedrock_agent_runtime = [] chime_bot = ["chrono"] diff --git a/lambda-events/src/custom_serde/mod.rs b/lambda-events/src/custom_serde/mod.rs index 729dee3d..983c77cd 100644 --- a/lambda-events/src/custom_serde/mod.rs +++ b/lambda-events/src/custom_serde/mod.rs @@ -1,8 +1,11 @@ use base64::Engine; +#[cfg(feature = "appsync_events")] +use serde::{de::DeserializeOwned, ser::Error as SerError, Serialize}; use serde::{ de::{Deserialize, Deserializer, Error as DeError}, ser::Serializer, }; + use std::collections::HashMap; #[cfg(feature = "codebuild")] @@ -15,7 +18,8 @@ pub type CodeBuildNumber = f32; feature = "apigw", feature = "s3", feature = "iot", - feature = "lambda_function_urls" + feature = "lambda_function_urls", + feature = "appsync_events" ))] mod headers; #[cfg(any( @@ -23,7 +27,8 @@ mod headers; feature = "apigw", feature = "s3", feature = "iot", - feature = "lambda_function_urls" + feature = "lambda_function_urls", + feature = "appsync_events" ))] pub(crate) use self::headers::*; @@ -94,6 +99,28 @@ where Ok(opt.unwrap_or_default()) } +#[cfg(feature = "appsync_events")] +pub(crate) fn serialize_stringified_json(value: &T, serializer: S) -> Result +where + T: Serialize, + S: Serializer, +{ + let json_str = serde_json::to_string(value).map_err(S::Error::custom)?; + + json_str.serialize(serializer) +} + +#[cfg(feature = "appsync_events")] +pub(crate) fn deserialize_stringified_json<'de, T, D>(deserializer: D) -> Result +where + T: DeserializeOwned, + D: Deserializer<'de>, +{ + let json_str = String::deserialize(deserializer)?; + + serde_json::from_str(&json_str).map_err(D::Error::custom) +} + #[cfg(test)] #[allow(deprecated)] mod test { diff --git a/lambda-events/src/event/appsync_events/mod.rs b/lambda-events/src/event/appsync_events/mod.rs new file mode 100644 index 00000000..7c9536d8 --- /dev/null +++ b/lambda-events/src/event/appsync_events/mod.rs @@ -0,0 +1,178 @@ +use std::{collections::HashMap, fmt}; + +use crate::custom_serde::{ + deserialize_headers, deserialize_lambda_map, deserialize_stringified_json, serialize_headers, + serialize_stringified_json, +}; +use http::HeaderMap; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Value; + +/// `AppSyncEventsLambdaAuthorizerRequest` contains an authorization request from AppSync Events. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsLambdaAuthorizerRequest { + #[serde(default)] + pub authorization_token: Option, + pub request_context: AppSyncEventsLambdaAuthorizerRequestContext, + #[serde(deserialize_with = "deserialize_headers", default)] + #[serde(serialize_with = "serialize_headers")] + pub headers: HeaderMap, +} + +/// `AppSyncEventsLambdaAuthorizerRequestContext` contains the parameters of the AppSync Events invocation which triggered +/// this authorization request. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsLambdaAuthorizerRequestContext { + #[serde(default)] + pub account_id: Option, + #[serde(default)] + pub api_id: Option, + #[serde(default)] + pub operation: Option, + #[serde(default)] + pub request_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channel_namespace_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channel: Option, +} + +/// `AppSyncEventsOperation` represent all the possible operations which +/// triggered this authorization request. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum AppSyncEventsOperation { + EventConnect, + EventSubscribe, + EventPublish, +} + +impl fmt::Display for AppSyncEventsOperation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let val = match self { + AppSyncEventsOperation::EventConnect => "EVENT_CONNECT", + AppSyncEventsOperation::EventSubscribe => "EVENT_SUBSCRIBE", + AppSyncEventsOperation::EventPublish => "EVENT_PUBLISH", + }; + + write!(f, "{val}") + } +} + +/// `AppSyncEventsLambdaAuthorizerResponse` represents the expected format of an authorization response to AppSync Events. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsLambdaAuthorizerResponse +where + T1: DeserializeOwned + Serialize, +{ + pub is_authorized: bool, + #[serde(deserialize_with = "deserialize_lambda_map")] + #[serde(default)] + #[serde(bound = "")] + pub handler_context: HashMap, + pub ttl_override: Option, +} + +/// `AppSyncEventsWebscoketMessage` represents all possible messages which can be sent between +/// AppSync Events and a connected websocket client. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "snake_case", tag = "type")] +pub enum AppSyncEventsWebsocketMessage +where + T1: DeserializeOwned + Serialize, +{ + ConnectionInit, + ConnectionAck(AppSyncEventsConnectionAckMessage), + #[serde(rename = "ka")] + KeepAlive, + #[serde(bound = "")] + Subscribe(AppSyncEventsSubscribeMessage), + SubscribeSuccess(AppSyncEventsSubscribeSuccessMessage), + SubscribeError(AppSyncEventsErrorMessage), + #[serde(bound = "")] + Data(AppSyncEventsDataMessage), + BroadcastError(AppSyncEventsErrorMessage), + Unsubscribe(AppSyncEventsUnsubscribeMessage), + UnsubscribeSuccess(AppSyncEventsUnsubscribeSuccessMessage), + UnsubscribeError(AppSyncEventsErrorMessage), +} + +/// `AppSyncEventsConnectionAckMessage` contains the connection paramters for this acknowledged +/// connection. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsConnectionAckMessage { + pub connection_timeout_ms: Option, +} + +/// `AppSyncEventsSubscribeMessage` contains the parameters to subscribe to an AppSync Events channel. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsSubscribeMessage { + pub id: Option, + pub channel: Option, + #[serde(deserialize_with = "deserialize_headers", default)] + #[serde(serialize_with = "serialize_headers")] + pub authorization: HeaderMap, +} + +/// `AppSyncEventsSubscribeSuccessMessage` contains the subscription parameters for this +/// successful subscription. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsSubscribeSuccessMessage { + pub id: Option, +} + +/// `AppSyncEventsErrorMessage` contains one or more AppSync Events errors. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsErrorMessage { + pub id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub errors: Option>, +} + +/// `AppSyncEventSubscribeErrorDescription` contains information about an error. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventErrorDescription { + #[serde(default)] + pub error_type: Option, + #[serde(default)] + pub message: Option, +} + +/// `AppSyncEventsDataMessage` represents an incoming event on a subscribed AppSync Events channel. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsDataMessage +where + T1: DeserializeOwned + Serialize, +{ + pub id: Option, + #[serde( + bound = "", + deserialize_with = "deserialize_stringified_json", + serialize_with = "serialize_stringified_json" + )] + pub event: T1, +} + +/// `AppSyncEventsUnsubscribeMessage` contains the parameters to unsubscribe to an AppSync Events channel. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsUnsubscribeMessage { + pub id: Option, +} + +/// `AppSyncEventsUnsubscribeSuccessMessage` contains the unsubscription parameters for this +/// successful unsubscription. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSyncEventsUnsubscribeSuccessMessage { + pub id: Option, +} diff --git a/lambda-events/src/event/mod.rs b/lambda-events/src/event/mod.rs index d63acc4d..aca06e08 100644 --- a/lambda-events/src/event/mod.rs +++ b/lambda-events/src/event/mod.rs @@ -13,6 +13,10 @@ pub mod apigw; #[cfg(feature = "appsync")] pub mod appsync; +/// AWS Lambda event definitions for appsync. +#[cfg(feature = "appsync_events")] +pub mod appsync_events; + /// AWS Lambda event definitions for autoscaling. #[cfg(feature = "autoscaling")] pub mod autoscaling; diff --git a/lambda-events/src/lib.rs b/lambda-events/src/lib.rs index e21cdc13..10536640 100644 --- a/lambda-events/src/lib.rs +++ b/lambda-events/src/lib.rs @@ -29,6 +29,10 @@ pub use event::apigw; #[cfg(feature = "appsync")] pub use event::appsync; +/// AWS Lambda event definitions for appsync events. +#[cfg(feature = "appsync_events")] +pub use event::appsync_events; + /// AWS Lambda event definitions for autoscaling. #[cfg(feature = "autoscaling")] pub use event::autoscaling;