aws_smithy_http_server_python/
lambda.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Python wrappers for Lambda related types.
7
8use std::collections::HashMap;
9
10use lambda_http::Context;
11use pyo3::pyclass;
12
13/// AWS Mobile SDK client fields.
14#[pyclass(name = "ClientApplication")]
15#[derive(Clone)]
16pub struct PyClientApplication {
17    /// The mobile app installation id
18    ///
19    /// :type str:
20    #[pyo3(get)]
21    installation_id: String,
22
23    /// The app title for the mobile app as registered with AWS' mobile services.
24    ///
25    /// :type str:
26    #[pyo3(get)]
27    app_title: String,
28
29    /// The version name of the application as registered with AWS' mobile services.
30    ///
31    /// :type str:
32    #[pyo3(get)]
33    app_version_name: String,
34
35    /// The app version code.
36    ///
37    /// :type str:
38    #[pyo3(get)]
39    app_version_code: String,
40
41    /// The package name for the mobile application invoking the function
42    ///
43    /// :type str:
44    #[pyo3(get)]
45    app_package_name: String,
46}
47
48/// Client context sent by the AWS Mobile SDK.
49#[pyclass(name = "ClientContext")]
50#[derive(Clone)]
51pub struct PyClientContext {
52    /// Information about the mobile application invoking the function.
53    ///
54    /// :type ClientApplication:
55    #[pyo3(get)]
56    client: PyClientApplication,
57
58    /// Custom properties attached to the mobile event context.
59    ///
60    /// :type typing.Dict[str, str]:
61    #[pyo3(get)]
62    custom: HashMap<String, String>,
63
64    /// Environment settings from the mobile client.
65    ///
66    /// :type typing.Dict[str, str]:
67    #[pyo3(get)]
68    environment: HashMap<String, String>,
69}
70
71/// Cognito identity information sent with the event
72#[pyclass(name = "CognitoIdentity")]
73#[derive(Clone)]
74pub struct PyCognitoIdentity {
75    /// The unique identity id for the Cognito credentials invoking the function.
76    ///
77    /// :type str:
78    #[pyo3(get)]
79    identity_id: String,
80
81    /// The identity pool id the caller is "registered" with.
82    ///
83    /// :type str:
84    #[pyo3(get)]
85    identity_pool_id: String,
86}
87
88/// Configuration derived from environment variables.
89#[pyclass(name = "Config")]
90#[derive(Clone)]
91pub struct PyConfig {
92    /// The name of the function.
93    ///
94    /// :type str:
95    #[pyo3(get)]
96    function_name: String,
97
98    /// The amount of memory available to the function in MB.
99    ///
100    /// :type int:
101    #[pyo3(get)]
102    memory: i32,
103
104    /// The version of the function being executed.
105    ///
106    /// :type str:
107    #[pyo3(get)]
108    version: String,
109
110    /// The name of the Amazon CloudWatch Logs stream for the function.
111    ///
112    /// :type str:
113    #[pyo3(get)]
114    log_stream: String,
115
116    /// The name of the Amazon CloudWatch Logs group for the function.
117    ///
118    /// :type str:
119    #[pyo3(get)]
120    log_group: String,
121}
122
123/// The Lambda function execution context. The values in this struct
124/// are populated using the [Lambda environment variables](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html)
125/// and the headers returned by the poll request to the Runtime APIs.
126#[derive(Clone)]
127#[pyclass(name = "LambdaContext")]
128pub struct PyLambdaContext {
129    /// The AWS request ID generated by the Lambda service.
130    ///
131    /// :type str:
132    #[pyo3(get)]
133    request_id: String,
134
135    /// The execution deadline for the current invocation in milliseconds.
136    ///
137    /// :type int:
138    #[pyo3(get)]
139    deadline: u64,
140
141    /// The ARN of the Lambda function being invoked.
142    ///
143    /// :type str:
144    #[pyo3(get)]
145    invoked_function_arn: String,
146
147    /// The X-Ray trace ID for the current invocation.
148    ///
149    /// :type typing.Optional\[str\]:
150    #[pyo3(get)]
151    xray_trace_id: Option<String>,
152
153    /// The client context object sent by the AWS mobile SDK. This field is
154    /// empty unless the function is invoked using an AWS mobile SDK.
155    ///
156    /// :type typing.Optional\[ClientContext\]:
157    #[pyo3(get)]
158    client_context: Option<PyClientContext>,
159
160    /// The Cognito identity that invoked the function. This field is empty
161    /// unless the invocation request to the Lambda APIs was made using AWS
162    /// credentials issues by Amazon Cognito Identity Pools.
163    ///
164    /// :type typing.Optional\[CognitoIdentity\]:
165    #[pyo3(get)]
166    identity: Option<PyCognitoIdentity>,
167
168    /// Lambda function configuration from the local environment variables.
169    /// Includes information such as the function name, memory allocation,
170    /// version, and log streams.
171    ///
172    /// :type Config:
173    #[pyo3(get)]
174    env_config: PyConfig,
175}
176
177impl PyLambdaContext {
178    /// Create Python-compatible version of [Context].
179    pub fn new(ctx: Context) -> Self {
180        Self {
181            request_id: ctx.request_id,
182            deadline: ctx.deadline,
183            invoked_function_arn: ctx.invoked_function_arn,
184            xray_trace_id: ctx.xray_trace_id,
185            client_context: ctx.client_context.map(|client_ctx| PyClientContext {
186                client: PyClientApplication {
187                    installation_id: client_ctx.client.installation_id,
188                    app_title: client_ctx.client.app_title,
189                    app_version_name: client_ctx.client.app_version_name,
190                    app_version_code: client_ctx.client.app_version_code,
191                    app_package_name: client_ctx.client.app_package_name,
192                },
193                custom: client_ctx.custom,
194                environment: client_ctx.environment,
195            }),
196            identity: ctx.identity.map(|identity| PyCognitoIdentity {
197                identity_id: identity.identity_id,
198                identity_pool_id: identity.identity_pool_id,
199            }),
200            env_config: PyConfig {
201                function_name: ctx.env_config.function_name,
202                memory: ctx.env_config.memory,
203                version: ctx.env_config.version,
204                log_stream: ctx.env_config.log_stream,
205                log_group: ctx.env_config.log_group,
206            },
207        }
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use http::{header::HeaderName, HeaderMap, HeaderValue};
214    use lambda_http::lambda_runtime::{Config, Context};
215    use pyo3::{prelude::*, py_run};
216
217    use super::*;
218
219    #[test]
220    fn py_lambda_context() -> PyResult<()> {
221        pyo3::prepare_freethreaded_python();
222
223        let headers = HeaderMap::from_iter([
224            (
225                HeaderName::from_static("lambda-runtime-aws-request-id"),
226                HeaderValue::from_static("my-id"),
227            ),
228            (
229                HeaderName::from_static("lambda-runtime-deadline-ms"),
230                HeaderValue::from_static("123"),
231            ),
232            (
233                HeaderName::from_static("lambda-runtime-invoked-function-arn"),
234                HeaderValue::from_static("arn::myarn"),
235            ),
236            (
237                HeaderName::from_static("lambda-runtime-trace-id"),
238                HeaderValue::from_static("my-trace-id"),
239            ),
240            (
241                HeaderName::from_static("lambda-runtime-client-context"),
242                HeaderValue::from_str(
243                    &r#"
244{
245    "client": {
246        "installationId": "my-installation-id",
247        "appTitle": "my-app-title",
248        "appVersionName": "my-app-version-name",
249        "appVersionCode": "my-app-version-code",
250        "appPackageName": "my-app-package-name"
251    },
252    "custom": {
253        "custom-key": "custom-val"
254    },
255    "environment": {
256        "environment-key": "environment-val"
257    }
258}
259"#
260                    .split_whitespace()
261                    .collect::<String>(),
262                )
263                .unwrap(),
264            ),
265            (
266                HeaderName::from_static("lambda-runtime-cognito-identity"),
267                HeaderValue::from_str(
268                    &r#"
269{
270    "identity_id": "my-identity-id",
271    "identity_pool_id": "my-identity-pool-id"
272}
273"#
274                    .split_whitespace()
275                    .collect::<String>(),
276                )
277                .unwrap(),
278            ),
279        ]);
280        let lambda_context = Context::try_from(headers).unwrap();
281        let lambda_context = lambda_context.with_config(&Config {
282            function_name: "my-fn".to_string(),
283            memory: 128,
284            version: "my-version".to_string(),
285            log_stream: "my-log-stream".to_string(),
286            log_group: "my-log-group".to_string(),
287        });
288
289        Python::with_gil(|py| {
290            let ctx = PyCell::new(py, PyLambdaContext::new(lambda_context))?;
291            py_run!(
292                py,
293                ctx,
294                r#"
295assert ctx.request_id == "my-id"
296assert ctx.deadline == 123
297assert ctx.invoked_function_arn == "arn::myarn"
298assert ctx.xray_trace_id == "my-trace-id"
299
300assert ctx.client_context.client.installation_id == "my-installation-id"
301assert ctx.client_context.client.app_title == "my-app-title"
302assert ctx.client_context.client.app_version_name == "my-app-version-name"
303assert ctx.client_context.client.app_version_code == "my-app-version-code"
304assert ctx.client_context.client.app_package_name == "my-app-package-name"
305assert ctx.client_context.custom == {"custom-key":"custom-val"}
306assert ctx.client_context.environment == {"environment-key":"environment-val"}
307
308assert ctx.identity.identity_id == "my-identity-id"
309assert ctx.identity.identity_pool_id == "my-identity-pool-id"
310
311assert ctx.env_config.function_name == "my-fn"
312assert ctx.env_config.memory == 128
313assert ctx.env_config.version == "my-version"
314assert ctx.env_config.log_stream == "my-log-stream"
315assert ctx.env_config.log_group == "my-log-group"
316"#
317            );
318            Ok(())
319        })
320    }
321}