Skip to main content

synapse_proxy/transform/
wrap.rs

1//! `wrap` built-in: nest the incoming JSON body under a key, then inject sibling
2//! fields. Reproduces the integration `/call` envelope:
3//! `{ <under>: <original body>, org: …, workspace: … }`.
4
5use async_trait::async_trait;
6use serde_json::Value;
7
8use crate::config::WrapSpec;
9use crate::context::ResolvedContext;
10
11use super::inject::{set_body_path, Inject};
12use super::{ProxyRequest, RequestTransform, TransformError};
13
14pub struct Wrap {
15    under: String,
16    siblings: Vec<Inject>,
17}
18
19impl Wrap {
20    pub fn from_spec(spec: &WrapSpec) -> anyhow::Result<Self> {
21        let siblings = spec
22            .inject
23            .iter()
24            .map(Inject::from_spec)
25            .collect::<anyhow::Result<Vec<_>>>()?;
26        Ok(Self {
27            under: spec.under.clone(),
28            siblings,
29        })
30    }
31}
32
33#[async_trait]
34impl RequestTransform for Wrap {
35    async fn apply(
36        &self,
37        ctx: &ResolvedContext,
38        req: &mut ProxyRequest,
39    ) -> Result<(), TransformError> {
40        // Take the existing body, nest it under `under`, then inject siblings.
41        let original = req.body_json_mut()?.take(); // serde_json::Value::take leaves Null
42        let mut envelope = Value::Object(Default::default());
43        set_body_path(&mut envelope, &self.under, original);
44        *req.body_json_mut()? = envelope;
45        for inj in &self.siblings {
46            inj.apply(ctx, req).await?;
47        }
48        Ok(())
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use crate::config::InjectSpec;
56    use crate::transform::ProxyRequest;
57    use axum::http::{HeaderMap, Method};
58    use std::collections::HashMap;
59
60    fn ctx() -> ResolvedContext {
61        crate::context::ContextStore::new(HashMap::from([("org".into(), "acme".into())])).resolve()
62    }
63
64    #[tokio::test]
65    async fn wraps_body_and_injects_sibling() {
66        let wrap = Wrap::from_spec(&WrapSpec {
67            under: "request".into(),
68            inject: vec![InjectSpec {
69                header: None,
70                body: Some("org".into()),
71                from_context: Some("org".into()),
72                constant: None,
73            }],
74        })
75        .unwrap();
76        let mut r = ProxyRequest::from_parts(
77            Method::POST,
78            "/x".into(),
79            None,
80            HeaderMap::new(),
81            b"{\"method\":\"GET\",\"path\":\"/p\"}".to_vec(),
82        );
83        wrap.apply(&ctx(), &mut r).await.unwrap();
84        let v: Value = serde_json::from_slice(&r.into_body_bytes()).unwrap();
85        assert_eq!(v["request"]["method"], "GET");
86        assert_eq!(v["request"]["path"], "/p");
87        assert_eq!(v["org"], "acme");
88    }
89}