Skip to main content

slack_volt_lambda/
lib.rs

1use lambda_http::{Body, Request, Response, run, service_fn};
2use slack_volt::App;
3use slack_volt::middleware::Headers;
4use std::sync::Arc;
5use tracing;
6
7pub struct LambdaAdapter;
8
9impl LambdaAdapter {
10    pub async fn run(app: App) -> Result<(), lambda_http::Error> {
11        let app = Arc::new(app);
12        run(service_fn(move |req: Request| {
13            let app = app.clone();
14            async move { handle_request(req, &app).await }
15        }))
16        .await
17    }
18}
19
20async fn handle_request(
21    req: Request,
22    app: &App,
23) -> Result<Response<Body>, lambda_http::Error> {
24    let headers = extract_headers(&req);
25    let content_type = headers.content_type.clone();
26    let body = match req.body() {
27        Body::Text(s) => s.clone(),
28        Body::Binary(b) => String::from_utf8_lossy(b).to_string(),
29        Body::Empty => String::new(),
30    };
31
32    tracing::debug!(content_type = %content_type, body_len = body.len(), "incoming slack request");
33
34    match app.dispatch_async(&content_type, &body, headers).await {
35        Ok(ack) => {
36            if ack.is_empty() {
37                let resp = Response::builder()
38                    .status(200)
39                    .body(Body::Empty)?;
40                Ok(resp)
41            } else {
42                let response_body = serde_json::to_string(&ack)?;
43                let resp = Response::builder()
44                    .status(200)
45                    .header("content-type", "application/json")
46                    .body(Body::Text(response_body))?;
47                Ok(resp)
48            }
49        }
50        Err(slack_volt::Error::NoHandler { kind, id }) => {
51            tracing::warn!(kind, id, "no handler registered");
52            let resp = Response::builder()
53                .status(200)
54                .body(Body::Empty)?;
55            Ok(resp)
56        }
57        Err(slack_volt::Error::SignatureVerification(msg)) => {
58            tracing::error!(msg, "signature verification failed");
59            let resp = Response::builder()
60                .status(401)
61                .body(Body::Text("unauthorized".to_string()))?;
62            Ok(resp)
63        }
64        Err(e) => {
65            tracing::error!(error = %e, "handler error");
66            let resp = Response::builder()
67                .status(200)
68                .body(Body::Empty)?;
69            Ok(resp)
70        }
71    }
72}
73
74fn extract_headers(req: &Request) -> Headers {
75    let get = |name: &str| {
76        req.headers()
77            .get(name)
78            .and_then(|v| v.to_str().ok())
79            .unwrap_or("")
80            .to_string()
81    };
82
83    Headers {
84        timestamp: get("x-slack-request-timestamp"),
85        signature: get("x-slack-signature"),
86        content_type: get("content-type"),
87    }
88}