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 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