Skip to main content

apollo_router/query_planner/
convert.rs

1use std::sync::Arc;
2
3use apollo_compiler::executable;
4use apollo_federation::query_plan as next;
5
6use crate::query_planner::fetch::SubgraphOperation;
7use crate::query_planner::plan;
8use crate::query_planner::rewrites;
9use crate::query_planner::selection;
10use crate::query_planner::subscription;
11
12pub(crate) fn convert_root_query_plan_node(js: &next::QueryPlan) -> Option<plan::PlanNode> {
13    let next::QueryPlan {
14        node,
15        statistics: _,
16    } = js;
17    option(node)
18}
19
20impl From<&'_ next::TopLevelPlanNode> for plan::PlanNode {
21    fn from(value: &'_ next::TopLevelPlanNode) -> Self {
22        match value {
23            next::TopLevelPlanNode::Subscription(node) => node.into(),
24            next::TopLevelPlanNode::Fetch(node) => node.into(),
25            next::TopLevelPlanNode::Sequence(node) => node.into(),
26            next::TopLevelPlanNode::Parallel(node) => node.into(),
27            next::TopLevelPlanNode::Flatten(node) => node.into(),
28            next::TopLevelPlanNode::Defer(node) => node.into(),
29            next::TopLevelPlanNode::Condition(node) => node.as_ref().into(),
30        }
31    }
32}
33
34impl From<&'_ next::PlanNode> for plan::PlanNode {
35    fn from(value: &'_ next::PlanNode) -> Self {
36        match value {
37            next::PlanNode::Fetch(node) => node.into(),
38            next::PlanNode::Sequence(node) => node.into(),
39            next::PlanNode::Parallel(node) => node.into(),
40            next::PlanNode::Flatten(node) => node.into(),
41            next::PlanNode::Defer(node) => node.into(),
42            next::PlanNode::Condition(node) => node.as_ref().into(),
43        }
44    }
45}
46impl From<&'_ Box<next::PlanNode>> for plan::PlanNode {
47    fn from(value: &'_ Box<next::PlanNode>) -> Self {
48        value.as_ref().into()
49    }
50}
51
52impl From<&'_ next::SubscriptionNode> for plan::PlanNode {
53    fn from(value: &'_ next::SubscriptionNode) -> Self {
54        let next::SubscriptionNode { primary, rest } = value;
55        Self::Subscription {
56            primary: primary.as_ref().into(),
57            rest: option(rest).map(Box::new),
58        }
59    }
60}
61
62impl From<&'_ Box<next::FetchNode>> for plan::PlanNode {
63    fn from(value: &'_ Box<next::FetchNode>) -> Self {
64        let next::FetchNode {
65            subgraph_name,
66            id,
67            variable_usages,
68            requires,
69            operation_document,
70            operation_name,
71            operation_kind,
72            input_rewrites,
73            output_rewrites,
74            context_rewrites,
75        } = &**value;
76        Self::Fetch(super::fetch::FetchNode {
77            service_name: subgraph_name.clone(),
78            requires: requires.as_deref().map(vec).unwrap_or_default(),
79            variable_usages: variable_usages.iter().map(|v| v.clone().into()).collect(),
80            // TODO: use Arc in apollo_federation to avoid this clone
81            operation: SubgraphOperation::from_parsed(Arc::new(operation_document.clone())),
82            operation_name: operation_name.clone().map(|n| n.into()),
83            operation_kind: (*operation_kind).into(),
84            id: id.map(|id| id.to_string()),
85            input_rewrites: option_vec(input_rewrites),
86            output_rewrites: option_vec(output_rewrites),
87            context_rewrites: option_vec(context_rewrites),
88            schema_aware_hash: Default::default(),
89            authorization: Default::default(),
90        })
91    }
92}
93
94impl From<&'_ next::SequenceNode> for plan::PlanNode {
95    fn from(value: &'_ next::SequenceNode) -> Self {
96        let next::SequenceNode { nodes } = value;
97        Self::Sequence { nodes: vec(nodes) }
98    }
99}
100
101impl From<&'_ next::ParallelNode> for plan::PlanNode {
102    fn from(value: &'_ next::ParallelNode) -> Self {
103        let next::ParallelNode { nodes } = value;
104        Self::Parallel { nodes: vec(nodes) }
105    }
106}
107
108impl From<&'_ next::FlattenNode> for plan::PlanNode {
109    fn from(value: &'_ next::FlattenNode) -> Self {
110        let next::FlattenNode { path, node } = value;
111        Self::Flatten(plan::FlattenNode {
112            path: crate::json_ext::Path(vec(path)),
113            node: Box::new(node.into()),
114        })
115    }
116}
117
118impl From<&'_ next::DeferNode> for plan::PlanNode {
119    fn from(value: &'_ next::DeferNode) -> Self {
120        let next::DeferNode { primary, deferred } = value;
121        Self::Defer {
122            primary: primary.into(),
123            deferred: vec(deferred),
124        }
125    }
126}
127
128impl From<&'_ next::ConditionNode> for plan::PlanNode {
129    fn from(value: &'_ next::ConditionNode) -> Self {
130        let next::ConditionNode {
131            condition_variable,
132            if_clause,
133            else_clause,
134        } = value;
135        Self::Condition {
136            condition: condition_variable.to_string(),
137            if_clause: if_clause.as_ref().map(Into::into).map(Box::new),
138            else_clause: else_clause.as_ref().map(Into::into).map(Box::new),
139        }
140    }
141}
142
143impl From<&'_ next::FetchNode> for subscription::SubscriptionNode {
144    fn from(value: &'_ next::FetchNode) -> Self {
145        let next::FetchNode {
146            subgraph_name,
147            id: _,
148            variable_usages,
149            requires: _,
150            operation_document,
151            operation_name,
152            operation_kind,
153            input_rewrites,
154            output_rewrites,
155            context_rewrites: _,
156        } = value;
157        Self {
158            service_name: subgraph_name.clone(),
159            variable_usages: variable_usages.iter().map(|v| v.clone().into()).collect(),
160            // TODO: use Arc in apollo_federation to avoid this clone
161            operation: SubgraphOperation::from_parsed(Arc::new(operation_document.clone())),
162            operation_name: operation_name.clone().map(|n| n.into()),
163            operation_kind: (*operation_kind).into(),
164            input_rewrites: option_vec(input_rewrites),
165            output_rewrites: option_vec(output_rewrites),
166        }
167    }
168}
169
170impl From<&'_ next::PrimaryDeferBlock> for plan::Primary {
171    fn from(value: &'_ next::PrimaryDeferBlock) -> Self {
172        let next::PrimaryDeferBlock {
173            sub_selection,
174            node,
175        } = value;
176        Self {
177            node: option(node).map(Box::new),
178            subselection: sub_selection.as_ref().map(|s| s.to_string()),
179        }
180    }
181}
182
183impl From<&'_ next::DeferredDeferBlock> for plan::DeferredNode {
184    fn from(value: &'_ next::DeferredDeferBlock) -> Self {
185        let next::DeferredDeferBlock {
186            depends,
187            label,
188            query_path,
189            sub_selection,
190            node,
191        } = value;
192        Self {
193            depends: vec(depends),
194            label: label.clone(),
195            query_path: crate::json_ext::Path(
196                query_path
197                    .iter()
198                    .filter_map(|e| match e {
199                        next::QueryPathElement::Field(field) => Some(
200                            // TODO: type conditioned fetching once it s available in the rust planner
201                            crate::graphql::JsonPathElement::Key(
202                                field.response_key().to_string(),
203                                None,
204                            ),
205                        ),
206                        next::QueryPathElement::InlineFragment(inline) => {
207                            inline.type_condition.as_ref().map(|cond| {
208                                crate::graphql::JsonPathElement::Fragment(cond.to_string())
209                            })
210                        }
211                    })
212                    .collect(),
213            ),
214            node: option(node).map(Arc::new),
215            subselection: sub_selection.as_ref().map(|s| s.to_string()),
216        }
217    }
218}
219
220impl From<&'_ next::DeferredDependency> for plan::Depends {
221    fn from(value: &'_ next::DeferredDependency) -> Self {
222        let next::DeferredDependency { id } = value;
223        Self { id: id.clone() }
224    }
225}
226
227impl From<&'_ executable::Selection> for selection::Selection {
228    fn from(value: &'_ executable::Selection) -> Self {
229        match value {
230            executable::Selection::Field(field) => Self::Field(field.as_ref().into()),
231            executable::Selection::InlineFragment(inline) => {
232                Self::InlineFragment(inline.as_ref().into())
233            }
234            executable::Selection::FragmentSpread(_) => unreachable!(),
235        }
236    }
237}
238
239impl From<&'_ executable::Field> for selection::Field {
240    fn from(value: &'_ executable::Field) -> Self {
241        let executable::Field {
242            definition: _,
243            alias,
244            name,
245            arguments: _,
246            directives: _,
247            selection_set,
248        } = value;
249        Self {
250            alias: alias.clone(),
251            name: name.clone(),
252            selections: if selection_set.selections.is_empty() {
253                None
254            } else {
255                Some(vec(&selection_set.selections))
256            },
257        }
258    }
259}
260
261impl From<&'_ executable::InlineFragment> for selection::InlineFragment {
262    fn from(value: &'_ executable::InlineFragment) -> Self {
263        let executable::InlineFragment {
264            type_condition,
265            directives: _,
266            selection_set,
267        } = value;
268        Self {
269            type_condition: type_condition.clone(),
270            selections: vec(&selection_set.selections),
271        }
272    }
273}
274
275impl From<&'_ Arc<next::FetchDataRewrite>> for rewrites::DataRewrite {
276    fn from(value: &'_ Arc<next::FetchDataRewrite>) -> Self {
277        match value.as_ref() {
278            next::FetchDataRewrite::ValueSetter(setter) => Self::ValueSetter(setter.into()),
279            next::FetchDataRewrite::KeyRenamer(renamer) => Self::KeyRenamer(renamer.into()),
280        }
281    }
282}
283
284impl From<&'_ next::FetchDataValueSetter> for rewrites::DataValueSetter {
285    fn from(value: &'_ next::FetchDataValueSetter) -> Self {
286        let next::FetchDataValueSetter { path, set_value_to } = value;
287        Self {
288            path: crate::json_ext::Path(vec(path)),
289            set_value_to: set_value_to.clone(),
290        }
291    }
292}
293
294impl From<&'_ next::FetchDataKeyRenamer> for rewrites::DataKeyRenamer {
295    fn from(value: &'_ next::FetchDataKeyRenamer) -> Self {
296        let next::FetchDataKeyRenamer {
297            path,
298            rename_key_to,
299        } = value;
300        Self {
301            path: crate::json_ext::Path(vec(path)),
302            rename_key_to: rename_key_to.clone(),
303        }
304    }
305}
306
307impl From<&'_ next::FetchDataPathElement> for crate::json_ext::PathElement {
308    fn from(value: &'_ next::FetchDataPathElement) -> Self {
309        // TODO: Go all in on Name eventually
310        match value {
311            next::FetchDataPathElement::Key(name, conditions) => Self::Key(
312                name.to_string(),
313                conditions
314                    .as_ref()
315                    .map(|conditions| conditions.iter().map(|c| c.to_string()).collect()),
316            ),
317            next::FetchDataPathElement::AnyIndex(conditions) => Self::Flatten(
318                conditions
319                    .as_ref()
320                    .map(|conditions| conditions.iter().map(|c| c.to_string()).collect()),
321            ),
322            next::FetchDataPathElement::TypenameEquals(value) => Self::Fragment(value.to_string()),
323            next::FetchDataPathElement::Parent => Self::Key("..".to_owned(), None),
324        }
325    }
326}
327
328fn vec<'a, T, U>(value: &'a [T]) -> Vec<U>
329where
330    U: From<&'a T>,
331{
332    value.iter().map(Into::into).collect()
333}
334
335fn option<'a, T, U>(value: &'a Option<T>) -> Option<U>
336where
337    U: From<&'a T>,
338{
339    value.as_ref().map(Into::into)
340}
341
342fn option_vec<'a, T, U>(value: &'a [T]) -> Option<Vec<U>>
343where
344    U: From<&'a T>,
345{
346    if value.is_empty() {
347        None
348    } else {
349        Some(vec(value))
350    }
351}