graphql 1.11.4 → 1.12.1
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/object_generator.rb +2 -0
- 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/generators/graphql/templates/union.erb +1 -1
- data/lib/graphql.rb +55 -4
- data/lib/graphql/analysis/analyze_query.rb +7 -0
- data/lib/graphql/analysis/ast.rb +11 -2
- data/lib/graphql/analysis/ast/visitor.rb +9 -1
- data/lib/graphql/argument.rb +3 -3
- data/lib/graphql/backtrace.rb +28 -19
- data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
- data/lib/graphql/backtrace/table.rb +22 -2
- data/lib/graphql/backtrace/tracer.rb +40 -8
- 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.rb +198 -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/define/assign_global_id_field.rb +2 -2
- 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.rb +20 -6
- data/lib/graphql/execution/interpreter/arguments.rb +57 -5
- data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
- data/lib/graphql/execution/interpreter/runtime.rb +236 -120
- 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/integer_decoding_error.rb +17 -0
- data/lib/graphql/interface_type.rb +3 -1
- data/lib/graphql/introspection.rb +96 -0
- data/lib/graphql/introspection/field_type.rb +7 -3
- data/lib/graphql/introspection/input_value_type.rb +6 -0
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/type_type.rb +7 -3
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/block_string.rb +24 -5
- data/lib/graphql/language/document_from_schema_definition.rb +50 -23
- data/lib/graphql/language/lexer.rb +7 -3
- data/lib/graphql/language/lexer.rl +7 -3
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +107 -103
- data/lib/graphql/language/parser.y +4 -0
- data/lib/graphql/language/sanitized_printer.rb +59 -26
- data/lib/graphql/name_validator.rb +6 -7
- data/lib/graphql/object_type.rb +2 -0
- data/lib/graphql/pagination/connection.rb +5 -1
- data/lib/graphql/pagination/connections.rb +15 -17
- data/lib/graphql/query.rb +8 -3
- data/lib/graphql/query/context.rb +18 -3
- data/lib/graphql/query/serial_execution.rb +1 -0
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/relay/array_connection.rb +2 -2
- 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/range_add.rb +14 -5
- data/lib/graphql/relay/type_extensions.rb +2 -0
- data/lib/graphql/scalar_type.rb +2 -0
- data/lib/graphql/schema.rb +104 -39
- data/lib/graphql/schema/argument.rb +74 -5
- data/lib/graphql/schema/build_from_definition.rb +203 -86
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive.rb +76 -0
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +57 -0
- data/lib/graphql/schema/enum.rb +3 -0
- data/lib/graphql/schema/enum_value.rb +12 -6
- data/lib/graphql/schema/field.rb +59 -24
- data/lib/graphql/schema/field/connection_extension.rb +10 -8
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/input_object.rb +38 -25
- data/lib/graphql/schema/interface.rb +2 -1
- data/lib/graphql/schema/late_bound_type.rb +2 -2
- data/lib/graphql/schema/loader.rb +1 -0
- data/lib/graphql/schema/member.rb +4 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
- data/lib/graphql/schema/member/build_type.rb +17 -7
- data/lib/graphql/schema/member/has_arguments.rb +70 -51
- 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_fields.rb +2 -2
- data/lib/graphql/schema/member/has_validators.rb +31 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
- data/lib/graphql/schema/object.rb +11 -0
- data/lib/graphql/schema/printer.rb +5 -4
- data/lib/graphql/schema/relay_classic_mutation.rb +4 -2
- data/lib/graphql/schema/resolver.rb +7 -0
- data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
- data/lib/graphql/schema/subscription.rb +19 -1
- data/lib/graphql/schema/timeout.rb +29 -15
- data/lib/graphql/schema/timeout_middleware.rb +2 -0
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validation.rb +10 -0
- data/lib/graphql/schema/validator.rb +163 -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/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +25 -17
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +33 -7
- data/lib/graphql/subscriptions.rb +18 -23
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -7
- data/lib/graphql/tracing.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
- data/lib/graphql/tracing/platform_tracing.rb +4 -2
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/tracing/skylight_tracing.rb +1 -1
- data/lib/graphql/types/int.rb +9 -2
- data/lib/graphql/types/relay.rb +11 -3
- data/lib/graphql/types/relay/base_connection.rb +2 -91
- data/lib/graphql/types/relay/base_edge.rb +2 -34
- 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/string.rb +7 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- 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/readme.md +1 -1
- metadata +49 -6
- 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 = {}
|
@@ -43,26 +45,63 @@ module GraphQL
|
|
43
45
|
# might be stored up in lazies.
|
44
46
|
# @return [void]
|
45
47
|
def run_eager
|
46
|
-
|
47
48
|
root_operation = query.selected_operation
|
48
49
|
root_op_type = root_operation.operation_type || "query"
|
49
50
|
root_type = schema.root_type_for_operation(root_op_type)
|
50
51
|
path = []
|
51
|
-
|
52
|
-
@interpreter_context[:current_path] = path
|
52
|
+
set_all_interpreter_context(query.root_value, nil, nil, path)
|
53
53
|
object_proxy = authorized_new(root_type, query.root_value, context, path)
|
54
54
|
object_proxy = schema.sync_lazy(object_proxy)
|
55
55
|
if object_proxy.nil?
|
56
56
|
# Root .authorized? returned false.
|
57
57
|
write_in_response(path, nil)
|
58
|
-
nil
|
59
58
|
else
|
60
|
-
|
61
|
-
|
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
|
62
70
|
end
|
71
|
+
delete_interpreter_context(:current_path)
|
72
|
+
delete_interpreter_context(:current_field)
|
73
|
+
delete_interpreter_context(:current_object)
|
74
|
+
delete_interpreter_context(:current_arguments)
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
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
|
63
102
|
end
|
64
103
|
|
65
|
-
def gather_selections(owner_object, owner_type, selections, selections_by_name)
|
104
|
+
def gather_selections(owner_object, owner_type, selections, selections_by_name = {})
|
66
105
|
selections.each do |node|
|
67
106
|
# Skip gathering this if the directive says so
|
68
107
|
if !directives_include?(node, owner_object, owner_type)
|
@@ -114,139 +153,186 @@ module GraphQL
|
|
114
153
|
raise "Invariant: unexpected selection class: #{node.class}"
|
115
154
|
end
|
116
155
|
end
|
156
|
+
selections_by_name
|
117
157
|
end
|
118
158
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
159
|
+
NO_ARGS = {}.freeze
|
160
|
+
|
161
|
+
# @return [void]
|
162
|
+
def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection:, gathered_selections:, after:)
|
163
|
+
set_all_interpreter_context(owner_object, nil, nil, path)
|
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
|
134
183
|
end
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
dynamic_field
|
145
|
-
else
|
146
|
-
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
147
|
-
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?(path)
|
192
|
+
break
|
148
193
|
end
|
149
|
-
|
194
|
+
end
|
195
|
+
nil
|
196
|
+
end
|
150
197
|
|
151
|
-
|
152
|
-
next_path << result_name
|
153
|
-
next_path.freeze
|
198
|
+
attr_reader :progress_path
|
154
199
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
200
|
+
# @return [void]
|
201
|
+
def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field)
|
202
|
+
# As a performance optimization, the hash key will be a `Node` if
|
203
|
+
# there's only one selection of the field. But if there are multiple
|
204
|
+
# selections of the field, it will be an Array of nodes
|
205
|
+
if field_ast_nodes_or_ast_node.is_a?(Array)
|
206
|
+
field_ast_nodes = field_ast_nodes_or_ast_node
|
207
|
+
ast_node = field_ast_nodes.first
|
208
|
+
else
|
209
|
+
field_ast_nodes = nil
|
210
|
+
ast_node = field_ast_nodes_or_ast_node
|
211
|
+
end
|
212
|
+
field_name = ast_node.name
|
213
|
+
field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
|
214
|
+
is_introspection = false
|
215
|
+
if field_defn.nil?
|
216
|
+
field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
217
|
+
is_introspection = true
|
218
|
+
entry_point_field
|
219
|
+
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
220
|
+
is_introspection = true
|
221
|
+
dynamic_field
|
222
|
+
else
|
223
|
+
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
return_type = field_defn.type
|
162
227
|
|
163
|
-
|
164
|
-
|
228
|
+
next_path = path.dup
|
229
|
+
next_path << result_name
|
230
|
+
next_path.freeze
|
165
231
|
|
166
|
-
|
167
|
-
|
168
|
-
|
232
|
+
# This seems janky, but we need to know
|
233
|
+
# the field's return type at this path in order
|
234
|
+
# to propagate `null`
|
235
|
+
set_type_at_path(next_path, return_type)
|
236
|
+
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
237
|
+
set_all_interpreter_context(nil, field_defn, nil, next_path)
|
169
238
|
|
170
|
-
|
171
|
-
|
172
|
-
rescue GraphQL::ExecutionError => e
|
173
|
-
continue_value(next_path, e, owner_type, field_defn, return_type.non_null?, ast_node)
|
174
|
-
next
|
175
|
-
end
|
239
|
+
context.scoped_context = scoped_context
|
240
|
+
object = owner_object
|
176
241
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
242
|
+
if is_introspection
|
243
|
+
object = authorized_new(field_defn.owner, object, context, next_path)
|
244
|
+
end
|
245
|
+
|
246
|
+
begin
|
247
|
+
kwarg_arguments = arguments(object, field_defn, ast_node)
|
248
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => e
|
249
|
+
continue_value(next_path, e, owner_type, field_defn, return_type.non_null?, ast_node)
|
250
|
+
return
|
251
|
+
end
|
182
252
|
|
183
|
-
|
253
|
+
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|
|
254
|
+
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
255
|
+
continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node)
|
256
|
+
next
|
257
|
+
end
|
184
258
|
|
259
|
+
kwarg_arguments = if resolved_arguments.empty? && field_defn.extras.empty?
|
260
|
+
# We can avoid allocating the `{ Symbol => Object }` hash in this case
|
261
|
+
NO_ARGS
|
262
|
+
else
|
263
|
+
# Bundle up the extras, then make a new arguments instance
|
264
|
+
# that includes the extras, too.
|
265
|
+
extra_args = {}
|
185
266
|
field_defn.extras.each do |extra|
|
186
267
|
case extra
|
187
268
|
when :ast_node
|
188
|
-
|
269
|
+
extra_args[:ast_node] = ast_node
|
189
270
|
when :execution_errors
|
190
|
-
|
271
|
+
extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
|
191
272
|
when :path
|
192
|
-
|
273
|
+
extra_args[:path] = next_path
|
193
274
|
when :lookahead
|
194
275
|
if !field_ast_nodes
|
195
276
|
field_ast_nodes = [ast_node]
|
196
277
|
end
|
197
|
-
|
278
|
+
|
279
|
+
extra_args[:lookahead] = Execution::Lookahead.new(
|
198
280
|
query: query,
|
199
281
|
ast_nodes: field_ast_nodes,
|
200
282
|
field: field_defn,
|
201
283
|
)
|
202
284
|
when :argument_details
|
203
|
-
|
285
|
+
# Use this flag to tell Interpreter::Arguments to add itself
|
286
|
+
# to the keyword args hash _before_ freezing everything.
|
287
|
+
extra_args[:argument_details] = :__arguments_add_self
|
204
288
|
else
|
205
|
-
|
289
|
+
extra_args[extra] = field_defn.fetch_extra(extra, context)
|
206
290
|
end
|
207
291
|
end
|
292
|
+
resolved_arguments = resolved_arguments.merge_extras(extra_args)
|
293
|
+
resolved_arguments.keyword_arguments
|
294
|
+
end
|
208
295
|
|
209
|
-
|
296
|
+
set_all_interpreter_context(nil, nil, kwarg_arguments, nil)
|
210
297
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
298
|
+
# Optimize for the case that field is selected only once
|
299
|
+
if field_ast_nodes.nil? || field_ast_nodes.size == 1
|
300
|
+
next_selections = ast_node.selections
|
301
|
+
else
|
302
|
+
next_selections = []
|
303
|
+
field_ast_nodes.each { |f| next_selections.concat(f.selections) }
|
304
|
+
end
|
218
305
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
end
|
306
|
+
field_result = resolve_with_directives(object, ast_node) do
|
307
|
+
# Actually call the field resolver and capture the result
|
308
|
+
app_result = begin
|
309
|
+
query.with_error_handling do
|
310
|
+
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
|
311
|
+
field_defn.resolve(object, kwarg_arguments, context)
|
226
312
|
end
|
227
|
-
rescue GraphQL::ExecutionError => err
|
228
|
-
err
|
229
313
|
end
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
314
|
+
rescue GraphQL::ExecutionError => err
|
315
|
+
err
|
316
|
+
end
|
317
|
+
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|
|
318
|
+
continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node)
|
319
|
+
if RawValue === continue_value
|
320
|
+
# Write raw value directly to the response without resolving nested objects
|
321
|
+
write_in_response(next_path, continue_value.resolve)
|
322
|
+
elsif HALT != continue_value
|
323
|
+
continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, kwarg_arguments)
|
238
324
|
end
|
239
325
|
end
|
326
|
+
end
|
240
327
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
else
|
247
|
-
field_result
|
248
|
-
end
|
328
|
+
# If this field is a root mutation field, immediately resolve
|
329
|
+
# all of its child fields before moving on to the next root mutation field.
|
330
|
+
# (Subselections of this mutation will still be resolved level-by-level.)
|
331
|
+
if is_eager_field
|
332
|
+
Interpreter::Resolve.resolve_all([field_result])
|
249
333
|
end
|
334
|
+
|
335
|
+
nil
|
250
336
|
end
|
251
337
|
end
|
252
338
|
|
@@ -307,7 +393,7 @@ module GraphQL
|
|
307
393
|
resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
|
308
394
|
resolved_value ||= value
|
309
395
|
|
310
|
-
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|
|
396
|
+
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|
|
311
397
|
possible_types = query.possible_types(current_type)
|
312
398
|
|
313
399
|
if !possible_types.include?(resolved_type)
|
@@ -327,12 +413,13 @@ module GraphQL
|
|
327
413
|
rescue GraphQL::ExecutionError => err
|
328
414
|
err
|
329
415
|
end
|
330
|
-
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|
|
416
|
+
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|
|
331
417
|
continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node)
|
332
418
|
if HALT != continue_value
|
333
419
|
response_hash = {}
|
334
420
|
write_in_response(path, response_hash)
|
335
|
-
|
421
|
+
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
422
|
+
evaluate_selections(path, context.scoped_context, continue_value, current_type, is_eager_selection: false, gathered_selections: gathered_selections, after: nil)
|
336
423
|
response_hash
|
337
424
|
end
|
338
425
|
end
|
@@ -350,7 +437,7 @@ module GraphQL
|
|
350
437
|
idx += 1
|
351
438
|
set_type_at_path(next_path, inner_type)
|
352
439
|
# This will update `response_list` with the lazy
|
353
|
-
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|
|
440
|
+
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|
|
354
441
|
continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node)
|
355
442
|
if HALT != continue_value
|
356
443
|
continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments)
|
@@ -412,30 +499,39 @@ module GraphQL
|
|
412
499
|
true
|
413
500
|
end
|
414
501
|
|
502
|
+
def set_all_interpreter_context(object, field, arguments, path)
|
503
|
+
if object
|
504
|
+
@context[:current_object] = @interpreter_context[:current_object] = object
|
505
|
+
end
|
506
|
+
if field
|
507
|
+
@context[:current_field] = @interpreter_context[:current_field] = field
|
508
|
+
end
|
509
|
+
if arguments
|
510
|
+
@context[:current_arguments] = @interpreter_context[:current_arguments] = arguments
|
511
|
+
end
|
512
|
+
if path
|
513
|
+
@context[:current_path] = @interpreter_context[:current_path] = path
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
415
517
|
# @param obj [Object] Some user-returned value that may want to be batched
|
416
518
|
# @param path [Array<String>]
|
417
519
|
# @param field [GraphQL::Schema::Field]
|
418
520
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
419
521
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
420
522
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
421
|
-
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true, &block)
|
422
|
-
|
423
|
-
@interpreter_context[:current_arguments] = arguments
|
424
|
-
@interpreter_context[:current_path] = path
|
425
|
-
@interpreter_context[:current_field] = field
|
523
|
+
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, ast_node:, eager: false, trace: true, &block)
|
524
|
+
set_all_interpreter_context(owner_object, field, arguments, path)
|
426
525
|
if schema.lazy?(lazy_obj)
|
427
526
|
lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
|
428
|
-
|
429
|
-
@interpreter_context[:current_field] = field
|
430
|
-
@interpreter_context[:current_object] = owner_object
|
431
|
-
@interpreter_context[:current_arguments] = arguments
|
527
|
+
set_all_interpreter_context(owner_object, field, arguments, path)
|
432
528
|
context.scoped_context = scoped_context
|
433
529
|
# Wrap the execution of _this_ method with tracing,
|
434
530
|
# but don't wrap the continuation below
|
435
531
|
inner_obj = begin
|
436
532
|
query.with_error_handling do
|
437
533
|
if trace
|
438
|
-
query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments}) do
|
534
|
+
query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
|
439
535
|
schema.sync_lazy(lazy_obj)
|
440
536
|
end
|
441
537
|
else
|
@@ -445,7 +541,7 @@ module GraphQL
|
|
445
541
|
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
|
446
542
|
err
|
447
543
|
end
|
448
|
-
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)
|
544
|
+
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)
|
449
545
|
end
|
450
546
|
|
451
547
|
if eager
|
@@ -460,9 +556,7 @@ module GraphQL
|
|
460
556
|
end
|
461
557
|
|
462
558
|
def arguments(graphql_object, arg_owner, ast_node)
|
463
|
-
|
464
|
-
if arg_owner.arguments_statically_coercible? &&
|
465
|
-
(!arg_owner.is_a?(GraphQL::Schema::Field) || (arg_owner.extras.empty? && arg_owner.extensions.empty?))
|
559
|
+
if arg_owner.arguments_statically_coercible?
|
466
560
|
query.arguments_for(ast_node, arg_owner)
|
467
561
|
else
|
468
562
|
# The arguments must be prepared in the context of the given object
|
@@ -503,6 +597,16 @@ module GraphQL
|
|
503
597
|
end
|
504
598
|
end
|
505
599
|
|
600
|
+
def value_at(path)
|
601
|
+
i = 0
|
602
|
+
value = @response.final_value
|
603
|
+
while value && (part = path[i])
|
604
|
+
value = value[part]
|
605
|
+
i += 1
|
606
|
+
end
|
607
|
+
value
|
608
|
+
end
|
609
|
+
|
506
610
|
# To propagate nulls, we have to know what the field type was
|
507
611
|
# at previous parts of the response.
|
508
612
|
# This hash matches the response
|
@@ -539,6 +643,18 @@ module GraphQL
|
|
539
643
|
res && res[:__dead]
|
540
644
|
end
|
541
645
|
|
646
|
+
# Set this pair in the Query context, but also in the interpeter namespace,
|
647
|
+
# for compatibility.
|
648
|
+
def set_interpreter_context(key, value)
|
649
|
+
@interpreter_context[key] = value
|
650
|
+
@context[key] = value
|
651
|
+
end
|
652
|
+
|
653
|
+
def delete_interpreter_context(key)
|
654
|
+
@interpreter_context.delete(key)
|
655
|
+
@context.delete(key)
|
656
|
+
end
|
657
|
+
|
542
658
|
def resolve_type(type, value, path)
|
543
659
|
trace_payload = { context: context, type: type, object: value, path: path }
|
544
660
|
resolved_type, resolved_value = query.trace("resolve_type", trace_payload) do
|