graphql 1.11.7 → 1.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_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
|