1use sms_core::{Headers, InboundRegistry};
2use sms_web_generic::{HeaderConverter, ResponseConverter, WebhookProcessor};
3use tide::{Request, Response, Result, StatusCode};
4
5#[derive(Clone)]
6pub struct AppState {
7 pub registry: InboundRegistry,
8}
9
10pub struct TideHeaderConverter;
12
13impl HeaderConverter for TideHeaderConverter {
14 type HeaderType = Request<AppState>;
15
16 fn to_generic_headers(req: &Self::HeaderType) -> Headers {
17 req.iter()
18 .map(|(name, values)| {
19 let value = values
20 .iter()
21 .map(|v| v.as_str())
22 .collect::<Vec<_>>()
23 .join(", ");
24 (name.as_str().to_string(), value)
25 })
26 .collect()
27 }
28}
29
30pub struct TideResponseConverter;
32
33impl ResponseConverter for TideResponseConverter {
34 type ResponseType = Result<Response>;
35
36 fn from_webhook_response(response: sms_core::WebhookResponse) -> Self::ResponseType {
37 let status = StatusCode::try_from(response.status.as_u16())
38 .unwrap_or(StatusCode::InternalServerError);
39
40 let mut res = Response::new(status);
41 res.set_body(response.body);
42
43 let content_type = if response.content_type == "application/json" {
45 tide::http::mime::JSON
46 } else {
47 tide::http::mime::PLAIN
48 };
49 res.set_content_type(content_type);
50 Ok(res)
51 }
52}
53
54pub async fn unified_webhook(mut req: Request<AppState>) -> Result<Response> {
56 let provider = req.param("provider")?.to_string();
57 let body = req.body_bytes().await?;
58 let processor = WebhookProcessor::new(req.state().registry.clone());
59 let generic_headers = TideHeaderConverter::to_generic_headers(&req);
60 let response = processor.process_webhook(&provider, generic_headers, &body);
61 TideResponseConverter::from_webhook_response(response)
62}
63
64pub fn configure_routes(app: &mut tide::Server<AppState>) {
66 app.at("/webhooks/:provider").post(unified_webhook);
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn tide_types_compile() {
75 let registry = InboundRegistry::new();
76 let _state = AppState { registry };
77 }
80}