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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b07a54288795f5b321029340a61e97dd9209d395712dc9ac5921ebd5c52523a1
4
- data.tar.gz: 83e4dea46c7b4ff7a3716c2936773893df58e732a50c36ee1a5af8e4e5cab4bd
3
+ metadata.gz: 0b8fbbdd8c300982092ee9297953f87e014420801e1a6058a49e36d5de8fb85b
4
+ data.tar.gz: 1fbd9b21161e8452a2fa55c4617ffabe8834c49e1b0635a58e17ed9b0cb43315
5
5
  SHA512:
6
- metadata.gz: 831a54675315529b6bc1d489296a15325f88bcccbbb8f480c786641410de853989425591f4e60a6249ef123a3aa9e204780388925f2a027d758ac5594ca6c306
7
- data.tar.gz: f66ab6dfc996aa9982f6c5d09e2bf98b5a5de8b85220ea839f854e53edd3f55144404b5ddff91f2681f7d8fee21071bb54f20265951424840c527b7203dd9ace
6
+ metadata.gz: 9eb747176cb0b39ced4ce35a10bab8f4a8e0b7448a5d6a9a74e8e74f484aba7f1bea46c1eff3ca3b118ee3be5fd2f8e7f5ebffb23dd5e6480a92abe620af9523
7
+ data.tar.gz: 64d935449b052d52b1068f22cc7df4b33608bb2b05bb5f8670da0e045f1c0faa06e44aa137acedab469312db5cda5144755a8d99b116a1c7ac09f809759ef198
@@ -251,7 +251,7 @@ module GraphQL
251
251
 
252
252
  if @data && @data.length > 0
253
253
  result["data"] = raw ? @data : GraphQL::Stitching::Shaper.new(
254
- schema: @supergraph.schema,
254
+ supergraph: @supergraph,
255
255
  request: @request,
256
256
  ).perform!(@data)
257
257
  end
@@ -37,10 +37,11 @@ module GraphQL
37
37
  when "query"
38
38
  parent_type = @supergraph.schema.query
39
39
 
40
- selections_by_location = @request.operation.selections.each_with_object({}) do |node, memo|
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
- memo[locations.first] ||= []
43
- memo[locations.first] << node
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 = @request.operation.selections.each_with_object([]) do |node, memo|
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 memo.none? || memo.last[:location] != next_location
57
- memo << { location: next_location, selections: [] }
58
+ if location_groups.none? || location_groups.last[:location] != next_location
59
+ location_groups << { location: next_location, selections: [] }
58
60
  end
59
61
 
60
- memo.last[:selections] << node
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
- grouping << after_key.to_s << "/" << location << "/" << parent_type.graphql_name
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 = Util.named_type_for_field_node(@supergraph.schema, parent_type, node)
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.schema.types[node.type.name]
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.schema.types[fragment.type.name]
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.schema.possible_types(parent_type).each do |possible_type|
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.schema.types[op.boundary["type_name"]]
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(schema:, request:)
8
- @schema = schema
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.public_send(@request.operation.operation_type)
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
- node_type = parent_type.fields[node.name].type
32
- named_type = Util.named_type_for_field_node(@schema, parent_type, node)
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 = @schema.types[node.type.name]
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 = @schema.types[fragment.type.name]
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
- type.kind.abstract? && @schema.possible_types(type).any? { _1.graphql_name == typename }
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)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Stitching
5
- VERSION = "0.3.3"
5
+ VERSION = "0.3.4"
6
6
  end
7
7
  end
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.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-24 00:00:00.000000000 Z
11
+ date: 2023-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql