graphql-stitching 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|