graphql 2.5.21 → 2.5.26
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/graphql/analysis.rb +20 -13
- data/lib/graphql/execution/field_resolve_step.rb +631 -0
- data/lib/graphql/execution/finalize.rb +217 -0
- data/lib/graphql/execution/input_values.rb +261 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
- data/lib/graphql/execution/interpreter/runtime.rb +3 -2
- data/lib/graphql/execution/interpreter.rb +6 -9
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/load_argument_step.rb +64 -0
- data/lib/graphql/execution/next.rb +28 -7
- data/lib/graphql/execution/prepare_object_step.rb +128 -0
- data/lib/graphql/execution/runner.rb +410 -0
- data/lib/graphql/execution/selections_step.rb +91 -0
- data/lib/graphql/execution.rb +8 -5
- data/lib/graphql/execution_error.rb +5 -1
- data/lib/graphql/language/lexer.rb +11 -7
- data/lib/graphql/query/context.rb +7 -1
- data/lib/graphql/query/partial.rb +18 -3
- data/lib/graphql/query.rb +10 -1
- data/lib/graphql/runtime_error.rb +6 -0
- data/lib/graphql/schema/directive.rb +23 -9
- data/lib/graphql/schema/field/connection_extension.rb +2 -15
- data/lib/graphql/schema/field/scope_extension.rb +0 -4
- data/lib/graphql/schema/field.rb +26 -23
- data/lib/graphql/schema/field_extension.rb +11 -41
- data/lib/graphql/schema/interface.rb +26 -0
- data/lib/graphql/schema/list.rb +5 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +0 -10
- data/lib/graphql/schema/member/has_dataloader.rb +20 -0
- data/lib/graphql/schema/member/has_fields.rb +6 -1
- data/lib/graphql/schema/non_null.rb +1 -1
- data/lib/graphql/schema/resolver.rb +18 -3
- data/lib/graphql/schema/subscription.rb +0 -2
- data/lib/graphql/schema/visibility/profile.rb +68 -49
- data/lib/graphql/schema/wrapper.rb +7 -1
- data/lib/graphql/schema.rb +12 -10
- data/lib/graphql/static_validation/base_visitor.rb +90 -66
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -2
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +322 -256
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +34 -10
- data/lib/graphql/subscriptions/event.rb +1 -0
- data/lib/graphql/subscriptions.rb +35 -0
- data/lib/graphql/tracing/perfetto_trace.rb +2 -2
- data/lib/graphql/tracing/trace.rb +6 -0
- data/lib/graphql/unauthorized_error.rb +4 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -3
- metadata +11 -8
- data/lib/graphql/execution/next/field_resolve_step.rb +0 -690
- data/lib/graphql/execution/next/load_argument_step.rb +0 -60
- data/lib/graphql/execution/next/prepare_object_step.rb +0 -129
- data/lib/graphql/execution/next/runner.rb +0 -389
- data/lib/graphql/execution/next/selections_step.rb +0 -37
data/lib/graphql/execution.rb
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require "graphql/execution/directive_checks"
|
|
3
|
+
require "graphql/execution/next"
|
|
3
4
|
require "graphql/execution/interpreter"
|
|
4
5
|
require "graphql/execution/lazy"
|
|
5
6
|
require "graphql/execution/lookahead"
|
|
6
7
|
require "graphql/execution/multiplex"
|
|
7
|
-
require "graphql/execution/next"
|
|
8
8
|
require "graphql/execution/errors"
|
|
9
9
|
|
|
10
10
|
module GraphQL
|
|
11
11
|
module Execution
|
|
12
12
|
# @api private
|
|
13
|
-
class Skip < GraphQL::
|
|
13
|
+
class Skip < GraphQL::RuntimeError
|
|
14
|
+
attr_accessor :path
|
|
15
|
+
def ast_nodes=(_ignored); end
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
def finalize_graphql_result(query, result_data, key)
|
|
18
|
+
result_data.delete(key)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
18
21
|
end
|
|
19
22
|
end
|
|
@@ -6,7 +6,7 @@ module GraphQL
|
|
|
6
6
|
class ExecutionError < GraphQL::RuntimeError
|
|
7
7
|
# @return [GraphQL::Language::Nodes::Field] the field where the error occurred
|
|
8
8
|
def ast_node
|
|
9
|
-
ast_nodes
|
|
9
|
+
ast_nodes&.first
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def ast_node=(new_node)
|
|
@@ -36,6 +36,10 @@ module GraphQL
|
|
|
36
36
|
super(message)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def finalize_graphql_result(query, result_data, key)
|
|
40
|
+
result_data[key] = nil
|
|
41
|
+
end
|
|
42
|
+
|
|
39
43
|
# @return [Hash] An entry for the response's "errors" key
|
|
40
44
|
def to_h
|
|
41
45
|
hash = {
|
|
@@ -28,7 +28,15 @@ module GraphQL
|
|
|
28
28
|
attr_reader :pos, :tokens_count
|
|
29
29
|
|
|
30
30
|
def advance
|
|
31
|
-
|
|
31
|
+
loop do
|
|
32
|
+
@scanner.skip(IGNORE_REGEXP)
|
|
33
|
+
if @scanner.skip(COMMENT_REGEXP)
|
|
34
|
+
@tokens_count += 1
|
|
35
|
+
next
|
|
36
|
+
end
|
|
37
|
+
break
|
|
38
|
+
end
|
|
39
|
+
|
|
32
40
|
if @scanner.eos?
|
|
33
41
|
@finished = true
|
|
34
42
|
return false
|
|
@@ -178,12 +186,8 @@ module GraphQL
|
|
|
178
186
|
raise GraphQL::ParseError.new(message, line, col, @string, filename: @filename)
|
|
179
187
|
end
|
|
180
188
|
|
|
181
|
-
IGNORE_REGEXP =
|
|
182
|
-
|
|
183
|
-
[, \c\r\n\t]+ |
|
|
184
|
-
\#.*$
|
|
185
|
-
)*
|
|
186
|
-
}x
|
|
189
|
+
IGNORE_REGEXP = /[, \c\r\n\t]+/
|
|
190
|
+
COMMENT_REGEXP = /\#[^\n]*/
|
|
187
191
|
IDENTIFIER_REGEXP = /[_A-Za-z][_0-9A-Za-z]*/
|
|
188
192
|
INT_REGEXP = /-?(?:[0]|[1-9][0-9]*)/
|
|
189
193
|
FLOAT_DECIMAL_REGEXP = /[.][0-9]+/
|
|
@@ -112,7 +112,7 @@ module GraphQL
|
|
|
112
112
|
# Return this value to tell the runtime
|
|
113
113
|
# to exclude this field from the response altogether
|
|
114
114
|
def skip
|
|
115
|
-
GraphQL::Execution::
|
|
115
|
+
GraphQL::Execution::Skip.new
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
# Add error at query-level.
|
|
@@ -126,6 +126,12 @@ module GraphQL
|
|
|
126
126
|
nil
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
+
# @param value [Object] Any object to be inserted directly into the final response
|
|
130
|
+
# @return [GraphQL::Execution::Interpreter::RawValue] Return this from the field
|
|
131
|
+
def raw_value(value)
|
|
132
|
+
GraphQL::Execution::Interpreter::RawValue.new(value)
|
|
133
|
+
end
|
|
134
|
+
|
|
129
135
|
# @example Print the GraphQL backtrace during field resolution
|
|
130
136
|
# puts ctx.backtrace
|
|
131
137
|
#
|
|
@@ -32,6 +32,7 @@ module GraphQL
|
|
|
32
32
|
@multiplex = nil
|
|
33
33
|
@result_values = nil
|
|
34
34
|
@result = nil
|
|
35
|
+
@finalizers = @top_level_finalizers = nil
|
|
35
36
|
|
|
36
37
|
if fragment_node
|
|
37
38
|
@ast_nodes = [fragment_node]
|
|
@@ -51,6 +52,10 @@ module GraphQL
|
|
|
51
52
|
@leaf
|
|
52
53
|
end
|
|
53
54
|
|
|
55
|
+
def root_value
|
|
56
|
+
object
|
|
57
|
+
end
|
|
58
|
+
|
|
54
59
|
attr_reader :context, :query, :ast_nodes, :root_type, :object, :field_definition, :path, :schema
|
|
55
60
|
|
|
56
61
|
attr_accessor :multiplex, :result_values
|
|
@@ -90,10 +95,22 @@ module GraphQL
|
|
|
90
95
|
@query.fragments
|
|
91
96
|
end
|
|
92
97
|
|
|
98
|
+
def validate
|
|
99
|
+
@query.validate
|
|
100
|
+
end
|
|
101
|
+
|
|
93
102
|
def valid?
|
|
94
103
|
@query.valid?
|
|
95
104
|
end
|
|
96
105
|
|
|
106
|
+
def query?
|
|
107
|
+
true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def run_partials(...)
|
|
111
|
+
@query.run_partials(...)
|
|
112
|
+
end
|
|
113
|
+
|
|
97
114
|
def analyzers
|
|
98
115
|
EmptyObjects::EMPTY_ARRAY
|
|
99
116
|
end
|
|
@@ -107,7 +124,7 @@ module GraphQL
|
|
|
107
124
|
end
|
|
108
125
|
|
|
109
126
|
def selected_operation
|
|
110
|
-
ast_nodes.
|
|
127
|
+
Language::Nodes::OperationDefinition.new(selections: ast_nodes.flat_map(&:selections))
|
|
111
128
|
end
|
|
112
129
|
|
|
113
130
|
def static_errors
|
|
@@ -123,7 +140,6 @@ module GraphQL
|
|
|
123
140
|
def set_type_info_from_path
|
|
124
141
|
selections = [@query.selected_operation]
|
|
125
142
|
type = @query.root_type
|
|
126
|
-
parent_type = nil
|
|
127
143
|
field_defn = nil
|
|
128
144
|
|
|
129
145
|
@path.each do |name_in_doc|
|
|
@@ -162,7 +178,6 @@ module GraphQL
|
|
|
162
178
|
end
|
|
163
179
|
field_name = next_selections.first.name
|
|
164
180
|
field_defn = @schema.get_field(type, field_name, @query.context) || raise("Invariant: no field called #{field_name} on #{type.graphql_name}")
|
|
165
|
-
parent_type = type
|
|
166
181
|
type = field_defn.type
|
|
167
182
|
if type.non_null?
|
|
168
183
|
type = type.of_type
|
data/lib/graphql/query.rb
CHANGED
|
@@ -159,6 +159,7 @@ module GraphQL
|
|
|
159
159
|
@root_value = root_value
|
|
160
160
|
@fragments = nil
|
|
161
161
|
@operations = nil
|
|
162
|
+
@finalizers = @top_level_finalizers = nil
|
|
162
163
|
@validate = validate
|
|
163
164
|
self.static_validator = static_validator if static_validator
|
|
164
165
|
context_tracers = (context ? context.fetch(:tracers, []) : [])
|
|
@@ -262,6 +263,10 @@ module GraphQL
|
|
|
262
263
|
with_prepared_ast { @operations }
|
|
263
264
|
end
|
|
264
265
|
|
|
266
|
+
def path
|
|
267
|
+
EmptyObjects::EMPTY_ARRAY
|
|
268
|
+
end
|
|
269
|
+
|
|
265
270
|
# Run subtree partials of this query and return their results.
|
|
266
271
|
# Each partial is identified with a `path:` and `object:`
|
|
267
272
|
# where the path references a field in the AST and the object will be treated
|
|
@@ -271,7 +276,11 @@ module GraphQL
|
|
|
271
276
|
# @return [Array<GraphQL::Query::Result>]
|
|
272
277
|
def run_partials(partials_hashes)
|
|
273
278
|
partials = partials_hashes.map { |partial_options| Partial.new(query: self, **partial_options) }
|
|
274
|
-
|
|
279
|
+
if context[:__graphql_execute_next]
|
|
280
|
+
Execution::Next.run_all(@schema, partials, context: @context)
|
|
281
|
+
else
|
|
282
|
+
Execution::Interpreter.run_all(@schema, partials, context: @context)
|
|
283
|
+
end
|
|
275
284
|
end
|
|
276
285
|
|
|
277
286
|
# Get the result for this query, executing it once
|
|
@@ -31,17 +31,25 @@ module GraphQL
|
|
|
31
31
|
|
|
32
32
|
def locations(*new_locations)
|
|
33
33
|
if !new_locations.empty?
|
|
34
|
+
is_runtime = false
|
|
34
35
|
new_locations.each do |new_loc|
|
|
35
|
-
|
|
36
|
+
loc_sym = new_loc.to_sym
|
|
37
|
+
if !LOCATIONS.include?(loc_sym)
|
|
36
38
|
raise ArgumentError, "#{self} (#{self.graphql_name}) has an invalid directive location: `locations #{new_loc}` "
|
|
37
39
|
end
|
|
40
|
+
is_runtime ||= RUNTIME_LOCATIONS.include?(loc_sym)
|
|
38
41
|
end
|
|
39
42
|
@locations = new_locations
|
|
43
|
+
@is_runtime = is_runtime
|
|
40
44
|
else
|
|
41
45
|
@locations ||= (superclass.respond_to?(:locations) ? superclass.locations : [])
|
|
42
46
|
end
|
|
43
47
|
end
|
|
44
48
|
|
|
49
|
+
def runtime?
|
|
50
|
+
@is_runtime
|
|
51
|
+
end
|
|
52
|
+
|
|
45
53
|
def default_directive(new_default_directive = nil)
|
|
46
54
|
if new_default_directive != nil
|
|
47
55
|
@default_directive = new_default_directive
|
|
@@ -104,8 +112,12 @@ module GraphQL
|
|
|
104
112
|
|
|
105
113
|
def inherited(subclass)
|
|
106
114
|
super
|
|
115
|
+
parent_class = self
|
|
107
116
|
subclass.class_exec do
|
|
108
117
|
@default_graphql_name ||= nil
|
|
118
|
+
@locations = parent_class.locations
|
|
119
|
+
@is_runtime = parent_class.runtime?
|
|
120
|
+
@repeatable = false
|
|
109
121
|
end
|
|
110
122
|
end
|
|
111
123
|
end
|
|
@@ -177,13 +189,16 @@ module GraphQL
|
|
|
177
189
|
end
|
|
178
190
|
|
|
179
191
|
LOCATIONS = [
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
192
|
+
*(RUNTIME_LOCATIONS = [
|
|
193
|
+
QUERY = :QUERY,
|
|
194
|
+
MUTATION = :MUTATION,
|
|
195
|
+
SUBSCRIPTION = :SUBSCRIPTION,
|
|
196
|
+
FIELD = :FIELD,
|
|
197
|
+
FRAGMENT_DEFINITION = :FRAGMENT_DEFINITION,
|
|
198
|
+
FRAGMENT_SPREAD = :FRAGMENT_SPREAD,
|
|
199
|
+
INLINE_FRAGMENT = :INLINE_FRAGMENT,
|
|
200
|
+
VARIABLE_DEFINITION = :VARIABLE_DEFINITION,
|
|
201
|
+
]),
|
|
187
202
|
SCHEMA = :SCHEMA,
|
|
188
203
|
SCALAR = :SCALAR,
|
|
189
204
|
OBJECT = :OBJECT,
|
|
@@ -195,7 +210,6 @@ module GraphQL
|
|
|
195
210
|
ENUM_VALUE = :ENUM_VALUE,
|
|
196
211
|
INPUT_OBJECT = :INPUT_OBJECT,
|
|
197
212
|
INPUT_FIELD_DEFINITION = :INPUT_FIELD_DEFINITION,
|
|
198
|
-
VARIABLE_DEFINITION = :VARIABLE_DEFINITION,
|
|
199
213
|
]
|
|
200
214
|
|
|
201
215
|
DEFAULT_DEPRECATION_REASON = 'No longer supported'
|
|
@@ -12,22 +12,13 @@ module GraphQL
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# Remove pagination args before passing it to a user method
|
|
15
|
-
def resolve(object
|
|
15
|
+
def resolve(object: nil, objects: nil, arguments:, context:)
|
|
16
16
|
next_args = arguments.dup
|
|
17
17
|
next_args.delete(:first)
|
|
18
18
|
next_args.delete(:last)
|
|
19
19
|
next_args.delete(:before)
|
|
20
20
|
next_args.delete(:after)
|
|
21
|
-
yield(object, next_args, arguments)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def resolve_next(objects:, arguments:, context:)
|
|
25
|
-
next_args = arguments.dup
|
|
26
|
-
next_args.delete(:first)
|
|
27
|
-
next_args.delete(:last)
|
|
28
|
-
next_args.delete(:before)
|
|
29
|
-
next_args.delete(:after)
|
|
30
|
-
yield(objects, next_args, arguments)
|
|
21
|
+
yield(object || objects, next_args, arguments)
|
|
31
22
|
end
|
|
32
23
|
|
|
33
24
|
def after_resolve(value:, object:, arguments:, context:, memo:)
|
|
@@ -36,10 +27,6 @@ module GraphQL
|
|
|
36
27
|
context.schema.connections.populate_connection(field, object.object, resolved_value, original_arguments, context)
|
|
37
28
|
end
|
|
38
29
|
end
|
|
39
|
-
|
|
40
|
-
def after_resolve_next(**kwargs)
|
|
41
|
-
raise "This should never be called -- it's hardcoded in execution instead."
|
|
42
|
-
end
|
|
43
30
|
end
|
|
44
31
|
end
|
|
45
32
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
|
@@ -197,6 +197,7 @@ module GraphQL
|
|
|
197
197
|
# @param resolve_batch [Symbol, true, nil] Used by {Schema.execute_next} map `objects` to a same-sized Array of results. Called on the owner type class with `objects, context, **arguments`.
|
|
198
198
|
# @param resolve_each [Symbol, true, nil] Used by {Schema.execute_next} to get a value value for each item. Called on the owner type class with `object, context, **arguments`.
|
|
199
199
|
# @param resolve_legacy_instance_method [Symbol, true, nil] Used by {Schema.execute_next} to get a value value for each item. Calls an instance method on the object type class.
|
|
200
|
+
# @param dataload [Class, Hash] Shorthand for making dataloader calls
|
|
200
201
|
# @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
|
|
201
202
|
# @param default_page_size [Integer, nil] For connections, the default number of items to return from this field, or `nil` to return unlimited results.
|
|
202
203
|
# @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
|
|
@@ -219,7 +220,7 @@ module GraphQL
|
|
|
219
220
|
# @param relay_nodes_field [Boolean] (Private, used by GraphQL-Ruby)
|
|
220
221
|
# @param extras [Array<:ast_node, :parent, :lookahead, :owner, :execution_errors, :graphql_name, :argument_details, Symbol>] Extra arguments to be injected into the resolver for this field
|
|
221
222
|
# @param definition_block [Proc] an additional block for configuring the field. Receive the field as a block param, or, if no block params are defined, then the block is `instance_eval`'d on the new {Field}.
|
|
222
|
-
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, resolve_legacy_instance_method: nil, resolve_static: nil, resolve_each: nil, resolve_batch: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
|
|
223
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, resolve_legacy_instance_method: nil, resolve_static: nil, resolve_each: nil, resolve_batch: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, dataload: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
|
|
223
224
|
if name.nil?
|
|
224
225
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
|
225
226
|
end
|
|
@@ -269,29 +270,32 @@ module GraphQL
|
|
|
269
270
|
@resolver_method = (resolver_method || name_s).to_sym
|
|
270
271
|
|
|
271
272
|
if resolve_static
|
|
272
|
-
@
|
|
273
|
-
@
|
|
273
|
+
@execution_mode = :resolve_static
|
|
274
|
+
@execution_mode_key = resolve_static == true ? @method_sym : resolve_static
|
|
274
275
|
elsif resolve_batch
|
|
275
|
-
@
|
|
276
|
-
@
|
|
276
|
+
@execution_mode = :resolve_batch
|
|
277
|
+
@execution_mode_key = resolve_batch == true ? @method_sym : resolve_batch
|
|
277
278
|
elsif resolve_each
|
|
278
|
-
@
|
|
279
|
-
@
|
|
279
|
+
@execution_mode = :resolve_each
|
|
280
|
+
@execution_mode_key = resolve_each == true ? @method_sym : resolve_each
|
|
280
281
|
elsif hash_key
|
|
281
|
-
@
|
|
282
|
-
@
|
|
282
|
+
@execution_mode = :hash_key
|
|
283
|
+
@execution_mode_key = hash_key
|
|
283
284
|
elsif dig
|
|
284
|
-
@
|
|
285
|
-
@
|
|
285
|
+
@execution_mode = :dig
|
|
286
|
+
@execution_mode_key = dig
|
|
286
287
|
elsif resolver_class
|
|
287
|
-
@
|
|
288
|
-
@
|
|
288
|
+
@execution_mode = :resolver_class
|
|
289
|
+
@execution_mode_key = resolver_class
|
|
289
290
|
elsif resolve_legacy_instance_method
|
|
290
|
-
@
|
|
291
|
-
@
|
|
291
|
+
@execution_mode = :resolve_legacy_instance_method
|
|
292
|
+
@execution_mode_key = resolve_legacy_instance_method == true ? @method_sym : resolve_legacy_instance_method
|
|
293
|
+
elsif dataload
|
|
294
|
+
@execution_mode = :dataload
|
|
295
|
+
@execution_mode_key = dataload
|
|
292
296
|
else
|
|
293
|
-
@
|
|
294
|
-
@
|
|
297
|
+
@execution_mode = :direct_send
|
|
298
|
+
@execution_mode_key = @method_sym
|
|
295
299
|
end
|
|
296
300
|
|
|
297
301
|
@complexity = complexity
|
|
@@ -365,7 +369,7 @@ module GraphQL
|
|
|
365
369
|
end
|
|
366
370
|
|
|
367
371
|
# @api private
|
|
368
|
-
attr_reader :
|
|
372
|
+
attr_reader :execution_mode_key, :execution_mode
|
|
369
373
|
|
|
370
374
|
# Calls the definition block, if one was given.
|
|
371
375
|
# This is deferred so that references to the return type
|
|
@@ -661,10 +665,9 @@ module GraphQL
|
|
|
661
665
|
end
|
|
662
666
|
|
|
663
667
|
def authorizes?(context)
|
|
664
|
-
method(:authorized?).owner != GraphQL::Schema::Field ||
|
|
665
|
-
(args = context.types.arguments(self)) &&
|
|
666
|
-
|
|
667
|
-
)
|
|
668
|
+
method(:authorized?).owner != GraphQL::Schema::Field ||
|
|
669
|
+
((args = context.types.arguments(self)) && (args.any? { |a| a.authorizes?(context) })) ||
|
|
670
|
+
(@resolver_class&.authorizes?(context)) || false
|
|
668
671
|
end
|
|
669
672
|
|
|
670
673
|
def authorized?(object, args, context)
|
|
@@ -926,7 +929,7 @@ ERR
|
|
|
926
929
|
def run_next_extensions_before_resolve(objs, args, ctx, extended, idx: 0, &block)
|
|
927
930
|
extension = @extensions[idx]
|
|
928
931
|
if extension
|
|
929
|
-
extension.
|
|
932
|
+
extension.resolve(objects: objs, arguments: args, context: ctx) do |extended_objs, extended_args, memo|
|
|
930
933
|
if memo
|
|
931
934
|
memos = extended.memos ||= {}
|
|
932
935
|
memos[idx] = memo
|
|
@@ -123,33 +123,16 @@ module GraphQL
|
|
|
123
123
|
#
|
|
124
124
|
# Whatever this method returns will be used for execution.
|
|
125
125
|
#
|
|
126
|
-
# @param object [Object] The object the field is being resolved on
|
|
126
|
+
# @param object [Object] The object the field is being resolved on (not passed by new execution)
|
|
127
|
+
# @param objects [Array<Object>] The objects the field is being resolved on (passed by new execution)
|
|
127
128
|
# @param arguments [Hash] Ruby keyword arguments for resolving this field
|
|
128
129
|
# @param context [Query::Context] the context for this query
|
|
129
|
-
# @yieldparam
|
|
130
|
+
# @yieldparam object_or_objects [Object, Array<Object>] The object or objects (new execution) to continue resolving the field on
|
|
130
131
|
# @yieldparam arguments [Hash] The keyword arguments to continue resolving with
|
|
131
132
|
# @yieldparam memo [Object] Any extension-specific value which will be passed to {#after_resolve} later
|
|
132
133
|
# @return [Object] The return value for this field.
|
|
133
|
-
def resolve(object
|
|
134
|
-
yield(object, arguments, nil)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Called before batch-resolving {#field}. It should either:
|
|
138
|
-
#
|
|
139
|
-
# - `yield` values to continue execution; OR
|
|
140
|
-
# - return something else to shortcut field execution.
|
|
141
|
-
#
|
|
142
|
-
# Whatever this method returns will be used for execution.
|
|
143
|
-
#
|
|
144
|
-
# @param objects [Array<Object>] The objects the field is being resolved on
|
|
145
|
-
# @param arguments [Hash] Ruby keyword arguments for resolving this field
|
|
146
|
-
# @param context [Query::Context] the context for this query
|
|
147
|
-
# @yieldparam objects [Array<Object>] The objects to continue resolving the field on. Length must be the same as passed-in `objects:`
|
|
148
|
-
# @yieldparam arguments [Hash] The keyword arguments to continue resolving with
|
|
149
|
-
# @yieldparam memo [Object] Any extension-specific value which will be passed to {#after_resolve} later
|
|
150
|
-
# @return [Array<Object>] The return value for this field, length matching passed-in `objects:`.
|
|
151
|
-
def resolve_next(objects:, arguments:, context:)
|
|
152
|
-
yield(objects, arguments, nil)
|
|
134
|
+
def resolve(object: nil, objects: nil, arguments:, context:)
|
|
135
|
+
yield(object.nil? ? objects : object, arguments, nil)
|
|
153
136
|
end
|
|
154
137
|
|
|
155
138
|
# Called after {#field} was resolved, and after any lazy values (like `Promise`s) were synced,
|
|
@@ -157,29 +140,16 @@ module GraphQL
|
|
|
157
140
|
#
|
|
158
141
|
# Whatever this hook returns will be used as the return value.
|
|
159
142
|
#
|
|
160
|
-
# @param object [Object] The object the field is being resolved on
|
|
143
|
+
# @param object [Object] The object the field is being resolved on (not passed by new execution)
|
|
144
|
+
# @param objects [Array<Object>] The object the field is being resolved on (passed by new execution)
|
|
161
145
|
# @param arguments [Hash] Ruby keyword arguments for resolving this field
|
|
162
146
|
# @param context [Query::Context] the context for this query
|
|
163
|
-
# @param value [Object] Whatever the field previously returned
|
|
147
|
+
# @param value [Object] Whatever the field previously returned (not passed by new execution)
|
|
148
|
+
# @param values [Array<Object>] Whatever the field previously returned (passed by new execution)
|
|
164
149
|
# @param memo [Object] The third value yielded by {#resolve}, or `nil` if there wasn't one
|
|
165
150
|
# @return [Object] The return value for this field.
|
|
166
|
-
def after_resolve(object
|
|
167
|
-
value
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Called after {#field} was batch-resolved, and after any lazy values (like `Promise`s) were synced,
|
|
171
|
-
# but before the value was added to the GraphQL response.
|
|
172
|
-
#
|
|
173
|
-
# Whatever this hook returns will be used as the return value.
|
|
174
|
-
#
|
|
175
|
-
# @param objects [Array<Object>] The objects the field is being resolved on
|
|
176
|
-
# @param arguments [Hash] Ruby keyword arguments for resolving this field
|
|
177
|
-
# @param context [Query::Context] the context for this query
|
|
178
|
-
# @param values [Array<Object>] Whatever the field returned, one for each of `objects`
|
|
179
|
-
# @param memo [Object] The third value yielded by {#resolve}, or `nil` if there wasn't one
|
|
180
|
-
# @return [Array<Object>] The return values for this field, length matching `objects:`.
|
|
181
|
-
def after_resolve_next(objects:, arguments:, context:, values:, memo:)
|
|
182
|
-
values
|
|
151
|
+
def after_resolve(object: nil, objects: nil, arguments:, context:, values: nil, value: nil, memo:)
|
|
152
|
+
value.nil? ? values : value
|
|
183
153
|
end
|
|
184
154
|
end
|
|
185
155
|
end
|
|
@@ -33,6 +33,26 @@ module GraphQL
|
|
|
33
33
|
self::DefinitionMethods.module_exec(&block)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Instance methods defined in this block will become class methods on objects that implement this interface.
|
|
37
|
+
# Use it to implement `resolve_each:`, `resolve_batch:`, and `resolve_static:` fields.
|
|
38
|
+
# @example
|
|
39
|
+
# field :thing, String, resolve_static: true
|
|
40
|
+
#
|
|
41
|
+
# resolver_methods do
|
|
42
|
+
# def thing
|
|
43
|
+
# Somehow.get.thing
|
|
44
|
+
# end
|
|
45
|
+
# end
|
|
46
|
+
def resolver_methods(&block)
|
|
47
|
+
if !defined?(@_resolver_methods)
|
|
48
|
+
resolver_methods_module = Module.new
|
|
49
|
+
@_resolver_methods = resolver_methods_module
|
|
50
|
+
const_set(:ResolverMethods, resolver_methods_module)
|
|
51
|
+
extend(self::ResolverMethods)
|
|
52
|
+
end
|
|
53
|
+
self::ResolverMethods.module_exec(&block)
|
|
54
|
+
end
|
|
55
|
+
|
|
36
56
|
# @see {Schema::Warden} hides interfaces without visible implementations
|
|
37
57
|
def visible?(context)
|
|
38
58
|
true
|
|
@@ -79,6 +99,12 @@ module GraphQL
|
|
|
79
99
|
if !backtrace_line
|
|
80
100
|
raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
|
|
81
101
|
end
|
|
102
|
+
|
|
103
|
+
child_class.ancestors.reverse_each do |ancestor|
|
|
104
|
+
if ancestor.const_defined?(:ResolverMethods)
|
|
105
|
+
child_class.extend(ancestor::ResolverMethods)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
82
108
|
end
|
|
83
109
|
|
|
84
110
|
super
|
data/lib/graphql/schema/list.rb
CHANGED
|
@@ -19,7 +19,11 @@ module GraphQL
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def to_type_signature
|
|
22
|
-
"[#{@of_type.to_type_signature}]"
|
|
22
|
+
@type_signature ||= -"[#{@of_type.to_type_signature}]"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def authorizes?(ctx)
|
|
26
|
+
of_type.authorizes?(ctx)
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
# This is for introspection, where it's expected the name will be `null`
|
|
@@ -26,16 +26,6 @@ module GraphQL
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
# Just a convenience method to point out that people should use graphql_name instead
|
|
30
|
-
def name(new_name = nil)
|
|
31
|
-
return super() if new_name.nil?
|
|
32
|
-
|
|
33
|
-
fail(
|
|
34
|
-
"The new name override method is `graphql_name`, not `name`. Usage: "\
|
|
35
|
-
"graphql_name \"#{new_name}\""
|
|
36
|
-
)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
29
|
# Call this method to provide a new description; OR
|
|
40
30
|
# call it without an argument to get the description
|
|
41
31
|
# @param new_description [String]
|
|
@@ -56,6 +56,16 @@ module GraphQL
|
|
|
56
56
|
source.load(find_by_value)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# @see dataload_record Like `dataload_record`, but accepts an Array of `find_by_values`
|
|
60
|
+
def dataload_all_records(model, find_by_values, find_by: nil)
|
|
61
|
+
source = if find_by
|
|
62
|
+
dataloader.with(Dataloader::ActiveRecordSource, model, find_by: find_by)
|
|
63
|
+
else
|
|
64
|
+
dataloader.with(Dataloader::ActiveRecordSource, model)
|
|
65
|
+
end
|
|
66
|
+
source.load_all(find_by_values)
|
|
67
|
+
end
|
|
68
|
+
|
|
59
69
|
# Look up an associated record using a Rails association (via {Dataloader::ActiveRecordAssociationSource})
|
|
60
70
|
# @param association_name [Symbol] A `belongs_to` or `has_one` association. (If a `has_many` association is named here, it will be selected without pagination.)
|
|
61
71
|
# @param record [ActiveRecord::Base] The object that the association belongs to.
|
|
@@ -73,6 +83,16 @@ module GraphQL
|
|
|
73
83
|
end
|
|
74
84
|
source.load(record)
|
|
75
85
|
end
|
|
86
|
+
|
|
87
|
+
# @see dataload_association Like `dataload_assocation` but accepts an Array of records (required param)
|
|
88
|
+
def dataload_all_associations(records, association_name, scope: nil)
|
|
89
|
+
source = if scope
|
|
90
|
+
dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name, scope)
|
|
91
|
+
else
|
|
92
|
+
dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name)
|
|
93
|
+
end
|
|
94
|
+
source.load_all(records)
|
|
95
|
+
end
|
|
76
96
|
end
|
|
77
97
|
end
|
|
78
98
|
end
|
|
@@ -48,6 +48,7 @@ module GraphQL
|
|
|
48
48
|
# @option kwargs [Boolean] :dynamic_introspection (Private, used by GraphQL-Ruby)
|
|
49
49
|
# @option kwargs [Boolean] :relay_node_field (Private, used by GraphQL-Ruby)
|
|
50
50
|
# @option kwargs [Boolean] :relay_nodes_field (Private, used by GraphQL-Ruby)
|
|
51
|
+
# @option kwargs [Class, Hash] :dataload Shorthand for dataloader lookups
|
|
51
52
|
# @option kwargs [Array<:ast_node, :parent, :lookahead, :owner, :execution_errors, :graphql_name, :argument_details, Symbol>] :extras Extra arguments to be injected into the resolver for this field
|
|
52
53
|
# @param kwargs [Hash] Keywords for defining the field. Any not documented here will be passed to your base field class where they must be handled.
|
|
53
54
|
# @param definition_block [Proc] an additional block for configuring the field. Receive the field as a block param, or, if no block params are defined, then the block is `instance_eval`'d on the new {Field}.
|
|
@@ -149,10 +150,14 @@ module GraphQL
|
|
|
149
150
|
|
|
150
151
|
def global_id_field(field_name, **kwargs)
|
|
151
152
|
type = self
|
|
152
|
-
field field_name, "ID", **kwargs, null: false
|
|
153
|
+
field field_name, "ID", **kwargs, null: false, resolve_each: true
|
|
153
154
|
define_method(field_name) do
|
|
154
155
|
context.schema.id_from_object(object, type, context)
|
|
155
156
|
end
|
|
157
|
+
|
|
158
|
+
define_singleton_method(field_name) do |object, context|
|
|
159
|
+
context.schema.id_from_object(object, type, context)
|
|
160
|
+
end
|
|
156
161
|
end
|
|
157
162
|
|
|
158
163
|
# @param new_has_no_fields [Boolean] Call with `true` to make this Object type ignore the requirement to have any defined fields.
|