graphql 2.0.28 → 2.2.11
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/request.rb +5 -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 +79 -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 +75 -57
- data/lib/graphql/language/parser.rb +707 -1986
- data/lib/graphql/language/printer.rb +303 -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/base_64_encoder.rb +3 -5
- 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 +39 -35
- 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 +15 -11
- 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 +16 -8
- 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/unique_within_type.rb +1 -1
- data/lib/graphql/schema/warden.rb +96 -94
- data/lib/graphql/schema.rb +252 -78
- 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 +2 -3
- 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 +2 -2
- 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/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +14 -12
- data/lib/graphql/testing/helpers.rb +129 -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/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 +112 -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 +6 -5
- data/readme.md +12 -2
- metadata +46 -38
- 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/schema/base_64_bp.rb +0 -26
- 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
|
|
428
|
-
|
|
429
|
-
return_type = field_defn.type
|
|
249
|
+
field_defn = query.warden.get_field(owner_type, field_name)
|
|
430
250
|
|
|
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
|
|
@@ -536,6 +352,15 @@ module GraphQL
|
|
|
536
352
|
end
|
|
537
353
|
|
|
538
354
|
field_result = call_method_on_directives(:resolve, object, directives) do
|
|
355
|
+
if directives.any?
|
|
356
|
+
# This might be executed in a different context; reset this info
|
|
357
|
+
runtime_state = get_current_runtime_state
|
|
358
|
+
runtime_state.current_field = field_defn
|
|
359
|
+
runtime_state.current_object = object
|
|
360
|
+
runtime_state.current_arguments = resolved_arguments
|
|
361
|
+
runtime_state.current_result_name = result_name
|
|
362
|
+
runtime_state.current_result = selection_result
|
|
363
|
+
end
|
|
539
364
|
# Actually call the field resolver and capture the result
|
|
540
365
|
app_result = begin
|
|
541
366
|
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
|
@@ -550,10 +375,12 @@ module GraphQL
|
|
|
550
375
|
ex_err
|
|
551
376
|
end
|
|
552
377
|
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|
|
|
378
|
+
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
379
|
continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
|
555
380
|
if HALT != continue_value
|
|
556
|
-
|
|
381
|
+
was_scoped = runtime_state.was_authorized_by_scope_items
|
|
382
|
+
runtime_state.was_authorized_by_scope_items = nil
|
|
383
|
+
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
384
|
end
|
|
558
385
|
end
|
|
559
386
|
end
|
|
@@ -695,7 +522,7 @@ module GraphQL
|
|
|
695
522
|
end
|
|
696
523
|
when Array
|
|
697
524
|
# It's an array full of execution errors; add them all.
|
|
698
|
-
if value.any? && value.all?
|
|
525
|
+
if value.any? && value.all?(GraphQL::ExecutionError)
|
|
699
526
|
list_type_at_all = (field && (field.type.list?))
|
|
700
527
|
if selection_result.nil? || !dead_result?(selection_result)
|
|
701
528
|
value.each_with_index do |error, index|
|
|
@@ -733,7 +560,7 @@ module GraphQL
|
|
|
733
560
|
# Location information from `path` and `ast_node`.
|
|
734
561
|
#
|
|
735
562
|
# @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
|
|
563
|
+
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
564
|
if current_type.non_null?
|
|
738
565
|
current_type = current_type.of_type
|
|
739
566
|
is_non_null = true
|
|
@@ -750,7 +577,7 @@ module GraphQL
|
|
|
750
577
|
r
|
|
751
578
|
when "UNION", "INTERFACE"
|
|
752
579
|
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|
|
|
580
|
+
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
581
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
|
755
582
|
resolved_type, resolved_value = resolved_type_result
|
|
756
583
|
else
|
|
@@ -767,20 +594,21 @@ module GraphQL
|
|
|
767
594
|
set_result(selection_result, result_name, nil, false, is_non_null)
|
|
768
595
|
nil
|
|
769
596
|
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)
|
|
597
|
+
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
598
|
end
|
|
772
599
|
end
|
|
773
600
|
when "OBJECT"
|
|
774
601
|
object_proxy = begin
|
|
775
|
-
current_type.wrap(value, context)
|
|
602
|
+
was_scoped ? current_type.wrap_scoped(value, context) : current_type.wrap(value, context)
|
|
776
603
|
rescue GraphQL::ExecutionError => err
|
|
777
604
|
err
|
|
778
605
|
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|
|
|
606
|
+
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
607
|
continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
|
|
781
608
|
if HALT != continue_value
|
|
782
609
|
response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
|
783
610
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
|
611
|
+
|
|
784
612
|
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
|
785
613
|
# There are two possibilities for `gathered_selections`:
|
|
786
614
|
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
|
@@ -800,11 +628,9 @@ module GraphQL
|
|
|
800
628
|
end
|
|
801
629
|
# reset this mutable state
|
|
802
630
|
# Unset `result_name` here because it's already included in the new response hash
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
st.current_result = this_result
|
|
807
|
-
|
|
631
|
+
runtime_state.current_object = continue_value
|
|
632
|
+
runtime_state.current_result_name = nil
|
|
633
|
+
runtime_state.current_result = this_result
|
|
808
634
|
# This is a less-frequent case; use a fast check since it's often not there.
|
|
809
635
|
if (directives = selections[:graphql_directives])
|
|
810
636
|
selections.delete(:graphql_directives)
|
|
@@ -818,6 +644,7 @@ module GraphQL
|
|
|
818
644
|
this_result,
|
|
819
645
|
final_result,
|
|
820
646
|
owner_object.object,
|
|
647
|
+
runtime_state,
|
|
821
648
|
)
|
|
822
649
|
this_result
|
|
823
650
|
end
|
|
@@ -839,10 +666,10 @@ module GraphQL
|
|
|
839
666
|
idx += 1
|
|
840
667
|
if use_dataloader_job
|
|
841
668
|
@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)
|
|
669
|
+
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
670
|
end
|
|
844
671
|
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)
|
|
672
|
+
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
673
|
end
|
|
847
674
|
end
|
|
848
675
|
|
|
@@ -873,16 +700,15 @@ module GraphQL
|
|
|
873
700
|
end
|
|
874
701
|
end
|
|
875
702
|
|
|
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
|
|
703
|
+
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
|
|
704
|
+
runtime_state.current_result_name = this_idx
|
|
705
|
+
runtime_state.current_result = response_list
|
|
880
706
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
|
881
707
|
# 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|
|
|
708
|
+
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
709
|
continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
|
|
884
710
|
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)
|
|
711
|
+
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
712
|
end
|
|
887
713
|
end
|
|
888
714
|
end
|
|
@@ -959,16 +785,21 @@ module GraphQL
|
|
|
959
785
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
|
960
786
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
|
961
787
|
# @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)
|
|
788
|
+
def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
|
|
963
789
|
if lazy?(lazy_obj)
|
|
964
790
|
orig_result = result
|
|
791
|
+
was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
|
|
965
792
|
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
793
|
+
# This block might be called in a new fiber;
|
|
794
|
+
# In that case, this will initialize a new state
|
|
795
|
+
# to avoid conflicting with the parent fiber.
|
|
796
|
+
runtime_state = get_current_runtime_state
|
|
797
|
+
runtime_state.current_object = owner_object
|
|
798
|
+
runtime_state.current_field = field
|
|
799
|
+
runtime_state.current_arguments = arguments
|
|
800
|
+
runtime_state.current_result_name = result_name
|
|
801
|
+
runtime_state.current_result = orig_result
|
|
802
|
+
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
|
972
803
|
# Wrap the execution of _this_ method with tracing,
|
|
973
804
|
# but don't wrap the continuation below
|
|
974
805
|
inner_obj = begin
|
|
@@ -988,7 +819,7 @@ module GraphQL
|
|
|
988
819
|
ex_err
|
|
989
820
|
end
|
|
990
821
|
end
|
|
991
|
-
yield(inner_obj)
|
|
822
|
+
yield(inner_obj, runtime_state)
|
|
992
823
|
end
|
|
993
824
|
|
|
994
825
|
if eager
|
|
@@ -1005,7 +836,7 @@ module GraphQL
|
|
|
1005
836
|
end
|
|
1006
837
|
else
|
|
1007
838
|
# Don't need to reset state here because it _wasn't_ lazy.
|
|
1008
|
-
yield(lazy_obj)
|
|
839
|
+
yield(lazy_obj, runtime_state)
|
|
1009
840
|
end
|
|
1010
841
|
end
|
|
1011
842
|
|