graphql 1.11.7 → 1.12.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +7 -5
- data/lib/generators/graphql/relay.rb +55 -0
- data/lib/generators/graphql/relay_generator.rb +20 -0
- data/lib/generators/graphql/templates/base_connection.erb +8 -0
- data/lib/generators/graphql/templates/base_edge.erb +8 -0
- data/lib/generators/graphql/templates/node_type.erb +9 -0
- data/lib/generators/graphql/templates/object.erb +1 -1
- data/lib/generators/graphql/templates/query_type.erb +1 -3
- data/lib/generators/graphql/templates/schema.erb +8 -35
- data/lib/graphql.rb +38 -4
- data/lib/graphql/analysis/analyze_query.rb +7 -0
- data/lib/graphql/analysis/ast.rb +11 -2
- data/lib/graphql/analysis/ast/visitor.rb +9 -1
- data/lib/graphql/backtrace.rb +28 -19
- data/lib/graphql/backtrace/inspect_result.rb +0 -1
- data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
- data/lib/graphql/backtrace/table.rb +22 -3
- data/lib/graphql/backtrace/traced_error.rb +0 -1
- data/lib/graphql/backtrace/tracer.rb +37 -10
- data/lib/graphql/backwards_compatibility.rb +2 -1
- data/lib/graphql/base_type.rb +1 -1
- data/lib/graphql/compatibility/execution_specification.rb +1 -0
- data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
- data/lib/graphql/dataloader.rb +208 -0
- data/lib/graphql/dataloader/null_dataloader.rb +21 -0
- data/lib/graphql/dataloader/request.rb +19 -0
- data/lib/graphql/dataloader/request_all.rb +19 -0
- data/lib/graphql/dataloader/source.rb +107 -0
- data/lib/graphql/define/assign_global_id_field.rb +1 -1
- data/lib/graphql/define/instance_definable.rb +32 -2
- data/lib/graphql/define/type_definer.rb +5 -5
- data/lib/graphql/deprecated_dsl.rb +7 -2
- data/lib/graphql/deprecation.rb +13 -0
- data/lib/graphql/enum_type.rb +2 -0
- data/lib/graphql/execution/errors.rb +4 -0
- data/lib/graphql/execution/execute.rb +7 -0
- data/lib/graphql/execution/interpreter.rb +11 -7
- data/lib/graphql/execution/interpreter/arguments.rb +51 -14
- data/lib/graphql/execution/interpreter/arguments_cache.rb +37 -14
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
- data/lib/graphql/execution/interpreter/resolve.rb +33 -25
- data/lib/graphql/execution/interpreter/runtime.rb +173 -123
- data/lib/graphql/execution/multiplex.rb +36 -23
- data/lib/graphql/function.rb +4 -0
- data/lib/graphql/input_object_type.rb +2 -0
- data/lib/graphql/interface_type.rb +3 -1
- data/lib/graphql/internal_representation/document.rb +2 -2
- data/lib/graphql/internal_representation/rewrite.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +50 -23
- data/lib/graphql/object_type.rb +2 -2
- data/lib/graphql/pagination/connection.rb +5 -1
- data/lib/graphql/pagination/connections.rb +6 -16
- data/lib/graphql/parse_error.rb +0 -1
- data/lib/graphql/query.rb +10 -2
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/arguments_cache.rb +0 -1
- data/lib/graphql/query/context.rb +4 -2
- data/lib/graphql/query/executor.rb +0 -1
- data/lib/graphql/query/null_context.rb +3 -2
- data/lib/graphql/query/serial_execution.rb +1 -0
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/relay/base_connection.rb +7 -0
- data/lib/graphql/relay/connection_instrumentation.rb +4 -4
- data/lib/graphql/relay/connection_type.rb +1 -1
- data/lib/graphql/relay/mutation.rb +1 -0
- data/lib/graphql/relay/node.rb +3 -0
- data/lib/graphql/relay/type_extensions.rb +2 -0
- data/lib/graphql/scalar_type.rb +2 -0
- data/lib/graphql/schema.rb +64 -26
- data/lib/graphql/schema/argument.rb +86 -7
- data/lib/graphql/schema/build_from_definition.rb +139 -51
- data/lib/graphql/schema/directive.rb +76 -0
- data/lib/graphql/schema/directive/flagged.rb +57 -0
- data/lib/graphql/schema/enum.rb +3 -0
- data/lib/graphql/schema/enum_value.rb +12 -6
- data/lib/graphql/schema/field.rb +40 -16
- data/lib/graphql/schema/field/connection_extension.rb +3 -2
- data/lib/graphql/schema/find_inherited_value.rb +3 -1
- data/lib/graphql/schema/input_object.rb +39 -24
- data/lib/graphql/schema/interface.rb +1 -0
- data/lib/graphql/schema/member.rb +4 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
- data/lib/graphql/schema/member/build_type.rb +3 -3
- data/lib/graphql/schema/member/has_arguments.rb +54 -49
- data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
- data/lib/graphql/schema/member/has_directives.rb +98 -0
- data/lib/graphql/schema/member/has_fields.rb +1 -4
- data/lib/graphql/schema/member/has_validators.rb +31 -0
- data/lib/graphql/schema/member/instrumentation.rb +0 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/middleware_chain.rb +1 -1
- data/lib/graphql/schema/object.rb +11 -0
- data/lib/graphql/schema/printer.rb +5 -4
- data/lib/graphql/schema/resolver.rb +7 -0
- data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
- data/lib/graphql/schema/subscription.rb +19 -1
- data/lib/graphql/schema/timeout_middleware.rb +3 -1
- data/lib/graphql/schema/validation.rb +4 -2
- data/lib/graphql/schema/validator.rb +163 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
- data/lib/graphql/schema/validator/format_validator.rb +49 -0
- data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
- data/lib/graphql/schema/validator/length_validator.rb +57 -0
- data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
- data/lib/graphql/schema/validator/required_validator.rb +68 -0
- data/lib/graphql/static_validation/validator.rb +4 -0
- data/lib/graphql/subscriptions.rb +17 -20
- data/lib/graphql/subscriptions/event.rb +0 -1
- data/lib/graphql/subscriptions/instrumentation.rb +0 -1
- data/lib/graphql/subscriptions/serialize.rb +0 -1
- data/lib/graphql/subscriptions/subscription_root.rb +1 -1
- data/lib/graphql/tracing.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/skylight_tracing.rb +1 -1
- data/lib/graphql/types/relay.rb +11 -3
- data/lib/graphql/types/relay/base_connection.rb +2 -92
- data/lib/graphql/types/relay/base_edge.rb +2 -35
- data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
- data/lib/graphql/types/relay/default_relay.rb +27 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
- data/lib/graphql/types/relay/has_node_field.rb +41 -0
- data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
- data/lib/graphql/types/relay/node.rb +2 -4
- data/lib/graphql/types/relay/node_behaviors.rb +15 -0
- data/lib/graphql/types/relay/node_field.rb +1 -19
- data/lib/graphql/types/relay/nodes_field.rb +1 -19
- data/lib/graphql/types/relay/page_info.rb +2 -14
- data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
- data/lib/graphql/union_type.rb +2 -0
- data/lib/graphql/upgrader/member.rb +1 -0
- data/lib/graphql/upgrader/schema.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- metadata +50 -93
- data/lib/graphql/types/relay/base_field.rb +0 -22
- data/lib/graphql/types/relay/base_interface.rb +0 -29
- data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -6,17 +6,21 @@ module GraphQL
|
|
6
6
|
class ArgumentsCache
|
7
7
|
def initialize(query)
|
8
8
|
@query = query
|
9
|
+
@dataloader = query.context.dataloader
|
9
10
|
@storage = Hash.new do |h, ast_node|
|
10
11
|
h[ast_node] = Hash.new do |h2, arg_owner|
|
11
12
|
h2[arg_owner] = Hash.new do |h3, parent_object|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
dataload_for(ast_node, arg_owner, parent_object) do |kwarg_arguments|
|
14
|
+
h3[parent_object] = @query.schema.after_lazy(kwarg_arguments) do |resolved_args|
|
15
|
+
h3[parent_object] = resolved_args
|
16
|
+
end
|
17
|
+
end
|
16
18
|
|
17
|
-
h3
|
18
|
-
#
|
19
|
-
h3[parent_object] =
|
19
|
+
if !h3.key?(parent_object)
|
20
|
+
# TODO should i bother putting anything here?
|
21
|
+
h3[parent_object] = NO_ARGUMENTS
|
22
|
+
else
|
23
|
+
h3[parent_object]
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|
@@ -25,6 +29,25 @@ module GraphQL
|
|
25
29
|
|
26
30
|
def fetch(ast_node, argument_owner, parent_object)
|
27
31
|
@storage[ast_node][argument_owner][parent_object]
|
32
|
+
# If any jobs were enqueued, run them now,
|
33
|
+
# since this might have been called outside of execution.
|
34
|
+
# (The jobs are responsible for updating `result` in-place.)
|
35
|
+
@dataloader.run
|
36
|
+
# Ack, the _hash_ is updated, but the key is eventually
|
37
|
+
# overridden with an immutable arguments instance.
|
38
|
+
# The first call queues up the job,
|
39
|
+
# then this call fetches the result.
|
40
|
+
# TODO this should be better, find a solution
|
41
|
+
# that works with merging the runtime.rb code
|
42
|
+
@storage[ast_node][argument_owner][parent_object]
|
43
|
+
end
|
44
|
+
|
45
|
+
# @yield [Interpreter::Arguments, Lazy<Interpreter::Arguments>] The finally-loaded arguments
|
46
|
+
def dataload_for(ast_node, argument_owner, parent_object, &block)
|
47
|
+
# First, normalize all AST or Ruby values to a plain Ruby hash
|
48
|
+
args_hash = self.class.prepare_args_hash(@query, ast_node)
|
49
|
+
argument_owner.coerce_arguments(parent_object, args_hash, @query.context, &block)
|
50
|
+
nil
|
28
51
|
end
|
29
52
|
|
30
53
|
private
|
@@ -33,7 +56,7 @@ module GraphQL
|
|
33
56
|
|
34
57
|
NO_VALUE_GIVEN = Object.new
|
35
58
|
|
36
|
-
def prepare_args_hash(ast_arg_or_hash_or_value)
|
59
|
+
def self.prepare_args_hash(query, ast_arg_or_hash_or_value)
|
37
60
|
case ast_arg_or_hash_or_value
|
38
61
|
when Hash
|
39
62
|
if ast_arg_or_hash_or_value.empty?
|
@@ -41,27 +64,27 @@ module GraphQL
|
|
41
64
|
end
|
42
65
|
args_hash = {}
|
43
66
|
ast_arg_or_hash_or_value.each do |k, v|
|
44
|
-
args_hash[k] = prepare_args_hash(v)
|
67
|
+
args_hash[k] = prepare_args_hash(query, v)
|
45
68
|
end
|
46
69
|
args_hash
|
47
70
|
when Array
|
48
|
-
ast_arg_or_hash_or_value.map { |v| prepare_args_hash(v) }
|
71
|
+
ast_arg_or_hash_or_value.map { |v| prepare_args_hash(query, v) }
|
49
72
|
when GraphQL::Language::Nodes::Field, GraphQL::Language::Nodes::InputObject, GraphQL::Language::Nodes::Directive
|
50
73
|
if ast_arg_or_hash_or_value.arguments.empty?
|
51
74
|
return NO_ARGUMENTS
|
52
75
|
end
|
53
76
|
args_hash = {}
|
54
77
|
ast_arg_or_hash_or_value.arguments.each do |arg|
|
55
|
-
v = prepare_args_hash(arg.value)
|
78
|
+
v = prepare_args_hash(query, arg.value)
|
56
79
|
if v != NO_VALUE_GIVEN
|
57
80
|
args_hash[arg.name] = v
|
58
81
|
end
|
59
82
|
end
|
60
83
|
args_hash
|
61
84
|
when GraphQL::Language::Nodes::VariableIdentifier
|
62
|
-
if
|
63
|
-
variable_value =
|
64
|
-
prepare_args_hash(variable_value)
|
85
|
+
if query.variables.key?(ast_arg_or_hash_or_value.name)
|
86
|
+
variable_value = query.variables[ast_arg_or_hash_or_value.name]
|
87
|
+
prepare_args_hash(query, variable_value)
|
65
88
|
else
|
66
89
|
NO_VALUE_GIVEN
|
67
90
|
end
|
@@ -6,10 +6,9 @@ module GraphQL
|
|
6
6
|
module Resolve
|
7
7
|
# Continue field results in `results` until there's nothing else to continue.
|
8
8
|
# @return [void]
|
9
|
-
def self.resolve_all(results)
|
10
|
-
|
11
|
-
|
12
|
-
end
|
9
|
+
def self.resolve_all(results, dataloader)
|
10
|
+
dataloader.append_job { resolve(results, dataloader) }
|
11
|
+
nil
|
13
12
|
end
|
14
13
|
|
15
14
|
# After getting `results` back from an interpreter evaluation,
|
@@ -24,33 +23,42 @@ module GraphQL
|
|
24
23
|
# return {Lazy} instances if there's more work to be done,
|
25
24
|
# or return {Hash}/{Array} if the query should be continued.
|
26
25
|
#
|
27
|
-
# @
|
28
|
-
|
29
|
-
|
26
|
+
# @return [void]
|
27
|
+
def self.resolve(results, dataloader)
|
28
|
+
# There might be pending jobs here that _will_ write lazies
|
29
|
+
# into the result hash. We should run them out, so we
|
30
|
+
# can be sure that all lazies will be present in the result hashes.
|
31
|
+
# A better implementation would somehow interleave (or unify)
|
32
|
+
# these approaches.
|
33
|
+
dataloader.run
|
30
34
|
next_results = []
|
31
|
-
|
32
|
-
# Work through the queue until it's empty
|
33
|
-
while results.size > 0
|
35
|
+
while results.any?
|
34
36
|
result_value = results.shift
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
if result_value.is_a?(Lazy)
|
41
|
-
# Since this field returned another lazy,
|
42
|
-
# add it to the same queue
|
43
|
-
results << result_value
|
44
|
-
elsif result_value.is_a?(Hash)
|
45
|
-
# This is part of the next level, add it
|
46
|
-
next_results.concat(result_value.values)
|
37
|
+
if result_value.is_a?(Hash)
|
38
|
+
results.concat(result_value.values)
|
39
|
+
next
|
47
40
|
elsif result_value.is_a?(Array)
|
48
|
-
|
49
|
-
|
41
|
+
results.concat(result_value)
|
42
|
+
next
|
43
|
+
elsif result_value.is_a?(Lazy)
|
44
|
+
loaded_value = result_value.value
|
45
|
+
if loaded_value.is_a?(Lazy)
|
46
|
+
# Since this field returned another lazy,
|
47
|
+
# add it to the same queue
|
48
|
+
results << loaded_value
|
49
|
+
elsif loaded_value.is_a?(Hash) || loaded_value.is_a?(Array)
|
50
|
+
# Add these values in wholesale --
|
51
|
+
# they might be modified by later work in the dataloader.
|
52
|
+
next_results << loaded_value
|
53
|
+
end
|
50
54
|
end
|
51
55
|
end
|
52
56
|
|
53
|
-
next_results
|
57
|
+
if next_results.any?
|
58
|
+
dataloader.append_job { resolve(next_results, dataloader) }
|
59
|
+
end
|
60
|
+
|
61
|
+
nil
|
54
62
|
end
|
55
63
|
end
|
56
64
|
end
|
@@ -19,8 +19,10 @@ module GraphQL
|
|
19
19
|
|
20
20
|
def initialize(query:, response:)
|
21
21
|
@query = query
|
22
|
+
@dataloader = query.multiplex.dataloader
|
22
23
|
@schema = query.schema
|
23
24
|
@context = query.context
|
25
|
+
@multiplex_context = query.multiplex.context
|
24
26
|
@interpreter_context = @context.namespace(:interpreter)
|
25
27
|
@response = response
|
26
28
|
@dead_paths = {}
|
@@ -54,7 +56,18 @@ module GraphQL
|
|
54
56
|
# Root .authorized? returned false.
|
55
57
|
write_in_response(path, nil)
|
56
58
|
else
|
57
|
-
|
59
|
+
gathered_selections = gather_selections(object_proxy, root_type, root_operation.selections)
|
60
|
+
# Make the first fiber which will begin execution
|
61
|
+
@dataloader.append_job {
|
62
|
+
evaluate_selections(
|
63
|
+
path,
|
64
|
+
context.scoped_context,
|
65
|
+
object_proxy,
|
66
|
+
root_type,
|
67
|
+
root_op_type == "mutation",
|
68
|
+
gathered_selections,
|
69
|
+
)
|
70
|
+
}
|
58
71
|
end
|
59
72
|
delete_interpreter_context(:current_path)
|
60
73
|
delete_interpreter_context(:current_field)
|
@@ -63,7 +76,7 @@ module GraphQL
|
|
63
76
|
nil
|
64
77
|
end
|
65
78
|
|
66
|
-
def gather_selections(owner_object, owner_type, selections, selections_by_name)
|
79
|
+
def gather_selections(owner_object, owner_type, selections, selections_by_name = {})
|
67
80
|
selections.each do |node|
|
68
81
|
# Skip gathering this if the directive says so
|
69
82
|
if !directives_include?(node, owner_object, owner_type)
|
@@ -115,144 +128,172 @@ module GraphQL
|
|
115
128
|
raise "Invariant: unexpected selection class: #{node.class}"
|
116
129
|
end
|
117
130
|
end
|
131
|
+
selections_by_name
|
118
132
|
end
|
119
133
|
|
120
134
|
NO_ARGS = {}.freeze
|
121
135
|
|
122
|
-
|
136
|
+
# @return [void]
|
137
|
+
def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection, gathered_selections)
|
123
138
|
set_all_interpreter_context(owner_object, nil, nil, path)
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
139
|
+
|
140
|
+
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
141
|
+
@dataloader.append_job {
|
142
|
+
evaluate_selection(
|
143
|
+
path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_selection
|
144
|
+
)
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
|
151
|
+
attr_reader :progress_path
|
152
|
+
|
153
|
+
# @return [void]
|
154
|
+
def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field)
|
155
|
+
# As a performance optimization, the hash key will be a `Node` if
|
156
|
+
# there's only one selection of the field. But if there are multiple
|
157
|
+
# selections of the field, it will be an Array of nodes
|
158
|
+
if field_ast_nodes_or_ast_node.is_a?(Array)
|
159
|
+
field_ast_nodes = field_ast_nodes_or_ast_node
|
160
|
+
ast_node = field_ast_nodes.first
|
161
|
+
else
|
162
|
+
field_ast_nodes = nil
|
163
|
+
ast_node = field_ast_nodes_or_ast_node
|
164
|
+
end
|
165
|
+
field_name = ast_node.name
|
166
|
+
field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
|
167
|
+
is_introspection = false
|
168
|
+
if field_defn.nil?
|
169
|
+
field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
170
|
+
is_introspection = true
|
171
|
+
entry_point_field
|
172
|
+
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
173
|
+
is_introspection = true
|
174
|
+
dynamic_field
|
133
175
|
else
|
134
|
-
|
135
|
-
ast_node = field_ast_nodes_or_ast_node
|
176
|
+
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
136
177
|
end
|
137
|
-
|
138
|
-
|
139
|
-
is_introspection = false
|
140
|
-
if field_defn.nil?
|
141
|
-
field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
142
|
-
is_introspection = true
|
143
|
-
entry_point_field
|
144
|
-
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
145
|
-
is_introspection = true
|
146
|
-
dynamic_field
|
147
|
-
else
|
148
|
-
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
149
|
-
end
|
150
|
-
end
|
151
|
-
return_type = field_defn.type
|
178
|
+
end
|
179
|
+
return_type = field_defn.type
|
152
180
|
|
153
|
-
|
154
|
-
|
155
|
-
|
181
|
+
next_path = path.dup
|
182
|
+
next_path << result_name
|
183
|
+
next_path.freeze
|
156
184
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
185
|
+
# This seems janky, but we need to know
|
186
|
+
# the field's return type at this path in order
|
187
|
+
# to propagate `null`
|
188
|
+
set_type_at_path(next_path, return_type)
|
189
|
+
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
190
|
+
set_all_interpreter_context(nil, field_defn, nil, next_path)
|
163
191
|
|
164
|
-
|
165
|
-
|
192
|
+
context.scoped_context = scoped_context
|
193
|
+
object = owner_object
|
166
194
|
|
167
|
-
|
168
|
-
|
195
|
+
if is_introspection
|
196
|
+
object = authorized_new(field_defn.owner, object, context, next_path)
|
197
|
+
end
|
198
|
+
|
199
|
+
total_args_count = field_defn.arguments.size
|
200
|
+
if total_args_count == 0
|
201
|
+
kwarg_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
202
|
+
evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field)
|
203
|
+
else
|
204
|
+
# TODO remove all arguments(...) usages?
|
205
|
+
@query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
|
206
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field)
|
169
207
|
end
|
208
|
+
end
|
209
|
+
end
|
170
210
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
211
|
+
def evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field) # rubocop:disable Metrics/ParameterLists
|
212
|
+
context.scoped_context = scoped_context
|
213
|
+
return_type = field_defn.type
|
214
|
+
after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |resolved_arguments|
|
215
|
+
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
216
|
+
continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node)
|
175
217
|
next
|
176
218
|
end
|
177
219
|
|
178
|
-
|
179
|
-
case
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
kwarg_arguments[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
|
197
|
-
when :path
|
198
|
-
kwarg_arguments[:path] = next_path
|
199
|
-
when :lookahead
|
200
|
-
if !field_ast_nodes
|
201
|
-
field_ast_nodes = [ast_node]
|
202
|
-
end
|
203
|
-
kwarg_arguments[:lookahead] = Execution::Lookahead.new(
|
204
|
-
query: query,
|
205
|
-
ast_nodes: field_ast_nodes,
|
206
|
-
field: field_defn,
|
207
|
-
)
|
208
|
-
when :argument_details
|
209
|
-
kwarg_arguments[:argument_details] = resolved_arguments
|
210
|
-
else
|
211
|
-
kwarg_arguments[extra] = field_defn.fetch_extra(extra, context)
|
220
|
+
kwarg_arguments = if resolved_arguments.empty? && field_defn.extras.empty?
|
221
|
+
# We can avoid allocating the `{ Symbol => Object }` hash in this case
|
222
|
+
NO_ARGS
|
223
|
+
else
|
224
|
+
# Bundle up the extras, then make a new arguments instance
|
225
|
+
# that includes the extras, too.
|
226
|
+
extra_args = {}
|
227
|
+
field_defn.extras.each do |extra|
|
228
|
+
case extra
|
229
|
+
when :ast_node
|
230
|
+
extra_args[:ast_node] = ast_node
|
231
|
+
when :execution_errors
|
232
|
+
extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
|
233
|
+
when :path
|
234
|
+
extra_args[:path] = next_path
|
235
|
+
when :lookahead
|
236
|
+
if !field_ast_nodes
|
237
|
+
field_ast_nodes = [ast_node]
|
212
238
|
end
|
239
|
+
|
240
|
+
extra_args[:lookahead] = Execution::Lookahead.new(
|
241
|
+
query: query,
|
242
|
+
ast_nodes: field_ast_nodes,
|
243
|
+
field: field_defn,
|
244
|
+
)
|
245
|
+
when :argument_details
|
246
|
+
# Use this flag to tell Interpreter::Arguments to add itself
|
247
|
+
# to the keyword args hash _before_ freezing everything.
|
248
|
+
extra_args[:argument_details] = :__arguments_add_self
|
249
|
+
else
|
250
|
+
extra_args[extra] = field_defn.fetch_extra(extra, context)
|
213
251
|
end
|
214
252
|
end
|
253
|
+
resolved_arguments = resolved_arguments.merge_extras(extra_args)
|
254
|
+
resolved_arguments.keyword_arguments
|
255
|
+
end
|
215
256
|
|
216
|
-
|
257
|
+
set_all_interpreter_context(nil, nil, kwarg_arguments, nil)
|
217
258
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
259
|
+
# Optimize for the case that field is selected only once
|
260
|
+
if field_ast_nodes.nil? || field_ast_nodes.size == 1
|
261
|
+
next_selections = ast_node.selections
|
262
|
+
else
|
263
|
+
next_selections = []
|
264
|
+
field_ast_nodes.each { |f| next_selections.concat(f.selections) }
|
265
|
+
end
|
225
266
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
end
|
267
|
+
field_result = resolve_with_directives(object, ast_node) do
|
268
|
+
# Actually call the field resolver and capture the result
|
269
|
+
app_result = begin
|
270
|
+
query.with_error_handling do
|
271
|
+
query.trace("execute_field", {owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments}) do
|
272
|
+
field_defn.resolve(object, kwarg_arguments, context)
|
233
273
|
end
|
234
|
-
rescue GraphQL::ExecutionError => err
|
235
|
-
err
|
236
274
|
end
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
275
|
+
rescue GraphQL::ExecutionError => err
|
276
|
+
err
|
277
|
+
end
|
278
|
+
after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |inner_result|
|
279
|
+
continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node)
|
280
|
+
if RawValue === continue_value
|
281
|
+
# Write raw value directly to the response without resolving nested objects
|
282
|
+
write_in_response(next_path, continue_value.resolve)
|
283
|
+
elsif HALT != continue_value
|
284
|
+
continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, kwarg_arguments)
|
245
285
|
end
|
246
286
|
end
|
287
|
+
end
|
247
288
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
289
|
+
# If this field is a root mutation field, immediately resolve
|
290
|
+
# all of its child fields before moving on to the next root mutation field.
|
291
|
+
# (Subselections of this mutation will still be resolved level-by-level.)
|
292
|
+
if is_eager_field
|
293
|
+
Interpreter::Resolve.resolve_all([field_result], @dataloader)
|
294
|
+
else
|
295
|
+
# Return this from `after_lazy` because it might be another lazy that needs to be resolved
|
296
|
+
field_result
|
256
297
|
end
|
257
298
|
end
|
258
299
|
end
|
@@ -314,7 +355,7 @@ module GraphQL
|
|
314
355
|
resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
|
315
356
|
resolved_value ||= value
|
316
357
|
|
317
|
-
after_lazy(resolved_type_or_lazy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
|
358
|
+
after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
|
318
359
|
possible_types = query.possible_types(current_type)
|
319
360
|
|
320
361
|
if !possible_types.include?(resolved_type)
|
@@ -334,12 +375,13 @@ module GraphQL
|
|
334
375
|
rescue GraphQL::ExecutionError => err
|
335
376
|
err
|
336
377
|
end
|
337
|
-
after_lazy(object_proxy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |inner_object|
|
378
|
+
after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |inner_object|
|
338
379
|
continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node)
|
339
380
|
if HALT != continue_value
|
340
381
|
response_hash = {}
|
341
382
|
write_in_response(path, response_hash)
|
342
|
-
|
383
|
+
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
384
|
+
evaluate_selections(path, context.scoped_context, continue_value, current_type, false, gathered_selections)
|
343
385
|
response_hash
|
344
386
|
end
|
345
387
|
end
|
@@ -357,7 +399,7 @@ module GraphQL
|
|
357
399
|
idx += 1
|
358
400
|
set_type_at_path(next_path, inner_type)
|
359
401
|
# This will update `response_list` with the lazy
|
360
|
-
after_lazy(inner_value, owner: inner_type, path: next_path, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments) do |inner_inner_value|
|
402
|
+
after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments) do |inner_inner_value|
|
361
403
|
continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node)
|
362
404
|
if HALT != continue_value
|
363
405
|
continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments)
|
@@ -440,7 +482,7 @@ module GraphQL
|
|
440
482
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
441
483
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
442
484
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
443
|
-
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true, &block)
|
485
|
+
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, ast_node:, eager: false, trace: true, &block)
|
444
486
|
set_all_interpreter_context(owner_object, field, arguments, path)
|
445
487
|
if schema.lazy?(lazy_obj)
|
446
488
|
lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
|
@@ -451,7 +493,7 @@ module GraphQL
|
|
451
493
|
inner_obj = begin
|
452
494
|
query.with_error_handling do
|
453
495
|
if trace
|
454
|
-
query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments}) do
|
496
|
+
query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
|
455
497
|
schema.sync_lazy(lazy_obj)
|
456
498
|
end
|
457
499
|
else
|
@@ -461,7 +503,7 @@ module GraphQL
|
|
461
503
|
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
|
462
504
|
err
|
463
505
|
end
|
464
|
-
after_lazy(inner_obj, owner: owner, field: field, path: path, scoped_context: context.scoped_context, owner_object: owner_object, arguments: arguments, eager: eager, trace: trace, &block)
|
506
|
+
after_lazy(inner_obj, owner: owner, field: field, path: path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: owner_object, arguments: arguments, eager: eager, trace: trace, &block)
|
465
507
|
end
|
466
508
|
|
467
509
|
if eager
|
@@ -476,9 +518,7 @@ module GraphQL
|
|
476
518
|
end
|
477
519
|
|
478
520
|
def arguments(graphql_object, arg_owner, ast_node)
|
479
|
-
|
480
|
-
if arg_owner.arguments_statically_coercible? &&
|
481
|
-
(!arg_owner.is_a?(GraphQL::Schema::Field) || (arg_owner.extras.empty? && arg_owner.extensions.empty?))
|
521
|
+
if arg_owner.arguments_statically_coercible?
|
482
522
|
query.arguments_for(ast_node, arg_owner)
|
483
523
|
else
|
484
524
|
# The arguments must be prepared in the context of the given object
|
@@ -519,6 +559,16 @@ module GraphQL
|
|
519
559
|
end
|
520
560
|
end
|
521
561
|
|
562
|
+
def value_at(path)
|
563
|
+
i = 0
|
564
|
+
value = @response.final_value
|
565
|
+
while value && (part = path[i])
|
566
|
+
value = value[part]
|
567
|
+
i += 1
|
568
|
+
end
|
569
|
+
value
|
570
|
+
end
|
571
|
+
|
522
572
|
# To propagate nulls, we have to know what the field type was
|
523
573
|
# at previous parts of the response.
|
524
574
|
# This hash matches the response
|