Skip to main content

saddle_core/request_context/
unified.rs

1//! Schema 1 projection. Legacy diagnostic serialization remains independent.
2use super::*;
3mod select;
4pub use select::SelectedContextValue;
5use crate::context_json::JsonSubtree;
6use serde::ser::SerializeMap;
7
8pub struct UnifiedContext<'a>(pub(super) &'a RequestExecutionView);
9
10struct StringId(u64);
11impl Serialize for StringId {
12    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { s.collect_str(&self.0) }
13}
14struct Empty;
15impl Serialize for Empty {
16    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { s.serialize_map(Some(0))?.end() }
17}
18fn present<T: Serialize, M: SerializeMap>(map: &mut M, key: &str, fact: ContextFact<T>) -> Result<(), M::Error> {
19    if let ContextFact::Present(value) = fact { map.serialize_entry(key, &value)?; }
20    Ok(())
21}
22fn state<T>(fact: ContextFact<T>) -> Option<&'static str> {
23    match fact {
24        ContextFact::Present(_) => None,
25        ContextFact::NotEstablished => Some("not_established"),
26        ContextFact::NotApplicable => Some("not_applicable"),
27        ContextFact::Unavailable => Some("unavailable"),
28    }
29}
30#[derive(Serialize)]
31struct Application { application: ContextLabel }
32struct Request<'a>(&'a RequestExecutionView);
33impl Serialize for Request<'_> {
34    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
35        let mut map = s.serialize_map(None)?;
36        map.serialize_entry("localRequestId", &StringId(self.0.inner.root.local))?;
37        present(&mut map, "requestId", self.0.diagnostic_snapshot().request)?;
38        if let Some(ingress) = &self.0.inner.ingress {
39            map.serialize_entry("callId", &ingress.call_id)?;
40            map.serialize_entry("deadlineUnixMs", &ingress.deadline_unix_ms)?;
41        }
42        map.end()
43    }
44}
45struct Interface<'a>(&'a RequestExecutionView);
46impl Serialize for Interface<'_> {
47    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
48        let mut map = s.serialize_map(None)?;
49        map.serialize_entry("entry", "profusegw")?;
50        if let Some(identity) = self.0.identity() { map.serialize_entry("interfaceId", &identity.route)?; }
51        map.end()
52    }
53}
54struct Execution<'a>(&'a RequestExecutionView);
55impl Serialize for Execution<'_> {
56    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
57        let facts = self.0.inner.facts;
58        let mut map = s.serialize_map(None)?;
59        if let ContextFact::Present(task) = facts.task { map.serialize_entry("taskId", &StringId(task))?; }
60        if let ContextFact::Present(scope) = facts.scope { map.serialize_entry("scopeId", &StringId(scope.as_u64()))?; }
61        present(&mut map, "dbOperation", facts.db_operation)?;
62        map.serialize_entry("phase", &facts.phase)?;
63        map.end()
64    }
65}
66struct Call<'a>(&'a RequestExecutionView);
67impl Serialize for Call<'_> {
68    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
69        let mut map = s.serialize_map(None)?;
70        if let Some(child) = &self.0.inner.child {
71            map.serialize_entry("spanId", &SpanProjection(child.span))?;
72            map.serialize_entry("attempt", &child.attempt)?;
73            map.serialize_entry("service", &child.service)?;
74            map.serialize_entry("method", &child.operation)?;
75        }
76        map.end()
77    }
78}
79struct Trace<'a> { view: &'a RequestExecutionView, source: Option<JsonSubtree<'a>> }
80impl Serialize for Trace<'_> {
81    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
82        let mut map = s.serialize_map(None)?;
83        if let Some(source) = self.source {
84            for (key, value) in source.children() {
85                let key = key.ok_or_else(|| serde::ser::Error::custom("trace is not an object"))?;
86                if key != "traceId" && key != "rpcId" { map.serialize_entry(key, &value)?; }
87            }
88        }
89        let snapshot = self.view.diagnostic_snapshot();
90        present(&mut map, "traceId", snapshot.trace_id)?;
91        present(&mut map, "rpcId", snapshot.rpc_id)?;
92        map.end()
93    }
94}
95#[derive(Serialize)]
96struct Input<'a> {
97    #[serde(rename = "requestData", skip_serializing_if = "Option::is_none")]
98    request_data: Option<JsonSubtree<'a>>,
99}
100#[derive(Serialize)]
101struct Auth<'a> {
102    state: &'static str,
103    #[serde(rename = "subjectId", skip_serializing_if = "Option::is_none")]
104    subject: Option<JsonSubtree<'a>>,
105}
106struct Meta<'a> { view: &'a RequestExecutionView, tree: Option<JsonSubtree<'a>> }
107struct FieldStates<'a> { view: &'a RequestExecutionView, tree: Option<JsonSubtree<'a>> }
108impl Serialize for Meta<'_> {
109    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
110        let mut map = s.serialize_map(None)?;
111        map.serialize_entry("revision", &StringId(self.view.inner.revision))?;
112        if let ContextFact::Present(scope) = self.view.inner.facts.scope { map.serialize_entry("scopeId", &StringId(scope.as_u64()))?; }
113        map.serialize_entry("fieldStates", &FieldStates { view: self.view, tree: self.tree })?;
114        map.serialize_entry("redactions", &Redactions(self.view))?;
115        map.end()
116    }
117}
118struct Redactions<'a>(&'a RequestExecutionView);
119impl Serialize for Redactions<'_> {
120    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
121        let mut map = s.serialize_map(None)?;
122        if let Some(source) = &self.0.inner.business {
123            let mut result = Ok(());
124            source.with_secrets(&mut |paths| {
125                for path in paths {
126                    if result.is_err() { break; }
127                    result = map.serialize_entry(path, "secret");
128                }
129            });
130            result?;
131        }
132        map.end()
133    }
134}
135struct ProtectedSource<'a>(&'a dyn crate::context_json::ContextSource);
136impl Serialize for ProtectedSource<'_> {
137    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
138        let mut serializer = Some(s);
139        let mut result = None;
140        self.0.with_tree_and_secrets(&mut |tree, secrets| {
141            let value = crate::context_json::RedactedSubtree::new(tree, secrets, "/business");
142            result = Some(value.serialize(serializer.take().expect("single business read")));
143        });
144        result.expect("business source provides one immutable tree")
145    }
146}
147impl Serialize for FieldStates<'_> {
148    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
149        let mut map = s.serialize_map(None)?;
150        for path in [
151            "/traceInfo/traceId", "/traceInfo/rpcId", "/requestInfo/requestId", "/interfaceInfo/interfaceId",
152            "/interfaceInfo/module", "/interfaceInfo/service", "/interfaceInfo/operation",
153            "/requestInfo/callId", "/requestInfo/deadlineUnixMs", "/inputInfo/requestData", "/ldcInfo", "/authInfo/subjectId",
154            "/profuseContext", "/executionInfo/taskId", "/executionInfo/scopeId", "/executionInfo/dbOperation",
155            "/callInfo/callId", "/callInfo/parentCallId", "/callInfo/kind", "/callInfo/dependencyAlias", "/callInfo/service",
156            "/callInfo/method", "/callInfo/contractSha256", "/callInfo/spanId", "/callInfo/attempt",
157        ] {
158            if let Some(state) = select::state_at(self.view, self.tree, path) { map.serialize_entry(path, state)?; }
159        }
160        map.end()
161    }
162}
163impl UnifiedContext<'_> {
164    fn serialize_tree<S: Serializer>(&self, s: S, tree: Option<JsonSubtree<'_>>) -> Result<S::Ok, S::Error> {
165        let select = |path| tree.and_then(|tree| tree.pointer(path).ok().flatten());
166        let mut map = s.serialize_map(None)?;
167        map.serialize_entry("schemaVersion", "1.0")?;
168        map.serialize_entry("applicationInfo", &Application { application: self.0.inner.root.application })?;
169        map.serialize_entry("traceInfo", &Trace { view: self.0, source: select("/profuseGwContext/traceInfo") })?;
170        if let Some(ldc) = select("/profuseGwContext/ldcInfo") { map.serialize_entry("ldcInfo", &ldc)?; }
171        map.serialize_entry("requestInfo", &Request(self.0))?;
172        map.serialize_entry("interfaceInfo", &Interface(self.0))?;
173        map.serialize_entry("inputInfo", &Input { request_data: select("/requestData") })?;
174        map.serialize_entry("callInfo", &Call(self.0))?;
175        map.serialize_entry("executionInfo", &Execution(self.0))?;
176        map.serialize_entry("authInfo", &Auth { state: "unavailable", subject: select("/profuseGwContext/userInfo/userId") })?;
177        if let Some(profuse) = select("/profuseContext") { map.serialize_entry("profuseContext", &profuse)?; }
178        if let Some(business) = &self.0.inner.business {
179            map.serialize_entry("business", &ProtectedSource(business.as_ref()))?;
180        } else { map.serialize_entry("business", &Empty)?; }
181        map.serialize_entry("_meta", &Meta { view: self.0, tree })?;
182        map.end()
183    }
184}
185impl Serialize for UnifiedContext<'_> {
186    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
187        if let Some(ingress) = &self.0.inner.ingress {
188            let mut serializer = Some(s);
189            let mut result = None;
190            ingress.source.with_tree(&mut |tree| {
191                result = Some(self.serialize_tree(serializer.take().expect("single context read"), Some(tree)));
192            });
193            result.expect("context source must provide one immutable tree")
194        } else { self.serialize_tree(s, None) }
195    }
196}