Skip to content

Commit 6ca7aab

Browse files
authored
ensure selection order. (#179)
1 parent 5f5ea31 commit 6ca7aab

5 files changed

Lines changed: 81 additions & 9 deletions

File tree

lib/graphql/stitching/executor/shaper.rb

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ def resolve_object_scope(raw_object, parent_type, selections, typename = nil)
3030
case node
3131
when GraphQL::Language::Nodes::Field
3232
field_name = node.alias || node.name
33+
raw_value = raw_object.delete(field_name)
3334

3435
if @request.query.get_field(parent_type, node.name).introspection?
35-
if node.name == TYPENAME && parent_type == @root_type && node != TypeResolver::TYPENAME_EXPORT_NODE
36-
raw_object[field_name] = @root_type.graphql_name
36+
next if TypeResolver.export_key?(field_name)
37+
38+
raw_object[field_name] = if node.name == TYPENAME && parent_type == @root_type
39+
@root_type.graphql_name
40+
else
41+
raw_value
3742
end
3843
next
3944
end
@@ -42,14 +47,14 @@ def resolve_object_scope(raw_object, parent_type, selections, typename = nil)
4247
named_type = node_type.unwrap
4348

4449
raw_object[field_name] = if node_type.list?
45-
resolve_list_scope(raw_object[field_name], Util.unwrap_non_null(node_type), node.selections)
50+
resolve_list_scope(raw_value, Util.unwrap_non_null(node_type), node.selections)
4651
elsif Util.is_leaf_type?(named_type)
47-
raw_object[field_name]
52+
raw_value
4853
else
49-
resolve_object_scope(raw_object[field_name], named_type, node.selections)
54+
resolve_object_scope(raw_value, named_type, node.selections)
5055
end
5156

52-
return nil if raw_object[field_name].nil? && node_type.non_null?
57+
return nil if node_type.non_null? && raw_object[field_name].nil?
5358

5459
when GraphQL::Language::Nodes::InlineFragment
5560
fragment_type = node.type ? @supergraph.memoized_schema_types[node.type.name] : parent_type

lib/graphql/stitching/planner.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ def extract_locale_selections(
213213
input_selections.each do |node|
214214
case node
215215
when GraphQL::Language::Nodes::Field
216-
if node.alias&.start_with?(TypeResolver::EXPORT_PREFIX) && node != TypeResolver::TYPENAME_EXPORT_NODE
216+
if node.alias&.start_with?(TypeResolver::EXPORT_PREFIX) && node.object_id != TypeResolver::TYPENAME_EXPORT_NODE.object_id
217217
raise StitchingError, %(Alias "#{node.alias}" is not allowed because "#{TypeResolver::EXPORT_PREFIX}" is a reserved prefix.)
218218
elsif node.name == TYPENAME
219219
locale_selections << node

test/graphql/stitching/executor/shaper_grooming_test.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,64 @@ def test_grooms_through_fragment_spreads
112112
assert_equal expected, GraphQL::Stitching::Executor::Shaper.new(request).perform!(raw)
113113
end
114114

115+
def test_grooms_returned_field_order_to_match_selection
116+
schema_sdl = %|
117+
type Shop {
118+
name: String
119+
currency: String
120+
}
121+
type Product {
122+
title: String
123+
price: Float
124+
parent: Product
125+
}
126+
type Query {
127+
products: [Product]!
128+
shop: Shop
129+
}
130+
|
131+
query = %|{
132+
products {
133+
title
134+
price
135+
parent {
136+
price
137+
title
138+
}
139+
}
140+
shop {
141+
name
142+
currency
143+
}
144+
}|
145+
request = GraphQL::Stitching::Request.new(
146+
supergraph_from_schema(schema_sdl),
147+
query,
148+
)
149+
raw = {
150+
"shop" => {
151+
"currency" => "USD",
152+
"name" => "Dog Haus",
153+
},
154+
"products" => [
155+
{
156+
"parent" => { "title" => "Leash set", "price" => 25.99 },
157+
"title" => "Collar",
158+
"price" => 7.99,
159+
},
160+
{
161+
"parent" => { "title" => "Bowl set", "price" => 19.99 },
162+
"title" => "Bowl",
163+
"price" => 5.99,
164+
},
165+
],
166+
}
167+
168+
expected = %|{"products":[{"title":"Collar","price":7.99,"parent":{"price":25.99,"title":"Leash set"}},{"title":"Bowl","price":5.99,"parent":{"price":19.99,"title":"Bowl set"}}],"shop":{"name":"Dog Haus","currency":"USD"}}|
169+
170+
assert_equal expected, GraphQL::Stitching::Executor::Shaper.new(request).perform!(raw).to_json
171+
end
172+
115173
def test_renames_root_query_typenames
116174
schema_sdl = "type Query { field: String }"
117175
source = %|

test/graphql/stitching/integration/introspection_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def test_performs_schema_introspection_with_other_stitching
5151
},
5252
}
5353

54-
assert_equal expected, result
54+
assert_equal expected, result.to_h
5555
end
5656

5757
def test_performs_type_introspection_with_other_stitching
@@ -83,6 +83,6 @@ def test_performs_type_introspection_with_other_stitching
8383
},
8484
}
8585

86-
assert_equal expected, result
86+
assert_equal expected, result.to_h
8787
end
8888
end

test/graphql/stitching/planner/plan_introspection_test.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,13 @@ def test_errors_for_reserved_selection_alias
6262
).plan
6363
end
6464
end
65+
66+
def test_errors_for_reserved_typehint_alias
67+
assert_error %|Alias "_export___typename" is not allowed because "_export_" is a reserved prefix| do
68+
GraphQL::Stitching::Request.new(
69+
@supergraph,
70+
%|{ a { _export___typename: __typename } }|,
71+
).plan
72+
end
73+
end
6574
end

0 commit comments

Comments
 (0)