From fb2d0b69a3390a4902db42df5669743434acb2fe Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:21:09 +0200 Subject: [PATCH 01/40] wip Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/conditions.rs | 1 + .../plugins/telemetry/config_new/events.rs | 211 +++++++++++++++++- 2 files changed, 200 insertions(+), 12 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditions.rs b/apollo-router/src/plugins/telemetry/config_new/conditions.rs index 773b4a4d82..fd9055279b 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditions.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditions.rs @@ -52,6 +52,7 @@ impl Condition where T: Selector, { + // Returns Some(false) if it doesn't fulfill the condition pub(crate) fn evaluate_request(&mut self, request: &T::Request) -> Option { match self { Condition::Eq(eq) => match (eq[0].on_request(request), eq[1].on_request(request)) { diff --git a/apollo-router/src/plugins/telemetry/config_new/events.rs b/apollo-router/src/plugins/telemetry/config_new/events.rs index a7dfa619b6..907a5a8f07 100644 --- a/apollo-router/src/plugins/telemetry/config_new/events.rs +++ b/apollo-router/src/plugins/telemetry/config_new/events.rs @@ -1,8 +1,16 @@ +use std::collections::HashMap; use std::fmt::Debug; +use std::sync::Arc; +use opentelemetry::KeyValue; +use parking_lot::Mutex; use schemars::JsonSchema; use serde::Deserialize; +use tower::BoxError; +use super::instruments::Instrumented; +use super::Selector; +use super::Selectors; use crate::plugins::telemetry::config_new::attributes::RouterAttributes; use crate::plugins::telemetry::config_new::attributes::SubgraphAttributes; use crate::plugins::telemetry::config_new::attributes::SupergraphAttributes; @@ -11,9 +19,10 @@ use crate::plugins::telemetry::config_new::extendable::Extendable; use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::selectors::SubgraphSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; +use crate::services::router; +use crate::Context; /// Events are -#[allow(dead_code)] #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] #[serde(deny_unknown_fields, default)] pub(crate) struct Events { @@ -25,7 +34,93 @@ pub(crate) struct Events { subgraph: Extendable>, } -#[allow(dead_code)] +impl Events { + pub(crate) fn new_router_events(&self) -> RouterCustomEvents { + let mut router_events = Vec::new(); + if self.router.attributes.request != EventLevel::Off { + router_events.push(CustomEvent { + inner: Mutex::new(CustomEventInner { + name: "router.request".to_string(), + level: self.router.attributes.request, + event_on: EventOn::Request, + message: Arc::new(String::from("router request")), + selectors: None, + condition: Condition::True, + attributes: Vec::new(), + }), + }); + } + if self.router.attributes.response != EventLevel::Off { + router_events.push(CustomEvent { + inner: Mutex::new(CustomEventInner { + name: "router.response".to_string(), + level: self.router.attributes.response, + event_on: EventOn::Response, + message: Arc::new(String::from("router response")), + selectors: None, + condition: Condition::True, + attributes: Vec::new(), + }), + }); + } + if self.router.attributes.error != EventLevel::Off { + router_events.push(CustomEvent { + inner: Mutex::new(CustomEventInner { + name: "router.error".to_string(), + level: self.router.attributes.error, + event_on: EventOn::Error, + message: Arc::new(String::from("router error")), + selectors: None, + condition: Condition::True, + attributes: Vec::new(), + }), + }); + } + + for (event_name, event_cfg) in &self.router.custom { + router_events.push(CustomEvent { + inner: Mutex::new(CustomEventInner { + name: event_name.clone(), + level: event_cfg.level, + event_on: event_cfg.on, + message: event_cfg.message.clone(), + selectors: event_cfg.attributes.clone().into(), + condition: event_cfg.condition.clone(), + attributes: Vec::new(), + }), + }) + } + + router_events + } +} + +pub(crate) type RouterCustomEvents = + Vec>; + +impl Instrumented for RouterCustomEvents { + type Request = router::Request; + type Response = router::Response; + + fn on_request(&self, request: &Self::Request) { + for custom_event in self { + custom_event.on_request(request); + } + } + + fn on_response(&self, response: &Self::Response) { + for custom_event in self { + custom_event.on_response(response); + } + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + for custom_event in self { + custom_event.on_error(error, ctx); + } + } +} + #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] struct RouterEvents { @@ -37,7 +132,6 @@ struct RouterEvents { error: EventLevel, } -#[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] struct SupergraphEvents { @@ -49,7 +143,6 @@ struct SupergraphEvents { error: EventLevel, } -#[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] struct SubgraphEvents { @@ -61,8 +154,7 @@ struct SubgraphEvents { error: EventLevel, } -#[allow(dead_code)] -#[derive(Deserialize, JsonSchema, Clone, Debug, Default)] +#[derive(Deserialize, JsonSchema, Clone, Debug, Default, PartialEq, Copy)] #[serde(rename_all = "snake_case")] pub(crate) enum EventLevel { Info, @@ -75,7 +167,6 @@ pub(crate) enum EventLevel { /// An event that can be logged as part of a trace. /// The event has an implicit `type` attribute that matches the name of the event in the yaml /// and a message that can be used to provide additional information. -#[allow(dead_code)] #[derive(Deserialize, JsonSchema, Clone, Debug)] pub(crate) struct Event where @@ -86,14 +177,14 @@ where level: EventLevel, /// The event message. - message: String, + message: Arc, /// When to trigger the event. on: EventOn, /// The event attributes. - #[serde(default = "Extendable::empty::")] - attributes: Extendable, + #[serde(default = "Extendable::empty_arc::")] + attributes: Arc>, /// The event conditions. #[serde(default = "Condition::empty::")] @@ -101,8 +192,7 @@ where } /// When to trigger the event. -#[allow(dead_code)] -#[derive(Deserialize, JsonSchema, Clone, Debug)] +#[derive(Deserialize, JsonSchema, Clone, Debug, Copy, PartialEq)] #[serde(rename_all = "snake_case")] pub(crate) enum EventOn { /// Log the event on request @@ -112,3 +202,100 @@ pub(crate) enum EventOn { /// Log the event on error Error, } + +struct CustomEvent +where + A: Selectors + Default, + T: Selector + Debug, +{ + inner: Mutex>, +} + +struct CustomEventInner +where + A: Selectors + Default, + T: Selector + Debug, +{ + name: String, + level: EventLevel, + event_on: EventOn, + message: Arc, + selectors: Option>>, + condition: Condition, + attributes: Vec, +} + +impl Instrumented for CustomEvent +where + A: Selectors + Default, + T: Selector + Debug + Debug, +{ + type Request = Request; + type Response = Response; + + fn on_request(&self, request: &Self::Request) { + let mut inner = self.inner.lock(); + if inner.condition.evaluate_request(request) == Some(false) { + return; + } + if let Some(selectors) = &inner.selectors { + inner.attributes = selectors.on_request(request); + } + + if let EventOn::Request = inner.event_on { + inner.send_event(); + } + } + + fn on_response(&self, response: &Self::Response) { + let mut inner = self.inner.lock(); + if inner.event_on != EventOn::Response { + return; + } + + if !inner.condition.evaluate_response(response) { + return; + } + if let Some(selectors) = &inner.selectors { + inner + .attributes + .append(&mut selectors.on_response(response).into_iter().collect()); + } + + inner.send_event(); + } + + fn on_error(&self, error: &BoxError, _ctx: &Context) { + let mut inner = self.inner.lock(); + if inner.event_on != EventOn::Error { + return; + } + if let Some(selectors) = &inner.selectors { + inner.attributes.append(&mut selectors.on_error(error)); + } + + inner.send_event(); + } +} + +impl CustomEventInner +where + A: Selectors + Default, + T: Selector + Debug + Debug, +{ + fn send_event(&self) { + let attributes: HashMap<&str, &str> = self + .attributes + .iter() + .map(|kv| (kv.key.as_str(), kv.value.as_str().as_ref())) + .collect(); + match self.level { + EventLevel::Info => { + ::tracing::info!(attributes = ?attributes, "{}", self.message); + } + EventLevel::Warn => todo!(), + EventLevel::Error => todo!(), + EventLevel::Off => todo!(), + } + } +} From ae546b8f279927ba809083f8d82050e3e9760083 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:41:28 +0200 Subject: [PATCH 02/40] add support of events Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/plugins/telemetry/config.rs | 1 - .../plugins/telemetry/config_new/events.rs | 435 +++++++++++++++--- .../telemetry/config_new/extendable.rs | 6 - apollo-router/src/plugins/telemetry/mod.rs | 32 +- .../src/services/subgraph_service.rs | 100 ++++ .../src/services/supergraph/service.rs | 45 +- 6 files changed, 537 insertions(+), 82 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config.rs b/apollo-router/src/plugins/telemetry/config.rs index a3725a6d3f..3dd3c676c7 100644 --- a/apollo-router/src/plugins/telemetry/config.rs +++ b/apollo-router/src/plugins/telemetry/config.rs @@ -83,7 +83,6 @@ pub(crate) struct Exporters { #[derive(Clone, Default, Debug, Deserialize, JsonSchema)] #[serde(deny_unknown_fields, default)] pub(crate) struct Instrumentation { - #[serde(skip)] /// Event configuration pub(crate) events: config_new::events::Events, /// Span configuration diff --git a/apollo-router/src/plugins/telemetry/config_new/events.rs b/apollo-router/src/plugins/telemetry/config_new/events.rs index 907a5a8f07..b5d0e32c9f 100644 --- a/apollo-router/src/plugins/telemetry/config_new/events.rs +++ b/apollo-router/src/plugins/telemetry/config_new/events.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::fmt::Debug; use std::sync::Arc; -use opentelemetry::KeyValue; use parking_lot::Mutex; use schemars::JsonSchema; use serde::Deserialize; @@ -20,6 +19,8 @@ use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::selectors::SubgraphSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; use crate::services::router; +use crate::services::subgraph; +use crate::services::supergraph; use crate::Context; /// Events are @@ -27,58 +28,72 @@ use crate::Context; #[serde(deny_unknown_fields, default)] pub(crate) struct Events { /// Router service events - router: Extendable>, + router: Extendable>, /// Subgraph service events - supergraph: Extendable>, + supergraph: Extendable>, /// Supergraph service events - subgraph: Extendable>, + subgraph: Extendable>, } impl Events { - pub(crate) fn new_router_events(&self) -> RouterCustomEvents { - let mut router_events = Vec::new(); - if self.router.attributes.request != EventLevel::Off { - router_events.push(CustomEvent { - inner: Mutex::new(CustomEventInner { - name: "router.request".to_string(), - level: self.router.attributes.request, - event_on: EventOn::Request, - message: Arc::new(String::from("router request")), - selectors: None, - condition: Condition::True, - attributes: Vec::new(), - }), - }); - } - if self.router.attributes.response != EventLevel::Off { - router_events.push(CustomEvent { + pub(crate) fn new_router_events(&self) -> RouterEvents { + let custom_events = self + .router + .custom + .iter() + .map(|(event_name, event_cfg)| CustomEvent { inner: Mutex::new(CustomEventInner { - name: "router.response".to_string(), - level: self.router.attributes.response, - event_on: EventOn::Response, - message: Arc::new(String::from("router response")), - selectors: None, - condition: Condition::True, + name: event_name.clone(), + level: event_cfg.level, + event_on: event_cfg.on, + message: event_cfg.message.clone(), + selectors: event_cfg.attributes.clone().into(), + condition: event_cfg.condition.clone(), attributes: Vec::new(), }), - }); + }) + .collect(); + + RouterEvents { + request: self.router.attributes.request, + response: self.router.attributes.response, + error: self.router.attributes.error, + custom: custom_events, } - if self.router.attributes.error != EventLevel::Off { - router_events.push(CustomEvent { + } + + pub(crate) fn new_supergraph_events(&self) -> SupergraphEvents { + let custom_events = self + .supergraph + .custom + .iter() + .map(|(event_name, event_cfg)| CustomEvent { inner: Mutex::new(CustomEventInner { - name: "router.error".to_string(), - level: self.router.attributes.error, - event_on: EventOn::Error, - message: Arc::new(String::from("router error")), - selectors: None, - condition: Condition::True, + name: event_name.clone(), + level: event_cfg.level, + event_on: event_cfg.on, + message: event_cfg.message.clone(), + selectors: event_cfg.attributes.clone().into(), + condition: event_cfg.condition.clone(), attributes: Vec::new(), }), - }); + }) + .collect(); + + SupergraphEvents { + request: self.supergraph.attributes.request, + response: self.supergraph.attributes.response, + error: self.supergraph.attributes.error, + custom: custom_events, } + } - for (event_name, event_cfg) in &self.router.custom { - router_events.push(CustomEvent { + pub(crate) fn new_subgraph_events(&self) -> SubgraphEvents { + let custom_events = self + .subgraph + .custom + .iter() + .map(|(event_name, event_cfg)| CustomEvent { inner: Mutex::new(CustomEventInner { name: event_name.clone(), level: event_cfg.level, @@ -89,33 +104,299 @@ impl Events { attributes: Vec::new(), }), }) - } + .collect(); - router_events + SubgraphEvents { + request: self.subgraph.attributes.request, + response: self.subgraph.attributes.response, + error: self.subgraph.attributes.error, + custom: custom_events, + } } } -pub(crate) type RouterCustomEvents = - Vec>; +pub(crate) type RouterEvents = + CustomEvents; + +pub(crate) type SupergraphEvents = CustomEvents< + supergraph::Request, + supergraph::Response, + SupergraphAttributes, + SupergraphSelector, +>; + +pub(crate) type SubgraphEvents = + CustomEvents; + +// pub(crate) struct RouterEvents { +// request: EventLevel, +// response: EventLevel, +// error: EventLevel, +// custom: Vec>, +// } + +// impl Instrumented for RouterEvents { +// type Request = router::Request; +// type Response = router::Response; + +// fn on_request(&self, request: &Self::Request) { +// if self.request != EventLevel::Off { +// let mut attrs = HashMap::new(); +// attrs.insert( +// "headers".to_string(), +// format!("{:?}", request.router_request.headers()), +// ); +// attrs.insert( +// "method".to_string(), +// format!("{}", request.router_request.method()), +// ); +// attrs.insert( +// "uri".to_string(), +// format!("{}", request.router_request.uri()), +// ); +// attrs.insert( +// "version".to_string(), +// format!("{:?}", request.router_request.version()), +// ); +// attrs.insert( +// "body".to_string(), +// format!("{:?}", request.router_request.body()), +// ); +// log_level(self.request, "router.request", &attrs, ""); +// } +// for custom_event in &self.custom { +// custom_event.on_request(request); +// } +// } + +// fn on_response(&self, response: &Self::Response) { +// if self.response != EventLevel::Off { +// let mut attrs = HashMap::new(); +// attrs.insert( +// "headers".to_string(), +// format!("{:?}", response.response.headers()), +// ); +// attrs.insert( +// "status".to_string(), +// format!("{}", response.response.status()), +// ); +// attrs.insert( +// "version".to_string(), +// format!("{:?}", response.response.version()), +// ); +// attrs.insert( +// "body".to_string(), +// format!("{:?}", response.response.body()), +// ); +// log_level(self.response, "router.response", &attrs, ""); +// } +// for custom_event in &self.custom { +// custom_event.on_response(response); +// } +// } + +// fn on_error(&self, error: &BoxError, ctx: &Context) { +// if self.error != EventLevel::Off { +// let mut attrs = HashMap::new(); +// attrs.insert("error".to_string(), error.to_string()); +// log_level(self.error, "router.error", &attrs, ""); +// } +// for custom_event in &self.custom { +// custom_event.on_error(error, ctx); +// } +// } +// } + +pub(crate) struct CustomEvents +where + Attributes: Selectors + Default, + Sel: Selector + Debug, +{ + request: EventLevel, + response: EventLevel, + error: EventLevel, + custom: Vec>, +} -impl Instrumented for RouterCustomEvents { +impl Instrumented + for CustomEvents +{ type Request = router::Request; type Response = router::Response; fn on_request(&self, request: &Self::Request) { - for custom_event in self { + if self.request != EventLevel::Off { + let mut attrs = HashMap::with_capacity(5); + attrs.insert( + "http.request.headers".to_string(), + format!("{:?}", request.router_request.headers()), + ); + attrs.insert( + "http.request.method".to_string(), + format!("{}", request.router_request.method()), + ); + attrs.insert( + "http.request.uri".to_string(), + format!("{}", request.router_request.uri()), + ); + attrs.insert( + "http.request.version".to_string(), + format!("{:?}", request.router_request.version()), + ); + attrs.insert( + "http.request.body".to_string(), + format!("{:?}", request.router_request.body()), + ); + log_event(self.request, "router.request", &attrs, ""); + } + for custom_event in &self.custom { + custom_event.on_request(request); + } + } + + fn on_response(&self, response: &Self::Response) { + if self.response != EventLevel::Off { + let mut attrs = HashMap::with_capacity(4); + attrs.insert( + "http.response.headers".to_string(), + format!("{:?}", response.response.headers()), + ); + attrs.insert( + "http.response.status".to_string(), + format!("{}", response.response.status()), + ); + attrs.insert( + "http.response.version".to_string(), + format!("{:?}", response.response.version()), + ); + attrs.insert( + "http.response.body".to_string(), + format!("{:?}", response.response.body()), + ); + log_event(self.response, "router.response", &attrs, ""); + } + for custom_event in &self.custom { + custom_event.on_response(response); + } + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + if self.error != EventLevel::Off { + let mut attrs = HashMap::with_capacity(1); + attrs.insert("error".to_string(), error.to_string()); + log_event(self.error, "router.error", &attrs, ""); + } + for custom_event in &self.custom { + custom_event.on_error(error, ctx); + } + } +} + +impl Instrumented + for CustomEvents< + supergraph::Request, + supergraph::Response, + SupergraphAttributes, + SupergraphSelector, + > +{ + type Request = supergraph::Request; + type Response = supergraph::Response; + + fn on_request(&self, request: &Self::Request) { + if self.request != EventLevel::Off { + let mut attrs = HashMap::new(); + attrs.insert( + "http.request.headers".to_string(), + format!("{:?}", request.supergraph_request.headers()), + ); + attrs.insert( + "http.request.method".to_string(), + format!("{}", request.supergraph_request.method()), + ); + attrs.insert( + "http.request.uri".to_string(), + format!("{}", request.supergraph_request.uri()), + ); + attrs.insert( + "http.request.version".to_string(), + format!("{:?}", request.supergraph_request.version()), + ); + attrs.insert( + "http.request.body".to_string(), + serde_json::to_string(request.supergraph_request.body()).unwrap_or_default(), + ); + log_event(self.request, "supergraph.request", &attrs, ""); + } + if self.response != EventLevel::Off { + request + .context + .extensions() + .lock() + .insert(SupergraphEventResponseLevel(self.response)); + } + for custom_event in &self.custom { + custom_event.on_request(request); + } + } + + fn on_response(&self, response: &Self::Response) { + for custom_event in &self.custom { + custom_event.on_response(response); + } + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + if self.error != EventLevel::Off { + let mut attrs = HashMap::new(); + attrs.insert("error".to_string(), error.to_string()); + log_event(self.error, "supergraph.error", &attrs, ""); + } + for custom_event in &self.custom { + custom_event.on_error(error, ctx); + } + } +} + +impl Instrumented + for CustomEvents +{ + type Request = subgraph::Request; + type Response = subgraph::Response; + + fn on_request(&self, request: &Self::Request) { + if self.request != EventLevel::Off { + request + .context + .extensions() + .lock() + .insert(SubgraphEventRequestLevel(self.request)); + } + if self.response != EventLevel::Off { + request + .context + .extensions() + .lock() + .insert(SubgraphEventResponseLevel(self.response)); + } + for custom_event in &self.custom { custom_event.on_request(request); } } fn on_response(&self, response: &Self::Response) { - for custom_event in self { + for custom_event in &self.custom { custom_event.on_response(response); } } fn on_error(&self, error: &BoxError, ctx: &Context) { - for custom_event in self { + if self.error != EventLevel::Off { + let mut attrs = HashMap::new(); + attrs.insert("error".to_string(), error.to_string()); + log_event(self.error, "subgraph.error", &attrs, ""); + } + for custom_event in &self.custom { custom_event.on_error(error, ctx); } } @@ -123,7 +404,7 @@ impl Instrumented for RouterCustomEvents { #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] -struct RouterEvents { +struct RouterEventsConfig { /// Log the router request request: EventLevel, /// Log the router response @@ -132,9 +413,16 @@ struct RouterEvents { error: EventLevel, } +#[derive(Clone)] +pub(crate) struct SupergraphEventResponseLevel(pub(crate) EventLevel); +#[derive(Clone)] +pub(crate) struct SubgraphEventResponseLevel(pub(crate) EventLevel); +#[derive(Clone)] +pub(crate) struct SubgraphEventRequestLevel(pub(crate) EventLevel); + #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] -struct SupergraphEvents { +struct SupergraphEventsConfig { /// Log the supergraph request request: EventLevel, /// Log the supergraph response @@ -145,7 +433,7 @@ struct SupergraphEvents { #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] -struct SubgraphEvents { +struct SubgraphEventsConfig { /// Log the subgraph request request: EventLevel, /// Log the subgraph response @@ -203,7 +491,7 @@ pub(crate) enum EventOn { Error, } -struct CustomEvent +pub(crate) struct CustomEvent where A: Selectors + Default, T: Selector + Debug, @@ -235,14 +523,18 @@ where fn on_request(&self, request: &Self::Request) { let mut inner = self.inner.lock(); - if inner.condition.evaluate_request(request) == Some(false) { + if inner.condition.evaluate_request(request) != Some(true) + && inner.event_on == EventOn::Request + { return; } if let Some(selectors) = &inner.selectors { inner.attributes = selectors.on_request(request); } - if let EventOn::Request = inner.event_on { + if inner.event_on == EventOn::Request + && inner.condition.evaluate_request(request) != Some(false) + { inner.send_event(); } } @@ -257,9 +549,8 @@ where return; } if let Some(selectors) = &inner.selectors { - inner - .attributes - .append(&mut selectors.on_response(response).into_iter().collect()); + let mut new_attributes = selectors.on_response(response); + inner.attributes.append(&mut new_attributes); } inner.send_event(); @@ -271,7 +562,8 @@ where return; } if let Some(selectors) = &inner.selectors { - inner.attributes.append(&mut selectors.on_error(error)); + let mut new_attributes = selectors.on_error(error); + inner.attributes.append(&mut new_attributes); } inner.send_event(); @@ -283,19 +575,30 @@ where A: Selectors + Default, T: Selector + Debug + Debug, { + #[inline] fn send_event(&self) { - let attributes: HashMap<&str, &str> = self + let attributes: HashMap = self .attributes .iter() - .map(|kv| (kv.key.as_str(), kv.value.as_str().as_ref())) + .map(|kv| (kv.key.to_string(), kv.value.to_string())) .collect(); - match self.level { - EventLevel::Info => { - ::tracing::info!(attributes = ?attributes, "{}", self.message); - } - EventLevel::Warn => todo!(), - EventLevel::Error => todo!(), - EventLevel::Off => todo!(), + log_event(self.level, &self.name, &attributes, &self.message); + } +} + +#[inline] +pub(crate) fn log_event( + level: EventLevel, + kind: &str, + attributes: &HashMap, + message: &str, +) { + match level { + EventLevel::Info => { + ::tracing::info!(%kind, attributes = ?attributes, "{}", message); } + EventLevel::Warn => ::tracing::warn!(%kind, attributes = ?attributes, "{}", message), + EventLevel::Error => ::tracing::error!(%kind, attributes = ?attributes, "{}", message), + EventLevel::Off => {} } } diff --git a/apollo-router/src/plugins/telemetry/config_new/extendable.rs b/apollo-router/src/plugins/telemetry/config_new/extendable.rs index ae08b71539..90b7dce2f2 100644 --- a/apollo-router/src/plugins/telemetry/config_new/extendable.rs +++ b/apollo-router/src/plugins/telemetry/config_new/extendable.rs @@ -49,12 +49,6 @@ where } impl Extendable<(), ()> { - pub(crate) fn empty() -> Extendable - where - A: Default, - { - Default::default() - } pub(crate) fn empty_arc() -> Arc> where A: Default, diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index cd82bb0c17..9139ab1646 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -60,6 +60,9 @@ use self::config::Conf; use self::config::Sampler; use self::config::SamplerOption; use self::config::TraceIdFormat; +use self::config_new::events::RouterEvents; +use self::config_new::events::SubgraphEvents; +use self::config_new::events::SupergraphEvents; use self::config_new::instruments::Instrumented; use self::config_new::instruments::RouterInstruments; use self::config_new::instruments::SubgraphInstruments; @@ -126,7 +129,7 @@ use crate::ListenAddr; pub(crate) mod apollo; pub(crate) mod apollo_exporter; pub(crate) mod config; -mod config_new; +pub(crate) mod config_new; pub(crate) mod dynamic_attribute; mod endpoint; mod fmt_layer; @@ -373,15 +376,21 @@ impl Plugin for Telemetry { .new_router_instruments(); custom_instruments.on_request(request); + let custom_events: RouterEvents = + config_request.instrumentation.events.new_router_events(); + custom_events.on_request(request); + ( custom_attributes, custom_instruments, + custom_events, request.context.clone(), ) }, - move |(custom_attributes, custom_instruments, ctx): ( + move |(custom_attributes, custom_instruments, custom_events, ctx): ( Vec, RouterInstruments, + RouterEvents, Context, ), fut| { @@ -411,6 +420,7 @@ impl Plugin for Telemetry { .on_response(response), ); custom_instruments.on_response(response); + custom_events.on_response(response); if expose_trace_id.enabled { let header_name = expose_trace_id @@ -444,6 +454,7 @@ impl Plugin for Telemetry { config.instrumentation.spans.router.attributes.on_error(err), ); custom_instruments.on_error(err, &ctx); + custom_events.on_error(err, &ctx); } response @@ -529,9 +540,12 @@ impl Plugin for Telemetry { ); custom_instruments.on_request(req); - (req.context.clone(), custom_instruments, custom_attributes) + let supergraph_events = config.instrumentation.events.new_supergraph_events(); + supergraph_events.on_request(req); + + (req.context.clone(), custom_instruments, custom_attributes, supergraph_events) }, - move |(ctx, custom_instruments, custom_attributes): (Context, SupergraphCustomInstruments, Vec), fut| { + move |(ctx, custom_instruments, custom_attributes, supergraph_events): (Context, SupergraphCustomInstruments, Vec, SupergraphEvents), fut| { let config = config_map_res.clone(); let sender = metrics_sender.clone(); let start = Instant::now(); @@ -544,10 +558,12 @@ impl Plugin for Telemetry { Ok(resp) => { span.set_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_response(resp)); custom_instruments.on_response(resp); + supergraph_events.on_response(resp); }, Err(err) => { span.set_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_error(err)); custom_instruments.on_error(err, &ctx); + supergraph_events.on_error(err, &ctx); }, } result = Self::update_otel_metrics( @@ -624,17 +640,21 @@ impl Plugin for Telemetry { .instruments .new_subgraph_instruments(); custom_instruments.on_request(sub_request); + let custom_events = config.instrumentation.events.new_subgraph_events(); + custom_events.on_request(sub_request); ( sub_request.context.clone(), custom_instruments, custom_attributes, + custom_events, ) }, - move |(context, custom_instruments, custom_attributes): ( + move |(context, custom_instruments, custom_attributes, custom_events): ( Context, SubgraphInstruments, Vec, + SubgraphEvents, ), f: BoxFuture<'static, Result>| { let subgraph_attribute = subgraph_attribute.clone(); @@ -662,6 +682,7 @@ impl Plugin for Telemetry { .on_response(resp), ); custom_instruments.on_response(resp); + custom_events.on_response(resp); } Err(err) => { span.record(OTEL_STATUS_CODE, OTEL_STATUS_CODE_ERROR); @@ -670,6 +691,7 @@ impl Plugin for Telemetry { conf.instrumentation.spans.subgraph.attributes.on_error(err), ); custom_instruments.on_error(err, &context); + custom_events.on_error(err, &context); } } diff --git a/apollo-router/src/services/subgraph_service.rs b/apollo-router/src/services/subgraph_service.rs index 048541c32e..57126fb344 100644 --- a/apollo-router/src/services/subgraph_service.rs +++ b/apollo-router/src/services/subgraph_service.rs @@ -50,6 +50,9 @@ use crate::plugins::subscription::SubscriptionConfig; use crate::plugins::subscription::SubscriptionMode; use crate::plugins::subscription::WebSocketConfiguration; use crate::plugins::subscription::SUBSCRIPTION_WS_CUSTOM_CONNECTION_PARAMS; +use crate::plugins::telemetry::config_new::events::log_event; +use crate::plugins::telemetry::config_new::events::SubgraphEventRequestLevel; +use crate::plugins::telemetry::config_new::events::SubgraphEventResponseLevel; use crate::plugins::telemetry::LOGGING_DISPLAY_BODY; use crate::plugins::telemetry::LOGGING_DISPLAY_HEADERS; use crate::protocols::websocket::convert_websocket_stream; @@ -508,6 +511,38 @@ async fn call_websocket( request }; + let subgraph_request_event = context + .extensions() + .lock() + .get::() + .cloned(); + if let Some(level) = subgraph_request_event { + let mut attrs = HashMap::with_capacity(5); + attrs.insert( + "http.request.headers".to_string(), + format!("{:?}", request.headers()), + ); + attrs.insert( + "http.request.method".to_string(), + format!("{}", request.method()), + ); + attrs.insert( + "http.request.version".to_string(), + format!("{:?}", request.version()), + ); + attrs.insert( + "http.request.body".to_string(), + serde_json::to_string(request.body()).unwrap_or_default(), + ); + attrs.insert("subgraph.name".to_string(), service_name.to_string()); + log_event( + level.0, + "subgraph.request", + &attrs, + &format!("Websocket request body to subgraph {service_name:?}"), + ); + } + if display_headers { tracing::info!(http.request.headers = ?request.headers(), apollo.subgraph.name = %service_name, "Websocket request headers to subgraph {service_name:?}"); } @@ -627,6 +662,37 @@ async fn call_http( let (parts, _) = subgraph_request.into_parts(); let body = serde_json::to_string(&body).expect("JSON serialization should not fail"); + + let subgraph_request_event = context + .extensions() + .lock() + .get::() + .cloned(); + if let Some(level) = subgraph_request_event { + let mut attrs = HashMap::with_capacity(5); + attrs.insert( + "http.request.headers".to_string(), + format!("{:?}", parts.headers), + ); + attrs.insert( + "http.request.method".to_string(), + format!("{}", parts.method), + ); + attrs.insert( + "http.request.version".to_string(), + format!("{:?}", parts.version), + ); + attrs.insert("http.request.body".to_string(), body.clone()); + attrs.insert("subgraph.name".to_string(), service_name.to_string()); + + log_event( + level.0, + "subgraph.request", + &attrs, + &format!("Request to subgraph {service_name:?}"), + ); + } + let mut request = http::Request::from_parts(parts, Body::from(body)); request @@ -687,6 +753,40 @@ async fn call_http( .instrument(subgraph_req_span) .await?; + let subgraph_response_event = context + .extensions() + .lock() + .get::() + .cloned(); + if let Some(level) = subgraph_response_event { + let mut attrs = HashMap::with_capacity(5); + attrs.insert( + "http.response.headers".to_string(), + format!("{:?}", parts.headers), + ); + attrs.insert( + "http.response.status".to_string(), + format!("{}", parts.status), + ); + attrs.insert( + "http.response.version".to_string(), + format!("{:?}", parts.version), + ); + if let Some(Ok(b)) = &body { + attrs.insert( + "http.response.body".to_string(), + String::from_utf8_lossy(b).to_string(), + ); + } + attrs.insert("subgraph.name".to_string(), service_name.to_string()); + log_event( + level.0, + "subgraph.response", + &attrs, + &format!("Raw response from subgraph {service_name:?} received"), + ); + } + if display_body { if let Some(Ok(b)) = &body { tracing::info!( diff --git a/apollo-router/src/services/supergraph/service.rs b/apollo-router/src/services/supergraph/service.rs index e1d0c9bf77..cc42c20413 100644 --- a/apollo-router/src/services/supergraph/service.rs +++ b/apollo-router/src/services/supergraph/service.rs @@ -1,5 +1,6 @@ //! Implements the router phase of the request lifecycle. +use std::collections::HashMap; use std::sync::atomic::Ordering; use std::sync::Arc; use std::task::Poll; @@ -32,6 +33,8 @@ use crate::graphql::IntoGraphQLErrors; use crate::graphql::Response; use crate::plugin::DynPlugin; use crate::plugins::subscription::SubscriptionConfig; +use crate::plugins::telemetry::config_new::events::log_event; +use crate::plugins::telemetry::config_new::events::SupergraphEventResponseLevel; use crate::plugins::telemetry::tracing::apollo_telemetry::APOLLO_PRIVATE_DURATION_NS; use crate::plugins::telemetry::Telemetry; use crate::plugins::telemetry::LOGGING_DISPLAY_BODY; @@ -323,10 +326,44 @@ async fn service_call( let (parts, response_stream) = response.into_parts(); - Ok(SupergraphResponse { - context, - response: http::Response::from_parts(parts, response_stream.boxed()), - }) + let supergraph_response_event = context + .extensions() + .lock() + .get::() + .cloned(); + match supergraph_response_event { + Some(level) => { + let mut attrs = HashMap::with_capacity(4); + attrs.insert( + "http.response.headers".to_string(), + format!("{:?}", parts.headers), + ); + attrs.insert( + "http.response.status".to_string(), + format!("{}", parts.status), + ); + attrs.insert( + "http.response.version".to_string(), + format!("{:?}", parts.version), + ); + let response_stream = Box::pin(response_stream.inspect(move |resp| { + attrs.insert( + "http.response.body".to_string(), + serde_json::to_string(resp).unwrap_or_default(), + ); + log_event(level.0, "supergraph.response", &attrs, ""); + })); + + Ok(SupergraphResponse { + context, + response: http::Response::from_parts(parts, response_stream.boxed()), + }) + } + None => Ok(SupergraphResponse { + context, + response: http::Response::from_parts(parts, response_stream.boxed()), + }), + } } } // This should never happen because if we have an empty query plan we should have error in errors vec From 62d400df2928165a275fd7ea5b510495f47c67f8 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:58:17 +0200 Subject: [PATCH 03/40] add tests for events Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../plugins/telemetry/config_new/events.rs | 211 +++++++++++------- ...ew__events__tests__router_events@logs.snap | 136 +++++++++++ ...__events__tests__subgraph_events@logs.snap | 40 ++++ ...events__tests__supergraph_events@logs.snap | 55 +++++ .../src/plugins/telemetry/logging/mod.rs | 12 +- .../testdata/custom_events.router.yaml | 78 +++++++ 6 files changed, 447 insertions(+), 85 deletions(-) create mode 100644 apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap create mode 100644 apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap create mode 100644 apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap create mode 100644 apollo-router/src/plugins/telemetry/testdata/custom_events.router.yaml diff --git a/apollo-router/src/plugins/telemetry/config_new/events.rs b/apollo-router/src/plugins/telemetry/config_new/events.rs index b5d0e32c9f..994d2b0a22 100644 --- a/apollo-router/src/plugins/telemetry/config_new/events.rs +++ b/apollo-router/src/plugins/telemetry/config_new/events.rs @@ -128,85 +128,6 @@ pub(crate) type SupergraphEvents = CustomEvents< pub(crate) type SubgraphEvents = CustomEvents; -// pub(crate) struct RouterEvents { -// request: EventLevel, -// response: EventLevel, -// error: EventLevel, -// custom: Vec>, -// } - -// impl Instrumented for RouterEvents { -// type Request = router::Request; -// type Response = router::Response; - -// fn on_request(&self, request: &Self::Request) { -// if self.request != EventLevel::Off { -// let mut attrs = HashMap::new(); -// attrs.insert( -// "headers".to_string(), -// format!("{:?}", request.router_request.headers()), -// ); -// attrs.insert( -// "method".to_string(), -// format!("{}", request.router_request.method()), -// ); -// attrs.insert( -// "uri".to_string(), -// format!("{}", request.router_request.uri()), -// ); -// attrs.insert( -// "version".to_string(), -// format!("{:?}", request.router_request.version()), -// ); -// attrs.insert( -// "body".to_string(), -// format!("{:?}", request.router_request.body()), -// ); -// log_level(self.request, "router.request", &attrs, ""); -// } -// for custom_event in &self.custom { -// custom_event.on_request(request); -// } -// } - -// fn on_response(&self, response: &Self::Response) { -// if self.response != EventLevel::Off { -// let mut attrs = HashMap::new(); -// attrs.insert( -// "headers".to_string(), -// format!("{:?}", response.response.headers()), -// ); -// attrs.insert( -// "status".to_string(), -// format!("{}", response.response.status()), -// ); -// attrs.insert( -// "version".to_string(), -// format!("{:?}", response.response.version()), -// ); -// attrs.insert( -// "body".to_string(), -// format!("{:?}", response.response.body()), -// ); -// log_level(self.response, "router.response", &attrs, ""); -// } -// for custom_event in &self.custom { -// custom_event.on_response(response); -// } -// } - -// fn on_error(&self, error: &BoxError, ctx: &Context) { -// if self.error != EventLevel::Off { -// let mut attrs = HashMap::new(); -// attrs.insert("error".to_string(), error.to_string()); -// log_level(self.error, "router.error", &attrs, ""); -// } -// for custom_event in &self.custom { -// custom_event.on_error(error, ctx); -// } -// } -// } - pub(crate) struct CustomEvents where Attributes: Selectors + Default, @@ -602,3 +523,135 @@ pub(crate) fn log_event( EventLevel::Off => {} } } + +#[cfg(test)] +mod tests { + use http::header::CONTENT_LENGTH; + use http::HeaderValue; + use tracing::instrument::WithSubscriber; + + use super::*; + use crate::assert_snapshot_subscriber; + use crate::graphql; + use crate::plugins::telemetry::logging::test::PluginTestHarness; + use crate::plugins::telemetry::Telemetry; + + #[tokio::test(flavor = "multi_thread")] + async fn test_router_events() { + let test_harness: PluginTestHarness = PluginTestHarness::builder() + .yaml(include_str!("../testdata/custom_events.router.yaml")) + .build() + .await; + + async { + test_harness + .call_router( + router::Request::fake_builder() + .header(CONTENT_LENGTH, "0") + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .build() + .unwrap(), + |_r| { + router::Response::fake_builder() + .header("custom-header", "val1") + .header(CONTENT_LENGTH, "25") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json_bytes::json!({"data": "res"})) + .build() + .expect("expecting valid response") + }, + ) + .await + .expect("expecting successful response"); + // Without the header to enable custom event + test_harness + .call_router( + router::Request::fake_builder() + .header("custom-header", "val1") + .build() + .unwrap(), + |_r| { + router::Response::fake_builder() + .header("custom-header", "val1") + .data(serde_json_bytes::json!({"data": "res"})) + .build() + .expect("expecting valid response") + }, + ) + .await + .expect("expecting successful response"); + } + .with_subscriber(assert_snapshot_subscriber!()) + .await + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_supergraph_events() { + let test_harness: PluginTestHarness = PluginTestHarness::builder() + .yaml(include_str!("../testdata/custom_events.router.yaml")) + .build() + .await; + + async { + test_harness + .call_supergraph( + supergraph::Request::fake_builder() + .query("query { foo }") + .header("x-log-request", HeaderValue::from_static("log")) + .build() + .unwrap(), + |_r| { + supergraph::Response::fake_builder() + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json::json!({"data": "res"}).to_string()) + .build() + .expect("expecting valid response") + }, + ) + .await + .expect("expecting successful response"); + } + .with_subscriber(assert_snapshot_subscriber!()) + .await + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_subgraph_events() { + let test_harness: PluginTestHarness = PluginTestHarness::builder() + .yaml(include_str!("../testdata/custom_events.router.yaml")) + .build() + .await; + + async { + let mut subgraph_req = http::Request::new( + graphql::Request::fake_builder() + .query("query { foo }") + .build(), + ); + subgraph_req + .headers_mut() + .insert("x-log-request", HeaderValue::from_static("log")); + test_harness + .call_subgraph( + subgraph::Request::fake_builder() + .subgraph_name("subgraph") + .subgraph_request(subgraph_req) + .build(), + |_r| { + subgraph::Response::fake2_builder() + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json::json!({"data": "res"}).to_string()) + .build() + .expect("expecting valid response") + }, + ) + .await + .expect("expecting successful response"); + } + .with_subscriber(assert_snapshot_subscriber!()) + .await + } +} diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap new file mode 100644 index 0000000000..61a06a51a1 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap @@ -0,0 +1,136 @@ +--- +source: apollo-router/src/plugins/telemetry/config_new/events.rs +expression: yaml +--- +- fields: + attributes: "{\"http.request.version\": \"HTTP/1.1\", \"http.request.method\": \"GET\", \"http.request.body\": \"Body(Empty)\", \"http.request.uri\": \"http://example.com/\", \"http.request.headers\": \"{\\\"custom-header\\\": \\\"val1\\\", \\\"content-length\\\": \\\"0\\\", \\\"x-log-request\\\": \\\"log\\\"}\"}" + kind: router.request + level: INFO + message: "" + span: + http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" + spans: + - http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" +- fields: + attributes: "{\"http.request.body.size\": \"0\"}" + kind: my.request_event + level: INFO + message: my event message + span: + http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" + spans: + - http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" +- fields: + attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.headers\": \"{\\\"content-length\\\": \\\"25\\\", \\\"custom-header\\\": \\\"val1\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.response.status\": \"200 OK\", \"http.response.version\": \"HTTP/1.1\"}" + kind: router.response + level: INFO + message: "" + span: + apollo_private.duration_ns: 136286 + http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" + spans: + - apollo_private.duration_ns: 136286 + http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" +- fields: + attributes: "{\"http.response.body.size\": \"25\"}" + kind: my.response_event + level: INFO + message: my response event message + span: + apollo_private.duration_ns: 136286 + http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" + spans: + - apollo_private.duration_ns: 136286 + http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" +- fields: + attributes: "{\"http.request.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.request.uri\": \"http://example.com/\", \"http.request.version\": \"HTTP/1.1\", \"http.request.body\": \"Body(Empty)\", \"http.request.method\": \"GET\"}" + kind: router.request + level: INFO + message: "" + span: + http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" + spans: + - http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" +- fields: + attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.version\": \"HTTP/1.1\", \"http.response.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.response.status\": \"200 OK\"}" + kind: router.response + level: INFO + message: "" + span: + apollo_private.duration_ns: 168441 + http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" + spans: + - apollo_private.duration_ns: 168441 + http.flavor: HTTP/1.1 + http.method: GET + http.request.method: GET + http.route: "http://example.com/" + name: router + otel.kind: INTERNAL + trace_id: "" diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap new file mode 100644 index 0000000000..aadfcf0a00 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap @@ -0,0 +1,40 @@ +--- +source: apollo-router/src/plugins/telemetry/config_new/events.rs +expression: yaml +--- +- fields: + attributes: "{}" + kind: my.request.event + level: INFO + message: my event message + span: + apollo.subgraph.name: subgraph + graphql.document: "query { foo }" + graphql.operation.name: "" + name: subgraph + otel.kind: INTERNAL + spans: + - apollo.subgraph.name: subgraph + graphql.document: "query { foo }" + graphql.operation.name: "" + name: subgraph + otel.kind: INTERNAL +- fields: + attributes: "{\"subgraph.name\": \"subgraph\", \"response_status\": \"200\"}" + kind: my.response.event + level: ERROR + message: my response event message + span: + apollo.subgraph.name: subgraph + graphql.document: "query { foo }" + graphql.operation.name: "" + name: subgraph + otel.kind: INTERNAL + otel.status_code: OK + spans: + - apollo.subgraph.name: subgraph + graphql.document: "query { foo }" + graphql.operation.name: "" + name: subgraph + otel.kind: INTERNAL + otel.status_code: OK diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap new file mode 100644 index 0000000000..e2a9361e25 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap @@ -0,0 +1,55 @@ +--- +source: apollo-router/src/plugins/telemetry/config_new/events.rs +expression: yaml +--- +- fields: + attributes: "{\"http.request.method\": \"POST\", \"http.request.headers\": \"{\\\"x-log-request\\\": \\\"log\\\", \\\"content-type\\\": \\\"application/json\\\"}\", \"http.request.version\": \"HTTP/1.1\", \"http.request.body\": \"{\\\"query\\\":\\\"query { foo }\\\"}\", \"http.request.uri\": \"http://default/\"}" + kind: supergraph.request + level: INFO + message: "" + span: + apollo_private.field_level_instrumentation_ratio: 0.01 + apollo_private.graphql.variables: "{}" + graphql.document: "query { foo }" + name: supergraph + otel.kind: INTERNAL + spans: + - apollo_private.field_level_instrumentation_ratio: 0.01 + apollo_private.graphql.variables: "{}" + graphql.document: "query { foo }" + name: supergraph + otel.kind: INTERNAL +- fields: + attributes: "{}" + kind: my.request.event + level: INFO + message: my event message + span: + apollo_private.field_level_instrumentation_ratio: 0.01 + apollo_private.graphql.variables: "{}" + graphql.document: "query { foo }" + name: supergraph + otel.kind: INTERNAL + spans: + - apollo_private.field_level_instrumentation_ratio: 0.01 + apollo_private.graphql.variables: "{}" + graphql.document: "query { foo }" + name: supergraph + otel.kind: INTERNAL +- fields: + attributes: "{}" + kind: my.response_event + level: WARN + message: my response event message + span: + apollo_private.field_level_instrumentation_ratio: 0.01 + apollo_private.graphql.variables: "{}" + graphql.document: "query { foo }" + name: supergraph + otel.kind: INTERNAL + spans: + - apollo_private.field_level_instrumentation_ratio: 0.01 + apollo_private.graphql.variables: "{}" + graphql.document: "query { foo }" + name: supergraph + otel.kind: INTERNAL diff --git a/apollo-router/src/plugins/telemetry/logging/mod.rs b/apollo-router/src/plugins/telemetry/logging/mod.rs index 1064f28d35..e8c4c6172a 100644 --- a/apollo-router/src/plugins/telemetry/logging/mod.rs +++ b/apollo-router/src/plugins/telemetry/logging/mod.rs @@ -1,6 +1,6 @@ //TODO move telemetry logging functionality to this file #[cfg(test)] -mod test { +pub(crate) mod test { use std::any::TypeId; use tower::BoxError; @@ -149,13 +149,13 @@ mod test { // In particular the `TestHarness` isn't good for testing things with logging. // For now let's try and increase the coverage of the telemetry plugin using this and see how it goes. - struct PluginTestHarness { + pub(crate) struct PluginTestHarness { plugin: Box, phantom: std::marker::PhantomData, } #[buildstructor::buildstructor] impl PluginTestHarness { - #[builder] + #[builder(visibility = "pub(crate)")] async fn new(yaml: Option<&'static str>) -> Self { let factory = crate::plugin::plugins() .find(|factory| factory.type_id == TypeId::of::()) @@ -184,7 +184,7 @@ mod test { } #[allow(dead_code)] - async fn call_router( + pub(crate) async fn call_router( &self, request: router::Request, response_fn: fn(router::Request) -> router::Response, @@ -197,7 +197,7 @@ mod test { self.plugin.router_service(service).call(request).await } - async fn call_supergraph( + pub(crate) async fn call_supergraph( &self, request: supergraph::Request, response_fn: fn(supergraph::Request) -> supergraph::Response, @@ -210,7 +210,7 @@ mod test { self.plugin.supergraph_service(service).call(request).await } - async fn call_subgraph( + pub(crate) async fn call_subgraph( &self, request: subgraph::Request, response_fn: fn(subgraph::Request) -> subgraph::Response, diff --git a/apollo-router/src/plugins/telemetry/testdata/custom_events.router.yaml b/apollo-router/src/plugins/telemetry/testdata/custom_events.router.yaml new file mode 100644 index 0000000000..c355136f65 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/testdata/custom_events.router.yaml @@ -0,0 +1,78 @@ +telemetry: + apollo: + client_name_header: name_header + client_version_header: version_header + instrumentation: + events: + router: + # Standard events + request: info + response: info + error: info + + # Custom events + my.request_event: + message: "my event message" + level: info + on: request + attributes: + http.request.body.size: true + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - request_header: "x-log-request" + my.response_event: + message: "my response event message" + level: info + on: response + attributes: + http.response.body.size: true + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - response_header: "x-log-request" + supergraph: + # Standard events + request: info + response: info + error: info + + # Custom events + my.request.event: + message: "my event message" + level: info + on: request + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - request_header: "x-log-request" + my.response_event: + message: "my response event message" + level: warn + on: response + condition: + eq: + - "log" + - response_header: "x-log-request" + subgraph: + # Standard events + request: info + response: warn + error: error + + # Custom events + my.request.event: + message: "my event message" + level: info + on: request + my.response.event: + message: "my response event message" + level: error + on: response + attributes: + subgraph.name: true + response_status: + subgraph_response_status: code \ No newline at end of file From 28d93cd44aee303a713dd632fab11989c7c71ef2 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 12 Apr 2024 16:22:23 +0200 Subject: [PATCH 04/40] fix test Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .gitignore | 1 + ...nfiguration__tests__schema_generation.snap | 5998 +++++++++++++++++ .../plugins/telemetry/config_new/events.rs | 62 +- ...ew__events__tests__router_events@logs.snap | 20 +- ...events__tests__supergraph_events@logs.snap | 2 +- 5 files changed, 6059 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index ad362c28b0..c8cb2b01a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Generated by Cargo # will have compiled files and executables **/target/ +.cargo_check # These are backup files generated by rustfmt **/*.rs.bk diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index 98ebb36d81..71dfc5c71b 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -6673,6 +6673,6004 @@ expression: "&schema" "description": "Instrumentation configuration", "type": "object", "properties": { + "events": { + "description": "Event configuration", + "type": "object", + "properties": { + "router": { + "description": "Router service events", + "type": "object", + "properties": { + "error": { + "description": "Log the router error", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + }, + "request": { + "description": "Log the router request", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + }, + "response": { + "description": "Log the router response", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + } + }, + "additionalProperties": { + "description": "An event that can be logged as part of a trace. The event has an implicit `type` attribute that matches the name of the event in the yaml and a message that can be used to provide additional information.", + "type": "object", + "required": [ + "level", + "message", + "on" + ], + "properties": { + "attributes": { + "description": "The event attributes.", + "type": "object", + "properties": { + "baggage": { + "description": "All key values from trace baggage.", + "default": null, + "type": "boolean", + "nullable": true + }, + "dd.trace_id": { + "description": "The datadog trace ID. This can be output in logs and used to correlate traces in Datadog.", + "default": null, + "type": "boolean", + "nullable": true + }, + "error.type": { + "description": "Describes a class of error the operation ended with. Examples: * timeout * name_resolution_error * 500 Requirement level: Conditionally Required: If request has ended with an error.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.body.size": { + "description": "The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.method": { + "description": "HTTP request method. Examples: * GET * POST * HEAD Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.body.size": { + "description": "The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.status_code": { + "description": "HTTP response status code. Examples: * 200 Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.route": { + "description": "The matched route (path template in the format used by the respective server framework). Examples: * /graphql Requirement level: Conditionally Required: If and only if it’s available", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.address": { + "description": "Local socket address. Useful in case of a multi-IP host. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.port": { + "description": "Local socket port. Useful in case of a multi-port host. Examples: * 65123 Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.address": { + "description": "Peer address of the network connection - IP address or Unix domain socket name. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.port": { + "description": "Peer port number of the network connection. Examples: * 65123 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.name": { + "description": "OSI application layer or non-OSI equivalent. Examples: * http * spdy Requirement level: Recommended: if not default (http).", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.version": { + "description": "Version of the protocol specified in network.protocol.name. Examples: * 1.0 * 1.1 * 2 * 3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.transport": { + "description": "OSI transport layer. Examples: * tcp * udp Requirement level: Conditionally Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.type": { + "description": "OSI network layer or non-OSI equivalent. Examples: * ipv4 * ipv6 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.address": { + "description": "Name of the local HTTP server that received the request. Examples: * example.com * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.port": { + "description": "Port of the local HTTP server that received the request. Examples: * 80 * 8080 * 443 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "trace_id": { + "description": "The OpenTelemetry trace ID. This can be output in logs.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.path": { + "description": "The URI path component Examples: * /search Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.query": { + "description": "The URI query component Examples: * q=OpenTelemetry Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.scheme": { + "description": "The URI scheme component identifying the used protocol. Examples: * http * https Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "user_agent.original": { + "description": "Value of the HTTP User-Agent header sent by the client. Examples: * CERN-LineMode/2.15 * libwww/2.17b3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "condition": { + "description": "The event conditions.", + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "level": { + "description": "The log level of the event.", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + }, + "message": { + "description": "The event message.", + "type": "string" + }, + "on": { + "description": "When to trigger the event.", + "oneOf": [ + { + "description": "Log the event on request", + "type": "string", + "enum": [ + "request" + ] + }, + { + "description": "Log the event on response", + "type": "string", + "enum": [ + "response" + ] + }, + { + "description": "Log the event on error", + "type": "string", + "enum": [ + "error" + ] + } + ] + } + } + } + }, + "subgraph": { + "description": "Supergraph service events", + "type": "object", + "properties": { + "error": { + "description": "Log the subgraph error", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + }, + "request": { + "description": "Log the subgraph request", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + }, + "response": { + "description": "Log the subgraph response", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + } + }, + "additionalProperties": { + "description": "An event that can be logged as part of a trace. The event has an implicit `type` attribute that matches the name of the event in the yaml and a message that can be used to provide additional information.", + "type": "object", + "required": [ + "level", + "message", + "on" + ], + "properties": { + "attributes": { + "description": "The event attributes.", + "type": "object", + "properties": { + "subgraph.graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.name": { + "description": "The name of the subgraph Examples: * products Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "condition": { + "description": "The event conditions.", + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "level": { + "description": "The log level of the event.", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + }, + "message": { + "description": "The event message.", + "type": "string" + }, + "on": { + "description": "When to trigger the event.", + "oneOf": [ + { + "description": "Log the event on request", + "type": "string", + "enum": [ + "request" + ] + }, + { + "description": "Log the event on response", + "type": "string", + "enum": [ + "response" + ] + }, + { + "description": "Log the event on error", + "type": "string", + "enum": [ + "error" + ] + } + ] + } + } + } + }, + "supergraph": { + "description": "Subgraph service events", + "type": "object", + "properties": { + "error": { + "description": "Log the supergraph error", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + }, + "request": { + "description": "Log the supergraph request", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + }, + "response": { + "description": "Log the supergraph response", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + } + }, + "additionalProperties": { + "description": "An event that can be logged as part of a trace. The event has an implicit `type` attribute that matches the name of the event in the yaml and a message that can be used to provide additional information.", + "type": "object", + "required": [ + "level", + "message", + "on" + ], + "properties": { + "attributes": { + "description": "The event attributes.", + "type": "object", + "properties": { + "graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "condition": { + "description": "The event conditions.", + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "level": { + "description": "The log level of the event.", + "type": "string", + "enum": [ + "info", + "warn", + "error", + "off" + ] + }, + "message": { + "description": "The event message.", + "type": "string" + }, + "on": { + "description": "When to trigger the event.", + "oneOf": [ + { + "description": "Log the event on request", + "type": "string", + "enum": [ + "request" + ] + }, + { + "description": "Log the event on response", + "type": "string", + "enum": [ + "response" + ] + }, + { + "description": "Log the event on error", + "type": "string", + "enum": [ + "error" + ] + } + ] + } + } + } + } + }, + "additionalProperties": false + }, "instruments": { "description": "Instrument configuration", "type": "object", diff --git a/apollo-router/src/plugins/telemetry/config_new/events.rs b/apollo-router/src/plugins/telemetry/config_new/events.rs index 994d2b0a22..b1261fbf97 100644 --- a/apollo-router/src/plugins/telemetry/config_new/events.rs +++ b/apollo-router/src/plugins/telemetry/config_new/events.rs @@ -2,6 +2,8 @@ use std::collections::HashMap; use std::fmt::Debug; use std::sync::Arc; +use http::HeaderName; +use http::HeaderValue; use parking_lot::Mutex; use schemars::JsonSchema; use serde::Deserialize; @@ -148,10 +150,18 @@ impl Instrumented fn on_request(&self, request: &Self::Request) { if self.request != EventLevel::Off { let mut attrs = HashMap::with_capacity(5); - attrs.insert( - "http.request.headers".to_string(), - format!("{:?}", request.router_request.headers()), - ); + // #[cfg(test)] + // let headers: indexmap::IndexMap = request + // .router_request + // .headers() + // .clone() + // .into_iter() + // .filter_map(|(name, val)| Some((name?, val))) + // .collect(); + // #[cfg(not(test))] + let headers = request.router_request.headers(); + + attrs.insert("http.request.headers".to_string(), format!("{:?}", headers)); attrs.insert( "http.request.method".to_string(), format!("{}", request.router_request.method()), @@ -178,9 +188,20 @@ impl Instrumented fn on_response(&self, response: &Self::Response) { if self.response != EventLevel::Off { let mut attrs = HashMap::with_capacity(4); + + // #[cfg(test)] + // let headers: indexmap::IndexMap = response + // .response + // .headers() + // .clone() + // .into_iter() + // .filter_map(|(name, val)| Some((name?, val))) + // .collect(); + // #[cfg(not(test))] + let headers = response.response.headers(); attrs.insert( "http.response.headers".to_string(), - format!("{:?}", response.response.headers()), + format!("{:?}", headers), ); attrs.insert( "http.response.status".to_string(), @@ -226,11 +247,18 @@ impl Instrumented fn on_request(&self, request: &Self::Request) { if self.request != EventLevel::Off { - let mut attrs = HashMap::new(); - attrs.insert( - "http.request.headers".to_string(), - format!("{:?}", request.supergraph_request.headers()), - ); + let mut attrs = HashMap::with_capacity(5); + // #[cfg(test)] + // let headers: indexmap::IndexMap = request + // .supergraph_request + // .headers() + // .clone() + // .into_iter() + // .filter_map(|(name, val)| Some((name?, val))) + // .collect(); + // #[cfg(not(test))] + let headers = request.supergraph_request.headers(); + attrs.insert("http.request.headers".to_string(), format!("{:?}", headers)); attrs.insert( "http.request.method".to_string(), format!("{}", request.supergraph_request.method()), @@ -269,7 +297,7 @@ impl Instrumented fn on_error(&self, error: &BoxError, ctx: &Context) { if self.error != EventLevel::Off { - let mut attrs = HashMap::new(); + let mut attrs = HashMap::with_capacity(1); attrs.insert("error".to_string(), error.to_string()); log_event(self.error, "supergraph.error", &attrs, ""); } @@ -313,7 +341,8 @@ impl Instrumented fn on_error(&self, error: &BoxError, ctx: &Context) { if self.error != EventLevel::Off { - let mut attrs = HashMap::new(); + let mut attrs = HashMap::with_capacity(1); + attrs.insert("error".to_string(), error.to_string()); log_event(self.error, "subgraph.error", &attrs, ""); } @@ -503,6 +532,7 @@ where .iter() .map(|kv| (kv.key.to_string(), kv.value.to_string())) .collect(); + log_event(self.level, &self.name, &attributes, &self.message); } } @@ -514,6 +544,9 @@ pub(crate) fn log_event( attributes: &HashMap, message: &str, ) { + #[cfg(test)] + let attributes: indexmap::IndexMap = attributes.clone().into_iter().collect(); + match level { EventLevel::Info => { ::tracing::info!(%kind, attributes = ?attributes, "{}", message); @@ -582,8 +615,11 @@ mod tests { .await .expect("expecting successful response"); } - .with_subscriber(assert_snapshot_subscriber!()) + .with_subscriber( + assert_snapshot_subscriber!({r#"[].span["apollo_private.duration_ns"]"# => "[duration]", r#"[].spans[]["apollo_private.duration_ns"]"# => "[duration]", "[].fields.attributes" => insta::sorted_redaction()}), + ) .await + // TODO check attributes individually instead of a snapshot because it's not sorted } #[tokio::test(flavor = "multi_thread")] diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap index 61a06a51a1..99194e8e96 100644 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap @@ -3,7 +3,7 @@ source: apollo-router/src/plugins/telemetry/config_new/events.rs expression: yaml --- - fields: - attributes: "{\"http.request.version\": \"HTTP/1.1\", \"http.request.method\": \"GET\", \"http.request.body\": \"Body(Empty)\", \"http.request.uri\": \"http://example.com/\", \"http.request.headers\": \"{\\\"custom-header\\\": \\\"val1\\\", \\\"content-length\\\": \\\"0\\\", \\\"x-log-request\\\": \\\"log\\\"}\"}" + attributes: "{\"http.request.method\": \"GET\", \"http.request.body\": \"Body(Empty)\", \"http.request.version\": \"HTTP/1.1\", \"http.request.uri\": \"http://example.com/\", \"http.request.headers\": \"{\\\"x-log-request\\\": \\\"log\\\", \\\"content-length\\\": \\\"0\\\", \\\"custom-header\\\": \\\"val1\\\"}\"}" kind: router.request level: INFO message: "" @@ -45,12 +45,12 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.headers\": \"{\\\"content-length\\\": \\\"25\\\", \\\"custom-header\\\": \\\"val1\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.response.status\": \"200 OK\", \"http.response.version\": \"HTTP/1.1\"}" + attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.status\": \"200 OK\", \"http.response.headers\": \"{\\\"custom-header\\\": \\\"val1\\\", \\\"content-length\\\": \\\"25\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.response.version\": \"HTTP/1.1\"}" kind: router.response level: INFO message: "" span: - apollo_private.duration_ns: 136286 + apollo_private.duration_ns: "[duration]" http.flavor: HTTP/1.1 http.method: GET http.request.method: GET @@ -59,7 +59,7 @@ expression: yaml otel.kind: INTERNAL trace_id: "" spans: - - apollo_private.duration_ns: 136286 + - apollo_private.duration_ns: "[duration]" http.flavor: HTTP/1.1 http.method: GET http.request.method: GET @@ -73,7 +73,7 @@ expression: yaml level: INFO message: my response event message span: - apollo_private.duration_ns: 136286 + apollo_private.duration_ns: "[duration]" http.flavor: HTTP/1.1 http.method: GET http.request.method: GET @@ -82,7 +82,7 @@ expression: yaml otel.kind: INTERNAL trace_id: "" spans: - - apollo_private.duration_ns: 136286 + - apollo_private.duration_ns: "[duration]" http.flavor: HTTP/1.1 http.method: GET http.request.method: GET @@ -91,7 +91,7 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.request.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.request.uri\": \"http://example.com/\", \"http.request.version\": \"HTTP/1.1\", \"http.request.body\": \"Body(Empty)\", \"http.request.method\": \"GET\"}" + attributes: "{\"http.request.method\": \"GET\", \"http.request.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.request.version\": \"HTTP/1.1\", \"http.request.body\": \"Body(Empty)\", \"http.request.uri\": \"http://example.com/\"}" kind: router.request level: INFO message: "" @@ -112,12 +112,12 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.version\": \"HTTP/1.1\", \"http.response.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.response.status\": \"200 OK\"}" + attributes: "{\"http.response.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.response.version\": \"HTTP/1.1\", \"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.status\": \"200 OK\"}" kind: router.response level: INFO message: "" span: - apollo_private.duration_ns: 168441 + apollo_private.duration_ns: "[duration]" http.flavor: HTTP/1.1 http.method: GET http.request.method: GET @@ -126,7 +126,7 @@ expression: yaml otel.kind: INTERNAL trace_id: "" spans: - - apollo_private.duration_ns: 168441 + - apollo_private.duration_ns: "[duration]" http.flavor: HTTP/1.1 http.method: GET http.request.method: GET diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap index e2a9361e25..02f32fd155 100644 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap @@ -3,7 +3,7 @@ source: apollo-router/src/plugins/telemetry/config_new/events.rs expression: yaml --- - fields: - attributes: "{\"http.request.method\": \"POST\", \"http.request.headers\": \"{\\\"x-log-request\\\": \\\"log\\\", \\\"content-type\\\": \\\"application/json\\\"}\", \"http.request.version\": \"HTTP/1.1\", \"http.request.body\": \"{\\\"query\\\":\\\"query { foo }\\\"}\", \"http.request.uri\": \"http://default/\"}" + attributes: "{\"http.request.version\": \"HTTP/1.1\", \"http.request.uri\": \"http://default/\", \"http.request.body\": \"{\\\"query\\\":\\\"query { foo }\\\"}\", \"http.request.headers\": \"{\\\"content-type\\\": \\\"application/json\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.request.method\": \"POST\"}" kind: supergraph.request level: INFO message: "" From 5bc0287313dc027782b9d45c105796e7247b55e1 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 15 Apr 2024 10:42:20 +0200 Subject: [PATCH 05/40] fix test and ordering of attributes and headers Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../plugins/telemetry/config_new/events.rs | 66 +++++++++++-------- ...ew__events__tests__router_events@logs.snap | 8 +-- ...__events__tests__subgraph_events@logs.snap | 2 +- ...events__tests__supergraph_events@logs.snap | 2 +- 4 files changed, 43 insertions(+), 35 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/events.rs b/apollo-router/src/plugins/telemetry/config_new/events.rs index b1261fbf97..057201e326 100644 --- a/apollo-router/src/plugins/telemetry/config_new/events.rs +++ b/apollo-router/src/plugins/telemetry/config_new/events.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::fmt::Debug; use std::sync::Arc; -use http::HeaderName; use http::HeaderValue; use parking_lot::Mutex; use schemars::JsonSchema; @@ -150,15 +149,17 @@ impl Instrumented fn on_request(&self, request: &Self::Request) { if self.request != EventLevel::Off { let mut attrs = HashMap::with_capacity(5); - // #[cfg(test)] - // let headers: indexmap::IndexMap = request - // .router_request - // .headers() - // .clone() - // .into_iter() - // .filter_map(|(name, val)| Some((name?, val))) - // .collect(); - // #[cfg(not(test))] + #[cfg(test)] + let mut headers: indexmap::IndexMap = request + .router_request + .headers() + .clone() + .into_iter() + .filter_map(|(name, val)| Some((name?.to_string(), val))) + .collect(); + #[cfg(test)] + headers.sort_keys(); + #[cfg(not(test))] let headers = request.router_request.headers(); attrs.insert("http.request.headers".to_string(), format!("{:?}", headers)); @@ -189,15 +190,17 @@ impl Instrumented if self.response != EventLevel::Off { let mut attrs = HashMap::with_capacity(4); - // #[cfg(test)] - // let headers: indexmap::IndexMap = response - // .response - // .headers() - // .clone() - // .into_iter() - // .filter_map(|(name, val)| Some((name?, val))) - // .collect(); - // #[cfg(not(test))] + #[cfg(test)] + let mut headers: indexmap::IndexMap = response + .response + .headers() + .clone() + .into_iter() + .filter_map(|(name, val)| Some((name?.to_string(), val))) + .collect(); + #[cfg(test)] + headers.sort_keys(); + #[cfg(not(test))] let headers = response.response.headers(); attrs.insert( "http.response.headers".to_string(), @@ -248,15 +251,17 @@ impl Instrumented fn on_request(&self, request: &Self::Request) { if self.request != EventLevel::Off { let mut attrs = HashMap::with_capacity(5); - // #[cfg(test)] - // let headers: indexmap::IndexMap = request - // .supergraph_request - // .headers() - // .clone() - // .into_iter() - // .filter_map(|(name, val)| Some((name?, val))) - // .collect(); - // #[cfg(not(test))] + #[cfg(test)] + let mut headers: indexmap::IndexMap = request + .supergraph_request + .headers() + .clone() + .into_iter() + .filter_map(|(name, val)| Some((name?.to_string(), val))) + .collect(); + #[cfg(test)] + headers.sort_keys(); + #[cfg(not(test))] let headers = request.supergraph_request.headers(); attrs.insert("http.request.headers".to_string(), format!("{:?}", headers)); attrs.insert( @@ -545,7 +550,10 @@ pub(crate) fn log_event( message: &str, ) { #[cfg(test)] - let attributes: indexmap::IndexMap = attributes.clone().into_iter().collect(); + let mut attributes: indexmap::IndexMap = + attributes.clone().into_iter().collect(); + #[cfg(test)] + attributes.sort_keys(); match level { EventLevel::Info => { diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap index 99194e8e96..836cf272f7 100644 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap @@ -3,7 +3,7 @@ source: apollo-router/src/plugins/telemetry/config_new/events.rs expression: yaml --- - fields: - attributes: "{\"http.request.method\": \"GET\", \"http.request.body\": \"Body(Empty)\", \"http.request.version\": \"HTTP/1.1\", \"http.request.uri\": \"http://example.com/\", \"http.request.headers\": \"{\\\"x-log-request\\\": \\\"log\\\", \\\"content-length\\\": \\\"0\\\", \\\"custom-header\\\": \\\"val1\\\"}\"}" + attributes: "{\"http.request.body\": \"Body(Empty)\", \"http.request.headers\": \"{\\\"content-length\\\": \\\"0\\\", \\\"custom-header\\\": \\\"val1\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.request.method\": \"GET\", \"http.request.uri\": \"http://example.com/\", \"http.request.version\": \"HTTP/1.1\"}" kind: router.request level: INFO message: "" @@ -45,7 +45,7 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.status\": \"200 OK\", \"http.response.headers\": \"{\\\"custom-header\\\": \\\"val1\\\", \\\"content-length\\\": \\\"25\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.response.version\": \"HTTP/1.1\"}" + attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.headers\": \"{\\\"content-length\\\": \\\"25\\\", \\\"custom-header\\\": \\\"val1\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.response.status\": \"200 OK\", \"http.response.version\": \"HTTP/1.1\"}" kind: router.response level: INFO message: "" @@ -91,7 +91,7 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.request.method\": \"GET\", \"http.request.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.request.version\": \"HTTP/1.1\", \"http.request.body\": \"Body(Empty)\", \"http.request.uri\": \"http://example.com/\"}" + attributes: "{\"http.request.body\": \"Body(Empty)\", \"http.request.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.request.method\": \"GET\", \"http.request.uri\": \"http://example.com/\", \"http.request.version\": \"HTTP/1.1\"}" kind: router.request level: INFO message: "" @@ -112,7 +112,7 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.response.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.response.version\": \"HTTP/1.1\", \"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.status\": \"200 OK\"}" + attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.response.status\": \"200 OK\", \"http.response.version\": \"HTTP/1.1\"}" kind: router.response level: INFO message: "" diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap index aadfcf0a00..4a89ba611a 100644 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap @@ -20,7 +20,7 @@ expression: yaml name: subgraph otel.kind: INTERNAL - fields: - attributes: "{\"subgraph.name\": \"subgraph\", \"response_status\": \"200\"}" + attributes: "{\"response_status\": \"200\", \"subgraph.name\": \"subgraph\"}" kind: my.response.event level: ERROR message: my response event message diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap index 02f32fd155..b74445222b 100644 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap @@ -3,7 +3,7 @@ source: apollo-router/src/plugins/telemetry/config_new/events.rs expression: yaml --- - fields: - attributes: "{\"http.request.version\": \"HTTP/1.1\", \"http.request.uri\": \"http://default/\", \"http.request.body\": \"{\\\"query\\\":\\\"query { foo }\\\"}\", \"http.request.headers\": \"{\\\"content-type\\\": \\\"application/json\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.request.method\": \"POST\"}" + attributes: "{\"http.request.body\": \"{\\\"query\\\":\\\"query { foo }\\\"}\", \"http.request.headers\": \"{\\\"content-type\\\": \\\"application/json\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.request.method\": \"POST\", \"http.request.uri\": \"http://default/\", \"http.request.version\": \"HTTP/1.1\"}" kind: supergraph.request level: INFO message: "" From 2e5694741060b04199e536dd44537118b2aead63 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:17:43 +0200 Subject: [PATCH 06/40] fix lint Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/plugins/telemetry/config_new/events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/events.rs b/apollo-router/src/plugins/telemetry/config_new/events.rs index 057201e326..a2a5fa2365 100644 --- a/apollo-router/src/plugins/telemetry/config_new/events.rs +++ b/apollo-router/src/plugins/telemetry/config_new/events.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::fmt::Debug; use std::sync::Arc; +#[cfg(test)] use http::HeaderValue; use parking_lot::Mutex; use schemars::JsonSchema; @@ -627,7 +628,6 @@ mod tests { assert_snapshot_subscriber!({r#"[].span["apollo_private.duration_ns"]"# => "[duration]", r#"[].spans[]["apollo_private.duration_ns"]"# => "[duration]", "[].fields.attributes" => insta::sorted_redaction()}), ) .await - // TODO check attributes individually instead of a snapshot because it's not sorted } #[tokio::test(flavor = "multi_thread")] From 8ef1a154e7f9ebba44ba111c071325288a32d741 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:19:58 +0200 Subject: [PATCH 07/40] delete useless commment Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/plugins/telemetry/config_new/conditions.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditions.rs b/apollo-router/src/plugins/telemetry/config_new/conditions.rs index fd9055279b..773b4a4d82 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditions.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditions.rs @@ -52,7 +52,6 @@ impl Condition where T: Selector, { - // Returns Some(false) if it doesn't fulfill the condition pub(crate) fn evaluate_request(&mut self, request: &T::Request) -> Option { match self { Condition::Eq(eq) => match (eq[0].on_request(request), eq[1].on_request(request)) { From 9bba758d3d4cd713eb7106ec177003eeb205ed1a Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:30:45 +0200 Subject: [PATCH 08/40] update metrics Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/configuration/metrics.rs | 6 ++ ...__test__metrics@telemetry.router.yaml.snap | 5 +- .../testdata/metrics/telemetry.router.yaml | 75 ++++++++++++++++++- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/configuration/metrics.rs b/apollo-router/src/configuration/metrics.rs index fa9a4d4a67..fdf5c12be1 100644 --- a/apollo-router/src/configuration/metrics.rs +++ b/apollo-router/src/configuration/metrics.rs @@ -308,6 +308,12 @@ impl InstrumentData { "$..tracing.zipkin[?(@.enabled==true)]", opt.events, "$..events", + opt.events.router, + "$..events.router", + opt.events.supergraph, + "$..events.supergraph", + opt.events.subgraph, + "$..events.subgraph", opt.instruments, "$..instruments", opt.instruments.router, diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap index 842ddd0340..1431bd3f5a 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap @@ -7,7 +7,10 @@ expression: "&metrics.non_zero()" datapoints: - value: 1 attributes: - opt.events: false + opt.events: true + opt.events.router: true + opt.events.subgraph: true + opt.events.supergraph: true opt.instruments: true opt.instruments.default_attribute_requirement_level: false opt.instruments.router: true diff --git a/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml b/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml index b67bf4fc3b..b7942cf3de 100644 --- a/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml +++ b/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml @@ -98,4 +98,77 @@ telemetry: exists: subgraph_response_data: "$.products[*].price1" attributes: - subgraph.name: true \ No newline at end of file + subgraph.name: true + events: + router: + # Standard events + request: info + response: info + error: info + + # Custom events + my.request_event: + message: "my event message" + level: info + on: request + attributes: + http.request.body.size: true + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - request_header: "x-log-request" + my.response_event: + message: "my response event message" + level: info + on: response + attributes: + http.response.body.size: true + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - response_header: "x-log-request" + supergraph: + # Standard events + request: info + response: info + error: info + + # Custom events + my.request.event: + message: "my event message" + level: info + on: request + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - request_header: "x-log-request" + my.response_event: + message: "my response event message" + level: warn + on: response + condition: + eq: + - "log" + - response_header: "x-log-request" + subgraph: + # Standard events + request: info + response: warn + error: error + + # Custom events + my.request.event: + message: "my event message" + level: info + on: request + my.response.event: + message: "my response event message" + level: error + on: response + attributes: + subgraph.name: true + response_status: + subgraph_response_status: code \ No newline at end of file From 3235c5734249b42779820ba23f5b7840042ed67c Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:21:56 +0200 Subject: [PATCH 09/40] wip Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/logging/mod.rs | 4 +- .../plugins/telemetry/dynamic_attribute.rs | 205 ++- .../src/plugins/telemetry/fmt_layer.rs | 12 +- .../src/plugins/telemetry/formatters/json.rs | 10 +- apollo-router/src/plugins/telemetry/mod.rs | 22 +- .../src/plugins/telemetry/otel/layer.rs | 1382 +++++++++++++++++ .../src/plugins/telemetry/otel/mod.rs | 23 + .../src/plugins/telemetry/otel/span_ext.rs | 209 +++ .../src/plugins/telemetry/otel/tracer.rs | 233 +++ apollo-router/src/plugins/telemetry/reload.rs | 7 +- 10 files changed, 2071 insertions(+), 36 deletions(-) create mode 100644 apollo-router/src/plugins/telemetry/otel/layer.rs create mode 100644 apollo-router/src/plugins/telemetry/otel/mod.rs create mode 100644 apollo-router/src/plugins/telemetry/otel/span_ext.rs create mode 100644 apollo-router/src/plugins/telemetry/otel/tracer.rs diff --git a/apollo-router/src/logging/mod.rs b/apollo-router/src/logging/mod.rs index 5d0b75879d..a78552fda5 100644 --- a/apollo-router/src/logging/mod.rs +++ b/apollo-router/src/logging/mod.rs @@ -41,7 +41,7 @@ pub(crate) mod test { use tracing_core::Subscriber; use tracing_subscriber::layer::SubscriberExt; - use crate::plugins::telemetry::dynamic_attribute::DynAttributeLayer; + use crate::plugins::telemetry::dynamic_attribute::DynSpanAttributeLayer; pub(crate) struct SnapshotSubscriber { buffer: Arc>>, @@ -104,7 +104,7 @@ pub(crate) mod test { tracing_subscriber::registry::Registry::default() .with(level) - .with(DynAttributeLayer::new()) + .with(DynSpanAttributeLayer::new()) .with( tracing_subscriber::fmt::Layer::default() .json() diff --git a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs index 42cfa60703..3c47009e6c 100644 --- a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs +++ b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs @@ -1,5 +1,9 @@ +use std::collections::HashMap; + use opentelemetry::Key; use opentelemetry::KeyValue; +use tracing::field::Visit; +use tracing::Event; use tracing_opentelemetry::OtelData; use tracing_subscriber::layer::Context; use tracing_subscriber::registry::LookupSpan; @@ -9,6 +13,8 @@ use tracing_subscriber::Registry; use super::reload::IsSampled; use super::tracing::APOLLO_PRIVATE_PREFIX; +pub(crate) const APOLLO_PRIVATE_KIND: &str = "apollo_private.kind"; + #[derive(Debug, Default)] pub(crate) struct LogAttributes { attributes: Vec, @@ -28,9 +34,10 @@ impl LogAttributes { } } -pub(crate) struct DynAttributeLayer; +/// To add dynamic attributes for spans +pub(crate) struct DynSpanAttributeLayer; -impl Layer for DynAttributeLayer +impl Layer for DynSpanAttributeLayer where S: tracing_core::Subscriber + for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>, { @@ -48,19 +55,20 @@ where } } -impl DynAttributeLayer { +impl DynSpanAttributeLayer { pub(crate) fn new() -> Self { Self {} } } -pub(crate) trait DynAttribute { - fn set_dyn_attribute(&self, key: Key, value: opentelemetry::Value); - fn set_dyn_attributes(&self, attributes: impl IntoIterator); +/// To add dynamic attributes for spans +pub(crate) trait SpanDynAttribute { + fn set_span_dyn_attribute(&self, key: Key, value: opentelemetry::Value); + fn set_span_dyn_attributes(&self, attributes: impl IntoIterator); } -impl DynAttribute for ::tracing::Span { - fn set_dyn_attribute(&self, key: Key, value: opentelemetry::Value) { +impl SpanDynAttribute for ::tracing::Span { + fn set_span_dyn_attribute(&self, key: Key, value: opentelemetry::Value) { self.with_subscriber(move |(id, dispatch)| { if let Some(reg) = dispatch.downcast_ref::() { match reg.span(id) { @@ -110,7 +118,7 @@ impl DynAttribute for ::tracing::Span { }); } - fn set_dyn_attributes(&self, attributes: impl IntoIterator) { + fn set_span_dyn_attributes(&self, attributes: impl IntoIterator) { let mut attributes = attributes.into_iter().peekable(); if attributes.peek().is_none() { return; @@ -166,3 +174,182 @@ impl DynAttribute for ::tracing::Span { }); } } + +// TODO il me faut un autre layer juste pour les attributs d'events et il faut qu'il passe après celui de otel + +pub(crate) struct EventsAttributes { + pub(crate) events_attributes: HashMap, +} + +impl Default for EventsAttributes { + fn default() -> Self { + Self { + events_attributes: HashMap::with_capacity(0), + } + } +} + +/// To add dynamic attributes for spans +pub(crate) struct DynEventAttributeLayer; + +impl Layer for DynEventAttributeLayer +where + S: tracing_core::Subscriber + for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>, +{ + fn on_new_span( + &self, + _attrs: &tracing_core::span::Attributes<'_>, + id: &tracing_core::span::Id, + ctx: Context<'_, S>, + ) { + let span = ctx.span(id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + if extensions.get_mut::().is_none() { + extensions.insert(EventsAttributes::default()); + } + } + + /// Notifies this layer that an event has occurred. + fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { + // Je récupère mes eventsAttributes et specifiquement mon attribut pour l'event et je l'ajoute à mon otelData events.last() + let mut event_kind = EventKindVisitor::default(); + event.record(&mut event_kind); + if let Some(event_kind) = event_kind.0 { + let span = ctx.event_span(event); + if let Some(span) = span { + let mut extensions = span.extensions_mut(); + if let (Some(attributes), Some(otel_events)) = ( + extensions + .get::() + .and_then(|attrs| attrs.events_attributes.get(&event_kind)), + extensions + .get_mut::() + .and_then(|od| od.builder.events.as_mut()) + .and_then(|e| e.last_mut()), + ) { + // otel_data.builder.events. + } + } + } + } + + // The best solution might be to directly fetch eventsAttributes from otel layer +} + +impl DynEventAttributeLayer { + pub(crate) fn new() -> Self { + Self {} + } +} + +#[derive(Default)] +struct EventKindVisitor(Option); + +impl Visit for EventKindVisitor { + fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn std::fmt::Debug) { + if field.name() == APOLLO_PRIVATE_KIND { + self.0 = Some(format!("{value:?}")); + } + } + + fn record_str(&mut self, field: &tracing_core::Field, value: &str) { + if field.name() == APOLLO_PRIVATE_KIND { + self.0 = Some(value.to_string()); + } + } +} + +/// To add dynamic attributes for spans +pub(crate) trait EventDynAttribute { + fn set_event_dyn_attribute(&self, event_name: String, key: Key, value: opentelemetry::Value); + fn set_event_dyn_attributes( + &self, + event_name: String, + attributes: impl IntoIterator, + ); +} + +impl EventDynAttribute for ::tracing::Span { + fn set_event_dyn_attribute(&self, event_name: String, key: Key, value: opentelemetry::Value) { + self.with_subscriber(move |(id, dispatch)| { + if let Some(reg) = dispatch.downcast_ref::() { + match reg.span(id) { + None => eprintln!("no spanref, this is a bug"), + Some(s) => { + if key.as_str().starts_with(APOLLO_PRIVATE_PREFIX) { + return; + } + let mut extensions = s.extensions_mut(); + match extensions.get_mut::() { + Some(attributes) => { + match attributes.events_attributes.get_mut(&event_name) { + Some(attributes) => { + attributes.insert(KeyValue::new(key, value)); + } + None => { + attributes.events_attributes.insert( + event_name, + LogAttributes { + attributes: vec![KeyValue::new(key, value)], + }, + ); + } + } + } + None => { + // Can't use ::tracing::error! because it could create deadlock on extensions + eprintln!("no EventsAttributes, this is a bug"); + } + } + } + }; + } else { + ::tracing::error!("no Registry, this is a bug"); + } + }); + } + + fn set_event_dyn_attributes( + &self, + event_name: String, + attributes: impl IntoIterator, + ) { + let mut attributes = attributes.into_iter().peekable(); + if attributes.peek().is_none() { + return; + } + self.with_subscriber(move |(id, dispatch)| { + if let Some(reg) = dispatch.downcast_ref::() { + match reg.span(id) { + None => eprintln!("no spanref, this is a bug"), + Some(s) => { + let mut extensions = s.extensions_mut(); + match extensions.get_mut::() { + Some(registered_attributes) => { + match registered_attributes.events_attributes.get_mut(&event_name) { + Some(registered_attributes) => { + registered_attributes.extend(attributes); + } + None => { + registered_attributes.events_attributes.insert( + event_name, + LogAttributes { + attributes: attributes.collect(), + }, + ); + } + } + } + None => { + // Can't use ::tracing::error! because it could create deadlock on extensions + eprintln!("no EventsAttributes, this is a bug"); + } + } + } + }; + } else { + ::tracing::error!("no Registry, this is a bug"); + } + }); + } +} diff --git a/apollo-router/src/plugins/telemetry/fmt_layer.rs b/apollo-router/src/plugins/telemetry/fmt_layer.rs index ff28719fdb..614b95069c 100644 --- a/apollo-router/src/plugins/telemetry/fmt_layer.rs +++ b/apollo-router/src/plugins/telemetry/fmt_layer.rs @@ -274,7 +274,7 @@ mod tests { use crate::plugins::telemetry::config_new::logging::JsonFormat; use crate::plugins::telemetry::config_new::logging::RateLimit; use crate::plugins::telemetry::config_new::logging::TextFormat; - use crate::plugins::telemetry::dynamic_attribute::DynAttribute; + use crate::plugins::telemetry::dynamic_attribute::SpanDynAttribute; #[derive(Default, Clone)] struct LogBuffer(Arc>>); @@ -311,8 +311,8 @@ mod tests { first = "one", apollo_private.should_not_display = "this should be skipped" ); - test_span.set_dyn_attribute("another".into(), 2.into()); - test_span.set_dyn_attribute("custom_dyn".into(), "test".into()); + test_span.set_span_dyn_attribute("another".into(), 2.into()); + test_span.set_span_dyn_attribute("custom_dyn".into(), "test".into()); let _enter = test_span.enter(); info!(event_attr = "foo", "Hello from test"); } @@ -323,8 +323,8 @@ mod tests { first = "one", apollo_private.should_not_display = "this should be skipped" ); - test_span.set_dyn_attribute("another".into(), 2.into()); - test_span.set_dyn_attribute("custom_dyn".into(), "test".into()); + test_span.set_span_dyn_attribute("another".into(), 2.into()); + test_span.set_span_dyn_attribute("custom_dyn".into(), "test".into()); let _enter = test_span.enter(); { let nested_test_span = info_span!( @@ -334,7 +334,7 @@ mod tests { ); let _enter = nested_test_span.enter(); - nested_test_span.set_dyn_attributes([ + nested_test_span.set_span_dyn_attributes([ KeyValue::new("inner", -42_i64), KeyValue::new("graphql.operation.kind", "Subscription"), ]); diff --git a/apollo-router/src/plugins/telemetry/formatters/json.rs b/apollo-router/src/plugins/telemetry/formatters/json.rs index 0af89c2a8b..dbca144005 100644 --- a/apollo-router/src/plugins/telemetry/formatters/json.rs +++ b/apollo-router/src/plugins/telemetry/formatters/json.rs @@ -368,8 +368,8 @@ mod test { use tracing_subscriber::Layer; use tracing_subscriber::Registry; - use crate::plugins::telemetry::dynamic_attribute::DynAttribute; - use crate::plugins::telemetry::dynamic_attribute::DynAttributeLayer; + use crate::plugins::telemetry::dynamic_attribute::DynSpanAttributeLayer; + use crate::plugins::telemetry::dynamic_attribute::SpanDynAttribute; use crate::plugins::telemetry::formatters::json::extract_dd_trace_id; struct RequiresDatadogLayer; @@ -407,11 +407,11 @@ mod test { subscriber::with_default( Registry::default() .with(RequiresDatadogLayer) - .with(DynAttributeLayer) + .with(DynSpanAttributeLayer) .with(tracing_opentelemetry::layer()), || { let root_span = tracing::info_span!("root"); - root_span.set_dyn_attribute("dd.trace_id".into(), "1234".into()); + root_span.set_span_dyn_attribute("dd.trace_id".into(), "1234".into()); let _root_span = root_span.enter(); tracing::info!("test"); }, @@ -424,7 +424,7 @@ mod test { subscriber::with_default( Registry::default() .with(RequiresDatadogLayer) - .with(DynAttributeLayer) + .with(DynSpanAttributeLayer) .with(tracing_opentelemetry::layer()), || { let root_span = tracing::info_span!("root"); diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index 5bc482462b..98b7982b7b 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -92,7 +92,7 @@ use crate::plugins::telemetry::apollo_exporter::proto::reports::StatsContext; use crate::plugins::telemetry::config::AttributeValue; use crate::plugins::telemetry::config::MetricsCommon; use crate::plugins::telemetry::config::TracingCommon; -use crate::plugins::telemetry::dynamic_attribute::DynAttribute; +use crate::plugins::telemetry::dynamic_attribute::SpanDynAttribute; use crate::plugins::telemetry::fmt_layer::create_fmt_layer; use crate::plugins::telemetry::metrics::apollo::studio::SingleContextualizedStats; use crate::plugins::telemetry::metrics::apollo::studio::SinglePathErrorStats; @@ -334,7 +334,7 @@ impl Plugin for Telemetry { if !use_legacy_request_span { let span = Span::current(); - span.set_dyn_attribute( + span.set_span_dyn_attribute( HTTP_REQUEST_METHOD, request.router_request.method().to_string().into(), ); @@ -410,7 +410,7 @@ impl Plugin for Telemetry { async move { let span = Span::current(); - span.set_dyn_attributes(custom_attributes); + span.set_span_dyn_attributes(custom_attributes); let response: Result = fut.await; span.record( @@ -420,7 +420,7 @@ impl Plugin for Telemetry { let expose_trace_id = &config.exporters.tracing.response_trace_id; if let Ok(response) = &response { - span.set_dyn_attributes( + span.set_span_dyn_attributes( config .instrumentation .spans @@ -482,7 +482,7 @@ impl Plugin for Telemetry { } } else if let Err(err) = &response { span.record(OTEL_STATUS_CODE, OTEL_STATUS_CODE_ERROR); - span.set_dyn_attributes( + span.set_span_dyn_attributes( config.instrumentation.spans.router.attributes.on_error(err), ); custom_instruments.on_error(err, &ctx); @@ -587,16 +587,16 @@ impl Plugin for Telemetry { async move { let span = Span::current(); - span.set_dyn_attributes(custom_attributes); + span.set_span_dyn_attributes(custom_attributes); let mut result: Result = fut.await; match &result { Ok(resp) => { - span.set_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_response(resp)); + span.set_span_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_response(resp)); custom_instruments.on_response(resp); supergraph_events.on_response(resp); }, Err(err) => { - span.set_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_error(err)); + span.set_span_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_error(err)); custom_instruments.on_error(err, &ctx); supergraph_events.on_error(err, &ctx); }, @@ -699,7 +699,7 @@ impl Plugin for Telemetry { let now = Instant::now(); async move { let span = Span::current(); - span.set_dyn_attributes(custom_attributes); + span.set_span_dyn_attributes(custom_attributes); let result: Result = f.await; match &result { @@ -709,7 +709,7 @@ impl Plugin for Telemetry { } else { span.record(OTEL_STATUS_CODE, OTEL_STATUS_CODE_OK); } - span.set_dyn_attributes( + span.set_span_dyn_attributes( conf.instrumentation .spans .subgraph @@ -722,7 +722,7 @@ impl Plugin for Telemetry { Err(err) => { span.record(OTEL_STATUS_CODE, OTEL_STATUS_CODE_ERROR); - span.set_dyn_attributes( + span.set_span_dyn_attributes( conf.instrumentation.spans.subgraph.attributes.on_error(err), ); custom_instruments.on_error(err, &context); diff --git a/apollo-router/src/plugins/telemetry/otel/layer.rs b/apollo-router/src/plugins/telemetry/otel/layer.rs new file mode 100644 index 0000000000..9a12b5c50a --- /dev/null +++ b/apollo-router/src/plugins/telemetry/otel/layer.rs @@ -0,0 +1,1382 @@ +use super::{OtelData, PreSampledTracer}; +use once_cell::unsync; +use opentelemetry::{ + trace::{self as otel, noop, OrderMap, TraceContextExt}, + Context as OtelContext, Key, KeyValue, StringValue, Value, +}; +use std::any::TypeId; +use std::fmt; +use std::marker; +use std::thread; +use std::time::{Instant, SystemTime}; +use tracing_core::span::{self, Attributes, Id, Record}; +use tracing_core::{field, Event, Subscriber}; +use tracing_subscriber::layer::Context; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::Layer; + +const SPAN_NAME_FIELD: &str = "otel.name"; +const SPAN_KIND_FIELD: &str = "otel.kind"; +const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code"; +const SPAN_STATUS_MESSAGE_FIELD: &str = "otel.status_message"; + +const FIELD_EXCEPTION_MESSAGE: &str = "exception.message"; +const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace"; + +/// An [OpenTelemetry] propagation layer for use in a project that uses +/// [tracing]. +/// +/// [OpenTelemetry]: https://opentelemetry.io +/// [tracing]: https://github.com/tokio-rs/tracing +pub(crate) struct OpenTelemetryLayer { + tracer: T, + location: bool, + tracked_inactivity: bool, + with_threads: bool, + exception_config: ExceptionFieldConfig, + get_context: WithContext, + _registry: marker::PhantomData, +} + +impl Default for OpenTelemetryLayer +where + S: Subscriber + for<'span> LookupSpan<'span>, +{ + fn default() -> Self { + OpenTelemetryLayer::new(noop::NoopTracer::new()) + } +} + +/// Construct a layer to track spans via [OpenTelemetry]. +/// +/// [OpenTelemetry]: https://opentelemetry.io +/// +/// # Examples +/// +/// ```rust,no_run +/// use tracing_subscriber::layer::SubscriberExt; +/// use tracing_subscriber::Registry; +/// +/// // Use the tracing subscriber `Registry`, or any other subscriber +/// // that impls `LookupSpan` +/// let subscriber = Registry::default().with(tracing_opentelemetry::layer()); +/// # drop(subscriber); +/// ``` +pub(crate) fn layer() -> OpenTelemetryLayer +where + S: Subscriber + for<'span> LookupSpan<'span>, +{ + OpenTelemetryLayer::default() +} + +// this function "remembers" the types of the subscriber so that we +// can downcast to something aware of them without knowing those +// types at the callsite. +// +// See https://github.com/tokio-rs/tracing/blob/4dad420ee1d4607bad79270c1520673fa6266a3d/tracing-error/src/layer.rs +pub(crate) struct WithContext( + #[allow(clippy::type_complexity)] + fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer)), +); + +impl WithContext { + // This function allows a function to be called in the context of the + // "remembered" subscriber. + pub(crate) fn with_context( + &self, + dispatch: &tracing::Dispatch, + id: &span::Id, + mut f: impl FnMut(&mut OtelData, &dyn PreSampledTracer), + ) { + (self.0)(dispatch, id, &mut f) + } +} + +fn str_to_span_kind(s: &str) -> Option { + match s { + s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server), + s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client), + s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer), + s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer), + s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal), + _ => None, + } +} + +fn str_to_status(s: &str) -> otel::Status { + match s { + s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok, + s if s.eq_ignore_ascii_case("error") => otel::Status::error(""), + _ => otel::Status::Unset, + } +} + +struct SpanEventVisitor<'a, 'b> { + event_builder: &'a mut otel::Event, + span_builder: Option<&'b mut otel::SpanBuilder>, + exception_config: ExceptionFieldConfig, +} + +impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { + /// Record events on the underlying OpenTelemetry [`Span`] from `bool` values. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_bool(&mut self, field: &field::Field, value: bool) { + match field.name() { + "message" => self.event_builder.name = value.to_string().into(), + // Skip fields that are actually log metadata that have already been handled + #[cfg(feature = "tracing-log")] + name if name.starts_with("log.") => (), + name => { + self.event_builder + .attributes + .push(KeyValue::new(name, value)); + } + } + } + + /// Record events on the underlying OpenTelemetry [`Span`] from `f64` values. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_f64(&mut self, field: &field::Field, value: f64) { + match field.name() { + "message" => self.event_builder.name = value.to_string().into(), + // Skip fields that are actually log metadata that have already been handled + #[cfg(feature = "tracing-log")] + name if name.starts_with("log.") => (), + name => { + self.event_builder + .attributes + .push(KeyValue::new(name, value)); + } + } + } + + /// Record events on the underlying OpenTelemetry [`Span`] from `i64` values. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_i64(&mut self, field: &field::Field, value: i64) { + match field.name() { + "message" => self.event_builder.name = value.to_string().into(), + // Skip fields that are actually log metadata that have already been handled + #[cfg(feature = "tracing-log")] + name if name.starts_with("log.") => (), + name => { + self.event_builder + .attributes + .push(KeyValue::new(name, value)); + } + } + } + + /// Record events on the underlying OpenTelemetry [`Span`] from `&str` values. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_str(&mut self, field: &field::Field, value: &str) { + match field.name() { + "message" => self.event_builder.name = value.to_string().into(), + // Skip fields that are actually log metadata that have already been handled + #[cfg(feature = "tracing-log")] + name if name.starts_with("log.") => (), + name => { + self.event_builder + .attributes + .push(KeyValue::new(name, value.to_string())); + } + } + } + + /// Record events on the underlying OpenTelemetry [`Span`] from values that + /// implement Debug. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { + match field.name() { + "message" => self.event_builder.name = format!("{:?}", value).into(), + // Skip fields that are actually log metadata that have already been handled + #[cfg(feature = "tracing-log")] + name if name.starts_with("log.") => (), + name => { + self.event_builder + .attributes + .push(KeyValue::new(name, format!("{:?}", value))); + } + } + } + + /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s + /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_error( + &mut self, + field: &tracing_core::Field, + value: &(dyn std::error::Error + 'static), + ) { + let mut chain: Vec = Vec::new(); + let mut next_err = value.source(); + + while let Some(err) = next_err { + chain.push(err.to_string().into()); + next_err = err.source(); + } + + let error_msg = value.to_string(); + + if self.exception_config.record { + self.event_builder + .attributes + .push(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone())); + + // NOTE: This is actually not the stacktrace of the exception. This is + // the "source chain". It represents the heirarchy of errors from the + // app level to the lowest level such as IO. It does not represent all + // of the callsites in the code that led to the error happening. + // `std::error::Error::backtrace` is a nightly-only API and cannot be + // used here until the feature is stabilized. + self.event_builder + .attributes + .push(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone())); + } + + if self.exception_config.propagate { + if let Some(span) = &mut self.span_builder { + if let Some(attrs) = span.attributes.as_mut() { + attrs.insert( + Key::new(FIELD_EXCEPTION_MESSAGE), + Value::String(error_msg.clone().into()), + ); + + // NOTE: This is actually not the stacktrace of the exception. This is + // the "source chain". It represents the heirarchy of errors from the + // app level to the lowest level such as IO. It does not represent all + // of the callsites in the code that led to the error happening. + // `std::error::Error::backtrace` is a nightly-only API and cannot be + // used here until the feature is stabilized. + attrs.insert( + Key::new(FIELD_EXCEPTION_STACKTRACE), + Value::Array(chain.clone().into()), + ); + } + } + } + + self.event_builder + .attributes + .push(Key::new(field.name()).string(error_msg)); + self.event_builder + .attributes + .push(Key::new(format!("{}.chain", field.name())).array(chain)); + } +} + +/// Control over opentelemetry conventional exception fields +#[derive(Clone, Copy)] +struct ExceptionFieldConfig { + /// If an error value is recorded on an event/span, should the otel fields + /// be added + record: bool, + + /// If an error value is recorded on an event, should the otel fields be + /// added to the corresponding span + propagate: bool, +} + +struct SpanAttributeVisitor<'a> { + span_builder: &'a mut otel::SpanBuilder, + exception_config: ExceptionFieldConfig, +} + +impl<'a> SpanAttributeVisitor<'a> { + fn record(&mut self, attribute: KeyValue) { + debug_assert!(self.span_builder.attributes.is_some()); + if let Some(v) = self.span_builder.attributes.as_mut() { + v.insert(attribute.key, attribute.value); + } + } +} + +impl<'a> field::Visit for SpanAttributeVisitor<'a> { + /// Set attributes on the underlying OpenTelemetry [`Span`] from `bool` values. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_bool(&mut self, field: &field::Field, value: bool) { + self.record(KeyValue::new(field.name(), value)); + } + + /// Set attributes on the underlying OpenTelemetry [`Span`] from `f64` values. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_f64(&mut self, field: &field::Field, value: f64) { + self.record(KeyValue::new(field.name(), value)); + } + + /// Set attributes on the underlying OpenTelemetry [`Span`] from `i64` values. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_i64(&mut self, field: &field::Field, value: i64) { + self.record(KeyValue::new(field.name(), value)); + } + + /// Set attributes on the underlying OpenTelemetry [`Span`] from `&str` values. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_str(&mut self, field: &field::Field, value: &str) { + match field.name() { + SPAN_NAME_FIELD => self.span_builder.name = value.to_string().into(), + SPAN_KIND_FIELD => self.span_builder.span_kind = str_to_span_kind(value), + SPAN_STATUS_CODE_FIELD => self.span_builder.status = str_to_status(value), + SPAN_STATUS_MESSAGE_FIELD => { + self.span_builder.status = otel::Status::error(value.to_string()) + } + _ => self.record(KeyValue::new(field.name(), value.to_string())), + } + } + + /// Set attributes on the underlying OpenTelemetry [`Span`] from values that + /// implement Debug. + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { + match field.name() { + SPAN_NAME_FIELD => self.span_builder.name = format!("{:?}", value).into(), + SPAN_KIND_FIELD => { + self.span_builder.span_kind = str_to_span_kind(&format!("{:?}", value)) + } + SPAN_STATUS_CODE_FIELD => { + self.span_builder.status = str_to_status(&format!("{:?}", value)) + } + SPAN_STATUS_MESSAGE_FIELD => { + self.span_builder.status = otel::Status::error(format!("{:?}", value)) + } + _ => self.record(Key::new(field.name()).string(format!("{:?}", value))), + } + } + + /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s + /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_error( + &mut self, + field: &tracing_core::Field, + value: &(dyn std::error::Error + 'static), + ) { + let mut chain: Vec = Vec::new(); + let mut next_err = value.source(); + + while let Some(err) = next_err { + chain.push(err.to_string().into()); + next_err = err.source(); + } + + let error_msg = value.to_string(); + + if self.exception_config.record { + self.record(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone())); + + // NOTE: This is actually not the stacktrace of the exception. This is + // the "source chain". It represents the heirarchy of errors from the + // app level to the lowest level such as IO. It does not represent all + // of the callsites in the code that led to the error happening. + // `std::error::Error::backtrace` is a nightly-only API and cannot be + // used here until the feature is stabilized. + self.record(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone())); + } + + self.record(Key::new(field.name()).string(error_msg)); + self.record(Key::new(format!("{}.chain", field.name())).array(chain)); + } +} + +impl OpenTelemetryLayer +where + S: Subscriber + for<'span> LookupSpan<'span>, + T: otel::Tracer + PreSampledTracer + 'static, +{ + /// Set the [`Tracer`] that this layer will use to produce and track + /// OpenTelemetry [`Span`]s. + /// + /// [`Tracer`]: opentelemetry::trace::Tracer + /// [`Span`]: opentelemetry::trace::Span + /// + /// # Examples + /// + /// ```no_run + /// use tracing_opentelemetry::OpenTelemetryLayer; + /// use tracing_subscriber::layer::SubscriberExt; + /// use tracing_subscriber::Registry; + /// + /// // Create a jaeger exporter pipeline for a `trace_demo` service. + /// let tracer = opentelemetry_jaeger::new_agent_pipeline() + /// .with_service_name("trace_demo") + /// .install_simple() + /// .expect("Error initializing Jaeger exporter"); + /// + /// // Create a layer with the configured tracer + /// let otel_layer = OpenTelemetryLayer::new(tracer); + /// + /// // Use the tracing subscriber `Registry`, or any other subscriber + /// // that impls `LookupSpan` + /// let subscriber = Registry::default().with(otel_layer); + /// # drop(subscriber); + /// ``` + pub(crate) fn new(tracer: T) -> Self { + OpenTelemetryLayer { + tracer, + location: true, + tracked_inactivity: true, + with_threads: true, + exception_config: ExceptionFieldConfig { + record: false, + propagate: false, + }, + get_context: WithContext(Self::get_context), + _registry: marker::PhantomData, + } + } + + /// Set the [`Tracer`] that this layer will use to produce and track + /// OpenTelemetry [`Span`]s. + /// + /// [`Tracer`]: opentelemetry::trace::Tracer + /// [`Span`]: opentelemetry::trace::Span + /// + /// # Examples + /// + /// ```no_run + /// use tracing_subscriber::layer::SubscriberExt; + /// use tracing_subscriber::Registry; + /// + /// // Create a jaeger exporter pipeline for a `trace_demo` service. + /// let tracer = opentelemetry_jaeger::new_agent_pipeline() + /// .with_service_name("trace_demo") + /// .install_simple() + /// .expect("Error initializing Jaeger exporter"); + /// + /// // Create a layer with the configured tracer + /// let otel_layer = tracing_opentelemetry::layer().with_tracer(tracer); + /// + /// // Use the tracing subscriber `Registry`, or any other subscriber + /// // that impls `LookupSpan` + /// let subscriber = Registry::default().with(otel_layer); + /// # drop(subscriber); + /// ``` + pub(crate) fn with_tracer(self, tracer: Tracer) -> OpenTelemetryLayer + where + Tracer: otel::Tracer + PreSampledTracer + 'static, + { + OpenTelemetryLayer { + tracer, + location: self.location, + tracked_inactivity: self.tracked_inactivity, + with_threads: self.with_threads, + exception_config: self.exception_config, + get_context: WithContext(OpenTelemetryLayer::::get_context), + _registry: self._registry, + } + } + + /// Sets whether or not span and event metadata should include OpenTelemetry + /// exception fields such as `exception.message` and `exception.backtrace` + /// when an `Error` value is recorded. If multiple error values are recorded + /// on the same span/event, only the most recently recorded error value will + /// show up under these fields. + /// + /// These attributes follow the [OpenTelemetry semantic conventions for + /// exceptions][conv]. + /// + /// By default, these attributes are not recorded. + /// + /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/ + pub(crate) fn with_exception_fields(self, exception_fields: bool) -> Self { + Self { + exception_config: ExceptionFieldConfig { + record: exception_fields, + ..self.exception_config + }, + ..self + } + } + + /// Sets whether or not reporting an `Error` value on an event will + /// propagate the OpenTelemetry exception fields such as `exception.message` + /// and `exception.backtrace` to the corresponding span. You do not need to + /// enable `with_exception_fields` in order to enable this. If multiple + /// error values are recorded on the same span/event, only the most recently + /// recorded error value will show up under these fields. + /// + /// These attributes follow the [OpenTelemetry semantic conventions for + /// exceptions][conv]. + /// + /// By default, these attributes are not propagated to the span. + /// + /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/ + pub(crate) fn with_exception_field_propagation( + self, + exception_field_propagation: bool, + ) -> Self { + Self { + exception_config: ExceptionFieldConfig { + propagate: exception_field_propagation, + ..self.exception_config + }, + ..self + } + } + + /// Sets whether or not span and event metadata should include OpenTelemetry + /// attributes with location information, such as the file, module and line number. + /// + /// These attributes follow the [OpenTelemetry semantic conventions for + /// source locations][conv]. + /// + /// By default, locations are enabled. + /// + /// [conv]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes/ + pub(crate) fn with_location(self, location: bool) -> Self { + Self { location, ..self } + } + + /// Sets whether or not spans metadata should include the _busy time_ + /// (total time for which it was entered), and _idle time_ (total time + /// the span existed but was not entered). + pub(crate) fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self { + Self { + tracked_inactivity, + ..self + } + } + + /// Sets whether or not spans record additional attributes for the thread + /// name and thread ID of the thread they were created on, following the + /// [OpenTelemetry semantic conventions for threads][conv]. + /// + /// By default, thread attributes are enabled. + /// + /// [conv]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#general-thread-attributes/ + pub(crate) fn with_threads(self, threads: bool) -> Self { + Self { + with_threads: threads, + ..self + } + } + + /// Retrieve the parent OpenTelemetry [`Context`] from the current tracing + /// [`span`] through the [`Registry`]. This [`Context`] links spans to their + /// parent for proper hierarchical visualization. + /// + /// [`Context`]: opentelemetry::Context + /// [`span`]: tracing::Span + /// [`Registry`]: tracing_subscriber::Registry + fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, S>) -> OtelContext { + // If a span is specified, it _should_ exist in the underlying `Registry`. + if let Some(parent) = attrs.parent() { + let span = ctx.span(parent).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + extensions + .get_mut::() + .map(|builder| self.tracer.sampled_context(builder)) + .unwrap_or_default() + // Else if the span is inferred from context, look up any available current span. + } else if attrs.is_contextual() { + ctx.lookup_current() + .and_then(|span| { + let mut extensions = span.extensions_mut(); + extensions + .get_mut::() + .map(|builder| self.tracer.sampled_context(builder)) + }) + .unwrap_or_else(OtelContext::current) + // Explicit root spans should have no parent context. + } else { + OtelContext::new() + } + } + + fn get_context( + dispatch: &tracing::Dispatch, + id: &span::Id, + f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer), + ) { + let subscriber = dispatch + .downcast_ref::() + .expect("subscriber should downcast to expected type; this is a bug!"); + let span = subscriber + .span(id) + .expect("registry should have a span for the current ID"); + let layer = dispatch + .downcast_ref::>() + .expect("layer should downcast to expected type; this is a bug!"); + + let mut extensions = span.extensions_mut(); + if let Some(builder) = extensions.get_mut::() { + f(builder, &layer.tracer); + } + } + + fn extra_span_attrs(&self) -> usize { + let mut extra_attrs = 0; + if self.location { + extra_attrs += 3; + } + if self.with_threads { + extra_attrs += 2; + } + extra_attrs + } +} + +thread_local! { + static THREAD_ID: unsync::Lazy = unsync::Lazy::new(|| { + // OpenTelemetry's semantic conventions require the thread ID to be + // recorded as an integer, but `std::thread::ThreadId` does not expose + // the integer value on stable, so we have to convert it to a `usize` by + // parsing it. Since this requires allocating a `String`, store it in a + // thread local so we only have to do this once. + // TODO(eliza): once `std::thread::ThreadId::as_u64` is stabilized + // (https://github.com/rust-lang/rust/issues/67939), just use that. + thread_id_integer(thread::current().id()) + }); +} + +impl Layer for OpenTelemetryLayer +where + S: Subscriber + for<'span> LookupSpan<'span>, + T: otel::Tracer + PreSampledTracer + 'static, +{ + /// Creates an [OpenTelemetry `Span`] for the corresponding [tracing `Span`]. + /// + /// [OpenTelemetry `Span`]: opentelemetry::trace::Span + /// [tracing `Span`]: tracing::Span + fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { + let span = ctx.span(id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + + if self.tracked_inactivity && extensions.get_mut::().is_none() { + extensions.insert(Timings::new()); + } + + let parent_cx = self.parent_context(attrs, &ctx); + let mut builder = self + .tracer + .span_builder(attrs.metadata().name()) + .with_start_time(SystemTime::now()) + // Eagerly assign span id so children have stable parent id + .with_span_id(self.tracer.new_span_id()); + + // Record new trace id if there is no active parent span + if !parent_cx.has_active_span() { + builder.trace_id = Some(self.tracer.new_trace_id()); + } + + let builder_attrs = builder.attributes.get_or_insert(OrderMap::with_capacity( + attrs.fields().len() + self.extra_span_attrs(), + )); + + if self.location { + let meta = attrs.metadata(); + + if let Some(filename) = meta.file() { + builder_attrs.insert("code.filepath".into(), filename.into()); + } + + if let Some(module) = meta.module_path() { + builder_attrs.insert("code.namespace".into(), module.into()); + } + + if let Some(line) = meta.line() { + builder_attrs.insert("code.lineno".into(), (line as i64).into()); + } + } + + if self.with_threads { + THREAD_ID.with(|id| builder_attrs.insert("thread.id".into(), (**id as i64).into())); + if let Some(name) = std::thread::current().name() { + // TODO(eliza): it's a bummer that we have to allocate here, but + // we can't easily get the string as a `static`. it would be + // nice if `opentelemetry` could also take `Arc`s as + // `String` values... + builder_attrs.insert("thread.name".into(), name.to_owned().into()); + } + } + + attrs.record(&mut SpanAttributeVisitor { + span_builder: &mut builder, + exception_config: self.exception_config, + }); + extensions.insert(OtelData { builder, parent_cx }); + } + + fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { + if !self.tracked_inactivity { + return; + } + + let span = ctx.span(id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + + if let Some(timings) = extensions.get_mut::() { + let now = Instant::now(); + timings.idle += (now - timings.last).as_nanos() as i64; + timings.last = now; + } + } + + fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { + if !self.tracked_inactivity { + return; + } + + let span = ctx.span(id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + + if let Some(timings) = extensions.get_mut::() { + let now = Instant::now(); + timings.busy += (now - timings.last).as_nanos() as i64; + timings.last = now; + } + } + + /// Record OpenTelemetry [`attributes`] for the given values. + /// + /// [`attributes`]: opentelemetry::trace::SpanBuilder::attributes + fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { + let span = ctx.span(id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + if let Some(data) = extensions.get_mut::() { + values.record(&mut SpanAttributeVisitor { + span_builder: &mut data.builder, + exception_config: self.exception_config, + }); + } + } + + fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context) { + let span = ctx.span(id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + let data = extensions + .get_mut::() + .expect("Missing otel data span extensions"); + + let follows_span = ctx + .span(follows) + .expect("Span to follow not found, this is a bug"); + let mut follows_extensions = follows_span.extensions_mut(); + let follows_data = follows_extensions + .get_mut::() + .expect("Missing otel data span extensions"); + + let follows_context = self + .tracer + .sampled_context(follows_data) + .span() + .span_context() + .clone(); + let follows_link = otel::Link::new(follows_context, Vec::new()); + if let Some(ref mut links) = data.builder.links { + links.push(follows_link); + } else { + data.builder.links = Some(vec![follows_link]); + } + } + + /// Records OpenTelemetry [`Event`] data on event. + /// + /// Note: an [`ERROR`]-level event will also set the OpenTelemetry span status code to + /// [`Error`], signaling that an error has occurred. + /// + /// [`Event`]: opentelemetry::trace::Event + /// [`ERROR`]: tracing::Level::ERROR + /// [`Error`]: opentelemetry::trace::StatusCode::Error + fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { + // Ignore events that are not in the context of a span + if let Some(span) = ctx.lookup_current() { + // Performing read operations before getting a write lock to avoid a deadlock + // See https://github.com/tokio-rs/tracing/issues/763 + #[cfg(feature = "tracing-log")] + let normalized_meta = event.normalized_metadata(); + #[cfg(feature = "tracing-log")] + let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); + #[cfg(not(feature = "tracing-log"))] + let meta = event.metadata(); + + let target = Key::new("target"); + + #[cfg(feature = "tracing-log")] + let target = if normalized_meta.is_some() { + target.string(meta.target().to_owned()) + } else { + target.string(event.metadata().target()) + }; + + #[cfg(not(feature = "tracing-log"))] + let target = target.string(meta.target()); + + let mut extensions = span.extensions_mut(); + let span_builder = extensions + .get_mut::() + .map(|data| &mut data.builder); + + let mut otel_event = otel::Event::new( + String::new(), + SystemTime::now(), + vec![Key::new("level").string(meta.level().as_str()), target], + 0, + ); + event.record(&mut SpanEventVisitor { + event_builder: &mut otel_event, + span_builder, + exception_config: self.exception_config, + }); + + if let Some(OtelData { builder, .. }) = extensions.get_mut::() { + if builder.status == otel::Status::Unset + && *meta.level() == tracing_core::Level::ERROR + { + builder.status = otel::Status::error("") + } + + if self.location { + #[cfg(not(feature = "tracing-log"))] + let normalized_meta: Option> = None; + let (file, module) = match &normalized_meta { + Some(meta) => ( + meta.file().map(|s| Value::from(s.to_owned())), + meta.module_path().map(|s| Value::from(s.to_owned())), + ), + None => ( + event.metadata().file().map(Value::from), + event.metadata().module_path().map(Value::from), + ), + }; + + if let Some(file) = file { + otel_event + .attributes + .push(KeyValue::new("code.filepath", file)); + } + if let Some(module) = module { + otel_event + .attributes + .push(KeyValue::new("code.namespace", module)); + } + if let Some(line) = meta.line() { + otel_event + .attributes + .push(KeyValue::new("code.lineno", line as i64)); + } + } + + println!("ICIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII !!!!!!!!!!!!!!!!!!!!"); + if let Some(ref mut events) = builder.events { + events.push(otel_event); + } else { + builder.events = Some(vec![otel_event]); + } + } + }; + } + + /// Exports an OpenTelemetry [`Span`] on close. + /// + /// [`Span`]: opentelemetry::trace::Span + fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { + let span = ctx.span(&id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + + if let Some(OtelData { + mut builder, + parent_cx, + }) = extensions.remove::() + { + if self.tracked_inactivity { + // Append busy/idle timings when enabled. + if let Some(timings) = extensions.get_mut::() { + let busy_ns = Key::new("busy_ns"); + let idle_ns = Key::new("idle_ns"); + + let attributes = builder + .attributes + .get_or_insert_with(|| OrderMap::with_capacity(2)); + attributes.insert(busy_ns, timings.busy.into()); + attributes.insert(idle_ns, timings.idle.into()); + } + } + + // Assign end time, build and start span, drop span to export + builder + .with_end_time(SystemTime::now()) + .start_with_context(&self.tracer, &parent_cx); + } + } + + // SAFETY: this is safe because the `WithContext` function pointer is valid + // for the lifetime of `&self`. + unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { + match id { + id if id == TypeId::of::() => Some(self as *const _ as *const ()), + id if id == TypeId::of::() => { + Some(&self.get_context as *const _ as *const ()) + } + _ => None, + } + } +} + +struct Timings { + idle: i64, + busy: i64, + last: Instant, +} + +impl Timings { + fn new() -> Self { + Self { + idle: 0, + busy: 0, + last: Instant::now(), + } + } +} + +fn thread_id_integer(id: thread::ThreadId) -> u64 { + let thread_id = format!("{:?}", id); + thread_id + .trim_start_matches("ThreadId(") + .trim_end_matches(')') + .parse::() + .expect("thread ID should parse as an integer") +} + +#[cfg(test)] +mod tests { + use super::*; + use opentelemetry::{ + trace::{noop, TraceFlags}, + StringValue, + }; + use std::{ + borrow::Cow, + collections::HashMap, + error::Error, + fmt::Display, + sync::{Arc, Mutex}, + thread, + time::SystemTime, + }; + use tracing_subscriber::prelude::*; + + #[derive(Debug, Clone)] + struct TestTracer(Arc>>); + impl otel::Tracer for TestTracer { + type Span = noop::NoopSpan; + fn start_with_context(&self, _name: T, _context: &OtelContext) -> Self::Span + where + T: Into>, + { + noop::NoopSpan::new() + } + fn span_builder(&self, name: T) -> otel::SpanBuilder + where + T: Into>, + { + otel::SpanBuilder::from_name(name) + } + fn build_with_context( + &self, + builder: otel::SpanBuilder, + parent_cx: &OtelContext, + ) -> Self::Span { + *self.0.lock().unwrap() = Some(OtelData { + builder, + parent_cx: parent_cx.clone(), + }); + noop::NoopSpan::new() + } + } + + impl PreSampledTracer for TestTracer { + fn sampled_context(&self, _builder: &mut super::OtelData) -> OtelContext { + OtelContext::new() + } + fn new_trace_id(&self) -> otel::TraceId { + otel::TraceId::INVALID + } + fn new_span_id(&self) -> otel::SpanId { + otel::SpanId::INVALID + } + } + + impl TestTracer { + fn with_data(&self, f: impl FnOnce(&OtelData) -> T) -> T { + let lock = self.0.lock().unwrap(); + let data = lock.as_ref().expect("no span data has been recorded yet"); + f(data) + } + } + + #[derive(Debug, Clone)] + struct TestSpan(otel::SpanContext); + impl otel::Span for TestSpan { + fn add_event_with_timestamp>>( + &mut self, + _: T, + _: SystemTime, + _: Vec, + ) { + } + fn span_context(&self) -> &otel::SpanContext { + &self.0 + } + fn is_recording(&self) -> bool { + false + } + fn set_attribute(&mut self, _attribute: KeyValue) {} + fn set_status(&mut self, _status: otel::Status) {} + fn update_name>>(&mut self, _new_name: T) {} + fn end_with_timestamp(&mut self, _timestamp: SystemTime) {} + } + + #[derive(Debug)] + struct TestDynError { + msg: &'static str, + source: Option>, + } + impl Display for TestDynError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.msg) + } + } + impl Error for TestDynError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match &self.source { + Some(source) => Some(source), + None => None, + } + } + } + impl TestDynError { + fn new(msg: &'static str) -> Self { + Self { msg, source: None } + } + fn with_parent(self, parent_msg: &'static str) -> Self { + Self { + msg: parent_msg, + source: Some(Box::new(self)), + } + } + } + + #[test] + fn dynamic_span_names() { + let dynamic_name = "GET http://example.com".to_string(); + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("static_name", otel.name = dynamic_name.as_str()); + }); + + let recorded_name = tracer + .0 + .lock() + .unwrap() + .as_ref() + .map(|b| b.builder.name.clone()); + assert_eq!(recorded_name, Some(dynamic_name.into())) + } + + #[test] + fn span_kind() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request", otel.kind = "server"); + }); + + let recorded_kind = tracer.with_data(|data| data.builder.span_kind.clone()); + assert_eq!(recorded_kind, Some(otel::SpanKind::Server)) + } + + #[test] + fn span_status_code() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok); + }); + + let recorded_status = tracer.with_data(|data| data.builder.status.clone()); + assert_eq!(recorded_status, otel::Status::Ok) + } + + #[test] + fn span_status_message() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); + + let message = "message"; + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request", otel.status_message = message); + }); + + let recorded_status_message = tracer + .0 + .lock() + .unwrap() + .as_ref() + .unwrap() + .builder + .status + .clone(); + + assert_eq!(recorded_status_message, otel::Status::error(message)) + } + + #[test] + fn trace_id_from_existing_context() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); + let trace_id = otel::TraceId::from(42u128); + let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new( + trace_id, + otel::SpanId::from(1u64), + TraceFlags::default(), + false, + Default::default(), + ))); + let _g = existing_cx.attach(); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request", otel.kind = "server"); + }); + + let recorded_trace_id = + tracer.with_data(|data| data.parent_cx.span().span_context().trace_id()); + assert_eq!(recorded_trace_id, trace_id) + } + + #[test] + fn includes_timings() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with( + layer() + .with_tracer(tracer.clone()) + .with_tracked_inactivity(true), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request"); + }); + + let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); + let keys = attributes + .iter() + .map(|(key, _)| key.as_str()) + .collect::>(); + assert!(keys.contains(&"idle_ns")); + assert!(keys.contains(&"busy_ns")); + } + + #[test] + fn records_error_fields() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with( + layer() + .with_tracer(tracer.clone()) + .with_exception_fields(true), + ); + + let err = TestDynError::new("base error") + .with_parent("intermediate error") + .with_parent("user error"); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!( + "request", + error = &err as &(dyn std::error::Error + 'static) + ); + }); + + let attributes = tracer + .0 + .lock() + .unwrap() + .as_ref() + .unwrap() + .builder + .attributes + .as_ref() + .unwrap() + .clone(); + + let key_values = attributes + .into_iter() + .map(|(key, value)| (key.as_str().to_owned(), value)) + .collect::>(); + + assert_eq!(key_values["error"].as_str(), "user error"); + assert_eq!( + key_values["error.chain"], + Value::Array( + vec![ + StringValue::from("intermediate error"), + StringValue::from("base error") + ] + .into() + ) + ); + + assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); + assert_eq!( + key_values[FIELD_EXCEPTION_STACKTRACE], + Value::Array( + vec![ + StringValue::from("intermediate error"), + StringValue::from("base error") + ] + .into() + ) + ); + } + + #[test] + fn includes_span_location() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry() + .with(layer().with_tracer(tracer.clone()).with_location(true)); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request"); + }); + + let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); + let keys = attributes + .iter() + .map(|(key, _)| key.as_str()) + .collect::>(); + assert!(keys.contains(&"code.filepath")); + assert!(keys.contains(&"code.namespace")); + assert!(keys.contains(&"code.lineno")); + } + + #[test] + fn excludes_span_location() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry() + .with(layer().with_tracer(tracer.clone()).with_location(false)); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request"); + }); + + let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); + let keys = attributes + .iter() + .map(|(key, _)| key.as_str()) + .collect::>(); + assert!(!keys.contains(&"code.filepath")); + assert!(!keys.contains(&"code.namespace")); + assert!(!keys.contains(&"code.lineno")); + } + + #[test] + fn includes_thread() { + let thread = thread::current(); + let expected_name = thread + .name() + .map(|name| Value::String(name.to_owned().into())); + let expected_id = Value::I64(thread_id_integer(thread.id()) as i64); + + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry() + .with(layer().with_tracer(tracer.clone()).with_threads(true)); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request"); + }); + + let attributes = tracer + .with_data(|data| data.builder.attributes.as_ref().unwrap().clone()) + .drain(..) + .map(|(key, value)| (key.as_str().to_string(), value)) + .collect::>(); + assert_eq!(attributes.get("thread.name"), expected_name.as_ref()); + assert_eq!(attributes.get("thread.id"), Some(&expected_id)); + } + + #[test] + fn excludes_thread() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry() + .with(layer().with_tracer(tracer.clone()).with_threads(false)); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request"); + }); + + let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); + let keys = attributes + .iter() + .map(|(key, _)| key.as_str()) + .collect::>(); + assert!(!keys.contains(&"thread.name")); + assert!(!keys.contains(&"thread.id")); + } + + #[test] + fn propagates_error_fields_from_event_to_span() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with( + layer() + .with_tracer(tracer.clone()) + .with_exception_field_propagation(true), + ); + + let err = TestDynError::new("base error") + .with_parent("intermediate error") + .with_parent("user error"); + + tracing::subscriber::with_default(subscriber, || { + let _guard = tracing::debug_span!("request",).entered(); + + tracing::error!( + error = &err as &(dyn std::error::Error + 'static), + "request error!" + ) + }); + + let attributes = tracer + .0 + .lock() + .unwrap() + .as_ref() + .unwrap() + .builder + .attributes + .as_ref() + .unwrap() + .clone(); + + let key_values = attributes + .into_iter() + .map(|(key, value)| (key.as_str().to_owned(), value)) + .collect::>(); + + assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); + assert_eq!( + key_values[FIELD_EXCEPTION_STACKTRACE], + Value::Array( + vec![ + StringValue::from("intermediate error"), + StringValue::from("base error") + ] + .into() + ) + ); + } +} diff --git a/apollo-router/src/plugins/telemetry/otel/mod.rs b/apollo-router/src/plugins/telemetry/otel/mod.rs new file mode 100644 index 0000000000..f1ec71adc1 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/otel/mod.rs @@ -0,0 +1,23 @@ +/// Implementation of the trace::Layer as a source of OpenTelemetry data. +mod layer; +/// Span extension which enables OpenTelemetry context management. +mod span_ext; +/// Protocols for OpenTelemetry Tracers that are compatible with Tracing +mod tracer; + +pub(crate) use layer::{layer, OpenTelemetryLayer}; + +pub use span_ext::OpenTelemetrySpanExt; +pub(crate) use tracer::PreSampledTracer; + +/// Per-span OpenTelemetry data tracked by this crate. +/// +/// Useful for implementing [PreSampledTracer] in alternate otel SDKs. +#[derive(Debug, Clone)] +pub(crate) struct OtelData { + /// The parent otel `Context` for the current tracing span. + pub(crate) parent_cx: opentelemetry::Context, + + /// The otel span data recorded during the current tracing span. + pub(crate) builder: opentelemetry::trace::SpanBuilder, +} diff --git a/apollo-router/src/plugins/telemetry/otel/span_ext.rs b/apollo-router/src/plugins/telemetry/otel/span_ext.rs new file mode 100644 index 0000000000..cfbb0d03ec --- /dev/null +++ b/apollo-router/src/plugins/telemetry/otel/span_ext.rs @@ -0,0 +1,209 @@ +use super::layer::WithContext; +use opentelemetry::{trace::SpanContext, Context, Key, KeyValue, Value}; + +/// Utility functions to allow tracing [`Span`]s to accept and return +/// [OpenTelemetry] [`Context`]s. +/// +/// [`Span`]: tracing::Span +/// [OpenTelemetry]: https://opentelemetry.io +/// [`Context`]: opentelemetry::Context +pub trait OpenTelemetrySpanExt { + /// Associates `self` with a given OpenTelemetry trace, using the provided + /// parent [`Context`]. + /// + /// [`Context`]: opentelemetry::Context + /// + /// # Examples + /// + /// ```rust + /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt}; + /// use opentelemetry_sdk::propagation::TraceContextPropagator; + /// use tracing_opentelemetry::OpenTelemetrySpanExt; + /// use std::collections::HashMap; + /// use tracing::Span; + /// + /// // Example carrier, could be a framework header map that impls otel's `Extractor`. + /// let mut carrier = HashMap::new(); + /// + /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc. + /// let propagator = TraceContextPropagator::new(); + /// + /// // Extract otel parent context via the chosen propagator + /// let parent_context = propagator.extract(&carrier); + /// + /// // Generate a tracing span as usual + /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); + /// + /// // Assign parent trace from external context + /// app_root.set_parent(parent_context.clone()); + /// + /// // Or if the current span has been created elsewhere: + /// Span::current().set_parent(parent_context); + /// ``` + fn set_parent(&self, cx: Context); + + /// Associates `self` with a given OpenTelemetry trace, using the provided + /// followed span [`SpanContext`]. + /// + /// [`SpanContext`]: opentelemetry::trace::SpanContext + /// + /// # Examples + /// + /// ```rust + /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt}; + /// use opentelemetry_sdk::propagation::TraceContextPropagator; + /// use tracing_opentelemetry::OpenTelemetrySpanExt; + /// use std::collections::HashMap; + /// use tracing::Span; + /// + /// // Example carrier, could be a framework header map that impls otel's `Extractor`. + /// let mut carrier = HashMap::new(); + /// + /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc. + /// let propagator = TraceContextPropagator::new(); + /// + /// // Extract otel context of linked span via the chosen propagator + /// let linked_span_otel_context = propagator.extract(&carrier); + /// + /// // Extract the linked span context from the otel context + /// let linked_span_context = linked_span_otel_context.span().span_context().clone(); + /// + /// // Generate a tracing span as usual + /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); + /// + /// // Assign linked trace from external context + /// app_root.add_link(linked_span_context); + /// + /// // Or if the current span has been created elsewhere: + /// let linked_span_context = linked_span_otel_context.span().span_context().clone(); + /// Span::current().add_link(linked_span_context); + /// ``` + fn add_link(&self, cx: SpanContext); + + /// Associates `self` with a given OpenTelemetry trace, using the provided + /// followed span [`SpanContext`] and attributes. + /// + /// [`SpanContext`]: opentelemetry::trace::SpanContext + fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec); + + /// Extracts an OpenTelemetry [`Context`] from `self`. + /// + /// [`Context`]: opentelemetry::Context + /// + /// # Examples + /// + /// ```rust + /// use opentelemetry::Context; + /// use tracing_opentelemetry::OpenTelemetrySpanExt; + /// use tracing::Span; + /// + /// fn make_request(cx: Context) { + /// // perform external request after injecting context + /// // e.g. if the request's headers impl `opentelemetry::propagation::Injector` + /// // then `propagator.inject_context(cx, request.headers_mut())` + /// } + /// + /// // Generate a tracing span as usual + /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); + /// + /// // To include tracing context in client requests from _this_ app, + /// // extract the current OpenTelemetry context. + /// make_request(app_root.context()); + /// + /// // Or if the current span has been created elsewhere: + /// make_request(Span::current().context()) + /// ``` + fn context(&self) -> Context; + + /// Sets an OpenTelemetry attribute directly for this span, bypassing `tracing`. + /// If fields set here conflict with `tracing` fields, the `tracing` fields will supersede fields set with `set_attribute`. + /// This allows for more than 32 fields. + /// + /// # Examples + /// + /// ```rust + /// use opentelemetry::Context; + /// use tracing_opentelemetry::OpenTelemetrySpanExt; + /// use tracing::Span; + /// + /// // Generate a tracing span as usual + /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); + /// + /// // Set the `http.request.header.x_forwarded_for` attribute to `example`. + /// app_root.set_attribute("http.request.header.x_forwarded_for", "example"); + /// ``` + fn set_attribute(&self, key: impl Into, value: impl Into); +} + +impl OpenTelemetrySpanExt for tracing::Span { + fn set_parent(&self, cx: Context) { + let mut cx = Some(cx); + self.with_subscriber(move |(id, subscriber)| { + if let Some(get_context) = subscriber.downcast_ref::() { + get_context.with_context(subscriber, id, move |data, _tracer| { + if let Some(cx) = cx.take() { + data.parent_cx = cx; + } + }); + } + }); + } + + fn add_link(&self, cx: SpanContext) { + self.add_link_with_attributes(cx, Vec::new()) + } + + fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec) { + if cx.is_valid() { + let mut cx = Some(cx); + let mut att = Some(attributes); + self.with_subscriber(move |(id, subscriber)| { + if let Some(get_context) = subscriber.downcast_ref::() { + get_context.with_context(subscriber, id, move |data, _tracer| { + if let Some(cx) = cx.take() { + let attr = att.take().unwrap_or_default(); + let follows_link = opentelemetry::trace::Link::new(cx, attr); + data.builder + .links + .get_or_insert_with(|| Vec::with_capacity(1)) + .push(follows_link); + } + }); + } + }); + } + } + + fn context(&self) -> Context { + let mut cx = None; + self.with_subscriber(|(id, subscriber)| { + if let Some(get_context) = subscriber.downcast_ref::() { + get_context.with_context(subscriber, id, |builder, tracer| { + cx = Some(tracer.sampled_context(builder)); + }) + } + }); + + cx.unwrap_or_default() + } + + fn set_attribute(&self, key: impl Into, value: impl Into) { + self.with_subscriber(move |(id, subscriber)| { + if let Some(get_context) = subscriber.downcast_ref::() { + let mut key = Some(key.into()); + let mut value = Some(value.into()); + get_context.with_context(subscriber, id, move |builder, _| { + if builder.builder.attributes.is_none() { + builder.builder.attributes = Some(Default::default()); + } + builder + .builder + .attributes + .as_mut() + .unwrap() + .insert(key.take().unwrap(), value.take().unwrap()); + }) + } + }); + } +} diff --git a/apollo-router/src/plugins/telemetry/otel/tracer.rs b/apollo-router/src/plugins/telemetry/otel/tracer.rs new file mode 100644 index 0000000000..ff69b38143 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/otel/tracer.rs @@ -0,0 +1,233 @@ +use super::OtelData; +use opentelemetry::trace::OrderMap; +use opentelemetry::{ + trace as otel, + trace::{ + noop, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind, + TraceContextExt, TraceFlags, TraceId, TraceState, + }, + Context as OtelContext, +}; +use opentelemetry_sdk::trace::{Tracer as SdkTracer, TracerProvider as SdkTracerProvider}; + +/// An interface for authors of OpenTelemetry SDKs to build pre-sampled tracers. +/// +/// The OpenTelemetry spec does not allow trace ids to be updated after a span +/// has been created. In order to associate extracted parent trace ids with +/// existing `tracing` spans, `tracing-opentelemetry` builds up otel span data +/// using a [`SpanBuilder`] instead, and creates / exports full otel spans only +/// when the associated `tracing` span is closed. However, in order to properly +/// inject otel [`Context`] information to downstream requests, the sampling +/// state must now be known _before_ the otel span has been created. +/// +/// The logic for coming to a sampling decision and creating an injectable span +/// context from a [`SpanBuilder`] is encapsulated in the +/// [`PreSampledTracer::sampled_context`] method and has been implemented +/// for the standard OpenTelemetry SDK, but this trait may be implemented by +/// authors of alternate OpenTelemetry SDK implementations if they wish to have +/// `tracing` compatibility. +/// +/// See the [`OpenTelemetrySpanExt::set_parent`] and +/// [`OpenTelemetrySpanExt::context`] methods for example usage. +/// +/// [`Tracer`]: opentelemetry::trace::Tracer +/// [`SpanBuilder`]: opentelemetry::trace::SpanBuilder +/// [`PreSampledTracer::sampled_span_context`]: crate::PreSampledTracer::sampled_span_context +/// [`OpenTelemetrySpanExt::set_parent`]: crate::OpenTelemetrySpanExt::set_parent +/// [`OpenTelemetrySpanExt::context`]: crate::OpenTelemetrySpanExt::context +/// [`Context`]: opentelemetry::Context +pub trait PreSampledTracer { + /// Produce an otel context containing an active and pre-sampled span for + /// the given span builder data. + /// + /// The sampling decision, span context information, and parent context + /// values must match the values recorded when the tracing span is closed. + fn sampled_context(&self, data: &mut OtelData) -> OtelContext; + + /// Generate a new trace id. + fn new_trace_id(&self) -> otel::TraceId; + + /// Generate a new span id. + fn new_span_id(&self) -> otel::SpanId; +} + +impl PreSampledTracer for noop::NoopTracer { + fn sampled_context(&self, data: &mut OtelData) -> OtelContext { + data.parent_cx.clone() + } + + fn new_trace_id(&self) -> otel::TraceId { + otel::TraceId::INVALID + } + + fn new_span_id(&self) -> otel::SpanId { + otel::SpanId::INVALID + } +} + +impl PreSampledTracer for SdkTracer { + fn sampled_context(&self, data: &mut OtelData) -> OtelContext { + // Ensure tracing pipeline is still installed. + if self.provider().is_none() { + return OtelContext::new(); + } + let provider = self.provider().unwrap(); + let parent_cx = &data.parent_cx; + let builder = &mut data.builder; + + // Gather trace state + let (trace_id, parent_trace_flags) = current_trace_state(builder, parent_cx, &provider); + + // Sample or defer to existing sampling decisions + let (flags, trace_state) = if let Some(result) = &builder.sampling_result { + process_sampling_result(result, parent_trace_flags) + } else { + builder.sampling_result = Some(provider.config().sampler.should_sample( + Some(parent_cx), + trace_id, + &builder.name, + builder.span_kind.as_ref().unwrap_or(&SpanKind::Internal), + builder.attributes.as_ref().unwrap_or(&OrderMap::default()), + builder.links.as_deref().unwrap_or(&[]), + )); + + process_sampling_result( + builder.sampling_result.as_ref().unwrap(), + parent_trace_flags, + ) + } + .unwrap_or_default(); + + let span_id = builder.span_id.unwrap_or(SpanId::INVALID); + let span_context = SpanContext::new(trace_id, span_id, flags, false, trace_state); + parent_cx.with_remote_span_context(span_context) + } + + fn new_trace_id(&self) -> otel::TraceId { + self.provider() + .map(|provider| provider.config().id_generator.new_trace_id()) + .unwrap_or(otel::TraceId::INVALID) + } + + fn new_span_id(&self) -> otel::SpanId { + self.provider() + .map(|provider| provider.config().id_generator.new_span_id()) + .unwrap_or(otel::SpanId::INVALID) + } +} + +fn current_trace_state( + builder: &SpanBuilder, + parent_cx: &OtelContext, + provider: &SdkTracerProvider, +) -> (TraceId, TraceFlags) { + if parent_cx.has_active_span() { + let span = parent_cx.span(); + let sc = span.span_context(); + (sc.trace_id(), sc.trace_flags()) + } else { + ( + builder + .trace_id + .unwrap_or_else(|| provider.config().id_generator.new_trace_id()), + Default::default(), + ) + } +} + +fn process_sampling_result( + sampling_result: &SamplingResult, + trace_flags: TraceFlags, +) -> Option<(TraceFlags, TraceState)> { + match sampling_result { + SamplingResult { + decision: SamplingDecision::Drop, + .. + } => None, + SamplingResult { + decision: SamplingDecision::RecordOnly, + trace_state, + .. + } => Some((trace_flags & !TraceFlags::SAMPLED, trace_state.clone())), + SamplingResult { + decision: SamplingDecision::RecordAndSample, + trace_state, + .. + } => Some((trace_flags | TraceFlags::SAMPLED, trace_state.clone())), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use opentelemetry::trace::{SpanBuilder, SpanId, TracerProvider as _}; + use opentelemetry_sdk::trace::{config, Sampler, TracerProvider}; + + #[test] + fn assigns_default_trace_id_if_missing() { + let provider = TracerProvider::default(); + let tracer = provider.tracer("test"); + let mut builder = SpanBuilder::from_name("empty".to_string()); + builder.span_id = Some(SpanId::from(1u64)); + builder.trace_id = None; + let parent_cx = OtelContext::new(); + let cx = tracer.sampled_context(&mut OtelData { builder, parent_cx }); + let span = cx.span(); + let span_context = span.span_context(); + + assert!(span_context.is_valid()); + } + + #[rustfmt::skip] + fn sampler_data() -> Vec<(&'static str, Sampler, OtelContext, Option, bool)> { + vec![ + // No parent samples + ("empty_parent_cx_always_on", Sampler::AlwaysOn, OtelContext::new(), None, true), + ("empty_parent_cx_always_off", Sampler::AlwaysOff, OtelContext::new(), None, false), + + // Remote parent samples + ("remote_parent_cx_always_on", Sampler::AlwaysOn, OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, true), + ("remote_parent_cx_always_off", Sampler::AlwaysOff, OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, false), + ("sampled_remote_parent_cx_parent_based", Sampler::ParentBased(Box::new(Sampler::AlwaysOff)), OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, true), + ("unsampled_remote_parent_cx_parent_based", Sampler::ParentBased(Box::new(Sampler::AlwaysOn)), OtelContext::new().with_remote_span_context(span_context(TraceFlags::default(), true)), None, false), + + // Existing sampling result defers + ("previous_drop_result_always_on", Sampler::AlwaysOn, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::Drop, attributes: vec![], trace_state: Default::default() }), false), + ("previous_record_and_sample_result_always_off", Sampler::AlwaysOff, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::RecordAndSample, attributes: vec![], trace_state: Default::default() }), true), + + // Existing local parent, defers + ("previous_drop_result_always_on", Sampler::AlwaysOn, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::Drop, attributes: vec![], trace_state: Default::default() }), false), + ("previous_record_and_sample_result_always_off", Sampler::AlwaysOff, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::RecordAndSample, attributes: vec![], trace_state: Default::default() }), true), + ] + } + + #[test] + fn sampled_context() { + for (name, sampler, parent_cx, previous_sampling_result, is_sampled) in sampler_data() { + let provider = TracerProvider::builder() + .with_config(config().with_sampler(sampler)) + .build(); + let tracer = provider.tracer("test"); + let mut builder = SpanBuilder::from_name("parent".to_string()); + builder.sampling_result = previous_sampling_result; + let sampled = tracer.sampled_context(&mut OtelData { builder, parent_cx }); + + assert_eq!( + sampled.span().span_context().is_sampled(), + is_sampled, + "{}", + name + ) + } + } + + fn span_context(trace_flags: TraceFlags, is_remote: bool) -> SpanContext { + SpanContext::new( + TraceId::from(1u128), + SpanId::from(1u64), + trace_flags, + is_remote, + Default::default(), + ) + } +} diff --git a/apollo-router/src/plugins/telemetry/reload.rs b/apollo-router/src/plugins/telemetry/reload.rs index 58a8c39e0e..9c32649cdc 100644 --- a/apollo-router/src/plugins/telemetry/reload.rs +++ b/apollo-router/src/plugins/telemetry/reload.rs @@ -33,7 +33,7 @@ use tracing_subscriber::Registry; use super::config::SamplerOption; use super::config_new::logging::RateLimit; -use super::dynamic_attribute::DynAttributeLayer; +use super::dynamic_attribute::DynSpanAttributeLayer; use super::fmt_layer::FmtLayer; use super::formatters::json::Json; use super::metrics::span_metrics_exporter::SpanMetricsLayer; @@ -47,7 +47,8 @@ use crate::plugins::telemetry::formatters::FilteringFormatter; use crate::plugins::telemetry::tracing::reload::ReloadTracer; use crate::router_factory::STARTING_SPAN_NAME; -pub(crate) type LayeredRegistry = Layered>; +pub(crate) type LayeredRegistry = + Layered>; pub(super) type LayeredTracer = Layered< Filtered< @@ -116,7 +117,7 @@ pub(crate) fn init_telemetry(log_level: &str) -> Result<()> { // Env filter is separate because of https://github.com/tokio-rs/tracing/issues/1629 // the tracing registry is only created once tracing_subscriber::registry() - .with(DynAttributeLayer::new()) + .with(DynSpanAttributeLayer::new()) .with(SpanMetricsLayer::default()) .with(opentelemetry_layer) .with(fmt_layer) From c3151bff83a4b0e8a6e81f508926f9f091ac9e8e Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 15 Apr 2024 19:45:34 +0200 Subject: [PATCH 10/40] wip Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- Cargo.lock | 1 + apollo-router/Cargo.toml | 3 +- apollo-router/src/lib.rs | 1 + .../telemetry/config_new/attributes.rs | 5 +- .../src/plugins/telemetry/config_new/mod.rs | 7 +- .../plugins/telemetry/config_new/selectors.rs | 9 +- .../plugins/telemetry/dynamic_attribute.rs | 125 +++++++++--------- .../src/plugins/telemetry/formatters/json.rs | 9 +- .../src/plugins/telemetry/formatters/mod.rs | 2 +- .../src/plugins/telemetry/formatters/text.rs | 2 +- apollo-router/src/plugins/telemetry/mod.rs | 3 +- .../src/plugins/telemetry/otel/layer.rs | 33 ++++- .../src/plugins/telemetry/otel/mod.rs | 4 + .../src/plugins/telemetry/otel/tracer.rs | 12 +- apollo-router/src/plugins/telemetry/reload.rs | 7 +- .../src/plugins/telemetry/tracing/reload.rs | 9 +- apollo-router/src/services/external.rs | 2 +- apollo-router/src/services/http/service.rs | 2 +- apollo-router/src/tracer.rs | 10 +- apollo-router/tests/common.rs | 2 +- 20 files changed, 150 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f4db1f55fa..a51dfc6d43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -337,6 +337,7 @@ dependencies = [ "opentelemetry-stdout", "opentelemetry-zipkin", "opentelemetry_api", + "opentelemetry_sdk 0.20.0", "p256 0.13.2", "parking_lot", "paste", diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index bac9991d7a..ce8592e2dd 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -146,6 +146,7 @@ once_cell = "1.19.0" # https://github.com/apollographql/router/pull/1509. A comment which exists # there (and on `tracing` packages below) should be updated should this change. opentelemetry = { version = "0.20.0", features = ["trace", "metrics"] } +opentelemetry_sdk = { version = "0.20.0", default-features = false, features = ["trace"] } opentelemetry_api = "0.20.0" opentelemetry-aws = "0.8.0" opentelemetry-datadog = { version = "0.8.0", features = ["reqwest-client"] } @@ -225,7 +226,6 @@ tower-service = "0.3.2" tracing = "0.1.37" tracing-core = "0.1.31" tracing-futures = { version = "0.2.5", features = ["futures-03"] } -tracing-opentelemetry = "0.21.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] } trust-dns-resolver = "0.23.2" url = { version = "2.5.0", features = ["serde"] } @@ -322,6 +322,7 @@ tracing-subscriber = { version = "0.3", default-features = false, features = [ "env-filter", "fmt", ] } +tracing-opentelemetry = "0.21.0" tracing-test = "0.2.4" walkdir = "2.4.0" wiremock = "0.5.22" diff --git a/apollo-router/src/lib.rs b/apollo-router/src/lib.rs index f10afe636a..2aaf16ae2e 100644 --- a/apollo-router/src/lib.rs +++ b/apollo-router/src/lib.rs @@ -89,6 +89,7 @@ pub use crate::context::Context; pub use crate::executable::main; pub use crate::executable::Executable; pub use crate::notification::Notify; +pub use crate::plugins::telemetry::otel; pub use crate::router::ApolloRouterError; pub use crate::router::ConfigurationSource; pub use crate::router::LicenseSource; diff --git a/apollo-router/src/plugins/telemetry/config_new/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/attributes.rs index 81f99cc8cd..c9d5e97e0c 100644 --- a/apollo-router/src/plugins/telemetry/config_new/attributes.rs +++ b/apollo-router/src/plugins/telemetry/config_new/attributes.rs @@ -35,7 +35,6 @@ use serde::Deserialize; use serde::Serialize; use tower::BoxError; use tracing::Span; -use tracing_opentelemetry::OpenTelemetrySpanExt; use crate::axum_factory::utils::ConnectionInfo; use crate::context::OPERATION_KIND; @@ -44,6 +43,7 @@ use crate::plugins::telemetry::config_new::trace_id; use crate::plugins::telemetry::config_new::DatadogId; use crate::plugins::telemetry::config_new::DefaultForLevel; use crate::plugins::telemetry::config_new::Selectors; +use crate::plugins::telemetry::otel::OpenTelemetrySpanExt; use crate::plugins::telemetry::otlp::TelemetryDataKind; use crate::services::router; use crate::services::router::Request; @@ -1009,13 +1009,14 @@ mod test { use crate::plugins::telemetry::config_new::attributes::SUBGRAPH_GRAPHQL_OPERATION_TYPE; use crate::plugins::telemetry::config_new::attributes::SUBGRAPH_NAME; use crate::plugins::telemetry::config_new::Selectors; + use crate::plugins::telemetry::otel; use crate::services::router; use crate::services::subgraph; use crate::services::supergraph; #[test] fn test_router_trace_attributes() { - let subscriber = tracing_subscriber::registry().with(tracing_opentelemetry::layer()); + let subscriber = tracing_subscriber::registry().with(otel::layer()); subscriber::with_default(subscriber, || { let span_context = SpanContext::new( TraceId::from_u128(42), diff --git a/apollo-router/src/plugins/telemetry/config_new/mod.rs b/apollo-router/src/plugins/telemetry/config_new/mod.rs index 67d758456d..ea82854d71 100644 --- a/apollo-router/src/plugins/telemetry/config_new/mod.rs +++ b/apollo-router/src/plugins/telemetry/config_new/mod.rs @@ -5,8 +5,8 @@ use opentelemetry::KeyValue; use paste::paste; use tower::BoxError; use tracing::Span; -use tracing_opentelemetry::OpenTelemetrySpanExt; +use super::otel::OpenTelemetrySpanExt; use super::otlp::TelemetryDataKind; use crate::plugins::telemetry::config::AttributeValue; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; @@ -179,6 +179,7 @@ mod test { use crate::plugins::telemetry::config_new::trace_id; use crate::plugins::telemetry::config_new::DatadogId; use crate::plugins::telemetry::config_new::ToOtelValue; + use crate::plugins::telemetry::otel; #[test] fn dd_convert() { @@ -190,7 +191,7 @@ mod test { #[test] fn test_trace_id() { // Create a span with a trace ID - let subscriber = tracing_subscriber::registry().with(tracing_opentelemetry::layer()); + let subscriber = tracing_subscriber::registry().with(otel::layer()); tracing::subscriber::with_default(subscriber, || { let span_context = SpanContext::new( TraceId::from_u128(42), @@ -212,7 +213,7 @@ mod test { #[test] fn test_baggage() { // Create a span with a trace ID - let subscriber = tracing_subscriber::registry().with(tracing_opentelemetry::layer()); + let subscriber = tracing_subscriber::registry().with(otel::layer()); tracing::subscriber::with_default(subscriber, || { let span_context = SpanContext::new( TraceId::from_u128(42), diff --git a/apollo-router/src/plugins/telemetry/config_new/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/selectors.rs index 300905d96a..898d6a3bb0 100644 --- a/apollo-router/src/plugins/telemetry/config_new/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/selectors.rs @@ -932,6 +932,7 @@ mod test { use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; use crate::plugins::telemetry::config_new::selectors::TraceIdFormat; use crate::plugins::telemetry::config_new::Selector; + use crate::plugins::telemetry::otel; #[test] fn router_static() { @@ -1481,7 +1482,7 @@ mod test { #[test] fn router_baggage() { - let subscriber = tracing_subscriber::registry().with(tracing_opentelemetry::layer()); + let subscriber = tracing_subscriber::registry().with(otel::layer()); subscriber::with_default(subscriber, || { let selector = RouterSelector::Baggage { baggage: "baggage_key".to_string(), @@ -1519,7 +1520,7 @@ mod test { #[test] fn supergraph_baggage() { - let subscriber = tracing_subscriber::registry().with(tracing_opentelemetry::layer()); + let subscriber = tracing_subscriber::registry().with(otel::layer()); subscriber::with_default(subscriber, || { let selector = SupergraphSelector::Baggage { baggage: "baggage_key".to_string(), @@ -1557,7 +1558,7 @@ mod test { #[test] fn subgraph_baggage() { - let subscriber = tracing_subscriber::registry().with(tracing_opentelemetry::layer()); + let subscriber = tracing_subscriber::registry().with(otel::layer()); subscriber::with_default(subscriber, || { let selector = SubgraphSelector::Baggage { baggage: "baggage_key".to_string(), @@ -1588,7 +1589,7 @@ mod test { #[test] fn router_trace_id() { - let subscriber = tracing_subscriber::registry().with(tracing_opentelemetry::layer()); + let subscriber = tracing_subscriber::registry().with(otel::layer()); subscriber::with_default(subscriber, || { let selector = RouterSelector::TraceId { trace_id: TraceIdFormat::OpenTelemetry, diff --git a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs index 3c47009e6c..024fb95ff4 100644 --- a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs +++ b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs @@ -2,18 +2,19 @@ use std::collections::HashMap; use opentelemetry::Key; use opentelemetry::KeyValue; +use opentelemetry::OrderMap; use tracing::field::Visit; use tracing::Event; -use tracing_opentelemetry::OtelData; use tracing_subscriber::layer::Context; use tracing_subscriber::registry::LookupSpan; use tracing_subscriber::Layer; use tracing_subscriber::Registry; +use super::otel::OtelData; use super::reload::IsSampled; use super::tracing::APOLLO_PRIVATE_PREFIX; -pub(crate) const APOLLO_PRIVATE_KIND: &str = "apollo_private.kind"; +pub(crate) const APOLLO_PRIVATE_CUSTOM_EVENT: &str = "apollo_private.custom_event"; #[derive(Debug, Default)] pub(crate) struct LogAttributes { @@ -53,6 +54,21 @@ where extensions.insert(LogAttributes::default()); } } + + fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { + println!("LAAA >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<"); + // dbg!(event); + // let span = ctx.event_span(event); + // if let Some(span) = span { + // let mut extensions = span.extensions_mut(); + // if let Some(events) = extensions + // .get_mut::() + // .and_then(|ext| ext.builder.events.as_mut()) + // { + // dbg!(&events.last()); + // } + // } + } } impl DynSpanAttributeLayer { @@ -175,8 +191,6 @@ impl SpanDynAttribute for ::tracing::Span { } } -// TODO il me faut un autre layer juste pour les attributs d'events et il faut qu'il passe après celui de otel - pub(crate) struct EventsAttributes { pub(crate) events_attributes: HashMap, } @@ -209,29 +223,29 @@ where } } - /// Notifies this layer that an event has occurred. - fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { - // Je récupère mes eventsAttributes et specifiquement mon attribut pour l'event et je l'ajoute à mon otelData events.last() - let mut event_kind = EventKindVisitor::default(); - event.record(&mut event_kind); - if let Some(event_kind) = event_kind.0 { - let span = ctx.event_span(event); - if let Some(span) = span { - let mut extensions = span.extensions_mut(); - if let (Some(attributes), Some(otel_events)) = ( - extensions - .get::() - .and_then(|attrs| attrs.events_attributes.get(&event_kind)), - extensions - .get_mut::() - .and_then(|od| od.builder.events.as_mut()) - .and_then(|e| e.last_mut()), - ) { - // otel_data.builder.events. - } - } - } - } + // Notifies this layer that an event has occurred. + // fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { + // // Je récupère mes eventsAttributes et specifiquement mon attribut pour l'event et je l'ajoute à mon otelData events.last() + // let mut event_kind = EventKindVisitor::default(); + // event.record(&mut event_kind); + // if let Some(event_kind) = event_kind.0 { + // let span = ctx.event_span(event); + // if let Some(span) = span { + // let mut extensions = span.extensions_mut(); + // if let (Some(attributes), Some(otel_events)) = ( + // extensions + // .get::() + // .and_then(|attrs| attrs.events_attributes.get(&event_kind)), + // extensions + // .get_mut::() + // .and_then(|od| od.builder.events.as_mut()) + // .and_then(|e| e.last_mut()), + // ) { + // // otel_data.builder.events. + // } + // } + // } + // } // The best solution might be to directly fetch eventsAttributes from otel layer } @@ -247,13 +261,13 @@ struct EventKindVisitor(Option); impl Visit for EventKindVisitor { fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn std::fmt::Debug) { - if field.name() == APOLLO_PRIVATE_KIND { + if field.name() == APOLLO_PRIVATE_CUSTOM_EVENT { self.0 = Some(format!("{value:?}")); } } fn record_str(&mut self, field: &tracing_core::Field, value: &str) { - if field.name() == APOLLO_PRIVATE_KIND { + if field.name() == APOLLO_PRIVATE_CUSTOM_EVENT { self.0 = Some(value.to_string()); } } @@ -261,7 +275,9 @@ impl Visit for EventKindVisitor { /// To add dynamic attributes for spans pub(crate) trait EventDynAttribute { + /// Always use before sending the event fn set_event_dyn_attribute(&self, event_name: String, key: Key, value: opentelemetry::Value); + /// Always use before sending the event fn set_event_dyn_attributes( &self, event_name: String, @@ -280,22 +296,17 @@ impl EventDynAttribute for ::tracing::Span { return; } let mut extensions = s.extensions_mut(); - match extensions.get_mut::() { - Some(attributes) => { - match attributes.events_attributes.get_mut(&event_name) { - Some(attributes) => { - attributes.insert(KeyValue::new(key, value)); - } - None => { - attributes.events_attributes.insert( - event_name, - LogAttributes { - attributes: vec![KeyValue::new(key, value)], - }, - ); - } + match extensions.get_mut::() { + Some(otel_data) => match &mut otel_data.event_attributes { + Some(attributes) => { + attributes.insert(key, value); } - } + None => { + let mut order_map = OrderMap::new(); + order_map.insert(key, value); + otel_data.event_attributes = Some(order_map); + } + }, None => { // Can't use ::tracing::error! because it could create deadlock on extensions eprintln!("no EventsAttributes, this is a bug"); @@ -324,22 +335,18 @@ impl EventDynAttribute for ::tracing::Span { None => eprintln!("no spanref, this is a bug"), Some(s) => { let mut extensions = s.extensions_mut(); - match extensions.get_mut::() { - Some(registered_attributes) => { - match registered_attributes.events_attributes.get_mut(&event_name) { - Some(registered_attributes) => { - registered_attributes.extend(attributes); - } - None => { - registered_attributes.events_attributes.insert( - event_name, - LogAttributes { - attributes: attributes.collect(), - }, - ); - } + match extensions.get_mut::() { + Some(otel_data) => match &mut otel_data.event_attributes { + Some(event_attributes) => { + event_attributes + .extend(attributes.map(|kv| (kv.key, kv.value))); } - } + None => { + otel_data.event_attributes = Some(OrderMap::from_iter( + attributes.map(|kv| (kv.key, kv.value)), + )); + } + }, None => { // Can't use ::tracing::error! because it could create deadlock on extensions eprintln!("no EventsAttributes, this is a bug"); diff --git a/apollo-router/src/plugins/telemetry/formatters/json.rs b/apollo-router/src/plugins/telemetry/formatters/json.rs index dbca144005..7113ff5a72 100644 --- a/apollo-router/src/plugins/telemetry/formatters/json.rs +++ b/apollo-router/src/plugins/telemetry/formatters/json.rs @@ -10,7 +10,6 @@ use serde::ser::Serializer as _; use serde_json::Serializer; use tracing_core::Event; use tracing_core::Subscriber; -use tracing_opentelemetry::OtelData; use tracing_serde::AsSerde; use tracing_subscriber::layer::Context; use tracing_subscriber::registry::LookupSpan; @@ -23,6 +22,7 @@ use super::EXCLUDED_ATTRIBUTES; use crate::plugins::telemetry::config_new::logging::JsonFormat; use crate::plugins::telemetry::dynamic_attribute::LogAttributes; use crate::plugins::telemetry::formatters::to_list; +use crate::plugins::telemetry::otel::OtelData; #[derive(Debug)] pub(crate) struct Json { @@ -371,6 +371,7 @@ mod test { use crate::plugins::telemetry::dynamic_attribute::DynSpanAttributeLayer; use crate::plugins::telemetry::dynamic_attribute::SpanDynAttribute; use crate::plugins::telemetry::formatters::json::extract_dd_trace_id; + use crate::plugins::telemetry::otel; struct RequiresDatadogLayer; impl Layer for RequiresDatadogLayer @@ -393,7 +394,7 @@ mod test { subscriber::with_default( Registry::default() .with(RequiresDatadogLayer) - .with(tracing_opentelemetry::layer()), + .with(otel::layer()), || { let root_span = tracing::info_span!("root", dd.trace_id = "1234"); let _root_span = root_span.enter(); @@ -408,7 +409,7 @@ mod test { Registry::default() .with(RequiresDatadogLayer) .with(DynSpanAttributeLayer) - .with(tracing_opentelemetry::layer()), + .with(otel::layer()), || { let root_span = tracing::info_span!("root"); root_span.set_span_dyn_attribute("dd.trace_id".into(), "1234".into()); @@ -425,7 +426,7 @@ mod test { Registry::default() .with(RequiresDatadogLayer) .with(DynSpanAttributeLayer) - .with(tracing_opentelemetry::layer()), + .with(otel::layer()), || { let root_span = tracing::info_span!("root"); let _root_span = root_span.enter(); diff --git a/apollo-router/src/plugins/telemetry/formatters/mod.rs b/apollo-router/src/plugins/telemetry/formatters/mod.rs index 024ac287e6..286a560668 100644 --- a/apollo-router/src/plugins/telemetry/formatters/mod.rs +++ b/apollo-router/src/plugins/telemetry/formatters/mod.rs @@ -15,7 +15,6 @@ use parking_lot::Mutex; use serde_json::Number; use tracing::Subscriber; use tracing_core::callsite::Identifier; -use tracing_opentelemetry::OtelData; use tracing_subscriber::fmt::format::Writer; use tracing_subscriber::fmt::FormatEvent; use tracing_subscriber::fmt::FormatFields; @@ -29,6 +28,7 @@ use crate::metrics::layer::METRIC_PREFIX_COUNTER; use crate::metrics::layer::METRIC_PREFIX_HISTOGRAM; use crate::metrics::layer::METRIC_PREFIX_MONOTONIC_COUNTER; use crate::metrics::layer::METRIC_PREFIX_VALUE; +use crate::plugins::telemetry::otel::OtelData; pub(crate) const APOLLO_PRIVATE_PREFIX: &str = "apollo_private."; // This list comes from Otel https://opentelemetry.io/docs/specs/semconv/attributes-registry/code/ and diff --git a/apollo-router/src/plugins/telemetry/formatters/text.rs b/apollo-router/src/plugins/telemetry/formatters/text.rs index 31a1bdc488..fe907b1b24 100644 --- a/apollo-router/src/plugins/telemetry/formatters/text.rs +++ b/apollo-router/src/plugins/telemetry/formatters/text.rs @@ -10,7 +10,6 @@ use serde_json::Value; use tracing_core::Event; use tracing_core::Level; use tracing_core::Subscriber; -use tracing_opentelemetry::OtelData; use tracing_subscriber::field; use tracing_subscriber::field::Visit; use tracing_subscriber::fmt::format::DefaultVisitor; @@ -28,6 +27,7 @@ use super::EXCLUDED_ATTRIBUTES; use crate::plugins::telemetry::config_new::logging::TextFormat; use crate::plugins::telemetry::dynamic_attribute::LogAttributes; use crate::plugins::telemetry::formatters::to_list; +use crate::plugins::telemetry::otel::OtelData; use crate::plugins::telemetry::tracing::APOLLO_PRIVATE_PREFIX; pub(crate) struct Text { diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index 98b7982b7b..e1bb7cd03e 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -48,7 +48,6 @@ use tokio::runtime::Handle; use tower::BoxError; use tower::ServiceBuilder; use tower::ServiceExt; -use tracing_opentelemetry::OpenTelemetrySpanExt; use self::apollo::ForwardValues; use self::apollo::LicensedOperationCountByType; @@ -102,6 +101,7 @@ use crate::plugins::telemetry::metrics::apollo::studio::SingleStatsReport; use crate::plugins::telemetry::metrics::prometheus::commit_prometheus; use crate::plugins::telemetry::metrics::MetricsBuilder; use crate::plugins::telemetry::metrics::MetricsConfigurator; +use crate::plugins::telemetry::otel::OpenTelemetrySpanExt; use crate::plugins::telemetry::reload::metrics_layer; use crate::plugins::telemetry::reload::OPENTELEMETRY_TRACER_HANDLE; use crate::plugins::telemetry::tracing::apollo_telemetry::decode_ftv1_trace; @@ -136,6 +136,7 @@ mod fmt_layer; pub(crate) mod formatters; mod logging; pub(crate) mod metrics; +pub mod otel; mod otlp; pub(crate) mod reload; mod resource; diff --git a/apollo-router/src/plugins/telemetry/otel/layer.rs b/apollo-router/src/plugins/telemetry/otel/layer.rs index 9a12b5c50a..af5211e6e3 100644 --- a/apollo-router/src/plugins/telemetry/otel/layer.rs +++ b/apollo-router/src/plugins/telemetry/otel/layer.rs @@ -1,3 +1,5 @@ +use crate::plugins::telemetry::dynamic_attribute::APOLLO_PRIVATE_CUSTOM_EVENT; + use super::{OtelData, PreSampledTracer}; use once_cell::unsync; use opentelemetry::{ @@ -115,6 +117,7 @@ struct SpanEventVisitor<'a, 'b> { event_builder: &'a mut otel::Event, span_builder: Option<&'b mut otel::SpanBuilder>, exception_config: ExceptionFieldConfig, + custom_event: bool, } impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { @@ -124,6 +127,7 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { fn record_bool(&mut self, field: &field::Field, value: bool) { match field.name() { "message" => self.event_builder.name = value.to_string().into(), + APOLLO_PRIVATE_CUSTOM_EVENT => self.custom_event = true, // Skip fields that are actually log metadata that have already been handled #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => (), @@ -705,7 +709,11 @@ where span_builder: &mut builder, exception_config: self.exception_config, }); - extensions.insert(OtelData { builder, parent_cx }); + extensions.insert(OtelData { + builder, + parent_cx, + event_attributes: None, + }); } fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { @@ -814,9 +822,9 @@ where let target = target.string(meta.target()); let mut extensions = span.extensions_mut(); - let span_builder = extensions + let (span_builder, event_attributes) = extensions .get_mut::() - .map(|data| &mut data.builder); + .map(|data| (&mut data.builder, &mut event_attributes)); let mut otel_event = otel::Event::new( String::new(), @@ -824,11 +832,24 @@ where vec![Key::new("level").string(meta.level().as_str()), target], 0, ); - event.record(&mut SpanEventVisitor { + let mut span_event_visit = SpanEventVisitor { event_builder: &mut otel_event, span_builder, exception_config: self.exception_config, - }); + custom_event: false, + }; + event.record(&mut span_event_visit); + let custom_event = span_event_visit.custom_event; + // TODO if custom_event, add event_attributes + if custom_event { + if let Some(event_attributes) = event_attributes.clear() { + otel_event.attributes.extend( + event_attributes + .into_iter() + .map(|(key, value)| KeyValue::new(key, value)), + ) + } + } if let Some(OtelData { builder, .. }) = extensions.get_mut::() { if builder.status == otel::Status::Unset @@ -888,6 +909,7 @@ where if let Some(OtelData { mut builder, parent_cx, + .. }) = extensions.remove::() { if self.tracked_inactivity { @@ -991,6 +1013,7 @@ mod tests { *self.0.lock().unwrap() = Some(OtelData { builder, parent_cx: parent_cx.clone(), + event_attributes: None, }); noop::NoopSpan::new() } diff --git a/apollo-router/src/plugins/telemetry/otel/mod.rs b/apollo-router/src/plugins/telemetry/otel/mod.rs index f1ec71adc1..7ee0880222 100644 --- a/apollo-router/src/plugins/telemetry/otel/mod.rs +++ b/apollo-router/src/plugins/telemetry/otel/mod.rs @@ -7,6 +7,7 @@ mod tracer; pub(crate) use layer::{layer, OpenTelemetryLayer}; +use opentelemetry::{Key, OrderMap, Value}; pub use span_ext::OpenTelemetrySpanExt; pub(crate) use tracer::PreSampledTracer; @@ -20,4 +21,7 @@ pub(crate) struct OtelData { /// The otel span data recorded during the current tracing span. pub(crate) builder: opentelemetry::trace::SpanBuilder, + + /// Attributes gathered for the next event + pub(crate) event_attributes: Option>, } diff --git a/apollo-router/src/plugins/telemetry/otel/tracer.rs b/apollo-router/src/plugins/telemetry/otel/tracer.rs index ff69b38143..ff2716f222 100644 --- a/apollo-router/src/plugins/telemetry/otel/tracer.rs +++ b/apollo-router/src/plugins/telemetry/otel/tracer.rs @@ -171,7 +171,11 @@ mod tests { builder.span_id = Some(SpanId::from(1u64)); builder.trace_id = None; let parent_cx = OtelContext::new(); - let cx = tracer.sampled_context(&mut OtelData { builder, parent_cx }); + let cx = tracer.sampled_context(&mut OtelData { + builder, + parent_cx, + event_attributes: None, + }); let span = cx.span(); let span_context = span.span_context(); @@ -210,7 +214,11 @@ mod tests { let tracer = provider.tracer("test"); let mut builder = SpanBuilder::from_name("parent".to_string()); builder.sampling_result = previous_sampling_result; - let sampled = tracer.sampled_context(&mut OtelData { builder, parent_cx }); + let sampled = tracer.sampled_context(&mut OtelData { + builder, + parent_cx, + event_attributes: None, + }); assert_eq!( sampled.span().span_context().is_sampled(), diff --git a/apollo-router/src/plugins/telemetry/reload.rs b/apollo-router/src/plugins/telemetry/reload.rs index 9c32649cdc..350dce62d5 100644 --- a/apollo-router/src/plugins/telemetry/reload.rs +++ b/apollo-router/src/plugins/telemetry/reload.rs @@ -16,8 +16,6 @@ use rand::thread_rng; use rand::Rng; use tower::BoxError; use tracing_core::Subscriber; -use tracing_opentelemetry::OpenTelemetryLayer; -use tracing_opentelemetry::PreSampledTracer; use tracing_subscriber::filter::Filtered; use tracing_subscriber::fmt::FormatFields; use tracing_subscriber::layer::Filter; @@ -44,6 +42,9 @@ use crate::metrics::meter_provider; use crate::plugins::telemetry::formatters::filter_metric_events; use crate::plugins::telemetry::formatters::text::Text; use crate::plugins::telemetry::formatters::FilteringFormatter; +use crate::plugins::telemetry::otel; +use crate::plugins::telemetry::otel::OpenTelemetryLayer; +use crate::plugins::telemetry::otel::PreSampledTracer; use crate::plugins::telemetry::tracing::reload::ReloadTracer; use crate::router_factory::STARTING_SPAN_NAME; @@ -85,7 +86,7 @@ pub(crate) fn init_telemetry(log_level: &str) -> Result<()> { None, ), ); - let opentelemetry_layer = tracing_opentelemetry::layer() + let opentelemetry_layer = otel::layer() .with_tracer(hot_tracer.clone()) .with_filter(SamplingFilter::new()); diff --git a/apollo-router/src/plugins/telemetry/tracing/reload.rs b/apollo-router/src/plugins/telemetry/tracing/reload.rs index fcf7d1a395..c058ede1a6 100644 --- a/apollo-router/src/plugins/telemetry/tracing/reload.rs +++ b/apollo-router/src/plugins/telemetry/tracing/reload.rs @@ -4,7 +4,9 @@ use std::sync::RwLock; use opentelemetry::trace::SpanBuilder; use opentelemetry::trace::Tracer; -use tracing_opentelemetry::PreSampledTracer; + +use crate::plugins::telemetry::otel::OtelData; +use crate::plugins::telemetry::otel::PreSampledTracer; #[derive(Clone)] pub(crate) struct ReloadTracer { @@ -12,10 +14,7 @@ pub(crate) struct ReloadTracer { } impl PreSampledTracer for ReloadTracer { - fn sampled_context( - &self, - data: &mut tracing_opentelemetry::OtelData, - ) -> opentelemetry::Context { + fn sampled_context(&self, data: &mut OtelData) -> opentelemetry::Context { self.parent .read() .expect("parent tracer must be available") diff --git a/apollo-router/src/services/external.rs b/apollo-router/src/services/external.rs index b1f0982a12..cf75320b62 100644 --- a/apollo-router/src/services/external.rs +++ b/apollo-router/src/services/external.rs @@ -20,8 +20,8 @@ use serde::Serialize; use strum_macros::Display; use tower::BoxError; use tower::Service; -use tracing_opentelemetry::OpenTelemetrySpanExt; +use crate::plugins::telemetry::otel::OpenTelemetrySpanExt; use crate::plugins::telemetry::reload::prepare_context; use crate::query_planner::QueryPlan; use crate::Context; diff --git a/apollo-router/src/services/http/service.rs b/apollo-router/src/services/http/service.rs index 89c95ec7cf..cb6eba3a34 100644 --- a/apollo-router/src/services/http/service.rs +++ b/apollo-router/src/services/http/service.rs @@ -31,7 +31,6 @@ use tower_http::decompression::Decompression; use tower_http::decompression::DecompressionBody; use tower_http::decompression::DecompressionLayer; use tracing::Instrument; -use tracing_opentelemetry::OpenTelemetrySpanExt; use super::HttpRequest; use super::HttpResponse; @@ -39,6 +38,7 @@ use crate::axum_factory::compression::Compressor; use crate::configuration::TlsClientAuth; use crate::error::FetchError; use crate::plugins::authentication::subgraph::SigningParamsConfig; +use crate::plugins::telemetry::otel::OpenTelemetrySpanExt; use crate::plugins::telemetry::reload::prepare_context; use crate::plugins::telemetry::LOGGING_DISPLAY_BODY; use crate::plugins::telemetry::LOGGING_DISPLAY_HEADERS; diff --git a/apollo-router/src/tracer.rs b/apollo-router/src/tracer.rs index a846b240ac..d90341c5ba 100644 --- a/apollo-router/src/tracer.rs +++ b/apollo-router/src/tracer.rs @@ -7,7 +7,8 @@ use opentelemetry::trace::TraceContextExt; use serde::Deserialize; use serde::Serialize; use tracing::Span; -use tracing_opentelemetry::OpenTelemetrySpanExt; + +use crate::plugins::telemetry::otel::OpenTelemetrySpanExt; /// Trace ID #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] @@ -59,6 +60,7 @@ mod test { use tracing_subscriber::Registry; use super::TraceId; + use crate::plugins::telemetry::otel; // If we try to run more than one test concurrently which relies on the existence of a pipeline, // then the tests will fail due to manipulation of global state in the opentelemetry crates. @@ -101,7 +103,7 @@ mod test { .build(); let tracer = provider.versioned_tracer("noop", None::, None::, None); - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + let telemetry = otel::layer().with_tracer(tracer); // Use the tracing subscriber `Registry`, or any other subscriber // that impls `LookupSpan` let subscriber = Registry::default().with(telemetry); @@ -125,7 +127,7 @@ mod test { .with_simple_exporter(opentelemetry_stdout::SpanExporter::default()) .build(); let tracer = provider.versioned_tracer("noop", None::, None::, None); - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + let telemetry = otel::layer().with_tracer(tracer); // Use the tracing subscriber `Registry`, or any other subscriber // that impls `LookupSpan` let subscriber = Registry::default().with(telemetry); @@ -150,7 +152,7 @@ mod test { .with_simple_exporter(opentelemetry_stdout::SpanExporter::default()) .build(); let tracer = provider.versioned_tracer("noop", None::, None::, None); - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + let telemetry = otel::layer().with_tracer(tracer); // Use the tracing subscriber `Registry`, or any other subscriber // that impls `LookupSpan` let subscriber = Registry::default().with(telemetry); diff --git a/apollo-router/tests/common.rs b/apollo-router/tests/common.rs index 526b5f8cfd..bd76cde912 100644 --- a/apollo-router/tests/common.rs +++ b/apollo-router/tests/common.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; use std::process::Stdio; use std::time::Duration; +use apollo_router::otel::OpenTelemetrySpanExt; use buildstructor::buildstructor; use http::header::ACCEPT; use http::header::CONTENT_TYPE; @@ -49,7 +50,6 @@ use tracing_core::Dispatch; use tracing_core::LevelFilter; use tracing_futures::Instrument; use tracing_futures::WithSubscriber; -use tracing_opentelemetry::OpenTelemetrySpanExt; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::EnvFilter; use tracing_subscriber::Layer; From 3f73f8b2d1f0cfb68e00b69ce1b552faba2f43d6 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:33:59 +0200 Subject: [PATCH 11/40] refactor event attributes Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../plugins/telemetry/config_new/events.rs | 35 ++- ...ew__events__tests__router_events@logs.snap | 6 - ...__events__tests__subgraph_events@logs.snap | 2 - ...events__tests__supergraph_events@logs.snap | 3 - .../plugins/telemetry/dynamic_attribute.rs | 89 +------ .../src/plugins/telemetry/formatters/json.rs | 11 + .../src/plugins/telemetry/formatters/text.rs | 223 +++++++++++++----- .../src/plugins/telemetry/otel/layer.rs | 111 ++++----- .../src/plugins/telemetry/otel/tracer.rs | 38 ++- .../src/services/subgraph_service.rs | 6 +- .../src/services/supergraph/service.rs | 2 +- .../tests/integration/telemetry/logging.rs | 2 + 12 files changed, 290 insertions(+), 238 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/events.rs b/apollo-router/src/plugins/telemetry/config_new/events.rs index f9dc96d656..b558ac73cb 100644 --- a/apollo-router/src/plugins/telemetry/config_new/events.rs +++ b/apollo-router/src/plugins/telemetry/config_new/events.rs @@ -4,10 +4,12 @@ use std::sync::Arc; #[cfg(test)] use http::HeaderValue; +use opentelemetry::Key; use parking_lot::Mutex; use schemars::JsonSchema; use serde::Deserialize; use tower::BoxError; +use tracing::Span; use super::instruments::Instrumented; use super::Selector; @@ -20,6 +22,7 @@ use crate::plugins::telemetry::config_new::extendable::Extendable; use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::selectors::SubgraphSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; +use crate::plugins::telemetry::dynamic_attribute::EventDynAttribute; use crate::services::router; use crate::services::subgraph; use crate::services::supergraph; @@ -180,7 +183,7 @@ impl Instrumented "http.request.body".to_string(), format!("{:?}", request.router_request.body()), ); - log_event(self.request, "router.request", &attrs, ""); + log_event(self.request, "router.request", attrs, ""); } for custom_event in &self.custom { custom_event.on_request(request); @@ -219,7 +222,7 @@ impl Instrumented "http.response.body".to_string(), format!("{:?}", response.response.body()), ); - log_event(self.response, "router.response", &attrs, ""); + log_event(self.response, "router.response", attrs, ""); } for custom_event in &self.custom { custom_event.on_response(response); @@ -230,7 +233,7 @@ impl Instrumented if self.error != EventLevel::Off { let mut attrs = HashMap::with_capacity(1); attrs.insert("error".to_string(), error.to_string()); - log_event(self.error, "router.error", &attrs, ""); + log_event(self.error, "router.error", attrs, ""); } for custom_event in &self.custom { custom_event.on_error(error, ctx); @@ -281,7 +284,7 @@ impl Instrumented "http.request.body".to_string(), serde_json::to_string(request.supergraph_request.body()).unwrap_or_default(), ); - log_event(self.request, "supergraph.request", &attrs, ""); + log_event(self.request, "supergraph.request", attrs, ""); } if self.response != EventLevel::Off { request @@ -305,7 +308,7 @@ impl Instrumented if self.error != EventLevel::Off { let mut attrs = HashMap::with_capacity(1); attrs.insert("error".to_string(), error.to_string()); - log_event(self.error, "supergraph.error", &attrs, ""); + log_event(self.error, "supergraph.error", attrs, ""); } for custom_event in &self.custom { custom_event.on_error(error, ctx); @@ -350,7 +353,7 @@ impl Instrumented let mut attrs = HashMap::with_capacity(1); attrs.insert("error".to_string(), error.to_string()); - log_event(self.error, "subgraph.error", &attrs, ""); + log_event(self.error, "subgraph.error", attrs, ""); } for custom_event in &self.custom { custom_event.on_error(error, ctx); @@ -539,7 +542,7 @@ where .map(|kv| (kv.key.to_string(), kv.value.to_string())) .collect(); - log_event(self.level, &self.name, &attributes, &self.message); + log_event(self.level, &self.name, attributes, &self.message); } } @@ -547,7 +550,7 @@ where pub(crate) fn log_event( level: EventLevel, kind: &str, - attributes: &HashMap, + attributes: HashMap, message: &str, ) { #[cfg(test)] @@ -555,13 +558,23 @@ pub(crate) fn log_event( attributes.clone().into_iter().collect(); #[cfg(test)] attributes.sort_keys(); + let span = Span::current(); + span.set_event_dyn_attributes( + attributes + .into_iter() + .map(|(key, value)| Key::from(key).string(value)), + ); match level { EventLevel::Info => { - ::tracing::info!(%kind, attributes = ?attributes, "{}", message); + ::tracing::info!(%kind, "{}", message); + } + EventLevel::Warn => { + ::tracing::warn!(%kind, "{}", message) + } + EventLevel::Error => { + ::tracing::error!(%kind, "{}", message) } - EventLevel::Warn => ::tracing::warn!(%kind, attributes = ?attributes, "{}", message), - EventLevel::Error => ::tracing::error!(%kind, attributes = ?attributes, "{}", message), EventLevel::Off => {} } } diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap index 836cf272f7..29ce2dc07c 100644 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__router_events@logs.snap @@ -3,7 +3,6 @@ source: apollo-router/src/plugins/telemetry/config_new/events.rs expression: yaml --- - fields: - attributes: "{\"http.request.body\": \"Body(Empty)\", \"http.request.headers\": \"{\\\"content-length\\\": \\\"0\\\", \\\"custom-header\\\": \\\"val1\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.request.method\": \"GET\", \"http.request.uri\": \"http://example.com/\", \"http.request.version\": \"HTTP/1.1\"}" kind: router.request level: INFO message: "" @@ -24,7 +23,6 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.request.body.size\": \"0\"}" kind: my.request_event level: INFO message: my event message @@ -45,7 +43,6 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.headers\": \"{\\\"content-length\\\": \\\"25\\\", \\\"custom-header\\\": \\\"val1\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.response.status\": \"200 OK\", \"http.response.version\": \"HTTP/1.1\"}" kind: router.response level: INFO message: "" @@ -68,7 +65,6 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.response.body.size\": \"25\"}" kind: my.response_event level: INFO message: my response event message @@ -91,7 +87,6 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.request.body\": \"Body(Empty)\", \"http.request.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.request.method\": \"GET\", \"http.request.uri\": \"http://example.com/\", \"http.request.version\": \"HTTP/1.1\"}" kind: router.request level: INFO message: "" @@ -112,7 +107,6 @@ expression: yaml otel.kind: INTERNAL trace_id: "" - fields: - attributes: "{\"http.response.body\": \"Body(Full(b\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"data\\\\\\\":\\\\\\\"res\\\\\\\"}}\\\"))\", \"http.response.headers\": \"{\\\"custom-header\\\": \\\"val1\\\"}\", \"http.response.status\": \"200 OK\", \"http.response.version\": \"HTTP/1.1\"}" kind: router.response level: INFO message: "" diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap index 4a89ba611a..b0d8235272 100644 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__subgraph_events@logs.snap @@ -3,7 +3,6 @@ source: apollo-router/src/plugins/telemetry/config_new/events.rs expression: yaml --- - fields: - attributes: "{}" kind: my.request.event level: INFO message: my event message @@ -20,7 +19,6 @@ expression: yaml name: subgraph otel.kind: INTERNAL - fields: - attributes: "{\"response_status\": \"200\", \"subgraph.name\": \"subgraph\"}" kind: my.response.event level: ERROR message: my response event message diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap index b74445222b..e83288be4e 100644 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__events__tests__supergraph_events@logs.snap @@ -3,7 +3,6 @@ source: apollo-router/src/plugins/telemetry/config_new/events.rs expression: yaml --- - fields: - attributes: "{\"http.request.body\": \"{\\\"query\\\":\\\"query { foo }\\\"}\", \"http.request.headers\": \"{\\\"content-type\\\": \\\"application/json\\\", \\\"x-log-request\\\": \\\"log\\\"}\", \"http.request.method\": \"POST\", \"http.request.uri\": \"http://default/\", \"http.request.version\": \"HTTP/1.1\"}" kind: supergraph.request level: INFO message: "" @@ -20,7 +19,6 @@ expression: yaml name: supergraph otel.kind: INTERNAL - fields: - attributes: "{}" kind: my.request.event level: INFO message: my event message @@ -37,7 +35,6 @@ expression: yaml name: supergraph otel.kind: INTERNAL - fields: - attributes: "{}" kind: my.response_event level: WARN message: my response event message diff --git a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs index 024fb95ff4..bba0fb14e9 100644 --- a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs +++ b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs @@ -14,8 +14,6 @@ use super::otel::OtelData; use super::reload::IsSampled; use super::tracing::APOLLO_PRIVATE_PREFIX; -pub(crate) const APOLLO_PRIVATE_CUSTOM_EVENT: &str = "apollo_private.custom_event"; - #[derive(Debug, Default)] pub(crate) struct LogAttributes { attributes: Vec, @@ -56,7 +54,6 @@ where } fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { - println!("LAAA >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<"); // dbg!(event); // let span = ctx.event_span(event); // if let Some(span) = span { @@ -203,90 +200,16 @@ impl Default for EventsAttributes { } } -/// To add dynamic attributes for spans -pub(crate) struct DynEventAttributeLayer; - -impl Layer for DynEventAttributeLayer -where - S: tracing_core::Subscriber + for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>, -{ - fn on_new_span( - &self, - _attrs: &tracing_core::span::Attributes<'_>, - id: &tracing_core::span::Id, - ctx: Context<'_, S>, - ) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - if extensions.get_mut::().is_none() { - extensions.insert(EventsAttributes::default()); - } - } - - // Notifies this layer that an event has occurred. - // fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { - // // Je récupère mes eventsAttributes et specifiquement mon attribut pour l'event et je l'ajoute à mon otelData events.last() - // let mut event_kind = EventKindVisitor::default(); - // event.record(&mut event_kind); - // if let Some(event_kind) = event_kind.0 { - // let span = ctx.event_span(event); - // if let Some(span) = span { - // let mut extensions = span.extensions_mut(); - // if let (Some(attributes), Some(otel_events)) = ( - // extensions - // .get::() - // .and_then(|attrs| attrs.events_attributes.get(&event_kind)), - // extensions - // .get_mut::() - // .and_then(|od| od.builder.events.as_mut()) - // .and_then(|e| e.last_mut()), - // ) { - // // otel_data.builder.events. - // } - // } - // } - // } - - // The best solution might be to directly fetch eventsAttributes from otel layer -} - -impl DynEventAttributeLayer { - pub(crate) fn new() -> Self { - Self {} - } -} - -#[derive(Default)] -struct EventKindVisitor(Option); - -impl Visit for EventKindVisitor { - fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn std::fmt::Debug) { - if field.name() == APOLLO_PRIVATE_CUSTOM_EVENT { - self.0 = Some(format!("{value:?}")); - } - } - - fn record_str(&mut self, field: &tracing_core::Field, value: &str) { - if field.name() == APOLLO_PRIVATE_CUSTOM_EVENT { - self.0 = Some(value.to_string()); - } - } -} - /// To add dynamic attributes for spans pub(crate) trait EventDynAttribute { /// Always use before sending the event - fn set_event_dyn_attribute(&self, event_name: String, key: Key, value: opentelemetry::Value); + fn set_event_dyn_attribute(&self, key: Key, value: opentelemetry::Value); /// Always use before sending the event - fn set_event_dyn_attributes( - &self, - event_name: String, - attributes: impl IntoIterator, - ); + fn set_event_dyn_attributes(&self, attributes: impl IntoIterator); } impl EventDynAttribute for ::tracing::Span { - fn set_event_dyn_attribute(&self, event_name: String, key: Key, value: opentelemetry::Value) { + fn set_event_dyn_attribute(&self, key: Key, value: opentelemetry::Value) { self.with_subscriber(move |(id, dispatch)| { if let Some(reg) = dispatch.downcast_ref::() { match reg.span(id) { @@ -320,11 +243,7 @@ impl EventDynAttribute for ::tracing::Span { }); } - fn set_event_dyn_attributes( - &self, - event_name: String, - attributes: impl IntoIterator, - ) { + fn set_event_dyn_attributes(&self, attributes: impl IntoIterator) { let mut attributes = attributes.into_iter().peekable(); if attributes.peek().is_none() { return; diff --git a/apollo-router/src/plugins/telemetry/formatters/json.rs b/apollo-router/src/plugins/telemetry/formatters/json.rs index 7113ff5a72..668b4f146c 100644 --- a/apollo-router/src/plugins/telemetry/formatters/json.rs +++ b/apollo-router/src/plugins/telemetry/formatters/json.rs @@ -19,6 +19,7 @@ use super::get_trace_and_span_id; use super::EventFormatter; use super::APOLLO_PRIVATE_PREFIX; use super::EXCLUDED_ATTRIBUTES; +use crate::plugins::telemetry::config::AttributeValue; use crate::plugins::telemetry::config_new::logging::JsonFormat; use crate::plugins::telemetry::dynamic_attribute::LogAttributes; use crate::plugins::telemetry::formatters::to_list; @@ -234,6 +235,16 @@ where .serialize_entry("span_id", &span_id.to_string()) .unwrap_or(()); } + }; + let event_attributes = { + let mut extensions = span.extensions_mut(); + let mut otel_data = extensions.get_mut::(); + otel_data.as_mut().and_then(|od| od.event_attributes.take()) + }; + if let Some(event_attributes) = event_attributes { + for (key, value) in event_attributes { + serializer.serialize_entry(key.as_str(), &AttributeValue::from(value))?; + } } } diff --git a/apollo-router/src/plugins/telemetry/formatters/text.rs b/apollo-router/src/plugins/telemetry/formatters/text.rs index fe907b1b24..a8c4e4085a 100644 --- a/apollo-router/src/plugins/telemetry/formatters/text.rs +++ b/apollo-router/src/plugins/telemetry/formatters/text.rs @@ -6,13 +6,17 @@ use std::fmt; use nu_ansi_term::Color; use nu_ansi_term::Style; use opentelemetry::sdk::Resource; +use opentelemetry::Key; +use opentelemetry::OrderMap; use serde_json::Value; use tracing_core::Event; +use tracing_core::Field; use tracing_core::Level; use tracing_core::Subscriber; use tracing_subscriber::field; use tracing_subscriber::field::Visit; -use tracing_subscriber::fmt::format::DefaultVisitor; +use tracing_subscriber::field::VisitFmt; +use tracing_subscriber::field::VisitOutput; use tracing_subscriber::fmt::format::Writer; #[cfg(not(test))] use tracing_subscriber::fmt::time::FormatTime; @@ -357,63 +361,24 @@ where self.format_target(&mut writer, meta.target())?; } self.format_location(event, &mut writer)?; - - let mut visitor = CustomVisitor::new(DefaultVisitor::new(writer.by_ref(), true)); - event.record(&mut visitor); + let mut default_visitor = + DefaultVisitor::new(writer.by_ref(), true, self.config.ansi_escape_codes); + + if let Some(span) = ctx.event_span(event) { + let mut extensions = span.extensions_mut(); + let otel_data = extensions.get_mut::(); + if let Some(event_attributes) = otel_data.and_then(|od| od.event_attributes.take()) { + for (key, value) in event_attributes { + default_visitor.log_debug(key.as_str(), &value.as_str()); + } + } + } + event.record(&mut default_visitor); writeln!(writer) } } -struct CustomVisitor(N); - -impl CustomVisitor -where - N: field::Visit, -{ - fn new(inner: N) -> Self { - Self(inner) - } -} - -// TODO we are now able to filter fields here, for now it's just a passthrough -impl Visit for CustomVisitor -where - N: Visit, -{ - fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn fmt::Debug) { - self.0.record_debug(field, value) - } - - fn record_str(&mut self, field: &tracing_core::Field, value: &str) { - self.0.record_str(field, value) - } - - fn record_error( - &mut self, - field: &tracing_core::Field, - value: &(dyn std::error::Error + 'static), - ) { - self.0.record_error(field, value) - } - - fn record_f64(&mut self, field: &tracing_core::Field, value: f64) { - self.0.record_f64(field, value) - } - - fn record_i64(&mut self, field: &tracing_core::Field, value: i64) { - self.0.record_i64(field, value) - } - - fn record_u64(&mut self, field: &tracing_core::Field, value: u64) { - self.0.record_u64(field, value) - } - - fn record_bool(&mut self, field: &tracing_core::Field, value: bool) { - self.0.record_bool(field, value) - } -} - struct FmtThreadName<'a> { name: &'a str, } @@ -456,3 +421,155 @@ impl<'a> fmt::Display for FmtThreadName<'a> { write!(f, "{:>width$}", self.name, width = max_len) } } + +/// The [visitor] produced by [`DefaultFields`]'s [`MakeVisitor`] implementation. +/// +/// [visitor]: super::super::field::Visit +/// [`MakeVisitor`]: super::super::field::MakeVisitor +#[derive(Debug)] +struct DefaultVisitor<'a> { + writer: Writer<'a>, + is_empty: bool, + is_ansi: bool, + result: fmt::Result, +} + +// === impl DefaultVisitor === + +impl<'a> DefaultVisitor<'a> { + /// Returns a new default visitor that formats to the provided `writer`. + /// + /// # Arguments + /// - `writer`: the writer to format to. + /// - `is_empty`: whether or not any fields have been previously written to + /// that writer. + fn new(writer: Writer<'a>, is_empty: bool, is_ansi: bool) -> Self { + Self { + writer, + is_empty, + is_ansi, + result: Ok(()), + } + } + + fn maybe_pad(&mut self) { + if self.is_empty { + self.is_empty = false; + } else { + self.result = write!(self.writer, " "); + } + } + + fn bold(&self) -> Style { + if self.is_ansi { + return Style::new().bold(); + } + + Style::new() + } + + fn dimmed(&self) -> Style { + if self.is_ansi { + return Style::new().dimmed(); + } + + Style::new() + } + + fn italic(&self) -> Style { + if self.is_ansi { + return Style::new().italic(); + } + + Style::new() + } + + fn log_debug(&mut self, field_name: &str, value: &dyn fmt::Debug) { + if self.result.is_err() { + return; + } + + self.maybe_pad(); + self.result = match field_name { + "message" => write!(self.writer, "{:?}", value), + name if name.starts_with("r#") => write!( + self.writer, + "{}{}{:?}", + self.italic().paint(&name[2..]), + self.dimmed().paint("="), + value + ), + name => write!( + self.writer, + "{}{}{:?}", + self.italic().paint(name), + self.dimmed().paint("="), + value + ), + }; + } +} + +impl<'a> field::Visit for DefaultVisitor<'a> { + fn record_str(&mut self, field: &Field, value: &str) { + if self.result.is_err() { + return; + } + + if field.name() == "message" { + self.record_debug(field, &format_args!("{}", value)) + } else { + self.record_debug(field, &value) + } + } + + fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { + if let Some(source) = value.source() { + let italic = self.italic(); + self.record_debug( + field, + &format_args!( + "{} {}{}{}{}", + value, + italic.paint(field.name()), + italic.paint(".sources"), + self.dimmed().paint("="), + ErrorSourceList(source) + ), + ) + } else { + self.record_debug(field, &format_args!("{}", value)) + } + } + + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { + self.log_debug(field.name(), value) + } +} + +impl<'a> VisitOutput for DefaultVisitor<'a> { + fn finish(self) -> fmt::Result { + self.result + } +} + +impl<'a> VisitFmt for DefaultVisitor<'a> { + fn writer(&mut self) -> &mut dyn fmt::Write { + &mut self.writer + } +} + +/// Renders an error into a list of sources, *including* the error +struct ErrorSourceList<'a>(&'a (dyn std::error::Error + 'static)); + +impl<'a> std::fmt::Display for ErrorSourceList<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut list = f.debug_list(); + let mut curr = Some(self.0); + while let Some(curr_err) = curr { + list.entry(&format_args!("{}", curr_err)); + curr = curr_err.source(); + } + list.finish() + } +} diff --git a/apollo-router/src/plugins/telemetry/otel/layer.rs b/apollo-router/src/plugins/telemetry/otel/layer.rs index af5211e6e3..aac1d564ae 100644 --- a/apollo-router/src/plugins/telemetry/otel/layer.rs +++ b/apollo-router/src/plugins/telemetry/otel/layer.rs @@ -1,22 +1,34 @@ -use crate::plugins::telemetry::dynamic_attribute::APOLLO_PRIVATE_CUSTOM_EVENT; - -use super::{OtelData, PreSampledTracer}; -use once_cell::unsync; -use opentelemetry::{ - trace::{self as otel, noop, OrderMap, TraceContextExt}, - Context as OtelContext, Key, KeyValue, StringValue, Value, -}; use std::any::TypeId; use std::fmt; use std::marker; use std::thread; -use std::time::{Instant, SystemTime}; -use tracing_core::span::{self, Attributes, Id, Record}; -use tracing_core::{field, Event, Subscriber}; +use std::time::Instant; +use std::time::SystemTime; + +use once_cell::unsync; +use opentelemetry::trace::noop; +use opentelemetry::trace::OrderMap; +use opentelemetry::trace::TraceContextExt; +use opentelemetry::trace::{self as otel}; +use opentelemetry::Context as OtelContext; +use opentelemetry::Key; +use opentelemetry::KeyValue; +use opentelemetry::StringValue; +use opentelemetry::Value; +use tracing_core::field; +use tracing_core::span::Attributes; +use tracing_core::span::Id; +use tracing_core::span::Record; +use tracing_core::span::{self}; +use tracing_core::Event; +use tracing_core::Subscriber; use tracing_subscriber::layer::Context; use tracing_subscriber::registry::LookupSpan; use tracing_subscriber::Layer; +use super::OtelData; +use super::PreSampledTracer; + const SPAN_NAME_FIELD: &str = "otel.name"; const SPAN_KIND_FIELD: &str = "otel.kind"; const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code"; @@ -127,11 +139,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { fn record_bool(&mut self, field: &field::Field, value: bool) { match field.name() { "message" => self.event_builder.name = value.to_string().into(), - APOLLO_PRIVATE_CUSTOM_EVENT => self.custom_event = true, - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), name => { + if name == "kind" { + self.custom_event = true; + } self.event_builder .attributes .push(KeyValue::new(name, value)); @@ -802,29 +813,14 @@ where if let Some(span) = ctx.lookup_current() { // Performing read operations before getting a write lock to avoid a deadlock // See https://github.com/tokio-rs/tracing/issues/763 - #[cfg(feature = "tracing-log")] - let normalized_meta = event.normalized_metadata(); - #[cfg(feature = "tracing-log")] - let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); - #[cfg(not(feature = "tracing-log"))] let meta = event.metadata(); - let target = Key::new("target"); - #[cfg(feature = "tracing-log")] - let target = if normalized_meta.is_some() { - target.string(meta.target().to_owned()) - } else { - target.string(event.metadata().target()) - }; - - #[cfg(not(feature = "tracing-log"))] let target = target.string(meta.target()); let mut extensions = span.extensions_mut(); - let (span_builder, event_attributes) = extensions - .get_mut::() - .map(|data| (&mut data.builder, &mut event_attributes)); + let mut otel_data = extensions.get_mut::(); + let span_builder = otel_data.as_mut().map(|o| &mut o.builder); let mut otel_event = otel::Event::new( String::new(), @@ -840,9 +836,11 @@ where }; event.record(&mut span_event_visit); let custom_event = span_event_visit.custom_event; - // TODO if custom_event, add event_attributes + // Add custom event attributes for this event if custom_event { - if let Some(event_attributes) = event_attributes.clear() { + let event_attributes = otel_data.as_ref().and_then(|o| o.event_attributes.clone()); + + if let Some(event_attributes) = event_attributes { otel_event.attributes.extend( event_attributes .into_iter() @@ -859,18 +857,10 @@ where } if self.location { - #[cfg(not(feature = "tracing-log"))] - let normalized_meta: Option> = None; - let (file, module) = match &normalized_meta { - Some(meta) => ( - meta.file().map(|s| Value::from(s.to_owned())), - meta.module_path().map(|s| Value::from(s.to_owned())), - ), - None => ( - event.metadata().file().map(Value::from), - event.metadata().module_path().map(Value::from), - ), - }; + let (file, module) = ( + event.metadata().file().map(Value::from), + event.metadata().module_path().map(Value::from), + ); if let Some(file) = file { otel_event @@ -889,7 +879,6 @@ where } } - println!("ICIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII !!!!!!!!!!!!!!!!!!!!"); if let Some(ref mut events) = builder.events { events.push(otel_event); } else { @@ -973,22 +962,22 @@ fn thread_id_integer(id: thread::ThreadId) -> u64 { #[cfg(test)] mod tests { - use super::*; - use opentelemetry::{ - trace::{noop, TraceFlags}, - StringValue, - }; - use std::{ - borrow::Cow, - collections::HashMap, - error::Error, - fmt::Display, - sync::{Arc, Mutex}, - thread, - time::SystemTime, - }; + use std::borrow::Cow; + use std::collections::HashMap; + use std::error::Error; + use std::fmt::Display; + use std::sync::Arc; + use std::sync::Mutex; + use std::thread; + use std::time::SystemTime; + + use opentelemetry::trace::noop; + use opentelemetry::trace::TraceFlags; + use opentelemetry::StringValue; use tracing_subscriber::prelude::*; + use super::*; + #[derive(Debug, Clone)] struct TestTracer(Arc>>); impl otel::Tracer for TestTracer { diff --git a/apollo-router/src/plugins/telemetry/otel/tracer.rs b/apollo-router/src/plugins/telemetry/otel/tracer.rs index ff2716f222..4a369568d3 100644 --- a/apollo-router/src/plugins/telemetry/otel/tracer.rs +++ b/apollo-router/src/plugins/telemetry/otel/tracer.rs @@ -1,14 +1,21 @@ -use super::OtelData; +use opentelemetry::trace as otel; +use opentelemetry::trace::noop; use opentelemetry::trace::OrderMap; -use opentelemetry::{ - trace as otel, - trace::{ - noop, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind, - TraceContextExt, TraceFlags, TraceId, TraceState, - }, - Context as OtelContext, -}; -use opentelemetry_sdk::trace::{Tracer as SdkTracer, TracerProvider as SdkTracerProvider}; +use opentelemetry::trace::SamplingDecision; +use opentelemetry::trace::SamplingResult; +use opentelemetry::trace::SpanBuilder; +use opentelemetry::trace::SpanContext; +use opentelemetry::trace::SpanId; +use opentelemetry::trace::SpanKind; +use opentelemetry::trace::TraceContextExt; +use opentelemetry::trace::TraceFlags; +use opentelemetry::trace::TraceId; +use opentelemetry::trace::TraceState; +use opentelemetry::Context as OtelContext; +use opentelemetry_sdk::trace::Tracer as SdkTracer; +use opentelemetry_sdk::trace::TracerProvider as SdkTracerProvider; + +use super::OtelData; /// An interface for authors of OpenTelemetry SDKs to build pre-sampled tracers. /// @@ -36,7 +43,7 @@ use opentelemetry_sdk::trace::{Tracer as SdkTracer, TracerProvider as SdkTracerP /// [`OpenTelemetrySpanExt::set_parent`]: crate::OpenTelemetrySpanExt::set_parent /// [`OpenTelemetrySpanExt::context`]: crate::OpenTelemetrySpanExt::context /// [`Context`]: opentelemetry::Context -pub trait PreSampledTracer { +pub(crate) trait PreSampledTracer { /// Produce an otel context containing an active and pre-sampled span for /// the given span builder data. /// @@ -159,9 +166,14 @@ fn process_sampling_result( #[cfg(test)] mod tests { + use opentelemetry::trace::SpanBuilder; + use opentelemetry::trace::SpanId; + use opentelemetry::trace::TracerProvider as _; + use opentelemetry_sdk::trace::config; + use opentelemetry_sdk::trace::Sampler; + use opentelemetry_sdk::trace::TracerProvider; + use super::*; - use opentelemetry::trace::{SpanBuilder, SpanId, TracerProvider as _}; - use opentelemetry_sdk::trace::{config, Sampler, TracerProvider}; #[test] fn assigns_default_trace_id_if_missing() { diff --git a/apollo-router/src/services/subgraph_service.rs b/apollo-router/src/services/subgraph_service.rs index 57126fb344..d5a6d80360 100644 --- a/apollo-router/src/services/subgraph_service.rs +++ b/apollo-router/src/services/subgraph_service.rs @@ -538,7 +538,7 @@ async fn call_websocket( log_event( level.0, "subgraph.request", - &attrs, + attrs, &format!("Websocket request body to subgraph {service_name:?}"), ); } @@ -688,7 +688,7 @@ async fn call_http( log_event( level.0, "subgraph.request", - &attrs, + attrs, &format!("Request to subgraph {service_name:?}"), ); } @@ -782,7 +782,7 @@ async fn call_http( log_event( level.0, "subgraph.response", - &attrs, + attrs, &format!("Raw response from subgraph {service_name:?} received"), ); } diff --git a/apollo-router/src/services/supergraph/service.rs b/apollo-router/src/services/supergraph/service.rs index fd26678f9b..67c944585f 100644 --- a/apollo-router/src/services/supergraph/service.rs +++ b/apollo-router/src/services/supergraph/service.rs @@ -353,7 +353,7 @@ async fn service_call( "http.response.body".to_string(), serde_json::to_string(resp).unwrap_or_default(), ); - log_event(level.0, "supergraph.response", &attrs, ""); + log_event(level.0, "supergraph.response", attrs.clone(), ""); })); Ok(SupergraphResponse { diff --git a/apollo-router/tests/integration/telemetry/logging.rs b/apollo-router/tests/integration/telemetry/logging.rs index e7c0b3180f..bdbc0d077b 100644 --- a/apollo-router/tests/integration/telemetry/logging.rs +++ b/apollo-router/tests/integration/telemetry/logging.rs @@ -21,6 +21,8 @@ async fn test_json() -> Result<(), BoxError> { router.execute_query(&query).await; router.assert_log_contains("span_id").await; router.graceful_shutdown().await; + // TODO add more tests for custom events + // try to use insta with logs Ok(()) } From 91402d7346fd9ecc1926950abdbdf79c1914b975 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:39:59 +0200 Subject: [PATCH 12/40] tests wip Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../src/plugins/telemetry/fmt_layer.rs | 46 +++++++++++++++++++ .../src/plugins/telemetry/formatters/text.rs | 30 +++++++++++- ...ests__text_logging_with_custom_events.snap | 5 ++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events.snap diff --git a/apollo-router/src/plugins/telemetry/fmt_layer.rs b/apollo-router/src/plugins/telemetry/fmt_layer.rs index 614b95069c..1743faa4d6 100644 --- a/apollo-router/src/plugins/telemetry/fmt_layer.rs +++ b/apollo-router/src/plugins/telemetry/fmt_layer.rs @@ -271,6 +271,9 @@ mod tests { use tracing_subscriber::layer::SubscriberExt; use super::*; + use crate::otel; + use crate::plugins::telemetry::config_new::events::log_event; + use crate::plugins::telemetry::config_new::events::EventLevel; use crate::plugins::telemetry::config_new::logging::JsonFormat; use crate::plugins::telemetry::config_new::logging::RateLimit; use crate::plugins::telemetry::config_new::logging::TextFormat; @@ -462,4 +465,47 @@ mod tests { insta::assert_display_snapshot!(buff.to_string()); } + + #[tokio::test] + async fn test_text_logging_with_custom_events() { + let buff = LogBuffer::default(); + let text_format = TextFormat { + ansi_escape_codes: false, + ..Default::default() + }; + let format = Text::new(Default::default(), text_format); + let fmt_layer = FmtLayer::new( + FilteringFormatter::new(format, filter_metric_events, &RateLimit::default()), + buff.clone(), + ) + .boxed(); + + ::tracing::subscriber::with_default( + fmt::Subscriber::new().with(otel::layer()).with(fmt_layer), + || { + let test_span = info_span!( + "test", + first = "one", + apollo_private.should_not_display = "this should be skipped" + ); + test_span.set_span_dyn_attribute("another".into(), 2.into()); + test_span.set_span_dyn_attribute("custom_dyn".into(), "test".into()); + let _enter = test_span.enter(); + let mut attributes = HashMap::new(); + attributes.insert("http.response.body.size".to_string(), "125".to_string()); + attributes.insert( + "http.response.body".to_string(), + r#"{"foo": "bar"}"#.to_string(), + ); + log_event( + EventLevel::Info, + "my_custom_event", + attributes, + "my message", + ); + }, + ); + + insta::assert_display_snapshot!(buff.to_string()); + } } diff --git a/apollo-router/src/plugins/telemetry/formatters/text.rs b/apollo-router/src/plugins/telemetry/formatters/text.rs index a8c4e4085a..5f836bad3d 100644 --- a/apollo-router/src/plugins/telemetry/formatters/text.rs +++ b/apollo-router/src/plugins/telemetry/formatters/text.rs @@ -369,7 +369,7 @@ where let otel_data = extensions.get_mut::(); if let Some(event_attributes) = otel_data.and_then(|od| od.event_attributes.take()) { for (key, value) in event_attributes { - default_visitor.log_debug(key.as_str(), &value.as_str()); + default_visitor.log_debug_attrs(key.as_str(), &value.as_str()); } } } @@ -484,6 +484,34 @@ impl<'a> DefaultVisitor<'a> { Style::new() } + fn log_debug_attrs(&mut self, field_name: &str, value: &dyn fmt::Debug) { + let style = self.dimmed(); + + self.result = write!(self.writer, "{}", style.prefix()); + if self.result.is_err() { + return; + } + + self.maybe_pad(); + self.result = match field_name { + name if name.starts_with("r#") => write!( + self.writer, + "{}{}{:?}", + self.italic().paint(&name[2..]), + self.dimmed().paint("="), + value + ), + name => write!( + self.writer, + "{}{}{:?}", + self.italic().paint(name), + self.dimmed().paint("="), + value + ), + }; + self.result = write!(self.writer, "{}", style.suffix()); + } + fn log_debug(&mut self, field_name: &str, value: &dyn fmt::Debug) { if self.result.is_err() { return; diff --git a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events.snap b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events.snap new file mode 100644 index 0000000000..a3410675a2 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events.snap @@ -0,0 +1,5 @@ +--- +source: apollo-router/src/plugins/telemetry/fmt_layer.rs +expression: buff.to_string() +--- +[timestamp] INFO test{another=2,custom_dyn=test,first=one,first=one,} http.response.body="{\"foo\": \"bar\"}" http.response.body.size="125" my message kind=my_custom_event From 81f7e87b2458fdee0bc11bdaef94fef8a630411b Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 16 Apr 2024 17:09:42 +0200 Subject: [PATCH 13/40] fix custom event detection in otel Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../src/plugins/telemetry/dynamic_attribute.rs | 12 ------------ .../src/plugins/telemetry/otel/layer.rs | 18 ++++++------------ 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs index bba0fb14e9..6df58a6d56 100644 --- a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs +++ b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs @@ -188,18 +188,6 @@ impl SpanDynAttribute for ::tracing::Span { } } -pub(crate) struct EventsAttributes { - pub(crate) events_attributes: HashMap, -} - -impl Default for EventsAttributes { - fn default() -> Self { - Self { - events_attributes: HashMap::with_capacity(0), - } - } -} - /// To add dynamic attributes for spans pub(crate) trait EventDynAttribute { /// Always use before sending the event diff --git a/apollo-router/src/plugins/telemetry/otel/layer.rs b/apollo-router/src/plugins/telemetry/otel/layer.rs index aac1d564ae..9eb1845ec5 100644 --- a/apollo-router/src/plugins/telemetry/otel/layer.rs +++ b/apollo-router/src/plugins/telemetry/otel/layer.rs @@ -140,9 +140,6 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { match field.name() { "message" => self.event_builder.name = value.to_string().into(), name => { - if name == "kind" { - self.custom_event = true; - } self.event_builder .attributes .push(KeyValue::new(name, value)); @@ -173,9 +170,6 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { fn record_i64(&mut self, field: &field::Field, value: i64) { match field.name() { "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), name => { self.event_builder .attributes @@ -190,10 +184,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { fn record_str(&mut self, field: &field::Field, value: &str) { match field.name() { "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), name => { + if name == "kind" { + self.custom_event = true; + } self.event_builder .attributes .push(KeyValue::new(name, value.to_string())); @@ -208,10 +202,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { match field.name() { "message" => self.event_builder.name = format!("{:?}", value).into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), name => { + if name == "kind" { + self.custom_event = true; + } self.event_builder .attributes .push(KeyValue::new(name, format!("{:?}", value))); From 6a82c3eff4188febf0bb9b28e47df7c9b1fa1d99 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Wed, 17 Apr 2024 09:37:14 +0200 Subject: [PATCH 14/40] add tests Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../src/plugins/telemetry/fmt_layer.rs | 51 +++++++++++++++++++ ...ests__json_logging_with_custom_events.snap | 6 +++ ...ests__text_logging_with_custom_events.snap | 1 + 3 files changed, 58 insertions(+) create mode 100644 apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__json_logging_with_custom_events.snap diff --git a/apollo-router/src/plugins/telemetry/fmt_layer.rs b/apollo-router/src/plugins/telemetry/fmt_layer.rs index 1743faa4d6..71ba1c85d2 100644 --- a/apollo-router/src/plugins/telemetry/fmt_layer.rs +++ b/apollo-router/src/plugins/telemetry/fmt_layer.rs @@ -503,9 +503,60 @@ mod tests { attributes, "my message", ); + + error!(http.method = "GET", "Hello from test"); }, ); insta::assert_display_snapshot!(buff.to_string()); } + + #[tokio::test] + async fn test_json_logging_with_custom_events() { + let buff = LogBuffer::default(); + let text_format = JsonFormat { + display_span_list: false, + display_current_span: false, + display_resource: false, + ..Default::default() + }; + let format = Json::new(Default::default(), text_format); + let fmt_layer = FmtLayer::new( + FilteringFormatter::new(format, filter_metric_events, &RateLimit::default()), + buff.clone(), + ) + .boxed(); + + ::tracing::subscriber::with_default( + fmt::Subscriber::new().with(otel::layer()).with(fmt_layer), + || { + let test_span = info_span!( + "test", + first = "one", + apollo_private.should_not_display = "this should be skipped" + ); + test_span.set_span_dyn_attribute("another".into(), 2.into()); + test_span.set_span_dyn_attribute("custom_dyn".into(), "test".into()); + let _enter = test_span.enter(); + let mut attributes = HashMap::new(); + attributes.insert("http.response.body.size".to_string(), "125".to_string()); + attributes.insert( + "http.response.body".to_string(), + r#"{"foo": "bar"}"#.to_string(), + ); + log_event( + EventLevel::Info, + "my_custom_event", + attributes, + "my message", + ); + + error!(http.method = "GET", "Hello from test"); + }, + ); + + insta::assert_display_snapshot!(buff.to_string()); + } + + // TODO add test using on_request/on_reponse/on_error } diff --git a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__json_logging_with_custom_events.snap b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__json_logging_with_custom_events.snap new file mode 100644 index 0000000000..9557356f67 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__json_logging_with_custom_events.snap @@ -0,0 +1,6 @@ +--- +source: apollo-router/src/plugins/telemetry/fmt_layer.rs +expression: buff.to_string() +--- +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","http.response.body":"{\"foo\": \"bar\"}","http.response.body.size":"125","message":"my message","kind":"my_custom_event","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"ERROR","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","message":"Hello from test","http.method":"GET","target":"apollo_router::plugins::telemetry::fmt_layer::tests"} diff --git a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events.snap b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events.snap index a3410675a2..4739acb547 100644 --- a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events.snap +++ b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events.snap @@ -3,3 +3,4 @@ source: apollo-router/src/plugins/telemetry/fmt_layer.rs expression: buff.to_string() --- [timestamp] INFO test{another=2,custom_dyn=test,first=one,first=one,} http.response.body="{\"foo\": \"bar\"}" http.response.body.size="125" my message kind=my_custom_event +[timestamp] ERROR test{another=2,custom_dyn=test,first=one,first=one,} Hello from test http.method="GET" From c83c6d9cf54ec12170e72be50f6310e15e5ab49a Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:32:33 +0200 Subject: [PATCH 15/40] improve handling of object as an otel attribute Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/plugins/telemetry/config_new/mod.rs | 7 ++++++- apollo-router/src/plugins/telemetry/dynamic_attribute.rs | 2 -- apollo-router/src/plugins/telemetry/formatters/text.rs | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/mod.rs b/apollo-router/src/plugins/telemetry/config_new/mod.rs index ea82854d71..d397fbf0ac 100644 --- a/apollo-router/src/plugins/telemetry/config_new/mod.rs +++ b/apollo-router/src/plugins/telemetry/config_new/mod.rs @@ -128,7 +128,12 @@ macro_rules! impl_to_otel_value { Some(opentelemetry::Value::Array(opentelemetry::Array::Bool( value.iter().filter_map(|v| v.as_bool()).collect(), ))) - } else if value.iter().all(|v| v.is_string()) { + } else if value.iter().all(|v| v.is_object()) { + Some(opentelemetry::Value::Array(opentelemetry::Array::String( + value.iter().map(|v| v.to_string().into()).collect(), + ))) + } + else if value.iter().all(|v| v.is_string()) { Some(opentelemetry::Value::Array(opentelemetry::Array::String( value .iter() diff --git a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs index 6df58a6d56..f9fb9d11ca 100644 --- a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs +++ b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use opentelemetry::Key; use opentelemetry::KeyValue; use opentelemetry::OrderMap; diff --git a/apollo-router/src/plugins/telemetry/formatters/text.rs b/apollo-router/src/plugins/telemetry/formatters/text.rs index 5f836bad3d..4d805a5317 100644 --- a/apollo-router/src/plugins/telemetry/formatters/text.rs +++ b/apollo-router/src/plugins/telemetry/formatters/text.rs @@ -6,15 +6,12 @@ use std::fmt; use nu_ansi_term::Color; use nu_ansi_term::Style; use opentelemetry::sdk::Resource; -use opentelemetry::Key; -use opentelemetry::OrderMap; use serde_json::Value; use tracing_core::Event; use tracing_core::Field; use tracing_core::Level; use tracing_core::Subscriber; use tracing_subscriber::field; -use tracing_subscriber::field::Visit; use tracing_subscriber::field::VisitFmt; use tracing_subscriber::field::VisitOutput; use tracing_subscriber::fmt::format::Writer; From b513b51669c44e0338b3975865141767d1a66206 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:47:10 +0200 Subject: [PATCH 16/40] fix snapshot Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../src/plugins/telemetry/fmt_layer.rs | 51 +++++++++++++++++++ ...layer__tests__text_logging_attributes.snap | 3 +- ..._text_logging_attributes_nested_spans.snap | 5 +- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/fmt_layer.rs b/apollo-router/src/plugins/telemetry/fmt_layer.rs index 71ba1c85d2..f5a31593df 100644 --- a/apollo-router/src/plugins/telemetry/fmt_layer.rs +++ b/apollo-router/src/plugins/telemetry/fmt_layer.rs @@ -272,6 +272,7 @@ mod tests { use super::*; use crate::otel; + use crate::plugins::telemetry::config_new::events; use crate::plugins::telemetry::config_new::events::log_event; use crate::plugins::telemetry::config_new::events::EventLevel; use crate::plugins::telemetry::config_new::logging::JsonFormat; @@ -558,5 +559,55 @@ mod tests { insta::assert_display_snapshot!(buff.to_string()); } + #[tokio::test] + async fn test_json_logging_with_custom_events_with_instrumented() { + let buff = LogBuffer::default(); + let text_format = JsonFormat { + display_span_list: false, + display_current_span: false, + display_resource: false, + ..Default::default() + }; + let format = Json::new(Default::default(), text_format); + let fmt_layer = FmtLayer::new( + FilteringFormatter::new(format, filter_metric_events, &RateLimit::default()), + buff.clone(), + ) + .boxed(); + let event_config: events::Events = + serde_yaml::from_str(include_str!("testdata/custom_events.router.yaml")).unwrap(); + + ::tracing::subscriber::with_default( + fmt::Subscriber::new().with(otel::layer()).with(fmt_layer), + move || { + let test_span = info_span!( + "test", + first = "one", + apollo_private.should_not_display = "this should be skipped" + ); + test_span.set_span_dyn_attribute("another".into(), 2.into()); + test_span.set_span_dyn_attribute("custom_dyn".into(), "test".into()); + let _enter = test_span.enter(); + + let mut attributes = HashMap::new(); + attributes.insert("http.response.body.size".to_string(), "125".to_string()); + attributes.insert( + "http.response.body".to_string(), + r#"{"foo": "bar"}"#.to_string(), + ); + log_event( + EventLevel::Info, + "my_custom_event", + attributes, + "my message", + ); + + error!(http.method = "GET", "Hello from test"); + }, + ); + + insta::assert_display_snapshot!(buff.to_string()); + } + // TODO add test using on_request/on_reponse/on_error } diff --git a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_attributes.snap b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_attributes.snap index 171af540d6..d175c52858 100644 --- a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_attributes.snap +++ b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_attributes.snap @@ -2,5 +2,4 @@ source: apollo-router/src/plugins/telemetry/fmt_layer.rs expression: buff --- -[timestamp] INFO test{another=2,custom_dyn=test,first=one,}  Hello from test event_attr="foo" - +[timestamp] INFO test{another=2,custom_dyn=test,first=one,}  Hello from test event_attr="foo" diff --git a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_attributes_nested_spans.snap b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_attributes_nested_spans.snap index 83ee3a15d0..2827f39a47 100644 --- a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_attributes_nested_spans.snap +++ b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_attributes_nested_spans.snap @@ -2,6 +2,5 @@ source: apollo-router/src/plugins/telemetry/fmt_layer.rs expression: buff.to_string() --- -[timestamp] ERROR nested_test{graphql.operation.kind=Subscription,inner=-42,two=two,} test{another=2,custom_dyn=test,first=one,}  Hello from nested test http.method="GET" -[timestamp] INFO test{another=2,custom_dyn=test,first=one,}  Hello from test event_attr="foo" - +[timestamp] ERROR nested_test{graphql.operation.kind=Subscription,inner=-42,two=two,} test{another=2,custom_dyn=test,first=one,}  Hello from nested test http.method="GET" +[timestamp] INFO test{another=2,custom_dyn=test,first=one,}  Hello from test event_attr="foo" From 7c41055de3872dd98b082bde9ff815b55eba45e4 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:47:51 +0200 Subject: [PATCH 17/40] add more tests for events Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../src/plugins/telemetry/config_new/mod.rs | 7 +- .../plugins/telemetry/dynamic_attribute.rs | 16 - .../src/plugins/telemetry/fmt_layer.rs | 303 +++++++++++++++++- .../src/plugins/telemetry/formatters/text.rs | 1 + apollo-router/src/plugins/telemetry/mod.rs | 1 + .../src/plugins/telemetry/otel/layer.rs | 43 +-- ..._with_custom_events_with_instrumented.snap | 17 + ..._with_custom_events_with_instrumented.snap | 17 + .../testdata/custom_events.router.yaml | 5 +- .../tests/integration/telemetry/logging.rs | 3 +- 10 files changed, 350 insertions(+), 63 deletions(-) create mode 100644 apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__json_logging_with_custom_events_with_instrumented.snap create mode 100644 apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events_with_instrumented.snap diff --git a/apollo-router/src/plugins/telemetry/config_new/mod.rs b/apollo-router/src/plugins/telemetry/config_new/mod.rs index d397fbf0ac..d2c79178a0 100644 --- a/apollo-router/src/plugins/telemetry/config_new/mod.rs +++ b/apollo-router/src/plugins/telemetry/config_new/mod.rs @@ -132,8 +132,7 @@ macro_rules! impl_to_otel_value { Some(opentelemetry::Value::Array(opentelemetry::Array::String( value.iter().map(|v| v.to_string().into()).collect(), ))) - } - else if value.iter().all(|v| v.is_string()) { + } else if value.iter().all(|v| v.is_string()) { Some(opentelemetry::Value::Array(opentelemetry::Array::String( value .iter() @@ -142,10 +141,10 @@ macro_rules! impl_to_otel_value { .collect(), ))) } else { - None + Some(serde_json::to_string(value).ok()?.into()) } } - _ => None, + value => Some(serde_json::to_string(value).ok()?.into()) } } } diff --git a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs index f9fb9d11ca..2d1a99ce75 100644 --- a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs +++ b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs @@ -1,8 +1,6 @@ use opentelemetry::Key; use opentelemetry::KeyValue; use opentelemetry::OrderMap; -use tracing::field::Visit; -use tracing::Event; use tracing_subscriber::layer::Context; use tracing_subscriber::registry::LookupSpan; use tracing_subscriber::Layer; @@ -50,20 +48,6 @@ where extensions.insert(LogAttributes::default()); } } - - fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { - // dbg!(event); - // let span = ctx.event_span(event); - // if let Some(span) = span { - // let mut extensions = span.extensions_mut(); - // if let Some(events) = extensions - // .get_mut::() - // .and_then(|ext| ext.builder.events.as_mut()) - // { - // dbg!(&events.last()); - // } - // } - } } impl DynSpanAttributeLayer { diff --git a/apollo-router/src/plugins/telemetry/fmt_layer.rs b/apollo-router/src/plugins/telemetry/fmt_layer.rs index f5a31593df..cbdf1bc915 100644 --- a/apollo-router/src/plugins/telemetry/fmt_layer.rs +++ b/apollo-router/src/plugins/telemetry/fmt_layer.rs @@ -264,6 +264,8 @@ mod tests { use std::sync::Mutex; use std::sync::MutexGuard; + use http::header::CONTENT_LENGTH; + use http::HeaderValue; use tracing::error; use tracing::info; use tracing::info_span; @@ -271,14 +273,96 @@ mod tests { use tracing_subscriber::layer::SubscriberExt; use super::*; + use crate::graphql; use crate::otel; use crate::plugins::telemetry::config_new::events; use crate::plugins::telemetry::config_new::events::log_event; use crate::plugins::telemetry::config_new::events::EventLevel; + use crate::plugins::telemetry::config_new::instruments::Instrumented; use crate::plugins::telemetry::config_new::logging::JsonFormat; use crate::plugins::telemetry::config_new::logging::RateLimit; use crate::plugins::telemetry::config_new::logging::TextFormat; use crate::plugins::telemetry::dynamic_attribute::SpanDynAttribute; + use crate::services::router; + use crate::services::subgraph; + use crate::services::supergraph; + + const EVENT_CONFIGURATION: &str = r#" +router: + # Standard events + request: info + response: info + error: info + + # Custom events + my.request_event: + message: "my event message" + level: info + on: request + attributes: + http.request.body.size: true + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - request_header: "x-log-request" + my.response_event: + message: "my response event message" + level: info + on: response + attributes: + http.response.body.size: true + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - response_header: "x-log-request" +supergraph: + # Standard events + request: info + response: warn + error: info + + # Custom events + my.request.event: + message: "my event message" + level: info + on: request + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - request_header: "x-log-request" + my.response_event: + message: "my response event message" + level: warn + on: response + condition: + eq: + - "log" + - response_header: "x-log-request" +subgraph: + # Standard events + request: info + response: warn + error: error + + # Custom events + my.subgraph.request.event: + message: "my event message" + level: info + on: request + my.subgraph.response.event: + message: "my response event message" + level: error + on: response + attributes: + subgraph.name: true + response_status: + subgraph_response_status: code + "my.custom.attribute": + subgraph_response_data: "$.*" + default: "missing""#; #[derive(Default, Clone)] struct LogBuffer(Arc>>); @@ -574,8 +658,8 @@ mod tests { buff.clone(), ) .boxed(); - let event_config: events::Events = - serde_yaml::from_str(include_str!("testdata/custom_events.router.yaml")).unwrap(); + + let event_config: events::Events = serde_yaml::from_str(EVENT_CONFIGURATION).unwrap(); ::tracing::subscriber::with_default( fmt::Subscriber::new().with(otel::layer()).with(fmt_layer), @@ -603,6 +687,221 @@ mod tests { ); error!(http.method = "GET", "Hello from test"); + + let router_events = event_config.new_router_events(); + let router_req = router::Request::fake_builder() + .header(CONTENT_LENGTH, "0") + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .build() + .unwrap(); + router_events.on_request(&router_req); + + let router_resp = router::Response::fake_builder() + .header("custom-header", "val1") + .header(CONTENT_LENGTH, "25") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json_bytes::json!({"data": "res"})) + .build() + .expect("expecting valid response"); + router_events.on_response(&router_resp); + + let supergraph_events = event_config.new_supergraph_events(); + let supergraph_req = supergraph::Request::fake_builder() + .query("query { foo }") + .header("x-log-request", HeaderValue::from_static("log")) + .build() + .unwrap(); + supergraph_events.on_request(&supergraph_req); + + let supergraph_resp = supergraph::Response::fake_builder() + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json::json!({"data": "res"}).to_string()) + .build() + .expect("expecting valid response"); + supergraph_events.on_response(&supergraph_resp); + + let subgraph_events = event_config.new_subgraph_events(); + let mut subgraph_req = http::Request::new( + graphql::Request::fake_builder() + .query("query { foo }") + .build(), + ); + subgraph_req + .headers_mut() + .insert("x-log-request", HeaderValue::from_static("log")); + + let subgraph_req = subgraph::Request::fake_builder() + .subgraph_name("subgraph") + .subgraph_request(subgraph_req) + .build(); + subgraph_events.on_request(&subgraph_req); + + let subgraph_resp = subgraph::Response::fake2_builder() + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json::json!({"products": [{"id": 1234, "name": "first_name"}, {"id": 567, "name": "second_name"}]})) + .build() + .expect("expecting valid response"); + subgraph_events.on_response(&subgraph_resp); + + let subgraph_events = event_config.new_subgraph_events(); + let mut subgraph_req = http::Request::new( + graphql::Request::fake_builder() + .query("query { foo }") + .build(), + ); + subgraph_req + .headers_mut() + .insert("x-log-request", HeaderValue::from_static("log")); + + let subgraph_req = subgraph::Request::fake_builder() + .subgraph_name("subgraph_bis") + .subgraph_request(subgraph_req) + .build(); + subgraph_events.on_request(&subgraph_req); + + let subgraph_resp = subgraph::Response::fake2_builder() + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json::json!({"products": [{"id": 1234, "name": "first_name"}, {"id": 567, "name": "second_name"}], "other": {"foo": "bar"}})) + .build() + .expect("expecting valid response"); + subgraph_events.on_response(&subgraph_resp); + }, + ); + + insta::assert_display_snapshot!(buff.to_string()); + } + + #[tokio::test] + async fn test_text_logging_with_custom_events_with_instrumented() { + let buff = LogBuffer::default(); + let text_format = TextFormat { + display_span_list: true, + display_current_span: false, + display_resource: false, + ansi_escape_codes: false, + ..Default::default() + }; + let format = Text::new(Default::default(), text_format); + let fmt_layer = FmtLayer::new( + FilteringFormatter::new(format, filter_metric_events, &RateLimit::default()), + buff.clone(), + ) + .boxed(); + + let event_config: events::Events = serde_yaml::from_str(EVENT_CONFIGURATION).unwrap(); + + ::tracing::subscriber::with_default( + fmt::Subscriber::new().with(otel::layer()).with(fmt_layer), + move || { + let test_span = info_span!( + "test", + first = "one", + apollo_private.should_not_display = "this should be skipped" + ); + test_span.set_span_dyn_attribute("another".into(), 2.into()); + test_span.set_span_dyn_attribute("custom_dyn".into(), "test".into()); + let _enter = test_span.enter(); + + let mut attributes = HashMap::new(); + attributes.insert("http.response.body.size".to_string(), "125".to_string()); + attributes.insert( + "http.response.body".to_string(), + r#"{"foo": "bar"}"#.to_string(), + ); + log_event( + EventLevel::Info, + "my_custom_event", + attributes, + "my message", + ); + + error!(http.method = "GET", "Hello from test"); + + let router_events = event_config.new_router_events(); + let router_req = router::Request::fake_builder() + .header(CONTENT_LENGTH, "0") + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .build() + .unwrap(); + router_events.on_request(&router_req); + + let router_resp = router::Response::fake_builder() + .header("custom-header", "val1") + .header(CONTENT_LENGTH, "25") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json_bytes::json!({"data": "res"})) + .build() + .expect("expecting valid response"); + router_events.on_response(&router_resp); + + let supergraph_events = event_config.new_supergraph_events(); + let supergraph_req = supergraph::Request::fake_builder() + .query("query { foo }") + .header("x-log-request", HeaderValue::from_static("log")) + .build() + .unwrap(); + supergraph_events.on_request(&supergraph_req); + + let supergraph_resp = supergraph::Response::fake_builder() + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json::json!({"data": "res"}).to_string()) + .build() + .expect("expecting valid response"); + supergraph_events.on_response(&supergraph_resp); + + let subgraph_events = event_config.new_subgraph_events(); + let mut subgraph_req = http::Request::new( + graphql::Request::fake_builder() + .query("query { foo }") + .build(), + ); + subgraph_req + .headers_mut() + .insert("x-log-request", HeaderValue::from_static("log")); + + let subgraph_req = subgraph::Request::fake_builder() + .subgraph_name("subgraph") + .subgraph_request(subgraph_req) + .build(); + subgraph_events.on_request(&subgraph_req); + + let subgraph_resp = subgraph::Response::fake2_builder() + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json::json!({"products": [{"id": 1234, "name": "first_name"}, {"id": 567, "name": "second_name"}]})) + .build() + .expect("expecting valid response"); + subgraph_events.on_response(&subgraph_resp); + + let subgraph_events = event_config.new_subgraph_events(); + let mut subgraph_req = http::Request::new( + graphql::Request::fake_builder() + .query("query { foo }") + .build(), + ); + subgraph_req + .headers_mut() + .insert("x-log-request", HeaderValue::from_static("log")); + + let subgraph_req = subgraph::Request::fake_builder() + .subgraph_name("subgraph_bis") + .subgraph_request(subgraph_req) + .build(); + subgraph_events.on_request(&subgraph_req); + + let subgraph_resp = subgraph::Response::fake2_builder() + .header("custom-header", "val1") + .header("x-log-request", HeaderValue::from_static("log")) + .data(serde_json::json!({"products": [{"id": 1234, "name": "first_name"}, {"id": 567, "name": "second_name"}], "other": {"foo": "bar"}})) + .build() + .expect("expecting valid response"); + subgraph_events.on_response(&subgraph_resp); }, ); diff --git a/apollo-router/src/plugins/telemetry/formatters/text.rs b/apollo-router/src/plugins/telemetry/formatters/text.rs index 4d805a5317..4b2a8e608a 100644 --- a/apollo-router/src/plugins/telemetry/formatters/text.rs +++ b/apollo-router/src/plugins/telemetry/formatters/text.rs @@ -457,6 +457,7 @@ impl<'a> DefaultVisitor<'a> { } } + #[allow(dead_code)] fn bold(&self) -> Style { if self.is_ansi { return Style::new().bold(); diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index e1bb7cd03e..4a8dabc01b 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -136,6 +136,7 @@ mod fmt_layer; pub(crate) mod formatters; mod logging; pub(crate) mod metrics; +/// Opentelemetry utils pub mod otel; mod otlp; pub(crate) mod reload; diff --git a/apollo-router/src/plugins/telemetry/otel/layer.rs b/apollo-router/src/plugins/telemetry/otel/layer.rs index 9eb1845ec5..a4fac43e5c 100644 --- a/apollo-router/src/plugins/telemetry/otel/layer.rs +++ b/apollo-router/src/plugins/telemetry/otel/layer.rs @@ -153,9 +153,6 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { fn record_f64(&mut self, field: &field::Field, value: f64) { match field.name() { "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), name => { self.event_builder .attributes @@ -498,6 +495,7 @@ where /// By default, these attributes are not recorded. /// /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/ + #[allow(dead_code)] pub(crate) fn with_exception_fields(self, exception_fields: bool) -> Self { Self { exception_config: ExceptionFieldConfig { @@ -521,6 +519,7 @@ where /// By default, these attributes are not propagated to the span. /// /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/ + #[allow(dead_code)] pub(crate) fn with_exception_field_propagation( self, exception_field_propagation: bool, @@ -534,22 +533,10 @@ where } } - /// Sets whether or not span and event metadata should include OpenTelemetry - /// attributes with location information, such as the file, module and line number. - /// - /// These attributes follow the [OpenTelemetry semantic conventions for - /// source locations][conv]. - /// - /// By default, locations are enabled. - /// - /// [conv]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes/ - pub(crate) fn with_location(self, location: bool) -> Self { - Self { location, ..self } - } - /// Sets whether or not spans metadata should include the _busy time_ /// (total time for which it was entered), and _idle time_ (total time /// the span existed but was not entered). + #[allow(dead_code)] pub(crate) fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self { Self { tracked_inactivity, @@ -564,6 +551,7 @@ where /// By default, thread attributes are enabled. /// /// [conv]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#general-thread-attributes/ + #[allow(dead_code)] pub(crate) fn with_threads(self, threads: bool) -> Self { Self { with_threads: threads, @@ -1253,8 +1241,7 @@ mod tests { #[test] fn includes_span_location() { let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry() - .with(layer().with_tracer(tracer.clone()).with_location(true)); + let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); tracing::subscriber::with_default(subscriber, || { tracing::debug_span!("request"); @@ -1270,26 +1257,6 @@ mod tests { assert!(keys.contains(&"code.lineno")); } - #[test] - fn excludes_span_location() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry() - .with(layer().with_tracer(tracer.clone()).with_location(false)); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); - let keys = attributes - .iter() - .map(|(key, _)| key.as_str()) - .collect::>(); - assert!(!keys.contains(&"code.filepath")); - assert!(!keys.contains(&"code.namespace")); - assert!(!keys.contains(&"code.lineno")); - } - #[test] fn includes_thread() { let thread = thread::current(); diff --git a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__json_logging_with_custom_events_with_instrumented.snap b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__json_logging_with_custom_events_with_instrumented.snap new file mode 100644 index 0000000000..e45d69951a --- /dev/null +++ b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__json_logging_with_custom_events_with_instrumented.snap @@ -0,0 +1,17 @@ +--- +source: apollo-router/src/plugins/telemetry/fmt_layer.rs +expression: buff.to_string() +--- +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","http.response.body":"{\"foo\": \"bar\"}","http.response.body.size":"125","message":"my message","kind":"my_custom_event","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"ERROR","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","message":"Hello from test","http.method":"GET","target":"apollo_router::plugins::telemetry::fmt_layer::tests"} +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","http.request.body":"Body(Empty)","http.request.headers":"{\"content-length\": \"0\", \"custom-header\": \"val1\", \"x-log-request\": \"log\"}","http.request.method":"GET","http.request.uri":"http://example.com/","http.request.version":"HTTP/1.1","message":"","kind":"router.request","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","http.request.body.size":"0","message":"my event message","kind":"my.request_event","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","http.response.body":"Body(Full(b\"{\\\"data\\\":{\\\"data\\\":\\\"res\\\"}}\"))","http.response.headers":"{\"content-length\": \"25\", \"custom-header\": \"val1\", \"x-log-request\": \"log\"}","http.response.status":"200 OK","http.response.version":"HTTP/1.1","message":"","kind":"router.response","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","http.response.body.size":"25","message":"my response event message","kind":"my.response_event","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","http.request.body":"{\"query\":\"query { foo }\"}","http.request.headers":"{\"content-type\": \"application/json\", \"x-log-request\": \"log\"}","http.request.method":"POST","http.request.uri":"http://default/","http.request.version":"HTTP/1.1","message":"","kind":"supergraph.request","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","message":"my event message","kind":"my.request.event","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"WARN","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","message":"my response event message","kind":"my.response_event","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","message":"my event message","kind":"my.subgraph.request.event","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"ERROR","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","my.custom.attribute":"[\"{\"id\":1234,\"name\":\"first_name\"}\",\"{\"id\":567,\"name\":\"second_name\"}\"]","response_status":"200","subgraph.name":"subgraph","message":"my response event message","kind":"my.subgraph.response.event","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"INFO","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","message":"my event message","kind":"my.subgraph.request.event","target":"apollo_router::plugins::telemetry::config_new::events"} +{"timestamp":"[timestamp]","level":"ERROR","trace_id":"00000000000000000000000000000000","span_id":"0000000000000000","my.custom.attribute":"[[{\"id\":1234,\"name\":\"first_name\"},{\"id\":567,\"name\":\"second_name\"}],{\"foo\":\"bar\"}]","response_status":"200","subgraph.name":"subgraph_bis","message":"my response event message","kind":"my.subgraph.response.event","target":"apollo_router::plugins::telemetry::config_new::events"} diff --git a/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events_with_instrumented.snap b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events_with_instrumented.snap new file mode 100644 index 0000000000..fe8c933416 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/snapshots/apollo_router__plugins__telemetry__fmt_layer__tests__text_logging_with_custom_events_with_instrumented.snap @@ -0,0 +1,17 @@ +--- +source: apollo-router/src/plugins/telemetry/fmt_layer.rs +expression: buff.to_string() +--- +[timestamp] INFO http.response.body="{\"foo\": \"bar\"}" http.response.body.size="125" my message kind=my_custom_event +[timestamp] ERROR Hello from test http.method="GET" +[timestamp] INFO http.request.body="Body(Empty)" http.request.headers="{\"content-length\": \"0\", \"custom-header\": \"val1\", \"x-log-request\": \"log\"}" http.request.method="GET" http.request.uri="http://example.com/" http.request.version="HTTP/1.1" kind=router.request +[timestamp] INFO http.request.body.size="0" my event message kind=my.request_event +[timestamp] INFO http.response.body="Body(Full(b\"{\\\"data\\\":{\\\"data\\\":\\\"res\\\"}}\"))" http.response.headers="{\"content-length\": \"25\", \"custom-header\": \"val1\", \"x-log-request\": \"log\"}" http.response.status="200 OK" http.response.version="HTTP/1.1" kind=router.response +[timestamp] INFO http.response.body.size="25" my response event message kind=my.response_event +[timestamp] INFO http.request.body="{\"query\":\"query { foo }\"}" http.request.headers="{\"content-type\": \"application/json\", \"x-log-request\": \"log\"}" http.request.method="POST" http.request.uri="http://default/" http.request.version="HTTP/1.1" kind=supergraph.request +[timestamp] INFO my event message kind=my.request.event +[timestamp] WARN my response event message kind=my.response_event +[timestamp] INFO my event message kind=my.subgraph.request.event +[timestamp] ERROR my.custom.attribute="[\"{\"id\":1234,\"name\":\"first_name\"}\",\"{\"id\":567,\"name\":\"second_name\"}\"]" response_status="200" subgraph.name="subgraph" my response event message kind=my.subgraph.response.event +[timestamp] INFO my event message kind=my.subgraph.request.event +[timestamp] ERROR my.custom.attribute="[[{\"id\":1234,\"name\":\"first_name\"},{\"id\":567,\"name\":\"second_name\"}],{\"foo\":\"bar\"}]" response_status="200" subgraph.name="subgraph_bis" my response event message kind=my.subgraph.response.event diff --git a/apollo-router/src/plugins/telemetry/testdata/custom_events.router.yaml b/apollo-router/src/plugins/telemetry/testdata/custom_events.router.yaml index c355136f65..a8c0efa3fb 100644 --- a/apollo-router/src/plugins/telemetry/testdata/custom_events.router.yaml +++ b/apollo-router/src/plugins/telemetry/testdata/custom_events.router.yaml @@ -75,4 +75,7 @@ telemetry: attributes: subgraph.name: true response_status: - subgraph_response_status: code \ No newline at end of file + subgraph_response_status: code + "my.custom.attribute": + subgraph_response_data: "$.*" + default: "missing" \ No newline at end of file diff --git a/apollo-router/tests/integration/telemetry/logging.rs b/apollo-router/tests/integration/telemetry/logging.rs index bdbc0d077b..a3ac6d243e 100644 --- a/apollo-router/tests/integration/telemetry/logging.rs +++ b/apollo-router/tests/integration/telemetry/logging.rs @@ -21,8 +21,7 @@ async fn test_json() -> Result<(), BoxError> { router.execute_query(&query).await; router.assert_log_contains("span_id").await; router.graceful_shutdown().await; - // TODO add more tests for custom events - // try to use insta with logs + Ok(()) } From 6d0c2326bb756a6da28a2177f1b220cd53093c80 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:54:19 +0200 Subject: [PATCH 18/40] fix otel value test Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/plugins/telemetry/config_new/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/mod.rs b/apollo-router/src/plugins/telemetry/config_new/mod.rs index d2c79178a0..fede9cb797 100644 --- a/apollo-router/src/plugins/telemetry/config_new/mod.rs +++ b/apollo-router/src/plugins/telemetry/config_new/mod.rs @@ -267,7 +267,13 @@ mod test { ); // Arrays must be uniform - assert!(json!(["1", 1]).maybe_to_otel_value().is_none()); - assert!(json!([1.0, 1]).maybe_to_otel_value().is_none()); + assert_eq!( + json!(["1", 1]).maybe_to_otel_value(), + Some(r#"["1",1]"#.to_string().into()) + ); + assert_eq!( + json!([1.0, 1]).maybe_to_otel_value(), + Some(r#"[1.0,1]"#.to_string().into()) + ); } } From 080ef4c5b401474bc13665090e30cf59f82d440e Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 19 Apr 2024 09:59:02 +0200 Subject: [PATCH 19/40] fixes Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/lib.rs | 1 - apollo-router/src/plugins/telemetry/config_new/mod.rs | 3 ++- apollo-router/src/plugins/telemetry/fmt_layer.rs | 2 +- apollo-router/src/plugins/telemetry/mod.rs | 2 +- apollo-router/src/plugins/telemetry/otel/mod.rs | 2 +- apollo-router/src/plugins/telemetry/otel/span_ext.rs | 2 +- apollo-router/tests/common.rs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apollo-router/src/lib.rs b/apollo-router/src/lib.rs index e25f7e2144..4dc56b4ea9 100644 --- a/apollo-router/src/lib.rs +++ b/apollo-router/src/lib.rs @@ -90,7 +90,6 @@ pub use crate::context::Context; pub use crate::executable::main; pub use crate::executable::Executable; pub use crate::notification::Notify; -pub use crate::plugins::telemetry::otel; pub use crate::router::ApolloRouterError; pub use crate::router::ConfigurationSource; pub use crate::router::LicenseSource; diff --git a/apollo-router/src/plugins/telemetry/config_new/mod.rs b/apollo-router/src/plugins/telemetry/config_new/mod.rs index fede9cb797..b146dcda59 100644 --- a/apollo-router/src/plugins/telemetry/config_new/mod.rs +++ b/apollo-router/src/plugins/telemetry/config_new/mod.rs @@ -144,7 +144,8 @@ macro_rules! impl_to_otel_value { Some(serde_json::to_string(value).ok()?.into()) } } - value => Some(serde_json::to_string(value).ok()?.into()) + $type::Object(value) => Some(serde_json::to_string(value).ok()?.into()), + _ => None } } } diff --git a/apollo-router/src/plugins/telemetry/fmt_layer.rs b/apollo-router/src/plugins/telemetry/fmt_layer.rs index cbdf1bc915..450f831b0b 100644 --- a/apollo-router/src/plugins/telemetry/fmt_layer.rs +++ b/apollo-router/src/plugins/telemetry/fmt_layer.rs @@ -274,7 +274,6 @@ mod tests { use super::*; use crate::graphql; - use crate::otel; use crate::plugins::telemetry::config_new::events; use crate::plugins::telemetry::config_new::events::log_event; use crate::plugins::telemetry::config_new::events::EventLevel; @@ -283,6 +282,7 @@ mod tests { use crate::plugins::telemetry::config_new::logging::RateLimit; use crate::plugins::telemetry::config_new::logging::TextFormat; use crate::plugins::telemetry::dynamic_attribute::SpanDynAttribute; + use crate::plugins::telemetry::otel; use crate::services::router; use crate::services::subgraph; use crate::services::supergraph; diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index 4a8dabc01b..bdf5a081b8 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -137,7 +137,7 @@ pub(crate) mod formatters; mod logging; pub(crate) mod metrics; /// Opentelemetry utils -pub mod otel; +pub(crate) mod otel; mod otlp; pub(crate) mod reload; mod resource; diff --git a/apollo-router/src/plugins/telemetry/otel/mod.rs b/apollo-router/src/plugins/telemetry/otel/mod.rs index eb11766a9c..dc6f3d1282 100644 --- a/apollo-router/src/plugins/telemetry/otel/mod.rs +++ b/apollo-router/src/plugins/telemetry/otel/mod.rs @@ -8,7 +8,7 @@ pub(crate) mod tracer; pub(crate) use layer::{layer, OpenTelemetryLayer}; use opentelemetry::{Key, OrderMap, Value}; -pub use span_ext::OpenTelemetrySpanExt; +pub(crate) use span_ext::OpenTelemetrySpanExt; pub(crate) use tracer::PreSampledTracer; /// Per-span OpenTelemetry data tracked by this crate. diff --git a/apollo-router/src/plugins/telemetry/otel/span_ext.rs b/apollo-router/src/plugins/telemetry/otel/span_ext.rs index cfbb0d03ec..4f4648ff15 100644 --- a/apollo-router/src/plugins/telemetry/otel/span_ext.rs +++ b/apollo-router/src/plugins/telemetry/otel/span_ext.rs @@ -7,7 +7,7 @@ use opentelemetry::{trace::SpanContext, Context, Key, KeyValue, Value}; /// [`Span`]: tracing::Span /// [OpenTelemetry]: https://opentelemetry.io /// [`Context`]: opentelemetry::Context -pub trait OpenTelemetrySpanExt { +pub(crate) trait OpenTelemetrySpanExt { /// Associates `self` with a given OpenTelemetry trace, using the provided /// parent [`Context`]. /// diff --git a/apollo-router/tests/common.rs b/apollo-router/tests/common.rs index bd76cde912..526b5f8cfd 100644 --- a/apollo-router/tests/common.rs +++ b/apollo-router/tests/common.rs @@ -6,7 +6,6 @@ use std::path::PathBuf; use std::process::Stdio; use std::time::Duration; -use apollo_router::otel::OpenTelemetrySpanExt; use buildstructor::buildstructor; use http::header::ACCEPT; use http::header::CONTENT_TYPE; @@ -50,6 +49,7 @@ use tracing_core::Dispatch; use tracing_core::LevelFilter; use tracing_futures::Instrument; use tracing_futures::WithSubscriber; +use tracing_opentelemetry::OpenTelemetrySpanExt; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::EnvFilter; use tracing_subscriber::Layer; From c9ce9bdde9328ac9bc4d848d4855b857672cb4a1 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:00:18 +0200 Subject: [PATCH 20/40] fix lint Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/plugins/telemetry/otel/layer.rs | 2 +- apollo-router/src/plugins/telemetry/otel/mod.rs | 8 +++++--- apollo-router/src/plugins/telemetry/otel/span_ext.rs | 8 ++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/otel/layer.rs b/apollo-router/src/plugins/telemetry/otel/layer.rs index a4fac43e5c..725a25d22b 100644 --- a/apollo-router/src/plugins/telemetry/otel/layer.rs +++ b/apollo-router/src/plugins/telemetry/otel/layer.rs @@ -16,10 +16,10 @@ use opentelemetry::KeyValue; use opentelemetry::StringValue; use opentelemetry::Value; use tracing_core::field; +use tracing_core::span; use tracing_core::span::Attributes; use tracing_core::span::Id; use tracing_core::span::Record; -use tracing_core::span::{self}; use tracing_core::Event; use tracing_core::Subscriber; use tracing_subscriber::layer::Context; diff --git a/apollo-router/src/plugins/telemetry/otel/mod.rs b/apollo-router/src/plugins/telemetry/otel/mod.rs index dc6f3d1282..85ec219012 100644 --- a/apollo-router/src/plugins/telemetry/otel/mod.rs +++ b/apollo-router/src/plugins/telemetry/otel/mod.rs @@ -5,9 +5,11 @@ pub(crate) mod span_ext; /// Protocols for OpenTelemetry Tracers that are compatible with Tracing pub(crate) mod tracer; -pub(crate) use layer::{layer, OpenTelemetryLayer}; - -use opentelemetry::{Key, OrderMap, Value}; +pub(crate) use layer::layer; +pub(crate) use layer::OpenTelemetryLayer; +use opentelemetry::Key; +use opentelemetry::OrderMap; +use opentelemetry::Value; pub(crate) use span_ext::OpenTelemetrySpanExt; pub(crate) use tracer::PreSampledTracer; diff --git a/apollo-router/src/plugins/telemetry/otel/span_ext.rs b/apollo-router/src/plugins/telemetry/otel/span_ext.rs index 4f4648ff15..4474a8232c 100644 --- a/apollo-router/src/plugins/telemetry/otel/span_ext.rs +++ b/apollo-router/src/plugins/telemetry/otel/span_ext.rs @@ -1,6 +1,10 @@ -use super::layer::WithContext; -use opentelemetry::{trace::SpanContext, Context, Key, KeyValue, Value}; +use opentelemetry::trace::SpanContext; +use opentelemetry::Context; +use opentelemetry::Key; +use opentelemetry::KeyValue; +use opentelemetry::Value; +use super::layer::WithContext; /// Utility functions to allow tracing [`Span`]s to accept and return /// [OpenTelemetry] [`Context`]s. /// From 514092f1906b32977b87d7323bf59bc707742254 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:49:26 +0200 Subject: [PATCH 21/40] add conditions on custom attributes for spans + a new selector for graphql error Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- Cargo.lock | 1 + apollo-router/Cargo.toml | 2 +- ...nfiguration__tests__schema_generation.snap | 6021 ++++++++++++++--- apollo-router/src/context/mod.rs | 2 + .../telemetry/config_new/attributes.rs | 61 + .../plugins/telemetry/config_new/selectors.rs | 26 +- .../src/plugins/telemetry/config_new/spans.rs | 272 +- apollo-router/src/plugins/telemetry/mod.rs | 6 + 8 files changed, 5363 insertions(+), 1028 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d66079580..d20a0f4d2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3960,6 +3960,7 @@ checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", + "serde", ] [[package]] diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index 22ce66ab53..105b7e0b43 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -240,7 +240,7 @@ tokio-tungstenite = { version = "0.20.1", features = [ tokio-rustls = "0.24.1" http-serde = "1.1.3" hmac = "0.12.1" -parking_lot = "0.12.1" +parking_lot = { version = "0.12.1", features = ["serde"] } memchr = "2.7.1" brotli = "3.4.0" zstd = "0.13.0" diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index 1b52d1d57c..1bf679c3ed 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -7341,6 +7341,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -7805,6 +7818,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -8210,6 +8236,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -13382,6 +13421,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -13936,6 +13988,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -14490,6 +14555,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -15039,6 +15117,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -15503,6 +15594,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -15908,6 +16012,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -16399,6 +16516,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -24962,6 +25092,7 @@ expression: "&schema" } }, "additionalProperties": { + "type": "object", "anyOf": [ { "description": "A header from the request", @@ -25348,421 +25479,1846 @@ expression: "&schema" }, { "type": "string" - } - ] - } - } - }, - "additionalProperties": false - }, - "subgraph": { - "description": "Attributes to include on the subgraph span. Subgraph spans contain information about the subgraph request and response and therefore contain subgraph specific attributes.", - "type": "object", - "properties": { - "attributes": { - "description": "Custom attributes that are attached to the subgraph span.", - "type": "object", - "properties": { - "subgraph.graphql.document": { - "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "subgraph.graphql.operation.name": { - "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "subgraph.graphql.operation.type": { - "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "subgraph.name": { - "description": "The name of the subgraph Examples: * products Requirement level: Required", - "default": null, - "type": "boolean", - "nullable": true - } - }, - "additionalProperties": { - "anyOf": [ - { - "type": "object", - "required": [ - "subgraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_operation_name": { - "description": "The operation name from the subgraph query.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_operation_kind" - ], - "properties": { - "subgraph_operation_kind": { - "description": "The kind of the subgraph operation (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] - } - }, - "additionalProperties": false }, { "type": "object", "required": [ - "subgraph_query" + "on_graphql_error" ], "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_query": { - "description": "The graphql query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" } }, "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_query_variable": { - "description": "The name of a subgraph query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", - "type": "object", - "required": [ - "subgraph_response_body" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_response_body": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_data" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_response_data": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_errors" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } + } + ], + "properties": { + "condition": { + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" ], - "nullable": true - }, - "subgraph_response_errors": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "subgraph": { + "description": "Attributes to include on the subgraph span. Subgraph spans contain information about the subgraph request and response and therefore contain subgraph specific attributes.", + "type": "object", + "properties": { + "attributes": { + "description": "Custom attributes that are attached to the subgraph span.", + "type": "object", + "properties": { + "subgraph.graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.name": { + "description": "The name of the subgraph Examples: * products Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "type": "object", + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", "required": [ - "subgraph_request_header" + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" ], "properties": { "default": { @@ -25770,63 +27326,1899 @@ expression: "&schema" "type": "string", "nullable": true }, - "subgraph_request_header": { - "description": "The name of a subgraph request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ], + "properties": { + "condition": { + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false }, - "subgraph_response_header": { - "description": "The name of a subgraph response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_status" - ], - "properties": { - "subgraph_response_status": { - "description": "The subgraph http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" ] } - }, - "additionalProperties": false - }, + ], + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "supergraph": { + "description": "Configuration of supergraph spans. Supergraph spans contain information about the graphql request and response and therefore contain graphql specific attributes.", + "type": "object", + "properties": { + "attributes": { + "description": "Custom attributes that are attached to the supergraph span.", + "type": "object", + "properties": { + "graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "type": "object", + "anyOf": [ { "type": "object", "required": [ - "supergraph_operation_name" + "operation_name" ], "properties": { "default": { @@ -25834,8 +29226,8 @@ expression: "&schema" "type": "string", "nullable": true }, - "supergraph_operation_name": { - "description": "The supergraph query operation name.", + "operation_name": { + "description": "The operation name from the query.", "oneOf": [ { "description": "The raw operation name.", @@ -25859,11 +29251,11 @@ expression: "&schema" { "type": "object", "required": [ - "supergraph_operation_kind" + "operation_kind" ], "properties": { - "supergraph_operation_kind": { - "description": "The supergraph query operation kind (query|mutation|subscription).", + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", "oneOf": [ { "description": "The raw operation kind.", @@ -25880,7 +29272,7 @@ expression: "&schema" { "type": "object", "required": [ - "supergraph_query" + "query" ], "properties": { "default": { @@ -25888,8 +29280,8 @@ expression: "&schema" "type": "string", "nullable": true }, - "supergraph_query": { - "description": "The supergraph query to the subgraph.", + "query": { + "description": "The graphql query.", "oneOf": [ { "description": "The raw query kind.", @@ -25906,7 +29298,7 @@ expression: "&schema" { "type": "object", "required": [ - "supergraph_query_variable" + "query_variable" ], "properties": { "default": { @@ -25968,8 +29360,8 @@ expression: "&schema" ], "nullable": true }, - "supergraph_query_variable": { - "description": "The supergraph query variable name.", + "query_variable": { + "description": "The name of a graphql query variable.", "type": "string" } }, @@ -25978,7 +29370,7 @@ expression: "&schema" { "type": "object", "required": [ - "supergraph_request_header" + "request_header" ], "properties": { "default": { @@ -25986,8 +29378,8 @@ expression: "&schema" "type": "string", "nullable": true }, - "supergraph_request_header": { - "description": "The supergraph request header name.", + "request_header": { + "description": "The name of the request header.", "type": "string" } }, @@ -25996,71 +29388,46 @@ expression: "&schema" { "type": "object", "required": [ - "request_context" + "response_header" ], "properties": { "default": { "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ { - "description": "String values", - "type": "string" + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] }, { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" ] } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" + ] } }, "additionalProperties": false @@ -26068,7 +29435,7 @@ expression: "&schema" { "type": "object", "required": [ - "response_context" + "request_context" ], "properties": { "default": { @@ -26130,8 +29497,8 @@ expression: "&schema" ], "nullable": true }, - "response_context": { - "description": "The response context key.", + "request_context": { + "description": "The request context key.", "type": "string" } }, @@ -26140,13 +29507,9 @@ expression: "&schema" { "type": "object", "required": [ - "baggage" + "response_context" ], "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" - }, "default": { "description": "Optional default value.", "anyOf": [ @@ -26204,151 +29567,11 @@ expression: "&schema" ] } ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - } - ] - } - } - }, - "additionalProperties": false - }, - "supergraph": { - "description": "Configuration of supergraph spans. Supergraph spans contain information about the graphql request and response and therefore contain graphql specific attributes.", - "type": "object", - "properties": { - "attributes": { - "description": "Custom attributes that are attached to the supergraph span.", - "default": { - "attributes": { - "graphql.document": null, - "graphql.operation.name": null, - "graphql.operation.type": null - }, - "custom": {} - }, - "type": "object", - "properties": { - "graphql.document": { - "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "graphql.operation.name": { - "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "graphql.operation.type": { - "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - } - }, - "additionalProperties": { - "anyOf": [ - { - "type": "object", - "required": [ - "operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "operation_name": { - "description": "The operation name from the query.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "operation_kind" - ], - "properties": { - "operation_kind": { - "description": "The operation kind from the query (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "query": { - "description": "The graphql query.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" } }, "additionalProperties": false @@ -26356,9 +29579,13 @@ expression: "&schema" { "type": "object", "required": [ - "query_variable" + "baggage" ], "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, "default": { "description": "Optional default value.", "anyOf": [ @@ -26384,350 +29611,1136 @@ expression: "&schema" "description": "Array of homogeneous values", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ], + "properties": { + "condition": { + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "query_variable": { - "description": "The name of a graphql query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "response_header": { - "description": "The name of the response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "type": "string" } ] } - ], - "nullable": true + }, + "additionalProperties": false }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + } + }, + "additionalProperties": false }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", "type": "string", - "nullable": true + "enum": [ + "true" + ] }, - "env": { - "description": "The name of the environment variable", - "type": "string" + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] } - }, - "additionalProperties": false - }, - { - "type": "string" + ], + "nullable": true } - ] + }, + "additionalProperties": false } } }, @@ -27658,6 +31671,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } @@ -28063,6 +32089,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the reponse body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } diff --git a/apollo-router/src/context/mod.rs b/apollo-router/src/context/mod.rs index 224ffc69d2..d9548b6d88 100644 --- a/apollo-router/src/context/mod.rs +++ b/apollo-router/src/context/mod.rs @@ -28,6 +28,8 @@ pub(crate) mod extensions; pub(crate) const OPERATION_NAME: &str = "operation_name"; /// The key of the resolved operation kind. This is subject to change and should not be relied on. pub(crate) const OPERATION_KIND: &str = "operation_kind"; +/// The key to know if the response body contains at least 1 GraphQL error +pub(crate) const CONTAINS_GRAPHQL_ERROR: &str = "apollo::telemetry::contains_graphql_error"; /// Holds [`Context`] entries. pub(crate) type Entries = Arc>; diff --git a/apollo-router/src/plugins/telemetry/config_new/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/attributes.rs index c9d5e97e0c..2e5a4584ca 100644 --- a/apollo-router/src/plugins/telemetry/config_new/attributes.rs +++ b/apollo-router/src/plugins/telemetry/config_new/attributes.rs @@ -1,5 +1,6 @@ use std::fmt::Debug; use std::net::SocketAddr; +use std::sync::Arc; use http::header::CONTENT_LENGTH; use http::header::FORWARDED; @@ -29,6 +30,7 @@ use opentelemetry_semantic_conventions::trace::URL_PATH; use opentelemetry_semantic_conventions::trace::URL_QUERY; use opentelemetry_semantic_conventions::trace::URL_SCHEME; use opentelemetry_semantic_conventions::trace::USER_AGENT_ORIGINAL; +use parking_lot::Mutex; use schemars::JsonSchema; use serde::Deserialize; #[cfg(test)] @@ -50,6 +52,9 @@ use crate::services::router::Request; use crate::services::subgraph; use crate::services::supergraph; +use super::conditions::Condition; +use super::Selector; + pub(crate) const SUBGRAPH_NAME: Key = Key::from_static_str("subgraph.name"); pub(crate) const SUBGRAPH_GRAPHQL_DOCUMENT: Key = Key::from_static_str("subgraph.graphql.document"); pub(crate) const SUBGRAPH_GRAPHQL_OPERATION_NAME: Key = @@ -77,6 +82,62 @@ pub(crate) enum DefaultAttributeRequirementLevel { Recommended, } +#[derive(Deserialize, JsonSchema, Clone, Debug, Default)] +#[serde(deny_unknown_fields)] +pub(crate) struct ConditionAttribute { + #[serde(flatten)] + pub(crate) selector: T, + #[schemars(with = "Option>", default)] + pub(crate) condition: Option>>>, +} + +impl DefaultForLevel for ConditionAttribute +where + T: DefaultForLevel, +{ + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + self.selector.defaults_for_level(requirement_level, kind); + } +} + +impl Selector for ConditionAttribute +where + T: Selector, +{ + type Request = Request; + type Response = Response; + + fn on_request(&self, request: &Self::Request) -> Option { + match &self.condition { + Some(condition) => { + if condition.lock().evaluate_request(request) == Some(true) { + self.selector.on_request(request) + } else { + None + } + } + None => self.selector.on_request(request), + } + } + + fn on_response(&self, response: &Self::Response) -> Option { + match &self.condition { + Some(condition) => { + if condition.lock().evaluate_response(response) { + self.selector.on_response(response) + } else { + None + } + } + None => self.selector.on_response(response), + } + } +} + #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] #[serde(deny_unknown_fields, default)] pub(crate) struct RouterAttributes { diff --git a/apollo-router/src/plugins/telemetry/config_new/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/selectors.rs index 898d6a3bb0..7d6dfa3f44 100644 --- a/apollo-router/src/plugins/telemetry/config_new/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/selectors.rs @@ -9,6 +9,7 @@ use serde::Serialize; use serde_json_bytes::ByteString; use sha2::Digest; +use crate::context::CONTAINS_GRAPHQL_ERROR; use crate::context::OPERATION_KIND; use crate::context::OPERATION_NAME; use crate::plugin::serde::deserialize_json_query; @@ -142,6 +143,10 @@ pub(crate) enum RouterSelector { default: Option, }, Static(String), + OnGraphQLError { + /// Boolean set to true if the reponse body contains graphql error + on_graphql_error: bool, + }, } #[derive(Deserialize, JsonSchema, Clone, Debug)] @@ -519,15 +524,22 @@ impl Selector for RouterSelector { .. } => response .context - .get::<_, serde_json_bytes::Value>(response_context) - .ok() - .flatten() + .get_json_value(response_context) .as_ref() .and_then(|v| v.maybe_to_otel_value()) .or_else(|| default.maybe_to_otel_value()), RouterSelector::Baggage { baggage, default, .. } => get_baggage(baggage).or_else(|| default.maybe_to_otel_value()), + RouterSelector::OnGraphQLError { on_graphql_error } if *on_graphql_error => { + if response.context.get_json_value(CONTAINS_GRAPHQL_ERROR) + == Some(serde_json_bytes::Value::Bool(true)) + { + Some(opentelemetry::Value::Bool(true)) + } else { + None + } + } _ => None, } } @@ -647,9 +659,7 @@ impl Selector for SupergraphSelector { .. } => response .context - .get::<_, serde_json_bytes::Value>(response_context) - .ok() - .flatten() + .get_json_value(response_context) .as_ref() .and_then(|v| v.maybe_to_otel_value()) .or_else(|| default.maybe_to_otel_value()), @@ -885,9 +895,7 @@ impl Selector for SubgraphSelector { .. } => response .context - .get::<_, serde_json_bytes::Value>(response_context) - .ok() - .flatten() + .get_json_value(response_context) .as_ref() .and_then(|v| v.maybe_to_otel_value()) .or_else(|| default.maybe_to_otel_value()), diff --git a/apollo-router/src/plugins/telemetry/config_new/spans.rs b/apollo-router/src/plugins/telemetry/config_new/spans.rs index 396077e7a1..00d73f3b5a 100644 --- a/apollo-router/src/plugins/telemetry/config_new/spans.rs +++ b/apollo-router/src/plugins/telemetry/config_new/spans.rs @@ -13,6 +13,8 @@ use crate::plugins::telemetry::config_new::DefaultForLevel; use crate::plugins::telemetry::otlp::TelemetryDataKind; use crate::plugins::telemetry::span_factory::SpanMode; +use super::attributes::ConditionAttribute; + #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] #[serde(deny_unknown_fields, default)] pub(crate) struct Spans { @@ -58,7 +60,7 @@ impl Spans { #[serde(deny_unknown_fields, default)] pub(crate) struct RouterSpans { /// Custom attributes that are attached to the router span. - pub(crate) attributes: Extendable, + pub(crate) attributes: Extendable>, } impl DefaultForLevel for RouterSpans { @@ -75,7 +77,7 @@ impl DefaultForLevel for RouterSpans { #[serde(deny_unknown_fields, default)] pub(crate) struct SupergraphSpans { /// Custom attributes that are attached to the supergraph span. - pub(crate) attributes: Extendable, + pub(crate) attributes: Extendable>, } impl DefaultForLevel for SupergraphSpans { fn defaults_for_level( @@ -91,7 +93,7 @@ impl DefaultForLevel for SupergraphSpans { #[serde(deny_unknown_fields, default)] pub(crate) struct SubgraphSpans { /// Custom attributes that are attached to the subgraph span. - pub(crate) attributes: Extendable, + pub(crate) attributes: Extendable>, } impl DefaultForLevel for SubgraphSpans { @@ -106,16 +108,25 @@ impl DefaultForLevel for SubgraphSpans { #[cfg(test)] mod test { + use std::sync::Arc; + use http::header::USER_AGENT; use opentelemetry_semantic_conventions::trace::GRAPHQL_DOCUMENT; use opentelemetry_semantic_conventions::trace::HTTP_REQUEST_METHOD; use opentelemetry_semantic_conventions::trace::NETWORK_PROTOCOL_VERSION; use opentelemetry_semantic_conventions::trace::URL_PATH; use opentelemetry_semantic_conventions::trace::USER_AGENT_ORIGINAL; + use parking_lot::Mutex; + use crate::context::CONTAINS_GRAPHQL_ERROR; use crate::graphql; + use crate::plugins::telemetry::config::AttributeValue; + use crate::plugins::telemetry::config_new::attributes::ConditionAttribute; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; use crate::plugins::telemetry::config_new::attributes::SUBGRAPH_GRAPHQL_DOCUMENT; + use crate::plugins::telemetry::config_new::conditions::Condition; + use crate::plugins::telemetry::config_new::conditions::SelectorOrValue; + use crate::plugins::telemetry::config_new::selectors::ResponseStatus; use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::selectors::SubgraphSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; @@ -128,6 +139,7 @@ mod test { use crate::services::router; use crate::services::subgraph; use crate::services::supergraph; + use crate::Context; #[test] fn test_router_spans_level_none() { @@ -329,15 +341,144 @@ mod test { .any(|key_val| key_val.key == SUBGRAPH_GRAPHQL_DOCUMENT)); } + #[test] + fn test_router_request_custom_attribute_on_graphql_error() { + let mut spans = RouterSpans::default(); + spans.attributes.custom.insert( + "test".to_string(), + ConditionAttribute { + selector: RouterSelector::ResponseHeader { + response_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: Some(Arc::new(Mutex::new(Condition::Eq([ + SelectorOrValue::Value(AttributeValue::Bool(true)), + SelectorOrValue::Selector(RouterSelector::OnGraphQLError { + on_graphql_error: true, + }), + ])))), + }, + ); + let context = Context::new(); + context.insert_json_value(CONTAINS_GRAPHQL_ERROR, serde_json_bytes::Value::Bool(true)); + let values = spans.attributes.on_response( + &router::Response::fake_builder() + .header("my-header", "test_val") + .context(context) + .build() + .unwrap(), + ); + assert!(values + .iter() + .any(|key_val| key_val.key == opentelemetry::Key::from_static_str("test"))); + } + + #[test] + fn test_router_request_custom_attribute_not_on_graphql_error() { + let mut spans = RouterSpans::default(); + spans.attributes.custom.insert( + "test".to_string(), + ConditionAttribute { + selector: RouterSelector::ResponseHeader { + response_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: Some(Arc::new(Mutex::new(Condition::Eq([ + SelectorOrValue::Value(AttributeValue::Bool(true)), + SelectorOrValue::Selector(RouterSelector::OnGraphQLError { + on_graphql_error: true, + }), + ])))), + }, + ); + let context = Context::new(); + context.insert_json_value(CONTAINS_GRAPHQL_ERROR, serde_json_bytes::Value::Bool(false)); + let values = spans.attributes.on_response( + &router::Response::fake_builder() + .header("my-header", "test_val") + .context(context) + .build() + .unwrap(), + ); + assert!(!values + .iter() + .any(|key_val| key_val.key == opentelemetry::Key::from_static_str("test"))); + } + + #[test] + fn test_router_request_custom_attribute_condition_true() { + let mut spans = RouterSpans::default(); + let selector = RouterSelector::RequestHeader { + request_header: "my-header".to_string(), + redact: None, + default: None, + }; + spans.attributes.custom.insert( + "test".to_string(), + ConditionAttribute { + selector: selector.clone(), + condition: Some(Arc::new(Mutex::new(Condition::Eq([ + SelectorOrValue::Value(AttributeValue::String("test_val".to_string())), + SelectorOrValue::Selector(selector), + ])))), + }, + ); + let values = spans.attributes.on_request( + &router::Request::fake_builder() + .method(http::Method::POST) + .header("my-header", "test_val") + .build() + .unwrap(), + ); + assert!(values + .iter() + .any(|key_val| key_val.key == opentelemetry::Key::from_static_str("test"))); + } + + #[test] + fn test_router_request_custom_attribute_condition_false() { + let mut spans = RouterSpans::default(); + let selector = RouterSelector::RequestHeader { + request_header: "my-header".to_string(), + redact: None, + default: None, + }; + spans.attributes.custom.insert( + "test".to_string(), + ConditionAttribute { + selector: selector.clone(), + condition: Some(Arc::new(Mutex::new(Condition::Eq([ + SelectorOrValue::Value(AttributeValue::String("test_val".to_string())), + SelectorOrValue::Selector(selector), + ])))), + }, + ); + let values = spans.attributes.on_request( + &router::Request::fake_builder() + .method(http::Method::POST) + .header("my-header", "bar") + .build() + .unwrap(), + ); + assert!(!values + .iter() + .any(|key_val| key_val.key == opentelemetry::Key::from_static_str("test"))); + } + #[test] fn test_router_request_custom_attribute() { let mut spans = RouterSpans::default(); spans.attributes.custom.insert( "test".to_string(), - RouterSelector::RequestHeader { - request_header: "my-header".to_string(), - redact: None, - default: None, + ConditionAttribute { + selector: RouterSelector::RequestHeader { + request_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: None, }, ); let values = spans.attributes.on_request( @@ -357,10 +498,13 @@ mod test { let mut spans = RouterSpans::default(); spans.attributes.custom.insert( "test".to_string(), - RouterSelector::ResponseHeader { - response_header: "my-header".to_string(), - redact: None, - default: None, + ConditionAttribute { + selector: RouterSelector::ResponseHeader { + response_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: None, }, ); let values = spans.attributes.on_response( @@ -379,10 +523,13 @@ mod test { let mut spans = SupergraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - SupergraphSelector::RequestHeader { - request_header: "my-header".to_string(), - redact: None, - default: None, + ConditionAttribute { + selector: SupergraphSelector::RequestHeader { + request_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: None, }, ); let values = spans.attributes.on_request( @@ -402,10 +549,13 @@ mod test { let mut spans = SupergraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - SupergraphSelector::ResponseHeader { - response_header: "my-header".to_string(), - redact: None, - default: None, + ConditionAttribute { + selector: SupergraphSelector::ResponseHeader { + response_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: None, }, ); let values = spans.attributes.on_response( @@ -424,10 +574,13 @@ mod test { let mut spans = SubgraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - SubgraphSelector::SubgraphRequestHeader { - subgraph_request_header: "my-header".to_string(), - redact: None, - default: None, + ConditionAttribute { + selector: SubgraphSelector::SubgraphRequestHeader { + subgraph_request_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: None, }, ); let values = spans.attributes.on_request( @@ -455,15 +608,49 @@ mod test { let mut spans = SubgraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - SubgraphSelector::SubgraphResponseHeader { - subgraph_response_header: "my-header".to_string(), - redact: None, - default: None, + ConditionAttribute { + selector: SubgraphSelector::SubgraphResponseHeader { + subgraph_response_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: None, + }, + ); + let values = spans.attributes.on_response( + &subgraph::Response::fake2_builder() + .header("my-header", "test_val") + .build() + .unwrap(), + ); + assert!(values + .iter() + .any(|key_val| key_val.key == opentelemetry::Key::from_static_str("test"))); + } + + #[test] + fn test_subgraph_response_custom_attribute_good_condition() { + let mut spans = SubgraphSpans::default(); + spans.attributes.custom.insert( + "test".to_string(), + ConditionAttribute { + selector: SubgraphSelector::SubgraphResponseHeader { + subgraph_response_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: Some(Arc::new(Mutex::new(Condition::Eq([ + SelectorOrValue::Value(AttributeValue::I64(200)), + SelectorOrValue::Selector(SubgraphSelector::SubgraphResponseStatus { + subgraph_response_status: ResponseStatus::Code, + }), + ])))), }, ); let values = spans.attributes.on_response( &subgraph::Response::fake2_builder() .header("my-header", "test_val") + .status_code(http::StatusCode::OK) .build() .unwrap(), ); @@ -471,4 +658,35 @@ mod test { .iter() .any(|key_val| key_val.key == opentelemetry::Key::from_static_str("test"))); } + + #[test] + fn test_subgraph_response_custom_attribute_bad_condition() { + let mut spans = SubgraphSpans::default(); + spans.attributes.custom.insert( + "test".to_string(), + ConditionAttribute { + selector: SubgraphSelector::SubgraphResponseHeader { + subgraph_response_header: "my-header".to_string(), + redact: None, + default: None, + }, + condition: Some(Arc::new(Mutex::new(Condition::Eq([ + SelectorOrValue::Value(AttributeValue::I64(400)), + SelectorOrValue::Selector(SubgraphSelector::SubgraphResponseStatus { + subgraph_response_status: ResponseStatus::Code, + }), + ])))), + }, + ); + let values = spans.attributes.on_response( + &subgraph::Response::fake2_builder() + .header("my-header", "test_val") + .status_code(http::StatusCode::OK) + .build() + .unwrap(), + ); + assert!(!values + .iter() + .any(|key_val| key_val.key == opentelemetry::Key::from_static_str("test"))); + } } diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index bdf5a081b8..2532bd9d61 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -76,6 +76,7 @@ use self::tracing::apollo_telemetry::APOLLO_PRIVATE_DURATION_NS; use self::tracing::apollo_telemetry::CLIENT_NAME_KEY; use self::tracing::apollo_telemetry::CLIENT_VERSION_KEY; use crate::axum_factory::utils::REQUEST_SPAN_NAME; +use crate::context::CONTAINS_GRAPHQL_ERROR; use crate::context::OPERATION_KIND; use crate::context::OPERATION_NAME; use crate::layers::instrument::InstrumentLayer; @@ -1258,6 +1259,11 @@ impl Telemetry { .enumerate() .map(move |(idx, response)| { let has_errors = !response.errors.is_empty(); + // Useful for selector in spans/instruments/events + ctx.insert_json_value( + CONTAINS_GRAPHQL_ERROR, + serde_json_bytes::Value::Bool(has_errors), + ); if !matches!(sender, Sender::Noop) { if operation_kind == OperationKind::Subscription { From 566cd51539aa241844bfc469f25c7216915022e3 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:09:13 +0200 Subject: [PATCH 22/40] add static field selector Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- ...nfiguration__tests__schema_generation.snap | 546 ++++++++++++++++++ .../plugins/telemetry/config_new/selectors.rs | 18 + .../src/plugins/telemetry/config_new/spans.rs | 32 + 3 files changed, 596 insertions(+) diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index 1bf679c3ed..0d7124ddb5 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -7342,6 +7342,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -7819,6 +7832,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -8237,6 +8263,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -9273,6 +9312,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -10191,6 +10243,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -11050,6 +11115,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -11682,6 +11760,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -12214,6 +12305,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -12687,6 +12791,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -13422,6 +13539,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -13989,6 +14119,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -14556,6 +14699,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -15118,6 +15274,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -15595,6 +15764,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -16013,6 +16195,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -16517,6 +16712,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -17425,6 +17633,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -18318,6 +18539,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -19211,6 +19445,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -20100,6 +20347,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -21018,6 +21278,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -21877,6 +22150,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -22822,6 +23108,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -23320,6 +23619,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -23852,6 +24164,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -24325,6 +24650,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -24884,6 +25222,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -25480,6 +25831,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -25955,6 +26319,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -26373,6 +26750,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -27335,6 +27725,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ], "properties": { @@ -28251,6 +28654,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -29110,6 +29526,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -29668,6 +30097,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ], "properties": { @@ -30198,6 +30640,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -30671,6 +31126,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -31672,6 +32140,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -32090,6 +32571,19 @@ expression: "&schema" { "type": "string" }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -33082,6 +33576,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -33941,6 +34448,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -34535,6 +35055,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } @@ -35008,6 +35541,19 @@ expression: "&schema" }, { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } ] } diff --git a/apollo-router/src/plugins/telemetry/config_new/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/selectors.rs index 7d6dfa3f44..c3ed9226aa 100644 --- a/apollo-router/src/plugins/telemetry/config_new/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/selectors.rs @@ -143,6 +143,10 @@ pub(crate) enum RouterSelector { default: Option, }, Static(String), + StaticField { + /// A static string value + r#static: String, + }, OnGraphQLError { /// Boolean set to true if the reponse body contains graphql error on_graphql_error: bool, @@ -257,6 +261,10 @@ pub(crate) enum SupergraphSelector { default: Option, }, Static(String), + StaticField { + /// A static string value + r#static: String, + }, } #[derive(Deserialize, JsonSchema, Clone, Derivative)] @@ -453,6 +461,10 @@ pub(crate) enum SubgraphSelector { default: Option, }, Static(String), + StaticField { + /// A static string value + r#static: String, + }, } impl Selector for RouterSelector { @@ -491,6 +503,7 @@ impl Selector for RouterSelector { baggage, default, .. } => get_baggage(baggage).or_else(|| default.maybe_to_otel_value()), RouterSelector::Static(val) => Some(val.clone().into()), + RouterSelector::StaticField { r#static } => Some(r#static.clone().into()), // Related to Response _ => None, } @@ -540,6 +553,7 @@ impl Selector for RouterSelector { None } } + RouterSelector::StaticField { r#static } => Some(r#static.clone().into()), _ => None, } } @@ -625,6 +639,7 @@ impl Selector for SupergraphSelector { .or_else(|| default.clone()) .map(opentelemetry::Value::from), SupergraphSelector::Static(val) => Some(val.clone().into()), + SupergraphSelector::StaticField { r#static } => Some(r#static.clone().into()), // For response _ => None, } @@ -663,6 +678,7 @@ impl Selector for SupergraphSelector { .as_ref() .and_then(|v| v.maybe_to_otel_value()) .or_else(|| default.maybe_to_otel_value()), + SupergraphSelector::StaticField { r#static } => Some(r#static.clone().into()), // For request _ => None, } @@ -804,6 +820,7 @@ impl Selector for SubgraphSelector { .or_else(|| default.clone()) .map(opentelemetry::Value::from), SubgraphSelector::Static(val) => Some(val.clone().into()), + SubgraphSelector::StaticField { r#static } => Some(r#static.clone().into()), // For response _ => None, @@ -899,6 +916,7 @@ impl Selector for SubgraphSelector { .as_ref() .and_then(|v| v.maybe_to_otel_value()) .or_else(|| default.maybe_to_otel_value()), + SubgraphSelector::StaticField { r#static } => Some(r#static.clone().into()), // For request _ => None, } diff --git a/apollo-router/src/plugins/telemetry/config_new/spans.rs b/apollo-router/src/plugins/telemetry/config_new/spans.rs index 00d73f3b5a..78fc8920cf 100644 --- a/apollo-router/src/plugins/telemetry/config_new/spans.rs +++ b/apollo-router/src/plugins/telemetry/config_new/spans.rs @@ -341,6 +341,38 @@ mod test { .any(|key_val| key_val.key == SUBGRAPH_GRAPHQL_DOCUMENT)); } + #[test] + fn test_router_request_static_custom_attribute_on_graphql_error() { + let mut spans = RouterSpans::default(); + spans.attributes.custom.insert( + "test".to_string(), + ConditionAttribute { + selector: RouterSelector::StaticField { + r#static: "my-static-value".to_string(), + }, + condition: Some(Arc::new(Mutex::new(Condition::Eq([ + SelectorOrValue::Value(AttributeValue::Bool(true)), + SelectorOrValue::Selector(RouterSelector::OnGraphQLError { + on_graphql_error: true, + }), + ])))), + }, + ); + let context = Context::new(); + context.insert_json_value(CONTAINS_GRAPHQL_ERROR, serde_json_bytes::Value::Bool(true)); + let values = spans.attributes.on_response( + &router::Response::fake_builder() + .header("my-header", "test_val") + .context(context) + .build() + .unwrap(), + ); + assert!(values.iter().any(|key_val| key_val.key + == opentelemetry::Key::from_static_str("test") + && key_val.value + == opentelemetry::Value::String("my-static-value".to_string().into()))); + } + #[test] fn test_router_request_custom_attribute_on_graphql_error() { let mut spans = RouterSpans::default(); From f27bbf147ff13cb30c686bac374389468f1458fd Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:19:59 +0200 Subject: [PATCH 23/40] update docs Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/instrumentation/instruments.mdx | 4 +-- .../telemetry/instrumentation/selectors.mdx | 6 +++- .../telemetry/instrumentation/spans.mdx | 31 +++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/docs/source/configuration/telemetry/instrumentation/instruments.mdx b/docs/source/configuration/telemetry/instrumentation/instruments.mdx index 6d26b18b67..e93c455417 100644 --- a/docs/source/configuration/telemetry/instrumentation/instruments.mdx +++ b/docs/source/configuration/telemetry/instrumentation/instruments.mdx @@ -338,8 +338,8 @@ telemetry: | `` | | | The name of the custom attribute. | | `` | | | The name of the custom instrument. | | `attributes` | [standard attributes](./standard-attributes) or [selectors](./selectors) | | The attributes of the custom instrument. | -| `condition` | [conditions](./conditions) | | The a condition for mutating the instrument. | -| `default_requirement_level` | `required`\|`recommended` | `required` | The default attribute requirement level. | +| `condition` | [conditions](./conditions) | | The condition for mutating the instrument. | +| `default_requirement_level` | `required`\|`recommended` | `required` | The default attribute requirement level. | | `type` | `counter`\|`histogram` | | The name of the custom instrument. | | `unit` | | | A unit name, for example `By` or `{request}`.| | `description` | | | The description of the custom instrument. | diff --git a/docs/source/configuration/telemetry/instrumentation/selectors.mdx b/docs/source/configuration/telemetry/instrumentation/selectors.mdx index 6e56b7e153..f30b955350 100644 --- a/docs/source/configuration/telemetry/instrumentation/selectors.mdx +++ b/docs/source/configuration/telemetry/instrumentation/selectors.mdx @@ -39,6 +39,8 @@ The router service is the initial entrypoint for all requests. It is HTTP centri | `response_context` | Yes | | The name of a response context key | | `baggage` | Yes | | The name of a baggage item | | `env` | Yes | | The name of an environment variable | +| `on_graphql_error` | No | `true`|`false` | Boolean set to true if the response payload contains a graphql error | +| `static` | No | | A static string value | #### Supergraph @@ -56,6 +58,7 @@ The supergraph service is executed after query parsing but before query executio | `response_context` | Yes | | The name of a response context key | | `baggage` | Yes | | The name of a baggage item | | `env` | Yes | | The name of an environment variable | +| `static` | No | | A static string value | #### Subgraph @@ -72,7 +75,7 @@ The subgraph service executes multiple times during query execution, with each e | `subgraph_response_errors` | Yes | | Json Path into the subgraph response body errors (it might impact performances) | | `subgraph_request_header` | Yes | | The name of a subgraph request header | | `subgraph_response_header` | Yes | | The name of a subgraph response header | -| `subgraph_response_status` | Yes | | The status of a subgraph response | +| `subgraph_response_status` | Yes | `code`\|`reason` | The status of a subgraph response | | `supergraph_operation_name` | Yes | | The operation name from the supergraph query | | `supergraph_operation_kind` | Yes | `string` | The operation kind from the supergraph query | | `supergraph_query` | Yes | | The graphql query to the supergraph | @@ -81,3 +84,4 @@ The subgraph service executes multiple times during query execution, with each e | `response_context` | Yes | | The name of a response context key | | `baggage` | Yes | | The name of a baggage item | | `env` | Yes | | The name of an environment variable | +| `static` | No | | A static string value | diff --git a/docs/source/configuration/telemetry/instrumentation/spans.mdx b/docs/source/configuration/telemetry/instrumentation/spans.mdx index 41fc4c399b..56b238d9c0 100644 --- a/docs/source/configuration/telemetry/instrumentation/spans.mdx +++ b/docs/source/configuration/telemetry/instrumentation/spans.mdx @@ -58,6 +58,24 @@ telemetry: response_header: "x-my-header" ``` +You can also have [conditions](./conditions) on custom attributes using [selectors](./selectors). You can only have conditions on a selector at the same execution level. +Example you can't have a condition on `response_header` if you want to set an attribute from `request_header`. + +```yaml title="desc.router.yaml" +telemetry: + instrumentation: + spans: + router: + attributes: + on_error: + response_status: reason + condition: + not: + eq: + - response_status: code + - 200 +``` + ### `default_attribute_requirement_level` The `default_attribute_requirement_level` option sets the default attributes to attach to spans, as defined by [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/otel/common/attribute-requirement-level/). @@ -153,6 +171,18 @@ telemetry: http.request.method: false # ... + # Conditional custom attribute + otel.status_description: # You can conditionally put a status description attribute on your span if it respect the condition + static: "there was an error" + condition: # http response status code != 200 or it contains a graphql error in the payload + any: + - not: + eq: + - response_status: code + - 200 + - eq: + - on_graphql_error + - true # Custom attributes "acme.custom_1": trace_id: datadog @@ -178,6 +208,7 @@ telemetry: |---------------------------------------|---------------------------------------------------------------------------|--------------------------------|------------------------------------------| | `` | | | The name of the custom attribute. | | `attributes` | [standard attributes](./standard-attributes)\|[selectors](./selectors) | | The attributes of the span. | +| `condition` | [conditions](./conditions) | | The condition for adding a custom attribute. | | `default_attribute_requirement_level` | `required`\|`recommended` | `required` | The default attribute requirement level. | | `legacy_request_span` | `true`\|`false` | | Include the `request` span in traces. | | `mode` | `spec_compliant` \| `deprecated` | `deprecated` | The attributes of the span. | From 0f5d6bd444a16e5620c64d3fba78fce39f73a1a5 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:30:53 +0200 Subject: [PATCH 24/40] fix lint Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/plugins/telemetry/config_new/attributes.rs | 5 ++--- apollo-router/src/plugins/telemetry/config_new/spans.rs | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/attributes.rs index 2e5a4584ca..a90f9a31a0 100644 --- a/apollo-router/src/plugins/telemetry/config_new/attributes.rs +++ b/apollo-router/src/plugins/telemetry/config_new/attributes.rs @@ -38,6 +38,8 @@ use serde::Serialize; use tower::BoxError; use tracing::Span; +use super::conditions::Condition; +use super::Selector; use crate::axum_factory::utils::ConnectionInfo; use crate::context::OPERATION_KIND; use crate::context::OPERATION_NAME; @@ -52,9 +54,6 @@ use crate::services::router::Request; use crate::services::subgraph; use crate::services::supergraph; -use super::conditions::Condition; -use super::Selector; - pub(crate) const SUBGRAPH_NAME: Key = Key::from_static_str("subgraph.name"); pub(crate) const SUBGRAPH_GRAPHQL_DOCUMENT: Key = Key::from_static_str("subgraph.graphql.document"); pub(crate) const SUBGRAPH_GRAPHQL_OPERATION_NAME: Key = diff --git a/apollo-router/src/plugins/telemetry/config_new/spans.rs b/apollo-router/src/plugins/telemetry/config_new/spans.rs index 78fc8920cf..f16477c157 100644 --- a/apollo-router/src/plugins/telemetry/config_new/spans.rs +++ b/apollo-router/src/plugins/telemetry/config_new/spans.rs @@ -1,6 +1,7 @@ use schemars::JsonSchema; use serde::Deserialize; +use super::attributes::ConditionAttribute; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; use crate::plugins::telemetry::config_new::attributes::RouterAttributes; use crate::plugins::telemetry::config_new::attributes::SubgraphAttributes; @@ -13,8 +14,6 @@ use crate::plugins::telemetry::config_new::DefaultForLevel; use crate::plugins::telemetry::otlp::TelemetryDataKind; use crate::plugins::telemetry::span_factory::SpanMode; -use super::attributes::ConditionAttribute; - #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] #[serde(deny_unknown_fields, default)] pub(crate) struct Spans { From a7b3a56e9411b705cb2a4b6725779699640a0150 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 13:49:22 +0100 Subject: [PATCH 25/40] Condition attribute schema --- .../telemetry/config_new/attributes.rs | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/attributes.rs index 7c831f60be..b981451ceb 100644 --- a/apollo-router/src/plugins/telemetry/config_new/attributes.rs +++ b/apollo-router/src/plugins/telemetry/config_new/attributes.rs @@ -1,3 +1,5 @@ +use std::any::type_name; +use std::collections::HashMap; use std::fmt::Debug; use std::net::SocketAddr; use std::sync::Arc; @@ -83,15 +85,35 @@ pub(crate) enum DefaultAttributeRequirementLevel { Recommended, } -#[derive(Deserialize, JsonSchema, Clone, Debug, Default)] +#[derive(Deserialize, Clone, Debug, Default)] #[serde(deny_unknown_fields)] pub(crate) struct ConditionAttribute { - #[serde(flatten)] pub(crate) selector: T, - #[schemars(with = "Option>", default)] pub(crate) condition: Option>>>, } +impl JsonSchema for ConditionAttribute +where + T: JsonSchema, +{ + fn schema_name() -> String { + format!("conditional_attribute_{}", type_name::()) + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + let mut selector = gen.subschema_for::>(); + if let Schema::Object(schema) = &mut selector { + if let Some(object) = &mut schema.object { + object + .properties + .insert("condition".to_string(), gen.subschema_for::>()); + } + } + + selector + } +} + impl DefaultForLevel for ConditionAttribute where T: DefaultForLevel, From af2ad4794020e241c5094443f6b115e7381c5881 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 13:54:41 +0100 Subject: [PATCH 26/40] Move condition attribute to separate module --- .../telemetry/config_new/attributes.rs | 84 ------------------ .../telemetry/config_new/conditional.rs | 88 +++++++++++++++++++ .../src/plugins/telemetry/config_new/mod.rs | 1 + .../src/plugins/telemetry/config_new/spans.rs | 4 +- 4 files changed, 91 insertions(+), 86 deletions(-) create mode 100644 apollo-router/src/plugins/telemetry/config_new/conditional.rs diff --git a/apollo-router/src/plugins/telemetry/config_new/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/attributes.rs index b981451ceb..c9d5e97e0c 100644 --- a/apollo-router/src/plugins/telemetry/config_new/attributes.rs +++ b/apollo-router/src/plugins/telemetry/config_new/attributes.rs @@ -1,8 +1,5 @@ -use std::any::type_name; -use std::collections::HashMap; use std::fmt::Debug; use std::net::SocketAddr; -use std::sync::Arc; use http::header::CONTENT_LENGTH; use http::header::FORWARDED; @@ -32,9 +29,6 @@ use opentelemetry_semantic_conventions::trace::URL_PATH; use opentelemetry_semantic_conventions::trace::URL_QUERY; use opentelemetry_semantic_conventions::trace::URL_SCHEME; use opentelemetry_semantic_conventions::trace::USER_AGENT_ORIGINAL; -use parking_lot::Mutex; -use schemars::gen::SchemaGenerator; -use schemars::schema::Schema; use schemars::JsonSchema; use serde::Deserialize; #[cfg(test)] @@ -42,8 +36,6 @@ use serde::Serialize; use tower::BoxError; use tracing::Span; -use super::conditions::Condition; -use super::Selector; use crate::axum_factory::utils::ConnectionInfo; use crate::context::OPERATION_KIND; use crate::context::OPERATION_NAME; @@ -85,82 +77,6 @@ pub(crate) enum DefaultAttributeRequirementLevel { Recommended, } -#[derive(Deserialize, Clone, Debug, Default)] -#[serde(deny_unknown_fields)] -pub(crate) struct ConditionAttribute { - pub(crate) selector: T, - pub(crate) condition: Option>>>, -} - -impl JsonSchema for ConditionAttribute -where - T: JsonSchema, -{ - fn schema_name() -> String { - format!("conditional_attribute_{}", type_name::()) - } - - fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let mut selector = gen.subschema_for::>(); - if let Schema::Object(schema) = &mut selector { - if let Some(object) = &mut schema.object { - object - .properties - .insert("condition".to_string(), gen.subschema_for::>()); - } - } - - selector - } -} - -impl DefaultForLevel for ConditionAttribute -where - T: DefaultForLevel, -{ - fn defaults_for_level( - &mut self, - requirement_level: DefaultAttributeRequirementLevel, - kind: TelemetryDataKind, - ) { - self.selector.defaults_for_level(requirement_level, kind); - } -} - -impl Selector for ConditionAttribute -where - T: Selector, -{ - type Request = Request; - type Response = Response; - - fn on_request(&self, request: &Self::Request) -> Option { - match &self.condition { - Some(condition) => { - if condition.lock().evaluate_request(request) == Some(true) { - self.selector.on_request(request) - } else { - None - } - } - None => self.selector.on_request(request), - } - } - - fn on_response(&self, response: &Self::Response) -> Option { - match &self.condition { - Some(condition) => { - if condition.lock().evaluate_response(response) { - self.selector.on_response(response) - } else { - None - } - } - None => self.selector.on_response(response), - } - } -} - #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] #[serde(deny_unknown_fields, default)] pub(crate) struct RouterAttributes { diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs new file mode 100644 index 0000000000..e3d8440ec9 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -0,0 +1,88 @@ +use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; +use crate::plugins::telemetry::config_new::conditions::Condition; +use crate::plugins::telemetry::config_new::{DefaultForLevel, Selector}; +use crate::plugins::telemetry::otlp::TelemetryDataKind; +use parking_lot::Mutex; +use schemars::gen::SchemaGenerator; +use schemars::schema::Schema; +use schemars::JsonSchema; +use serde::Deserialize; +use std::any::type_name; +use std::collections::HashMap; +use std::sync::Arc; + +#[derive(Deserialize, Clone, Debug, Default)] +#[serde(deny_unknown_fields)] +pub(crate) struct ConditionAttribute { + pub(crate) selector: T, + pub(crate) condition: Option>>>, +} + +impl JsonSchema for ConditionAttribute +where + T: JsonSchema, +{ + fn schema_name() -> String { + format!("conditional_attribute_{}", type_name::()) + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + let mut selector = gen.subschema_for::>(); + if let Schema::Object(schema) = &mut selector { + if let Some(object) = &mut schema.object { + object + .properties + .insert("condition".to_string(), gen.subschema_for::>()); + } + } + + selector + } +} + +impl DefaultForLevel for ConditionAttribute +where + T: DefaultForLevel, +{ + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + self.selector.defaults_for_level(requirement_level, kind); + } +} + +impl Selector for ConditionAttribute +where + T: Selector, +{ + type Request = Request; + type Response = Response; + + fn on_request(&self, request: &Self::Request) -> Option { + match &self.condition { + Some(condition) => { + if condition.lock().evaluate_request(request) == Some(true) { + self.selector.on_request(request) + } else { + None + } + } + None => self.selector.on_request(request), + } + } + + fn on_response(&self, response: &Self::Response) -> Option { + match &self.condition { + Some(condition) => { + if condition.lock().evaluate_response(response) { + self.selector.on_response(response) + } else { + None + } + } + None => self.selector.on_response(response), + } + } +} diff --git a/apollo-router/src/plugins/telemetry/config_new/mod.rs b/apollo-router/src/plugins/telemetry/config_new/mod.rs index b146dcda59..3ca0c9645b 100644 --- a/apollo-router/src/plugins/telemetry/config_new/mod.rs +++ b/apollo-router/src/plugins/telemetry/config_new/mod.rs @@ -15,6 +15,7 @@ use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequireme pub(crate) mod attributes; pub(crate) mod conditions; +mod conditional; pub(crate) mod events; mod experimental_when_header; pub(crate) mod extendable; diff --git a/apollo-router/src/plugins/telemetry/config_new/spans.rs b/apollo-router/src/plugins/telemetry/config_new/spans.rs index f16477c157..6bc8466d5b 100644 --- a/apollo-router/src/plugins/telemetry/config_new/spans.rs +++ b/apollo-router/src/plugins/telemetry/config_new/spans.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::Deserialize; -use super::attributes::ConditionAttribute; +use super::conditional::ConditionAttribute; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; use crate::plugins::telemetry::config_new::attributes::RouterAttributes; use crate::plugins::telemetry::config_new::attributes::SubgraphAttributes; @@ -120,9 +120,9 @@ mod test { use crate::context::CONTAINS_GRAPHQL_ERROR; use crate::graphql; use crate::plugins::telemetry::config::AttributeValue; - use crate::plugins::telemetry::config_new::attributes::ConditionAttribute; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; use crate::plugins::telemetry::config_new::attributes::SUBGRAPH_GRAPHQL_DOCUMENT; + use crate::plugins::telemetry::config_new::conditional::ConditionAttribute; use crate::plugins::telemetry::config_new::conditions::Condition; use crate::plugins::telemetry::config_new::conditions::SelectorOrValue; use crate::plugins::telemetry::config_new::selectors::ResponseStatus; From 5f43729e993369ce47016cd2cd19cf6d98410bd4 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 13:56:25 +0100 Subject: [PATCH 27/40] Rename `ConditionAttribute` to `Conditional` to match `Extendable` --- .../telemetry/config_new/conditional.rs | 8 ++--- .../src/plugins/telemetry/config_new/spans.rs | 36 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index e3d8440ec9..ea22b9fa1f 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -13,12 +13,12 @@ use std::sync::Arc; #[derive(Deserialize, Clone, Debug, Default)] #[serde(deny_unknown_fields)] -pub(crate) struct ConditionAttribute { +pub(crate) struct Conditional { pub(crate) selector: T, pub(crate) condition: Option>>>, } -impl JsonSchema for ConditionAttribute +impl JsonSchema for Conditional where T: JsonSchema, { @@ -40,7 +40,7 @@ where } } -impl DefaultForLevel for ConditionAttribute +impl DefaultForLevel for Conditional where T: DefaultForLevel, { @@ -53,7 +53,7 @@ where } } -impl Selector for ConditionAttribute +impl Selector for Conditional where T: Selector, { diff --git a/apollo-router/src/plugins/telemetry/config_new/spans.rs b/apollo-router/src/plugins/telemetry/config_new/spans.rs index 6bc8466d5b..39eb18adf8 100644 --- a/apollo-router/src/plugins/telemetry/config_new/spans.rs +++ b/apollo-router/src/plugins/telemetry/config_new/spans.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::Deserialize; -use super::conditional::ConditionAttribute; +use super::conditional::Conditional; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; use crate::plugins::telemetry::config_new::attributes::RouterAttributes; use crate::plugins::telemetry::config_new::attributes::SubgraphAttributes; @@ -59,7 +59,7 @@ impl Spans { #[serde(deny_unknown_fields, default)] pub(crate) struct RouterSpans { /// Custom attributes that are attached to the router span. - pub(crate) attributes: Extendable>, + pub(crate) attributes: Extendable>, } impl DefaultForLevel for RouterSpans { @@ -76,7 +76,7 @@ impl DefaultForLevel for RouterSpans { #[serde(deny_unknown_fields, default)] pub(crate) struct SupergraphSpans { /// Custom attributes that are attached to the supergraph span. - pub(crate) attributes: Extendable>, + pub(crate) attributes: Extendable>, } impl DefaultForLevel for SupergraphSpans { fn defaults_for_level( @@ -92,7 +92,7 @@ impl DefaultForLevel for SupergraphSpans { #[serde(deny_unknown_fields, default)] pub(crate) struct SubgraphSpans { /// Custom attributes that are attached to the subgraph span. - pub(crate) attributes: Extendable>, + pub(crate) attributes: Extendable>, } impl DefaultForLevel for SubgraphSpans { @@ -122,7 +122,7 @@ mod test { use crate::plugins::telemetry::config::AttributeValue; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; use crate::plugins::telemetry::config_new::attributes::SUBGRAPH_GRAPHQL_DOCUMENT; - use crate::plugins::telemetry::config_new::conditional::ConditionAttribute; + use crate::plugins::telemetry::config_new::conditional::Conditional; use crate::plugins::telemetry::config_new::conditions::Condition; use crate::plugins::telemetry::config_new::conditions::SelectorOrValue; use crate::plugins::telemetry::config_new::selectors::ResponseStatus; @@ -345,7 +345,7 @@ mod test { let mut spans = RouterSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: RouterSelector::StaticField { r#static: "my-static-value".to_string(), }, @@ -377,7 +377,7 @@ mod test { let mut spans = RouterSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: RouterSelector::ResponseHeader { response_header: "my-header".to_string(), redact: None, @@ -410,7 +410,7 @@ mod test { let mut spans = RouterSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: RouterSelector::ResponseHeader { response_header: "my-header".to_string(), redact: None, @@ -448,7 +448,7 @@ mod test { }; spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: selector.clone(), condition: Some(Arc::new(Mutex::new(Condition::Eq([ SelectorOrValue::Value(AttributeValue::String("test_val".to_string())), @@ -478,7 +478,7 @@ mod test { }; spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: selector.clone(), condition: Some(Arc::new(Mutex::new(Condition::Eq([ SelectorOrValue::Value(AttributeValue::String("test_val".to_string())), @@ -503,7 +503,7 @@ mod test { let mut spans = RouterSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: RouterSelector::RequestHeader { request_header: "my-header".to_string(), redact: None, @@ -529,7 +529,7 @@ mod test { let mut spans = RouterSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: RouterSelector::ResponseHeader { response_header: "my-header".to_string(), redact: None, @@ -554,7 +554,7 @@ mod test { let mut spans = SupergraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: SupergraphSelector::RequestHeader { request_header: "my-header".to_string(), redact: None, @@ -580,7 +580,7 @@ mod test { let mut spans = SupergraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: SupergraphSelector::ResponseHeader { response_header: "my-header".to_string(), redact: None, @@ -605,7 +605,7 @@ mod test { let mut spans = SubgraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: SubgraphSelector::SubgraphRequestHeader { subgraph_request_header: "my-header".to_string(), redact: None, @@ -639,7 +639,7 @@ mod test { let mut spans = SubgraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: SubgraphSelector::SubgraphResponseHeader { subgraph_response_header: "my-header".to_string(), redact: None, @@ -664,7 +664,7 @@ mod test { let mut spans = SubgraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: SubgraphSelector::SubgraphResponseHeader { subgraph_response_header: "my-header".to_string(), redact: None, @@ -695,7 +695,7 @@ mod test { let mut spans = SubgraphSpans::default(); spans.attributes.custom.insert( "test".to_string(), - ConditionAttribute { + Conditional { selector: SubgraphSelector::SubgraphResponseHeader { subgraph_response_header: "my-header".to_string(), redact: None, From 340d779eba19e9620f36f86a2a9084575d5960e0 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 16:00:47 +0100 Subject: [PATCH 28/40] Add tests, add deserializer. There's a failing test still. --- .../telemetry/config_new/conditional.rs | 205 ++++++++++++++++-- .../plugins/telemetry/config_new/selectors.rs | 2 +- 2 files changed, 190 insertions(+), 17 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index ea22b9fa1f..8bb9d0c602 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -1,21 +1,32 @@ -use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; -use crate::plugins::telemetry::config_new::conditions::Condition; -use crate::plugins::telemetry::config_new::{DefaultForLevel, Selector}; -use crate::plugins::telemetry::otlp::TelemetryDataKind; +use std::any::type_name; +use std::collections::HashMap; +use std::fmt::Debug; +use std::sync::Arc; + use parking_lot::Mutex; use schemars::gen::SchemaGenerator; use schemars::schema::Schema; use schemars::JsonSchema; +use serde::de::Error; +use serde::de::MapAccess; +use serde::de::Visitor; use serde::Deserialize; -use std::any::type_name; -use std::collections::HashMap; -use std::sync::Arc; +use serde::Deserializer; +use serde_json::Map; +use serde_json::Value; -#[derive(Deserialize, Clone, Debug, Default)] -#[serde(deny_unknown_fields)] -pub(crate) struct Conditional { - pub(crate) selector: T, - pub(crate) condition: Option>>>, +use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; +use crate::plugins::telemetry::config_new::conditions::Condition; +use crate::plugins::telemetry::config_new::DefaultForLevel; +use crate::plugins::telemetry::config_new::Selector; +use crate::plugins::telemetry::otlp::TelemetryDataKind; +/// Conditional is a stateful structure that may be called multiple times during the course of a request/response cycle. +/// As each callback is called the underlying condition is updated. If the condition can eventually be evaluated then it returns +/// Some(true|false) otherwise it returns None. +#[derive(Clone, Debug, Default)] +pub(crate) struct Conditional { + pub(crate) selector: Att, + pub(crate) condition: Option>>>, } impl JsonSchema for Conditional @@ -40,9 +51,9 @@ where } } -impl DefaultForLevel for Conditional +impl DefaultForLevel for Conditional where - T: DefaultForLevel, + Att: DefaultForLevel, { fn defaults_for_level( &mut self, @@ -53,9 +64,9 @@ where } } -impl Selector for Conditional +impl Selector for Conditional where - T: Selector, + Att: Selector, { type Request = Request; type Response = Response; @@ -86,3 +97,165 @@ where } } } + +/// Custom Deserializer for attributes that will deserialize into a custom field if possible, but otherwise into one of the pre-defined attributes. +impl<'de, Att> Deserialize<'de> for Conditional +where + Att: Deserialize<'de> + Debug + Sized, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ConditionalVisitor { + _phantom: std::marker::PhantomData, + } + impl<'de, Att> Visitor<'de> for ConditionalVisitor + where + Att: Deserialize<'de> + Debug, + { + type Value = Conditional; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a map structure") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut selector: Option = None; + let mut condition: Option> = None; + while let Some(key) = map.next_key::()? { + let value: Value = map.next_value()?; + if key == "condition" { + condition = Some( + Condition::::deserialize(value.clone()) + .map_err(|e| Error::custom(e.to_string()))?, + ) + } else { + let mut map = Map::new(); + map.insert(key.clone(), value); + let o = Value::Object(map); + selector = + Some(Att::deserialize(o).map_err(|e| Error::custom(e.to_string()))?) + } + } + if selector.is_none() { + return Err(A::Error::custom("selector is required")); + } + + Ok(Conditional { + selector: selector.expect("selector is required"), + condition: condition.map(|c| Arc::new(Mutex::new(c))), + }) + } + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + Ok(Conditional { + selector: Att::deserialize(Value::String(v.to_string())) + .map_err(|e| Error::custom(e.to_string()))?, + condition: None, + }) + } + } + + deserializer.deserialize_any(ConditionalVisitor:: { + _phantom: Default::default(), + }) + } +} + +#[cfg(test)] +mod test { + use http::StatusCode; + use opentelemetry_api::Value; + + use crate::plugins::telemetry::config_new::selectors::RouterSelector; + use crate::plugins::telemetry::config_new::Selector; + + #[test] + fn test_deserialization_ok_() { + let config = r#" + static: "there was an error" + condition: + any: + - eq: + - response_status: code + - 201 + "#; + + let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); + let result = conditional.on_response( + &crate::services::router::Response::fake_builder() + .status_code(StatusCode::from_u16(201).unwrap()) + .build() + .expect("req"), + ); + //TODO, none always get returned. + assert_eq!( + result.expect("expected result"), + Value::String("there was an error".into()) + ); + } + + #[test] + fn test_deserialization_missing_selector() { + let config = r#" + condition: + any: + - eq: + - response_status: code + - 201 + "#; + + serde_yaml::from_str::>(config) + .expect_err("Could have failed to deserialize"); + } + + #[test] + fn test_deserialization_invalid_selector() { + let config = r#" + invalid: "foo" + condition: + any: + - eq: + - response_status: code + - 201 + "#; + + let result = serde_yaml::from_str::>(config); + assert!(result + .expect_err("should have got error") + .to_string() + .contains("data did not match any variant of untagged enum RouterSelector"),) + } + + #[test] + fn test_deserialization_invalid_condition() { + let config = r#" + static: "foo" + condition: + aaargh: "" + "#; + + let result = serde_yaml::from_str::>(config); + assert!(result + .expect_err("should have got error") + .to_string() + .contains("unknown variant `aaargh`"),) + } + + #[test] + fn test_simple_value() { + let config = r#" + "foo" + "#; + + let result = serde_yaml::from_str::>(config) + .expect("should have parsed"); + assert!(result.condition.is_none()); + assert!(matches!(result.selector, RouterSelector::Static(_))); + } +} diff --git a/apollo-router/src/plugins/telemetry/config_new/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/selectors.rs index c3ed9226aa..02841fdf15 100644 --- a/apollo-router/src/plugins/telemetry/config_new/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/selectors.rs @@ -148,7 +148,7 @@ pub(crate) enum RouterSelector { r#static: String, }, OnGraphQLError { - /// Boolean set to true if the reponse body contains graphql error + /// Boolean set to true if the response body contains graphql error on_graphql_error: bool, }, } From 5978025f93df299e2df121c527d3ae82a3d755d0 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 16:25:34 +0100 Subject: [PATCH 29/40] Fix test. The issue is that you may have a selector on request that has a condition on response. This means stashing the value until you know it can be used. --- .../src/plugins/telemetry/config_new/conditional.rs | 13 +++++++++++-- .../src/plugins/telemetry/config_new/spans.rs | 13 +++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index 8bb9d0c602..282a190c18 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -27,6 +27,7 @@ use crate::plugins::telemetry::otlp::TelemetryDataKind; pub(crate) struct Conditional { pub(crate) selector: Att, pub(crate) condition: Option>>>, + pub(crate) value: Arc>>, } impl JsonSchema for Conditional @@ -72,10 +73,11 @@ where type Response = Response; fn on_request(&self, request: &Self::Request) -> Option { + *self.value.lock() = self.selector.on_request(request); match &self.condition { Some(condition) => { if condition.lock().evaluate_request(request) == Some(true) { - self.selector.on_request(request) + self.value.lock().clone() } else { None } @@ -85,10 +87,15 @@ where } fn on_response(&self, response: &Self::Response) -> Option { + let mut value = self.value.lock().take(); + if value.is_none() { + value = self.selector.on_response(response); + } + match &self.condition { Some(condition) => { if condition.lock().evaluate_response(response) { - self.selector.on_response(response) + value } else { None } @@ -147,6 +154,7 @@ where Ok(Conditional { selector: selector.expect("selector is required"), condition: condition.map(|c| Arc::new(Mutex::new(c))), + value: Arc::new(Default::default()), }) } fn visit_str(self, v: &str) -> Result @@ -157,6 +165,7 @@ where selector: Att::deserialize(Value::String(v.to_string())) .map_err(|e| Error::custom(e.to_string()))?, condition: None, + value: Arc::new(Default::default()), }) } } diff --git a/apollo-router/src/plugins/telemetry/config_new/spans.rs b/apollo-router/src/plugins/telemetry/config_new/spans.rs index 39eb18adf8..4d85f4b9e4 100644 --- a/apollo-router/src/plugins/telemetry/config_new/spans.rs +++ b/apollo-router/src/plugins/telemetry/config_new/spans.rs @@ -355,6 +355,7 @@ mod test { on_graphql_error: true, }), ])))), + value: Arc::new(Default::default()), }, ); let context = Context::new(); @@ -389,6 +390,7 @@ mod test { on_graphql_error: true, }), ])))), + value: Arc::new(Default::default()), }, ); let context = Context::new(); @@ -422,6 +424,7 @@ mod test { on_graphql_error: true, }), ])))), + value: Arc::new(Default::default()), }, ); let context = Context::new(); @@ -454,6 +457,7 @@ mod test { SelectorOrValue::Value(AttributeValue::String("test_val".to_string())), SelectorOrValue::Selector(selector), ])))), + value: Default::default(), }, ); let values = spans.attributes.on_request( @@ -484,6 +488,7 @@ mod test { SelectorOrValue::Value(AttributeValue::String("test_val".to_string())), SelectorOrValue::Selector(selector), ])))), + value: Arc::new(Default::default()), }, ); let values = spans.attributes.on_request( @@ -510,6 +515,7 @@ mod test { default: None, }, condition: None, + value: Arc::new(Default::default()), }, ); let values = spans.attributes.on_request( @@ -536,6 +542,7 @@ mod test { default: None, }, condition: None, + value: Arc::new(Default::default()), }, ); let values = spans.attributes.on_response( @@ -561,6 +568,7 @@ mod test { default: None, }, condition: None, + value: Arc::new(Default::default()), }, ); let values = spans.attributes.on_request( @@ -587,6 +595,7 @@ mod test { default: None, }, condition: None, + value: Arc::new(Default::default()), }, ); let values = spans.attributes.on_response( @@ -612,6 +621,7 @@ mod test { default: None, }, condition: None, + value: Arc::new(Default::default()), }, ); let values = spans.attributes.on_request( @@ -646,6 +656,7 @@ mod test { default: None, }, condition: None, + value: Arc::new(Default::default()), }, ); let values = spans.attributes.on_response( @@ -676,6 +687,7 @@ mod test { subgraph_response_status: ResponseStatus::Code, }), ])))), + value: Arc::new(Default::default()), }, ); let values = spans.attributes.on_response( @@ -707,6 +719,7 @@ mod test { subgraph_response_status: ResponseStatus::Code, }), ])))), + value: Arc::new(Default::default()), }, ); let values = spans.attributes.on_response( From fb8bc4cd52f276b8b1be1ae28c87fd8a86c56398 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 17:33:41 +0100 Subject: [PATCH 30/40] Improve condition logic to avoid computation unless absolutely necessary. Needs more tests. --- .../telemetry/config_new/conditional.rs | 87 +++++++++++++++---- 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index 282a190c18..7c4c2a8e73 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -1,6 +1,7 @@ use std::any::type_name; use std::collections::HashMap; use std::fmt::Debug; +use std::mem; use std::sync::Arc; use parking_lot::Mutex; @@ -20,6 +21,25 @@ use crate::plugins::telemetry::config_new::conditions::Condition; use crate::plugins::telemetry::config_new::DefaultForLevel; use crate::plugins::telemetry::config_new::Selector; use crate::plugins::telemetry::otlp::TelemetryDataKind; + +/// The state of the conditional. +#[derive(Debug, Default)] +pub(crate) enum State { + /// The conditional has not been evaluated yet or no value has been set via selector. + #[default] + Pending, + /// The conditional has been evaluated and the value has been obtained. + Value(T), + /// The conditional has been evaluated and the value has been returned, no further processing should take place. + Returned, +} + +impl From for State { + fn from(value: T) -> Self { + State::Value(value) + } +} + /// Conditional is a stateful structure that may be called multiple times during the course of a request/response cycle. /// As each callback is called the underlying condition is updated. If the condition can eventually be evaluated then it returns /// Some(true|false) otherwise it returns None. @@ -27,7 +47,7 @@ use crate::plugins::telemetry::otlp::TelemetryDataKind; pub(crate) struct Conditional { pub(crate) selector: Att, pub(crate) condition: Option>>>, - pub(crate) value: Arc>>, + pub(crate) value: Arc>>, } impl JsonSchema for Conditional @@ -73,34 +93,70 @@ where type Response = Response; fn on_request(&self, request: &Self::Request) -> Option { - *self.value.lock() = self.selector.on_request(request); match &self.condition { - Some(condition) => { - if condition.lock().evaluate_request(request) == Some(true) { - self.value.lock().clone() - } else { + Some(condition) => match condition.lock().evaluate_request(request) { + None => { + if let Some(value) = self.selector.on_request(request) { + *self.value.lock() = value.into(); + } + None + } + Some(true) => { + // The condition evaluated to true, so we can just return the value but may need to try again on the response. + match self.selector.on_request(request) { + None => None, + Some(value) => { + *self.value.lock() = State::Returned; + Some(value) + } + } + } + Some(false) => { + // The condition has been evaluated to false, so we can return None. it will never return true. + *self.value.lock() = State::Returned; None } + }, + None => { + // There is no condition to evaluate, so we can just return the value. + match self.selector.on_request(request) { + None => None, + Some(value) => { + *self.value.lock() = State::Returned; + Some(value) + } + } } - None => self.selector.on_request(request), } } fn on_response(&self, response: &Self::Response) -> Option { - let mut value = self.value.lock().take(); - if value.is_none() { - value = self.selector.on_response(response); - } + // We may have got the value from the request. + let value = mem::take(&mut *self.value.lock()); - match &self.condition { - Some(condition) => { + match (value, &self.condition) { + (State::Value(value), Some(condition)) => { + // We have a value already, let's see if the condition was evaluated to true. if condition.lock().evaluate_response(response) { - value + *self.value.lock() = State::Returned; + Some(value) } else { None } } - None => self.selector.on_response(response), + (State::Pending, Some(condition)) => { + // We don't have a value already, let's try to get it from the response if the condition was evaluated to true. + if condition.lock().evaluate_response(response) { + self.selector.on_response(response) + } else { + None + } + } + (State::Pending, None) => { + // We don't have a value already, and there is no condition. + self.selector.on_response(response) + } + _ => None, } } } @@ -202,7 +258,6 @@ mod test { .build() .expect("req"), ); - //TODO, none always get returned. assert_eq!( result.expect("expected result"), Value::String("there was an error".into()) From 875036b9dcf981a82ea1ea0b00296a7ba4620739 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 17:58:21 +0100 Subject: [PATCH 31/40] Add tests --- .../telemetry/config_new/conditional.rs | 100 +++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index 7c4c2a8e73..a5e25c2f15 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -237,11 +237,109 @@ mod test { use http::StatusCode; use opentelemetry_api::Value; + use crate::plugins::telemetry::config_new::conditional::Conditional; use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::Selector; + fn on_response(conditional: Conditional) -> Option { + conditional.on_response( + &crate::services::router::Response::fake_builder() + .status_code(StatusCode::from_u16(201).unwrap()) + .build() + .expect("resp"), + ) + } + + fn on_request(conditional: &Conditional) -> Option { + conditional.on_request( + &crate::services::router::Request::fake_builder() + .header("head", "val") + .build() + .expect("req"), + ) + } + + #[test] + fn test_value_from_response_condition_from_request() { + let config = r#" + response_status: code + condition: + any: + - eq: + - request_header: head + - "val" + "#; + + let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); + let result = on_request(&conditional); + assert!(result.is_none()); + let result = on_response(conditional); + assert_eq!(result.expect("expected result"), Value::I64(201)); + } + + #[test] + fn test_value_from_request_condition_from_response() { + let config = r#" + request_header: head + condition: + any: + - eq: + - response_status: code + - 201 + "#; + + let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); + let result = on_request(&conditional); + assert!(result.is_none()); + let result = on_response(conditional); + assert_eq!( + result.expect("expected result"), + Value::String("val".into()) + ); + } + + #[test] + fn test_value_from_request_condition_from_request() { + let config = r#" + request_header: head + condition: + any: + - eq: + - request_header: head + - val + "#; + + let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); + let result = on_request(&conditional); + assert_eq!( + result.expect("expected result"), + Value::String("val".into()) + ); + + let result = on_response(conditional); + assert!(result.is_none()); + } + + #[test] + fn test_value_from_response_condition_from_response() { + let config = r#" + response_status: code + condition: + any: + - eq: + - response_status: code + - 201 + "#; + + let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); + let result = on_request(&conditional); + assert!(result.is_none()); + let result = on_response(conditional); + assert_eq!(result.expect("expected result"), Value::I64(201)); + } + #[test] - fn test_deserialization_ok_() { + fn test_deserialization() { let config = r#" static: "there was an error" condition: From da40912b4cab383da992f705411d4193a1499c95 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 18:29:43 +0100 Subject: [PATCH 32/40] Schema update --- ...nfiguration__tests__schema_generation.snap | 8516 ++++++++--------- 1 file changed, 4258 insertions(+), 4258 deletions(-) diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index bf4f1f89a6..ec01e301f7 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -7410,7 +7410,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -7900,7 +7900,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -8331,7 +8331,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -13607,7 +13607,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -14187,7 +14187,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -14767,7 +14767,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -15342,7 +15342,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -15832,7 +15832,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -16263,7 +16263,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -16780,7 +16780,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -25492,2302 +25492,1447 @@ expression: "&schema" }, "additionalProperties": { "type": "object", - "anyOf": [ - { - "description": "A header from the request", - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "The request method.", - "type": "object", - "required": [ - "request_method" - ], - "properties": { - "request_method": { - "description": "The request method enabled or not", - "type": "boolean" - } - }, - "additionalProperties": false - }, - { - "description": "A header from the response", - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "response_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "description": "The trace ID of the request.", - "type": "object", - "required": [ - "trace_id" - ], - "properties": { - "trace_id": { - "description": "The format of the trace ID.", - "oneOf": [ - { - "description": "Open Telemetry trace ID, a hex string.", - "type": "string", - "enum": [ - "open_telemetry" - ] - }, - { - "description": "Datadog trace ID, a u64.", - "type": "string", - "enum": [ - "datadog" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "description": "A value from context.", - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A value from baggage.", - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "description": "A value from an environment variable.", - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "on_graphql_error" - ], - "properties": { - "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", - "type": "boolean" - } - }, - "additionalProperties": false - } - ], - "properties": { - "condition": { - "oneOf": [ - { - "description": "A condition to check a selection against a value.", - "type": "object", - "required": [ - "eq" - ], - "properties": { - "eq": { - "type": "array", - "items": { - "anyOf": [ - { - "description": "A constant value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ] - }, - { - "description": "Selector to extract a value from the pipeline.", - "anyOf": [ - { - "description": "A header from the request", - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "The request method.", - "type": "object", - "required": [ - "request_method" - ], - "properties": { - "request_method": { - "description": "The request method enabled or not", - "type": "boolean" - } - }, - "additionalProperties": false - }, - { - "description": "A header from the response", - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "response_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "description": "The trace ID of the request.", - "type": "object", - "required": [ - "trace_id" - ], - "properties": { - "trace_id": { - "description": "The format of the trace ID.", - "oneOf": [ - { - "description": "Open Telemetry trace ID, a hex string.", - "type": "string", - "enum": [ - "open_telemetry" - ] - }, - { - "description": "Datadog trace ID, a u64.", - "type": "string", - "enum": [ - "datadog" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "description": "A value from context.", - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A value from baggage.", - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "description": "A value from an environment variable.", - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "on_graphql_error" - ], - "properties": { - "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", - "type": "boolean" - } - }, - "additionalProperties": false - } - ] - } - ] - }, - "maxItems": 2, - "minItems": 2 - } - }, - "additionalProperties": false - }, + "properties": { + "condition": { + "oneOf": [ { - "description": "A condition to check a selection against a selector.", + "description": "A condition to check a selection against a value.", "type": "object", "required": [ - "exists" - ], - "properties": { - "exists": { - "anyOf": [ - { - "description": "A header from the request", - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } - ] - } - ], - "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "The request method.", - "type": "object", - "required": [ - "request_method" - ], - "properties": { - "request_method": { - "description": "The request method enabled or not", - "type": "boolean" - } - }, - "additionalProperties": false - }, - { - "description": "A header from the response", - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } - ] - } - ], - "nullable": true - }, - "response_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "description": "The trace ID of the request.", - "type": "object", - "required": [ - "trace_id" - ], - "properties": { - "trace_id": { - "description": "The format of the trace ID.", - "oneOf": [ - { - "description": "Open Telemetry trace ID, a hex string.", - "type": "string", - "enum": [ - "open_telemetry" - ] - }, - { - "description": "Datadog trace ID, a u64.", - "type": "string", - "enum": [ - "datadog" - ] - } - ] - } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] }, - "additionalProperties": false - }, - { - "description": "A value from context.", - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } }, - { - "description": "String values", - "type": "string" + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { + }, + { + "description": "i64 values", "type": "integer", "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { + }, + { + "description": "f64 values", "type": "number", "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { + }, + { + "description": "String values", "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - } - ] - } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A value from baggage.", - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } }, - { - "description": "f64 values", - "type": "number", - "format": "double" + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } }, - { - "description": "String values", - "type": "string" + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { + }, + { + "description": "i64 values", "type": "integer", "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { + }, + { + "description": "f64 values", "type": "number", "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { + }, + { + "description": "String values", "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - } - ] - } - ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "description": "A value from an environment variable.", - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "on_graphql_error" - ], - "properties": { - "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", - "type": "boolean" - } - }, - "additionalProperties": false - } - ] - } - }, - "additionalProperties": false - }, - { - "description": "All sub-conditions must be true.", - "type": "object", - "required": [ - "all" - ], - "properties": { - "all": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_RouterSelector" - } - } - }, - "additionalProperties": false - }, - { - "description": "At least one sub-conditions must be true.", - "type": "object", - "required": [ - "any" - ], - "properties": { - "any": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_RouterSelector" - } - } - }, - "additionalProperties": false - }, - { - "description": "The sub-condition must not be true", - "type": "object", - "required": [ - "not" - ], - "properties": { - "not": { - "$ref": "#/definitions/Condition_for_RouterSelector" + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the response body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 } }, "additionalProperties": false }, - { - "description": "Static true condition", - "type": "string", - "enum": [ - "true" - ] - }, - { - "description": "Static false condition", - "type": "string", - "enum": [ - "false" - ] - } - ], - "nullable": true - } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false - }, - "subgraph": { - "description": "Attributes to include on the subgraph span. Subgraph spans contain information about the subgraph request and response and therefore contain subgraph specific attributes.", - "type": "object", - "properties": { - "attributes": { - "description": "Custom attributes that are attached to the subgraph span.", - "type": "object", - "properties": { - "subgraph.graphql.document": { - "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "subgraph.graphql.operation.name": { - "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "subgraph.graphql.operation.type": { - "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "subgraph.name": { - "description": "The name of the subgraph Examples: * products Requirement level: Required", - "default": null, - "type": "boolean", - "nullable": true - } - }, - "additionalProperties": { - "type": "object", - "anyOf": [ - { - "type": "object", - "required": [ - "subgraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_operation_name": { - "description": "The operation name from the subgraph query.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_operation_kind" - ], - "properties": { - "subgraph_operation_kind": { - "description": "The kind of the subgraph operation (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_query": { - "description": "The graphql query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_query_variable": { - "description": "The name of a subgraph query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", - "type": "object", - "required": [ - "subgraph_response_body" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_response_body": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_data" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_response_data": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_errors" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "type": "string" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the response body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } ] } - ], - "nullable": true - }, - "subgraph_response_errors": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_request_header": { - "description": "The name of a subgraph request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "subgraph_response_header": { - "description": "The name of a subgraph response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_status" - ], - "properties": { - "subgraph_response_status": { - "description": "The subgraph http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "supergraph_operation_name": { - "description": "The supergraph query operation name.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_kind" - ], - "properties": { - "supergraph_operation_kind": { - "description": "The supergraph query operation kind (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_RouterSelector" } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", + }, + "additionalProperties": false + }, + { + "description": "Static true condition", "type": "string", - "nullable": true + "enum": [ + "true" + ] }, - "supergraph_query": { - "description": "The supergraph query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" ] } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" + ] + } + }, + "additionalProperties": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - } - ] - } - ], - "nullable": true + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } }, - "supergraph_query_variable": { - "description": "The supergraph query variable name.", - "type": "string" - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } }, - "supergraph_request_header": { - "description": "The supergraph request header name.", - "type": "string" - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - } - ] - } - ], - "nullable": true + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } }, - "request_context": { - "description": "The request context key.", - "type": "string" - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - } - ] - } - ], - "nullable": true + ] + } + ], + "nullable": true + } }, - "response_context": { - "description": "The response context key.", - "type": "string" - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + { + "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", - "type": "string" - } + "additionalProperties": false }, - "additionalProperties": false - } - ], + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the response body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "additionalProperties": false + }, + "subgraph": { + "description": "Attributes to include on the subgraph span. Subgraph spans contain information about the subgraph request and response and therefore contain subgraph specific attributes.", + "type": "object", + "properties": { + "attributes": { + "description": "Custom attributes that are attached to the subgraph span.", + "type": "object", + "properties": { + "subgraph.graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.name": { + "description": "The name of the subgraph Examples: * products Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "type": "object", "properties": { "condition": { "oneOf": [ @@ -28345,7 +27490,195 @@ expression: "&schema" "string" ] } - ] + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" } }, "additionalProperties": false @@ -28353,25 +27686,71 @@ expression: "&schema" { "type": "object", "required": [ - "supergraph_query" + "response_context" ], "properties": { "default": { "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "supergraph_query": { - "description": "The supergraph query to the subgraph.", - "oneOf": [ + "anyOf": [ { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } ] } - ] + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" } }, "additionalProperties": false @@ -28379,9 +27758,13 @@ expression: "&schema" { "type": "object", "required": [ - "supergraph_query_variable" + "baggage" ], "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, "default": { "description": "Optional default value.", "anyOf": [ @@ -28440,10 +27823,6 @@ expression: "&schema" } ], "nullable": true - }, - "supergraph_query_variable": { - "description": "The supergraph query variable name.", - "type": "string" } }, "additionalProperties": false @@ -28451,7 +27830,7 @@ expression: "&schema" { "type": "object", "required": [ - "supergraph_request_header" + "env" ], "properties": { "default": { @@ -28459,286 +27838,485 @@ expression: "&schema" "type": "string", "nullable": true }, - "supergraph_request_header": { - "description": "The supergraph request header name.", + "env": { + "description": "The name of the environment variable", "type": "string" } }, "additionalProperties": false }, + { + "type": "string" + }, { "type": "object", "required": [ - "request_context" + "static" ], "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { "type": "boolean" - }, - { - "description": "i64 values", + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { "type": "number", "format": "double" - }, - { - "description": "String values", + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" - } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { "type": "boolean" - }, - { - "description": "i64 values", + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { "type": "integer", "format": "int64" - }, - { - "description": "f64 values", + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { "type": "number", "format": "double" - }, - { - "description": "String values", + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { "type": "boolean" - }, - { - "description": "i64 values", + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { "type": "integer", "format": "int64" - }, - { - "description": "f64 values", + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { "type": "number", "format": "double" - }, - { - "description": "String values", + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] } - ], - "nullable": true - } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } + { + "description": "i64 values", + "type": "integer", + "format": "int64" }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", - "type": "string" - } + { + "description": "f64 values", + "type": "number", + "format": "double" }, - "additionalProperties": false - } - ] - } - ] - }, - "maxItems": 2, - "minItems": 2 - } - }, - "additionalProperties": false - }, - { - "description": "A condition to check a selection against a selector.", - "type": "object", - "required": [ - "exists" - ], - "properties": { - "exists": { - "anyOf": [ + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ - "subgraph_operation_name" + "supergraph_operation_name" ], "properties": { "default": { @@ -28746,8 +28324,8 @@ expression: "&schema" "type": "string", "nullable": true }, - "subgraph_operation_name": { - "description": "The operation name from the subgraph query.", + "supergraph_operation_name": { + "description": "The supergraph query operation name.", "oneOf": [ { "description": "The raw operation name.", @@ -28771,11 +28349,11 @@ expression: "&schema" { "type": "object", "required": [ - "subgraph_operation_kind" + "supergraph_operation_kind" ], "properties": { - "subgraph_operation_kind": { - "description": "The kind of the subgraph operation (query|mutation|subscription).", + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", "oneOf": [ { "description": "The raw operation kind.", @@ -28792,7 +28370,7 @@ expression: "&schema" { "type": "object", "required": [ - "subgraph_query" + "supergraph_query" ], "properties": { "default": { @@ -28800,8 +28378,8 @@ expression: "&schema" "type": "string", "nullable": true }, - "subgraph_query": { - "description": "The graphql query to the subgraph.", + "supergraph_query": { + "description": "The supergraph query to the subgraph.", "oneOf": [ { "description": "The raw query kind.", @@ -28818,7 +28396,7 @@ expression: "&schema" { "type": "object", "required": [ - "subgraph_query_variable" + "supergraph_query_variable" ], "properties": { "default": { @@ -28880,18 +28458,35 @@ expression: "&schema" ], "nullable": true }, - "subgraph_query_variable": { - "description": "The name of a subgraph query variable.", + "supergraph_query_variable": { + "description": "The supergraph query variable name.", "type": "string" } }, "additionalProperties": false }, { - "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", "type": "object", "required": [ - "subgraph_response_body" + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" ], "properties": { "default": { @@ -28953,8 +28548,8 @@ expression: "&schema" ], "nullable": true }, - "subgraph_response_body": { - "description": "The subgraph response body json path.", + "request_context": { + "description": "The request context key.", "type": "string" } }, @@ -28963,7 +28558,7 @@ expression: "&schema" { "type": "object", "required": [ - "subgraph_response_data" + "response_context" ], "properties": { "default": { @@ -29025,8 +28620,8 @@ expression: "&schema" ], "nullable": true }, - "subgraph_response_data": { - "description": "The subgraph response body json path.", + "response_context": { + "description": "The response context key.", "type": "string" } }, @@ -29035,9 +28630,13 @@ expression: "&schema" { "type": "object", "required": [ - "subgraph_response_errors" + "baggage" ], "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, "default": { "description": "Optional default value.", "anyOf": [ @@ -29096,10 +28695,6 @@ expression: "&schema" } ], "nullable": true - }, - "subgraph_response_errors": { - "description": "The subgraph response body json path.", - "type": "string" } }, "additionalProperties": false @@ -29107,7 +28702,7 @@ expression: "&schema" { "type": "object", "required": [ - "subgraph_request_header" + "env" ], "properties": { "default": { @@ -29115,1051 +28710,987 @@ expression: "&schema" "type": "string", "nullable": true }, - "subgraph_request_header": { - "description": "The name of a subgraph request header.", + "env": { + "description": "The name of the environment variable", "type": "string" } }, "additionalProperties": false }, { - "type": "object", - "required": [ - "subgraph_response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_response_header": { - "description": "The name of a subgraph response header.", - "type": "string" - } - }, - "additionalProperties": false + "type": "string" }, { "type": "object", "required": [ - "subgraph_response_status" + "static" ], "properties": { - "subgraph_response_status": { - "description": "The subgraph http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] + "static": { + "description": "A static string value", + "type": "string" } }, "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "supergraph_operation_name": { - "description": "The supergraph query operation name.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_kind" - ], - "properties": { - "supergraph_operation_kind": { - "description": "The supergraph query operation kind (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "supergraph_query": { - "description": "The supergraph query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "supergraph_query_variable": { - "description": "The supergraph query variable name.", + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "supergraph_request_header": { - "description": "The supergraph request header name.", - "type": "string" + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", - "type": "string" + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - } - ] - } + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true }, - "additionalProperties": false + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } }, - { - "description": "All sub-conditions must be true.", - "type": "object", - "required": [ - "all" - ], - "properties": { - "all": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_SubgraphSelector" + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - } + ], + "nullable": true }, - "additionalProperties": false + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } }, - { - "description": "At least one sub-conditions must be true.", - "type": "object", - "required": [ - "any" - ], - "properties": { - "any": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_SubgraphSelector" - } - } + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - "additionalProperties": false + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } }, - { - "description": "The sub-condition must not be true", - "type": "object", - "required": [ - "not" - ], - "properties": { - "not": { - "$ref": "#/definitions/Condition_for_SubgraphSelector" - } + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - "additionalProperties": false + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } }, - { - "description": "Static true condition", - "type": "string", - "enum": [ - "true" - ] + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } }, - { - "description": "Static false condition", - "type": "string", - "enum": [ - "false" - ] - } - ], - "nullable": true - } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false - }, - "supergraph": { - "description": "Configuration of supergraph spans. Supergraph spans contain information about the graphql request and response and therefore contain graphql specific attributes.", - "type": "object", - "properties": { - "attributes": { - "description": "Custom attributes that are attached to the supergraph span.", - "type": "object", - "properties": { - "graphql.document": { - "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "graphql.operation.name": { - "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "graphql.operation.type": { - "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - } - }, - "additionalProperties": { - "type": "object", - "anyOf": [ - { - "type": "object", - "required": [ - "operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } }, - "operation_name": { - "description": "The operation name from the query.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "operation_kind" - ], - "properties": { - "operation_kind": { - "description": "The operation kind from the query (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] - } + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } }, - "query": { - "description": "The graphql query.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - } - ] - } - ], - "nullable": true - }, - "query_variable": { - "description": "The name of a graphql query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } }, - "response_header": { - "description": "The name of the response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } }, - "request_context": { - "description": "The request context key.", - "type": "string" - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - } - ] - } - ], - "nullable": true + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } }, - "response_context": { - "description": "The response context key.", - "type": "string" - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - } - ] - } - ], - "nullable": true - } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } + "additionalProperties": false }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", - "type": "string" - } + { + "type": "string" }, - "additionalProperties": false - } - ], + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "additionalProperties": false + }, + "supergraph": { + "description": "Configuration of supergraph spans. Supergraph spans contain information about the graphql request and response and therefore contain graphql specific attributes.", + "type": "object", + "properties": { + "attributes": { + "description": "Custom attributes that are attached to the supergraph span.", + "type": "object", + "properties": { + "graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "type": "object", "properties": { "condition": { "oneOf": [ @@ -30310,7 +29841,216 @@ expression: "&schema" "string" ] } - ] + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" } }, "additionalProperties": false @@ -30318,7 +30058,7 @@ expression: "&schema" { "type": "object", "required": [ - "query_variable" + "response_context" ], "properties": { "default": { @@ -30380,8 +30120,8 @@ expression: "&schema" ], "nullable": true }, - "query_variable": { - "description": "The name of a graphql query variable.", + "response_context": { + "description": "The response context key.", "type": "string" } }, @@ -30390,17 +30130,71 @@ expression: "&schema" { "type": "object", "required": [ - "request_header" + "baggage" ], "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, "default": { "description": "Optional default value.", - "type": "string", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" } }, "additionalProperties": false @@ -30408,7 +30202,7 @@ expression: "&schema" { "type": "object", "required": [ - "response_header" + "env" ], "properties": { "default": { @@ -30416,315 +30210,485 @@ expression: "&schema" "type": "string", "nullable": true }, - "response_header": { - "description": "The name of the response header.", + "env": { + "description": "The name of the environment variable", "type": "string" } }, "additionalProperties": false }, { - "description": "A status from the response", + "type": "string" + }, + { "type": "object", "required": [ - "response_status" + "static" ], "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] + "static": { + "description": "A static string value", + "type": "string" } }, "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { "type": "boolean" - }, - { - "description": "i64 values", + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { "type": "integer", "format": "int64" - }, - { - "description": "f64 values", + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { "type": "number", "format": "double" - }, - { - "description": "String values", + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" - } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { "type": "boolean" - }, - { - "description": "i64 values", + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { "type": "integer", "format": "int64" - }, - { - "description": "f64 values", + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { "type": "number", "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { "type": "boolean" - }, - { - "description": "i64 values", + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { "type": "integer", "format": "int64" - }, - { - "description": "f64 values", + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { "type": "number", "format": "double" - }, - { - "description": "String values", + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] } - ], - "nullable": true - } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } + { + "description": "i64 values", + "type": "integer", + "format": "int64" }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", - "type": "string" - } + { + "description": "f64 values", + "type": "number", + "format": "double" }, - "additionalProperties": false - } - ] - } - ] - }, - "maxItems": 2, - "minItems": 2 - } - }, - "additionalProperties": false - }, - { - "description": "A condition to check a selection against a selector.", - "type": "object", - "required": [ - "exists" - ], - "properties": { - "exists": { - "anyOf": [ + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ - "operation_name" + "env" ], "properties": { "default": { @@ -30732,531 +30696,567 @@ expression: "&schema" "type": "string", "nullable": true }, - "operation_name": { - "description": "The operation name from the query.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] + "env": { + "description": "The name of the environment variable", + "type": "string" } }, "additionalProperties": false }, + { + "type": "string" + }, { "type": "object", "required": [ - "operation_kind" + "static" ], "properties": { - "operation_kind": { - "description": "The operation kind from the query (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + "static": { + "description": "A static string value", + "type": "string" } }, "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "query": { - "description": "The graphql query.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "query_variable": { - "description": "The name of a graphql query variable.", - "type": "string" + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "response_header": { - "description": "The name of the response header.", + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", - "type": "string" + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - } - ] - } - }, - "additionalProperties": false - }, - { - "description": "All sub-conditions must be true.", - "type": "object", - "required": [ - "all" - ], - "properties": { - "all": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_SupergraphSelector" + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - } + ], + "nullable": true }, - "additionalProperties": false + "response_context": { + "description": "The response context key.", + "type": "string" + } }, - { - "description": "At least one sub-conditions must be true.", - "type": "object", - "required": [ - "any" - ], - "properties": { - "any": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_SupergraphSelector" - } - } + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" }, - "additionalProperties": false + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } }, - { - "description": "The sub-condition must not be true", - "type": "object", - "required": [ - "not" - ], - "properties": { - "not": { - "$ref": "#/definitions/Condition_for_SupergraphSelector" - } + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - "additionalProperties": false + "env": { + "description": "The name of the environment variable", + "type": "string" + } }, - { - "description": "Static true condition", - "type": "string", - "enum": [ - "true" - ] + "additionalProperties": false + }, + { + "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } }, - { - "description": "Static false condition", - "type": "string", - "enum": [ - "false" - ] - } - ], - "nullable": true - } - }, - "additionalProperties": false + "additionalProperties": false + } + ] + } } } }, @@ -32208,7 +32208,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, @@ -32639,7 +32639,7 @@ expression: "&schema" ], "properties": { "on_graphql_error": { - "description": "Boolean set to true if the reponse body contains graphql error", + "description": "Boolean set to true if the response body contains graphql error", "type": "boolean" } }, From 8e99b394c59e637dcbd7929464375d724786fcba Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 18:43:05 +0100 Subject: [PATCH 33/40] Add more tests --- .../telemetry/config_new/conditional.rs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index a5e25c2f15..4f70ce3e58 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -338,6 +338,76 @@ mod test { assert_eq!(result.expect("expected result"), Value::I64(201)); } + #[test] + fn test_response_condition_from_request_fail() { + let config = r#" + response_status: code + condition: + any: + - eq: + - request_header: head + - 999 + "#; + + let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); + let result = on_request(&conditional); + assert!(result.is_none()); + let result = on_response(conditional); + assert!(result.is_none()); + } + #[test] + fn test_response_condition_from_response_fail() { + let config = r#" + response_status: code + condition: + any: + - eq: + - response_status: code + - 999 + "#; + + let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); + let result = on_request(&conditional); + assert!(result.is_none()); + let result = on_response(conditional); + assert!(result.is_none()); + } + + #[test] + fn test_request_condition_from_request_fail() { + let config = r#" + request_header: head + condition: + any: + - eq: + - request_header: head + - 999 + "#; + + let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); + let result = on_request(&conditional); + assert!(result.is_none()); + let result = on_response(conditional); + assert!(result.is_none()); + } + #[test] + fn test_request_condition_from_response_fail() { + let config = r#" + request_header: head + condition: + any: + - eq: + - response_status: code + - 999 + "#; + + let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); + let result = on_request(&conditional); + assert!(result.is_none()); + let result = on_response(conditional); + assert!(result.is_none()); + } + #[test] fn test_deserialization() { let config = r#" From 58f9b176b68843fe352b0148ab754142e3558be0 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 19:45:04 +0100 Subject: [PATCH 34/40] Allow non-object selectors --- ...nfiguration__tests__schema_generation.snap | 9937 +++++++++-------- .../telemetry/config_new/conditional.rs | 30 +- 2 files changed, 5006 insertions(+), 4961 deletions(-) diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index ec01e301f7..d610c5ed0b 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -25491,1408 +25491,1415 @@ expression: "&schema" } }, "additionalProperties": { - "type": "object", - "properties": { - "condition": { - "oneOf": [ - { - "description": "A condition to check a selection against a value.", - "type": "object", - "required": [ - "eq" - ], - "properties": { - "eq": { - "type": "array", - "items": { - "anyOf": [ - { - "description": "A constant value.", + "oneOf": [ + { + "type": "object", + "properties": { + "condition": { + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { "anyOf": [ { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + "description": "A constant value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ] - }, - { - "description": "Selector to extract a value from the pipeline.", - "anyOf": [ - { - "description": "A header from the request", - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", "anyOf": [ { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } }, { - "description": "String values", - "type": "string" + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } }, { - "description": "Array of homogeneous values", + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" } - ], - "nullable": true + }, + "additionalProperties": false }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "The request method.", - "type": "object", - "required": [ - "request_method" - ], - "properties": { - "request_method": { - "description": "The request method enabled or not", - "type": "boolean" - } - }, - "additionalProperties": false - }, - { - "description": "A header from the response", - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" } - ], - "nullable": true + }, + "additionalProperties": false }, - "response_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "description": "The trace ID of the request.", - "type": "object", - "required": [ - "trace_id" - ], - "properties": { - "trace_id": { - "description": "The format of the trace ID.", - "oneOf": [ - { - "description": "Open Telemetry trace ID, a hex string.", - "type": "string", - "enum": [ - "open_telemetry" - ] - }, - { - "description": "Datadog trace ID, a u64.", - "type": "string", - "enum": [ - "datadog" + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "description": "A value from context.", - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A value from baggage.", - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + }, + "additionalProperties": false }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", "type": "string" }, - { - "description": "Array of homogeneous values", + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "description": "A value from an environment variable.", - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", + { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the response body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "on_graphql_error" - ], - "properties": { - "on_graphql_error": { - "description": "Boolean set to true if the response body contains graphql error", - "type": "boolean" - } - }, - "additionalProperties": false + ] } ] - } - ] + }, + "maxItems": 2, + "minItems": 2 + } }, - "maxItems": 2, - "minItems": 2 - } - }, - "additionalProperties": false - }, - { - "description": "A condition to check a selection against a selector.", - "type": "object", - "required": [ - "exists" - ], - "properties": { - "exists": { - "anyOf": [ - { - "description": "A header from the request", - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" } - ], - "nullable": true + }, + "additionalProperties": false }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "The request method.", - "type": "object", - "required": [ - "request_method" - ], - "properties": { - "request_method": { - "description": "The request method enabled or not", - "type": "boolean" - } - }, - "additionalProperties": false - }, - { - "description": "A header from the response", - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" } - ], - "nullable": true + }, + "additionalProperties": false }, - "response_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "description": "The trace ID of the request.", - "type": "object", - "required": [ - "trace_id" - ], - "properties": { - "trace_id": { - "description": "The format of the trace ID.", - "oneOf": [ - { - "description": "Open Telemetry trace ID, a hex string.", - "type": "string", - "enum": [ - "open_telemetry" - ] - }, - { - "description": "Datadog trace ID, a u64.", - "type": "string", - "enum": [ - "datadog" + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "description": "A value from context.", - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A value from baggage.", - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + }, + "additionalProperties": false }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", "type": "string" }, - { - "description": "Array of homogeneous values", + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "description": "A value from an environment variable.", - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the response body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "on_graphql_error" - ], - "properties": { - "on_graphql_error": { - "description": "Boolean set to true if the response body contains graphql error", - "type": "boolean" - } - }, - "additionalProperties": false + ] } - ] - } - }, - "additionalProperties": false - }, - { - "description": "All sub-conditions must be true.", - "type": "object", - "required": [ - "all" - ], - "properties": { - "all": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_RouterSelector" - } - } - }, - "additionalProperties": false - }, - { - "description": "At least one sub-conditions must be true.", - "type": "object", - "required": [ - "any" - ], - "properties": { - "any": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_RouterSelector" - } - } - }, - "additionalProperties": false - }, - { - "description": "The sub-condition must not be true", - "type": "object", - "required": [ - "not" - ], - "properties": { - "not": { - "$ref": "#/definitions/Condition_for_RouterSelector" - } - }, - "additionalProperties": false - }, - { - "description": "Static true condition", - "type": "string", - "enum": [ - "true" - ] - }, - { - "description": "Static false condition", - "type": "string", - "enum": [ - "false" - ] - } - ] - } - }, - "additionalProperties": { - "anyOf": [ - { - "description": "A header from the request", - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } }, - { - "description": "f64 values", - "type": "number", - "format": "double" + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } }, - { - "description": "String values", - "type": "string" + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } }, - { - "description": "Array of homogeneous values", + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + } + }, + "additionalProperties": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" } - ], - "nullable": true + }, + "additionalProperties": false }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "The request method.", - "type": "object", - "required": [ - "request_method" - ], - "properties": { - "request_method": { - "description": "The request method enabled or not", - "type": "boolean" - } - }, - "additionalProperties": false - }, - { - "description": "A header from the response", - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", + "response_header": { + "description": "The name of the request header.", "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] } ] } - ], - "nullable": true + }, + "additionalProperties": false }, - "response_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "description": "The trace ID of the request.", - "type": "object", - "required": [ - "trace_id" - ], - "properties": { - "trace_id": { - "description": "The format of the trace ID.", - "oneOf": [ - { - "description": "Open Telemetry trace ID, a hex string.", - "type": "string", - "enum": [ - "open_telemetry" - ] - }, - { - "description": "Datadog trace ID, a u64.", - "type": "string", - "enum": [ - "datadog" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "description": "A value from context.", - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A value from baggage.", - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", "type": "string" }, - { - "description": "Array of homogeneous values", + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "description": "A value from an environment variable.", - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", + { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "on_graphql_error" + ], + "properties": { + "on_graphql_error": { + "description": "Boolean set to true if the response body contains graphql error", + "type": "boolean" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "on_graphql_error" - ], - "properties": { - "on_graphql_error": { - "description": "Boolean set to true if the response body contains graphql error", - "type": "boolean" - } - }, - "additionalProperties": false + ] } - ] - } + }, + { + "type": "string" + } + ] } } }, @@ -26932,2731 +26939,2738 @@ expression: "&schema" } }, "additionalProperties": { - "type": "object", - "properties": { - "condition": { - "oneOf": [ - { - "description": "A condition to check a selection against a value.", - "type": "object", - "required": [ - "eq" - ], - "properties": { - "eq": { - "type": "array", - "items": { - "anyOf": [ - { - "description": "A constant value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ] - }, - { - "description": "Selector to extract a value from the pipeline.", + "oneOf": [ + { + "type": "object", + "properties": { + "condition": { + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { "anyOf": [ { - "type": "object", - "required": [ - "subgraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" }, - "subgraph_operation_name": { - "description": "The operation name from the subgraph query.", - "oneOf": [ + { + "description": "Array of homogeneous values", + "anyOf": [ { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_operation_kind" - ], - "properties": { - "subgraph_operation_kind": { - "description": "The kind of the subgraph operation (query|mutation|subscription).", - "oneOf": [ + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_query": { - "description": "The graphql query to the subgraph.", - "oneOf": [ + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } ] } - }, - "additionalProperties": false + ] }, { - "type": "object", - "required": [ - "subgraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "Array of homogeneous values", - "anyOf": [ + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] } ] } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" ], - "nullable": true + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false }, - "subgraph_query_variable": { - "description": "The name of a subgraph query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", - "type": "object", - "required": [ - "subgraph_response_body" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "Array of homogeneous values", + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "subgraph_response_body": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_data" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "subgraph_response_data": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_errors" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "subgraph_response_body": { + "description": "The subgraph response body json path.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" } - ], - "nullable": true - }, - "subgraph_response_errors": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_request_header": { - "description": "The name of a subgraph request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "subgraph_response_header": { - "description": "The name of a subgraph response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_status" - ], - "properties": { - "subgraph_response_status": { - "description": "The subgraph http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "supergraph_operation_name": { - "description": "The supergraph query operation name.", - "oneOf": [ - { - "description": "The raw operation name.", + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" - ] + "nullable": true }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_kind" - ], - "properties": { - "supergraph_operation_kind": { - "description": "The supergraph query operation kind (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "supergraph_query": { - "description": "The supergraph query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "String values", + "subgraph_response_header": { + "description": "The name of a subgraph response header.", "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] } ] } - ], - "nullable": true + }, + "additionalProperties": false }, - "supergraph_query_variable": { - "description": "The supergraph query variable name.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false }, - "supergraph_request_header": { - "description": "The supergraph request header name.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "Array of homogeneous values", + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" } - ], - "nullable": true + }, + "additionalProperties": false }, - "request_context": { - "description": "The request context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "String values", + "supergraph_request_header": { + "description": "The supergraph request header name.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + }, + "additionalProperties": false }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true }, - { - "description": "String values", + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", "type": "string" }, - { - "description": "Array of homogeneous values", + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", + { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + ] } ] - } - ] + }, + "maxItems": 2, + "minItems": 2 + } }, - "maxItems": 2, - "minItems": 2 - } - }, - "additionalProperties": false - }, - { - "description": "A condition to check a selection against a selector.", - "type": "object", - "required": [ - "exists" - ], - "properties": { - "exists": { - "anyOf": [ - { - "type": "object", - "required": [ - "subgraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_operation_name": { - "description": "The operation name from the subgraph query.", - "oneOf": [ - { - "description": "The raw operation name.", + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" - ] + "nullable": true }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_operation_kind" - ], - "properties": { - "subgraph_operation_kind": { - "description": "The kind of the subgraph operation (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "subgraph_query": { - "description": "The graphql query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true }, - { - "description": "String values", + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "subgraph_query_variable": { - "description": "The name of a subgraph query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", - "type": "object", - "required": [ - "subgraph_response_body" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "subgraph_response_body": { + "description": "The subgraph response body json path.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "subgraph_response_body": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_data" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "subgraph_response_data": { + "description": "The subgraph response body json path.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" } - ], - "nullable": true + }, + "additionalProperties": false }, - "subgraph_response_data": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_errors" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "String values", + "subgraph_request_header": { + "description": "The name of a subgraph request header.", "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] } ] } - ], - "nullable": true - }, - "subgraph_response_errors": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_request_header": { - "description": "The name of a subgraph request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_response_header": { - "description": "The name of a subgraph response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_status" - ], - "properties": { - "subgraph_response_status": { - "description": "The subgraph http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "supergraph_operation_name": { - "description": "The supergraph query operation name.", - "oneOf": [ - { - "description": "The raw operation name.", + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" - ] + "nullable": true }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_kind" - ], - "properties": { - "supergraph_operation_kind": { - "description": "The supergraph query operation kind (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "supergraph_query": { - "description": "The supergraph query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" } - ], - "nullable": true - }, - "supergraph_query_variable": { - "description": "The supergraph query variable name.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "supergraph_request_header": { - "description": "The supergraph request header name.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "String values", + "supergraph_request_header": { + "description": "The supergraph request header name.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "request_context": { + "description": "The request context key.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + }, + "additionalProperties": false }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", "type": "string" }, - { - "description": "Array of homogeneous values", + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", + { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" ] } - }, - "additionalProperties": false - }, - { - "description": "All sub-conditions must be true.", - "type": "object", - "required": [ - "all" - ], - "properties": { - "all": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_SubgraphSelector" - } - } - }, - "additionalProperties": false - }, - { - "description": "At least one sub-conditions must be true.", - "type": "object", - "required": [ - "any" - ], - "properties": { - "any": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_SubgraphSelector" - } - } - }, - "additionalProperties": false - }, - { - "description": "The sub-condition must not be true", - "type": "object", - "required": [ - "not" - ], - "properties": { - "not": { - "$ref": "#/definitions/Condition_for_SubgraphSelector" - } - }, - "additionalProperties": false - }, - { - "description": "Static true condition", - "type": "string", - "enum": [ - "true" - ] - }, - { - "description": "Static false condition", - "type": "string", - "enum": [ - "false" ] } - ] - } - }, - "additionalProperties": { - "anyOf": [ - { - "type": "object", - "required": [ - "subgraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_operation_name": { - "description": "The operation name from the subgraph query.", - "oneOf": [ - { - "description": "The raw operation name.", + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" - ] + "nullable": true }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_operation_kind" - ], - "properties": { - "subgraph_operation_kind": { - "description": "The kind of the subgraph operation (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_query": { - "description": "The graphql query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "subgraph_query_variable": { - "description": "The name of a subgraph query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", - "type": "object", - "required": [ - "subgraph_response_body" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_response_body": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_data" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true }, - { - "description": "String values", + "subgraph_response_body": { + "description": "The subgraph response body json path.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "subgraph_response_data": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_errors" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "subgraph_response_data": { + "description": "The subgraph response body json path.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_response_errors": { - "description": "The subgraph response body json path.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_request_header": { - "description": "The name of a subgraph request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false }, - "subgraph_response_header": { - "description": "The name of a subgraph response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_status" - ], - "properties": { - "subgraph_response_status": { - "description": "The subgraph http response status code.", - "oneOf": [ - { - "description": "The http status code.", + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "code" - ] + "nullable": true }, - { - "description": "The http status reason.", + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "reason" + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "supergraph_operation_name": { - "description": "The supergraph query operation name.", - "oneOf": [ - { - "description": "The raw operation name.", + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" - ] + "nullable": true }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_kind" - ], - "properties": { - "supergraph_operation_kind": { - "description": "The supergraph query operation kind (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "supergraph_query": { - "description": "The supergraph query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" } - ], - "nullable": true - }, - "supergraph_query_variable": { - "description": "The supergraph query variable name.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "supergraph_request_header": { - "description": "The supergraph request header name.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "String values", + "supergraph_request_header": { + "description": "The supergraph request header name.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "request_context": { + "description": "The request context key.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + }, + "additionalProperties": false }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", "type": "string" }, - { - "description": "Array of homogeneous values", + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", + { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + ] } - ] - } + }, + { + "type": "string" + } + ] } } }, @@ -29690,1573 +29704,1580 @@ expression: "&schema" } }, "additionalProperties": { - "type": "object", - "properties": { - "condition": { - "oneOf": [ - { - "description": "A condition to check a selection against a value.", - "type": "object", - "required": [ - "eq" - ], - "properties": { - "eq": { - "type": "array", - "items": { - "anyOf": [ - { - "description": "A constant value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, + "oneOf": [ + { + "type": "object", + "properties": { + "condition": { + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ { - "description": "Array of homogeneous values", + "description": "A constant value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ] - }, - { - "description": "Selector to extract a value from the pipeline.", - "anyOf": [ - { - "type": "object", - "required": [ - "operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "description": "String values", + "type": "string" }, - "operation_name": { - "description": "The operation name from the query.", - "oneOf": [ + { + "description": "Array of homogeneous values", + "anyOf": [ { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "operation_kind" - ], - "properties": { - "operation_kind": { - "description": "The operation kind from the query (query|mutation|subscription).", - "oneOf": [ + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "query": { - "description": "The graphql query.", - "oneOf": [ + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } ] } - }, - "additionalProperties": false + ] }, { - "type": "object", - "required": [ - "query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] } ] } - ], - "nullable": true - }, - "query_variable": { - "description": "The name of a graphql query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "response_header": { - "description": "The name of the response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "Array of homogeneous values", + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" } - ], - "nullable": true + }, + "additionalProperties": false }, - "request_context": { - "description": "The request context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "String values", + "request_header": { + "description": "The name of the request header.", "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "Array of homogeneous values", + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + }, + "additionalProperties": false }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true }, - { - "description": "String values", + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", "type": "string" }, - { - "description": "Array of homogeneous values", + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true } - ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + ] } ] - } - ] + }, + "maxItems": 2, + "minItems": 2 + } }, - "maxItems": 2, - "minItems": 2 - } - }, - "additionalProperties": false - }, - { - "description": "A condition to check a selection against a selector.", - "type": "object", - "required": [ - "exists" - ], - "properties": { - "exists": { - "anyOf": [ - { - "type": "object", - "required": [ - "operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "operation_name": { - "description": "The operation name from the query.", - "oneOf": [ - { - "description": "The raw operation name.", + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exists" + ], + "properties": { + "exists": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" - ] + "nullable": true }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "operation_kind" - ], - "properties": { - "operation_kind": { - "description": "The operation kind from the query (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "query": { - "description": "The graphql query.", - "oneOf": [ - { - "description": "The raw query kind.", + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true }, - { - "description": "f64 values", - "type": "number", - "format": "double" + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "String values", + "request_header": { + "description": "The name of the request header.", "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "Array of homogeneous values", + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "String values", + "type": "string" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "query_variable": { - "description": "The name of a graphql query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "response_header": { - "description": "The name of the response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "request_context": { + "description": "The request context key.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true }, - { - "description": "String values", + "response_context": { + "description": "The response context key.", "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + }, + "additionalProperties": false }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", "type": "string" }, - { - "description": "Array of homogeneous values", + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", + { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + ] } - ] - } - }, - "additionalProperties": false - }, - { - "description": "All sub-conditions must be true.", - "type": "object", - "required": [ - "all" - ], - "properties": { - "all": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_SupergraphSelector" - } - } - }, - "additionalProperties": false - }, - { - "description": "At least one sub-conditions must be true.", - "type": "object", - "required": [ - "any" - ], - "properties": { - "any": { - "type": "array", - "items": { - "$ref": "#/definitions/Condition_for_SupergraphSelector" - } - } - }, - "additionalProperties": false - }, - { - "description": "The sub-condition must not be true", - "type": "object", - "required": [ - "not" - ], - "properties": { - "not": { - "$ref": "#/definitions/Condition_for_SupergraphSelector" + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] } - }, - "additionalProperties": false - }, - { - "description": "Static true condition", - "type": "string", - "enum": [ - "true" - ] - }, - { - "description": "Static false condition", - "type": "string", - "enum": [ - "false" ] } - ] - } - }, - "additionalProperties": { - "anyOf": [ - { - "type": "object", - "required": [ - "operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "operation_name": { - "description": "The operation name from the query.", - "oneOf": [ - { - "description": "The raw operation name.", + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" - ] + "nullable": true }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "operation_kind" - ], - "properties": { - "operation_kind": { - "description": "The operation kind from the query (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "query": { - "description": "The graphql query.", - "oneOf": [ - { - "description": "The raw query kind.", + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "string" + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } ] } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" } - ], - "nullable": true - }, - "query_variable": { - "description": "The name of a graphql query variable.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "request_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + }, + "additionalProperties": false }, - "response_header": { - "description": "The name of the response header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A status from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", "type": "string", - "enum": [ - "code" - ] + "nullable": true }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] + "request_header": { + "description": "The name of the request header.", + "type": "string" } - ] - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "description": "String values", + "response_header": { + "description": "The name of the response header.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] - } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + ], + "nullable": true }, - { - "description": "String values", + "request_context": { + "description": "The request context key.", "type": "string" - }, - { - "description": "Array of homogeneous values", + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", - "type": "string" + }, + "additionalProperties": false }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", "type": "string" }, - { - "description": "Array of homogeneous values", + "default": { + "description": "Optional default value.", "anyOf": [ { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } + "description": "bool values", + "type": "boolean" }, { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } + "description": "i64 values", + "type": "integer", + "format": "int64" }, { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } + "description": "f64 values", + "type": "number", + "format": "double" }, { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - ] + ], + "nullable": true } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" ], - "nullable": true - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, - "env": { - "description": "The name of the environment variable", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "string" - }, - { - "type": "object", - "required": [ - "static" - ], - "properties": { - "static": { - "description": "A static string value", + { "type": "string" + }, + { + "type": "object", + "required": [ + "static" + ], + "properties": { + "static": { + "description": "A static string value", + "type": "string" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + ] } - ] - } + }, + { + "type": "string" + } + ] } } }, diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index 4f70ce3e58..91227531d5 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use parking_lot::Mutex; use schemars::gen::SchemaGenerator; -use schemars::schema::Schema; +use schemars::schema::{Schema, SchemaObject, SubschemaValidation}; use schemars::JsonSchema; use serde::de::Error; use serde::de::MapAccess; @@ -67,8 +67,32 @@ where .insert("condition".to_string(), gen.subschema_for::>()); } } - - selector + Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + one_of: Some(vec![selector, gen.subschema_for::()]), + ..Default::default() + })), + ..Default::default() + }) + // let mut selector = gen.subschema_for::>(); + // if let Schema::Object(schema) = &mut selector { + // + // let subschema = SubschemaValidation { + // one_of: Some(vec![ + // schema.object.take() + // ]), + // ..Default::default() + // }; + // schema.subschemas = Some(Box::new(subschema)); + // + // if let Some(object) = &mut schema.object { + // object + // .properties + // .insert("condition".to_string(), gen.subschema_for::>()); + // } + // } + // + // selector } } From 48953160d320fb5fa3febf6f1dc8ee61901b9f00 Mon Sep 17 00:00:00 2001 From: bryn Date: Mon, 22 Apr 2024 19:53:18 +0100 Subject: [PATCH 35/40] Remove commented out code and add comment --- .../telemetry/config_new/conditional.rs | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index 91227531d5..643804deff 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -6,7 +6,9 @@ use std::sync::Arc; use parking_lot::Mutex; use schemars::gen::SchemaGenerator; -use schemars::schema::{Schema, SchemaObject, SubschemaValidation}; +use schemars::schema::Schema; +use schemars::schema::SchemaObject; +use schemars::schema::SubschemaValidation; use schemars::JsonSchema; use serde::de::Error; use serde::de::MapAccess; @@ -59,6 +61,7 @@ where } fn json_schema(gen: &mut SchemaGenerator) -> Schema { + // Add the condition and also allow string as a fallback. let mut selector = gen.subschema_for::>(); if let Schema::Object(schema) = &mut selector { if let Some(object) = &mut schema.object { @@ -74,25 +77,6 @@ where })), ..Default::default() }) - // let mut selector = gen.subschema_for::>(); - // if let Schema::Object(schema) = &mut selector { - // - // let subschema = SubschemaValidation { - // one_of: Some(vec![ - // schema.object.take() - // ]), - // ..Default::default() - // }; - // schema.subschemas = Some(Box::new(subschema)); - // - // if let Some(object) = &mut schema.object { - // object - // .properties - // .insert("condition".to_string(), gen.subschema_for::>()); - // } - // } - // - // selector } } From 5c87015d4b511d356fb35adcbe8804d3f7164347 Mon Sep 17 00:00:00 2001 From: bryn Date: Tue, 23 Apr 2024 09:05:56 +0100 Subject: [PATCH 36/40] Add failing test: `test_extendable_serde_conditional` Also improve error output from serde a little. --- .../telemetry/config_new/extendable.rs | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/extendable.rs b/apollo-router/src/plugins/telemetry/config_new/extendable.rs index b23ded0408..1d5561f942 100644 --- a/apollo-router/src/plugins/telemetry/config_new/extendable.rs +++ b/apollo-router/src/plugins/telemetry/config_new/extendable.rs @@ -12,8 +12,6 @@ use serde::de::MapAccess; use serde::de::Visitor; use serde::Deserialize; use serde::Deserializer; -#[cfg(test)] -use serde::Serialize; use serde_json::Map; use serde_json::Value; use tower::BoxError; @@ -26,7 +24,6 @@ use crate::plugins::telemetry::otlp::TelemetryDataKind; /// This struct can be used as an attributes container, it has a custom JsonSchema implementation that will merge the schemas of the attributes and custom fields. #[derive(Clone, Debug)] -#[cfg_attr(test, derive(Serialize))] pub(crate) struct Extendable where Att: Default, @@ -94,6 +91,15 @@ where } Err(_err) => { // We didn't manage to deserialize as a custom attribute, so stash the value and we'll try again later + // but let's try and deserialize it now so that we get a decent error message rather than 'unknown field' + let mut temp_attributes: Map = Map::new(); + temp_attributes.insert(key.clone(), value.clone()); + Att::deserialize(Value::Object(temp_attributes)).map_err(|e| { + A::Error::custom(format!( + "failed to parse attribute '{}': {}", + key, e + )) + })?; attributes.insert(key, value); } } @@ -190,10 +196,13 @@ where #[cfg(test)] mod test { - use insta::assert_yaml_snapshot; + use insta::assert_debug_snapshot; + use crate::plugins::telemetry::config_new::attributes::RouterAttributes; use crate::plugins::telemetry::config_new::attributes::SupergraphAttributes; + use crate::plugins::telemetry::config_new::conditional::Conditional; use crate::plugins::telemetry::config_new::extendable::Extendable; + use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; #[test] @@ -214,7 +223,7 @@ mod test { }), ) .unwrap(); - assert_yaml_snapshot!(o); + assert_debug_snapshot!(o); }); } @@ -234,4 +243,28 @@ mod test { ) .expect_err("Should have errored"); } + + #[test] + fn test_extendable_serde_conditional() { + let mut settings = insta::Settings::clone_current(); + settings.set_sort_maps(true); + settings.bind(|| { + let o = serde_json::from_value::< + Extendable>, + >(serde_json::json!({ + "http.request.method": true, + "http.response.status_code": true, + "url.path": true, + "http.request.header.x-my-header": { + "request_header": "x-my-header" + }, + "http.request.header.x-not-present": { + "request_header": "x-not-present", + "default": "nope" + } + })) + .unwrap(); + assert_debug_snapshot!(o); + }); + } } From 1bcfcc23a0aae69a600cad0202b3ccb81d9a9ff8 Mon Sep 17 00:00:00 2001 From: bryn Date: Tue, 23 Apr 2024 09:26:56 +0100 Subject: [PATCH 37/40] Fix deserialization, modify test to exercise multiple fields on attribute. --- .../telemetry/config_new/conditional.rs | 32 ++++----- .../telemetry/config_new/extendable.rs | 2 +- ...w__extendable__test__extendable_serde.snap | 35 +++++---- ...e__test__extendable_serde_conditional.snap | 72 +++++++++++++++++++ 4 files changed, 110 insertions(+), 31 deletions(-) create mode 100644 apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde_conditional.snap diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index 643804deff..6dd90c320e 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -194,8 +194,9 @@ where where A: MapAccess<'de>, { - let mut selector: Option = None; let mut condition: Option> = None; + let mut attributes = Map::new(); + // Separate out the condition from the rest of the attributes. while let Some(key) = map.next_key::()? { let value: Value = map.next_value()?; if key == "condition" { @@ -204,19 +205,16 @@ where .map_err(|e| Error::custom(e.to_string()))?, ) } else { - let mut map = Map::new(); - map.insert(key.clone(), value); - let o = Value::Object(map); - selector = - Some(Att::deserialize(o).map_err(|e| Error::custom(e.to_string()))?) + attributes.insert(key.clone(), value); } } - if selector.is_none() { - return Err(A::Error::custom("selector is required")); - } + + // Try to parse the attribute + let selector = + Att::deserialize(Value::Object(attributes)).map_err(A::Error::custom)?; Ok(Conditional { - selector: selector.expect("selector is required"), + selector, condition: condition.map(|c| Arc::new(Mutex::new(c))), value: Arc::new(Default::default()), }) @@ -419,7 +417,8 @@ mod test { #[test] fn test_deserialization() { let config = r#" - static: "there was an error" + request_header: head + default: hmm condition: any: - eq: @@ -428,15 +427,12 @@ mod test { "#; let conditional: super::Conditional = serde_yaml::from_str(config).unwrap(); - let result = conditional.on_response( - &crate::services::router::Response::fake_builder() - .status_code(StatusCode::from_u16(201).unwrap()) - .build() - .expect("req"), - ); + let result = on_request(&conditional); + assert!(result.is_none()); + let result = on_response(conditional); assert_eq!( result.expect("expected result"), - Value::String("there was an error".into()) + Value::String("val".into()) ); } diff --git a/apollo-router/src/plugins/telemetry/config_new/extendable.rs b/apollo-router/src/plugins/telemetry/config_new/extendable.rs index 1d5561f942..36a65d9935 100644 --- a/apollo-router/src/plugins/telemetry/config_new/extendable.rs +++ b/apollo-router/src/plugins/telemetry/config_new/extendable.rs @@ -81,7 +81,7 @@ where where A: MapAccess<'de>, { - let mut attributes: Map = Map::new(); + let mut attributes = Map::new(); let mut custom: HashMap = HashMap::new(); while let Some(key) = map.next_key()? { let value: Value = map.next_value()?; diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde.snap index 760c8c5b09..650cd228aa 100644 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde.snap +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde.snap @@ -2,15 +2,26 @@ source: apollo-router/src/plugins/telemetry/config_new/extendable.rs expression: o --- -attributes: - graphql.document: ~ - graphql.operation.name: true - graphql.operation.type: true -custom: - custom_1: - operation_name: string - default: ~ - custom_2: - operation_name: string - default: ~ - +Extendable { + attributes: SupergraphAttributes { + graphql_document: None, + graphql_operation_name: Some( + true, + ), + graphql_operation_type: Some( + true, + ), + }, + custom: { + "custom_1": OperationName { + operation_name: String, + redact: None, + default: None, + }, + "custom_2": OperationName { + operation_name: String, + redact: None, + default: None, + }, + }, +} diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde_conditional.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde_conditional.snap new file mode 100644 index 0000000000..1431429a85 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde_conditional.snap @@ -0,0 +1,72 @@ +--- +source: apollo-router/src/plugins/telemetry/config_new/extendable.rs +expression: o +--- +Extendable { + attributes: RouterAttributes { + datadog_trace_id: None, + trace_id: None, + baggage: None, + common: HttpCommonAttributes { + error_type: None, + http_request_body_size: None, + http_request_method: Some( + true, + ), + http_request_method_original: None, + http_response_body_size: None, + http_response_status_code: Some( + true, + ), + network_protocol_name: None, + network_protocol_version: None, + network_transport: None, + network_type: None, + }, + server: HttpServerAttributes { + client_address: None, + client_port: None, + http_route: None, + network_local_address: None, + network_local_port: None, + network_peer_address: None, + network_peer_port: None, + server_address: None, + server_port: None, + url_path: Some( + true, + ), + url_query: None, + url_scheme: None, + user_agent_original: None, + }, + }, + custom: { + "http.request.header.x-my-header": Conditional { + selector: RequestHeader { + request_header: "x-my-header", + redact: None, + default: None, + }, + condition: None, + value: Mutex { + data: Pending, + }, + }, + "http.request.header.x-not-present": Conditional { + selector: RequestHeader { + request_header: "x-not-present", + redact: None, + default: Some( + String( + "nope", + ), + ), + }, + condition: None, + value: Mutex { + data: Pending, + }, + }, + }, +} From c518504888a92b25018558785aa2856398ca6ad8 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:01:31 +0200 Subject: [PATCH 38/40] changing config test Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/testdata/config.router.yaml | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/apollo-router/src/plugins/telemetry/testdata/config.router.yaml b/apollo-router/src/plugins/telemetry/testdata/config.router.yaml index 602416bb16..39bc874c65 100644 --- a/apollo-router/src/plugins/telemetry/testdata/config.router.yaml +++ b/apollo-router/src/plugins/telemetry/testdata/config.router.yaml @@ -1,4 +1,146 @@ telemetry: + instrumentation: + spans: + mode: spec_compliant + default_attribute_requirement_level: recommended + router: + attributes: + "custom_one": + request_header: host + supergraph: + attributes: + graphql.document: true + subgraph: + attributes: + subgraph.graphql.document: true + instruments: + router: + http.server.request.body.size: + attributes: + # Standard attributes + http.response.status_code: true + "my_attribute": + response_header: "content-type" + http.server.request.duration: + attributes: + # Standard attributes + http.response.status_code: true + http.request.method: true + # Custom attribute + "my_attribute": + response_header: "content-type" + my.request.duration: # The name of your custom instrument/metric + value: duration + type: counter + unit: s + description: "my description" + acme.request.size: # The name of your custom instrument/metric + value: + request_header: "content-length" + type: counter + unit: s + description: "my description" + + acme.request.length: # The name of your custom instrument/metric + value: + request_header: "content-length" + type: histogram + unit: s + description: "my description" + supergraph: + acme.graphql.requests: + value: unit + type: counter + unit: request + description: "supergraph requests" + attributes: + static: hello + graphql_operation_kind: + operation_kind: string + subgraph: + request_including_price1: + value: unit + type: counter + unit: request + description: "supergraph requests" + condition: + exists: + subgraph_response_data: "$.products[*].price1" + attributes: + subgraph.name: true + events: + router: + # Standard events + request: info + response: info + error: info + + # Custom events + my.request_event: + message: "my event message" + level: info + on: request + attributes: + http.request.body.size: true + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - request_header: "x-log-request" + my.response_event: + message: "my response event message" + level: info + on: response + attributes: + http.response.body.size: true + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - response_header: "x-log-request" + supergraph: + # Standard events + request: info + response: info + error: info + + # Custom events + my.request.event: + message: "my event message" + level: info + on: request + # Only log when the x-log-request header is `log` + condition: + eq: + - "log" + - request_header: "x-log-request" + my.response_event: + message: "my response event message" + level: warn + on: response + condition: + eq: + - "log" + - response_header: "x-log-request" + subgraph: + # Standard events + request: info + response: warn + error: error + + # Custom events + my.request.event: + message: "my event message" + level: info + on: request + my.response.event: + message: "my response event message" + level: error + on: response + attributes: + subgraph.name: true + response_status: + subgraph_response_status: code exporters: tracing: common: From 81d81df45f7fbcf299322af25aa63ab084073d2b Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:02:57 +0200 Subject: [PATCH 39/40] don't lock in a match branch Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/conditional.rs | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index 6dd90c320e..99f9436994 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -102,29 +102,32 @@ where fn on_request(&self, request: &Self::Request) -> Option { match &self.condition { - Some(condition) => match condition.lock().evaluate_request(request) { - None => { - if let Some(value) = self.selector.on_request(request) { - *self.value.lock() = value.into(); + Some(condition) => { + let request_condition_res = condition.lock().evaluate_request(request); + match request_condition_res { + None => { + if let Some(value) = self.selector.on_request(request) { + *self.value.lock() = value.into(); + } + None } - None - } - Some(true) => { - // The condition evaluated to true, so we can just return the value but may need to try again on the response. - match self.selector.on_request(request) { - None => None, - Some(value) => { - *self.value.lock() = State::Returned; - Some(value) + Some(true) => { + // The condition evaluated to true, so we can just return the value but may need to try again on the response. + match self.selector.on_request(request) { + None => None, + Some(value) => { + *self.value.lock() = State::Returned; + Some(value) + } } } + Some(false) => { + // The condition has been evaluated to false, so we can return None. it will never return true. + *self.value.lock() = State::Returned; + None + } } - Some(false) => { - // The condition has been evaluated to false, so we can return None. it will never return true. - *self.value.lock() = State::Returned; - None - } - }, + } None => { // There is no condition to evaluate, so we can just return the value. match self.selector.on_request(request) { From 47115a1f96510fad6198dd329a7e25dbaaad05b4 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:09:58 +0200 Subject: [PATCH 40/40] fix tests Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/attributes.rs | 67 ++++---- .../telemetry/config_new/conditional.rs | 17 ++ .../telemetry/config_new/conditions.rs | 5 +- .../telemetry/config_new/extendable.rs | 157 +++++++++++++----- .../plugins/telemetry/config_new/selectors.rs | 17 +- ...w__extendable__test__extendable_serde.snap | 27 --- 6 files changed, 183 insertions(+), 107 deletions(-) delete mode 100644 apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde.snap diff --git a/apollo-router/src/plugins/telemetry/config_new/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/attributes.rs index c9d5e97e0c..0f5d84b96c 100644 --- a/apollo-router/src/plugins/telemetry/config_new/attributes.rs +++ b/apollo-router/src/plugins/telemetry/config_new/attributes.rs @@ -78,27 +78,28 @@ pub(crate) enum DefaultAttributeRequirementLevel { } #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] +#[cfg_attr(test, derive(PartialEq))] #[serde(deny_unknown_fields, default)] pub(crate) struct RouterAttributes { /// The datadog trace ID. /// This can be output in logs and used to correlate traces in Datadog. #[serde(rename = "dd.trace_id")] - datadog_trace_id: Option, + pub(crate) datadog_trace_id: Option, /// The OpenTelemetry trace ID. /// This can be output in logs. #[serde(rename = "trace_id")] - trace_id: Option, + pub(crate) trace_id: Option, /// All key values from trace baggage. - baggage: Option, + pub(crate) baggage: Option, /// Http attributes from Open Telemetry semantic conventions. #[serde(flatten)] - common: HttpCommonAttributes, + pub(crate) common: HttpCommonAttributes, /// Http server attributes from Open Telemetry semantic conventions. #[serde(flatten)] - server: HttpServerAttributes, + pub(crate) server: HttpServerAttributes, } impl DefaultForLevel for RouterAttributes { @@ -113,7 +114,7 @@ impl DefaultForLevel for RouterAttributes { } #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] -#[cfg_attr(test, derive(Serialize))] +#[cfg_attr(test, derive(Serialize, PartialEq))] #[serde(deny_unknown_fields, default)] pub(crate) struct SupergraphAttributes { /// The GraphQL document being executed. @@ -121,13 +122,13 @@ pub(crate) struct SupergraphAttributes { /// * query findBookById { bookById(id: ?) { name } } /// Requirement level: Recommended #[serde(rename = "graphql.document")] - graphql_document: Option, + pub(crate) graphql_document: Option, /// The name of the operation being executed. /// Examples: /// * findBookById /// Requirement level: Recommended #[serde(rename = "graphql.operation.name")] - graphql_operation_name: Option, + pub(crate) graphql_operation_name: Option, /// The type of the operation being executed. /// Examples: /// * query @@ -135,7 +136,7 @@ pub(crate) struct SupergraphAttributes { /// * mutation /// Requirement level: Recommended #[serde(rename = "graphql.operation.type")] - graphql_operation_type: Option, + pub(crate) graphql_operation_type: Option, } impl DefaultForLevel for SupergraphAttributes { @@ -227,6 +228,7 @@ impl DefaultForLevel for SubgraphAttributes { /// Common attributes for http server and client. /// See https://opentelemetry.io/docs/specs/semconv/http/http-spans/#common-attributes #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] +#[cfg_attr(test, derive(PartialEq))] #[serde(deny_unknown_fields, default)] pub(crate) struct HttpCommonAttributes { /// Describes a class of error the operation ended with. @@ -236,14 +238,14 @@ pub(crate) struct HttpCommonAttributes { /// * 500 /// Requirement level: Conditionally Required: If request has ended with an error. #[serde(rename = "error.type")] - error_type: Option, + pub(crate) error_type: Option, /// The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. /// Examples: /// * 3495 /// Requirement level: Recommended #[serde(rename = "http.request.body.size")] - http_request_body_size: Option, + pub(crate) http_request_body_size: Option, /// HTTP request method. /// Examples: @@ -252,7 +254,7 @@ pub(crate) struct HttpCommonAttributes { /// * HEAD /// Requirement level: Required #[serde(rename = "http.request.method")] - http_request_method: Option, + pub(crate) http_request_method: Option, /// Original HTTP method sent by the client in the request line. /// Examples: @@ -261,21 +263,21 @@ pub(crate) struct HttpCommonAttributes { /// * foo /// Requirement level: Conditionally Required (If and only if it’s different than http.request.method) #[serde(rename = "http.request.method.original", skip)] - http_request_method_original: Option, + pub(crate) http_request_method_original: Option, /// The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. /// Examples: /// * 3495 /// Requirement level: Recommended #[serde(rename = "http.response.body.size")] - http_response_body_size: Option, + pub(crate) http_response_body_size: Option, /// HTTP response status code. /// Examples: /// * 200 /// Requirement level: Conditionally Required: If and only if one was received/sent. #[serde(rename = "http.response.status_code")] - http_response_status_code: Option, + pub(crate) http_response_status_code: Option, /// OSI application layer or non-OSI equivalent. /// Examples: @@ -283,7 +285,7 @@ pub(crate) struct HttpCommonAttributes { /// * spdy /// Requirement level: Recommended: if not default (http). #[serde(rename = "network.protocol.name")] - network_protocol_name: Option, + pub(crate) network_protocol_name: Option, /// Version of the protocol specified in network.protocol.name. /// Examples: @@ -293,7 +295,7 @@ pub(crate) struct HttpCommonAttributes { /// * 3 /// Requirement level: Recommended #[serde(rename = "network.protocol.version")] - network_protocol_version: Option, + pub(crate) network_protocol_version: Option, /// OSI transport layer. /// Examples: @@ -301,7 +303,7 @@ pub(crate) struct HttpCommonAttributes { /// * udp /// Requirement level: Conditionally Required #[serde(rename = "network.transport")] - network_transport: Option, + pub(crate) network_transport: Option, /// OSI network layer or non-OSI equivalent. /// Examples: @@ -309,7 +311,7 @@ pub(crate) struct HttpCommonAttributes { /// * ipv6 /// Requirement level: Recommended #[serde(rename = "network.type")] - network_type: Option, + pub(crate) network_type: Option, } impl DefaultForLevel for HttpCommonAttributes { @@ -362,6 +364,7 @@ impl DefaultForLevel for HttpCommonAttributes { /// Attributes for Http servers /// See https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-server #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] +#[cfg_attr(test, derive(PartialEq))] #[serde(deny_unknown_fields, default)] pub(crate) struct HttpServerAttributes { /// Client address - domain name if available without reverse DNS lookup, otherwise IP address or Unix domain socket name. @@ -369,45 +372,45 @@ pub(crate) struct HttpServerAttributes { /// * 83.164.160.102 /// Requirement level: Recommended #[serde(rename = "client.address", skip)] - client_address: Option, + pub(crate) client_address: Option, /// The port of the original client behind all proxies, if known (e.g. from Forwarded or a similar header). Otherwise, the immediate client peer port. /// Examples: /// * 65123 /// Requirement level: Recommended #[serde(rename = "client.port", skip)] - client_port: Option, + pub(crate) client_port: Option, /// The matched route (path template in the format used by the respective server framework). /// Examples: /// * /graphql /// Requirement level: Conditionally Required: If and only if it’s available #[serde(rename = "http.route")] - http_route: Option, + pub(crate) http_route: Option, /// Local socket address. Useful in case of a multi-IP host. /// Examples: /// * 10.1.2.80 /// * /tmp/my.sock /// Requirement level: Opt-In #[serde(rename = "network.local.address")] - network_local_address: Option, + pub(crate) network_local_address: Option, /// Local socket port. Useful in case of a multi-port host. /// Examples: /// * 65123 /// Requirement level: Opt-In #[serde(rename = "network.local.port")] - network_local_port: Option, + pub(crate) network_local_port: Option, /// Peer address of the network connection - IP address or Unix domain socket name. /// Examples: /// * 10.1.2.80 /// * /tmp/my.sock /// Requirement level: Recommended #[serde(rename = "network.peer.address")] - network_peer_address: Option, + pub(crate) network_peer_address: Option, /// Peer port number of the network connection. /// Examples: /// * 65123 /// Requirement level: Recommended #[serde(rename = "network.peer.port")] - network_peer_port: Option, + pub(crate) network_peer_port: Option, /// Name of the local HTTP server that received the request. /// Examples: /// * example.com @@ -415,7 +418,7 @@ pub(crate) struct HttpServerAttributes { /// * /tmp/my.sock /// Requirement level: Recommended #[serde(rename = "server.address")] - server_address: Option, + pub(crate) server_address: Option, /// Port of the local HTTP server that received the request. /// Examples: /// * 80 @@ -423,19 +426,19 @@ pub(crate) struct HttpServerAttributes { /// * 443 /// Requirement level: Recommended #[serde(rename = "server.port")] - server_port: Option, + pub(crate) server_port: Option, /// The URI path component /// Examples: /// * /search /// Requirement level: Required #[serde(rename = "url.path")] - url_path: Option, + pub(crate) url_path: Option, /// The URI query component /// Examples: /// * q=OpenTelemetry /// Requirement level: Conditionally Required: If and only if one was received/sent. #[serde(rename = "url.query")] - url_query: Option, + pub(crate) url_query: Option, /// The URI scheme component identifying the used protocol. /// Examples: @@ -443,7 +446,7 @@ pub(crate) struct HttpServerAttributes { /// * https /// Requirement level: Required #[serde(rename = "url.scheme")] - url_scheme: Option, + pub(crate) url_scheme: Option, /// Value of the HTTP User-Agent header sent by the client. /// Examples: @@ -451,7 +454,7 @@ pub(crate) struct HttpServerAttributes { /// * libwww/2.17b3 /// Requirement level: Recommended #[serde(rename = "user_agent.original")] - user_agent_original: Option, + pub(crate) user_agent_original: Option, } impl DefaultForLevel for HttpServerAttributes { diff --git a/apollo-router/src/plugins/telemetry/config_new/conditional.rs b/apollo-router/src/plugins/telemetry/config_new/conditional.rs index 99f9436994..f4c575e459 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditional.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditional.rs @@ -26,6 +26,7 @@ use crate::plugins::telemetry::otlp::TelemetryDataKind; /// The state of the conditional. #[derive(Debug, Default)] +#[cfg_attr(test, derive(PartialEq))] pub(crate) enum State { /// The conditional has not been evaluated yet or no value has been set via selector. #[default] @@ -52,6 +53,22 @@ pub(crate) struct Conditional { pub(crate) value: Arc>>, } +#[cfg(test)] +impl PartialEq for Conditional +where + Att: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + let condition_eq = match (&self.condition, &other.condition) { + (Some(l), Some(r)) => *(l.lock()) == *(r.lock()), + (None, None) => true, + _ => false, + }; + let value_eq = *(self.value.lock()) == *(other.value.lock()); + self.selector == other.selector && value_eq && condition_eq + } +} + impl JsonSchema for Conditional where T: JsonSchema, diff --git a/apollo-router/src/plugins/telemetry/config_new/conditions.rs b/apollo-router/src/plugins/telemetry/config_new/conditions.rs index 773b4a4d82..bfb16eecb8 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditions.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditions.rs @@ -5,8 +5,8 @@ use serde::Deserialize; use crate::plugins::telemetry::config::AttributeValue; use crate::plugins::telemetry::config_new::Selector; -#[allow(dead_code)] #[derive(Deserialize, JsonSchema, Clone, Debug)] +#[cfg_attr(test, derive(PartialEq))] #[serde(deny_unknown_fields, rename_all = "snake_case")] pub(crate) enum Condition { /// A condition to check a selection against a value. @@ -37,8 +37,8 @@ impl Condition<()> { } } -#[allow(dead_code)] #[derive(Deserialize, JsonSchema, Clone, Debug)] +#[cfg_attr(test, derive(PartialEq))] #[serde(deny_unknown_fields, rename_all = "snake_case", untagged)] pub(crate) enum SelectorOrValue { /// A constant value. @@ -47,7 +47,6 @@ pub(crate) enum SelectorOrValue { Selector(T), } -#[allow(dead_code)] impl Condition where T: Selector, diff --git a/apollo-router/src/plugins/telemetry/config_new/extendable.rs b/apollo-router/src/plugins/telemetry/config_new/extendable.rs index 36a65d9935..d40301e23f 100644 --- a/apollo-router/src/plugins/telemetry/config_new/extendable.rs +++ b/apollo-router/src/plugins/telemetry/config_new/extendable.rs @@ -196,35 +196,63 @@ where #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use std::sync::Arc; + use parking_lot::Mutex; + + use crate::plugins::telemetry::config::AttributeValue; + use crate::plugins::telemetry::config_new::attributes::HttpCommonAttributes; + use crate::plugins::telemetry::config_new::attributes::HttpServerAttributes; use crate::plugins::telemetry::config_new::attributes::RouterAttributes; use crate::plugins::telemetry::config_new::attributes::SupergraphAttributes; use crate::plugins::telemetry::config_new::conditional::Conditional; + use crate::plugins::telemetry::config_new::conditions::Condition; + use crate::plugins::telemetry::config_new::conditions::SelectorOrValue; use crate::plugins::telemetry::config_new::extendable::Extendable; + use crate::plugins::telemetry::config_new::selectors::OperationName; + use crate::plugins::telemetry::config_new::selectors::ResponseStatus; use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; #[test] fn test_extendable_serde() { - let mut settings = insta::Settings::clone_current(); - settings.set_sort_maps(true); - settings.bind(|| { - let o = serde_json::from_value::>( - serde_json::json!({ - "graphql.operation.name": true, - "graphql.operation.type": true, - "custom_1": { - "operation_name": "string" - }, - "custom_2": { - "operation_name": "string" - } - }), - ) - .unwrap(); - assert_debug_snapshot!(o); - }); + let extendable_conf = serde_json::from_value::< + Extendable, + >(serde_json::json!({ + "graphql.operation.name": true, + "graphql.operation.type": true, + "custom_1": { + "operation_name": "string" + }, + "custom_2": { + "operation_name": "string" + } + })) + .unwrap(); + assert_eq!( + extendable_conf.attributes, + SupergraphAttributes { + graphql_document: None, + graphql_operation_name: Some(true), + graphql_operation_type: Some(true) + } + ); + assert_eq!( + extendable_conf.custom.get("custom_1"), + Some(&SupergraphSelector::OperationName { + operation_name: OperationName::String, + redact: None, + default: None + }) + ); + assert_eq!( + extendable_conf.custom.get("custom_2"), + Some(&SupergraphSelector::OperationName { + operation_name: OperationName::String, + redact: None, + default: None + }) + ); } #[test] @@ -246,25 +274,78 @@ mod test { #[test] fn test_extendable_serde_conditional() { - let mut settings = insta::Settings::clone_current(); - settings.set_sort_maps(true); - settings.bind(|| { - let o = serde_json::from_value::< - Extendable>, - >(serde_json::json!({ - "http.request.method": true, - "http.response.status_code": true, - "url.path": true, - "http.request.header.x-my-header": { - "request_header": "x-my-header" - }, - "http.request.header.x-not-present": { - "request_header": "x-not-present", - "default": "nope" + let extendable_conf = serde_json::from_value::< + Extendable>, + >(serde_json::json!({ + "http.request.method": true, + "http.response.status_code": true, + "url.path": true, + "http.request.header.x-my-header": { + "request_header": "x-my-header", + "condition": { + "eq": [ + 200, + { + "response_status": "code" + } + ] + } + }, + "http.request.header.x-not-present": { + "request_header": "x-not-present", + "default": "nope" + } + })) + .unwrap(); + assert_eq!( + extendable_conf.attributes, + RouterAttributes { + datadog_trace_id: None, + trace_id: None, + baggage: None, + common: HttpCommonAttributes { + http_request_method: Some(true), + http_response_status_code: Some(true), + ..Default::default() + }, + server: HttpServerAttributes { + url_path: Some(true), + ..Default::default() + } } - })) - .unwrap(); - assert_debug_snapshot!(o); - }); + ); + assert_eq!( + extendable_conf + .custom + .get("http.request.header.x-my-header"), + Some(&Conditional { + selector: RouterSelector::RequestHeader { + request_header: String::from("x-my-header"), + redact: None, + default: None + }, + condition: Some(Arc::new(Mutex::new(Condition::Eq([ + SelectorOrValue::Value(200.into()), + SelectorOrValue::Selector(RouterSelector::ResponseStatus { + response_status: ResponseStatus::Code + }) + ])))), + value: Default::default(), + }) + ); + assert_eq!( + extendable_conf + .custom + .get("http.request.header.x-not-present"), + Some(&Conditional { + selector: RouterSelector::RequestHeader { + request_header: String::from("x-not-present"), + redact: None, + default: Some(AttributeValue::String("nope".to_string())) + }, + condition: None, + value: Default::default(), + }) + ); } } diff --git a/apollo-router/src/plugins/telemetry/config_new/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/selectors.rs index 02841fdf15..e95128105f 100644 --- a/apollo-router/src/plugins/telemetry/config_new/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/selectors.rs @@ -25,6 +25,7 @@ use crate::services::subgraph; use crate::services::supergraph; #[derive(Deserialize, JsonSchema, Clone, Debug)] +#[cfg_attr(test, derive(PartialEq))] #[serde(deny_unknown_fields, rename_all = "snake_case")] pub(crate) enum TraceIdFormat { /// Open Telemetry trace ID, a hex string. @@ -34,7 +35,7 @@ pub(crate) enum TraceIdFormat { } #[derive(Deserialize, JsonSchema, Clone, Debug)] -#[cfg_attr(test, derive(Serialize))] +#[cfg_attr(test, derive(Serialize, PartialEq))] #[serde(deny_unknown_fields, rename_all = "snake_case")] pub(crate) enum OperationName { /// The raw operation name. @@ -44,7 +45,7 @@ pub(crate) enum OperationName { } #[derive(Deserialize, JsonSchema, Clone, Debug)] -#[cfg_attr(test, derive(Serialize))] +#[cfg_attr(test, derive(Serialize, PartialEq))] #[serde(deny_unknown_fields, rename_all = "snake_case")] pub(crate) enum Query { /// The raw query kind. @@ -52,7 +53,7 @@ pub(crate) enum Query { } #[derive(Deserialize, JsonSchema, Clone, Debug)] -#[cfg_attr(test, derive(Serialize))] +#[cfg_attr(test, derive(Serialize, PartialEq))] #[serde(deny_unknown_fields, rename_all = "snake_case")] pub(crate) enum ResponseStatus { /// The http status code. @@ -62,7 +63,7 @@ pub(crate) enum ResponseStatus { } #[derive(Deserialize, JsonSchema, Clone, Debug)] -#[cfg_attr(test, derive(Serialize))] +#[cfg_attr(test, derive(Serialize, PartialEq))] #[serde(deny_unknown_fields, rename_all = "snake_case")] pub(crate) enum OperationKind { /// The raw operation kind. @@ -70,6 +71,7 @@ pub(crate) enum OperationKind { } #[derive(Deserialize, JsonSchema, Clone, Debug)] +#[cfg_attr(test, derive(PartialEq))] #[serde(deny_unknown_fields, untagged)] pub(crate) enum RouterSelector { /// A header from the request @@ -154,7 +156,7 @@ pub(crate) enum RouterSelector { } #[derive(Deserialize, JsonSchema, Clone, Debug)] -#[cfg_attr(test, derive(Serialize))] +#[cfg_attr(test, derive(Serialize, PartialEq))] #[serde(deny_unknown_fields, untagged)] pub(crate) enum SupergraphSelector { OperationName { @@ -268,6 +270,7 @@ pub(crate) enum SupergraphSelector { } #[derive(Deserialize, JsonSchema, Clone, Derivative)] +#[cfg_attr(test, derivative(PartialEq))] #[serde(deny_unknown_fields, rename_all = "snake_case", untagged)] #[derivative(Debug)] pub(crate) enum SubgraphSelector { @@ -325,7 +328,7 @@ pub(crate) enum SubgraphSelector { SubgraphResponseData { /// The subgraph response body json path. #[schemars(with = "String")] - #[derivative(Debug = "ignore")] + #[derivative(Debug = "ignore", PartialEq = "ignore")] #[serde(deserialize_with = "deserialize_jsonpath")] subgraph_response_data: JsonPathInst, #[serde(skip)] @@ -338,7 +341,7 @@ pub(crate) enum SubgraphSelector { SubgraphResponseErrors { /// The subgraph response body json path. #[schemars(with = "String")] - #[derivative(Debug = "ignore")] + #[derivative(Debug = "ignore", PartialEq = "ignore")] #[serde(deserialize_with = "deserialize_jsonpath")] subgraph_response_errors: JsonPathInst, #[serde(skip)] diff --git a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde.snap b/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde.snap deleted file mode 100644 index 650cd228aa..0000000000 --- a/apollo-router/src/plugins/telemetry/config_new/snapshots/apollo_router__plugins__telemetry__config_new__extendable__test__extendable_serde.snap +++ /dev/null @@ -1,27 +0,0 @@ ---- -source: apollo-router/src/plugins/telemetry/config_new/extendable.rs -expression: o ---- -Extendable { - attributes: SupergraphAttributes { - graphql_document: None, - graphql_operation_name: Some( - true, - ), - graphql_operation_type: Some( - true, - ), - }, - custom: { - "custom_1": OperationName { - operation_name: String, - redact: None, - default: None, - }, - "custom_2": OperationName { - operation_name: String, - redact: None, - default: None, - }, - }, -}