graphql-stitching 1.5.2 → 1.6.0

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: cf60ae0ef85426a3011223bcc146a74a1664762e5488d8266d0afbf1c2143457
4
- data.tar.gz: d20da5d4817193de7e156441cab88b0855f3fa93aeabe5e760f8fd66ac5b3547
3
+ metadata.gz: 227a7813eed1050c451d9bbee900305cfbac67aff35baa88f6ba9551535d1e97
4
+ data.tar.gz: 1533e6109c6a72af3f7d07891714c09a73476ca0a5a7ea53d83cbe829564ace3
5
5
  SHA512:
6
- metadata.gz: b89843bfdd353a6b4aec47fce17a6bd6524b29f7d5fa5dc0452570dc1b4bec63691171f7cd3f841c393015df763e20e1be7cf907cc660fcc576cc93e08c3d4dd
7
- data.tar.gz: a20f68f8bac0a52271fdc76ec4f173347669acda7f49b18e177746c83df1ec42dc878e8d3af37545a99f5456acc6d9f7577600d79c4568925cd23837fe5c2d0a
6
+ metadata.gz: 8342158a10bc25fb68c833a3366c2fd6e831cf86cccf15a2491067680bb5299700783af57fd5042b394131b48759f077d32f4b210149a2b3cb34e43e3cca77ff
7
+ data.tar.gz: 7313c93bb8cedf7ac06708a1f7fba6a2c46196982380a28a6d9244cf7320ad0149cb17df8ecb1abe438e14eac08b2f71e154dd9f2d6c3095be8e849c46061e16
data/docs/request.md CHANGED
@@ -15,7 +15,6 @@ request = GraphQL::Stitching::Request.new(
15
15
 
16
16
  A `Request` provides the following information:
17
17
 
18
- - `req.document`: parsed AST of the GraphQL source.
19
18
  - `req.variables`: a hash of user-submitted variables.
20
19
  - `req.string`: the original GraphQL source string, or printed document.
21
20
  - `req.digest`: a digest of the request string, hashed by the `Stitching.digest` implementation.
@@ -31,6 +30,5 @@ A request manages the flow of stitching behaviors. These are sequenced by the `C
31
30
  component, or you may invoke them manually:
32
31
 
33
32
  1. `request.validate`: runs static validations on the request using the combined schema.
34
- 2. `request.prepare!`: inserts variable defaults and pre-renders skip/include conditional shaping.
35
- 3. `request.plan`: builds a plan for the request. May act as a setter for plans pulled from cache.
36
- 4. `request.execute`: executes the request, and returns the resulting data.
33
+ 2. `request.plan`: builds a plan for the request. May act as a setter for plans pulled from cache.
34
+ 3. `request.execute`: executes the request, and returns the resulting data.
@@ -45,7 +45,6 @@ module GraphQL
45
45
  return error_result(request, validation_errors) if validation_errors.any?
46
46
  end
47
47
 
48
- request.prepare!
49
48
  load_plan(request)
50
49
  request.execute
51
50
  rescue GraphQL::ParseError, GraphQL::ExecutionError => e
@@ -14,7 +14,7 @@ module GraphQL::Stitching
14
14
  end
15
15
 
16
16
  def perform!(raw)
17
- @root_type = @supergraph.schema.root_type_for_operation(@request.operation.operation_type)
17
+ @root_type = @request.query.root_type_for_operation(@request.operation.operation_type)
18
18
  resolve_object_scope(raw, @root_type, @request.operation.selections, @root_type.graphql_name)
19
19
  end
20
20
 
@@ -118,7 +118,7 @@ module GraphQL::Stitching
118
118
  def typename_in_type?(typename, type)
119
119
  return true if type.graphql_name == typename
120
120
 
121
- type.kind.abstract? && @supergraph.schema.possible_types(type).any? do |t|
121
+ type.kind.abstract? && @request.query.possible_types(type).any? do |t|
122
122
  t.graphql_name == typename
123
123
  end
124
124
  end
@@ -132,7 +132,7 @@ module GraphQL
132
132
  elsif @upload_types.include?(ast_node.name)
133
133
  files_by_path[path.dup] = value
134
134
  else
135
- type_def = request.supergraph.schema.get_type(ast_node.name)
135
+ type_def = request.query.get_type(ast_node.name)
136
136
  extract_type_node(type_def, value, files_by_path, path) if type_def&.kind&.input_object?
137
137
  end
138
138
  end
@@ -106,7 +106,7 @@ module GraphQL
106
106
 
107
107
  # A) Group all root selections by their preferred entrypoint locations.
108
108
  def build_root_entrypoints
109
- parent_type = @supergraph.schema.root_type_for_operation(@request.operation.operation_type)
109
+ parent_type = @request.query.root_type_for_operation(@request.operation.operation_type)
110
110
 
111
111
  case @request.operation.operation_type
112
112
  when QUERY_OP
@@ -331,7 +331,7 @@ module GraphQL
331
331
  end
332
332
 
333
333
  if expanded_selections
334
- @supergraph.schema.possible_types(parent_type).each do |possible_type|
334
+ @request.query.possible_types(parent_type).each do |possible_type|
335
335
  next unless @supergraph.locations_by_type[possible_type.graphql_name].include?(current_location)
336
336
 
337
337
  type_name = GraphQL::Language::Nodes::TypeName.new(name: possible_type.graphql_name)
@@ -14,26 +14,21 @@ module GraphQL
14
14
  # @return [Supergraph] supergraph instance that resolves the request.
15
15
  attr_reader :supergraph
16
16
 
17
- # @return [GraphQL::Language::Nodes::Document] the parsed GraphQL AST document.
18
- attr_reader :document
19
-
20
- # @return [Hash] input variables for the request.
21
- attr_reader :variables
22
-
23
- # @return [String] operation name selected for the request.
24
- attr_reader :operation_name
17
+ # @return [GraphQL::Query] query object defining the request.
18
+ attr_reader :query
25
19
 
26
20
  # @return [Hash] contextual object passed through resolver flows.
27
21
  attr_reader :context
28
22
 
29
23
  # Creates a new supergraph request.
30
24
  # @param supergraph [Supergraph] supergraph instance that resolves the request.
31
- # @param document [String, GraphQL::Language::Nodes::Document] the request string or parsed AST.
25
+ # @param source [String, GraphQL::Language::Nodes::Document] the request string or parsed AST.
32
26
  # @param operation_name [String, nil] operation name selected for the request.
33
27
  # @param variables [Hash, nil] input variables for the request.
34
28
  # @param context [Hash, nil] a contextual object passed through resolver flows.
35
- def initialize(supergraph, document, operation_name: nil, variables: nil, context: nil)
29
+ def initialize(supergraph, source, operation_name: nil, variables: nil, context: nil)
36
30
  @supergraph = supergraph
31
+ @prepared_document = nil
37
32
  @string = nil
38
33
  @digest = nil
39
34
  @normalized_string = nil
@@ -44,29 +39,32 @@ module GraphQL
44
39
  @fragment_definitions = nil
45
40
  @plan = nil
46
41
 
47
- @document = if document.is_a?(String)
48
- @string = document
49
- GraphQL.parse(document)
42
+ params = {
43
+ operation_name: operation_name,
44
+ variables: variables,
45
+ context: context,
46
+ }
47
+
48
+ if source.is_a?(String)
49
+ @string = source
50
+ params[:query] = source
50
51
  else
51
- document
52
+ params[:document] = source
52
53
  end
53
54
 
54
- @operation_name = operation_name
55
- @variables = variables || {}
56
-
57
- @query = GraphQL::Query.new(@supergraph.schema, document: @document, context: context)
55
+ @query = GraphQL::Query.new(@supergraph.schema, **params)
58
56
  @context = @query.context
59
57
  @context[:request] = self
60
58
  end
61
59
 
62
60
  # @return [String] the original document string, or a print of the parsed AST document.
63
61
  def string
64
- @string || normalized_string
62
+ with_prepared_document { @string || normalized_string }
65
63
  end
66
64
 
67
65
  # @return [String] a print of the parsed AST document with consistent whitespace.
68
66
  def normalized_string
69
- @normalized_string ||= @document.to_query_string
67
+ @normalized_string ||= prepared_document.to_query_string
70
68
  end
71
69
 
72
70
  # @return [String] a digest of the original document string. Generally faster but less consistent.
@@ -81,43 +79,48 @@ module GraphQL
81
79
 
82
80
  # @return [GraphQL::Language::Nodes::OperationDefinition] The selected root operation for the request.
83
81
  def operation
84
- @operation ||= begin
85
- operation_defs = @document.definitions.select do |d|
82
+ @operation ||= with_prepared_document do
83
+ selected_op = @query.selected_operation
84
+ raise GraphQL::ExecutionError, "No operation selected" unless selected_op
85
+
86
+ @prepared_document.definitions.find do |d|
86
87
  next unless d.is_a?(GraphQL::Language::Nodes::OperationDefinition)
87
- @operation_name ? d.name == @operation_name : true
88
- end
89
88
 
90
- if operation_defs.length < 1
91
- raise GraphQL::ExecutionError, "Invalid root operation for given name and operation type."
92
- elsif operation_defs.length > 1
93
- raise GraphQL::ExecutionError, "An operation name is required when sending multiple operations."
89
+ selected_op.name.nil? || d.name == selected_op.name
94
90
  end
91
+ end
92
+ end
95
93
 
96
- operation_defs.first
94
+ def operation_name
95
+ operation.name
96
+ end
97
+
98
+ # @return [String] A string of directives applied to the root operation. These are passed through in all subgraph requests.
99
+ def operation_directives
100
+ @operation_directives ||= if operation.directives.any?
101
+ printer = GraphQL::Language::Printer.new
102
+ operation.directives.map { printer.print(_1) }.join(" ")
97
103
  end
98
104
  end
99
105
 
100
106
  # @return [Boolean] true if operation type is a query
101
107
  def query?
102
- operation.operation_type == QUERY_OP
108
+ @query.query?
103
109
  end
104
110
 
105
111
  # @return [Boolean] true if operation type is a mutation
106
112
  def mutation?
107
- operation.operation_type == MUTATION_OP
113
+ @query.mutation?
108
114
  end
109
115
 
110
116
  # @return [Boolean] true if operation type is a subscription
111
117
  def subscription?
112
- operation.operation_type == SUBSCRIPTION_OP
118
+ @query.subscription?
113
119
  end
114
120
 
115
- # @return [String] A string of directives applied to the root operation. These are passed through in all subgraph requests.
116
- def operation_directives
117
- @operation_directives ||= if operation.directives.any?
118
- printer = GraphQL::Language::Printer.new
119
- operation.directives.map { printer.print(_1) }.join(" ")
120
- end
121
+ # @return [Hash<String, Any>] provided variables hash filled in with default values from definitions
122
+ def variables
123
+ @variables || with_prepared_document { @variables }
121
124
  end
122
125
 
123
126
  # @return [Hash<String, GraphQL::Language::Nodes::AbstractNode>] map of variable names to AST type definitions.
@@ -129,7 +132,7 @@ module GraphQL
129
132
 
130
133
  # @return [Hash<String, GraphQL::Language::Nodes::FragmentDefinition>] map of fragment names to their AST definitions.
131
134
  def fragment_definitions
132
- @fragment_definitions ||= @document.definitions.each_with_object({}) do |d, memo|
135
+ @fragment_definitions ||= prepared_document.definitions.each_with_object({}) do |d, memo|
133
136
  memo[d.name] = d if d.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
134
137
  end
135
138
  end
@@ -137,26 +140,7 @@ module GraphQL
137
140
  # Validates the request using the combined supergraph schema.
138
141
  # @return [Array<GraphQL::ExecutionError>] an array of static validation errors
139
142
  def validate
140
- result = @supergraph.schema.static_validator.validate(@query)
141
- result[:errors]
142
- end
143
-
144
- # Prepares the request for stitching by inserting variable defaults and applying @skip/@include conditionals.
145
- def prepare!
146
- operation.variables.each do |v|
147
- @variables[v.name] = v.default_value if @variables[v.name].nil? && !v.default_value.nil?
148
- end
149
-
150
- if @string.nil? || @string.match?(SKIP_INCLUDE_DIRECTIVE)
151
- SkipInclude.render(@document, @variables) do |modified_ast|
152
- @document = modified_ast
153
- @string = @normalized_string = nil
154
- @digest = @normalized_digest = nil
155
- @operation = @operation_directives = @variable_definitions = @plan = nil
156
- end
157
- end
158
-
159
- self
143
+ @query.static_errors
160
144
  end
161
145
 
162
146
  # Gets and sets the query plan for the request. Assigned query plans may pull from a cache,
@@ -191,6 +175,27 @@ module GraphQL
191
175
 
192
176
  private
193
177
 
178
+ # Prepares the request for stitching by applying @skip/@include conditionals.
179
+ def prepared_document
180
+ @prepared_document || with_prepared_document { @prepared_document }
181
+ end
182
+
183
+ def with_prepared_document
184
+ unless @prepared_document
185
+ @variables = @query.variables.to_h
186
+
187
+ @prepared_document = if @string.nil? || @string.match?(SKIP_INCLUDE_DIRECTIVE)
188
+ changed = false
189
+ doc = SkipInclude.render(@query.document, @variables) { changed = true }
190
+ @string = @normalized_string = doc.to_query_string if changed
191
+ doc
192
+ else
193
+ @query.document
194
+ end
195
+ end
196
+ yield
197
+ end
198
+
194
199
  # Adds a handler into context for enriching subscription updates with stitched data
195
200
  def add_subscription_update_handler
196
201
  request = self
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Stitching
5
- VERSION = "1.5.2"
5
+ VERSION = "1.6.0"
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: 1.5.2
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg MacWilliam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-09 00:00:00.000000000 Z
11
+ date: 2025-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql