graphql 2.0.30 → 2.3.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/mutation_root_generator.rb +2 -2
- 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 +5 -0
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +282 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/backtrace/trace.rb +12 -15
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +88 -0
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +11 -3
- data/lib/graphql/dataloader.rb +112 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
- data/lib/graphql/execution/interpreter/runtime.rb +163 -365
- data/lib/graphql/execution/interpreter.rb +92 -158
- 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 +38 -38
- data/lib/graphql/language/lexer.rb +305 -193
- data/lib/graphql/language/nodes.rb +113 -66
- data/lib/graphql/language/parser.rb +787 -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 +61 -0
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +66 -131
- data/lib/graphql/query/null_context.rb +4 -11
- data/lib/graphql/query/validation_pipeline.rb +4 -4
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +17 -26
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/schema/addition.rb +21 -11
- data/lib/graphql/schema/argument.rb +43 -8
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +9 -12
- 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 +3 -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 +49 -35
- data/lib/graphql/schema/has_single_input_argument.rb +157 -0
- data/lib/graphql/schema/input_object.rb +4 -4
- data/lib/graphql/schema/interface.rb +10 -10
- data/lib/graphql/schema/introspection_system.rb +4 -2
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/list.rb +2 -2
- data/lib/graphql/schema/loader.rb +2 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +63 -73
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- 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/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/mutation.rb +7 -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 +27 -13
- 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 -95
- data/lib/graphql/schema.rb +323 -102
- 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 +2 -2
- 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 +4 -3
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
- data/lib/graphql/subscriptions/event.rb +8 -2
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +15 -13
- data/lib/graphql/testing/helpers.rb +151 -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 +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/prometheus_trace.rb +9 -9
- 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/type_kinds.rb +1 -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 +13 -13
- data/readme.md +12 -2
- metadata +33 -26
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -276
- data/lib/graphql/analysis/ast.rb +0 -81
- 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
|
|
@@ -10,176 +11,19 @@ module GraphQL
|
|
|
10
11
|
class Runtime
|
|
11
12
|
class CurrentState
|
|
12
13
|
def initialize
|
|
13
|
-
@current_object = nil
|
|
14
14
|
@current_field = nil
|
|
15
15
|
@current_arguments = nil
|
|
16
16
|
@current_result_name = nil
|
|
17
17
|
@current_result = nil
|
|
18
|
+
@was_authorized_by_scope_items = nil
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
into_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
|
|
21
|
+
def current_object
|
|
22
|
+
@current_result.graphql_application_value
|
|
135
23
|
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
class GraphQLResultArray
|
|
139
|
-
include GraphQLResult
|
|
140
24
|
|
|
141
|
-
|
|
142
|
-
|
|
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
|
|
25
|
+
attr_accessor :current_result, :current_result_name,
|
|
26
|
+
:current_arguments, :current_field, :was_authorized_by_scope_items
|
|
183
27
|
end
|
|
184
28
|
|
|
185
29
|
# @return [GraphQL::Query]
|
|
@@ -198,7 +42,7 @@ module GraphQL
|
|
|
198
42
|
@lazies_at_depth = lazies_at_depth
|
|
199
43
|
@schema = query.schema
|
|
200
44
|
@context = query.context
|
|
201
|
-
@response =
|
|
45
|
+
@response = nil
|
|
202
46
|
# Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
|
|
203
47
|
@runtime_directive_names = []
|
|
204
48
|
noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
|
|
@@ -208,12 +52,6 @@ module GraphQL
|
|
|
208
52
|
@runtime_directive_names << name
|
|
209
53
|
end
|
|
210
54
|
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
55
|
# { Class => Boolean }
|
|
218
56
|
@lazy_cache = {}
|
|
219
57
|
@lazy_cache.compare_by_identity
|
|
@@ -227,16 +65,6 @@ module GraphQL
|
|
|
227
65
|
"#<#{self.class.name} response=#{@response.inspect}>"
|
|
228
66
|
end
|
|
229
67
|
|
|
230
|
-
def tap_or_each(obj_or_array)
|
|
231
|
-
if obj_or_array.is_a?(Array)
|
|
232
|
-
obj_or_array.each do |item|
|
|
233
|
-
yield(item, true)
|
|
234
|
-
end
|
|
235
|
-
else
|
|
236
|
-
yield(obj_or_array, false)
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
|
|
240
68
|
# This _begins_ the execution. Some deferred work
|
|
241
69
|
# might be stored up in lazies.
|
|
242
70
|
# @return [void]
|
|
@@ -244,28 +72,21 @@ module GraphQL
|
|
|
244
72
|
root_operation = query.selected_operation
|
|
245
73
|
root_op_type = root_operation.operation_type || "query"
|
|
246
74
|
root_type = schema.root_type_for_operation(root_op_type)
|
|
247
|
-
st = get_current_runtime_state
|
|
248
|
-
st.current_object = query.root_value
|
|
249
|
-
st.current_result = @response
|
|
250
75
|
runtime_object = root_type.wrap(query.root_value, context)
|
|
251
76
|
runtime_object = schema.sync_lazy(runtime_object)
|
|
77
|
+
is_eager = root_op_type == "mutation"
|
|
78
|
+
@response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
|
|
79
|
+
st = get_current_runtime_state
|
|
80
|
+
st.current_result = @response
|
|
252
81
|
|
|
253
82
|
if runtime_object.nil?
|
|
254
83
|
# Root .authorized? returned false.
|
|
255
84
|
@response = nil
|
|
256
85
|
else
|
|
257
86
|
call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
|
|
258
|
-
|
|
259
|
-
# This is kind of a hack -- `gathered_selections` is an Array if any of the selections
|
|
260
|
-
# require isolation during execution (because of runtime directives). In that case,
|
|
261
|
-
# make a new, isolated result hash for writing the result into. (That isolated response
|
|
262
|
-
# is eventually merged back into the main response)
|
|
263
|
-
#
|
|
264
|
-
# Otherwise, `gathered_selections` is a hash of selections which can be
|
|
265
|
-
# directly evaluated and the results can be written right into the main response hash.
|
|
266
|
-
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
|
87
|
+
each_gathered_selections(@response) do |selections, is_selection_array|
|
|
267
88
|
if is_selection_array
|
|
268
|
-
selection_response = GraphQLResultHash.new(nil, nil, false)
|
|
89
|
+
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
|
|
269
90
|
final_response = @response
|
|
270
91
|
else
|
|
271
92
|
selection_response = @response
|
|
@@ -273,32 +94,30 @@ module GraphQL
|
|
|
273
94
|
end
|
|
274
95
|
|
|
275
96
|
@dataloader.append_job {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
end
|
|
283
|
-
call_method_on_directives(:resolve, runtime_object, directives) do
|
|
284
|
-
evaluate_selections(
|
|
285
|
-
runtime_object,
|
|
286
|
-
root_type,
|
|
287
|
-
root_op_type == "mutation",
|
|
288
|
-
selections,
|
|
289
|
-
selection_response,
|
|
290
|
-
final_response,
|
|
291
|
-
nil,
|
|
292
|
-
)
|
|
293
|
-
end
|
|
97
|
+
evaluate_selections(
|
|
98
|
+
selections,
|
|
99
|
+
selection_response,
|
|
100
|
+
final_response,
|
|
101
|
+
nil,
|
|
102
|
+
)
|
|
294
103
|
}
|
|
295
104
|
end
|
|
296
105
|
end
|
|
297
106
|
end
|
|
298
|
-
delete_all_interpreter_context
|
|
299
107
|
nil
|
|
300
108
|
end
|
|
301
109
|
|
|
110
|
+
def each_gathered_selections(response_hash)
|
|
111
|
+
gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections)
|
|
112
|
+
if gathered_selections.is_a?(Array)
|
|
113
|
+
gathered_selections.each do |item|
|
|
114
|
+
yield(item, true)
|
|
115
|
+
end
|
|
116
|
+
else
|
|
117
|
+
yield(gathered_selections, false)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
302
121
|
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
|
|
303
122
|
selections.each do |node|
|
|
304
123
|
# Skip gathering this if the directive says so
|
|
@@ -311,7 +130,7 @@ module GraphQL
|
|
|
311
130
|
selections = selections_by_name[response_key]
|
|
312
131
|
# if there was already a selection of this field,
|
|
313
132
|
# use an array to hold all selections,
|
|
314
|
-
#
|
|
133
|
+
# otherwise, use the single node to represent the selection
|
|
315
134
|
if selections
|
|
316
135
|
# This field was already selected at least once,
|
|
317
136
|
# add this node to the list of selections
|
|
@@ -344,17 +163,26 @@ module GraphQL
|
|
|
344
163
|
type_defn = schema.get_type(node.type.name, context)
|
|
345
164
|
|
|
346
165
|
if query.warden.possible_types(type_defn).include?(owner_type)
|
|
347
|
-
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
|
166
|
+
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
|
167
|
+
if !result.equal?(next_selections)
|
|
168
|
+
selections_to_run = result
|
|
169
|
+
end
|
|
348
170
|
end
|
|
349
171
|
else
|
|
350
172
|
# it's an untyped fragment, definitely continue
|
|
351
|
-
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
|
173
|
+
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
|
174
|
+
if !result.equal?(next_selections)
|
|
175
|
+
selections_to_run = result
|
|
176
|
+
end
|
|
352
177
|
end
|
|
353
178
|
when GraphQL::Language::Nodes::FragmentSpread
|
|
354
179
|
fragment_def = query.fragments[node.name]
|
|
355
180
|
type_defn = query.get_type(fragment_def.type.name)
|
|
356
181
|
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)
|
|
182
|
+
result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
|
183
|
+
if !result.equal?(next_selections)
|
|
184
|
+
selections_to_run = result
|
|
185
|
+
end
|
|
358
186
|
end
|
|
359
187
|
else
|
|
360
188
|
raise "Invariant: unexpected selection class: #{node.class}"
|
|
@@ -367,38 +195,44 @@ module GraphQL
|
|
|
367
195
|
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
|
368
196
|
|
|
369
197
|
# @return [void]
|
|
370
|
-
def evaluate_selections(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
198
|
+
def evaluate_selections(gathered_selections, selections_result, target_result, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
199
|
+
runtime_state ||= get_current_runtime_state
|
|
200
|
+
runtime_state.current_result_name = nil
|
|
201
|
+
runtime_state.current_result = selections_result
|
|
202
|
+
# This is a less-frequent case; use a fast check since it's often not there.
|
|
203
|
+
if (directives = gathered_selections[:graphql_directives])
|
|
204
|
+
gathered_selections.delete(:graphql_directives)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
call_method_on_directives(:resolve, selections_result.graphql_application_value, directives) do
|
|
208
|
+
finished_jobs = 0
|
|
209
|
+
enqueued_jobs = gathered_selections.size
|
|
210
|
+
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
|
211
|
+
@dataloader.append_job {
|
|
212
|
+
evaluate_selection(
|
|
213
|
+
result_name, field_ast_nodes_or_ast_node, selections_result
|
|
214
|
+
)
|
|
215
|
+
finished_jobs += 1
|
|
216
|
+
if target_result && finished_jobs == enqueued_jobs
|
|
217
|
+
selections_result.merge_into(target_result)
|
|
218
|
+
end
|
|
219
|
+
}
|
|
220
|
+
# Field resolution may pause the fiber,
|
|
221
|
+
# so it wouldn't get to the `Resolve` call that happens below.
|
|
222
|
+
# So instead trigger a run from this outer context.
|
|
223
|
+
if selections_result.graphql_is_eager
|
|
224
|
+
@dataloader.clear_cache
|
|
225
|
+
@dataloader.run
|
|
226
|
+
@dataloader.clear_cache
|
|
386
227
|
end
|
|
387
|
-
}
|
|
388
|
-
# Field resolution may pause the fiber,
|
|
389
|
-
# so it wouldn't get to the `Resolve` call that happens below.
|
|
390
|
-
# So instead trigger a run from this outer context.
|
|
391
|
-
if is_eager_selection
|
|
392
|
-
@dataloader.run
|
|
393
228
|
end
|
|
229
|
+
selections_result
|
|
394
230
|
end
|
|
395
|
-
|
|
396
|
-
selections_result
|
|
397
231
|
end
|
|
398
232
|
|
|
399
233
|
# @return [void]
|
|
400
|
-
def evaluate_selection(result_name, field_ast_nodes_or_ast_node,
|
|
401
|
-
return if
|
|
234
|
+
def evaluate_selection(result_name, field_ast_nodes_or_ast_node, selections_result) # rubocop:disable Metrics/ParameterLists
|
|
235
|
+
return if selections_result.graphql_dead
|
|
402
236
|
# As a performance optimization, the hash key will be a `Node` if
|
|
403
237
|
# there's only one selection of the field. But if there are multiple
|
|
404
238
|
# selections of the field, it will be an Array of nodes
|
|
@@ -410,59 +244,42 @@ module GraphQL
|
|
|
410
244
|
ast_node = field_ast_nodes_or_ast_node
|
|
411
245
|
end
|
|
412
246
|
field_name = ast_node.name
|
|
413
|
-
|
|
414
|
-
|
|
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
|
|
247
|
+
owner_type = selections_result.graphql_result_type
|
|
248
|
+
field_defn = query.warden.get_field(owner_type, field_name)
|
|
430
249
|
|
|
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
250
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
251
|
+
runtime_state = get_current_runtime_state
|
|
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
|
-
|
|
256
|
+
owner_object = selections_result.graphql_application_value
|
|
257
|
+
if field_defn.dynamic_introspection
|
|
442
258
|
owner_object = field_defn.owner.wrap(owner_object, context)
|
|
443
259
|
end
|
|
444
260
|
|
|
445
|
-
|
|
446
|
-
if total_args_count == 0
|
|
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,
|
|
265
|
+
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state
|
|
451
266
|
)
|
|
452
267
|
else
|
|
453
|
-
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes,
|
|
268
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, 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_object, result_name, selections_result, 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,
|
|
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, object, result_name, selection_result, 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|
|
|
464
280
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
|
465
|
-
|
|
281
|
+
return_type_non_null = field_defn.type.non_null?
|
|
282
|
+
continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
|
466
283
|
next
|
|
467
284
|
end
|
|
468
285
|
|
|
@@ -500,7 +317,8 @@ module GraphQL
|
|
|
500
317
|
# to the keyword args hash _before_ freezing everything.
|
|
501
318
|
extra_args[:argument_details] = :__arguments_add_self
|
|
502
319
|
when :parent
|
|
503
|
-
|
|
320
|
+
parent_result = selection_result.graphql_parent
|
|
321
|
+
extra_args[:parent] = parent_result&.graphql_application_value&.object
|
|
504
322
|
else
|
|
505
323
|
extra_args[extra] = field_defn.fetch_extra(extra, context)
|
|
506
324
|
end
|
|
@@ -511,17 +329,15 @@ module GraphQL
|
|
|
511
329
|
resolved_arguments.keyword_arguments
|
|
512
330
|
end
|
|
513
331
|
|
|
514
|
-
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes,
|
|
332
|
+
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state)
|
|
515
333
|
end
|
|
516
334
|
end
|
|
517
335
|
|
|
518
|
-
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes,
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
st.current_result_name = result_name
|
|
524
|
-
st.current_result = selection_result
|
|
336
|
+
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
337
|
+
runtime_state.current_field = field_defn
|
|
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,14 @@ 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_arguments = resolved_arguments
|
|
360
|
+
runtime_state.current_result_name = result_name
|
|
361
|
+
runtime_state.current_result = selection_result
|
|
362
|
+
end
|
|
539
363
|
# Actually call the field resolver and capture the result
|
|
540
364
|
app_result = begin
|
|
541
365
|
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
|
@@ -550,32 +374,27 @@ module GraphQL
|
|
|
550
374
|
ex_err
|
|
551
375
|
end
|
|
552
376
|
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|
|
|
554
|
-
|
|
377
|
+
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|
|
|
378
|
+
owner_type = selection_result.graphql_result_type
|
|
379
|
+
return_type = field_defn.type
|
|
380
|
+
continue_value = continue_value(inner_result, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
|
|
555
381
|
if HALT != continue_value
|
|
556
|
-
|
|
382
|
+
was_scoped = runtime_state.was_authorized_by_scope_items
|
|
383
|
+
runtime_state.was_authorized_by_scope_items = nil
|
|
384
|
+
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
385
|
end
|
|
558
386
|
end
|
|
559
387
|
end
|
|
560
|
-
|
|
561
388
|
# If this field is a root mutation field, immediately resolve
|
|
562
389
|
# all of its child fields before moving on to the next root mutation field.
|
|
563
390
|
# (Subselections of this mutation will still be resolved level-by-level.)
|
|
564
|
-
if
|
|
391
|
+
if selection_result.graphql_is_eager
|
|
565
392
|
Interpreter::Resolve.resolve_all([field_result], @dataloader)
|
|
566
|
-
else
|
|
567
|
-
# Return this from `after_lazy` because it might be another lazy that needs to be resolved
|
|
568
|
-
field_result
|
|
569
393
|
end
|
|
570
394
|
end
|
|
571
395
|
|
|
572
|
-
|
|
573
|
-
def dead_result?(selection_result)
|
|
574
|
-
selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
|
|
575
|
-
end
|
|
576
|
-
|
|
577
396
|
def set_result(selection_result, result_name, value, is_child_result, is_non_null)
|
|
578
|
-
if !
|
|
397
|
+
if !selection_result.graphql_dead
|
|
579
398
|
if value.nil? && is_non_null
|
|
580
399
|
# This is an invalid nil that should be propagated
|
|
581
400
|
# One caller of this method passes a block,
|
|
@@ -629,11 +448,13 @@ module GraphQL
|
|
|
629
448
|
end
|
|
630
449
|
|
|
631
450
|
HALT = Object.new
|
|
632
|
-
def continue_value(value,
|
|
451
|
+
def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
|
633
452
|
case value
|
|
634
453
|
when nil
|
|
635
454
|
if is_non_null
|
|
636
455
|
set_result(selection_result, result_name, nil, false, is_non_null) do
|
|
456
|
+
# When this comes from a list item, use the parent object:
|
|
457
|
+
parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
|
|
637
458
|
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
|
|
638
459
|
err = parent_type::InvalidNullError.new(parent_type, field, value)
|
|
639
460
|
schema.type_error(err, context)
|
|
@@ -647,7 +468,7 @@ module GraphQL
|
|
|
647
468
|
# to avoid the overhead of checking three different classes
|
|
648
469
|
# every time.
|
|
649
470
|
if value.is_a?(GraphQL::ExecutionError)
|
|
650
|
-
if selection_result.nil? || !
|
|
471
|
+
if selection_result.nil? || !selection_result.graphql_dead
|
|
651
472
|
value.path ||= current_path
|
|
652
473
|
value.ast_node ||= ast_node
|
|
653
474
|
context.errors << value
|
|
@@ -665,7 +486,7 @@ module GraphQL
|
|
|
665
486
|
rescue GraphQL::ExecutionError => err
|
|
666
487
|
err
|
|
667
488
|
end
|
|
668
|
-
continue_value(next_value,
|
|
489
|
+
continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
|
|
669
490
|
elsif value.is_a?(GraphQL::UnauthorizedError)
|
|
670
491
|
# this hook might raise & crash, or it might return
|
|
671
492
|
# a replacement value
|
|
@@ -674,7 +495,7 @@ module GraphQL
|
|
|
674
495
|
rescue GraphQL::ExecutionError => err
|
|
675
496
|
err
|
|
676
497
|
end
|
|
677
|
-
continue_value(next_value,
|
|
498
|
+
continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
|
|
678
499
|
elsif GraphQL::Execution::SKIP == value
|
|
679
500
|
# It's possible a lazy was already written here
|
|
680
501
|
case selection_result
|
|
@@ -695,9 +516,9 @@ module GraphQL
|
|
|
695
516
|
end
|
|
696
517
|
when Array
|
|
697
518
|
# It's an array full of execution errors; add them all.
|
|
698
|
-
if value.any? && value.all?
|
|
519
|
+
if value.any? && value.all?(GraphQL::ExecutionError)
|
|
699
520
|
list_type_at_all = (field && (field.type.list?))
|
|
700
|
-
if selection_result.nil? || !
|
|
521
|
+
if selection_result.nil? || !selection_result.graphql_dead
|
|
701
522
|
value.each_with_index do |error, index|
|
|
702
523
|
error.ast_node ||= ast_node
|
|
703
524
|
error.path ||= current_path + (list_type_at_all ? [index] : [])
|
|
@@ -733,7 +554,7 @@ module GraphQL
|
|
|
733
554
|
# Location information from `path` and `ast_node`.
|
|
734
555
|
#
|
|
735
556
|
# @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
|
|
557
|
+
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
558
|
if current_type.non_null?
|
|
738
559
|
current_type = current_type.of_type
|
|
739
560
|
is_non_null = true
|
|
@@ -750,7 +571,7 @@ module GraphQL
|
|
|
750
571
|
r
|
|
751
572
|
when "UNION", "INTERFACE"
|
|
752
573
|
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|
|
|
574
|
+
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
575
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
|
755
576
|
resolved_type, resolved_value = resolved_type_result
|
|
756
577
|
else
|
|
@@ -767,60 +588,35 @@ module GraphQL
|
|
|
767
588
|
set_result(selection_result, result_name, nil, false, is_non_null)
|
|
768
589
|
nil
|
|
769
590
|
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)
|
|
591
|
+
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
592
|
end
|
|
772
593
|
end
|
|
773
594
|
when "OBJECT"
|
|
774
595
|
object_proxy = begin
|
|
775
|
-
current_type.wrap(value, context)
|
|
596
|
+
was_scoped ? current_type.wrap_scoped(value, context) : current_type.wrap(value, context)
|
|
776
597
|
rescue GraphQL::ExecutionError => err
|
|
777
598
|
err
|
|
778
599
|
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|
|
|
780
|
-
continue_value = continue_value(inner_object,
|
|
600
|
+
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|
|
|
601
|
+
continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
|
|
781
602
|
if HALT != continue_value
|
|
782
|
-
response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
|
603
|
+
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
|
|
783
604
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
|
784
|
-
|
|
785
|
-
# There are two possibilities for `gathered_selections`:
|
|
786
|
-
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
|
787
|
-
# This case is handled below, and the result can be written right into the main `response_hash` above.
|
|
788
|
-
# In this case, `gathered_selections` is a hash of selections.
|
|
789
|
-
# 2. Some selections of this object have runtime directives that may or may not modify execution.
|
|
790
|
-
# That part of the selection is evaluated in an isolated way, writing into a sub-response object which is
|
|
791
|
-
# eventually merged into the final response. In this case, `gathered_selections` is an array of things to run in isolation.
|
|
792
|
-
# (Technically, it's possible that one of those entries _doesn't_ require isolation.)
|
|
793
|
-
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
|
605
|
+
each_gathered_selections(response_hash) do |selections, is_selection_array|
|
|
794
606
|
if is_selection_array
|
|
795
|
-
this_result = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
|
607
|
+
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
|
|
796
608
|
final_result = response_hash
|
|
797
609
|
else
|
|
798
610
|
this_result = response_hash
|
|
799
611
|
final_result = nil
|
|
800
612
|
end
|
|
801
|
-
# reset this mutable state
|
|
802
|
-
# Unset `result_name` here because it's already included in the new response hash
|
|
803
|
-
st = get_current_runtime_state
|
|
804
|
-
st.current_object = continue_value
|
|
805
|
-
st.current_result_name = nil
|
|
806
|
-
st.current_result = this_result
|
|
807
613
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
continue_value,
|
|
815
|
-
current_type,
|
|
816
|
-
false,
|
|
817
|
-
selections,
|
|
818
|
-
this_result,
|
|
819
|
-
final_result,
|
|
820
|
-
owner_object.object,
|
|
821
|
-
)
|
|
822
|
-
this_result
|
|
823
|
-
end
|
|
614
|
+
evaluate_selections(
|
|
615
|
+
selections,
|
|
616
|
+
this_result,
|
|
617
|
+
final_result,
|
|
618
|
+
runtime_state,
|
|
619
|
+
)
|
|
824
620
|
end
|
|
825
621
|
end
|
|
826
622
|
end
|
|
@@ -829,7 +625,7 @@ module GraphQL
|
|
|
829
625
|
# This is true for objects, unions, and interfaces
|
|
830
626
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
|
831
627
|
inner_type_non_null = inner_type.non_null?
|
|
832
|
-
response_list = GraphQLResultArray.new(result_name, selection_result, is_non_null)
|
|
628
|
+
response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
|
|
833
629
|
set_result(selection_result, result_name, response_list, true, is_non_null)
|
|
834
630
|
idx = nil
|
|
835
631
|
list_value = begin
|
|
@@ -839,10 +635,10 @@ module GraphQL
|
|
|
839
635
|
idx += 1
|
|
840
636
|
if use_dataloader_job
|
|
841
637
|
@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,
|
|
638
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
|
|
843
639
|
end
|
|
844
640
|
else
|
|
845
|
-
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list,
|
|
641
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
|
|
846
642
|
end
|
|
847
643
|
end
|
|
848
644
|
|
|
@@ -867,22 +663,21 @@ module GraphQL
|
|
|
867
663
|
end
|
|
868
664
|
# Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set)
|
|
869
665
|
error_is_non_null = idx.nil? ? is_non_null : inner_type.non_null?
|
|
870
|
-
continue_value(list_value,
|
|
666
|
+
continue_value(list_value, field, error_is_non_null, ast_node, result_name, selection_result)
|
|
871
667
|
else
|
|
872
668
|
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
|
|
873
669
|
end
|
|
874
670
|
end
|
|
875
671
|
|
|
876
|
-
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list,
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
st.current_result = response_list
|
|
672
|
+
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
673
|
+
runtime_state.current_result_name = this_idx
|
|
674
|
+
runtime_state.current_result = response_list
|
|
880
675
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
|
881
676
|
# 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|
|
|
883
|
-
continue_value = continue_value(inner_inner_value,
|
|
677
|
+
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|
|
|
678
|
+
continue_value = continue_value(inner_inner_value, field, inner_type_non_null, ast_node, this_idx, response_list)
|
|
884
679
|
if HALT != continue_value
|
|
885
|
-
continue_field(continue_value, owner_type, field, inner_type, ast_node,
|
|
680
|
+
continue_field(continue_value, owner_type, field, inner_type, ast_node, response_list.graphql_selections, false, owner_object, arguments, this_idx, response_list, was_scoped, runtime_state)
|
|
886
681
|
end
|
|
887
682
|
end
|
|
888
683
|
end
|
|
@@ -902,7 +697,6 @@ module GraphQL
|
|
|
902
697
|
raw_dir_args = arguments(nil, dir_defn, dir_node)
|
|
903
698
|
dir_args = continue_value(
|
|
904
699
|
raw_dir_args, # value
|
|
905
|
-
dir_defn, # parent_type
|
|
906
700
|
nil, # field
|
|
907
701
|
false, # is_non_null
|
|
908
702
|
dir_node, # ast_node
|
|
@@ -959,16 +753,20 @@ module GraphQL
|
|
|
959
753
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
|
960
754
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
|
961
755
|
# @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)
|
|
756
|
+
def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
|
|
963
757
|
if lazy?(lazy_obj)
|
|
964
758
|
orig_result = result
|
|
759
|
+
was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
|
|
965
760
|
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
761
|
+
# This block might be called in a new fiber;
|
|
762
|
+
# In that case, this will initialize a new state
|
|
763
|
+
# to avoid conflicting with the parent fiber.
|
|
764
|
+
runtime_state = get_current_runtime_state
|
|
765
|
+
runtime_state.current_field = field
|
|
766
|
+
runtime_state.current_arguments = arguments
|
|
767
|
+
runtime_state.current_result_name = result_name
|
|
768
|
+
runtime_state.current_result = orig_result
|
|
769
|
+
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
|
972
770
|
# Wrap the execution of _this_ method with tracing,
|
|
973
771
|
# but don't wrap the continuation below
|
|
974
772
|
inner_obj = begin
|
|
@@ -988,7 +786,7 @@ module GraphQL
|
|
|
988
786
|
ex_err
|
|
989
787
|
end
|
|
990
788
|
end
|
|
991
|
-
yield(inner_obj)
|
|
789
|
+
yield(inner_obj, runtime_state)
|
|
992
790
|
end
|
|
993
791
|
|
|
994
792
|
if eager
|
|
@@ -1005,7 +803,7 @@ module GraphQL
|
|
|
1005
803
|
end
|
|
1006
804
|
else
|
|
1007
805
|
# Don't need to reset state here because it _wasn't_ lazy.
|
|
1008
|
-
yield(lazy_obj)
|
|
806
|
+
yield(lazy_obj, runtime_state)
|
|
1009
807
|
end
|
|
1010
808
|
end
|
|
1011
809
|
|