saddle_core/request_context/
unified.rs1use 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}