graphql 1.11.12 → 1.12.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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +5 -5
- data/lib/generators/graphql/relay_generator.rb +63 -0
- data/lib/generators/graphql/templates/base_connection.erb +8 -0
- data/lib/generators/graphql/templates/base_edge.erb +8 -0
- data/lib/generators/graphql/templates/node_type.erb +9 -0
- data/lib/generators/graphql/templates/object.erb +1 -1
- data/lib/generators/graphql/templates/query_type.erb +1 -3
- data/lib/generators/graphql/templates/schema.erb +8 -35
- data/lib/graphql/analysis/analyze_query.rb +7 -0
- data/lib/graphql/analysis/ast/visitor.rb +9 -1
- data/lib/graphql/analysis/ast.rb +11 -2
- data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
- data/lib/graphql/backtrace/table.rb +22 -2
- data/lib/graphql/backtrace/tracer.rb +40 -9
- data/lib/graphql/backtrace.rb +28 -19
- data/lib/graphql/backwards_compatibility.rb +1 -0
- data/lib/graphql/compatibility/execution_specification.rb +1 -0
- data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
- data/lib/graphql/dataloader/null_dataloader.rb +21 -0
- data/lib/graphql/dataloader/request.rb +24 -0
- data/lib/graphql/dataloader/request_all.rb +22 -0
- data/lib/graphql/dataloader/source.rb +93 -0
- data/lib/graphql/dataloader.rb +197 -0
- data/lib/graphql/define/assign_global_id_field.rb +1 -1
- data/lib/graphql/define/instance_definable.rb +32 -2
- data/lib/graphql/define/type_definer.rb +5 -5
- data/lib/graphql/deprecated_dsl.rb +5 -0
- data/lib/graphql/enum_type.rb +2 -0
- data/lib/graphql/execution/errors.rb +4 -0
- data/lib/graphql/execution/execute.rb +7 -0
- data/lib/graphql/execution/interpreter/arguments.rb +51 -14
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
- data/lib/graphql/execution/interpreter/runtime.rb +210 -124
- data/lib/graphql/execution/interpreter.rb +10 -6
- data/lib/graphql/execution/multiplex.rb +20 -6
- data/lib/graphql/function.rb +4 -0
- data/lib/graphql/input_object_type.rb +2 -0
- data/lib/graphql/interface_type.rb +3 -1
- data/lib/graphql/language/document_from_schema_definition.rb +50 -23
- data/lib/graphql/language/nodes.rb +0 -5
- data/lib/graphql/language/visitor.rb +0 -1
- data/lib/graphql/object_type.rb +2 -0
- data/lib/graphql/pagination/connection.rb +5 -1
- data/lib/graphql/pagination/connections.rb +6 -16
- data/lib/graphql/query/context.rb +4 -0
- data/lib/graphql/query/serial_execution.rb +1 -0
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/query.rb +2 -0
- data/lib/graphql/relay/base_connection.rb +7 -0
- data/lib/graphql/relay/connection_instrumentation.rb +4 -4
- data/lib/graphql/relay/connection_type.rb +1 -1
- data/lib/graphql/relay/mutation.rb +1 -0
- data/lib/graphql/relay/node.rb +3 -0
- data/lib/graphql/relay/type_extensions.rb +2 -0
- data/lib/graphql/scalar_type.rb +2 -0
- data/lib/graphql/schema/argument.rb +30 -10
- data/lib/graphql/schema/build_from_definition.rb +145 -58
- data/lib/graphql/schema/directive/flagged.rb +57 -0
- data/lib/graphql/schema/directive.rb +76 -0
- data/lib/graphql/schema/enum.rb +3 -0
- data/lib/graphql/schema/enum_value.rb +13 -7
- data/lib/graphql/schema/field/connection_extension.rb +3 -2
- data/lib/graphql/schema/field.rb +28 -10
- data/lib/graphql/schema/input_object.rb +36 -28
- data/lib/graphql/schema/interface.rb +1 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
- data/lib/graphql/schema/member/build_type.rb +3 -3
- data/lib/graphql/schema/member/has_arguments.rb +24 -6
- data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
- data/lib/graphql/schema/member/has_directives.rb +98 -0
- data/lib/graphql/schema/member/has_validators.rb +31 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member.rb +4 -0
- data/lib/graphql/schema/object.rb +11 -0
- data/lib/graphql/schema/printer.rb +5 -4
- data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
- data/lib/graphql/schema/resolver.rb +7 -0
- data/lib/graphql/schema/subscription.rb +19 -1
- data/lib/graphql/schema/timeout_middleware.rb +2 -0
- data/lib/graphql/schema/validation.rb +2 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
- data/lib/graphql/schema/validator/format_validator.rb +49 -0
- data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
- data/lib/graphql/schema/validator/length_validator.rb +57 -0
- data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
- data/lib/graphql/schema/validator/required_validator.rb +68 -0
- data/lib/graphql/schema/validator.rb +163 -0
- data/lib/graphql/schema.rb +72 -49
- data/lib/graphql/static_validation/base_visitor.rb +0 -3
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +4 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +1 -6
- data/lib/graphql/static_validation/validator.rb +12 -14
- data/lib/graphql/subscriptions.rb +17 -20
- data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/skylight_tracing.rb +1 -1
- data/lib/graphql/tracing.rb +2 -2
- data/lib/graphql/types/relay/base_connection.rb +2 -92
- data/lib/graphql/types/relay/base_edge.rb +2 -35
- data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
- data/lib/graphql/types/relay/default_relay.rb +27 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
- data/lib/graphql/types/relay/has_node_field.rb +41 -0
- data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
- data/lib/graphql/types/relay/node.rb +2 -4
- data/lib/graphql/types/relay/node_behaviors.rb +15 -0
- data/lib/graphql/types/relay/node_field.rb +1 -19
- data/lib/graphql/types/relay/nodes_field.rb +1 -19
- data/lib/graphql/types/relay/page_info.rb +2 -14
- data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
- data/lib/graphql/types/relay.rb +11 -3
- data/lib/graphql/union_type.rb +2 -0
- data/lib/graphql/upgrader/member.rb +1 -0
- data/lib/graphql/upgrader/schema.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +38 -4
- metadata +34 -9
- data/lib/graphql/types/relay/base_field.rb +0 -22
- data/lib/graphql/types/relay/base_interface.rb +0 -29
- data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -19,8 +19,10 @@ module GraphQL
|
|
19
19
|
|
20
20
|
def initialize(query:, response:)
|
21
21
|
@query = query
|
22
|
+
@dataloader = query.multiplex.dataloader
|
22
23
|
@schema = query.schema
|
23
24
|
@context = query.context
|
25
|
+
@multiplex_context = query.multiplex.context
|
24
26
|
@interpreter_context = @context.namespace(:interpreter)
|
25
27
|
@response = response
|
26
28
|
@dead_paths = {}
|
@@ -54,7 +56,17 @@ module GraphQL
|
|
54
56
|
# Root .authorized? returned false.
|
55
57
|
write_in_response(path, nil)
|
56
58
|
else
|
57
|
-
|
59
|
+
# Prepare this runtime state to be encapsulated in a Fiber
|
60
|
+
@progress_path = path
|
61
|
+
@progress_scoped_context = context.scoped_context
|
62
|
+
@progress_object = object_proxy
|
63
|
+
@progress_object_type = root_type
|
64
|
+
@progress_index = nil
|
65
|
+
@progress_is_eager_selection = root_op_type == "mutation"
|
66
|
+
@progress_selections = gather_selections(object_proxy, root_type, root_operation.selections)
|
67
|
+
|
68
|
+
# Make the first fiber which will begin execution
|
69
|
+
enqueue_selections_fiber
|
58
70
|
end
|
59
71
|
delete_interpreter_context(:current_path)
|
60
72
|
delete_interpreter_context(:current_field)
|
@@ -63,7 +75,33 @@ module GraphQL
|
|
63
75
|
nil
|
64
76
|
end
|
65
77
|
|
66
|
-
|
78
|
+
# Use `@dataloader` to enqueue a fiber that will pick up from the current point.
|
79
|
+
# @return [void]
|
80
|
+
def enqueue_selections_fiber
|
81
|
+
# Read these into local variables so that later assignments don't affect the block below.
|
82
|
+
path = @progress_path
|
83
|
+
scoped_context = @progress_scoped_context
|
84
|
+
owner_object = @progress_object
|
85
|
+
owner_type = @progress_object_type
|
86
|
+
idx = @progress_index
|
87
|
+
is_eager_selection = @progress_is_eager_selection
|
88
|
+
gathered_selections = @progress_selections
|
89
|
+
|
90
|
+
@dataloader.enqueue {
|
91
|
+
evaluate_selections(
|
92
|
+
path,
|
93
|
+
scoped_context,
|
94
|
+
owner_object,
|
95
|
+
owner_type,
|
96
|
+
is_eager_selection: is_eager_selection,
|
97
|
+
after: idx,
|
98
|
+
gathered_selections: gathered_selections,
|
99
|
+
)
|
100
|
+
}
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def gather_selections(owner_object, owner_type, selections, selections_by_name = {})
|
67
105
|
selections.each do |node|
|
68
106
|
# Skip gathering this if the directive says so
|
69
107
|
if !directives_include?(node, owner_object, owner_type)
|
@@ -115,145 +153,184 @@ module GraphQL
|
|
115
153
|
raise "Invariant: unexpected selection class: #{node.class}"
|
116
154
|
end
|
117
155
|
end
|
156
|
+
selections_by_name
|
118
157
|
end
|
119
158
|
|
120
159
|
NO_ARGS = {}.freeze
|
121
160
|
|
122
|
-
|
161
|
+
# @return [void]
|
162
|
+
def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection:, gathered_selections:, after:)
|
123
163
|
set_all_interpreter_context(owner_object, nil, nil, path)
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
164
|
+
|
165
|
+
@progress_path = path
|
166
|
+
@progress_scoped_context = scoped_context
|
167
|
+
@progress_object = owner_object
|
168
|
+
@progress_object_type = owner_type
|
169
|
+
@progress_index = nil
|
170
|
+
@progress_is_eager_selection = is_eager_selection
|
171
|
+
@progress_selections = gathered_selections
|
172
|
+
|
173
|
+
# Track `idx` manually to avoid an allocation on this hot path
|
174
|
+
idx = 0
|
175
|
+
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
176
|
+
prev_idx = idx
|
177
|
+
idx += 1
|
178
|
+
# TODO: this is how a `progress` resumes where this left off.
|
179
|
+
# Is there a better way to seek in the hash?
|
180
|
+
# I think we could also use the array of keys; it supports seeking just fine.
|
181
|
+
if after && prev_idx <= after
|
182
|
+
next
|
136
183
|
end
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
dynamic_field
|
147
|
-
else
|
148
|
-
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
149
|
-
end
|
184
|
+
@progress_index = prev_idx
|
185
|
+
# This is how the current runtime gives itself to `dataloader`
|
186
|
+
# so that the dataloader can enqueue another fiber to resume if needed.
|
187
|
+
@dataloader.current_runtime = self
|
188
|
+
evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_selection)
|
189
|
+
# The dataloader knows if ^^ that selection halted and later selections were executed in another fiber.
|
190
|
+
# If that's the case, then don't continue execution here.
|
191
|
+
if @dataloader.yielded?
|
192
|
+
break
|
150
193
|
end
|
151
|
-
|
194
|
+
end
|
195
|
+
nil
|
196
|
+
end
|
152
197
|
|
153
|
-
|
154
|
-
|
155
|
-
|
198
|
+
# @return [void]
|
199
|
+
def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field)
|
200
|
+
# As a performance optimization, the hash key will be a `Node` if
|
201
|
+
# there's only one selection of the field. But if there are multiple
|
202
|
+
# selections of the field, it will be an Array of nodes
|
203
|
+
if field_ast_nodes_or_ast_node.is_a?(Array)
|
204
|
+
field_ast_nodes = field_ast_nodes_or_ast_node
|
205
|
+
ast_node = field_ast_nodes.first
|
206
|
+
else
|
207
|
+
field_ast_nodes = nil
|
208
|
+
ast_node = field_ast_nodes_or_ast_node
|
209
|
+
end
|
210
|
+
field_name = ast_node.name
|
211
|
+
field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
|
212
|
+
is_introspection = false
|
213
|
+
if field_defn.nil?
|
214
|
+
field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
215
|
+
is_introspection = true
|
216
|
+
entry_point_field
|
217
|
+
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
218
|
+
is_introspection = true
|
219
|
+
dynamic_field
|
220
|
+
else
|
221
|
+
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
return_type = field_defn.type
|
156
225
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
set_type_at_path(next_path, return_type)
|
161
|
-
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
162
|
-
set_all_interpreter_context(nil, field_defn, nil, next_path)
|
226
|
+
next_path = path.dup
|
227
|
+
next_path << result_name
|
228
|
+
next_path.freeze
|
163
229
|
|
164
|
-
|
165
|
-
|
230
|
+
# This seems janky, but we need to know
|
231
|
+
# the field's return type at this path in order
|
232
|
+
# to propagate `null`
|
233
|
+
set_type_at_path(next_path, return_type)
|
234
|
+
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
235
|
+
set_all_interpreter_context(nil, field_defn, nil, next_path)
|
166
236
|
|
167
|
-
|
168
|
-
|
169
|
-
end
|
237
|
+
context.scoped_context = scoped_context
|
238
|
+
object = owner_object
|
170
239
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
240
|
+
if is_introspection
|
241
|
+
object = authorized_new(field_defn.owner, object, context, next_path)
|
242
|
+
end
|
243
|
+
|
244
|
+
begin
|
245
|
+
kwarg_arguments = arguments(object, field_defn, ast_node)
|
246
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => e
|
247
|
+
continue_value(next_path, e, owner_type, field_defn, return_type.non_null?, ast_node)
|
248
|
+
return
|
249
|
+
end
|
250
|
+
|
251
|
+
after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |resolved_arguments|
|
252
|
+
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
253
|
+
continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node)
|
175
254
|
next
|
176
255
|
end
|
177
256
|
|
178
|
-
|
179
|
-
case
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
kwarg_arguments[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
|
197
|
-
when :path
|
198
|
-
kwarg_arguments[:path] = next_path
|
199
|
-
when :lookahead
|
200
|
-
if !field_ast_nodes
|
201
|
-
field_ast_nodes = [ast_node]
|
202
|
-
end
|
203
|
-
kwarg_arguments[:lookahead] = Execution::Lookahead.new(
|
204
|
-
query: query,
|
205
|
-
ast_nodes: field_ast_nodes,
|
206
|
-
field: field_defn,
|
207
|
-
)
|
208
|
-
when :argument_details
|
209
|
-
kwarg_arguments[:argument_details] = resolved_arguments
|
210
|
-
else
|
211
|
-
kwarg_arguments[extra] = field_defn.fetch_extra(extra, context)
|
257
|
+
kwarg_arguments = if resolved_arguments.empty? && field_defn.extras.empty?
|
258
|
+
# We can avoid allocating the `{ Symbol => Object }` hash in this case
|
259
|
+
NO_ARGS
|
260
|
+
else
|
261
|
+
# Bundle up the extras, then make a new arguments instance
|
262
|
+
# that includes the extras, too.
|
263
|
+
extra_args = {}
|
264
|
+
field_defn.extras.each do |extra|
|
265
|
+
case extra
|
266
|
+
when :ast_node
|
267
|
+
extra_args[:ast_node] = ast_node
|
268
|
+
when :execution_errors
|
269
|
+
extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
|
270
|
+
when :path
|
271
|
+
extra_args[:path] = next_path
|
272
|
+
when :lookahead
|
273
|
+
if !field_ast_nodes
|
274
|
+
field_ast_nodes = [ast_node]
|
212
275
|
end
|
276
|
+
|
277
|
+
extra_args[:lookahead] = Execution::Lookahead.new(
|
278
|
+
query: query,
|
279
|
+
ast_nodes: field_ast_nodes,
|
280
|
+
field: field_defn,
|
281
|
+
)
|
282
|
+
when :argument_details
|
283
|
+
# Use this flag to tell Interpreter::Arguments to add itself
|
284
|
+
# to the keyword args hash _before_ freezing everything.
|
285
|
+
extra_args[:argument_details] = :__arguments_add_self
|
286
|
+
else
|
287
|
+
extra_args[extra] = field_defn.fetch_extra(extra, context)
|
213
288
|
end
|
214
289
|
end
|
290
|
+
resolved_arguments = resolved_arguments.merge_extras(extra_args)
|
291
|
+
resolved_arguments.keyword_arguments
|
292
|
+
end
|
215
293
|
|
216
|
-
|
294
|
+
set_all_interpreter_context(nil, nil, kwarg_arguments, nil)
|
217
295
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
296
|
+
# Optimize for the case that field is selected only once
|
297
|
+
if field_ast_nodes.nil? || field_ast_nodes.size == 1
|
298
|
+
next_selections = ast_node.selections
|
299
|
+
else
|
300
|
+
next_selections = []
|
301
|
+
field_ast_nodes.each { |f| next_selections.concat(f.selections) }
|
302
|
+
end
|
225
303
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
end
|
304
|
+
field_result = resolve_with_directives(object, ast_node) do
|
305
|
+
# Actually call the field resolver and capture the result
|
306
|
+
app_result = begin
|
307
|
+
query.with_error_handling do
|
308
|
+
query.trace("execute_field", {owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments}) do
|
309
|
+
field_defn.resolve(object, kwarg_arguments, context)
|
233
310
|
end
|
234
|
-
rescue GraphQL::ExecutionError => err
|
235
|
-
err
|
236
311
|
end
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
312
|
+
rescue GraphQL::ExecutionError => err
|
313
|
+
err
|
314
|
+
end
|
315
|
+
after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |inner_result|
|
316
|
+
continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node)
|
317
|
+
if RawValue === continue_value
|
318
|
+
# Write raw value directly to the response without resolving nested objects
|
319
|
+
write_in_response(next_path, continue_value.resolve)
|
320
|
+
elsif HALT != continue_value
|
321
|
+
continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, kwarg_arguments)
|
245
322
|
end
|
246
323
|
end
|
324
|
+
end
|
247
325
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
else
|
254
|
-
field_result
|
255
|
-
end
|
326
|
+
# If this field is a root mutation field, immediately resolve
|
327
|
+
# all of its child fields before moving on to the next root mutation field.
|
328
|
+
# (Subselections of this mutation will still be resolved level-by-level.)
|
329
|
+
if is_eager_field
|
330
|
+
Interpreter::Resolve.resolve_all([field_result])
|
256
331
|
end
|
332
|
+
|
333
|
+
nil
|
257
334
|
end
|
258
335
|
end
|
259
336
|
|
@@ -314,7 +391,7 @@ module GraphQL
|
|
314
391
|
resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
|
315
392
|
resolved_value ||= value
|
316
393
|
|
317
|
-
after_lazy(resolved_type_or_lazy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
|
394
|
+
after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
|
318
395
|
possible_types = query.possible_types(current_type)
|
319
396
|
|
320
397
|
if !possible_types.include?(resolved_type)
|
@@ -334,12 +411,13 @@ module GraphQL
|
|
334
411
|
rescue GraphQL::ExecutionError => err
|
335
412
|
err
|
336
413
|
end
|
337
|
-
after_lazy(object_proxy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |inner_object|
|
414
|
+
after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |inner_object|
|
338
415
|
continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node)
|
339
416
|
if HALT != continue_value
|
340
417
|
response_hash = {}
|
341
418
|
write_in_response(path, response_hash)
|
342
|
-
|
419
|
+
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
420
|
+
evaluate_selections(path, context.scoped_context, continue_value, current_type, is_eager_selection: false, gathered_selections: gathered_selections, after: nil)
|
343
421
|
response_hash
|
344
422
|
end
|
345
423
|
end
|
@@ -357,7 +435,7 @@ module GraphQL
|
|
357
435
|
idx += 1
|
358
436
|
set_type_at_path(next_path, inner_type)
|
359
437
|
# This will update `response_list` with the lazy
|
360
|
-
after_lazy(inner_value, owner: inner_type, path: next_path, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments) do |inner_inner_value|
|
438
|
+
after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments) do |inner_inner_value|
|
361
439
|
continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node)
|
362
440
|
if HALT != continue_value
|
363
441
|
continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments)
|
@@ -440,7 +518,7 @@ module GraphQL
|
|
440
518
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
441
519
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
442
520
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
443
|
-
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true, &block)
|
521
|
+
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, ast_node:, eager: false, trace: true, &block)
|
444
522
|
set_all_interpreter_context(owner_object, field, arguments, path)
|
445
523
|
if schema.lazy?(lazy_obj)
|
446
524
|
lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
|
@@ -451,7 +529,7 @@ module GraphQL
|
|
451
529
|
inner_obj = begin
|
452
530
|
query.with_error_handling do
|
453
531
|
if trace
|
454
|
-
query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments}) do
|
532
|
+
query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
|
455
533
|
schema.sync_lazy(lazy_obj)
|
456
534
|
end
|
457
535
|
else
|
@@ -461,7 +539,7 @@ module GraphQL
|
|
461
539
|
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
|
462
540
|
err
|
463
541
|
end
|
464
|
-
after_lazy(inner_obj, owner: owner, field: field, path: path, scoped_context: context.scoped_context, owner_object: owner_object, arguments: arguments, eager: eager, trace: trace, &block)
|
542
|
+
after_lazy(inner_obj, owner: owner, field: field, path: path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: owner_object, arguments: arguments, eager: eager, trace: trace, &block)
|
465
543
|
end
|
466
544
|
|
467
545
|
if eager
|
@@ -476,9 +554,7 @@ module GraphQL
|
|
476
554
|
end
|
477
555
|
|
478
556
|
def arguments(graphql_object, arg_owner, ast_node)
|
479
|
-
|
480
|
-
if arg_owner.arguments_statically_coercible? &&
|
481
|
-
(!arg_owner.is_a?(GraphQL::Schema::Field) || (arg_owner.extras.empty? && arg_owner.extensions.empty?))
|
557
|
+
if arg_owner.arguments_statically_coercible?
|
482
558
|
query.arguments_for(ast_node, arg_owner)
|
483
559
|
else
|
484
560
|
# The arguments must be prepared in the context of the given object
|
@@ -519,6 +595,16 @@ module GraphQL
|
|
519
595
|
end
|
520
596
|
end
|
521
597
|
|
598
|
+
def value_at(path)
|
599
|
+
i = 0
|
600
|
+
value = @response.final_value
|
601
|
+
while value && (part = path[i])
|
602
|
+
value = value[part]
|
603
|
+
i += 1
|
604
|
+
end
|
605
|
+
value
|
606
|
+
end
|
607
|
+
|
522
608
|
# To propagate nulls, we have to know what the field type was
|
523
609
|
# at previous parts of the response.
|
524
610
|
# This hash matches the response
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "fiber"
|
2
3
|
require "graphql/execution/interpreter/argument_value"
|
3
4
|
require "graphql/execution/interpreter/arguments"
|
4
5
|
require "graphql/execution/interpreter/arguments_cache"
|
@@ -22,12 +23,15 @@ module GraphQL
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def self.use(schema_class)
|
25
|
-
schema_class.interpreter
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
if schema_class.interpreter?
|
27
|
+
definition_line = caller(2, 1).first
|
28
|
+
warn("GraphQL::Execution::Interpreter is now the default; remove `use GraphQL::Execution::Interpreter` from the schema definition (#{definition_line})")
|
29
|
+
else
|
30
|
+
schema_class.query_execution_strategy(self)
|
31
|
+
schema_class.mutation_execution_strategy(self)
|
32
|
+
schema_class.subscription_execution_strategy(self)
|
33
|
+
schema_class.add_subscription_extension_if_necessary
|
34
|
+
end
|
31
35
|
end
|
32
36
|
|
33
37
|
def self.begin_multiplex(multiplex)
|
@@ -29,11 +29,13 @@ module GraphQL
|
|
29
29
|
|
30
30
|
include Tracing::Traceable
|
31
31
|
|
32
|
-
attr_reader :context, :queries, :schema, :max_complexity
|
32
|
+
attr_reader :context, :queries, :schema, :max_complexity, :dataloader
|
33
33
|
def initialize(schema:, queries:, context:, max_complexity:)
|
34
34
|
@schema = schema
|
35
35
|
@queries = queries
|
36
|
+
@queries.each { |q| q.multiplex = self }
|
36
37
|
@context = context
|
38
|
+
@context[:dataloader] = @dataloader = @schema.dataloader_class.new(context)
|
37
39
|
@tracers = schema.tracers + (context[:tracers] || [])
|
38
40
|
# Support `context: {backtrace: true}`
|
39
41
|
if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
|
@@ -79,20 +81,30 @@ module GraphQL
|
|
79
81
|
multiplex.schema.query_execution_strategy.begin_multiplex(multiplex)
|
80
82
|
queries = multiplex.queries
|
81
83
|
# Do as much eager evaluation of the query as possible
|
82
|
-
results =
|
83
|
-
|
84
|
+
results = []
|
85
|
+
queries.each_with_index do |query, idx|
|
86
|
+
multiplex.dataloader.enqueue {
|
87
|
+
results[idx] = begin_query(query, multiplex)
|
88
|
+
}
|
84
89
|
end
|
85
90
|
|
91
|
+
multiplex.dataloader.run
|
92
|
+
|
86
93
|
# Then, work through lazy results in a breadth-first way
|
87
|
-
multiplex.
|
94
|
+
multiplex.dataloader.enqueue {
|
95
|
+
multiplex.schema.query_execution_strategy.finish_multiplex(results, multiplex)
|
96
|
+
}
|
97
|
+
multiplex.dataloader.run
|
88
98
|
|
89
99
|
# Then, find all errors and assign the result to the query object
|
90
|
-
results.each_with_index
|
100
|
+
results.each_with_index do |data_result, idx|
|
91
101
|
query = queries[idx]
|
92
102
|
finish_query(data_result, query, multiplex)
|
93
103
|
# Get the Query::Result, not the Hash
|
94
|
-
query.result
|
104
|
+
results[idx] = query.result
|
95
105
|
end
|
106
|
+
|
107
|
+
results
|
96
108
|
rescue Exception
|
97
109
|
# TODO rescue at a higher level so it will catch errors in analysis, too
|
98
110
|
# Assign values here so that the query's `@executed` becomes true
|
@@ -144,6 +156,8 @@ module GraphQL
|
|
144
156
|
|
145
157
|
# use the old `query_execution_strategy` etc to run this query
|
146
158
|
def run_one_legacy(schema, query)
|
159
|
+
warn "Multiplex.run_one_legacy will be removed from GraphQL-Ruby 2.0, upgrade to the Interpreter to avoid this deprecated codepath: https://graphql-ruby.org/queries/interpreter.html"
|
160
|
+
|
147
161
|
query.result_values = if !query.valid?
|
148
162
|
all_errors = query.validation_errors + query.analysis_errors + query.context.errors
|
149
163
|
if all_errors.any?
|
data/lib/graphql/function.rb
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
module GraphQL
|
3
3
|
# @api deprecated
|
4
4
|
class Function
|
5
|
+
def self.inherited(subclass)
|
6
|
+
warn "GraphQL::Function (used for #{subclass}) will be removed from GraphQL-Ruby 2.0, please upgrade to resolvers: https://graphql-ruby.org/fields/resolvers.html"
|
7
|
+
end
|
8
|
+
|
5
9
|
# @return [Hash<String => GraphQL::Argument>] Arguments, keyed by name
|
6
10
|
def arguments
|
7
11
|
self.class.arguments
|
@@ -2,10 +2,12 @@
|
|
2
2
|
module GraphQL
|
3
3
|
# @api deprecated
|
4
4
|
class InterfaceType < GraphQL::BaseType
|
5
|
+
extend Define::InstanceDefinable::DeprecatedDefine
|
6
|
+
|
5
7
|
accepts_definitions :fields, :orphan_types, :resolve_type, field: GraphQL::Define::AssignObjectField
|
6
8
|
|
7
9
|
attr_accessor :fields, :orphan_types, :resolve_type_proc
|
8
|
-
attr_writer :type_membership_class
|
10
|
+
attr_writer :type_membership_class
|
9
11
|
ensure_defined :fields, :orphan_types, :resolve_type_proc, :resolve_type
|
10
12
|
|
11
13
|
def initialize
|