1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
use petgraph::visit::{Bfs, IntoNodeReferences};
use tracing::{instrument, trace};
use crate::query_planner::{
ast::{
merge_path::{MergePath, Segment},
selection_item::SelectionItem,
selection_set::find_selection_set_by_path_mut,
},
planner::fetch::{error::FetchGraphError, fetch_graph::FetchGraph, state::MultiTypeFetchStep},
};
impl FetchGraph<MultiTypeFetchStep> {
/// This method applies internal aliasing for fields in the fetch graph.
/// In case a fetch step contains a record of alias made to an output field, it needs to be propagated to all descendants steps that depends on this
/// output field, in multiple locations:
/// 1. In "input" selections
/// 2. In "response_path"
#[instrument(level = "trace", skip_all)]
pub(crate) fn apply_internal_aliases_patching(&mut self) -> Result<(), FetchGraphError> {
// First, iterate and find all nodes that needed to perform internal aliasing for fields
let mut nodes_with_aliases = self
.graph
.node_references()
.filter_map(|(index, node)| {
if !node.internal_aliases_locations.is_empty() {
Some((index, node.internal_aliases_locations.clone()))
} else {
None
}
})
.collect::<Vec<_>>();
trace!(
"found total of {} node with internal aliased fields",
nodes_with_aliases.len(),
);
while let Some((aliased_node_index, scoped_aliases_locations)) = nodes_with_aliases.pop() {
for (root_type_name, aliases_locations) in scoped_aliases_locations {
let mut bfs = Bfs::new(&self.graph, aliased_node_index);
trace!(
"Iterating step [{}], total of {} aliased fields in output selections of type {}",
aliased_node_index.index(),
aliases_locations.len(),
root_type_name
);
// Iterate and find all possible children of a node that needed aliasing.
// We can't really tell which nodes are affected, as they might be at any level of the hierarchy, so we travel the graph.
while let Some(decendent_idx) = bfs.next(&self.graph) {
if decendent_idx != aliased_node_index {
let decendent = self.get_step_data_mut(decendent_idx)?;
trace!(
"Checking if decendent [{}] is relevant for aliasing patching...",
decendent_idx.index()
);
for (alias_path, new_name) in aliases_locations.iter() {
// Last segment is the field that was aliased
let maybe_patched_field = alias_path.last();
// Build a path without the alias path, to make sure we don't patch the wrong field
let relative_path =
decendent.response_path.slice_from(alias_path.len());
if let Some(Segment::Field(field_seg, args_hash, condition)) =
maybe_patched_field
{
// TODO: Avoid "except" here of course.
let decendent_type_name = decendent
.input
.try_as_single()
.ok_or_else(|| {
FetchGraphError::Internal(
format!(
"Expected single input type for descendant node [{}] during alias patching, but found multi-type input",
decendent_idx.index()
)
)
})?
.to_string();
let selection = decendent
.input
.selections_for_definition_mut(&decendent_type_name)
.expect("selection set is missing");
trace!(
"field '{}' was aliased, relative selection path: '{}', checking if need to patch selection '{}'",
field_seg.field_name(),
relative_path,
selection
);
// First, check if the node's input selection set contains the field that was aliased
if let Some(selection) =
find_selection_set_by_path_mut(selection, &relative_path)
{
trace!("found selection to patch: {}", selection);
let item_to_patch = selection.items.iter_mut().find(|item| matches!(item, SelectionItem::Field(field) if field.name == field_seg.field_name() && field.arguments_hash() == *args_hash));
if let Some(SelectionItem::Field(field_to_patch)) =
item_to_patch
{
field_to_patch.alias = Some(field_to_patch.name.clone());
field_to_patch.name = new_name.clone();
trace!(
"path '{}' found in selection, patched applied, new selection: {}",
relative_path,
field_to_patch
);
}
} else {
trace!(
"path '{}' was not found in selection '{}', skipping...",
relative_path,
selection
);
}
// Then, check if the node's response_path is using the part that was aliased
let segment_idx_to_patch = decendent
.response_path
.inner
.iter()
.enumerate()
.find_map(|(idx, part)| {
if matches!(part, Segment::Field(ref f, a, c) if f.field_name() == field_seg.field_name() && a == args_hash && c == condition) {
Some(idx)
} else {
None
}
});
if let Some(segment_idx_to_patch) = segment_idx_to_patch {
trace!(
"Node [{}] is using aliased field {} in response_path (segment idx: {}, alias: {:?})",
decendent_idx.index(),
field_seg.field_name(),
segment_idx_to_patch,
alias_path
);
let mut new_path = (*decendent.response_path.inner).to_vec();
if let Some(Segment::Field(ref mut seg, _, _)) =
new_path.get_mut(segment_idx_to_patch)
{
seg.field_name = new_name.clone();
decendent.response_path = MergePath::new(new_path);
}
}
}
}
}
}
}
}
Ok(())
}
}