1use poem::{
2 web::{Data, Path},
3 http::{HeaderMap, StatusCode},
4 Request, Response, Result,
5};
6use bytes::Bytes;
7use sms_core::{Headers, InboundRegistry};
8use sms_web_generic::{HeaderConverter, ResponseConverter, WebhookProcessor};
9
10#[derive(Clone)]
11pub struct AppState {
12 pub registry: InboundRegistry,
13}
14
15pub struct PoemHeaderConverter;
17
18impl HeaderConverter for PoemHeaderConverter {
19 type HeaderType = HeaderMap;
20
21 fn to_generic_headers(headers: &Self::HeaderType) -> Headers {
22 headers
23 .iter()
24 .map(|(k, v)| {
25 (
26 k.as_str().to_string(),
27 v.to_str().unwrap_or_default().to_string(),
28 )
29 })
30 .collect()
31 }
32}
33
34pub struct PoemResponseConverter;
36
37impl ResponseConverter for PoemResponseConverter {
38 type ResponseType = Response;
39
40 fn from_webhook_response(response: sms_core::WebhookResponse) -> Self::ResponseType {
41 let status = StatusCode::from_u16(response.status.as_u16())
42 .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
43
44 Response::builder()
45 .status(status)
46 .header("content-type", response.content_type)
47 .body(response.body)
48 }
49}
50
51pub async fn unified_webhook(
53 req: &Request,
54 Path(provider): Path<String>,
55 body: Bytes,
56 Data(state): Data<&AppState>,
57) -> Result<Response> {
58 let processor = WebhookProcessor::new(state.registry.clone());
59 let generic_headers = PoemHeaderConverter::to_generic_headers(req.headers());
60 let response = processor.process_webhook(&provider, generic_headers, &body);
61 Ok(PoemResponseConverter::from_webhook_response(response))
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn poem_types_compile() {
70 let registry = InboundRegistry::new();
71 let _state = AppState { registry };
72 }
73}