graphql-stitching 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/stitching/executor.rb +1 -1
- data/lib/graphql/stitching/planner.rb +37 -17
- data/lib/graphql/stitching/shaper.rb +32 -11
- data/lib/graphql/stitching/supergraph.rb +24 -0
- data/lib/graphql/stitching/util.rb +0 -9
- data/lib/graphql/stitching/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b8fbbdd8c300982092ee9297953f87e014420801e1a6058a49e36d5de8fb85b
|
4
|
+
data.tar.gz: 1fbd9b21161e8452a2fa55c4617ffabe8834c49e1b0635a58e17ed9b0cb43315
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9eb747176cb0b39ced4ce35a10bab8f4a8e0b7448a5d6a9a74e8e74f484aba7f1bea46c1eff3ca3b118ee3be5fd2f8e7f5ebffb23dd5e6480a92abe620af9523
|
7
|
+
data.tar.gz: 64d935449b052d52b1068f22cc7df4b33608bb2b05bb5f8670da0e045f1c0faa06e44aa137acedab469312db5cda5144755a8d99b116a1c7ac09f809759ef198
|
@@ -37,10 +37,11 @@ module GraphQL
|
|
37
37
|
when "query"
|
38
38
|
parent_type = @supergraph.schema.query
|
39
39
|
|
40
|
-
selections_by_location =
|
40
|
+
selections_by_location = {}
|
41
|
+
each_selection_in_type(parent_type, @request.operation.selections) do |node|
|
41
42
|
locations = @supergraph.locations_by_type_and_field[parent_type.graphql_name][node.name] || SUPERGRAPH_LOCATIONS
|
42
|
-
|
43
|
-
|
43
|
+
selections_by_location[locations.first] ||= []
|
44
|
+
selections_by_location[locations.first] << node
|
44
45
|
end
|
45
46
|
|
46
47
|
selections_by_location.each do |location, selections|
|
@@ -50,14 +51,15 @@ module GraphQL
|
|
50
51
|
when "mutation"
|
51
52
|
parent_type = @supergraph.schema.mutation
|
52
53
|
|
53
|
-
location_groups =
|
54
|
+
location_groups = []
|
55
|
+
each_selection_in_type(parent_type, @request.operation.selections) do |node|
|
54
56
|
next_location = @supergraph.locations_by_type_and_field[parent_type.graphql_name][node.name].first
|
55
57
|
|
56
|
-
if
|
57
|
-
|
58
|
+
if location_groups.none? || location_groups.last[:location] != next_location
|
59
|
+
location_groups << { location: next_location, selections: [] }
|
58
60
|
end
|
59
61
|
|
60
|
-
|
62
|
+
location_groups.last[:selections] << node
|
61
63
|
end
|
62
64
|
|
63
65
|
location_groups.reduce(0) do |after_key, group|
|
@@ -75,6 +77,27 @@ module GraphQL
|
|
75
77
|
end
|
76
78
|
end
|
77
79
|
|
80
|
+
def each_selection_in_type(parent_type, input_selections, &block)
|
81
|
+
input_selections.each do |node|
|
82
|
+
case node
|
83
|
+
when GraphQL::Language::Nodes::Field
|
84
|
+
yield(node)
|
85
|
+
|
86
|
+
when GraphQL::Language::Nodes::InlineFragment
|
87
|
+
next unless parent_type.graphql_name == node.type.name
|
88
|
+
each_selection_in_type(parent_type, node.selections, &block)
|
89
|
+
|
90
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
91
|
+
fragment = @request.fragment_definitions[node.name]
|
92
|
+
next unless parent_type.graphql_name == fragment.type.name
|
93
|
+
each_selection_in_type(parent_type, fragment.selections, &block)
|
94
|
+
|
95
|
+
else
|
96
|
+
raise "Unexpected node of type #{node.class.name} in selection set."
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
78
101
|
# adds an operation (data access) to the plan which maps a data selection to an insertion point.
|
79
102
|
# note that planned operations are NOT always 1:1 with executed requests, as the executor can
|
80
103
|
# frequently batch different insertion points with the same location into a single request.
|
@@ -98,11 +121,8 @@ module GraphQL
|
|
98
121
|
# groupings coalesce similar operation parameters into a single operation
|
99
122
|
# multiple operations per service may still occur with different insertion points,
|
100
123
|
# but those will get query-batched together during execution.
|
101
|
-
grouping = String.new
|
102
|
-
|
103
|
-
grouping = insertion_path.reduce(grouping) do |memo, segment|
|
104
|
-
memo << "/" << segment
|
105
|
-
end
|
124
|
+
grouping = String.new("#{after_key}/#{location}/#{parent_type.graphql_name}")
|
125
|
+
insertion_path.each { grouping << "/#{_1}" }
|
106
126
|
|
107
127
|
if op = @operations_by_grouping[grouping]
|
108
128
|
op.selections.concat(locale_selections)
|
@@ -155,7 +175,7 @@ module GraphQL
|
|
155
175
|
next
|
156
176
|
end
|
157
177
|
|
158
|
-
field_type =
|
178
|
+
field_type = @supergraph.memoized_schema_fields(parent_type.graphql_name)[node.name].type.unwrap
|
159
179
|
extract_node_variables(node, locale_variables)
|
160
180
|
|
161
181
|
if Util.is_leaf_type?(field_type)
|
@@ -171,7 +191,7 @@ module GraphQL
|
|
171
191
|
when GraphQL::Language::Nodes::InlineFragment
|
172
192
|
next unless @supergraph.locations_by_type[node.type.name].include?(current_location)
|
173
193
|
|
174
|
-
fragment_type = @supergraph.
|
194
|
+
fragment_type = @supergraph.memoized_schema_types[node.type.name]
|
175
195
|
selection_set = extract_locale_selections(current_location, fragment_type, node.selections, insertion_path, after_key, locale_variables)
|
176
196
|
locale_selections << node.merge(selections: selection_set)
|
177
197
|
implements_fragments = true
|
@@ -180,7 +200,7 @@ module GraphQL
|
|
180
200
|
fragment = @request.fragment_definitions[node.name]
|
181
201
|
next unless @supergraph.locations_by_type[fragment.type.name].include?(current_location)
|
182
202
|
|
183
|
-
fragment_type = @supergraph.
|
203
|
+
fragment_type = @supergraph.memoized_schema_types[fragment.type.name]
|
184
204
|
selection_set = extract_locale_selections(current_location, fragment_type, fragment.selections, insertion_path, after_key, locale_variables)
|
185
205
|
locale_selections << GraphQL::Language::Nodes::InlineFragment.new(type: fragment.type, selections: selection_set)
|
186
206
|
implements_fragments = true
|
@@ -337,7 +357,7 @@ module GraphQL
|
|
337
357
|
end
|
338
358
|
|
339
359
|
if expanded_selections
|
340
|
-
@supergraph.
|
360
|
+
@supergraph.memoized_schema_possible_types(parent_type.graphql_name).each do |possible_type|
|
341
361
|
next unless @supergraph.locations_by_type[possible_type.graphql_name].include?(current_location)
|
342
362
|
|
343
363
|
type_name = GraphQL::Language::Nodes::TypeName.new(name: possible_type.graphql_name)
|
@@ -354,7 +374,7 @@ module GraphQL
|
|
354
374
|
@operations_by_grouping.each do |_grouping, op|
|
355
375
|
next unless op.boundary
|
356
376
|
|
357
|
-
boundary_type = @supergraph.
|
377
|
+
boundary_type = @supergraph.memoized_schema_types[op.boundary["type_name"]]
|
358
378
|
next unless boundary_type.kind.abstract?
|
359
379
|
next if boundary_type == op.parent_type
|
360
380
|
|
@@ -4,14 +4,14 @@
|
|
4
4
|
module GraphQL
|
5
5
|
module Stitching
|
6
6
|
class Shaper
|
7
|
-
def initialize(
|
8
|
-
@
|
7
|
+
def initialize(supergraph:, request:)
|
8
|
+
@supergraph = supergraph
|
9
9
|
@request = request
|
10
10
|
end
|
11
11
|
|
12
12
|
def perform!(raw)
|
13
|
-
root_type = @schema.
|
14
|
-
resolve_object_scope(raw, root_type, @request.operation.selections)
|
13
|
+
@root_type = @supergraph.schema.root_type_for_operation(@request.operation.operation_type)
|
14
|
+
resolve_object_scope(raw, @root_type, @request.operation.selections, @root_type.graphql_name)
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
@@ -25,11 +25,14 @@ module GraphQL
|
|
25
25
|
selections.each do |node|
|
26
26
|
case node
|
27
27
|
when GraphQL::Language::Nodes::Field
|
28
|
-
next if node.name.start_with?("__")
|
29
|
-
|
30
28
|
field_name = node.alias || node.name
|
31
|
-
|
32
|
-
|
29
|
+
|
30
|
+
next if introspection_field?(parent_type, node) do |is_root_typename|
|
31
|
+
raw_object[field_name] = @root_type.graphql_name if is_root_typename
|
32
|
+
end
|
33
|
+
|
34
|
+
node_type = @supergraph.memoized_schema_fields(parent_type.graphql_name)[node.name].type
|
35
|
+
named_type = node_type.unwrap
|
33
36
|
|
34
37
|
raw_object[field_name] = if node_type.list?
|
35
38
|
resolve_list_scope(raw_object[field_name], Util.unwrap_non_null(node_type), node.selections)
|
@@ -42,7 +45,7 @@ module GraphQL
|
|
42
45
|
return nil if raw_object[field_name].nil? && node_type.non_null?
|
43
46
|
|
44
47
|
when GraphQL::Language::Nodes::InlineFragment
|
45
|
-
fragment_type = @
|
48
|
+
fragment_type = @supergraph.memoized_schema_types[node.type.name]
|
46
49
|
next unless typename_in_type?(typename, fragment_type)
|
47
50
|
|
48
51
|
result = resolve_object_scope(raw_object, fragment_type, node.selections, typename)
|
@@ -50,7 +53,7 @@ module GraphQL
|
|
50
53
|
|
51
54
|
when GraphQL::Language::Nodes::FragmentSpread
|
52
55
|
fragment = @request.fragment_definitions[node.name]
|
53
|
-
fragment_type = @
|
56
|
+
fragment_type = @supergraph.memoized_schema_types[fragment.type.name]
|
54
57
|
next unless typename_in_type?(typename, fragment_type)
|
55
58
|
|
56
59
|
result = resolve_object_scope(raw_object, fragment_type, fragment.selections, typename)
|
@@ -93,9 +96,27 @@ module GraphQL
|
|
93
96
|
resolved_list
|
94
97
|
end
|
95
98
|
|
99
|
+
def introspection_field?(parent_type, node)
|
100
|
+
return false unless node.name.start_with?("__")
|
101
|
+
is_root = parent_type == @root_type
|
102
|
+
|
103
|
+
case node.name
|
104
|
+
when "__typename"
|
105
|
+
yield(is_root)
|
106
|
+
true
|
107
|
+
when "__schema", "__type"
|
108
|
+
is_root && @request.operation.operation_type == "query"
|
109
|
+
else
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
96
114
|
def typename_in_type?(typename, type)
|
97
115
|
return true if type.graphql_name == typename
|
98
|
-
|
116
|
+
|
117
|
+
type.kind.abstract? && @supergraph.memoized_schema_possible_types(type.graphql_name).any? do |t|
|
118
|
+
t.graphql_name == typename
|
119
|
+
end
|
99
120
|
end
|
100
121
|
end
|
101
122
|
end
|
@@ -46,6 +46,8 @@ module GraphQL
|
|
46
46
|
@boundaries = boundaries
|
47
47
|
@possible_keys_by_type = {}
|
48
48
|
@possible_keys_by_type_and_location = {}
|
49
|
+
@memoized_schema_possible_types = {}
|
50
|
+
@memoized_schema_fields = {}
|
49
51
|
|
50
52
|
# add introspection types into the fields mapping
|
51
53
|
@locations_by_type_and_field = INTROSPECTION_TYPES.each_with_object(fields) do |type_name, memo|
|
@@ -81,6 +83,28 @@ module GraphQL
|
|
81
83
|
}
|
82
84
|
end
|
83
85
|
|
86
|
+
def memoized_schema_types
|
87
|
+
@memoized_schema_types ||= @schema.types
|
88
|
+
end
|
89
|
+
|
90
|
+
def memoized_schema_possible_types(type_name)
|
91
|
+
@memoized_schema_possible_types[type_name] ||= @schema.possible_types(memoized_schema_types[type_name])
|
92
|
+
end
|
93
|
+
|
94
|
+
def memoized_schema_fields(type_name)
|
95
|
+
@memoized_schema_fields[type_name] ||= begin
|
96
|
+
fields = memoized_schema_types[type_name].fields
|
97
|
+
fields["__typename"] = @schema.introspection_system.dynamic_field(name: "__typename")
|
98
|
+
|
99
|
+
if type_name == @schema.query.graphql_name
|
100
|
+
fields["__schema"] = @schema.introspection_system.entry_point(name: "__schema")
|
101
|
+
fields["__type"] = @schema.introspection_system.entry_point(name: "__type")
|
102
|
+
end
|
103
|
+
|
104
|
+
fields
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
84
108
|
def execute_at_location(location, source, variables, context)
|
85
109
|
executable = executables[location]
|
86
110
|
|
@@ -37,15 +37,6 @@ module GraphQL
|
|
37
37
|
structure
|
38
38
|
end
|
39
39
|
|
40
|
-
# gets a named type for a field node, including hidden root introspections
|
41
|
-
def self.named_type_for_field_node(schema, parent_type, node)
|
42
|
-
if node.name == "__schema" && parent_type == schema.query
|
43
|
-
schema.types["__Schema"]
|
44
|
-
else
|
45
|
-
parent_type.fields[node.name].type.unwrap
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
40
|
# expands interfaces and unions to an array of their memberships
|
50
41
|
# like `schema.possible_types`, but includes child interfaces
|
51
42
|
def self.expand_abstract_type(schema, parent_type)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-stitching
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg MacWilliam
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|