1use crate::aws::aws_request_utils::{AwsRequestBuilderExt, AwsRequestSigner, AwsSignConfig};
2use crate::aws::credential_provider::AwsCredentialProvider;
3use alien_client_core::{ErrorData, Result};
4
5use alien_error::{Context, ContextError, IntoAlienError};
6use bon::Builder;
7use form_urlencoded;
8use reqwest::{Client, Method, StatusCode};
9use serde::de::DeserializeOwned;
10use serde::{Deserialize, Serialize};
11
12#[cfg(feature = "test-utils")]
13use mockall::automock;
14
15#[cfg_attr(feature = "test-utils", automock)]
16#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
17#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
18pub trait LambdaApi: Send + Sync + std::fmt::Debug {
19 async fn create_function(
20 &self,
21 request: CreateFunctionRequest,
22 ) -> Result<FunctionConfiguration>;
23 async fn create_function_url_config(
24 &self,
25 function_name: &str,
26 request: CreateFunctionUrlConfigRequest,
27 ) -> Result<CreateFunctionUrlConfigResponse>;
28 async fn add_permission(
29 &self,
30 function_name: &str,
31 request: AddPermissionRequest,
32 ) -> Result<AddPermissionResponse>;
33 async fn update_function_code(
34 &self,
35 function_name: &str,
36 request: UpdateFunctionCodeRequest,
37 ) -> Result<FunctionConfiguration>;
38 async fn update_function_configuration(
39 &self,
40 function_name: &str,
41 request: UpdateFunctionConfigurationRequest,
42 ) -> Result<FunctionConfiguration>;
43 async fn get_function_configuration(
44 &self,
45 function_name: &str,
46 qualifier: Option<String>,
47 ) -> Result<FunctionConfiguration>;
48 async fn delete_function_url_config(
49 &self,
50 function_name: &str,
51 qualifier: Option<String>,
52 ) -> Result<()>;
53 async fn get_function_url_config(
54 &self,
55 function_name: &str,
56 qualifier: Option<String>,
57 ) -> Result<FunctionUrlConfig>;
58 async fn delete_function(&self, function_name: &str, qualifier: Option<String>) -> Result<()>;
59 async fn get_policy(
60 &self,
61 function_name: &str,
62 qualifier: Option<String>,
63 ) -> Result<GetPolicyResponse>;
64
65 async fn invoke(&self, request: InvokeRequest) -> Result<InvokeResponse>;
67
68 async fn create_event_source_mapping(
70 &self,
71 request: CreateEventSourceMappingRequest,
72 ) -> Result<EventSourceMapping>;
73 async fn get_event_source_mapping(&self, uuid: &str) -> Result<EventSourceMapping>;
74 async fn update_event_source_mapping(
75 &self,
76 uuid: &str,
77 request: UpdateEventSourceMappingRequest,
78 ) -> Result<EventSourceMapping>;
79 async fn delete_event_source_mapping(&self, uuid: &str) -> Result<EventSourceMapping>;
80 async fn list_event_source_mappings(
81 &self,
82 request: ListEventSourceMappingsRequest,
83 ) -> Result<ListEventSourceMappingsResponse>;
84
85 async fn put_function_concurrency(
87 &self,
88 function_name: &str,
89 reserved_concurrent_executions: u32,
90 ) -> Result<()>;
91 async fn delete_function_concurrency(&self, function_name: &str) -> Result<()>;
92}
93
94#[derive(Debug, Clone)]
98pub struct LambdaClient {
99 client: Client,
100 credentials: AwsCredentialProvider,
101}
102
103impl LambdaClient {
104 pub fn new(client: Client, credentials: AwsCredentialProvider) -> Self {
105 Self {
106 client,
107 credentials,
108 }
109 }
110
111 fn sign_config(&self) -> AwsSignConfig {
112 AwsSignConfig {
113 service_name: "lambda".into(),
114 region: self.credentials.region().to_string(),
115 credentials: self.credentials.get_credentials(),
116 signing_region: None,
117 }
118 }
119
120 fn get_base_url(&self) -> String {
121 if let Some(override_url) = self.credentials.get_service_endpoint_option("lambda") {
122 override_url.to_string()
123 } else {
124 format!("https://lambda.{}.amazonaws.com", self.credentials.region())
125 }
126 }
127
128 async fn send_json<T: DeserializeOwned + Send + 'static>(
131 &self,
132 method: Method,
133 path: &str,
134 query_params: Option<Vec<(&str, String)>>,
135 body: Option<String>,
136 operation: &str,
137 resource: &str,
138 ) -> Result<T> {
139 self.credentials.ensure_fresh().await?;
140 let base_url = self.get_base_url();
141 let mut url = format!("{}{}", base_url.trim_end_matches('/'), path);
142 if let Some(qs) = query_params {
143 if !qs.is_empty() {
144 url.push('?');
145 url.push_str(
146 &qs.iter()
147 .map(|(k, v)| {
148 format!(
149 "{}={}",
150 k,
151 form_urlencoded::byte_serialize(v.as_bytes()).collect::<String>()
152 )
153 })
154 .collect::<Vec<_>>()
155 .join("&"),
156 );
157 }
158 }
159
160 let builder = self
161 .client
162 .request(method.clone(), &url)
163 .host(&format!(
164 "lambda.{}.amazonaws.com",
165 self.credentials.region()
166 ))
167 .content_type_json();
168
169 let builder = if let Some(ref b) = body {
170 builder.content_sha256(b).body(b.clone())
171 } else {
172 builder.content_sha256("")
173 };
174
175 let result =
176 crate::aws::aws_request_utils::sign_send_json(builder, &self.sign_config()).await;
177
178 Self::map_result(result, operation, resource, body.as_deref())
179 }
180
181 async fn send_no_body(
182 &self,
183 method: Method,
184 path: &str,
185 query_params: Option<Vec<(&str, String)>>,
186 operation: &str,
187 resource: &str,
188 ) -> Result<()> {
189 self.credentials.ensure_fresh().await?;
190 let base_url = self.get_base_url();
191 let mut url = format!("{}{}", base_url.trim_end_matches('/'), path);
192 if let Some(qs) = query_params {
193 if !qs.is_empty() {
194 url.push('?');
195 url.push_str(
196 &qs.iter()
197 .map(|(k, v)| {
198 format!(
199 "{}={}",
200 k,
201 form_urlencoded::byte_serialize(v.as_bytes()).collect::<String>()
202 )
203 })
204 .collect::<Vec<_>>()
205 .join("&"),
206 );
207 }
208 }
209
210 let builder = self
211 .client
212 .request(method, &url)
213 .host(&format!(
214 "lambda.{}.amazonaws.com",
215 self.credentials.region()
216 ))
217 .content_sha256("");
218
219 let result =
220 crate::aws::aws_request_utils::sign_send_no_response(builder, &self.sign_config())
221 .await;
222
223 Self::map_result(result, operation, resource, None)
224 }
225
226 fn map_result<T>(
227 result: Result<T>,
228 operation: &str,
229 resource: &str,
230 request_body: Option<&str>,
231 ) -> Result<T> {
232 match result {
233 Ok(v) => Ok(v),
234 Err(e) => {
235 if let Some(ErrorData::HttpResponseError {
236 http_status,
237 http_response_text: Some(ref text),
238 ..
239 }) = &e.error
240 {
241 let status = StatusCode::from_u16(*http_status)
242 .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
243 if let Some(mapped) =
244 Self::map_lambda_error(status, text, operation, resource, request_body)
245 {
246 Err(e.context(mapped))
247 } else {
248 Err(e)
250 }
251 } else {
252 Err(e)
253 }
254 }
255 }
256 }
257
258 fn map_lambda_error(
259 status: StatusCode,
260 body: &str,
261 operation: &str,
262 resource: &str,
263 request_body: Option<&str>,
264 ) -> Option<ErrorData> {
265 let parsed: std::result::Result<LambdaErrorResponse, _> = serde_json::from_str(body);
266 let (code, message) = match parsed {
267 Ok(e) => {
268 let c = e
269 .type_field_underscore
270 .or(e.type_field)
271 .or_else(|| e.error.as_ref().and_then(|d| d.code.clone()))
272 .unwrap_or_else(|| "UnknownErrorCode".into());
273 let m = e
274 .message
275 .or(e.message_capital)
276 .or_else(|| e.error.as_ref().and_then(|d| d.message.clone()))
277 .unwrap_or_else(|| "Unknown error".into());
278 (c, m)
279 }
280 Err(_) => {
281 return None;
283 }
284 };
285
286 Some(match code.as_str() {
287 "AccessDeniedException"
289 | "NotAuthorized"
290 | "UnrecognizedClientException"
291 | "ExpiredTokenException" => ErrorData::RemoteAccessDenied {
292 resource_type: "Function".into(),
293 resource_name: resource.into(),
294 },
295 "ThrottlingException" | "TooManyRequestsException" => {
297 ErrorData::RateLimitExceeded { message }
298 }
299 "ServiceUnavailable" | "InternalFailure" | "ServiceException" => {
301 ErrorData::RemoteServiceUnavailable { message }
302 }
303 "RequestTimeoutException" => ErrorData::Timeout { message },
305 "ResourceNotFoundException" => ErrorData::RemoteResourceNotFound {
307 resource_type: "Function".into(),
308 resource_name: resource.into(),
309 },
310 "ResourceConflictException" => ErrorData::RemoteResourceConflict {
312 message,
313 resource_type: "Function".into(),
314 resource_name: resource.into(),
315 },
316 "EC2AccessDeniedException" | "EC2ThrottledException" | "EC2UnexpectedException" => {
318 ErrorData::RemoteServiceUnavailable { message }
319 }
320 "EFSIOException"
321 | "EFSMountConnectivityException"
322 | "EFSMountFailureException"
323 | "EFSMountTimeoutException" => ErrorData::RemoteServiceUnavailable { message },
324 "ENILimitReachedException"
325 | "InvalidSubnetIDException"
326 | "InvalidSecurityGroupIDException"
327 | "SubnetIPAddressLimitReachedException" => {
328 ErrorData::RemoteServiceUnavailable { message }
329 }
330 "InvalidParameterValueException"
331 | "InvalidRequestContentException"
332 | "UnsupportedMediaTypeException" => {
333 if message.contains("cannot be assumed")
337 || message.contains("not authorized")
338 || message.contains("does not have permission to access the ECR image")
339 {
340 ErrorData::RemoteServiceUnavailable { message }
341 } else {
342 ErrorData::InvalidInput {
343 message,
344 field_name: None,
345 }
346 }
347 }
348 "InvalidRuntimeException" | "InvalidZipFileException" => {
349 ErrorData::RemoteServiceUnavailable { message }
350 }
351 "KMSAccessDeniedException"
352 | "KMSDisabledException"
353 | "KMSInvalidStateException"
354 | "KMSNotFoundException" => ErrorData::RemoteAccessDenied {
355 resource_type: "KMS Key".into(),
356 resource_name: resource.into(),
357 },
358 "RecursiveInvocationException" => ErrorData::InvalidInput {
359 message,
360 field_name: None,
361 },
362 "RequestTooLargeException" => ErrorData::InvalidInput {
363 message,
364 field_name: None,
365 },
366 "ResourceNotReadyException" => ErrorData::RemoteServiceUnavailable { message },
367 "SnapStartException" | "SnapStartNotReadyException" | "SnapStartTimeoutException" => {
368 ErrorData::RemoteServiceUnavailable { message }
369 }
370 _ => match status {
371 StatusCode::NOT_FOUND => ErrorData::RemoteResourceNotFound {
372 resource_type: "Function".into(),
373 resource_name: resource.into(),
374 },
375 StatusCode::CONFLICT => ErrorData::RemoteResourceConflict {
376 message,
377 resource_type: "Function".into(),
378 resource_name: resource.into(),
379 },
380 StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED => ErrorData::RemoteAccessDenied {
381 resource_type: "Function".into(),
382 resource_name: resource.into(),
383 },
384 StatusCode::TOO_MANY_REQUESTS => ErrorData::RateLimitExceeded { message },
385 StatusCode::SERVICE_UNAVAILABLE
386 | StatusCode::BAD_GATEWAY
387 | StatusCode::GATEWAY_TIMEOUT => ErrorData::RemoteServiceUnavailable { message },
388 _ => ErrorData::HttpResponseError {
389 message: format!("Lambda {operation} failed: {message}"),
390 url: format!("lambda.amazonaws.com"),
391 http_status: status.as_u16(),
392 http_response_text: Some(body.into()),
393 http_request_text: request_body.map(|s| s.to_string()),
394 },
395 },
396 })
397 }
398}
399
400#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
401#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
402impl LambdaApi for LambdaClient {
403 async fn create_function(
404 &self,
405 request: CreateFunctionRequest,
406 ) -> Result<FunctionConfiguration> {
407 let body = serde_json::to_string(&request).into_alien_error().context(
408 ErrorData::SerializationError {
409 message: format!(
410 "Failed to serialize CreateFunctionRequest for function '{}'",
411 request.function_name
412 ),
413 },
414 )?;
415 self.send_json(
416 Method::POST,
417 "/2015-03-31/functions",
418 None,
419 Some(body),
420 "CreateFunction",
421 &request.function_name,
422 )
423 .await
424 }
425
426 async fn create_function_url_config(
427 &self,
428 function_name: &str,
429 request: CreateFunctionUrlConfigRequest,
430 ) -> Result<CreateFunctionUrlConfigResponse> {
431 let body = serde_json::to_string(&request).into_alien_error().context(
432 ErrorData::SerializationError {
433 message: format!(
434 "Failed to serialize CreateFunctionUrlConfigRequest for function '{}'",
435 function_name
436 ),
437 },
438 )?;
439 let path = format!("/2021-10-31/functions/{}/url", function_name);
440 self.send_json(
441 Method::POST,
442 &path,
443 None,
444 Some(body),
445 "CreateFunctionUrlConfig",
446 function_name,
447 )
448 .await
449 }
450
451 async fn add_permission(
452 &self,
453 function_name: &str,
454 request: AddPermissionRequest,
455 ) -> Result<AddPermissionResponse> {
456 let body = serde_json::to_string(&request).into_alien_error().context(
457 ErrorData::SerializationError {
458 message: format!(
459 "Failed to serialize AddPermissionRequest for function '{}'",
460 function_name
461 ),
462 },
463 )?;
464 let path = format!("/2015-03-31/functions/{}/policy", function_name);
465 self.send_json(
466 Method::POST,
467 &path,
468 None,
469 Some(body),
470 "AddPermission",
471 function_name,
472 )
473 .await
474 }
475
476 async fn update_function_code(
477 &self,
478 function_name: &str,
479 request: UpdateFunctionCodeRequest,
480 ) -> Result<FunctionConfiguration> {
481 let body = serde_json::to_string(&request).into_alien_error().context(
482 ErrorData::SerializationError {
483 message: format!(
484 "Failed to serialize UpdateFunctionCodeRequest for function '{}'",
485 function_name
486 ),
487 },
488 )?;
489 let path = format!("/2015-03-31/functions/{}/code", function_name);
490 self.send_json(
491 Method::PUT,
492 &path,
493 None,
494 Some(body),
495 "UpdateFunctionCode",
496 function_name,
497 )
498 .await
499 }
500
501 async fn update_function_configuration(
502 &self,
503 function_name: &str,
504 request: UpdateFunctionConfigurationRequest,
505 ) -> Result<FunctionConfiguration> {
506 let body = serde_json::to_string(&request).into_alien_error().context(
507 ErrorData::SerializationError {
508 message: format!(
509 "Failed to serialize UpdateFunctionConfigurationRequest for function '{}'",
510 function_name
511 ),
512 },
513 )?;
514 let path = format!("/2015-03-31/functions/{}/configuration", function_name);
515 self.send_json(
516 Method::PUT,
517 &path,
518 None,
519 Some(body),
520 "UpdateFunctionConfiguration",
521 function_name,
522 )
523 .await
524 }
525
526 async fn get_function_configuration(
527 &self,
528 function_name: &str,
529 qualifier: Option<String>,
530 ) -> Result<FunctionConfiguration> {
531 let path = format!("/2015-03-31/functions/{}", function_name);
532 let mut qp = Vec::new();
533 if let Some(q) = qualifier {
534 qp.push(("Qualifier", q));
535 }
536 let resp: GetFunctionResponse = self
537 .send_json(
538 Method::GET,
539 &path,
540 Some(qp),
541 None,
542 "GetFunctionConfiguration",
543 function_name,
544 )
545 .await?;
546 Ok(resp.configuration)
547 }
548
549 async fn delete_function_url_config(
550 &self,
551 function_name: &str,
552 qualifier: Option<String>,
553 ) -> Result<()> {
554 let path = format!("/2021-10-31/functions/{}/url", function_name);
555 let mut qp = Vec::new();
556 if let Some(q) = qualifier {
557 qp.push(("Qualifier", q));
558 }
559 self.send_no_body(
560 Method::DELETE,
561 &path,
562 if qp.is_empty() { None } else { Some(qp) },
563 "DeleteFunctionUrlConfig",
564 function_name,
565 )
566 .await
567 }
568
569 async fn get_function_url_config(
570 &self,
571 function_name: &str,
572 qualifier: Option<String>,
573 ) -> Result<FunctionUrlConfig> {
574 let path = format!("/2021-10-31/functions/{}/url", function_name);
575 let mut qp = Vec::new();
576 if let Some(q) = qualifier {
577 qp.push(("Qualifier", q));
578 }
579 self.send_json(
580 Method::GET,
581 &path,
582 if qp.is_empty() { None } else { Some(qp) },
583 None,
584 "GetFunctionUrlConfig",
585 function_name,
586 )
587 .await
588 }
589
590 async fn delete_function(&self, function_name: &str, qualifier: Option<String>) -> Result<()> {
591 let path = format!("/2015-03-31/functions/{}", function_name);
592 let mut qp = Vec::new();
593 if let Some(q) = qualifier {
594 qp.push(("Qualifier", q));
595 }
596 self.send_no_body(
597 Method::DELETE,
598 &path,
599 if qp.is_empty() { None } else { Some(qp) },
600 "DeleteFunction",
601 function_name,
602 )
603 .await
604 }
605
606 async fn get_policy(
607 &self,
608 function_name: &str,
609 qualifier: Option<String>,
610 ) -> Result<GetPolicyResponse> {
611 let path = format!("/2015-03-31/functions/{}/policy", function_name);
612 let mut qp = Vec::new();
613 if let Some(q) = qualifier {
614 qp.push(("Qualifier", q));
615 }
616 self.send_json(
617 Method::GET,
618 &path,
619 if qp.is_empty() { None } else { Some(qp) },
620 None,
621 "GetPolicy",
622 function_name,
623 )
624 .await
625 }
626
627 async fn invoke(&self, request: InvokeRequest) -> Result<InvokeResponse> {
628 self.credentials.ensure_fresh().await?;
629 let function_name = &request.function_name;
630 let path = format!("/2015-03-31/functions/{}/invocations", function_name);
631
632 let base_url = self.get_base_url();
634 let mut url = format!("{}{}", base_url.trim_end_matches('/'), path);
635
636 if let Some(ref qualifier) = request.qualifier {
637 url.push('?');
638 url.push_str(&format!(
639 "Qualifier={}",
640 form_urlencoded::byte_serialize(qualifier.as_bytes()).collect::<String>()
641 ));
642 }
643
644 let mut builder = self.client.request(Method::POST, &url).host(&format!(
645 "lambda.{}.amazonaws.com",
646 self.credentials.region()
647 ));
648
649 match request.invocation_type {
651 InvocationType::RequestResponse => {
652 builder = builder.header("X-Amz-Invocation-Type", "RequestResponse");
653 }
654 InvocationType::Event => {
655 builder = builder.header("X-Amz-Invocation-Type", "Event");
656 }
657 InvocationType::DryRun => {
658 builder = builder.header("X-Amz-Invocation-Type", "DryRun");
659 }
660 }
661
662 if let Some(ref log_type) = request.log_type {
664 builder = builder.header("X-Amz-Log-Type", log_type);
665 }
666
667 if let Some(ref client_context) = request.client_context {
669 builder = builder.header("X-Amz-Client-Context", client_context);
670 }
671
672 builder = builder.content_sha256_bytes(&request.payload);
674 if !request.payload.is_empty() {
675 builder = builder.body(reqwest::Body::from(request.payload.clone()));
676 }
677
678 let signed_builder = builder.sign_aws_request(&self.sign_config())?;
679 let response = signed_builder.send().await.into_alien_error().context(
680 ErrorData::HttpRequestFailed {
681 message: format!("Failed to invoke Lambda function '{}'", function_name),
682 },
683 )?;
684
685 let status = response.status().as_u16();
686 let headers = response.headers().clone();
687 let payload =
688 response
689 .bytes()
690 .await
691 .into_alien_error()
692 .context(ErrorData::HttpRequestFailed {
693 message: format!(
694 "Failed to read invoke response body for function '{}'",
695 function_name
696 ),
697 })?;
698
699 let function_error = headers
701 .get("X-Amz-Function-Error")
702 .and_then(|v| v.to_str().ok())
703 .map(|s| s.to_string());
704
705 let log_result = headers
706 .get("X-Amz-Log-Result")
707 .and_then(|v| v.to_str().ok())
708 .map(|s| s.to_string());
709
710 let executed_version = headers
711 .get("X-Amz-Executed-Version")
712 .and_then(|v| v.to_str().ok())
713 .map(|s| s.to_string());
714
715 Ok(InvokeResponse {
716 status_code: status,
717 function_error,
718 log_result,
719 payload: payload.to_vec(),
720 executed_version,
721 })
722 }
723
724 async fn create_event_source_mapping(
725 &self,
726 request: CreateEventSourceMappingRequest,
727 ) -> Result<EventSourceMapping> {
728 let body = serde_json::to_string(&request).into_alien_error().context(
729 ErrorData::SerializationError {
730 message: format!(
731 "Failed to serialize CreateEventSourceMappingRequest for function '{}'",
732 request.function_name
733 ),
734 },
735 )?;
736 self.send_json(
737 Method::POST,
738 "/2015-03-31/event-source-mappings",
739 None,
740 Some(body),
741 "CreateEventSourceMapping",
742 &request.function_name,
743 )
744 .await
745 }
746
747 async fn get_event_source_mapping(&self, uuid: &str) -> Result<EventSourceMapping> {
748 let path = format!("/2015-03-31/event-source-mappings/{}", uuid);
749 self.send_json(
750 Method::GET,
751 &path,
752 None,
753 None,
754 "GetEventSourceMapping",
755 uuid,
756 )
757 .await
758 }
759
760 async fn update_event_source_mapping(
761 &self,
762 uuid: &str,
763 request: UpdateEventSourceMappingRequest,
764 ) -> Result<EventSourceMapping> {
765 let body = serde_json::to_string(&request).into_alien_error().context(
766 ErrorData::SerializationError {
767 message: format!(
768 "Failed to serialize UpdateEventSourceMappingRequest for UUID '{}'",
769 uuid
770 ),
771 },
772 )?;
773 let path = format!("/2015-03-31/event-source-mappings/{}", uuid);
774 self.send_json(
775 Method::PUT,
776 &path,
777 None,
778 Some(body),
779 "UpdateEventSourceMapping",
780 uuid,
781 )
782 .await
783 }
784
785 async fn delete_event_source_mapping(&self, uuid: &str) -> Result<EventSourceMapping> {
786 let path = format!("/2015-03-31/event-source-mappings/{}", uuid);
787 self.send_json(
788 Method::DELETE,
789 &path,
790 None,
791 None,
792 "DeleteEventSourceMapping",
793 uuid,
794 )
795 .await
796 }
797
798 async fn list_event_source_mappings(
799 &self,
800 request: ListEventSourceMappingsRequest,
801 ) -> Result<ListEventSourceMappingsResponse> {
802 let mut qp = Vec::new();
803 if let Some(ref arn) = request.event_source_arn {
804 qp.push(("EventSourceArn", arn.clone()));
805 }
806 if let Some(ref func) = request.function_name {
807 qp.push(("FunctionName", func.clone()));
808 }
809 if let Some(ref marker) = request.marker {
810 qp.push(("Marker", marker.clone()));
811 }
812 if let Some(max_items) = request.max_items {
813 qp.push(("MaxItems", max_items.to_string()));
814 }
815
816 let resource_name = request.function_name.as_deref().unwrap_or("unknown");
817 self.send_json(
818 Method::GET,
819 "/2015-03-31/event-source-mappings",
820 if qp.is_empty() { None } else { Some(qp) },
821 None,
822 "ListEventSourceMappings",
823 resource_name,
824 )
825 .await
826 }
827
828 async fn put_function_concurrency(
829 &self,
830 function_name: &str,
831 reserved_concurrent_executions: u32,
832 ) -> Result<()> {
833 let body =
834 serde_json::json!({ "ReservedConcurrentExecutions": reserved_concurrent_executions })
835 .to_string();
836 let path = format!("/2017-10-31/functions/{}/concurrency", function_name);
837 let _: serde_json::Value = self
838 .send_json(
839 Method::PUT,
840 &path,
841 None,
842 Some(body),
843 "PutFunctionConcurrency",
844 function_name,
845 )
846 .await?;
847 Ok(())
848 }
849
850 async fn delete_function_concurrency(&self, function_name: &str) -> Result<()> {
851 let path = format!("/2017-10-31/functions/{}/concurrency", function_name);
852 self.send_no_body(
853 Method::DELETE,
854 &path,
855 None,
856 "DeleteFunctionConcurrency",
857 function_name,
858 )
859 .await
860 }
861}
862
863#[derive(Debug, Deserialize)]
867struct LambdaErrorResponse {
868 #[serde(rename = "Type")]
869 type_field: Option<String>,
870 #[serde(rename = "__type")]
871 type_field_underscore: Option<String>,
872 #[serde(rename = "message")]
873 message: Option<String>,
874 #[serde(rename = "Message")]
875 message_capital: Option<String>,
876 #[serde(rename = "Error")]
877 error: Option<LambdaErrorDetails>,
878}
879
880#[derive(Debug, Deserialize)]
881struct LambdaErrorDetails {
882 #[serde(rename = "Code")]
883 code: Option<String>,
884 #[serde(rename = "Message")]
885 message: Option<String>,
886}
887
888#[derive(Debug, Clone, Serialize, Builder)]
893#[serde(rename_all = "PascalCase")]
894pub struct CreateFunctionRequest {
895 pub function_name: String,
896 pub role: String,
897 pub code: FunctionCode,
898 #[builder(default = "Image".to_string())]
899 pub package_type: String,
900 #[serde(skip_serializing_if = "Option::is_none")]
901 pub description: Option<String>,
902 #[serde(skip_serializing_if = "Option::is_none")]
903 pub timeout: Option<i32>,
904 #[serde(skip_serializing_if = "Option::is_none")]
905 pub memory_size: Option<i32>,
906 #[serde(skip_serializing_if = "Option::is_none")]
907 pub publish: Option<bool>,
908 #[serde(skip_serializing_if = "Option::is_none")]
909 pub environment: Option<Environment>,
910 #[serde(skip_serializing_if = "Option::is_none")]
911 pub architectures: Option<Vec<String>>,
912 #[serde(skip_serializing_if = "Option::is_none")]
913 pub tracing_config: Option<TracingConfig>,
914 #[serde(skip_serializing_if = "Option::is_none")]
915 pub tags: Option<std::collections::HashMap<String, String>>,
916 #[serde(skip_serializing_if = "Option::is_none")]
917 pub ephemeral_storage: Option<EphemeralStorage>,
918 #[serde(skip_serializing_if = "Option::is_none", rename = "KMSKeyArn")]
919 pub kms_key_arn: Option<String>,
920 #[serde(skip_serializing_if = "Option::is_none")]
922 pub vpc_config: Option<VpcConfig>,
923}
924
925#[derive(Debug, Clone, Serialize, Builder)]
926#[serde(rename_all = "PascalCase")]
927pub struct FunctionCode {
928 pub image_uri: Option<String>,
929}
930
931#[derive(Debug, Clone, Serialize, Builder)]
932#[serde(rename_all = "PascalCase")]
933pub struct Environment {
934 pub variables: Option<std::collections::HashMap<String, String>>,
935}
936
937#[derive(Debug, Clone, Deserialize)]
938#[serde(rename_all = "PascalCase")]
939pub struct FunctionConfiguration {
940 pub function_name: Option<String>,
941 pub function_arn: Option<String>,
942 pub state: Option<String>,
943 pub last_update_status: Option<String>,
944 #[serde(rename = "KMSKeyArn")]
945 pub kms_key_arn: Option<String>,
946}
947
948#[derive(Debug, Deserialize)]
949#[serde(rename_all = "PascalCase")]
950pub struct GetFunctionResponse {
951 pub configuration: FunctionConfiguration,
952}
953
954#[derive(Debug, Clone, Serialize, Builder)]
955#[serde(rename_all = "PascalCase")]
956pub struct CreateFunctionUrlConfigRequest {
957 pub auth_type: String,
958 pub cors: Option<Cors>,
959 pub invoke_mode: Option<String>,
960}
961
962#[derive(Debug, Deserialize)]
963#[serde(rename_all = "PascalCase")]
964pub struct CreateFunctionUrlConfigResponse {
965 pub function_url: String,
966 pub function_arn: String,
967 pub auth_type: String,
968}
969
970#[derive(Debug, Clone, Deserialize)]
971#[serde(rename_all = "PascalCase")]
972pub struct FunctionUrlConfig {
973 pub function_url: String,
974 pub auth_type: String,
975 pub cors: Option<Cors>,
976}
977
978#[derive(Debug, Serialize, Builder)]
979#[serde(rename_all = "PascalCase")]
980pub struct AddPermissionRequest {
981 pub statement_id: String,
982 pub action: String,
983 pub principal: String,
984 #[serde(skip_serializing_if = "Option::is_none")]
985 pub function_url_auth_type: Option<String>,
986 #[serde(skip_serializing_if = "Option::is_none")]
987 pub source_arn: Option<String>,
988 #[serde(skip_serializing_if = "Option::is_none")]
989 pub source_account: Option<String>,
990}
991
992#[derive(Debug, Deserialize)]
993#[serde(rename_all = "PascalCase")]
994pub struct AddPermissionResponse {
995 pub statement: Option<String>,
996}
997
998#[derive(Debug, Serialize, Builder)]
999#[serde(rename_all = "PascalCase")]
1000pub struct UpdateFunctionCodeRequest {
1001 pub image_uri: String,
1002 pub publish: Option<bool>,
1003}
1004
1005#[derive(Debug, Serialize, Builder)]
1006#[serde(rename_all = "PascalCase")]
1007pub struct UpdateFunctionConfigurationRequest {
1008 #[serde(skip_serializing_if = "Option::is_none")]
1009 pub role: Option<String>,
1010 #[serde(skip_serializing_if = "Option::is_none")]
1011 pub timeout: Option<i32>,
1012 #[serde(skip_serializing_if = "Option::is_none")]
1013 pub memory_size: Option<i32>,
1014 #[serde(skip_serializing_if = "Option::is_none")]
1015 pub environment: Option<Environment>,
1016 #[serde(skip_serializing_if = "Option::is_none")]
1018 pub vpc_config: Option<VpcConfig>,
1019}
1020
1021#[derive(Debug, Deserialize)]
1022#[serde(rename_all = "PascalCase")]
1023pub struct GetPolicyResponse {
1024 pub policy: Option<String>,
1025}
1026
1027#[derive(Debug, Clone, Serialize, Builder)]
1032#[serde(rename_all = "PascalCase")]
1033pub struct TracingConfig {
1034 pub mode: Option<String>,
1035}
1036
1037#[derive(Debug, Clone, Serialize, Builder)]
1038#[serde(rename_all = "PascalCase")]
1039pub struct EphemeralStorage {
1040 pub size: i32,
1041}
1042
1043#[derive(Debug, Clone, Serialize, Builder)]
1048#[serde(rename_all = "PascalCase")]
1049pub struct VpcConfig {
1050 #[serde(skip_serializing_if = "Option::is_none")]
1053 pub subnet_ids: Option<Vec<String>>,
1054
1055 #[serde(skip_serializing_if = "Option::is_none")]
1057 pub security_group_ids: Option<Vec<String>>,
1058}
1059
1060#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
1061#[serde(rename_all = "PascalCase")]
1062pub struct Cors {
1063 #[serde(skip_serializing_if = "Option::is_none")]
1064 pub allow_credentials: Option<bool>,
1065 #[serde(skip_serializing_if = "Option::is_none")]
1066 pub allow_headers: Option<Vec<String>>,
1067 #[serde(skip_serializing_if = "Option::is_none")]
1068 pub allow_methods: Option<Vec<String>>,
1069 #[serde(skip_serializing_if = "Option::is_none")]
1070 pub allow_origins: Option<Vec<String>>,
1071 #[serde(skip_serializing_if = "Option::is_none")]
1072 pub max_age: Option<i32>,
1073}
1074
1075#[derive(Debug, Clone, Serialize, Builder)]
1081#[serde(rename_all = "PascalCase")]
1082pub struct CreateEventSourceMappingRequest {
1083 pub event_source_arn: String,
1085
1086 pub function_name: String,
1088
1089 #[serde(skip_serializing_if = "Option::is_none")]
1091 pub batch_size: Option<i32>,
1092
1093 #[serde(skip_serializing_if = "Option::is_none")]
1095 pub enabled: Option<bool>,
1096
1097 #[serde(skip_serializing_if = "Option::is_none")]
1099 pub maximum_batching_window_in_seconds: Option<i32>,
1100
1101 #[serde(skip_serializing_if = "Option::is_none")]
1103 pub function_response_types: Option<Vec<String>>,
1104
1105 #[serde(skip_serializing_if = "Option::is_none")]
1107 pub filter_criteria: Option<FilterCriteria>,
1108
1109 #[serde(skip_serializing_if = "Option::is_none")]
1111 pub scaling_config: Option<ScalingConfig>,
1112}
1113
1114#[derive(Debug, Clone, Serialize, Builder)]
1116#[serde(rename_all = "PascalCase")]
1117pub struct UpdateEventSourceMappingRequest {
1118 #[serde(skip_serializing_if = "Option::is_none")]
1120 pub batch_size: Option<i32>,
1121
1122 #[serde(skip_serializing_if = "Option::is_none")]
1124 pub enabled: Option<bool>,
1125
1126 #[serde(skip_serializing_if = "Option::is_none")]
1128 pub function_name: Option<String>,
1129
1130 #[serde(skip_serializing_if = "Option::is_none")]
1132 pub maximum_batching_window_in_seconds: Option<i32>,
1133
1134 #[serde(skip_serializing_if = "Option::is_none")]
1136 pub function_response_types: Option<Vec<String>>,
1137
1138 #[serde(skip_serializing_if = "Option::is_none")]
1140 pub filter_criteria: Option<FilterCriteria>,
1141
1142 #[serde(skip_serializing_if = "Option::is_none")]
1144 pub scaling_config: Option<ScalingConfig>,
1145}
1146
1147#[derive(Debug, Clone, Serialize, Builder)]
1149#[serde(rename_all = "PascalCase")]
1150pub struct ListEventSourceMappingsRequest {
1151 #[serde(skip_serializing_if = "Option::is_none")]
1153 pub event_source_arn: Option<String>,
1154
1155 #[serde(skip_serializing_if = "Option::is_none")]
1157 pub function_name: Option<String>,
1158
1159 #[serde(skip_serializing_if = "Option::is_none")]
1161 pub marker: Option<String>,
1162
1163 #[serde(skip_serializing_if = "Option::is_none")]
1165 pub max_items: Option<i32>,
1166}
1167
1168#[derive(Debug, Deserialize)]
1170#[serde(rename_all = "PascalCase")]
1171pub struct ListEventSourceMappingsResponse {
1172 pub event_source_mappings: Option<Vec<EventSourceMapping>>,
1174
1175 pub next_marker: Option<String>,
1177}
1178
1179#[derive(Debug, Clone, Deserialize)]
1181#[serde(rename_all = "PascalCase")]
1182pub struct EventSourceMapping {
1183 #[serde(rename = "UUID")]
1185 pub uuid: Option<String>,
1186
1187 pub event_source_arn: Option<String>,
1189
1190 pub function_arn: Option<String>,
1192
1193 pub batch_size: Option<i32>,
1195
1196 pub last_modified: Option<f64>,
1198
1199 pub last_processing_result: Option<String>,
1201
1202 pub state: Option<String>,
1204
1205 pub state_transition_reason: Option<String>,
1207
1208 pub maximum_batching_window_in_seconds: Option<i32>,
1210
1211 pub function_response_types: Option<Vec<String>>,
1213
1214 pub filter_criteria: Option<FilterCriteria>,
1216
1217 pub scaling_config: Option<ScalingConfig>,
1219}
1220
1221#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
1223#[serde(rename_all = "PascalCase")]
1224pub struct FilterCriteria {
1225 #[serde(skip_serializing_if = "Option::is_none")]
1227 pub filters: Option<Vec<Filter>>,
1228}
1229
1230#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
1232#[serde(rename_all = "PascalCase")]
1233pub struct Filter {
1234 #[serde(skip_serializing_if = "Option::is_none")]
1236 pub pattern: Option<String>,
1237}
1238
1239#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
1241#[serde(rename_all = "PascalCase")]
1242pub struct ScalingConfig {
1243 #[serde(skip_serializing_if = "Option::is_none")]
1245 pub maximum_concurrency: Option<i32>,
1246}
1247
1248#[derive(Debug, Clone)]
1254pub enum InvocationType {
1255 RequestResponse,
1257 Event,
1259 DryRun,
1261}
1262
1263impl Default for InvocationType {
1264 fn default() -> Self {
1265 InvocationType::RequestResponse
1266 }
1267}
1268
1269#[derive(Debug, Clone, Builder)]
1271pub struct InvokeRequest {
1272 pub function_name: String,
1274 #[builder(default)]
1276 pub invocation_type: InvocationType,
1277 pub qualifier: Option<String>,
1279 pub client_context: Option<String>,
1281 pub log_type: Option<String>,
1283 #[builder(default)]
1285 pub payload: Vec<u8>,
1286}
1287
1288#[derive(Debug, Clone)]
1290pub struct InvokeResponse {
1291 pub status_code: u16,
1293 pub function_error: Option<String>,
1295 pub log_result: Option<String>,
1297 pub payload: Vec<u8>,
1299 pub executed_version: Option<String>,
1301}