1use std::sync::Arc;
7use std::time::Instant;
8
9use apollo_compiler::ExecutableDocument;
10use apollo_compiler::validation::Valid;
11use dashmap::DashMap;
12use dashmap::mapref::multiple::RefMulti;
13use dashmap::mapref::multiple::RefMutMulti;
14use derivative::Derivative;
15use extensions::sync::ExtensionsMutex;
16use serde::Deserialize;
17use serde::Serialize;
18use tower::BoxError;
19
20use crate::json_ext::Value;
21use crate::services::layers::query_analysis::ParsedDocument;
22
23pub(crate) mod deprecated;
24pub(crate) mod extensions;
25
26pub(crate) const OPERATION_NAME: &str = "apollo::supergraph::operation_name";
28pub(crate) const OPERATION_KIND: &str = "apollo::supergraph::operation_kind";
30pub(crate) const PERSISTED_QUERY_ID: &str = "apollo::supergraph::persisted_query_id";
32pub(crate) const CONTAINS_GRAPHQL_ERROR: &str = "apollo::telemetry::contains_graphql_error";
36pub(crate) const CHUNK_CONTAINS_GRAPHQL_ERROR: &str =
44 "apollo::telemetry::chunk_contains_graphql_error";
45pub(crate) const COUNTED_ERRORS: &str = "apollo::telemetry::counted_errors";
48pub(crate) const ROUTER_RESPONSE_ERRORS: &str = "apollo::router::response_errors";
52
53pub(crate) use deprecated::context_key_from_deprecated;
54pub(crate) use deprecated::context_key_to_deprecated;
55
56pub(crate) type Entries = Arc<DashMap<String, Value>>;
58
59#[derive(Clone, Deserialize, Serialize, Derivative)]
71#[serde(default)]
72#[derivative(Debug)]
73pub struct Context {
74 entries: Entries,
76
77 #[serde(skip)]
78 extensions: ExtensionsMutex,
79
80 #[serde(skip)]
82 pub(crate) created_at: Instant,
83
84 #[serde(skip)]
85 pub(crate) id: String,
86}
87
88impl Context {
89 pub fn new() -> Self {
91 let id = uuid::Uuid::new_v4()
92 .as_hyphenated()
93 .encode_lower(&mut uuid::Uuid::encode_buffer())
94 .to_string();
95 Context {
96 entries: Default::default(),
97 extensions: ExtensionsMutex::default(),
98 created_at: Instant::now(),
99 id,
100 }
101 }
102}
103
104impl FromIterator<(String, Value)> for Context {
105 fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
106 Self {
107 entries: Arc::new(DashMap::from_iter(iter)),
108 extensions: ExtensionsMutex::default(),
109 created_at: Instant::now(),
110 id: String::new(),
111 }
112 }
113}
114
115impl Context {
116 pub fn extensions(&self) -> &ExtensionsMutex {
125 &self.extensions
126 }
127
128 pub fn contains_key<K>(&self, key: K) -> bool
130 where
131 K: Into<String>,
132 {
133 self.entries.contains_key(&key.into())
134 }
135
136 pub fn get<K, V>(&self, key: K) -> Result<Option<V>, BoxError>
142 where
143 K: Into<String>,
144 V: for<'de> serde::Deserialize<'de>,
145 {
146 self.entries
147 .get(&key.into())
148 .map(|v| serde_json_bytes::from_value(v.value().clone()))
149 .transpose()
150 .map_err(|e| e.into())
151 }
152
153 pub fn insert<K, V>(&self, key: K, value: V) -> Result<Option<V>, BoxError>
159 where
160 K: Into<String>,
161 V: for<'de> serde::Deserialize<'de> + Serialize,
162 {
163 match serde_json_bytes::to_value(value) {
164 Ok(value) => self
165 .entries
166 .insert(key.into(), value)
167 .map(|v| serde_json_bytes::from_value(v))
168 .transpose()
169 .map_err(|e| e.into()),
170 Err(e) => Err(e.into()),
171 }
172 }
173
174 pub fn insert_json_value<K>(&self, key: K, value: Value) -> Option<Value>
178 where
179 K: Into<String>,
180 {
181 self.entries.insert(key.into(), value)
182 }
183
184 pub fn get_json_value<K>(&self, key: K) -> Option<Value>
186 where
187 K: Into<String>,
188 {
189 self.entries.get(&key.into()).map(|v| v.value().clone())
190 }
191
192 pub fn upsert<K, V>(&self, key: K, upsert: impl FnOnce(V) -> V) -> Result<(), BoxError>
205 where
206 K: Into<String>,
207 V: for<'de> serde::Deserialize<'de> + Serialize + Default,
208 {
209 let key = key.into();
210 self.entries
211 .entry(key.clone())
212 .or_try_insert_with(|| serde_json_bytes::to_value::<V>(Default::default()))?;
213 let mut result = Ok(());
214 self.entries
215 .alter(&key, |_, v| match serde_json_bytes::from_value(v.clone()) {
216 Ok(value) => match serde_json_bytes::to_value((upsert)(value)) {
217 Ok(value) => value,
218 Err(e) => {
219 result = Err(e);
220 v
221 }
222 },
223 Err(e) => {
224 result = Err(e);
225 v
226 }
227 });
228 result.map_err(|e| e.into())
229 }
230
231 pub(crate) fn upsert_json_value<K>(&self, key: K, upsert: impl FnOnce(Value) -> Value)
238 where
239 K: Into<String>,
240 {
241 let key = key.into();
242 self.entries.entry(key.clone()).or_insert(Value::Null);
243 self.entries.alter(&key, |_, v| upsert(v));
244 }
245
246 pub(crate) fn try_into_iter(
248 self,
249 ) -> Result<impl IntoIterator<Item = (String, Value)>, BoxError> {
250 Ok(Arc::try_unwrap(self.entries)
251 .map_err(|_e| anyhow::anyhow!("cannot take ownership of dashmap"))?
252 .into_iter())
253 }
254
255 pub fn iter(&self) -> impl Iterator<Item = RefMulti<'_, String, Value>> + '_ {
257 self.entries.iter()
258 }
259
260 pub fn iter_mut(&self) -> impl Iterator<Item = RefMutMulti<'_, String, Value>> + '_ {
262 self.entries.iter_mut()
263 }
264
265 pub(crate) fn extend(&self, other: &Context) {
266 for kv in other.entries.iter() {
267 self.entries.insert(kv.key().clone(), kv.value().clone());
268 }
269 }
270
271 pub(crate) fn retain(&self, f: impl Fn(&String, &Value) -> bool) {
272 self.entries.retain(|k, v| f(k, v));
273 }
274
275 pub(crate) fn len(&self) -> usize {
276 self.entries.len()
277 }
278
279 pub(crate) fn executable_document(&self) -> Option<Arc<Valid<ExecutableDocument>>> {
281 self.extensions()
282 .with_lock(|lock| lock.get::<ParsedDocument>().map(|d| d.executable.clone()))
283 }
284}
285
286impl Default for Context {
287 fn default() -> Self {
288 Self::new()
289 }
290}
291
292#[cfg(test)]
293mod test {
294 use crate::Configuration;
295 use crate::Context;
296 use crate::spec::Query;
297 use crate::spec::Schema;
298
299 #[test]
300 fn test_context_insert() {
301 let c = Context::new();
302 assert!(c.insert("key1", 1).is_ok());
303 assert_eq!(c.get("key1").unwrap(), Some(1));
304 }
305
306 #[test]
307 fn test_context_overwrite() {
308 let c = Context::new();
309 assert!(c.insert("overwrite", 2).is_ok());
310 assert!(c.insert("overwrite", 3).is_ok());
311 assert_eq!(c.get("overwrite").unwrap(), Some(3));
312 }
313
314 #[test]
315 fn test_context_upsert() {
316 let c = Context::new();
317 assert!(c.insert("present", 1).is_ok());
318 assert!(c.upsert("present", |v: usize| v + 1).is_ok());
319 assert_eq!(c.get("present").unwrap(), Some(2));
320 assert!(c.upsert("not_present", |v: usize| v + 1).is_ok());
321 assert_eq!(c.get("not_present").unwrap(), Some(1));
322 }
323
324 #[test]
325 fn test_context_marshall_errors() {
326 let c = Context::new();
327 assert!(c.insert("string", "Some value".to_string()).is_ok());
328 assert!(c.upsert("string", |v: usize| v + 1).is_err());
329 }
330
331 #[test]
332 fn it_iterates_over_context() {
333 let c = Context::new();
334 assert!(c.insert("one", 1).is_ok());
335 assert!(c.insert("two", 2).is_ok());
336 assert_eq!(c.iter().count(), 2);
337 assert_eq!(
338 c.iter()
339 .map(|r| serde_json_bytes::from_value::<usize>(r.value().clone()).unwrap())
341 .sum::<usize>(),
342 3
343 );
344 }
345
346 #[test]
347 fn it_iterates_mutably_over_context() {
348 let c = Context::new();
349 assert!(c.insert("one", 1usize).is_ok());
350 assert!(c.insert("two", 2usize).is_ok());
351 assert_eq!(c.iter().count(), 2);
352 c.iter_mut().for_each(|mut r| {
353 let new: usize = serde_json_bytes::from_value::<usize>(r.value().clone()).unwrap() + 1;
355 *r = new.into();
356 });
357 assert_eq!(c.get("one").unwrap(), Some(2));
358 assert_eq!(c.get("two").unwrap(), Some(3));
359 }
360
361 #[test]
362 fn context_extensions() {
363 let c = Context::new();
365 c.extensions().with_lock(|lock| lock.insert(1usize));
366 let v = c
367 .extensions()
368 .with_lock(|lock| lock.get::<usize>().cloned());
369 assert_eq!(v, Some(1usize));
370 }
371
372 #[test]
373 fn test_executable_document_access() {
374 let c = Context::new();
375 let schema = include_str!("../testdata/minimal_supergraph.graphql");
376 let schema = Schema::parse(schema, &Default::default()).unwrap();
377 let document =
378 Query::parse_document("{ me }", None, &schema, &Configuration::default()).unwrap();
379 assert!(c.executable_document().is_none());
380 c.extensions().with_lock(|lock| lock.insert(document));
381 assert!(c.executable_document().is_some());
382 }
383}