apollo_router/
json_ext.rs

1//! Performance oriented JSON manipulation.
2
3#![allow(missing_docs)] // FIXME
4
5use std::cmp::min;
6use std::fmt;
7
8use num_traits::ToPrimitive;
9use once_cell::sync::Lazy;
10use regex::Captures;
11use regex::Regex;
12use serde::Deserialize;
13use serde::Serialize;
14use serde_json_bytes::ByteString;
15use serde_json_bytes::Entry;
16use serde_json_bytes::Map;
17pub(crate) use serde_json_bytes::Value;
18
19use crate::error::FetchError;
20use crate::spec::Schema;
21use crate::spec::TYPENAME;
22
23/// A JSON object.
24pub(crate) type Object = Map<ByteString, Value>;
25
26const FRAGMENT_PREFIX: &str = "... on ";
27
28static TYPE_CONDITIONS_REGEX: Lazy<Regex> = Lazy::new(|| {
29    Regex::new(r"\|\[(?<condition>.+?)?\]")
30        .expect("this regex to check for type conditions is valid")
31});
32
33/// Extract the condition list from the regex captures.
34fn extract_matched_conditions(caps: &Captures) -> TypeConditions {
35    caps.name("condition")
36        .map(|c| c.as_str().split(',').map(|s| s.to_string()).collect())
37        .unwrap_or_default()
38}
39
40fn split_path_element_and_type_conditions(s: &str) -> (String, Option<TypeConditions>) {
41    let mut type_conditions = None;
42    let path_element = TYPE_CONDITIONS_REGEX.replace(s, |caps: &Captures| {
43        type_conditions = Some(extract_matched_conditions(caps));
44        ""
45    });
46    (path_element.to_string(), type_conditions)
47}
48
49macro_rules! extract_key_value_from_object {
50    ($object:expr, $key:literal, $pattern:pat => $var:ident) => {{
51        match $object.remove($key) {
52            Some($pattern) => Ok(Some($var)),
53            None | Some(crate::json_ext::Value::Null) => Ok(None),
54            _ => Err(concat!("invalid type for key: ", $key)),
55        }
56    }};
57    ($object:expr, $key:literal) => {{
58        match $object.remove($key) {
59            None | Some(crate::json_ext::Value::Null) => None,
60            Some(value) => Some(value),
61        }
62    }};
63}
64
65macro_rules! ensure_array {
66    ($value:expr) => {{
67        match $value {
68            crate::json_ext::Value::Array(a) => Ok(a),
69            _ => Err("invalid type, expected an array"),
70        }
71    }};
72}
73
74macro_rules! ensure_object {
75    ($value:expr) => {{
76        match $value {
77            crate::json_ext::Value::Object(o) => Ok(o),
78            _ => Err("invalid type, expected an object"),
79        }
80    }};
81}
82
83#[doc(hidden)]
84/// Extension trait for [`serde_json::Value`].
85pub(crate) trait ValueExt {
86    /// Deep merge the JSON objects, array and override the values in `&mut self` if they already
87    /// exists.
88    #[track_caller]
89    fn deep_merge(&mut self, other: Self);
90
91    /// Deep merge two JSON objects, overwriting values in `self` if it has the same key as `other`.
92    /// For GraphQL response objects, this uses schema information to avoid overwriting a concrete
93    /// `__typename` with an interface name.
94    #[track_caller]
95    fn type_aware_deep_merge(&mut self, other: Self, schema: &Schema);
96
97    /// Returns `true` if the values are equal and the objects are ordered the same.
98    ///
99    /// **Note:** this is recursive.
100    #[cfg(test)]
101    fn eq_and_ordered(&self, other: &Self) -> bool;
102
103    /// Returns `true` if the set is a subset of another, i.e., `other` contains at least all the
104    /// values in `self`.
105    #[track_caller]
106    #[cfg(test)]
107    fn is_subset(&self, superset: &Value) -> bool;
108
109    /// Create a `Value` by inserting a value at a subpath.
110    ///
111    /// This will create objects, arrays and null nodes as needed if they
112    /// are not present: the resulting Value is meant to be merged with an
113    /// existing one that contains those nodes.
114    #[track_caller]
115    fn from_path(path: &Path, value: Value) -> Value;
116
117    /// Insert a `Value` at a `Path`
118    #[track_caller]
119    fn insert(&mut self, path: &Path, value: Value) -> Result<(), FetchError>;
120
121    /// Get a `Value` from a `Path`
122    #[track_caller]
123    fn get_path<'a>(&'a self, schema: &Schema, path: &'a Path) -> Result<&'a Value, FetchError>;
124
125    /// Select all values matching a `Path`.
126    ///
127    /// the function passed as argument will be called with the values found and their Path
128    /// if it encounters an invalid value, it will ignore it and continue
129    #[track_caller]
130    fn select_values_and_paths<'a, F>(&'a self, schema: &Schema, path: &'a Path, f: F)
131    where
132        F: FnMut(&Path, &'a Value);
133
134    /// Select all values matching a `Path`, and allows to mutate those values.
135    ///
136    /// The behavior of the method is otherwise the same as it's non-mutable counterpart
137    #[track_caller]
138    fn select_values_and_paths_mut<'a, F>(&'a mut self, schema: &Schema, path: &'a Path, f: F)
139    where
140        F: FnMut(&Path, &'a mut Value);
141
142    #[track_caller]
143    fn is_valid_float_input(&self) -> bool;
144
145    #[track_caller]
146    fn is_valid_int_input(&self) -> bool;
147
148    #[track_caller]
149    fn is_valid_id_input(&self) -> bool;
150
151    /// Returns whether this value is an object that matches the provided type.
152    ///
153    /// More precisely, this checks that this value is an object, looks at
154    /// its `__typename` field and checks if that  `__typename` is either
155    /// `maybe_type` or a subtype of it.
156    ///
157    /// If the value is not an object, this will always return `false`, but
158    /// if the value is an object with no `__typename` field, then we default
159    /// to `true` (meaning that the absences of way to check, we assume the
160    /// value is of your expected type).
161    ///
162    /// TODO: in theory, this later default behaviour shouldn't matter since
163    /// we should avoid calling this in cases where the `__typename` is
164    /// unknown, but it is currently *relied* on due to some not-quite-right
165    /// behaviour. See the comment in `ExecutionService.call` around the call
166    /// to `select_values_and_paths` for details (the later relies on this
167    /// function to handle `PathElement::Fragment`).
168    #[track_caller]
169    fn is_object_of_type(&self, schema: &Schema, maybe_type: &str) -> bool;
170
171    fn as_i32(&self) -> Option<i32>;
172}
173
174impl ValueExt for Value {
175    fn deep_merge(&mut self, other: Self) {
176        match (self, other) {
177            (Value::Object(a), Value::Object(b)) => {
178                for (key, value) in b.into_iter() {
179                    match a.entry(key) {
180                        Entry::Vacant(e) => {
181                            e.insert(value);
182                        }
183                        Entry::Occupied(e) => {
184                            e.into_mut().deep_merge(value);
185                        }
186                    }
187                }
188            }
189            (Value::Array(a), Value::Array(mut b)) => {
190                for (b_value, a_value) in b.drain(..min(a.len(), b.len())).zip(a.iter_mut()) {
191                    a_value.deep_merge(b_value);
192                }
193
194                a.extend(b);
195            }
196            (_, Value::Null) => {}
197            (Value::Object(_), Value::Array(_)) => {
198                failfast_debug!("trying to replace an object with an array");
199            }
200            (Value::Array(_), Value::Object(_)) => {
201                failfast_debug!("trying to replace an array with an object");
202            }
203            (a, b) => {
204                if b != Value::Null {
205                    *a = b;
206                }
207            }
208        }
209    }
210
211    fn type_aware_deep_merge(&mut self, other: Self, schema: &Schema) {
212        match (self, other) {
213            (Value::Object(a), Value::Object(b)) => {
214                for (key, value) in b.into_iter() {
215                    let k = key.clone();
216                    match a.entry(key) {
217                        Entry::Vacant(e) => {
218                            e.insert(value);
219                        }
220                        Entry::Occupied(e) => match (e.into_mut(), value) {
221                            (Value::String(type1), Value::String(type2))
222                                if k.as_str() == TYPENAME =>
223                            {
224                                // If type1 is a subtype of type2, we skip this overwrite to preserve the more specific `__typename`
225                                // in the response. Ideally, we could use `Schema::is_implementation`, but that looks to be buggy
226                                // and does not catch the problem we are trying to resolve.
227                                if !schema.is_subtype(type2.as_str(), type1.as_str()) {
228                                    *type1 = type2;
229                                }
230                            }
231                            (t, s) => t.type_aware_deep_merge(s, schema),
232                        },
233                    }
234                }
235            }
236            (Value::Array(a), Value::Array(mut b)) => {
237                for (b_value, a_value) in b.drain(..min(a.len(), b.len())).zip(a.iter_mut()) {
238                    a_value.type_aware_deep_merge(b_value, schema);
239                }
240
241                a.extend(b);
242            }
243            (_, Value::Null) => {}
244            (Value::Object(_), Value::Array(_)) => {
245                failfast_debug!("trying to replace an object with an array");
246            }
247            (Value::Array(_), Value::Object(_)) => {
248                failfast_debug!("trying to replace an array with an object");
249            }
250            (a, b) => {
251                if b != Value::Null {
252                    *a = b;
253                }
254            }
255        }
256    }
257
258    #[cfg(test)]
259    fn eq_and_ordered(&self, other: &Self) -> bool {
260        match (self, other) {
261            (Value::Object(a), Value::Object(b)) => {
262                let mut it_a = a.iter();
263                let mut it_b = b.iter();
264
265                loop {
266                    match (it_a.next(), it_b.next()) {
267                        (Some(_), None) | (None, Some(_)) => break false,
268                        (None, None) => break true,
269                        (Some((field_a, value_a)), Some((field_b, value_b)))
270                            if field_a == field_b && ValueExt::eq_and_ordered(value_a, value_b) =>
271                        {
272                            continue;
273                        }
274                        (Some(_), Some(_)) => break false,
275                    }
276                }
277            }
278            (Value::Array(a), Value::Array(b)) => {
279                let mut it_a = a.iter();
280                let mut it_b = b.iter();
281
282                loop {
283                    match (it_a.next(), it_b.next()) {
284                        (Some(_), None) | (None, Some(_)) => break false,
285                        (None, None) => break true,
286                        (Some(value_a), Some(value_b))
287                            if ValueExt::eq_and_ordered(value_a, value_b) =>
288                        {
289                            continue;
290                        }
291                        (Some(_), Some(_)) => break false,
292                    }
293                }
294            }
295            (a, b) => a == b,
296        }
297    }
298
299    #[cfg(test)]
300    fn is_subset(&self, superset: &Value) -> bool {
301        match (self, superset) {
302            (Value::Object(subset), Value::Object(superset)) => {
303                subset.iter().all(|(key, value)| {
304                    if let Some(other) = superset.get(key) {
305                        value.is_subset(other)
306                    } else {
307                        false
308                    }
309                })
310            }
311            (Value::Array(subset), Value::Array(superset)) => {
312                subset.len() == superset.len()
313                    && subset.iter().enumerate().all(|(index, value)| {
314                        if let Some(other) = superset.get(index) {
315                            value.is_subset(other)
316                        } else {
317                            false
318                        }
319                    })
320            }
321            (a, b) => a == b,
322        }
323    }
324
325    #[track_caller]
326    fn from_path(path: &Path, value: Value) -> Value {
327        let mut res_value = Value::default();
328        let mut current_node = &mut res_value;
329
330        for p in path.iter() {
331            match p {
332                // Type conditions don't matter here since we're just creating a default value.
333                PathElement::Flatten(_) => {
334                    return res_value;
335                }
336
337                &PathElement::Index(index) => match current_node {
338                    Value::Array(a) => {
339                        for _ in 0..index {
340                            a.push(Value::default());
341                        }
342                        a.push(Value::default());
343                        current_node = a
344                            .get_mut(index)
345                            .expect("we just created the value at that index");
346                    }
347                    Value::Null => {
348                        let mut a = Vec::new();
349                        for _ in 0..index {
350                            a.push(Value::default());
351                        }
352                        a.push(Value::default());
353
354                        *current_node = Value::Array(a);
355                        current_node = current_node
356                            .as_array_mut()
357                            .expect("current_node was just set to a Value::Array")
358                            .get_mut(index)
359                            .expect("we just created the value at that index");
360                    }
361                    other => unreachable!("unreachable node: {:?}", other),
362                },
363                // Type conditions don't matter here since we're just creating a default value.
364                PathElement::Key(k, _) => {
365                    let mut m = Map::new();
366                    m.insert(k.as_str(), Value::default());
367
368                    *current_node = Value::Object(m);
369                    current_node = current_node
370                        .as_object_mut()
371                        .expect("current_node was just set to a Value::Object")
372                        .get_mut(k.as_str())
373                        .expect("the value at that key was just inserted");
374                }
375                PathElement::Fragment(_) => {}
376            }
377        }
378
379        *current_node = value;
380        res_value
381    }
382
383    /// Insert a `Value` at a `Path`
384    #[track_caller]
385    fn insert(&mut self, path: &Path, mut value: Value) -> Result<(), FetchError> {
386        let mut current_node = self;
387
388        for p in path.iter() {
389            match p {
390                PathElement::Flatten(type_conditions) => {
391                    value = filter_type_conditions(value, type_conditions);
392                    if current_node.is_null() {
393                        let a = Vec::new();
394                        *current_node = Value::Array(a);
395                    } else if !current_node.is_array() {
396                        return Err(FetchError::ExecutionPathNotFound {
397                            reason: "expected an array".to_string(),
398                        });
399                    }
400                }
401
402                &PathElement::Index(index) => match current_node {
403                    Value::Array(a) => {
404                        // add more elements if the index is after the end
405                        for _ in a.len()..index + 1 {
406                            a.push(Value::default());
407                        }
408                        current_node = a
409                            .get_mut(index)
410                            .expect("we just created the value at that index");
411                    }
412                    Value::Null => {
413                        let mut a = Vec::new();
414                        for _ in 0..index + 1 {
415                            a.push(Value::default());
416                        }
417
418                        *current_node = Value::Array(a);
419                        current_node = current_node
420                            .as_array_mut()
421                            .expect("current_node was just set to a Value::Array")
422                            .get_mut(index)
423                            .expect("we just created the value at that index");
424                    }
425                    _other => {
426                        return Err(FetchError::ExecutionPathNotFound {
427                            reason: "expected an array".to_string(),
428                        });
429                    }
430                },
431                PathElement::Key(k, type_conditions) => {
432                    value = filter_type_conditions(value, type_conditions);
433                    match current_node {
434                        Value::Object(o) => {
435                            current_node = o
436                                .get_mut(k.as_str())
437                                .expect("the value at that key was just inserted");
438                        }
439                        Value::Null => {
440                            let mut m = Map::new();
441                            m.insert(k.as_str(), Value::default());
442
443                            *current_node = Value::Object(m);
444                            current_node = current_node
445                                .as_object_mut()
446                                .expect("current_node was just set to a Value::Object")
447                                .get_mut(k.as_str())
448                                .expect("the value at that key was just inserted");
449                        }
450                        _other => {
451                            return Err(FetchError::ExecutionPathNotFound {
452                                reason: "expected an object".to_string(),
453                            });
454                        }
455                    }
456                }
457                PathElement::Fragment(_) => {}
458            }
459        }
460
461        *current_node = value;
462        Ok(())
463    }
464
465    /// Get a `Value` from a `Path`
466    #[track_caller]
467    fn get_path<'a>(&'a self, schema: &Schema, path: &'a Path) -> Result<&'a Value, FetchError> {
468        let mut res = Err(FetchError::ExecutionPathNotFound {
469            reason: "value not found".to_string(),
470        });
471        iterate_path(
472            schema,
473            &mut Path::default(),
474            &path.0,
475            self,
476            &mut |_path, value| {
477                res = Ok(value);
478            },
479        );
480        res
481    }
482
483    #[track_caller]
484    fn select_values_and_paths<'a, F>(&'a self, schema: &Schema, path: &'a Path, mut f: F)
485    where
486        F: FnMut(&Path, &'a Value),
487    {
488        iterate_path(schema, &mut Path::default(), &path.0, self, &mut f)
489    }
490
491    #[track_caller]
492    fn select_values_and_paths_mut<'a, F>(&'a mut self, schema: &Schema, path: &'a Path, mut f: F)
493    where
494        F: FnMut(&Path, &'a mut Value),
495    {
496        iterate_path_mut(schema, &mut Path::default(), &path.0, self, &mut f)
497    }
498
499    #[track_caller]
500    fn is_valid_id_input(&self) -> bool {
501        // https://spec.graphql.org/October2021/#sec-ID.Input-Coercion
502        match self {
503            // Any string and integer values are accepted
504            Value::String(_) => true,
505            Value::Number(n) => n.is_i64() || n.is_u64(),
506            _ => false,
507        }
508    }
509
510    #[track_caller]
511    fn is_valid_float_input(&self) -> bool {
512        // https://spec.graphql.org/draft/#sec-Float.Input-Coercion
513        match self {
514            // When expected as an input type, both integer and float input values are accepted.
515            Value::Number(n) if n.is_f64() => true,
516            // Integer input values are coerced to Float by adding an empty fractional part, for example 1.0 for the integer input value 1.
517            Value::Number(n) => n.is_i64(),
518            // All other input values, including strings with numeric content, must raise a request error indicating an incorrect type.
519            _ => false,
520        }
521    }
522
523    #[track_caller]
524    fn is_valid_int_input(&self) -> bool {
525        // https://spec.graphql.org/June2018/#sec-Int
526        // The Int scalar type represents a signed 32‐bit numeric non‐fractional value.
527        // When expected as an input type, only integer input values are accepted.
528        // All other input values, including strings with numeric content, must raise a query error indicating an incorrect type.
529        self.as_i64().and_then(|x| i32::try_from(x).ok()).is_some()
530            || self.as_u64().and_then(|x| i32::try_from(x).ok()).is_some()
531    }
532
533    #[track_caller]
534    fn is_object_of_type(&self, schema: &Schema, maybe_type: &str) -> bool {
535        self.is_object()
536            && self
537                .get(TYPENAME)
538                .and_then(|v| v.as_str())
539                .is_none_or(|typename| {
540                    typename == maybe_type || schema.is_subtype(maybe_type, typename)
541                })
542    }
543
544    fn as_i32(&self) -> Option<i32> {
545        self.as_i64()?.to_i32()
546    }
547}
548
549fn filter_type_conditions(value: Value, type_conditions: &Option<TypeConditions>) -> Value {
550    if let Some(tc) = type_conditions {
551        match value {
552            Value::Object(ref o) => {
553                if let Some(Value::String(type_name)) = &o.get("__typename")
554                    && !tc.iter().any(|tc| tc.as_str() == type_name.as_str())
555                {
556                    return Value::Null;
557                }
558            }
559            Value::Array(v) => {
560                return Value::Array(
561                    v.into_iter()
562                        .map(|v| filter_type_conditions(v, type_conditions))
563                        .collect(),
564                );
565            }
566            _ => {}
567        }
568    }
569    value
570}
571
572fn iterate_path<'a, F>(
573    schema: &Schema,
574    parent: &mut Path,
575    path: &'a [PathElement],
576    data: &'a Value,
577    f: &mut F,
578) where
579    F: FnMut(&Path, &'a Value),
580{
581    match path.first() {
582        None => f(parent, data),
583        Some(PathElement::Flatten(type_conditions)) => {
584            if let Some(array) = data.as_array() {
585                for (i, value) in array.iter().enumerate() {
586                    if let Some(tc) = type_conditions {
587                        if !tc.is_empty() {
588                            if let Value::Object(o) = value
589                                && let Some(Value::String(type_name)) = o.get("__typename")
590                                && tc.iter().any(|tc| tc.as_str() == type_name.as_str())
591                            {
592                                parent.push(PathElement::Index(i));
593                                iterate_path(schema, parent, &path[1..], value, f);
594                                parent.pop();
595                            }
596
597                            if let Value::Array(array) = value {
598                                for (i, value) in array.iter().enumerate() {
599                                    if let Value::Object(o) = value
600                                        && let Some(Value::String(type_name)) = o.get("__typename")
601                                        && tc.iter().any(|tc| tc.as_str() == type_name.as_str())
602                                    {
603                                        parent.push(PathElement::Index(i));
604                                        iterate_path(schema, parent, &path[1..], value, f);
605                                        parent.pop();
606                                    }
607                                }
608                            }
609                        }
610                    } else {
611                        parent.push(PathElement::Index(i));
612                        iterate_path(schema, parent, &path[1..], value, f);
613                        parent.pop();
614                    }
615                }
616            }
617        }
618        Some(PathElement::Index(i)) => {
619            if let Value::Array(a) = data
620                && let Some(value) = a.get(*i)
621            {
622                parent.push(PathElement::Index(*i));
623                iterate_path(schema, parent, &path[1..], value, f);
624                parent.pop();
625            }
626        }
627        Some(PathElement::Key(k, type_conditions)) => {
628            if let Some(tc) = type_conditions {
629                if !tc.is_empty() {
630                    if let Value::Object(o) = data {
631                        if let Some(value) = o.get(k.as_str())
632                            && let Some(Value::String(type_name)) = value.get("__typename")
633                            && tc.iter().any(|tc| tc.as_str() == type_name.as_str())
634                        {
635                            parent.push(PathElement::Key(k.to_string(), None));
636                            iterate_path(schema, parent, &path[1..], value, f);
637                            parent.pop();
638                        }
639                    } else if let Value::Array(array) = data {
640                        for (i, value) in array.iter().enumerate() {
641                            if let Value::Object(o) = value
642                                && let Some(Value::String(type_name)) = o.get("__typename")
643                                && tc.iter().any(|tc| tc.as_str() == type_name.as_str())
644                            {
645                                parent.push(PathElement::Index(i));
646                                iterate_path(schema, parent, path, value, f);
647                                parent.pop();
648                            }
649                        }
650                    }
651                }
652            } else if let Value::Object(o) = data {
653                if let Some(value) = o.get(k.as_str()) {
654                    parent.push(PathElement::Key(k.to_string(), None));
655                    iterate_path(schema, parent, &path[1..], value, f);
656                    parent.pop();
657                }
658            } else if let Value::Array(array) = data {
659                for (i, value) in array.iter().enumerate() {
660                    parent.push(PathElement::Index(i));
661                    iterate_path(schema, parent, path, value, f);
662                    parent.pop();
663                }
664            }
665        }
666        Some(PathElement::Fragment(name)) => {
667            if data.is_object_of_type(schema, name) {
668                // Note that (not unlike `Flatten`) we do not include the fragment in the `parent`
669                // path, because we want that path to be a "pure" response path. Fragments in path
670                // are used to essentially create a type-based choice in a "selection" path, but
671                // `parent` is a direct path to a specific position in the value and do not need
672                // fragments.
673                iterate_path(schema, parent, &path[1..], data, f);
674            } else if let Value::Array(array) = data {
675                for (i, value) in array.iter().enumerate() {
676                    parent.push(PathElement::Index(i));
677                    iterate_path(schema, parent, path, value, f);
678                    parent.pop();
679                }
680            }
681        }
682    }
683}
684
685fn iterate_path_mut<'a, F>(
686    schema: &Schema,
687    parent: &mut Path,
688    path: &'a [PathElement],
689    data: &'a mut Value,
690    f: &mut F,
691) where
692    F: FnMut(&Path, &'a mut Value),
693{
694    match path.first() {
695        None => f(parent, data),
696        Some(PathElement::Flatten(type_conditions)) => {
697            if let Some(array) = data.as_array_mut() {
698                for (i, value) in array.iter_mut().enumerate() {
699                    if let Some(tc) = type_conditions {
700                        if !tc.is_empty()
701                            && let Value::Object(o) = value
702                            && let Some(Value::String(type_name)) = o.get("__typename")
703                            && tc.iter().any(|tc| tc.as_str() == type_name.as_str())
704                        {
705                            parent.push(PathElement::Index(i));
706                            iterate_path_mut(schema, parent, &path[1..], value, f);
707                            parent.pop();
708                        }
709                    } else {
710                        parent.push(PathElement::Index(i));
711                        iterate_path_mut(schema, parent, &path[1..], value, f);
712                        parent.pop();
713                    }
714                }
715            }
716        }
717        Some(PathElement::Index(i)) => {
718            if let Value::Array(a) = data
719                && let Some(value) = a.get_mut(*i)
720            {
721                parent.push(PathElement::Index(*i));
722                iterate_path_mut(schema, parent, &path[1..], value, f);
723                parent.pop();
724            }
725        }
726        Some(PathElement::Key(k, type_conditions)) => {
727            if let Some(tc) = type_conditions {
728                if !tc.is_empty() {
729                    if let Value::Object(o) = data {
730                        if let Some(value) = o.get_mut(k.as_str())
731                            && let Some(Value::String(type_name)) = value.get("__typename")
732                            && tc.iter().any(|tc| tc.as_str() == type_name.as_str())
733                        {
734                            parent.push(PathElement::Key(k.to_string(), None));
735                            iterate_path_mut(schema, parent, &path[1..], value, f);
736                            parent.pop();
737                        }
738                    } else if let Value::Array(array) = data {
739                        for (i, value) in array.iter_mut().enumerate() {
740                            if let Value::Object(o) = value
741                                && let Some(Value::String(type_name)) = o.get("__typename")
742                                && tc.iter().any(|tc| tc.as_str() == type_name.as_str())
743                            {
744                                parent.push(PathElement::Index(i));
745                                iterate_path_mut(schema, parent, path, value, f);
746                                parent.pop();
747                            }
748                        }
749                    }
750                }
751            } else if let Value::Object(o) = data {
752                if let Some(value) = o.get_mut(k.as_str()) {
753                    parent.push(PathElement::Key(k.to_string(), None));
754                    iterate_path_mut(schema, parent, &path[1..], value, f);
755                    parent.pop();
756                }
757            } else if let Value::Array(array) = data {
758                for (i, value) in array.iter_mut().enumerate() {
759                    parent.push(PathElement::Index(i));
760                    iterate_path_mut(schema, parent, path, value, f);
761                    parent.pop();
762                }
763            }
764        }
765        Some(PathElement::Fragment(name)) => {
766            if data.is_object_of_type(schema, name) {
767                // Note that (not unlike `Flatten`) we do not include the fragment in the `parent`
768                // path, because we want that path to be a "pure" response path. Fragments in path
769                // are used to essentially create a type-based choice in a "selection" path, but
770                // `parent` is a direct path to a specific position in the value and do not need
771                // fragments.
772                iterate_path_mut(schema, parent, &path[1..], data, f);
773            } else if let Value::Array(array) = data {
774                for (i, value) in array.iter_mut().enumerate() {
775                    parent.push(PathElement::Index(i));
776                    iterate_path_mut(schema, parent, path, value, f);
777                    parent.pop();
778                }
779            }
780        }
781    }
782}
783
784/// A GraphQL path element that is composes of strings or numbers.
785/// e.g `/book/3/name`
786#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
787#[serde(untagged)]
788pub enum PathElement {
789    /// A path element that given an array will flatmap the content.
790    #[serde(
791        deserialize_with = "deserialize_flatten",
792        serialize_with = "serialize_flatten"
793    )]
794    Flatten(Option<TypeConditions>),
795
796    /// An index path element.
797    Index(usize),
798
799    /// A fragment application
800    #[serde(
801        deserialize_with = "deserialize_fragment",
802        serialize_with = "serialize_fragment"
803    )]
804    Fragment(String),
805
806    /// A key path element.
807    #[serde(deserialize_with = "deserialize_key", serialize_with = "serialize_key")]
808    Key(String, Option<TypeConditions>),
809}
810
811type TypeConditions = Vec<String>;
812
813#[derive(Clone, Debug, Eq, PartialEq)]
814pub enum ResponsePathElement<'a> {
815    /// An index path element.
816    Index(usize),
817
818    /// A key path element.
819    Key(&'a str),
820}
821
822fn deserialize_flatten<'de, D>(deserializer: D) -> Result<Option<TypeConditions>, D::Error>
823where
824    D: serde::Deserializer<'de>,
825{
826    deserializer.deserialize_str(FlattenVisitor)
827}
828
829struct FlattenVisitor;
830
831impl serde::de::Visitor<'_> for FlattenVisitor {
832    type Value = Option<TypeConditions>;
833
834    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
835        write!(
836            formatter,
837            "a string that is '@', potentially followed by type conditions"
838        )
839    }
840
841    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
842    where
843        E: serde::de::Error,
844    {
845        let (path_element, type_conditions) = split_path_element_and_type_conditions(s);
846        if path_element == "@" {
847            Ok(type_conditions)
848        } else {
849            Err(serde::de::Error::invalid_value(
850                serde::de::Unexpected::Str(s),
851                &self,
852            ))
853        }
854    }
855}
856
857fn serialize_flatten<S>(
858    type_conditions: &Option<TypeConditions>,
859    serializer: S,
860) -> Result<S::Ok, S::Error>
861where
862    S: serde::Serializer,
863{
864    let tc_string = if let Some(c) = type_conditions {
865        format!("|[{}]", c.join(","))
866    } else {
867        "".to_string()
868    };
869    let res = format!("@{tc_string}");
870    serializer.serialize_str(res.as_str())
871}
872
873fn deserialize_key<'de, D>(deserializer: D) -> Result<(String, Option<TypeConditions>), D::Error>
874where
875    D: serde::Deserializer<'de>,
876{
877    deserializer.deserialize_str(KeyVisitor)
878}
879
880struct KeyVisitor;
881
882impl serde::de::Visitor<'_> for KeyVisitor {
883    type Value = (String, Option<TypeConditions>);
884
885    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
886        write!(
887            formatter,
888            "a string, potentially followed by type conditions"
889        )
890    }
891
892    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
893    where
894        E: serde::de::Error,
895    {
896        Ok(split_path_element_and_type_conditions(s))
897    }
898}
899
900fn serialize_key<S>(
901    key: &String,
902    type_conditions: &Option<TypeConditions>,
903    serializer: S,
904) -> Result<S::Ok, S::Error>
905where
906    S: serde::Serializer,
907{
908    let tc_string = if let Some(c) = type_conditions {
909        format!("|[{}]", c.join(","))
910    } else {
911        "".to_string()
912    };
913    let res = format!("{key}{tc_string}");
914    serializer.serialize_str(res.as_str())
915}
916
917fn deserialize_fragment<'de, D>(deserializer: D) -> Result<String, D::Error>
918where
919    D: serde::Deserializer<'de>,
920{
921    deserializer.deserialize_str(FragmentVisitor)
922}
923
924struct FragmentVisitor;
925
926impl serde::de::Visitor<'_> for FragmentVisitor {
927    type Value = String;
928
929    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
930        write!(formatter, "a string that begins with '... on '")
931    }
932
933    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
934    where
935        E: serde::de::Error,
936    {
937        s.strip_prefix(FRAGMENT_PREFIX)
938            .map(|v| v.to_string())
939            .ok_or_else(|| serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self))
940    }
941}
942
943fn serialize_fragment<S>(name: &String, serializer: S) -> Result<S::Ok, S::Error>
944where
945    S: serde::Serializer,
946{
947    serializer.serialize_str(format!("{FRAGMENT_PREFIX}{name}").as_str())
948}
949
950fn flatten_from_str(s: &str) -> Result<PathElement, String> {
951    let (path_element, type_conditions) = split_path_element_and_type_conditions(s);
952    if path_element != "@" {
953        return Err("invalid flatten".to_string());
954    }
955    Ok(PathElement::Flatten(type_conditions))
956}
957
958fn key_from_str(s: &str) -> Result<PathElement, String> {
959    let (key, type_conditions) = split_path_element_and_type_conditions(s);
960    Ok(PathElement::Key(key, type_conditions))
961}
962
963/// A path into the result document.
964///
965/// This can be composed of strings and numbers
966#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default, Hash)]
967#[serde(transparent)]
968pub struct Path(pub Vec<PathElement>);
969
970impl Path {
971    pub fn from_slice<T: AsRef<str>>(s: &[T]) -> Self {
972        Self(
973            s.iter()
974                .map(|x| x.as_ref())
975                .map(|s| {
976                    if let Ok(index) = s.parse::<usize>() {
977                        PathElement::Index(index)
978                    } else if s.contains('@') {
979                        flatten_from_str(s).unwrap_or(PathElement::Flatten(None))
980                    } else {
981                        s.strip_prefix(FRAGMENT_PREFIX).map_or_else(
982                            || key_from_str(s).unwrap_or(PathElement::Key(s.to_string(), None)),
983                            |name| PathElement::Fragment(name.to_string()),
984                        )
985                    }
986                })
987                .collect(),
988        )
989    }
990
991    pub fn from_response_slice(s: &[ResponsePathElement]) -> Self {
992        Self(
993            s.iter()
994                .map(|x| match x {
995                    ResponsePathElement::Index(index) => PathElement::Index(*index),
996                    ResponsePathElement::Key(s) => PathElement::Key(s.to_string(), None),
997                })
998                .collect(),
999        )
1000    }
1001
1002    pub fn iter(&self) -> impl Iterator<Item = &PathElement> {
1003        self.0.iter()
1004    }
1005
1006    pub fn is_empty(&self) -> bool {
1007        self.0.is_empty()
1008    }
1009
1010    pub fn len(&self) -> usize {
1011        self.0.len()
1012    }
1013
1014    pub fn empty() -> Path {
1015        Path(Default::default())
1016    }
1017
1018    pub fn parent(&self) -> Option<Path> {
1019        if self.is_empty() {
1020            None
1021        } else {
1022            Some(Path(self.iter().take(self.len() - 1).cloned().collect()))
1023        }
1024    }
1025
1026    pub fn join(&self, other: impl AsRef<Self>) -> Self {
1027        let other = other.as_ref();
1028        let mut new = Vec::with_capacity(self.len() + other.len());
1029        new.extend(self.iter().cloned());
1030        new.extend(other.iter().cloned());
1031        Path(new)
1032    }
1033
1034    pub fn push(&mut self, element: PathElement) {
1035        self.0.push(element)
1036    }
1037
1038    pub fn pop(&mut self) -> Option<PathElement> {
1039        self.0.pop()
1040    }
1041
1042    pub fn last(&self) -> Option<&PathElement> {
1043        self.0.last()
1044    }
1045
1046    pub fn last_key(&mut self) -> Option<String> {
1047        self.0.last().and_then(|elem| match elem {
1048            PathElement::Key(key, type_conditions) => {
1049                let mut tc = String::new();
1050                if let Some(c) = type_conditions {
1051                    tc = format!("|[{}]", c.join(","));
1052                };
1053                Some(format!("{key}{tc}"))
1054            }
1055            _ => None,
1056        })
1057    }
1058
1059    pub fn starts_with(&self, other: &Path) -> bool {
1060        self.0.starts_with(&other.0[..])
1061    }
1062
1063    // Removes the empty key if at root (used for TypedConditions)
1064    pub fn remove_empty_key_root(&self) -> Self {
1065        if let Some(PathElement::Key(k, type_conditions)) = self.0.first()
1066            && k.is_empty()
1067            && type_conditions.is_none()
1068        {
1069            return Path(self.iter().skip(1).cloned().collect());
1070        }
1071
1072        self.clone()
1073    }
1074
1075    // Checks whether self and other are equal if PathElement::Flatten and PathElement::Index are
1076    // treated as equal
1077    pub fn equal_if_flattened(&self, other: &Path) -> bool {
1078        if self.len() != other.len() {
1079            return false;
1080        }
1081
1082        for (elem1, elem2) in self.iter().zip(other.iter()) {
1083            let equal_elements = match (elem1, elem2) {
1084                (PathElement::Index(_), PathElement::Flatten(_)) => true,
1085                (PathElement::Flatten(_), PathElement::Index(_)) => true,
1086                (elem1, elem2) => elem1 == elem2,
1087            };
1088            if !equal_elements {
1089                return false;
1090            }
1091        }
1092
1093        true
1094    }
1095}
1096
1097impl FromIterator<PathElement> for Path {
1098    fn from_iter<T: IntoIterator<Item = PathElement>>(iter: T) -> Self {
1099        Path(iter.into_iter().collect())
1100    }
1101}
1102
1103impl AsRef<Path> for Path {
1104    fn as_ref(&self) -> &Path {
1105        self
1106    }
1107}
1108
1109impl<T> From<T> for Path
1110where
1111    T: AsRef<str>,
1112{
1113    fn from(s: T) -> Self {
1114        Self(
1115            s.as_ref()
1116                .split('/')
1117                .map(|s| {
1118                    if let Ok(index) = s.parse::<usize>() {
1119                        PathElement::Index(index)
1120                    } else if s.contains('@') {
1121                        flatten_from_str(s).unwrap()
1122                    } else {
1123                        s.strip_prefix(FRAGMENT_PREFIX).map_or_else(
1124                            || key_from_str(s).unwrap_or(PathElement::Key(s.to_string(), None)),
1125                            |name| PathElement::Fragment(name.to_string()),
1126                        )
1127                    }
1128                })
1129                .collect(),
1130        )
1131    }
1132}
1133
1134impl fmt::Display for Path {
1135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1136        for element in self.iter() {
1137            write!(f, "/")?;
1138            match element {
1139                PathElement::Index(index) => write!(f, "{index}")?,
1140                PathElement::Key(key, type_conditions) => {
1141                    write!(f, "{key}")?;
1142                    if let Some(c) = type_conditions {
1143                        write!(f, "|[{}]", c.join(","))?;
1144                    };
1145                }
1146                PathElement::Flatten(type_conditions) => {
1147                    write!(f, "@")?;
1148                    if let Some(c) = type_conditions {
1149                        write!(f, "|[{}]", c.join(","))?;
1150                    };
1151                }
1152                PathElement::Fragment(name) => {
1153                    write!(f, "{FRAGMENT_PREFIX}{name}")?;
1154                }
1155            }
1156        }
1157        Ok(())
1158    }
1159}
1160
1161#[cfg(test)]
1162mod tests {
1163    use serde_json_bytes::json;
1164
1165    use super::*;
1166
1167    macro_rules! assert_is_subset {
1168        ($a:expr, $b:expr $(,)?) => {
1169            assert!($a.is_subset(&$b));
1170        };
1171    }
1172
1173    macro_rules! assert_is_not_subset {
1174        ($a:expr, $b:expr $(,)?) => {
1175            assert!(!$a.is_subset(&$b));
1176        };
1177    }
1178
1179    /// Functions that walk on path needs a schema to handle potential fragment (type conditions) in
1180    /// the path, and so we use the following simple schema for tests. Note however that tests that
1181    /// don't use fragments in the path essentially ignore this schema.
1182    fn test_schema() -> Schema {
1183        Schema::parse(
1184            r#"
1185           schema
1186             @link(url: "https://specs.apollo.dev/link/v1.0")
1187             @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
1188           {
1189             query: Query
1190           }
1191
1192           directive @join__graph(name: String!, url: String!) on ENUM_VALUE
1193           directive @link( url: String as: String for: link__Purpose import: [link__Import]) repeatable on SCHEMA
1194           scalar link__Import
1195
1196           enum join__Graph {
1197             FAKE @join__graph(name:"fake" url: "http://localhost:4001/fake")
1198           }
1199
1200           enum link__Purpose {
1201             SECURITY
1202             EXECUTION
1203           }
1204
1205           type Query {
1206             i: [I]
1207           }
1208
1209           interface I {
1210             x: Int
1211           }
1212
1213           type A implements I {
1214             x: Int
1215           }
1216
1217           type B {
1218             y: Int
1219           }
1220        "#,
1221            &Default::default(),
1222        )
1223        .unwrap()
1224    }
1225
1226    fn select_values<'a>(
1227        schema: &Schema,
1228        path: &'a Path,
1229        data: &'a Value,
1230    ) -> Result<Vec<&'a Value>, FetchError> {
1231        let mut v = Vec::new();
1232        data.select_values_and_paths(schema, path, |_path, value| {
1233            v.push(value);
1234        });
1235        Ok(v)
1236    }
1237
1238    #[test]
1239    fn test_get_at_path() {
1240        let schema = test_schema();
1241        let json = json!({"obj":{"arr":[{"prop1":1},{"prop1":2}]}});
1242        let path = Path::from("obj/arr/1/prop1");
1243        let result = select_values(&schema, &path, &json).unwrap();
1244        assert_eq!(result, vec![&Value::Number(2.into())]);
1245    }
1246
1247    #[test]
1248    fn test_get_at_path_flatmap() {
1249        let schema = test_schema();
1250        let json = json!({"obj":{"arr":[{"prop1":1},{"prop1":2}]}});
1251        let path = Path::from("obj/arr/@");
1252        let result = select_values(&schema, &path, &json).unwrap();
1253        assert_eq!(result, vec![&json!({"prop1":1}), &json!({"prop1":2})]);
1254    }
1255
1256    #[test]
1257    fn test_get_at_path_flatmap_nested() {
1258        let schema = test_schema();
1259        let json = json!({
1260            "obj": {
1261                "arr": [
1262                    {
1263                        "prop1": [
1264                            {"prop2": {"prop3": 1}, "prop4": -1},
1265                            {"prop2": {"prop3": 2}, "prop4": -2},
1266                        ],
1267                    },
1268                    {
1269                        "prop1": [
1270                            {"prop2": {"prop3": 3}, "prop4": -3},
1271                            {"prop2": {"prop3": 4}, "prop4": -4},
1272                        ],
1273                    },
1274                ],
1275            },
1276        });
1277        let path = Path::from("obj/arr/@/prop1/@/prop2");
1278        let result = select_values(&schema, &path, &json).unwrap();
1279        assert_eq!(
1280            result,
1281            vec![
1282                &json!({"prop3":1}),
1283                &json!({"prop3":2}),
1284                &json!({"prop3":3}),
1285                &json!({"prop3":4}),
1286            ],
1287        );
1288    }
1289
1290    #[test]
1291    fn test_deep_merge() {
1292        let mut json = json!({"obj":{"arr":[{"prop1":1},{"prop2":2}]}});
1293        json.deep_merge(json!({"obj":{"arr":[{"prop1":2,"prop3":3},{"prop4":4}]}}));
1294        assert_eq!(
1295            json,
1296            json!({"obj":{"arr":[{"prop1":2, "prop3":3},{"prop2":2, "prop4":4}]}})
1297        );
1298    }
1299
1300    #[test]
1301    fn interface_typename_merging() {
1302        let schema = Schema::parse(
1303                r#"
1304            schema
1305                @link(url: "https://specs.apollo.dev/link/v1.0")
1306                @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
1307            {
1308                query: Query
1309            }
1310            directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
1311            directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR
1312            directive @join__graph(name: String!, url: String!) on ENUM_VALUE
1313
1314            scalar link__Import
1315            scalar join__FieldSet
1316
1317            enum link__Purpose {
1318                SECURITY
1319                EXECUTION
1320            }
1321
1322            enum join__Graph {
1323                TEST @join__graph(name: "test", url: "http://localhost:4001/graphql")
1324            }
1325
1326            interface I {
1327                s: String
1328            }
1329
1330            type C implements I {
1331                s: String
1332            }
1333
1334            type Query {
1335                i: I
1336            }
1337        "#,
1338            &Default::default(),
1339        )
1340        .expect("valid schema");
1341        let mut response1 = json!({
1342            "__typename": "C"
1343        });
1344        let response2 = json!({
1345            "__typename": "I",
1346            "s": "data"
1347        });
1348
1349        response1.type_aware_deep_merge(response2, &schema);
1350
1351        assert_eq!(
1352            response1,
1353            json!({
1354                "__typename": "C",
1355                "s": "data"
1356            })
1357        );
1358    }
1359
1360    #[test]
1361    fn test_is_subset_eq() {
1362        assert_is_subset!(
1363            json!({"obj":{"arr":[{"prop1":1},{"prop4":4}]}}),
1364            json!({"obj":{"arr":[{"prop1":1},{"prop4":4}]}}),
1365        );
1366    }
1367
1368    #[test]
1369    fn test_is_subset_missing_pop() {
1370        assert_is_subset!(
1371            json!({"obj":{"arr":[{"prop1":1},{"prop4":4}]}}),
1372            json!({"obj":{"arr":[{"prop1":1,"prop3":3},{"prop4":4}]}}),
1373        );
1374    }
1375
1376    #[test]
1377    fn test_is_subset_array_lengths_differ() {
1378        assert_is_not_subset!(
1379            json!({"obj":{"arr":[{"prop1":1}]}}),
1380            json!({"obj":{"arr":[{"prop1":1,"prop3":3},{"prop4":4}]}}),
1381        );
1382    }
1383
1384    #[test]
1385    fn test_is_subset_extra_prop() {
1386        assert_is_not_subset!(
1387            json!({"obj":{"arr":[{"prop1":1,"prop3":3},{"prop4":4}]}}),
1388            json!({"obj":{"arr":[{"prop1":1},{"prop4":4}]}}),
1389        );
1390    }
1391
1392    #[test]
1393    fn eq_and_ordered() {
1394        // test not objects
1395        assert!(json!([1, 2, 3]).eq_and_ordered(&json!([1, 2, 3])));
1396        assert!(!json!([1, 3, 2]).eq_and_ordered(&json!([1, 2, 3])));
1397
1398        // test objects not nested
1399        assert!(json!({"foo":1,"bar":2}).eq_and_ordered(&json!({"foo":1,"bar":2})));
1400        assert!(!json!({"foo":1,"bar":2}).eq_and_ordered(&json!({"foo":1,"bar":3})));
1401        assert!(!json!({"foo":1,"bar":2}).eq_and_ordered(&json!({"foo":1,"bar":2,"baz":3})));
1402        assert!(!json!({"foo":1,"bar":2,"baz":3}).eq_and_ordered(&json!({"foo":1,"bar":2})));
1403        assert!(!json!({"bar":2,"foo":1}).eq_and_ordered(&json!({"foo":1,"bar":2})));
1404
1405        // test objects nested
1406        assert!(json!({"baz":{"foo":1,"bar":2}}).eq_and_ordered(&json!({"baz":{"foo":1,"bar":2}})));
1407        assert!(
1408            !json!({"baz":{"bar":2,"foo":1}}).eq_and_ordered(&json!({"baz":{"foo":1,"bar":2}}))
1409        );
1410        assert!(!json!([1,{"bar":2,"foo":1},2]).eq_and_ordered(&json!([1,{"foo":1,"bar":2},2])));
1411    }
1412
1413    #[test]
1414    fn test_from_path() {
1415        let json = json!([{"prop1":1},{"prop1":2}]);
1416        let path = Path::from("obj/arr");
1417        let result = Value::from_path(&path, json);
1418        assert_eq!(result, json!({"obj":{"arr":[{"prop1":1},{"prop1":2}]}}));
1419    }
1420
1421    #[test]
1422    fn test_from_path_index() {
1423        let json = json!({"prop1":1});
1424        let path = Path::from("obj/arr/1");
1425        let result = Value::from_path(&path, json);
1426        assert_eq!(result, json!({"obj":{"arr":[null, {"prop1":1}]}}));
1427    }
1428
1429    #[test]
1430    fn test_from_path_flatten() {
1431        let json = json!({"prop1":1});
1432        let path = Path::from("obj/arr/@/obj2");
1433        let result = Value::from_path(&path, json);
1434        assert_eq!(result, json!({"obj":{"arr":null}}));
1435    }
1436
1437    #[test]
1438    fn test_is_object_of_type() {
1439        let schema = test_schema();
1440
1441        // Basic matching
1442        assert!(json!({ "__typename": "A", "x": "42"}).is_object_of_type(&schema, "A"));
1443
1444        // Matching with subtyping
1445        assert!(json!({ "__typename": "A", "x": "42"}).is_object_of_type(&schema, "I"));
1446
1447        // Matching when missing __typename (see comment on the method declaration).
1448        assert!(json!({ "x": "42"}).is_object_of_type(&schema, "A"));
1449
1450        // Non-matching because not an object
1451        assert!(!json!([{ "__typename": "A", "x": "42"}]).is_object_of_type(&schema, "A"));
1452        assert!(!json!("foo").is_object_of_type(&schema, "I"));
1453        assert!(!json!(42).is_object_of_type(&schema, "I"));
1454
1455        // Non-matching because not of the asked type.
1456        assert!(!json!({ "__typename": "B", "y": "42"}).is_object_of_type(&schema, "A"));
1457        assert!(!json!({ "__typename": "B", "y": "42"}).is_object_of_type(&schema, "I"));
1458    }
1459
1460    #[test]
1461    fn test_get_at_path_with_conditions() {
1462        let schema = test_schema();
1463        let json = json!({
1464            "i": [
1465                {
1466                    "__typename": "A",
1467                    "x": 0,
1468                },
1469                {
1470                    "__typename": "B",
1471                    "y": 1,
1472                },
1473                {
1474                    "__typename": "B",
1475                    "y": 2,
1476                },
1477                {
1478                    "__typename": "A",
1479                    "x": 3,
1480                },
1481            ],
1482        });
1483        let path = Path::from("i/... on A");
1484        let result = select_values(&schema, &path, &json).unwrap();
1485        assert_eq!(
1486            result,
1487            vec![
1488                &json!({
1489                    "__typename": "A",
1490                    "x": 0,
1491                }),
1492                &json!({
1493                    "__typename": "A",
1494                    "x": 3,
1495                }),
1496            ],
1497        );
1498    }
1499
1500    #[test]
1501    fn path_serde_json() {
1502        let path: Path = serde_json::from_str(
1503            r#"[
1504            "k",
1505            "... on T",
1506            "@",
1507            "arr",
1508            3
1509        ]"#,
1510        )
1511        .unwrap();
1512        assert_eq!(
1513            path.0,
1514            vec![
1515                PathElement::Key("k".to_string(), None),
1516                PathElement::Fragment("T".to_string()),
1517                PathElement::Flatten(None),
1518                PathElement::Key("arr".to_string(), None),
1519                PathElement::Index(3),
1520            ]
1521        );
1522
1523        assert_eq!(
1524            serde_json::to_string(&path).unwrap(),
1525            "[\"k\",\"... on T\",\"@\",\"arr\",3]",
1526        );
1527    }
1528}