use super::*;
mod select;
pub use select::SelectedContextValue;
use crate::context_json::JsonSubtree;
use serde::ser::SerializeMap;
pub struct UnifiedContext<'a>(pub(super) &'a RequestExecutionView);
struct StringId(u64);
impl Serialize for StringId {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { s.collect_str(&self.0) }
}
struct Empty;
impl Serialize for Empty {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { s.serialize_map(Some(0))?.end() }
}
fn present<T: Serialize, M: SerializeMap>(map: &mut M, key: &str, fact: ContextFact<T>) -> Result<(), M::Error> {
if let ContextFact::Present(value) = fact { map.serialize_entry(key, &value)?; }
Ok(())
}
fn state<T>(fact: ContextFact<T>) -> Option<&'static str> {
match fact {
ContextFact::Present(_) => None,
ContextFact::NotEstablished => Some("not_established"),
ContextFact::NotApplicable => Some("not_applicable"),
ContextFact::Unavailable => Some("unavailable"),
}
}
#[derive(Serialize)]
struct Application { application: ContextLabel }
struct Request<'a>(&'a RequestExecutionView);
impl Serialize for Request<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut map = s.serialize_map(None)?;
map.serialize_entry("localRequestId", &StringId(self.0.inner.root.local))?;
present(&mut map, "requestId", self.0.diagnostic_snapshot().request)?;
if let Some(ingress) = &self.0.inner.ingress {
map.serialize_entry("callId", &ingress.call_id)?;
map.serialize_entry("deadlineUnixMs", &ingress.deadline_unix_ms)?;
}
map.end()
}
}
struct Interface<'a>(&'a RequestExecutionView);
impl Serialize for Interface<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut map = s.serialize_map(None)?;
map.serialize_entry("entry", "profusegw")?;
if let Some(identity) = self.0.identity() { map.serialize_entry("interfaceId", &identity.route)?; }
map.end()
}
}
struct Execution<'a>(&'a RequestExecutionView);
impl Serialize for Execution<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let facts = self.0.inner.facts;
let mut map = s.serialize_map(None)?;
if let ContextFact::Present(task) = facts.task { map.serialize_entry("taskId", &StringId(task))?; }
if let ContextFact::Present(scope) = facts.scope { map.serialize_entry("scopeId", &StringId(scope.as_u64()))?; }
present(&mut map, "dbOperation", facts.db_operation)?;
map.serialize_entry("phase", &facts.phase)?;
map.end()
}
}
struct Call<'a>(&'a RequestExecutionView);
impl Serialize for Call<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut map = s.serialize_map(None)?;
if let Some(child) = &self.0.inner.child {
map.serialize_entry("spanId", &SpanProjection(child.span))?;
map.serialize_entry("attempt", &child.attempt)?;
map.serialize_entry("service", &child.service)?;
map.serialize_entry("method", &child.operation)?;
}
map.end()
}
}
struct Trace<'a> { view: &'a RequestExecutionView, source: Option<JsonSubtree<'a>> }
impl Serialize for Trace<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut map = s.serialize_map(None)?;
if let Some(source) = self.source {
for (key, value) in source.children() {
let key = key.ok_or_else(|| serde::ser::Error::custom("trace is not an object"))?;
if key != "traceId" && key != "rpcId" { map.serialize_entry(key, &value)?; }
}
}
let snapshot = self.view.diagnostic_snapshot();
present(&mut map, "traceId", snapshot.trace_id)?;
present(&mut map, "rpcId", snapshot.rpc_id)?;
map.end()
}
}
#[derive(Serialize)]
struct Input<'a> {
#[serde(rename = "requestData", skip_serializing_if = "Option::is_none")]
request_data: Option<JsonSubtree<'a>>,
}
#[derive(Serialize)]
struct Auth<'a> {
state: &'static str,
#[serde(rename = "subjectId", skip_serializing_if = "Option::is_none")]
subject: Option<JsonSubtree<'a>>,
}
struct Meta<'a> { view: &'a RequestExecutionView, tree: Option<JsonSubtree<'a>> }
struct FieldStates<'a> { view: &'a RequestExecutionView, tree: Option<JsonSubtree<'a>> }
impl Serialize for Meta<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut map = s.serialize_map(None)?;
map.serialize_entry("revision", &StringId(self.view.inner.revision))?;
if let ContextFact::Present(scope) = self.view.inner.facts.scope { map.serialize_entry("scopeId", &StringId(scope.as_u64()))?; }
map.serialize_entry("fieldStates", &FieldStates { view: self.view, tree: self.tree })?;
map.serialize_entry("redactions", &Redactions(self.view))?;
map.end()
}
}
struct Redactions<'a>(&'a RequestExecutionView);
impl Serialize for Redactions<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut map = s.serialize_map(None)?;
if let Some(source) = &self.0.inner.business {
let mut result = Ok(());
source.with_secrets(&mut |paths| {
for path in paths {
if result.is_err() { break; }
result = map.serialize_entry(path, "secret");
}
});
result?;
}
map.end()
}
}
struct ProtectedSource<'a>(&'a dyn crate::context_json::ContextSource);
impl Serialize for ProtectedSource<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut serializer = Some(s);
let mut result = None;
self.0.with_tree_and_secrets(&mut |tree, secrets| {
let value = crate::context_json::RedactedSubtree::new(tree, secrets, "/business");
result = Some(value.serialize(serializer.take().expect("single business read")));
});
result.expect("business source provides one immutable tree")
}
}
impl Serialize for FieldStates<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut map = s.serialize_map(None)?;
for path in [
"/traceInfo/traceId", "/traceInfo/rpcId", "/requestInfo/requestId", "/interfaceInfo/interfaceId",
"/interfaceInfo/module", "/interfaceInfo/service", "/interfaceInfo/operation",
"/requestInfo/callId", "/requestInfo/deadlineUnixMs", "/inputInfo/requestData", "/ldcInfo", "/authInfo/subjectId",
"/profuseContext", "/executionInfo/taskId", "/executionInfo/scopeId", "/executionInfo/dbOperation",
"/callInfo/callId", "/callInfo/parentCallId", "/callInfo/kind", "/callInfo/dependencyAlias", "/callInfo/service",
"/callInfo/method", "/callInfo/contractSha256", "/callInfo/spanId", "/callInfo/attempt",
] {
if let Some(state) = select::state_at(self.view, self.tree, path) { map.serialize_entry(path, state)?; }
}
map.end()
}
}
impl UnifiedContext<'_> {
fn serialize_tree<S: Serializer>(&self, s: S, tree: Option<JsonSubtree<'_>>) -> Result<S::Ok, S::Error> {
let select = |path| tree.and_then(|tree| tree.pointer(path).ok().flatten());
let mut map = s.serialize_map(None)?;
map.serialize_entry("schemaVersion", "1.0")?;
map.serialize_entry("applicationInfo", &Application { application: self.0.inner.root.application })?;
map.serialize_entry("traceInfo", &Trace { view: self.0, source: select("/profuseGwContext/traceInfo") })?;
if let Some(ldc) = select("/profuseGwContext/ldcInfo") { map.serialize_entry("ldcInfo", &ldc)?; }
map.serialize_entry("requestInfo", &Request(self.0))?;
map.serialize_entry("interfaceInfo", &Interface(self.0))?;
map.serialize_entry("inputInfo", &Input { request_data: select("/requestData") })?;
map.serialize_entry("callInfo", &Call(self.0))?;
map.serialize_entry("executionInfo", &Execution(self.0))?;
map.serialize_entry("authInfo", &Auth { state: "unavailable", subject: select("/profuseGwContext/userInfo/userId") })?;
if let Some(profuse) = select("/profuseContext") { map.serialize_entry("profuseContext", &profuse)?; }
if let Some(business) = &self.0.inner.business {
map.serialize_entry("business", &ProtectedSource(business.as_ref()))?;
} else { map.serialize_entry("business", &Empty)?; }
map.serialize_entry("_meta", &Meta { view: self.0, tree })?;
map.end()
}
}
impl Serialize for UnifiedContext<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
if let Some(ingress) = &self.0.inner.ingress {
let mut serializer = Some(s);
let mut result = None;
ingress.source.with_tree(&mut |tree| {
result = Some(self.serialize_tree(serializer.take().expect("single context read"), Some(tree)));
});
result.expect("context source must provide one immutable tree")
} else { self.serialize_tree(s, None) }
}
}