use crate::aws::aws_request_utils::{AwsRequestBuilderExt, AwsRequestSigner, AwsSignConfig};
use crate::aws::AwsClientConfig;
use crate::aws::AwsClientConfigExt;
use alien_client_core::{ErrorData, Result};
use alien_error::{Context, ContextError, IntoAlienError};
use bon::Builder;
use form_urlencoded;
use reqwest::{Client, Method, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use async_trait::async_trait;
#[cfg(feature = "test-utils")]
use mockall::automock;
#[cfg_attr(feature = "test-utils", automock)]
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
pub trait LambdaApi: Send + Sync + std::fmt::Debug {
async fn create_function(
&self,
request: CreateFunctionRequest,
) -> Result<FunctionConfiguration>;
async fn create_function_url_config(
&self,
function_name: &str,
request: CreateFunctionUrlConfigRequest,
) -> Result<CreateFunctionUrlConfigResponse>;
async fn add_permission(
&self,
function_name: &str,
request: AddPermissionRequest,
) -> Result<AddPermissionResponse>;
async fn update_function_code(
&self,
function_name: &str,
request: UpdateFunctionCodeRequest,
) -> Result<FunctionConfiguration>;
async fn update_function_configuration(
&self,
function_name: &str,
request: UpdateFunctionConfigurationRequest,
) -> Result<FunctionConfiguration>;
async fn get_function_configuration(
&self,
function_name: &str,
qualifier: Option<String>,
) -> Result<FunctionConfiguration>;
async fn delete_function_url_config(
&self,
function_name: &str,
qualifier: Option<String>,
) -> Result<()>;
async fn get_function_url_config(
&self,
function_name: &str,
qualifier: Option<String>,
) -> Result<FunctionUrlConfig>;
async fn delete_function(&self, function_name: &str, qualifier: Option<String>) -> Result<()>;
async fn get_policy(
&self,
function_name: &str,
qualifier: Option<String>,
) -> Result<GetPolicyResponse>;
async fn invoke(&self, request: InvokeRequest) -> Result<InvokeResponse>;
async fn create_event_source_mapping(
&self,
request: CreateEventSourceMappingRequest,
) -> Result<EventSourceMapping>;
async fn get_event_source_mapping(&self, uuid: &str) -> Result<EventSourceMapping>;
async fn update_event_source_mapping(
&self,
uuid: &str,
request: UpdateEventSourceMappingRequest,
) -> Result<EventSourceMapping>;
async fn delete_event_source_mapping(&self, uuid: &str) -> Result<EventSourceMapping>;
async fn list_event_source_mappings(
&self,
request: ListEventSourceMappingsRequest,
) -> Result<ListEventSourceMappingsResponse>;
async fn put_function_concurrency(
&self,
function_name: &str,
reserved_concurrent_executions: u32,
) -> Result<()>;
async fn delete_function_concurrency(&self, function_name: &str) -> Result<()>;
}
#[derive(Debug, Clone)]
pub struct LambdaClient {
client: Client,
config: AwsClientConfig,
}
impl LambdaClient {
pub fn new(client: Client, config: AwsClientConfig) -> Self {
Self { client, config }
}
fn sign_config(&self) -> AwsSignConfig {
AwsSignConfig {
service_name: "lambda".into(),
region: self.config.region.clone(),
credentials: self.config.get_credentials(),
signing_region: None,
}
}
fn get_base_url(&self) -> String {
if let Some(override_url) = self.config.get_service_endpoint_option("lambda") {
override_url.to_string()
} else {
format!("https://lambda.{}.amazonaws.com", self.config.region)
}
}
async fn send_json<T: DeserializeOwned + Send + 'static>(
&self,
method: Method,
path: &str,
query_params: Option<Vec<(&str, String)>>,
body: Option<String>,
operation: &str,
resource: &str,
) -> Result<T> {
let base_url = self.get_base_url();
let mut url = format!("{}{}", base_url.trim_end_matches('/'), path);
if let Some(qs) = query_params {
if !qs.is_empty() {
url.push('?');
url.push_str(
&qs.iter()
.map(|(k, v)| {
format!(
"{}={}",
k,
form_urlencoded::byte_serialize(v.as_bytes()).collect::<String>()
)
})
.collect::<Vec<_>>()
.join("&"),
);
}
}
let builder = self
.client
.request(method.clone(), &url)
.host(&format!("lambda.{}.amazonaws.com", self.config.region))
.content_type_json();
let builder = if let Some(ref b) = body {
builder.content_sha256(b).body(b.clone())
} else {
builder.content_sha256("")
};
let result =
crate::aws::aws_request_utils::sign_send_json(builder, &self.sign_config()).await;
Self::map_result(result, operation, resource, body.as_deref())
}
async fn send_no_body(
&self,
method: Method,
path: &str,
query_params: Option<Vec<(&str, String)>>,
operation: &str,
resource: &str,
) -> Result<()> {
let base_url = self.get_base_url();
let mut url = format!("{}{}", base_url.trim_end_matches('/'), path);
if let Some(qs) = query_params {
if !qs.is_empty() {
url.push('?');
url.push_str(
&qs.iter()
.map(|(k, v)| {
format!(
"{}={}",
k,
form_urlencoded::byte_serialize(v.as_bytes()).collect::<String>()
)
})
.collect::<Vec<_>>()
.join("&"),
);
}
}
let builder = self
.client
.request(method, &url)
.host(&format!("lambda.{}.amazonaws.com", self.config.region))
.content_sha256("");
let result =
crate::aws::aws_request_utils::sign_send_no_response(builder, &self.sign_config())
.await;
Self::map_result(result, operation, resource, None)
}
fn map_result<T>(
result: Result<T>,
operation: &str,
resource: &str,
request_body: Option<&str>,
) -> Result<T> {
match result {
Ok(v) => Ok(v),
Err(e) => {
if let Some(ErrorData::HttpResponseError {
http_status,
http_response_text: Some(ref text),
..
}) = &e.error
{
let status = StatusCode::from_u16(*http_status)
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
if let Some(mapped) =
Self::map_lambda_error(status, text, operation, resource, request_body)
{
Err(e.context(mapped))
} else {
Err(e)
}
} else {
Err(e)
}
}
}
}
fn map_lambda_error(
status: StatusCode,
body: &str,
operation: &str,
resource: &str,
request_body: Option<&str>,
) -> Option<ErrorData> {
let parsed: std::result::Result<LambdaErrorResponse, _> = serde_json::from_str(body);
let (code, message) = match parsed {
Ok(e) => {
let c = e
.type_field_underscore
.or(e.type_field)
.or_else(|| e.error.as_ref().and_then(|d| d.code.clone()))
.unwrap_or_else(|| "UnknownErrorCode".into());
let m = e
.message
.or(e.message_capital)
.or_else(|| e.error.as_ref().and_then(|d| d.message.clone()))
.unwrap_or_else(|| "Unknown error".into());
(c, m)
}
Err(_) => {
return None;
}
};
Some(match code.as_str() {
"AccessDeniedException"
| "NotAuthorized"
| "UnrecognizedClientException"
| "ExpiredTokenException" => ErrorData::RemoteAccessDenied {
resource_type: "Function".into(),
resource_name: resource.into(),
},
"ThrottlingException" | "TooManyRequestsException" => {
ErrorData::RateLimitExceeded { message }
}
"ServiceUnavailable" | "InternalFailure" | "ServiceException" => {
ErrorData::RemoteServiceUnavailable { message }
}
"RequestTimeoutException" => ErrorData::Timeout { message },
"ResourceNotFoundException" => ErrorData::RemoteResourceNotFound {
resource_type: "Function".into(),
resource_name: resource.into(),
},
"ResourceConflictException" => ErrorData::RemoteResourceConflict {
message,
resource_type: "Function".into(),
resource_name: resource.into(),
},
"EC2AccessDeniedException" | "EC2ThrottledException" | "EC2UnexpectedException" => {
ErrorData::RemoteServiceUnavailable { message }
}
"EFSIOException"
| "EFSMountConnectivityException"
| "EFSMountFailureException"
| "EFSMountTimeoutException" => ErrorData::RemoteServiceUnavailable { message },
"ENILimitReachedException"
| "InvalidSubnetIDException"
| "InvalidSecurityGroupIDException"
| "SubnetIPAddressLimitReachedException" => {
ErrorData::RemoteServiceUnavailable { message }
}
"InvalidParameterValueException"
| "InvalidRequestContentException"
| "UnsupportedMediaTypeException" => ErrorData::InvalidInput {
message,
field_name: None,
},
"InvalidRuntimeException" | "InvalidZipFileException" => {
ErrorData::RemoteServiceUnavailable { message }
}
"KMSAccessDeniedException"
| "KMSDisabledException"
| "KMSInvalidStateException"
| "KMSNotFoundException" => ErrorData::RemoteAccessDenied {
resource_type: "KMS Key".into(),
resource_name: resource.into(),
},
"RecursiveInvocationException" => ErrorData::InvalidInput {
message,
field_name: None,
},
"RequestTooLargeException" => ErrorData::InvalidInput {
message,
field_name: None,
},
"ResourceNotReadyException" => ErrorData::RemoteServiceUnavailable { message },
"SnapStartException" | "SnapStartNotReadyException" | "SnapStartTimeoutException" => {
ErrorData::RemoteServiceUnavailable { message }
}
_ => match status {
StatusCode::NOT_FOUND => ErrorData::RemoteResourceNotFound {
resource_type: "Function".into(),
resource_name: resource.into(),
},
StatusCode::CONFLICT => ErrorData::RemoteResourceConflict {
message,
resource_type: "Function".into(),
resource_name: resource.into(),
},
StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED => ErrorData::RemoteAccessDenied {
resource_type: "Function".into(),
resource_name: resource.into(),
},
StatusCode::TOO_MANY_REQUESTS => ErrorData::RateLimitExceeded { message },
StatusCode::SERVICE_UNAVAILABLE
| StatusCode::BAD_GATEWAY
| StatusCode::GATEWAY_TIMEOUT => ErrorData::RemoteServiceUnavailable { message },
_ => ErrorData::HttpResponseError {
message: format!("Lambda operation failed: {}", message),
url: format!("lambda.amazonaws.com"),
http_status: status.as_u16(),
http_response_text: Some(body.into()),
http_request_text: request_body.map(|s| s.to_string()),
},
},
})
}
}
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
impl LambdaApi for LambdaClient {
async fn create_function(
&self,
request: CreateFunctionRequest,
) -> Result<FunctionConfiguration> {
let body = serde_json::to_string(&request).into_alien_error().context(
ErrorData::SerializationError {
message: format!(
"Failed to serialize CreateFunctionRequest for function '{}'",
request.function_name
),
},
)?;
self.send_json(
Method::POST,
"/2015-03-31/functions",
None,
Some(body),
"CreateFunction",
&request.function_name,
)
.await
}
async fn create_function_url_config(
&self,
function_name: &str,
request: CreateFunctionUrlConfigRequest,
) -> Result<CreateFunctionUrlConfigResponse> {
let body = serde_json::to_string(&request).into_alien_error().context(
ErrorData::SerializationError {
message: format!(
"Failed to serialize CreateFunctionUrlConfigRequest for function '{}'",
function_name
),
},
)?;
let path = format!("/2021-10-31/functions/{}/url", function_name);
self.send_json(
Method::POST,
&path,
None,
Some(body),
"CreateFunctionUrlConfig",
function_name,
)
.await
}
async fn add_permission(
&self,
function_name: &str,
request: AddPermissionRequest,
) -> Result<AddPermissionResponse> {
let body = serde_json::to_string(&request).into_alien_error().context(
ErrorData::SerializationError {
message: format!(
"Failed to serialize AddPermissionRequest for function '{}'",
function_name
),
},
)?;
let path = format!("/2015-03-31/functions/{}/policy", function_name);
self.send_json(
Method::POST,
&path,
None,
Some(body),
"AddPermission",
function_name,
)
.await
}
async fn update_function_code(
&self,
function_name: &str,
request: UpdateFunctionCodeRequest,
) -> Result<FunctionConfiguration> {
let body = serde_json::to_string(&request).into_alien_error().context(
ErrorData::SerializationError {
message: format!(
"Failed to serialize UpdateFunctionCodeRequest for function '{}'",
function_name
),
},
)?;
let path = format!("/2015-03-31/functions/{}/code", function_name);
self.send_json(
Method::PUT,
&path,
None,
Some(body),
"UpdateFunctionCode",
function_name,
)
.await
}
async fn update_function_configuration(
&self,
function_name: &str,
request: UpdateFunctionConfigurationRequest,
) -> Result<FunctionConfiguration> {
let body = serde_json::to_string(&request).into_alien_error().context(
ErrorData::SerializationError {
message: format!(
"Failed to serialize UpdateFunctionConfigurationRequest for function '{}'",
function_name
),
},
)?;
let path = format!("/2015-03-31/functions/{}/configuration", function_name);
self.send_json(
Method::PUT,
&path,
None,
Some(body),
"UpdateFunctionConfiguration",
function_name,
)
.await
}
async fn get_function_configuration(
&self,
function_name: &str,
qualifier: Option<String>,
) -> Result<FunctionConfiguration> {
let path = format!("/2015-03-31/functions/{}", function_name);
let mut qp = Vec::new();
if let Some(q) = qualifier {
qp.push(("Qualifier", q));
}
let resp: GetFunctionResponse = self
.send_json(
Method::GET,
&path,
Some(qp),
None,
"GetFunctionConfiguration",
function_name,
)
.await?;
Ok(resp.configuration)
}
async fn delete_function_url_config(
&self,
function_name: &str,
qualifier: Option<String>,
) -> Result<()> {
let path = format!("/2021-10-31/functions/{}/url", function_name);
let mut qp = Vec::new();
if let Some(q) = qualifier {
qp.push(("Qualifier", q));
}
self.send_no_body(
Method::DELETE,
&path,
if qp.is_empty() { None } else { Some(qp) },
"DeleteFunctionUrlConfig",
function_name,
)
.await
}
async fn get_function_url_config(
&self,
function_name: &str,
qualifier: Option<String>,
) -> Result<FunctionUrlConfig> {
let path = format!("/2021-10-31/functions/{}/url", function_name);
let mut qp = Vec::new();
if let Some(q) = qualifier {
qp.push(("Qualifier", q));
}
self.send_json(
Method::GET,
&path,
if qp.is_empty() { None } else { Some(qp) },
None,
"GetFunctionUrlConfig",
function_name,
)
.await
}
async fn delete_function(&self, function_name: &str, qualifier: Option<String>) -> Result<()> {
let path = format!("/2015-03-31/functions/{}", function_name);
let mut qp = Vec::new();
if let Some(q) = qualifier {
qp.push(("Qualifier", q));
}
self.send_no_body(
Method::DELETE,
&path,
if qp.is_empty() { None } else { Some(qp) },
"DeleteFunction",
function_name,
)
.await
}
async fn get_policy(
&self,
function_name: &str,
qualifier: Option<String>,
) -> Result<GetPolicyResponse> {
let path = format!("/2015-03-31/functions/{}/policy", function_name);
let mut qp = Vec::new();
if let Some(q) = qualifier {
qp.push(("Qualifier", q));
}
self.send_json(
Method::GET,
&path,
if qp.is_empty() { None } else { Some(qp) },
None,
"GetPolicy",
function_name,
)
.await
}
async fn invoke(&self, request: InvokeRequest) -> Result<InvokeResponse> {
let function_name = &request.function_name;
let path = format!("/2015-03-31/functions/{}/invocations", function_name);
let base_url = self.get_base_url();
let mut url = format!("{}{}", base_url.trim_end_matches('/'), path);
if let Some(ref qualifier) = request.qualifier {
url.push('?');
url.push_str(&format!(
"Qualifier={}",
form_urlencoded::byte_serialize(qualifier.as_bytes()).collect::<String>()
));
}
let mut builder = self
.client
.request(Method::POST, &url)
.host(&format!("lambda.{}.amazonaws.com", self.config.region));
match request.invocation_type {
InvocationType::RequestResponse => {
builder = builder.header("X-Amz-Invocation-Type", "RequestResponse");
}
InvocationType::Event => {
builder = builder.header("X-Amz-Invocation-Type", "Event");
}
InvocationType::DryRun => {
builder = builder.header("X-Amz-Invocation-Type", "DryRun");
}
}
if let Some(ref log_type) = request.log_type {
builder = builder.header("X-Amz-Log-Type", log_type);
}
if let Some(ref client_context) = request.client_context {
builder = builder.header("X-Amz-Client-Context", client_context);
}
builder = builder.content_sha256_bytes(&request.payload);
if !request.payload.is_empty() {
builder = builder.body(reqwest::Body::from(request.payload.clone()));
}
let signed_builder = builder.sign_aws_request(&self.sign_config())?;
let response = signed_builder.send().await.into_alien_error().context(
ErrorData::HttpRequestFailed {
message: format!("Failed to invoke Lambda function '{}'", function_name),
},
)?;
let status = response.status().as_u16();
let headers = response.headers().clone();
let payload =
response
.bytes()
.await
.into_alien_error()
.context(ErrorData::HttpRequestFailed {
message: format!(
"Failed to read invoke response body for function '{}'",
function_name
),
})?;
let function_error = headers
.get("X-Amz-Function-Error")
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string());
let log_result = headers
.get("X-Amz-Log-Result")
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string());
let executed_version = headers
.get("X-Amz-Executed-Version")
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string());
Ok(InvokeResponse {
status_code: status,
function_error,
log_result,
payload: payload.to_vec(),
executed_version,
})
}
async fn create_event_source_mapping(
&self,
request: CreateEventSourceMappingRequest,
) -> Result<EventSourceMapping> {
let body = serde_json::to_string(&request).into_alien_error().context(
ErrorData::SerializationError {
message: format!(
"Failed to serialize CreateEventSourceMappingRequest for function '{}'",
request.function_name
),
},
)?;
self.send_json(
Method::POST,
"/2015-03-31/event-source-mappings",
None,
Some(body),
"CreateEventSourceMapping",
&request.function_name,
)
.await
}
async fn get_event_source_mapping(&self, uuid: &str) -> Result<EventSourceMapping> {
let path = format!("/2015-03-31/event-source-mappings/{}", uuid);
self.send_json(
Method::GET,
&path,
None,
None,
"GetEventSourceMapping",
uuid,
)
.await
}
async fn update_event_source_mapping(
&self,
uuid: &str,
request: UpdateEventSourceMappingRequest,
) -> Result<EventSourceMapping> {
let body = serde_json::to_string(&request).into_alien_error().context(
ErrorData::SerializationError {
message: format!(
"Failed to serialize UpdateEventSourceMappingRequest for UUID '{}'",
uuid
),
},
)?;
let path = format!("/2015-03-31/event-source-mappings/{}", uuid);
self.send_json(
Method::PUT,
&path,
None,
Some(body),
"UpdateEventSourceMapping",
uuid,
)
.await
}
async fn delete_event_source_mapping(&self, uuid: &str) -> Result<EventSourceMapping> {
let path = format!("/2015-03-31/event-source-mappings/{}", uuid);
self.send_json(
Method::DELETE,
&path,
None,
None,
"DeleteEventSourceMapping",
uuid,
)
.await
}
async fn list_event_source_mappings(
&self,
request: ListEventSourceMappingsRequest,
) -> Result<ListEventSourceMappingsResponse> {
let mut qp = Vec::new();
if let Some(ref arn) = request.event_source_arn {
qp.push(("EventSourceArn", arn.clone()));
}
if let Some(ref func) = request.function_name {
qp.push(("FunctionName", func.clone()));
}
if let Some(ref marker) = request.marker {
qp.push(("Marker", marker.clone()));
}
if let Some(max_items) = request.max_items {
qp.push(("MaxItems", max_items.to_string()));
}
let resource_name = request.function_name.as_deref().unwrap_or("unknown");
self.send_json(
Method::GET,
"/2015-03-31/event-source-mappings",
if qp.is_empty() { None } else { Some(qp) },
None,
"ListEventSourceMappings",
resource_name,
)
.await
}
async fn put_function_concurrency(
&self,
function_name: &str,
reserved_concurrent_executions: u32,
) -> Result<()> {
let body =
serde_json::json!({ "ReservedConcurrentExecutions": reserved_concurrent_executions })
.to_string();
let path = format!("/2017-10-31/functions/{}/concurrency", function_name);
let _: serde_json::Value = self
.send_json(
Method::PUT,
&path,
None,
Some(body),
"PutFunctionConcurrency",
function_name,
)
.await?;
Ok(())
}
async fn delete_function_concurrency(&self, function_name: &str) -> Result<()> {
let path = format!("/2017-10-31/functions/{}/concurrency", function_name);
self.send_no_body(
Method::DELETE,
&path,
None,
"DeleteFunctionConcurrency",
function_name,
)
.await
}
}
#[derive(Debug, Deserialize)]
struct LambdaErrorResponse {
#[serde(rename = "Type")]
type_field: Option<String>,
#[serde(rename = "__type")]
type_field_underscore: Option<String>,
#[serde(rename = "message")]
message: Option<String>,
#[serde(rename = "Message")]
message_capital: Option<String>,
#[serde(rename = "Error")]
error: Option<LambdaErrorDetails>,
}
#[derive(Debug, Deserialize)]
struct LambdaErrorDetails {
#[serde(rename = "Code")]
code: Option<String>,
#[serde(rename = "Message")]
message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct CreateFunctionRequest {
pub function_name: String,
pub role: String,
pub code: FunctionCode,
#[builder(default = "Image".to_string())]
pub package_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_size: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub publish: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub environment: Option<Environment>,
#[serde(skip_serializing_if = "Option::is_none")]
pub architectures: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tracing_config: Option<TracingConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<std::collections::HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ephemeral_storage: Option<EphemeralStorage>,
#[serde(skip_serializing_if = "Option::is_none", rename = "KMSKeyArn")]
pub kms_key_arn: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vpc_config: Option<VpcConfig>,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct FunctionCode {
pub image_uri: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct Environment {
pub variables: Option<std::collections::HashMap<String, String>>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct FunctionConfiguration {
pub function_name: Option<String>,
pub function_arn: Option<String>,
pub state: Option<String>,
pub last_update_status: Option<String>,
#[serde(rename = "KMSKeyArn")]
pub kms_key_arn: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct GetFunctionResponse {
pub configuration: FunctionConfiguration,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct CreateFunctionUrlConfigRequest {
pub auth_type: String,
pub cors: Option<Cors>,
pub invoke_mode: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CreateFunctionUrlConfigResponse {
pub function_url: String,
pub function_arn: String,
pub auth_type: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct FunctionUrlConfig {
pub function_url: String,
pub auth_type: String,
pub cors: Option<Cors>,
}
#[derive(Debug, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct AddPermissionRequest {
pub statement_id: String,
pub action: String,
pub principal: String,
pub function_url_auth_type: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct AddPermissionResponse {
pub statement: Option<String>,
}
#[derive(Debug, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct UpdateFunctionCodeRequest {
pub image_uri: String,
pub publish: Option<bool>,
}
#[derive(Debug, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct UpdateFunctionConfigurationRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_size: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub environment: Option<Environment>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vpc_config: Option<VpcConfig>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct GetPolicyResponse {
pub policy: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct TracingConfig {
pub mode: Option<String>,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct EphemeralStorage {
pub size: i32,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct VpcConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub subnet_ids: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub security_group_ids: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct Cors {
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_credentials: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_headers: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_methods: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_origins: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_age: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct CreateEventSourceMappingRequest {
pub event_source_arn: String,
pub function_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub batch_size: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub maximum_batching_window_in_seconds: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub function_response_types: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter_criteria: Option<FilterCriteria>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scaling_config: Option<ScalingConfig>,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct UpdateEventSourceMappingRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub batch_size: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub function_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub maximum_batching_window_in_seconds: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub function_response_types: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter_criteria: Option<FilterCriteria>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scaling_config: Option<ScalingConfig>,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct ListEventSourceMappingsRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub event_source_arn: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub function_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub marker: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_items: Option<i32>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ListEventSourceMappingsResponse {
pub event_source_mappings: Option<Vec<EventSourceMapping>>,
pub next_marker: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct EventSourceMapping {
#[serde(rename = "UUID")]
pub uuid: Option<String>,
pub event_source_arn: Option<String>,
pub function_arn: Option<String>,
pub batch_size: Option<i32>,
pub last_modified: Option<f64>,
pub last_processing_result: Option<String>,
pub state: Option<String>,
pub state_transition_reason: Option<String>,
pub maximum_batching_window_in_seconds: Option<i32>,
pub function_response_types: Option<Vec<String>>,
pub filter_criteria: Option<FilterCriteria>,
pub scaling_config: Option<ScalingConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct FilterCriteria {
#[serde(skip_serializing_if = "Option::is_none")]
pub filters: Option<Vec<Filter>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct Filter {
#[serde(skip_serializing_if = "Option::is_none")]
pub pattern: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "PascalCase")]
pub struct ScalingConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub maximum_concurrency: Option<i32>,
}
#[derive(Debug, Clone)]
pub enum InvocationType {
RequestResponse,
Event,
DryRun,
}
impl Default for InvocationType {
fn default() -> Self {
InvocationType::RequestResponse
}
}
#[derive(Debug, Clone, Builder)]
pub struct InvokeRequest {
pub function_name: String,
#[builder(default)]
pub invocation_type: InvocationType,
pub qualifier: Option<String>,
pub client_context: Option<String>,
pub log_type: Option<String>,
#[builder(default)]
pub payload: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct InvokeResponse {
pub status_code: u16,
pub function_error: Option<String>,
pub log_result: Option<String>,
pub payload: Vec<u8>,
pub executed_version: Option<String>,
}