momento_functions_host/aws/lambda.rs
1//! Host interfaces for working with AWS Lambda
2use momento_functions_wit::host::momento::host;
3
4use crate::{
5 FunctionResult,
6 encoding::{Encode, Extract},
7};
8
9use super::auth;
10
11/// Lambda client for host interfaces.
12///
13/// This client uses Momento's host-provided AWS communication channel, which
14/// is kept hot at all times. When your Function has not run in several days or more,
15/// the channel is still hot and ready, keeping your Function invocations predictable
16/// even when your demand is unpredictable.
17pub struct LambdaClient {
18 client: host::aws_lambda::Client,
19}
20
21impl LambdaClient {
22 /// Create a new Lambda client.
23 ///
24 /// ```rust
25 /// # use momento_functions_host::aws::auth::AwsCredentialsProvider;
26 /// # use momento_functions_host::aws::lambda::LambdaClient;
27 /// # use momento_functions_host::build_environment_aws_credentials;
28 /// # use momento_functions_host::FunctionResult;
29 /// # fn f() -> FunctionResult<()> {
30 /// let client = LambdaClient::new(
31 /// &AwsCredentialsProvider::new(
32 /// "us-east-1",
33 /// build_environment_aws_credentials!()
34 /// )?
35 /// );
36 /// # Ok(())
37 /// # }
38 /// ```
39 pub fn new(credentials: &auth::AwsCredentialsProvider) -> Self {
40 Self {
41 client: host::aws_lambda::Client::new(credentials.resource()),
42 }
43 }
44
45 /// Invoke a lambda function.
46 ///
47 /// You can use strings, bytes, or structs that are Serializable.
48 ///
49 /// Examples:
50 /// ________
51 /// ```rust
52 /// use momento_functions_host::aws::lambda::LambdaClient;
53 /// use momento_functions_host::{FunctionResult, Error};
54 /// use momento_functions_host::encoding::Json;;
55 ///
56 /// # fn f(client: &LambdaClient) -> FunctionResult<()> {
57 /// // With a payload
58 /// client.invoke(
59 /// "my_lambda_function",
60 /// "hello world",
61 /// )?;
62 ///
63 /// // With a payload and a qualifier
64 /// client.invoke(
65 /// ("my_lambda_function", "v1"),
66 /// "hello world",
67 /// )?;
68 ///
69 /// // Without a payload
70 /// client.invoke(
71 /// "my_lambda_function",
72 /// (),
73 /// )?;
74 ///
75 /// // With literal bytes
76 /// client.invoke(
77 /// "my_lambda_function",
78 /// b"some literal bytes".to_vec(),
79 /// )?;
80 /// # Ok(())}
81 /// ```
82 /// ________
83 /// With json-encoded payloads
84 /// ```rust
85 /// use momento_functions_host::aws::lambda::LambdaClient;
86 /// use momento_functions_host::{FunctionResult, Error};
87 /// use momento_functions_host::encoding::Json;
88 ///
89 /// #[derive(serde::Serialize)]
90 /// struct MyStruct {
91 /// hello: String
92 /// }
93 /// #[derive(serde::Deserialize)]
94 /// struct Reply {
95 /// message: String
96 /// }
97 ///
98 /// # fn f(client: &LambdaClient) -> FunctionResult<()> {
99 /// // Just a request payload, encoded as JSON
100 /// client.invoke(
101 /// "my_lambda_function",
102 /// Json(MyStruct { hello: "hello".to_string() }),
103 /// )?;
104 ///
105 /// // Request and response payload, both encoded as JSON
106 /// let Json(reply): Json<Reply> = client.invoke(
107 /// "my_lambda_function",
108 /// Json(MyStruct { hello: "hello".to_string() }),
109 /// )?
110 /// .extract()?;
111 ///
112 /// let message = reply.message;
113 /// # Ok(())}
114 /// ```
115 pub fn invoke(
116 &self,
117 name: impl Into<LambdaName>,
118 payload: impl Encode,
119 ) -> FunctionResult<InvokeResponse> {
120 let (function_name, qualifier) = name.into().into_inner();
121 let request = host::aws_lambda::InvokeRequest {
122 function_name,
123 qualifier,
124 payload: Some(payload.try_serialize()?.into()),
125 invocation_type: host::aws_lambda::InvocationType::RequestResponse(
126 host::aws_lambda::InvokeSynchronousParameters {
127 log_type: None,
128 client_context: None,
129 },
130 ),
131 };
132 let output = self.client.invoke(&request)?;
133
134 Ok(InvokeResponse {
135 status_code: output.status_code,
136 payload: output.payload,
137 })
138 }
139}
140
141/// Result from Lambda
142pub struct InvokeResponse {
143 /// The status code of the response
144 status_code: i32,
145 /// The payload of the response
146 payload: Option<Vec<u8>>,
147}
148impl InvokeResponse {
149 /// Get the status code of the response
150 pub fn status_code(&self) -> i32 {
151 self.status_code
152 }
153
154 /// Take the payload of the response
155 ///
156 /// This consumes the payload; if you call it again, it will return None.
157 pub fn take_payload(&mut self) -> Option<Vec<u8>> {
158 self.payload.take()
159 }
160
161 /// Take the payload of the response and decode it.
162 ///
163 /// This consumes the payload; if you call it again, it will return an Error.
164 pub fn extract<E: Extract>(&mut self) -> FunctionResult<E> {
165 let payload = self
166 .take_payload()
167 .ok_or_else(|| crate::Error::MessageError("no payload in response".to_string()))?;
168 E::extract(payload)
169 }
170}
171
172/// Identifier for a Lambda function
173pub enum LambdaName {
174 /// Lambda function name
175 Name(String),
176 /// Lambda function ARN
177 Qualified {
178 /// Name of the lambda function
179 name: String,
180 /// Version or alias of the lambda function
181 qualifier: String,
182 },
183}
184impl LambdaName {
185 fn into_inner(self) -> (String, Option<String>) {
186 match self {
187 LambdaName::Name(name) => (name, None),
188 LambdaName::Qualified { name, qualifier } => (name, Some(qualifier)),
189 }
190 }
191}
192impl From<String> for LambdaName {
193 fn from(name: String) -> Self {
194 LambdaName::Name(name)
195 }
196}
197impl From<&str> for LambdaName {
198 fn from(name: &str) -> Self {
199 LambdaName::Name(name.to_string())
200 }
201}
202impl From<(String, String)> for LambdaName {
203 fn from((name, qualifier): (String, String)) -> Self {
204 LambdaName::Qualified { name, qualifier }
205 }
206}
207impl From<(&str, String)> for LambdaName {
208 fn from((name, qualifier): (&str, String)) -> Self {
209 LambdaName::Qualified {
210 name: name.to_string(),
211 qualifier,
212 }
213 }
214}
215impl From<(String, &str)> for LambdaName {
216 fn from((name, qualifier): (String, &str)) -> Self {
217 LambdaName::Qualified {
218 name,
219 qualifier: qualifier.to_string(),
220 }
221 }
222}
223impl From<(&str, &str)> for LambdaName {
224 fn from((name, qualifier): (&str, &str)) -> Self {
225 LambdaName::Qualified {
226 name: name.to_string(),
227 qualifier: qualifier.to_string(),
228 }
229 }
230}
231
232impl From<host::aws_lambda::LambdaError> for crate::Error {
233 fn from(e: host::aws_lambda::LambdaError) -> Self {
234 match e {
235 host::aws_lambda::LambdaError::Unauthorized(u) => Self::MessageError(u),
236 host::aws_lambda::LambdaError::Malformed(s) => Self::MessageError(s),
237 host::aws_lambda::LambdaError::Other(o) => Self::MessageError(o),
238 }
239 }
240}