graphql 2.0.27 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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/install_generator.rb +3 -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_resolver.erb +6 -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/field_usage.rb +32 -7
- data/lib/graphql/analysis/ast/query_complexity.rb +80 -128
- 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 +21 -11
- data/lib/graphql/backtrace/trace.rb +12 -15
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +85 -0
- data/lib/graphql/dataloader/source.rb +11 -3
- data/lib/graphql/dataloader.rb +109 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +70 -248
- data/lib/graphql/execution/interpreter.rb +91 -157
- data/lib/graphql/execution/lookahead.rb +88 -21
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +37 -37
- data/lib/graphql/language/lexer.rb +271 -177
- data/lib/graphql/language/nodes.rb +74 -56
- data/lib/graphql/language/parser.rb +697 -1986
- data/lib/graphql/language/printer.rb +299 -146
- 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/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +3 -3
- data/lib/graphql/pagination/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/pagination/relation_connection.rb +3 -3
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +36 -98
- data/lib/graphql/query/null_context.rb +4 -11
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +13 -22
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/schema/argument.rb +6 -1
- data/lib/graphql/schema/build_from_definition.rb +0 -11
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +1 -1
- data/lib/graphql/schema/enum.rb +3 -3
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +8 -5
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/input_object.rb +2 -2
- data/lib/graphql/schema/interface.rb +10 -10
- data/lib/graphql/schema/introspection_system.rb +2 -0
- data/lib/graphql/schema/loader.rb +0 -2
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +61 -38
- data/lib/graphql/schema/member/has_fields.rb +8 -5
- data/lib/graphql/schema/member/has_interfaces.rb +23 -9
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- 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 +7 -3
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/warden.rb +96 -94
- data/lib/graphql/schema.rb +219 -72
- 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/validator.rb +3 -0
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
- data/lib/graphql/subscriptions/event.rb +8 -2
- data/lib/graphql/subscriptions.rb +14 -12
- data/lib/graphql/testing/helpers.rb +125 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/data_dog_trace.rb +21 -34
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +2 -0
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/sentry_trace.rb +94 -0
- data/lib/graphql/tracing/trace.rb +1 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -3
- data/readme.md +12 -2
- metadata +33 -25
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
|
@@ -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,12 +49,6 @@ 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
|
|
@@ -244,6 +79,7 @@ module GraphQL
|
|
|
244
79
|
root_operation = query.selected_operation
|
|
245
80
|
root_op_type = root_operation.operation_type || "query"
|
|
246
81
|
root_type = schema.root_type_for_operation(root_op_type)
|
|
82
|
+
|
|
247
83
|
st = get_current_runtime_state
|
|
248
84
|
st.current_object = query.root_value
|
|
249
85
|
st.current_result = @response
|
|
@@ -275,6 +111,7 @@ module GraphQL
|
|
|
275
111
|
@dataloader.append_job {
|
|
276
112
|
st = get_current_runtime_state
|
|
277
113
|
st.current_object = query.root_value
|
|
114
|
+
st.current_result_name = nil
|
|
278
115
|
st.current_result = selection_response
|
|
279
116
|
# This is a less-frequent case; use a fast check since it's often not there.
|
|
280
117
|
if (directives = selections[:graphql_directives])
|
|
@@ -289,17 +126,18 @@ module GraphQL
|
|
|
289
126
|
selection_response,
|
|
290
127
|
final_response,
|
|
291
128
|
nil,
|
|
129
|
+
st,
|
|
292
130
|
)
|
|
293
131
|
end
|
|
294
132
|
}
|
|
295
133
|
end
|
|
296
134
|
end
|
|
297
135
|
end
|
|
298
|
-
delete_all_interpreter_context
|
|
299
136
|
nil
|
|
300
137
|
end
|
|
301
138
|
|
|
302
139
|
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
|
|
140
|
+
|
|
303
141
|
selections.each do |node|
|
|
304
142
|
# Skip gathering this if the directive says so
|
|
305
143
|
if !directives_include?(node, owner_object, owner_type)
|
|
@@ -367,18 +205,14 @@ module GraphQL
|
|
|
367
205
|
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
|
368
206
|
|
|
369
207
|
# @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
|
-
|
|
208
|
+
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
209
|
finished_jobs = 0
|
|
377
210
|
enqueued_jobs = gathered_selections.size
|
|
378
211
|
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
|
379
212
|
@dataloader.append_job {
|
|
213
|
+
runtime_state = get_current_runtime_state
|
|
380
214
|
evaluate_selection(
|
|
381
|
-
result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
|
|
215
|
+
result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object, runtime_state
|
|
382
216
|
)
|
|
383
217
|
finished_jobs += 1
|
|
384
218
|
if target_result && finished_jobs == enqueued_jobs
|
|
@@ -389,7 +223,9 @@ module GraphQL
|
|
|
389
223
|
# so it wouldn't get to the `Resolve` call that happens below.
|
|
390
224
|
# So instead trigger a run from this outer context.
|
|
391
225
|
if is_eager_selection
|
|
226
|
+
@dataloader.clear_cache
|
|
392
227
|
@dataloader.run
|
|
228
|
+
@dataloader.clear_cache
|
|
393
229
|
end
|
|
394
230
|
end
|
|
395
231
|
|
|
@@ -397,7 +233,7 @@ module GraphQL
|
|
|
397
233
|
end
|
|
398
234
|
|
|
399
235
|
# @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
|
|
236
|
+
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
237
|
return if dead_result?(selections_result)
|
|
402
238
|
# As a performance optimization, the hash key will be a `Node` if
|
|
403
239
|
# there's only one selection of the field. But if there are multiple
|
|
@@ -410,57 +246,38 @@ module GraphQL
|
|
|
410
246
|
ast_node = field_ast_nodes_or_ast_node
|
|
411
247
|
end
|
|
412
248
|
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
|
|
249
|
+
field_defn = query.warden.get_field(owner_type, field_name)
|
|
428
250
|
|
|
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
251
|
# 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
|
|
252
|
+
runtime_state.current_field = field_defn
|
|
253
|
+
runtime_state.current_result = selections_result
|
|
254
|
+
runtime_state.current_result_name = result_name
|
|
440
255
|
|
|
441
|
-
if
|
|
256
|
+
if field_defn.dynamic_introspection
|
|
442
257
|
owner_object = field_defn.owner.wrap(owner_object, context)
|
|
443
258
|
end
|
|
444
259
|
|
|
445
|
-
|
|
446
|
-
if
|
|
260
|
+
return_type = field_defn.type
|
|
261
|
+
if !field_defn.any_arguments?
|
|
447
262
|
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
|
448
263
|
if field_defn.extras.size == 0
|
|
449
264
|
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,
|
|
265
|
+
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
266
|
)
|
|
452
267
|
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,
|
|
268
|
+
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
269
|
end
|
|
455
270
|
else
|
|
456
271
|
@query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
|
|
457
|
-
|
|
272
|
+
runtime_state = get_current_runtime_state # This might be in a different fiber
|
|
273
|
+
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
274
|
end
|
|
459
275
|
end
|
|
460
276
|
end
|
|
461
277
|
|
|
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|
|
|
278
|
+
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
|
|
279
|
+
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|
|
|
280
|
+
return_type_non_null = return_type.non_null?
|
|
464
281
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
|
465
282
|
continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
|
466
283
|
next
|
|
@@ -511,17 +328,16 @@ module GraphQL
|
|
|
511
328
|
resolved_arguments.keyword_arguments
|
|
512
329
|
end
|
|
513
330
|
|
|
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)
|
|
331
|
+
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
332
|
end
|
|
516
333
|
end
|
|
517
334
|
|
|
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
|
|
335
|
+
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
|
|
336
|
+
runtime_state.current_field = field_defn
|
|
337
|
+
runtime_state.current_object = object
|
|
338
|
+
runtime_state.current_arguments = resolved_arguments
|
|
339
|
+
runtime_state.current_result_name = result_name
|
|
340
|
+
runtime_state.current_result = selection_result
|
|
525
341
|
# Optimize for the case that field is selected only once
|
|
526
342
|
if field_ast_nodes.nil? || field_ast_nodes.size == 1
|
|
527
343
|
next_selections = ast_node.selections
|
|
@@ -550,10 +366,12 @@ module GraphQL
|
|
|
550
366
|
ex_err
|
|
551
367
|
end
|
|
552
368
|
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|
|
|
369
|
+
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
370
|
continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
|
555
371
|
if HALT != continue_value
|
|
556
|
-
|
|
372
|
+
was_scoped = runtime_state.was_authorized_by_scope_items
|
|
373
|
+
runtime_state.was_authorized_by_scope_items = nil
|
|
374
|
+
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
375
|
end
|
|
558
376
|
end
|
|
559
377
|
end
|
|
@@ -695,7 +513,7 @@ module GraphQL
|
|
|
695
513
|
end
|
|
696
514
|
when Array
|
|
697
515
|
# It's an array full of execution errors; add them all.
|
|
698
|
-
if value.any? && value.all?
|
|
516
|
+
if value.any? && value.all?(GraphQL::ExecutionError)
|
|
699
517
|
list_type_at_all = (field && (field.type.list?))
|
|
700
518
|
if selection_result.nil? || !dead_result?(selection_result)
|
|
701
519
|
value.each_with_index do |error, index|
|
|
@@ -733,7 +551,7 @@ module GraphQL
|
|
|
733
551
|
# Location information from `path` and `ast_node`.
|
|
734
552
|
#
|
|
735
553
|
# @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
|
|
554
|
+
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
555
|
if current_type.non_null?
|
|
738
556
|
current_type = current_type.of_type
|
|
739
557
|
is_non_null = true
|
|
@@ -750,7 +568,7 @@ module GraphQL
|
|
|
750
568
|
r
|
|
751
569
|
when "UNION", "INTERFACE"
|
|
752
570
|
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|
|
|
571
|
+
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
572
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
|
755
573
|
resolved_type, resolved_value = resolved_type_result
|
|
756
574
|
else
|
|
@@ -767,20 +585,21 @@ module GraphQL
|
|
|
767
585
|
set_result(selection_result, result_name, nil, false, is_non_null)
|
|
768
586
|
nil
|
|
769
587
|
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)
|
|
588
|
+
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
589
|
end
|
|
772
590
|
end
|
|
773
591
|
when "OBJECT"
|
|
774
592
|
object_proxy = begin
|
|
775
|
-
current_type.wrap(value, context)
|
|
593
|
+
was_scoped ? current_type.wrap_scoped(value, context) : current_type.wrap(value, context)
|
|
776
594
|
rescue GraphQL::ExecutionError => err
|
|
777
595
|
err
|
|
778
596
|
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|
|
|
597
|
+
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
598
|
continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
|
|
781
599
|
if HALT != continue_value
|
|
782
600
|
response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
|
783
601
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
|
602
|
+
|
|
784
603
|
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
|
785
604
|
# There are two possibilities for `gathered_selections`:
|
|
786
605
|
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
|
@@ -800,11 +619,9 @@ module GraphQL
|
|
|
800
619
|
end
|
|
801
620
|
# reset this mutable state
|
|
802
621
|
# Unset `result_name` here because it's already included in the new response hash
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
st.current_result = this_result
|
|
807
|
-
|
|
622
|
+
runtime_state.current_object = continue_value
|
|
623
|
+
runtime_state.current_result_name = nil
|
|
624
|
+
runtime_state.current_result = this_result
|
|
808
625
|
# This is a less-frequent case; use a fast check since it's often not there.
|
|
809
626
|
if (directives = selections[:graphql_directives])
|
|
810
627
|
selections.delete(:graphql_directives)
|
|
@@ -818,6 +635,7 @@ module GraphQL
|
|
|
818
635
|
this_result,
|
|
819
636
|
final_result,
|
|
820
637
|
owner_object.object,
|
|
638
|
+
runtime_state,
|
|
821
639
|
)
|
|
822
640
|
this_result
|
|
823
641
|
end
|
|
@@ -839,10 +657,10 @@ module GraphQL
|
|
|
839
657
|
idx += 1
|
|
840
658
|
if use_dataloader_job
|
|
841
659
|
@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)
|
|
660
|
+
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
661
|
end
|
|
844
662
|
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)
|
|
663
|
+
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
664
|
end
|
|
847
665
|
end
|
|
848
666
|
|
|
@@ -873,16 +691,15 @@ module GraphQL
|
|
|
873
691
|
end
|
|
874
692
|
end
|
|
875
693
|
|
|
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
|
|
694
|
+
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
|
|
695
|
+
runtime_state.current_result_name = this_idx
|
|
696
|
+
runtime_state.current_result = response_list
|
|
880
697
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
|
881
698
|
# 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|
|
|
699
|
+
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
700
|
continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
|
|
884
701
|
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)
|
|
702
|
+
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
703
|
end
|
|
887
704
|
end
|
|
888
705
|
end
|
|
@@ -959,16 +776,21 @@ module GraphQL
|
|
|
959
776
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
|
960
777
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
|
961
778
|
# @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)
|
|
779
|
+
def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
|
|
963
780
|
if lazy?(lazy_obj)
|
|
964
781
|
orig_result = result
|
|
782
|
+
was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
|
|
965
783
|
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
784
|
+
# This block might be called in a new fiber;
|
|
785
|
+
# In that case, this will initialize a new state
|
|
786
|
+
# to avoid conflicting with the parent fiber.
|
|
787
|
+
runtime_state = get_current_runtime_state
|
|
788
|
+
runtime_state.current_object = owner_object
|
|
789
|
+
runtime_state.current_field = field
|
|
790
|
+
runtime_state.current_arguments = arguments
|
|
791
|
+
runtime_state.current_result_name = result_name
|
|
792
|
+
runtime_state.current_result = orig_result
|
|
793
|
+
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
|
972
794
|
# Wrap the execution of _this_ method with tracing,
|
|
973
795
|
# but don't wrap the continuation below
|
|
974
796
|
inner_obj = begin
|
|
@@ -988,7 +810,7 @@ module GraphQL
|
|
|
988
810
|
ex_err
|
|
989
811
|
end
|
|
990
812
|
end
|
|
991
|
-
yield(inner_obj)
|
|
813
|
+
yield(inner_obj, runtime_state)
|
|
992
814
|
end
|
|
993
815
|
|
|
994
816
|
if eager
|
|
@@ -1005,7 +827,7 @@ module GraphQL
|
|
|
1005
827
|
end
|
|
1006
828
|
else
|
|
1007
829
|
# Don't need to reset state here because it _wasn't_ lazy.
|
|
1008
|
-
yield(lazy_obj)
|
|
830
|
+
yield(lazy_obj, runtime_state)
|
|
1009
831
|
end
|
|
1010
832
|
end
|
|
1011
833
|
|