1use bytes::Bytes;
2use sms_core::{Headers, InboundRegistry};
3use sms_web_generic::{HeaderConverter, ResponseConverter, WebhookProcessor};
4use warp::{http::HeaderMap, hyper::StatusCode, Filter, Rejection, Reply};
5
6#[derive(Clone)]
7pub struct AppState {
8 pub registry: InboundRegistry,
9}
10
11pub struct WarpHeaderConverter;
13
14impl HeaderConverter for WarpHeaderConverter {
15 type HeaderType = HeaderMap;
16
17 fn to_generic_headers(headers: &Self::HeaderType) -> Headers {
18 headers
19 .iter()
20 .map(|(k, v)| {
21 (
22 k.as_str().to_string(),
23 v.to_str().unwrap_or_default().to_string(),
24 )
25 })
26 .collect()
27 }
28}
29
30pub struct WarpResponseConverter;
32
33impl ResponseConverter for WarpResponseConverter {
34 type ResponseType = warp::reply::Response;
35
36 fn from_webhook_response(response: sms_core::WebhookResponse) -> Self::ResponseType {
37 let status = StatusCode::from_u16(response.status.as_u16())
38 .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
39
40 warp::reply::with_status(
41 warp::reply::with_header(response.body, "content-type", response.content_type),
42 status,
43 )
44 .into_response()
45 }
46}
47
48pub async fn unified_webhook_handler(
50 provider: String,
51 headers: HeaderMap,
52 body: Bytes,
53 state: AppState,
54) -> Result<warp::reply::Response, Rejection> {
55 let processor = WebhookProcessor::new(state.registry);
56 let generic_headers = WarpHeaderConverter::to_generic_headers(&headers);
57 let response = processor.process_webhook(&provider, generic_headers, &body);
58 Ok(WarpResponseConverter::from_webhook_response(response))
59}
60
61pub fn webhook_filter(
63 state: AppState,
64) -> impl warp::Filter<Extract = (warp::reply::Response,), Error = Rejection> + Clone {
65 warp::path!("webhooks" / String)
66 .and(warp::post())
67 .and(warp::header::headers_cloned())
68 .and(warp::body::bytes())
69 .and(warp::any().map(move || state.clone()))
70 .and_then(unified_webhook_handler)
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use sms_core::InboundRegistry;
77
78 #[tokio::test]
79 async fn webhook_filter_compiles() {
80 let registry = InboundRegistry::new();
81 let state = AppState { registry };
82 let _filter = webhook_filter(state);
83 }
84}