graphql 2.0.27 → 2.1.3
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/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +2 -0
- data/lib/graphql/analysis/ast/analyzer.rb +7 -0
- data/lib/graphql/analysis/ast/query_depth.rb +7 -2
- data/lib/graphql/analysis/ast/visitor.rb +2 -2
- data/lib/graphql/analysis/ast.rb +15 -11
- data/lib/graphql/dataloader/source.rb +7 -0
- data/lib/graphql/dataloader.rb +38 -10
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +95 -254
- data/lib/graphql/execution/interpreter.rb +0 -6
- data/lib/graphql/execution/lookahead.rb +1 -1
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/language/block_string.rb +28 -16
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +36 -35
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/language/printer.rb +294 -145
- data/lib/graphql/language/sanitized_printer.rb +20 -22
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +20 -81
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/pagination/connection.rb +23 -1
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +32 -98
- data/lib/graphql/query.rb +2 -19
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field/scope_extension.rb +7 -1
- data/lib/graphql/schema/field.rb +7 -4
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/introspection_system.rb +2 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +19 -4
- data/lib/graphql/schema/member/has_fields.rb +4 -1
- data/lib/graphql/schema/member/has_interfaces.rb +21 -7
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +8 -7
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
- data/lib/graphql/schema/resolver.rb +4 -0
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/warden.rb +87 -89
- data/lib/graphql/schema.rb +125 -55
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +1 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
- data/lib/graphql/subscriptions.rb +11 -6
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/types/relay/connection_behaviors.rb +19 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -2
- metadata +23 -20
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/static_validation/type_stack.rb +0 -216
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "graphql/execution/interpreter/runtime/graphql_result"
|
2
3
|
|
3
4
|
module GraphQL
|
4
5
|
module Execution
|
@@ -15,171 +16,11 @@ module GraphQL
|
|
15
16
|
@current_arguments = nil
|
16
17
|
@current_result_name = nil
|
17
18
|
@current_result = nil
|
19
|
+
@was_authorized_by_scope_items = nil
|
18
20
|
end
|
19
21
|
|
20
22
|
attr_accessor :current_result, :current_result_name,
|
21
|
-
:current_arguments, :current_field, :current_object
|
22
|
-
end
|
23
|
-
|
24
|
-
module GraphQLResult
|
25
|
-
def initialize(result_name, parent_result, is_non_null_in_parent)
|
26
|
-
@graphql_parent = parent_result
|
27
|
-
if parent_result && parent_result.graphql_dead
|
28
|
-
@graphql_dead = true
|
29
|
-
end
|
30
|
-
@graphql_result_name = result_name
|
31
|
-
@graphql_is_non_null_in_parent = is_non_null_in_parent
|
32
|
-
# Jump through some hoops to avoid creating this duplicate storage if at all possible.
|
33
|
-
@graphql_metadata = nil
|
34
|
-
end
|
35
|
-
|
36
|
-
def path
|
37
|
-
@path ||= build_path([])
|
38
|
-
end
|
39
|
-
|
40
|
-
def build_path(path_array)
|
41
|
-
graphql_result_name && path_array.unshift(graphql_result_name)
|
42
|
-
@graphql_parent ? @graphql_parent.build_path(path_array) : path_array
|
43
|
-
end
|
44
|
-
|
45
|
-
attr_accessor :graphql_dead
|
46
|
-
attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent
|
47
|
-
|
48
|
-
# @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
|
49
|
-
attr_accessor :graphql_result_data
|
50
|
-
end
|
51
|
-
|
52
|
-
class GraphQLResultHash
|
53
|
-
def initialize(_result_name, _parent_result, _is_non_null_in_parent)
|
54
|
-
super
|
55
|
-
@graphql_result_data = {}
|
56
|
-
end
|
57
|
-
|
58
|
-
include GraphQLResult
|
59
|
-
|
60
|
-
attr_accessor :graphql_merged_into
|
61
|
-
|
62
|
-
def set_leaf(key, value)
|
63
|
-
# This is a hack.
|
64
|
-
# Basically, this object is merged into the root-level result at some point.
|
65
|
-
# But the problem is, some lazies are created whose closures retain reference to _this_
|
66
|
-
# object. When those lazies are resolved, they cause an update to this object.
|
67
|
-
#
|
68
|
-
# In order to return a proper top-level result, we have to update that top-level result object.
|
69
|
-
# In order to return a proper partial result (eg, for a directive), we have to update this object, too.
|
70
|
-
# Yowza.
|
71
|
-
if (t = @graphql_merged_into)
|
72
|
-
t.set_leaf(key, value)
|
73
|
-
end
|
74
|
-
|
75
|
-
@graphql_result_data[key] = value
|
76
|
-
# keep this up-to-date if it's been initialized
|
77
|
-
@graphql_metadata && @graphql_metadata[key] = value
|
78
|
-
|
79
|
-
value
|
80
|
-
end
|
81
|
-
|
82
|
-
def set_child_result(key, value)
|
83
|
-
if (t = @graphql_merged_into)
|
84
|
-
t.set_child_result(key, value)
|
85
|
-
end
|
86
|
-
@graphql_result_data[key] = value.graphql_result_data
|
87
|
-
# If we encounter some part of this response that requires metadata tracking,
|
88
|
-
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
89
|
-
(@graphql_metadata ||= @graphql_result_data.dup)[key] = value
|
90
|
-
value
|
91
|
-
end
|
92
|
-
|
93
|
-
def delete(key)
|
94
|
-
@graphql_metadata && @graphql_metadata.delete(key)
|
95
|
-
@graphql_result_data.delete(key)
|
96
|
-
end
|
97
|
-
|
98
|
-
def each
|
99
|
-
(@graphql_metadata || @graphql_result_data).each { |k, v| yield(k, v) }
|
100
|
-
end
|
101
|
-
|
102
|
-
def values
|
103
|
-
(@graphql_metadata || @graphql_result_data).values
|
104
|
-
end
|
105
|
-
|
106
|
-
def key?(k)
|
107
|
-
@graphql_result_data.key?(k)
|
108
|
-
end
|
109
|
-
|
110
|
-
def [](k)
|
111
|
-
(@graphql_metadata || @graphql_result_data)[k]
|
112
|
-
end
|
113
|
-
|
114
|
-
def merge_into(into_result)
|
115
|
-
self.each do |key, value|
|
116
|
-
case value
|
117
|
-
when GraphQLResultHash
|
118
|
-
next_into = into_result[key]
|
119
|
-
if next_into
|
120
|
-
value.merge_into(next_into)
|
121
|
-
else
|
122
|
-
into_result.set_child_result(key, value)
|
123
|
-
end
|
124
|
-
when GraphQLResultArray
|
125
|
-
# There's no special handling of arrays because currently, there's no way to split the execution
|
126
|
-
# of a list over several concurrent flows.
|
127
|
-
next_result.set_child_result(key, value)
|
128
|
-
else
|
129
|
-
# We have to assume that, since this passed the `fields_will_merge` selection,
|
130
|
-
# that the old and new values are the same.
|
131
|
-
into_result.set_leaf(key, value)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
@graphql_merged_into = into_result
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
class GraphQLResultArray
|
139
|
-
include GraphQLResult
|
140
|
-
|
141
|
-
def initialize(_result_name, _parent_result, _is_non_null_in_parent)
|
142
|
-
super
|
143
|
-
@graphql_result_data = []
|
144
|
-
end
|
145
|
-
|
146
|
-
def graphql_skip_at(index)
|
147
|
-
# Mark this index as dead. It's tricky because some indices may already be storing
|
148
|
-
# `Lazy`s. So the runtime is still holding indexes _before_ skipping,
|
149
|
-
# this object has to coordinate incoming writes to account for any already-skipped indices.
|
150
|
-
@skip_indices ||= []
|
151
|
-
@skip_indices << index
|
152
|
-
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < index}
|
153
|
-
delete_at_index = index - offset_by
|
154
|
-
@graphql_metadata && @graphql_metadata.delete_at(delete_at_index)
|
155
|
-
@graphql_result_data.delete_at(delete_at_index)
|
156
|
-
end
|
157
|
-
|
158
|
-
def set_leaf(idx, value)
|
159
|
-
if @skip_indices
|
160
|
-
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
|
161
|
-
idx -= offset_by
|
162
|
-
end
|
163
|
-
@graphql_result_data[idx] = value
|
164
|
-
@graphql_metadata && @graphql_metadata[idx] = value
|
165
|
-
value
|
166
|
-
end
|
167
|
-
|
168
|
-
def set_child_result(idx, value)
|
169
|
-
if @skip_indices
|
170
|
-
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
|
171
|
-
idx -= offset_by
|
172
|
-
end
|
173
|
-
@graphql_result_data[idx] = value.graphql_result_data
|
174
|
-
# If we encounter some part of this response that requires metadata tracking,
|
175
|
-
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
176
|
-
(@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
|
177
|
-
value
|
178
|
-
end
|
179
|
-
|
180
|
-
def values
|
181
|
-
(@graphql_metadata || @graphql_result_data)
|
182
|
-
end
|
23
|
+
:current_arguments, :current_field, :current_object, :was_authorized_by_scope_items
|
183
24
|
end
|
184
25
|
|
185
26
|
# @return [GraphQL::Query]
|
@@ -208,15 +49,16 @@ module GraphQL
|
|
208
49
|
@runtime_directive_names << name
|
209
50
|
end
|
210
51
|
end
|
211
|
-
# A cache of { Class => { String => Schema::Field } }
|
212
|
-
# Which assumes that MyObject.get_field("myField") will return the same field
|
213
|
-
# during the lifetime of a query
|
214
|
-
@fields_cache = Hash.new { |h, k| h[k] = {} }
|
215
|
-
# this can by by-identity since owners are the same object, but not the sub-hash, which uses strings.
|
216
|
-
@fields_cache.compare_by_identity
|
217
52
|
# { Class => Boolean }
|
218
53
|
@lazy_cache = {}
|
219
54
|
@lazy_cache.compare_by_identity
|
55
|
+
|
56
|
+
@gathered_selections_cache = Hash.new { |h, k|
|
57
|
+
cache = {}
|
58
|
+
cache.compare_by_identity
|
59
|
+
h[k] = cache
|
60
|
+
}
|
61
|
+
@gathered_selections_cache.compare_by_identity
|
220
62
|
end
|
221
63
|
|
222
64
|
def final_result
|
@@ -244,6 +86,7 @@ module GraphQL
|
|
244
86
|
root_operation = query.selected_operation
|
245
87
|
root_op_type = root_operation.operation_type || "query"
|
246
88
|
root_type = schema.root_type_for_operation(root_op_type)
|
89
|
+
|
247
90
|
st = get_current_runtime_state
|
248
91
|
st.current_object = query.root_value
|
249
92
|
st.current_result = @response
|
@@ -255,7 +98,7 @@ module GraphQL
|
|
255
98
|
@response = nil
|
256
99
|
else
|
257
100
|
call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
|
258
|
-
gathered_selections = gather_selections(runtime_object, root_type, root_operation.selections)
|
101
|
+
gathered_selections = gather_selections(runtime_object, root_type, nil, root_operation.selections)
|
259
102
|
# This is kind of a hack -- `gathered_selections` is an Array if any of the selections
|
260
103
|
# require isolation during execution (because of runtime directives). In that case,
|
261
104
|
# make a new, isolated result hash for writing the result into. (That isolated response
|
@@ -275,6 +118,7 @@ module GraphQL
|
|
275
118
|
@dataloader.append_job {
|
276
119
|
st = get_current_runtime_state
|
277
120
|
st.current_object = query.root_value
|
121
|
+
st.current_result_name = nil
|
278
122
|
st.current_result = selection_response
|
279
123
|
# This is a less-frequent case; use a fast check since it's often not there.
|
280
124
|
if (directives = selections[:graphql_directives])
|
@@ -289,20 +133,28 @@ module GraphQL
|
|
289
133
|
selection_response,
|
290
134
|
final_response,
|
291
135
|
nil,
|
136
|
+
st,
|
292
137
|
)
|
293
138
|
end
|
294
139
|
}
|
295
140
|
end
|
296
141
|
end
|
297
142
|
end
|
298
|
-
delete_all_interpreter_context
|
299
143
|
nil
|
300
144
|
end
|
301
145
|
|
302
|
-
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name =
|
146
|
+
def gather_selections(owner_object, owner_type, ast_node_for_caching, selections, selections_to_run = nil, selections_by_name = nil)
|
147
|
+
if ast_node_for_caching && (cached_selections = @gathered_selections_cache[ast_node_for_caching][owner_type])
|
148
|
+
return cached_selections
|
149
|
+
end
|
150
|
+
selections_by_name ||= {} # allocate this default here so we check the cache first
|
151
|
+
|
152
|
+
should_cache = true
|
153
|
+
|
303
154
|
selections.each do |node|
|
304
155
|
# Skip gathering this if the directive says so
|
305
156
|
if !directives_include?(node, owner_object, owner_type)
|
157
|
+
should_cache = false
|
306
158
|
next
|
307
159
|
end
|
308
160
|
|
@@ -327,6 +179,7 @@ module GraphQL
|
|
327
179
|
if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
|
328
180
|
next_selections = {}
|
329
181
|
next_selections[:graphql_directives] = node.directives
|
182
|
+
should_cache = false
|
330
183
|
if selections_to_run
|
331
184
|
selections_to_run << next_selections
|
332
185
|
else
|
@@ -344,41 +197,41 @@ module GraphQL
|
|
344
197
|
type_defn = schema.get_type(node.type.name, context)
|
345
198
|
|
346
199
|
if query.warden.possible_types(type_defn).include?(owner_type)
|
347
|
-
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
200
|
+
gather_selections(owner_object, owner_type, nil, node.selections, selections_to_run, next_selections)
|
348
201
|
end
|
349
202
|
else
|
350
203
|
# it's an untyped fragment, definitely continue
|
351
|
-
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
204
|
+
gather_selections(owner_object, owner_type, nil, node.selections, selections_to_run, next_selections)
|
352
205
|
end
|
353
206
|
when GraphQL::Language::Nodes::FragmentSpread
|
354
207
|
fragment_def = query.fragments[node.name]
|
355
208
|
type_defn = query.get_type(fragment_def.type.name)
|
356
209
|
if query.warden.possible_types(type_defn).include?(owner_type)
|
357
|
-
gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
210
|
+
gather_selections(owner_object, owner_type, nil, fragment_def.selections, selections_to_run, next_selections)
|
358
211
|
end
|
359
212
|
else
|
360
213
|
raise "Invariant: unexpected selection class: #{node.class}"
|
361
214
|
end
|
362
215
|
end
|
363
216
|
end
|
364
|
-
selections_to_run || selections_by_name
|
217
|
+
result = selections_to_run || selections_by_name
|
218
|
+
if should_cache
|
219
|
+
@gathered_selections_cache[ast_node_for_caching][owner_type] = result
|
220
|
+
end
|
221
|
+
result
|
365
222
|
end
|
366
223
|
|
367
224
|
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
368
225
|
|
369
226
|
# @return [void]
|
370
|
-
def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
|
371
|
-
st = get_current_runtime_state
|
372
|
-
st.current_object = owner_object
|
373
|
-
st.current_result_name = nil
|
374
|
-
st.current_result = selections_result
|
375
|
-
|
227
|
+
def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object, runtime_state) # rubocop:disable Metrics/ParameterLists
|
376
228
|
finished_jobs = 0
|
377
229
|
enqueued_jobs = gathered_selections.size
|
378
230
|
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
379
231
|
@dataloader.append_job {
|
232
|
+
runtime_state = get_current_runtime_state
|
380
233
|
evaluate_selection(
|
381
|
-
result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
|
234
|
+
result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object, runtime_state
|
382
235
|
)
|
383
236
|
finished_jobs += 1
|
384
237
|
if target_result && finished_jobs == enqueued_jobs
|
@@ -389,7 +242,9 @@ module GraphQL
|
|
389
242
|
# so it wouldn't get to the `Resolve` call that happens below.
|
390
243
|
# So instead trigger a run from this outer context.
|
391
244
|
if is_eager_selection
|
245
|
+
@dataloader.clear_cache
|
392
246
|
@dataloader.run
|
247
|
+
@dataloader.clear_cache
|
393
248
|
end
|
394
249
|
end
|
395
250
|
|
@@ -397,7 +252,7 @@ module GraphQL
|
|
397
252
|
end
|
398
253
|
|
399
254
|
# @return [void]
|
400
|
-
def evaluate_selection(result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
|
255
|
+
def evaluate_selection(result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_field, selections_result, parent_object, runtime_state) # rubocop:disable Metrics/ParameterLists
|
401
256
|
return if dead_result?(selections_result)
|
402
257
|
# As a performance optimization, the hash key will be a `Node` if
|
403
258
|
# there's only one selection of the field. But if there are multiple
|
@@ -410,57 +265,38 @@ module GraphQL
|
|
410
265
|
ast_node = field_ast_nodes_or_ast_node
|
411
266
|
end
|
412
267
|
field_name = ast_node.name
|
413
|
-
|
414
|
-
# because of how `is_introspection` is used to call `.authorized_new` later on.
|
415
|
-
field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name, @context)
|
416
|
-
is_introspection = false
|
417
|
-
if field_defn.nil?
|
418
|
-
field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
419
|
-
is_introspection = true
|
420
|
-
entry_point_field
|
421
|
-
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
422
|
-
is_introspection = true
|
423
|
-
dynamic_field
|
424
|
-
else
|
425
|
-
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
426
|
-
end
|
427
|
-
end
|
268
|
+
field_defn = query.warden.get_field(owner_type, field_name)
|
428
269
|
|
429
|
-
return_type = field_defn.type
|
430
|
-
|
431
|
-
# This seems janky, but we need to know
|
432
|
-
# the field's return type at this path in order
|
433
|
-
# to propagate `null`
|
434
|
-
return_type_non_null = return_type.non_null?
|
435
270
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
st.current_result_name = result_name
|
271
|
+
runtime_state.current_field = field_defn
|
272
|
+
runtime_state.current_result = selections_result
|
273
|
+
runtime_state.current_result_name = result_name
|
440
274
|
|
441
|
-
if
|
275
|
+
if field_defn.dynamic_introspection
|
442
276
|
owner_object = field_defn.owner.wrap(owner_object, context)
|
443
277
|
end
|
444
278
|
|
445
|
-
|
446
|
-
if
|
279
|
+
return_type = field_defn.type
|
280
|
+
if !field_defn.any_arguments?
|
447
281
|
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
448
282
|
if field_defn.extras.size == 0
|
449
283
|
evaluate_selection_with_resolved_keyword_args(
|
450
|
-
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type,
|
284
|
+
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type.non_null?, runtime_state
|
451
285
|
)
|
452
286
|
else
|
453
|
-
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type,
|
287
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, runtime_state)
|
454
288
|
end
|
455
289
|
else
|
456
290
|
@query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
|
457
|
-
|
291
|
+
runtime_state = get_current_runtime_state # This might be in a different fiber
|
292
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, runtime_state)
|
458
293
|
end
|
459
294
|
end
|
460
295
|
end
|
461
296
|
|
462
|
-
def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type,
|
463
|
-
after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
|
297
|
+
def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, runtime_state) # rubocop:disable Metrics/ParameterLists
|
298
|
+
after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_arguments, runtime_state|
|
299
|
+
return_type_non_null = return_type.non_null?
|
464
300
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
465
301
|
continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
466
302
|
next
|
@@ -511,17 +347,16 @@ module GraphQL
|
|
511
347
|
resolved_arguments.keyword_arguments
|
512
348
|
end
|
513
349
|
|
514
|
-
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null)
|
350
|
+
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null, runtime_state)
|
515
351
|
end
|
516
352
|
end
|
517
353
|
|
518
|
-
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
st.current_result = selection_result
|
354
|
+
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null, runtime_state) # rubocop:disable Metrics/ParameterLists
|
355
|
+
runtime_state.current_field = field_defn
|
356
|
+
runtime_state.current_object = object
|
357
|
+
runtime_state.current_arguments = resolved_arguments
|
358
|
+
runtime_state.current_result_name = result_name
|
359
|
+
runtime_state.current_result = selection_result
|
525
360
|
# Optimize for the case that field is selected only once
|
526
361
|
if field_ast_nodes.nil? || field_ast_nodes.size == 1
|
527
362
|
next_selections = ast_node.selections
|
@@ -550,10 +385,12 @@ module GraphQL
|
|
550
385
|
ex_err
|
551
386
|
end
|
552
387
|
end
|
553
|
-
after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
|
388
|
+
after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state|
|
554
389
|
continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
555
390
|
if HALT != continue_value
|
556
|
-
|
391
|
+
was_scoped = runtime_state.was_authorized_by_scope_items
|
392
|
+
runtime_state.was_authorized_by_scope_items = nil
|
393
|
+
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result, was_scoped, runtime_state)
|
557
394
|
end
|
558
395
|
end
|
559
396
|
end
|
@@ -733,7 +570,7 @@ module GraphQL
|
|
733
570
|
# Location information from `path` and `ast_node`.
|
734
571
|
#
|
735
572
|
# @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
|
736
|
-
def continue_field(value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
573
|
+
def continue_field(value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
|
737
574
|
if current_type.non_null?
|
738
575
|
current_type = current_type.of_type
|
739
576
|
is_non_null = true
|
@@ -750,7 +587,7 @@ module GraphQL
|
|
750
587
|
r
|
751
588
|
when "UNION", "INTERFACE"
|
752
589
|
resolved_type_or_lazy = resolve_type(current_type, value)
|
753
|
-
after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type_result|
|
590
|
+
after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_type_result, runtime_state|
|
754
591
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
755
592
|
resolved_type, resolved_value = resolved_type_result
|
756
593
|
else
|
@@ -767,21 +604,22 @@ module GraphQL
|
|
767
604
|
set_result(selection_result, result_name, nil, false, is_non_null)
|
768
605
|
nil
|
769
606
|
else
|
770
|
-
continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
|
607
|
+
continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state)
|
771
608
|
end
|
772
609
|
end
|
773
610
|
when "OBJECT"
|
774
611
|
object_proxy = begin
|
775
|
-
current_type.wrap(value, context)
|
612
|
+
was_scoped ? current_type.wrap_scoped(value, context) : current_type.wrap(value, context)
|
776
613
|
rescue GraphQL::ExecutionError => err
|
777
614
|
err
|
778
615
|
end
|
779
|
-
after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
|
616
|
+
after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state|
|
780
617
|
continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
|
781
618
|
if HALT != continue_value
|
782
619
|
response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
783
620
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
784
|
-
|
621
|
+
|
622
|
+
gathered_selections = gather_selections(continue_value, current_type, ast_node, next_selections)
|
785
623
|
# There are two possibilities for `gathered_selections`:
|
786
624
|
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
787
625
|
# This case is handled below, and the result can be written right into the main `response_hash` above.
|
@@ -800,11 +638,9 @@ module GraphQL
|
|
800
638
|
end
|
801
639
|
# reset this mutable state
|
802
640
|
# Unset `result_name` here because it's already included in the new response hash
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
st.current_result = this_result
|
807
|
-
|
641
|
+
runtime_state.current_object = continue_value
|
642
|
+
runtime_state.current_result_name = nil
|
643
|
+
runtime_state.current_result = this_result
|
808
644
|
# This is a less-frequent case; use a fast check since it's often not there.
|
809
645
|
if (directives = selections[:graphql_directives])
|
810
646
|
selections.delete(:graphql_directives)
|
@@ -818,6 +654,7 @@ module GraphQL
|
|
818
654
|
this_result,
|
819
655
|
final_result,
|
820
656
|
owner_object.object,
|
657
|
+
runtime_state,
|
821
658
|
)
|
822
659
|
this_result
|
823
660
|
end
|
@@ -839,10 +676,10 @@ module GraphQL
|
|
839
676
|
idx += 1
|
840
677
|
if use_dataloader_job
|
841
678
|
@dataloader.append_job do
|
842
|
-
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
679
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type, was_scoped, runtime_state)
|
843
680
|
end
|
844
681
|
else
|
845
|
-
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
682
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type, was_scoped, runtime_state)
|
846
683
|
end
|
847
684
|
end
|
848
685
|
|
@@ -873,16 +710,15 @@ module GraphQL
|
|
873
710
|
end
|
874
711
|
end
|
875
712
|
|
876
|
-
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
|
877
|
-
|
878
|
-
|
879
|
-
st.current_result = response_list
|
713
|
+
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
|
714
|
+
runtime_state.current_result_name = this_idx
|
715
|
+
runtime_state.current_result = response_list
|
880
716
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
881
717
|
# This will update `response_list` with the lazy
|
882
|
-
after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
|
718
|
+
after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list, runtime_state: runtime_state) do |inner_inner_value, runtime_state|
|
883
719
|
continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
|
884
720
|
if HALT != continue_value
|
885
|
-
continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
|
721
|
+
continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list, was_scoped, runtime_state)
|
886
722
|
end
|
887
723
|
end
|
888
724
|
end
|
@@ -959,16 +795,21 @@ module GraphQL
|
|
959
795
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
960
796
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
961
797
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
962
|
-
def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
|
798
|
+
def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
|
963
799
|
if lazy?(lazy_obj)
|
964
800
|
orig_result = result
|
801
|
+
was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
|
965
802
|
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
803
|
+
# This block might be called in a new fiber;
|
804
|
+
# In that case, this will initialize a new state
|
805
|
+
# to avoid conflicting with the parent fiber.
|
806
|
+
runtime_state = get_current_runtime_state
|
807
|
+
runtime_state.current_object = owner_object
|
808
|
+
runtime_state.current_field = field
|
809
|
+
runtime_state.current_arguments = arguments
|
810
|
+
runtime_state.current_result_name = result_name
|
811
|
+
runtime_state.current_result = orig_result
|
812
|
+
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
972
813
|
# Wrap the execution of _this_ method with tracing,
|
973
814
|
# but don't wrap the continuation below
|
974
815
|
inner_obj = begin
|
@@ -988,7 +829,7 @@ module GraphQL
|
|
988
829
|
ex_err
|
989
830
|
end
|
990
831
|
end
|
991
|
-
yield(inner_obj)
|
832
|
+
yield(inner_obj, runtime_state)
|
992
833
|
end
|
993
834
|
|
994
835
|
if eager
|
@@ -1005,7 +846,7 @@ module GraphQL
|
|
1005
846
|
end
|
1006
847
|
else
|
1007
848
|
# Don't need to reset state here because it _wasn't_ lazy.
|
1008
|
-
yield(lazy_obj)
|
849
|
+
yield(lazy_obj, runtime_state)
|
1009
850
|
end
|
1010
851
|
end
|
1011
852
|
|
@@ -98,12 +98,6 @@ module GraphQL
|
|
98
98
|
multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
|
99
99
|
Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
|
100
100
|
end
|
101
|
-
queries.each do |query|
|
102
|
-
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
103
|
-
if runtime
|
104
|
-
runtime.delete_all_interpreter_context
|
105
|
-
end
|
106
|
-
end
|
107
101
|
}
|
108
102
|
multiplex.dataloader.run
|
109
103
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
module GraphQL
|
3
3
|
module Introspection
|
4
4
|
class DynamicFields < Introspection::BaseObject
|
5
|
-
field :__typename, String, "The name of this type", null: false
|
5
|
+
field :__typename, String, "The name of this type", null: false, dynamic_introspection: true
|
6
6
|
|
7
7
|
def __typename
|
8
8
|
object.class.graphql_name
|
@@ -2,8 +2,8 @@
|
|
2
2
|
module GraphQL
|
3
3
|
module Introspection
|
4
4
|
class EntryPoints < Introspection::BaseObject
|
5
|
-
field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false
|
6
|
-
field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system" do
|
5
|
+
field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false, dynamic_introspection: true
|
6
|
+
field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true do
|
7
7
|
argument :name, String
|
8
8
|
end
|
9
9
|
|