Skip to main content

apollo_federation/connectors/runtime/
key.rs

1use std::sync::Arc;
2
3use apollo_compiler::Name;
4use apollo_compiler::executable::FieldSet;
5use apollo_compiler::validation::Valid;
6
7use crate::connectors::JSONSelection;
8use crate::connectors::runtime::inputs::RequestInputs;
9
10#[derive(Clone)]
11pub enum ResponseKey {
12    RootField {
13        name: String,
14        selection: Arc<JSONSelection>,
15        inputs: RequestInputs,
16    },
17    Entity {
18        index: usize,
19        selection: Arc<JSONSelection>,
20        inputs: RequestInputs,
21    },
22    EntityField {
23        index: usize,
24        field_name: String,
25        /// Is Some only if the output type is a concrete object type. If it's
26        /// an interface, it's treated as an interface object and we can't emit
27        /// a __typename in the response.
28        typename: Option<Name>,
29        selection: Arc<JSONSelection>,
30        inputs: RequestInputs,
31    },
32    BatchEntity {
33        selection: Arc<JSONSelection>,
34        keys: Valid<FieldSet>,
35        inputs: RequestInputs,
36    },
37}
38
39impl ResponseKey {
40    pub fn selection(&self) -> &JSONSelection {
41        match self {
42            ResponseKey::RootField { selection, .. } => selection,
43            ResponseKey::Entity { selection, .. } => selection,
44            ResponseKey::EntityField { selection, .. } => selection,
45            ResponseKey::BatchEntity { selection, .. } => selection,
46        }
47    }
48
49    pub fn inputs(&self) -> &RequestInputs {
50        match self {
51            ResponseKey::RootField { inputs, .. } => inputs,
52            ResponseKey::Entity { inputs, .. } => inputs,
53            ResponseKey::EntityField { inputs, .. } => inputs,
54            ResponseKey::BatchEntity { inputs, .. } => inputs,
55        }
56    }
57
58    /// Returns a serialized representation of the Path from apollo-router.
59    /// Intended to be parsed into a Path when converting a connectors
60    /// `RuntimeError` in the router's graphql::Error.
61    ///
62    /// This mimics the behavior of a GraphQL subgraph, including the `_entities`
63    /// field. When the path gets to `FetchNode::response_at_path`, it will be
64    /// amended and appended to a parent path to create the full path to the
65    /// field. For example:
66    ///
67    /// - parent path: `["posts", @, "user"]`
68    /// - path from key: `["_entities", 0, "user", "profile"]`
69    /// - result: `["posts", 1, "user", "profile"]`
70    pub fn path_string(&self) -> String {
71        match self {
72            ResponseKey::RootField { name, .. } => name.to_string(),
73            ResponseKey::Entity { index, .. } => format!("_entities/{index}"),
74            ResponseKey::EntityField {
75                index, field_name, ..
76            } => format!("_entities/{index}/{field_name}"),
77            ResponseKey::BatchEntity { .. } => "_entities".to_string(),
78        }
79    }
80}
81
82impl std::fmt::Debug for ResponseKey {
83    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84        match self {
85            Self::RootField {
86                name,
87                selection,
88                inputs,
89            } => f
90                .debug_struct("RootField")
91                .field("name", name)
92                .field("selection", &selection.to_string())
93                .field("inputs", inputs)
94                .finish(),
95            Self::Entity {
96                index,
97                selection,
98                inputs,
99            } => f
100                .debug_struct("Entity")
101                .field("index", index)
102                .field("selection", &selection.to_string())
103                .field("inputs", inputs)
104                .finish(),
105            Self::EntityField {
106                index,
107                field_name,
108                typename,
109                selection,
110                inputs,
111            } => f
112                .debug_struct("EntityField")
113                .field("index", index)
114                .field("field_name", field_name)
115                .field("typename", typename)
116                .field("selection", &selection.to_string())
117                .field("inputs", inputs)
118                .finish(),
119            Self::BatchEntity {
120                selection,
121                keys,
122                inputs,
123            } => f
124                .debug_struct("BatchEntity")
125                .field("selection", &selection.to_string())
126                .field("key", &keys.serialize().no_indent().to_string())
127                .field("inputs", inputs)
128                .finish(),
129        }
130    }
131}