graphql 2.0.13 → 2.0.15
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/templates/schema.erb +3 -0
- data/lib/graphql/backtrace/table.rb +2 -2
- data/lib/graphql/dataloader/source.rb +9 -0
- data/lib/graphql/dataloader.rb +4 -1
- data/lib/graphql/execution/interpreter/runtime.rb +23 -10
- data/lib/graphql/execution/interpreter.rb +185 -59
- data/lib/graphql/execution/lookahead.rb +39 -28
- data/lib/graphql/execution/multiplex.rb +1 -116
- data/lib/graphql/execution.rb +0 -1
- data/lib/graphql/introspection/type_type.rb +7 -0
- data/lib/graphql/introspection.rb +3 -2
- data/lib/graphql/language/document_from_schema_definition.rb +18 -18
- data/lib/graphql/language/printer.rb +20 -11
- data/lib/graphql/query/context.rb +19 -5
- data/lib/graphql/query.rb +1 -1
- data/lib/graphql/schema/build_from_definition.rb +32 -17
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/field.rb +3 -3
- data/lib/graphql/schema/input_object.rb +35 -0
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/member/build_type.rb +1 -1
- data/lib/graphql/schema/member/has_directives.rb +71 -56
- data/lib/graphql/schema/resolver/has_payload_type.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +3 -0
- data/lib/graphql/schema.rb +19 -7
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/literal_validator.rb +4 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/instrumentation_tracing.rb +83 -0
- data/lib/graphql/types/relay/node_behaviors.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +7 -4
- data/lib/graphql/execution/instrumentation.rb +0 -92
data/lib/graphql/execution.rb
CHANGED
@@ -29,6 +29,13 @@ module GraphQL
|
|
29
29
|
|
30
30
|
field :specifiedByURL, String, resolver_method: :specified_by_url
|
31
31
|
|
32
|
+
field :is_one_of, Boolean, null: false
|
33
|
+
|
34
|
+
def is_one_of
|
35
|
+
object.kind.input_object? &&
|
36
|
+
object.directives.any? { |d| d.graphql_name == "oneOf" }
|
37
|
+
end
|
38
|
+
|
32
39
|
def specified_by_url
|
33
40
|
if object.kind.scalar?
|
34
41
|
object.specified_by_url
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
module Introspection
|
4
|
-
def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false)
|
4
|
+
def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
|
5
5
|
# The introspection query to end all introspection queries, copied from
|
6
6
|
# https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js
|
7
|
-
<<-QUERY
|
7
|
+
<<-QUERY.gsub(/\n{2,}/, "\n")
|
8
8
|
query IntrospectionQuery {
|
9
9
|
__schema {
|
10
10
|
#{include_schema_description ? "description" : ""}
|
@@ -30,6 +30,7 @@ fragment FullType on __Type {
|
|
30
30
|
name
|
31
31
|
description
|
32
32
|
#{include_specified_by_url ? "specifiedByURL" : ""}
|
33
|
+
#{include_is_one_of ? "isOneOf" : ""}
|
33
34
|
fields(includeDeprecated: true) {
|
34
35
|
name
|
35
36
|
description
|
@@ -44,16 +44,18 @@ module GraphQL
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def build_schema_node
|
47
|
-
|
48
|
-
query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
|
49
|
-
mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
|
50
|
-
subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
|
51
|
-
# This only supports directives from parsing,
|
52
|
-
# use a custom printer to add to this list.
|
53
|
-
#
|
47
|
+
schema_options = {
|
54
48
|
# `@schema.directives` is covered by `build_definition_nodes`
|
55
|
-
directives:
|
56
|
-
|
49
|
+
directives: definition_directives(@schema, :schema_directives),
|
50
|
+
}
|
51
|
+
if !schema_respects_root_name_conventions?(@schema)
|
52
|
+
schema_options.merge!({
|
53
|
+
query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
|
54
|
+
mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
|
55
|
+
subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
|
56
|
+
})
|
57
|
+
end
|
58
|
+
GraphQL::Language::Nodes::SchemaDefinition.new(schema_options)
|
57
59
|
end
|
58
60
|
|
59
61
|
def build_object_type_node(object_type)
|
@@ -283,7 +285,9 @@ module GraphQL
|
|
283
285
|
private
|
284
286
|
|
285
287
|
def include_schema_node?
|
286
|
-
always_include_schema ||
|
288
|
+
always_include_schema ||
|
289
|
+
!schema_respects_root_name_conventions?(schema) ||
|
290
|
+
!schema.schema_directives.empty?
|
287
291
|
end
|
288
292
|
|
289
293
|
def schema_respects_root_name_conventions?(schema)
|
@@ -293,14 +297,14 @@ module GraphQL
|
|
293
297
|
end
|
294
298
|
|
295
299
|
def directives(member)
|
296
|
-
definition_directives(member)
|
300
|
+
definition_directives(member, :directives)
|
297
301
|
end
|
298
302
|
|
299
|
-
def definition_directives(member)
|
300
|
-
dirs = if !member.respond_to?(
|
303
|
+
def definition_directives(member, directives_method)
|
304
|
+
dirs = if !member.respond_to?(directives_method) || member.directives.empty?
|
301
305
|
[]
|
302
306
|
else
|
303
|
-
member.
|
307
|
+
member.public_send(directives_method).map do |dir|
|
304
308
|
args = []
|
305
309
|
dir.arguments.argument_values.each_value do |arg_value| # rubocop:disable Development/ContextIsPassedCop -- directive instance method
|
306
310
|
arg_defn = arg_value.definition
|
@@ -324,10 +328,6 @@ module GraphQL
|
|
324
328
|
dirs
|
325
329
|
end
|
326
330
|
|
327
|
-
def ast_directives(member)
|
328
|
-
member.ast_node ? member.ast_node.directives : []
|
329
|
-
end
|
330
|
-
|
331
331
|
attr_reader :schema, :warden, :always_include_schema,
|
332
332
|
:include_introspection_types, :include_built_in_directives, :include_built_in_scalars
|
333
333
|
end
|
@@ -132,10 +132,11 @@ module GraphQL
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def print_schema_definition(schema)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
135
|
+
has_conventional_names = (schema.query.nil? || schema.query == 'Query') &&
|
136
|
+
(schema.mutation.nil? || schema.mutation == 'Mutation') &&
|
137
|
+
(schema.subscription.nil? || schema.subscription == 'Subscription')
|
138
|
+
|
139
|
+
if has_conventional_names && schema.directives.empty?
|
139
140
|
return
|
140
141
|
end
|
141
142
|
|
@@ -145,14 +146,22 @@ module GraphQL
|
|
145
146
|
out << "\n "
|
146
147
|
out << print_node(dir)
|
147
148
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
149
|
+
if !has_conventional_names
|
150
|
+
out << "\n"
|
151
|
+
end
|
151
152
|
end
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
153
|
+
|
154
|
+
if !has_conventional_names
|
155
|
+
if schema.directives.empty?
|
156
|
+
out << " "
|
157
|
+
end
|
158
|
+
out << "{\n"
|
159
|
+
out << " query: #{schema.query}\n" if schema.query
|
160
|
+
out << " mutation: #{schema.mutation}\n" if schema.mutation
|
161
|
+
out << " subscription: #{schema.subscription}\n" if schema.subscription
|
162
|
+
out << "}"
|
163
|
+
end
|
164
|
+
out
|
156
165
|
end
|
157
166
|
|
158
167
|
def print_scalar_type_definition(scalar_type)
|
@@ -111,7 +111,7 @@ module GraphQL
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def current_path
|
114
|
-
@query_context
|
114
|
+
@query_context[:current_path] || @no_path
|
115
115
|
end
|
116
116
|
|
117
117
|
def key?(key)
|
@@ -189,12 +189,16 @@ module GraphQL
|
|
189
189
|
|
190
190
|
def_delegators :@query, :trace, :interpreter?
|
191
191
|
|
192
|
+
RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path])
|
192
193
|
# @!method []=(key, value)
|
193
194
|
# Reassign `key` to the hash passed to {Schema#execute} as `context:`
|
194
195
|
|
195
196
|
# Lookup `key` from the hash passed to {Schema#execute} as `context:`
|
196
197
|
def [](key)
|
197
|
-
if
|
198
|
+
if RUNTIME_METADATA_KEYS.include?(key)
|
199
|
+
thread_info = Thread.current[:__graphql_runtime_info]
|
200
|
+
thread_info && thread_info[key]
|
201
|
+
elsif @scoped_context.key?(key)
|
198
202
|
@scoped_context[key]
|
199
203
|
else
|
200
204
|
@provided_values[key]
|
@@ -212,7 +216,10 @@ module GraphQL
|
|
212
216
|
UNSPECIFIED_FETCH_DEFAULT = Object.new
|
213
217
|
|
214
218
|
def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
|
215
|
-
if
|
219
|
+
if RUNTIME_METADATA_KEYS.include?(key)
|
220
|
+
(thread_info = Thread.current[:__graphql_runtime_info]) &&
|
221
|
+
thread_info[key]
|
222
|
+
elsif @scoped_context.key?(key)
|
216
223
|
scoped_context[key]
|
217
224
|
elsif @provided_values.key?(key)
|
218
225
|
@provided_values[key]
|
@@ -226,7 +233,10 @@ module GraphQL
|
|
226
233
|
end
|
227
234
|
|
228
235
|
def dig(key, *other_keys)
|
229
|
-
if
|
236
|
+
if RUNTIME_METADATA_KEYS.include?(key)
|
237
|
+
(thread_info = Thread.current[:__graphql_runtime_info]).key?(key) &&
|
238
|
+
thread_info.dig(key)
|
239
|
+
elsif @scoped_context.key?(key)
|
230
240
|
@scoped_context.dig(key, *other_keys)
|
231
241
|
else
|
232
242
|
@provided_values.dig(key, *other_keys)
|
@@ -259,7 +269,11 @@ module GraphQL
|
|
259
269
|
# @param ns [Object] a usage-specific namespace identifier
|
260
270
|
# @return [Hash] namespaced storage
|
261
271
|
def namespace(ns)
|
262
|
-
|
272
|
+
if ns == :interpreter
|
273
|
+
self
|
274
|
+
else
|
275
|
+
@storage[ns]
|
276
|
+
end
|
263
277
|
end
|
264
278
|
|
265
279
|
# @return [Boolean] true if this namespace was accessed before
|
data/lib/graphql/query.rb
CHANGED
@@ -196,7 +196,7 @@ module GraphQL
|
|
196
196
|
# @return [Hash] A GraphQL response, with `"data"` and/or `"errors"` keys
|
197
197
|
def result
|
198
198
|
if !@executed
|
199
|
-
Execution::
|
199
|
+
Execution::Interpreter.run_all(@schema, [self], context: @context)
|
200
200
|
end
|
201
201
|
@result ||= Query::Result.new(query: self, values: @result_values)
|
202
202
|
end
|
@@ -6,16 +6,16 @@ module GraphQL
|
|
6
6
|
module BuildFromDefinition
|
7
7
|
class << self
|
8
8
|
# @see {Schema.from_definition}
|
9
|
-
def from_definition(definition_string, parser: GraphQL.default_parser, **kwargs)
|
10
|
-
from_document(parser.parse(definition_string), **kwargs)
|
9
|
+
def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
|
10
|
+
from_document(schema_superclass, parser.parse(definition_string), **kwargs)
|
11
11
|
end
|
12
12
|
|
13
|
-
def from_definition_path(definition_path, parser: GraphQL.default_parser, **kwargs)
|
14
|
-
from_document(parser.parse_file(definition_path), **kwargs)
|
13
|
+
def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs)
|
14
|
+
from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
|
15
15
|
end
|
16
16
|
|
17
|
-
def from_document(document, default_resolve:, using: {}, relay: false)
|
18
|
-
Builder.build(document, default_resolve: default_resolve || {}, relay: relay, using: using)
|
17
|
+
def from_document(schema_superclass, document, default_resolve:, using: {}, relay: false)
|
18
|
+
Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -23,7 +23,7 @@ module GraphQL
|
|
23
23
|
module Builder
|
24
24
|
extend self
|
25
25
|
|
26
|
-
def build(document, default_resolve:, using: {}, relay:)
|
26
|
+
def build(schema_superclass, document, default_resolve:, using: {}, relay:)
|
27
27
|
raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
|
28
28
|
|
29
29
|
if default_resolve.is_a?(Hash)
|
@@ -36,7 +36,7 @@ module GraphQL
|
|
36
36
|
end
|
37
37
|
schema_definition = schema_defns.first
|
38
38
|
types = {}
|
39
|
-
directives =
|
39
|
+
directives = schema_superclass.directives.dup
|
40
40
|
type_resolver = build_resolve_type(types, directives, ->(type_name) { types[type_name] ||= Schema::LateBoundType.new(type_name)})
|
41
41
|
# Make a different type resolver because we need to coerce directive arguments
|
42
42
|
# _while_ building the schema.
|
@@ -55,21 +55,24 @@ module GraphQL
|
|
55
55
|
end
|
56
56
|
})
|
57
57
|
|
58
|
+
directives.merge!(GraphQL::Schema.default_directives)
|
58
59
|
document.definitions.each do |definition|
|
59
60
|
if definition.is_a?(GraphQL::Language::Nodes::DirectiveDefinition)
|
60
61
|
directives[definition.name] = build_directive(definition, directive_type_resolver)
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
64
|
-
directives = GraphQL::Schema.default_directives.merge(directives)
|
65
|
-
|
66
65
|
# In case any directives referenced built-in types for their arguments:
|
67
66
|
replace_late_bound_types_with_built_in(types)
|
68
67
|
|
68
|
+
schema_extensions = nil
|
69
69
|
document.definitions.each do |definition|
|
70
70
|
case definition
|
71
71
|
when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
|
72
72
|
nil # already handled
|
73
|
+
when GraphQL::Language::Nodes::SchemaExtension
|
74
|
+
schema_extensions ||= []
|
75
|
+
schema_extensions << definition
|
73
76
|
else
|
74
77
|
# It's possible that this was already loaded by the directives
|
75
78
|
prev_type = types[definition.name]
|
@@ -104,7 +107,7 @@ module GraphQL
|
|
104
107
|
|
105
108
|
raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
|
106
109
|
|
107
|
-
Class.new(
|
110
|
+
schema_class = Class.new(schema_superclass) do
|
108
111
|
begin
|
109
112
|
# Add these first so that there's some chance of resolving late-bound types
|
110
113
|
orphan_types types.values
|
@@ -158,6 +161,14 @@ module GraphQL
|
|
158
161
|
child_class.definition_default_resolve = self.definition_default_resolve
|
159
162
|
end
|
160
163
|
end
|
164
|
+
|
165
|
+
if schema_extensions
|
166
|
+
schema_extensions.each do |ext|
|
167
|
+
build_directives(schema_class, ext, type_resolver)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
schema_class
|
161
172
|
end
|
162
173
|
|
163
174
|
NullResolveType = ->(type, obj, ctx) {
|
@@ -197,13 +208,18 @@ module GraphQL
|
|
197
208
|
|
198
209
|
def build_directives(definition, ast_node, type_resolver)
|
199
210
|
dirs = prepare_directives(ast_node, type_resolver)
|
200
|
-
dirs.each do |dir_class, options|
|
201
|
-
definition.
|
211
|
+
dirs.each do |(dir_class, options)|
|
212
|
+
if definition.respond_to?(:schema_directive)
|
213
|
+
# it's a schema
|
214
|
+
definition.schema_directive(dir_class, **options)
|
215
|
+
else
|
216
|
+
definition.directive(dir_class, **options)
|
217
|
+
end
|
202
218
|
end
|
203
219
|
end
|
204
220
|
|
205
221
|
def prepare_directives(ast_node, type_resolver)
|
206
|
-
dirs =
|
222
|
+
dirs = []
|
207
223
|
ast_node.directives.each do |dir_node|
|
208
224
|
if dir_node.name == "deprecated"
|
209
225
|
# This is handled using `deprecation_reason`
|
@@ -211,10 +227,10 @@ module GraphQL
|
|
211
227
|
else
|
212
228
|
dir_class = type_resolver.call(dir_node.name)
|
213
229
|
if dir_class.nil?
|
214
|
-
raise ArgumentError, "No definition for @#{dir_node.name} on #{ast_node.name} at #{ast_node.line}:#{ast_node.col}"
|
230
|
+
raise ArgumentError, "No definition for @#{dir_node.name} #{ast_node.respond_to?(:name) ? "on #{ast_node.name} " : ""}at #{ast_node.line}:#{ast_node.col}"
|
215
231
|
end
|
216
232
|
options = args_to_kwargs(dir_class, dir_node)
|
217
|
-
dirs[dir_class
|
233
|
+
dirs << [dir_class, options]
|
218
234
|
end
|
219
235
|
end
|
220
236
|
dirs
|
@@ -390,7 +406,6 @@ module GraphQL
|
|
390
406
|
graphql_name(interface_type_definition.name)
|
391
407
|
description(interface_type_definition.description)
|
392
408
|
interface_type_definition.interfaces.each do |interface_name|
|
393
|
-
"Implements: #{interface_type_definition} -> #{interface_name}"
|
394
409
|
interface_defn = type_resolver.call(interface_name)
|
395
410
|
implements(interface_defn)
|
396
411
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Directive < GraphQL::Schema::Member
|
5
|
+
class OneOf < GraphQL::Schema::Directive
|
6
|
+
description "Requires that exactly one field must be supplied and that field must not be `null`."
|
7
|
+
locations(GraphQL::Schema::Directive::INPUT_OBJECT)
|
8
|
+
default_directive true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -39,7 +39,7 @@ module GraphQL
|
|
39
39
|
transform_name = arguments[:by]
|
40
40
|
if TRANSFORMS.include?(transform_name) && return_value.respond_to?(transform_name)
|
41
41
|
return_value = return_value.public_send(transform_name)
|
42
|
-
response = context.namespace(:
|
42
|
+
response = context.namespace(:interpreter_runtime)[:runtime].final_result
|
43
43
|
*keys, last = path
|
44
44
|
keys.each do |key|
|
45
45
|
if response && (response = response[key])
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -129,10 +129,10 @@ module GraphQL
|
|
129
129
|
def connection?
|
130
130
|
if @connection.nil?
|
131
131
|
# Provide default based on type name
|
132
|
-
return_type_name = if @
|
133
|
-
Member::BuildType.to_type_name(@resolver_class.type)
|
134
|
-
elsif @return_type_expr
|
132
|
+
return_type_name = if @return_type_expr
|
135
133
|
Member::BuildType.to_type_name(@return_type_expr)
|
134
|
+
elsif @resolver_class && @resolver_class.type
|
135
|
+
Member::BuildType.to_type_name(@resolver_class.type)
|
136
136
|
else
|
137
137
|
# As a last ditch, try to force loading the return type:
|
138
138
|
type.unwrap.name
|
@@ -69,6 +69,19 @@ module GraphQL
|
|
69
69
|
true
|
70
70
|
end
|
71
71
|
|
72
|
+
def self.one_of
|
73
|
+
if !one_of?
|
74
|
+
if all_argument_definitions.any? { |arg| arg.type.non_null? }
|
75
|
+
raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
|
76
|
+
end
|
77
|
+
directive(GraphQL::Schema::Directive::OneOf)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.one_of?
|
82
|
+
directives.any? { |d| d.is_a?(GraphQL::Schema::Directive::OneOf) }
|
83
|
+
end
|
84
|
+
|
72
85
|
def unwrap_value(value)
|
73
86
|
case value
|
74
87
|
when Array
|
@@ -109,6 +122,14 @@ module GraphQL
|
|
109
122
|
class << self
|
110
123
|
def argument(*args, **kwargs, &block)
|
111
124
|
argument_defn = super(*args, **kwargs, &block)
|
125
|
+
if one_of?
|
126
|
+
if argument_defn.type.non_null?
|
127
|
+
raise ArgumentError, "Argument '#{argument_defn.path}' must be nullable because it is part of a OneOf type, add `required: false`."
|
128
|
+
end
|
129
|
+
if argument_defn.default_value?
|
130
|
+
raise ArgumentError, "Argument '#{argument_defn.path}' cannot have a default value because it is part of a OneOf type, remove `default_value: ...`."
|
131
|
+
end
|
132
|
+
end
|
112
133
|
# Add a method access
|
113
134
|
method_name = argument_defn.keyword
|
114
135
|
class_eval <<-RUBY, __FILE__, __LINE__
|
@@ -166,6 +187,20 @@ module GraphQL
|
|
166
187
|
end
|
167
188
|
end
|
168
189
|
|
190
|
+
if one_of?
|
191
|
+
if input.size == 1
|
192
|
+
input.each do |name, value|
|
193
|
+
if value.nil?
|
194
|
+
result ||= Query::InputValidationResult.new
|
195
|
+
result.add_problem("'#{graphql_name}' requires exactly one argument, but '#{name}' was `null`.")
|
196
|
+
end
|
197
|
+
end
|
198
|
+
else
|
199
|
+
result ||= Query::InputValidationResult.new
|
200
|
+
result.add_problem("'#{graphql_name}' requires exactly one argument, but #{input.size} were provided.")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
169
204
|
result
|
170
205
|
end
|
171
206
|
|
@@ -120,7 +120,7 @@ module GraphQL
|
|
120
120
|
def camelize(string)
|
121
121
|
return string if string == '_'
|
122
122
|
return string unless string.include?("_")
|
123
|
-
camelized = string.split('_').
|
123
|
+
camelized = string.split('_').each(&:capitalize!).join
|
124
124
|
camelized[0] = camelized[0].downcase
|
125
125
|
if (match_data = string.match(/\A(_+)/))
|
126
126
|
camelized = "#{match_data[0]}#{camelized}"
|
@@ -11,8 +11,7 @@ module GraphQL
|
|
11
11
|
# @return [void]
|
12
12
|
def directive(dir_class, **options)
|
13
13
|
@own_directives ||= []
|
14
|
-
|
15
|
-
@own_directives << dir_class.new(self, **options)
|
14
|
+
HasDirectives.add_directive(self, @own_directives, dir_class, options)
|
16
15
|
nil
|
17
16
|
end
|
18
17
|
|
@@ -20,78 +19,94 @@ module GraphQL
|
|
20
19
|
# @param dir_class [Class<GraphQL::Schema::Directive>]
|
21
20
|
# @return [viod]
|
22
21
|
def remove_directive(dir_class)
|
23
|
-
@own_directives
|
22
|
+
HasDirectives.remove_directive(@own_directives, dir_class)
|
24
23
|
nil
|
25
24
|
end
|
26
25
|
|
27
26
|
NO_DIRECTIVES = [].freeze
|
28
27
|
|
29
28
|
def directives
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
29
|
+
HasDirectives.get_directives(self, @own_directives, :directives)
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
def add_directive(schema_member, directives, directive_class, directive_options)
|
34
|
+
remove_directive(directives, directive_class) unless directive_class.repeatable?
|
35
|
+
directives << directive_class.new(schema_member, **directive_options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_directive(directives, directive_class)
|
39
|
+
directives && directives.reject! { |d| d.is_a?(directive_class) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_directives(schema_member, directives, directives_method)
|
43
|
+
case schema_member
|
44
|
+
when Class
|
45
|
+
inherited_directives = if schema_member.superclass.respond_to?(directives_method)
|
46
|
+
get_directives(schema_member.superclass, schema_member.superclass.public_send(directives_method), directives_method)
|
47
|
+
else
|
48
|
+
NO_DIRECTIVES
|
49
|
+
end
|
50
|
+
if inherited_directives.any? && directives
|
51
|
+
dirs = []
|
52
|
+
merge_directives(dirs, inherited_directives)
|
53
|
+
merge_directives(dirs, directives)
|
54
|
+
dirs
|
55
|
+
elsif directives
|
56
|
+
directives
|
57
|
+
elsif inherited_directives.any?
|
58
|
+
inherited_directives
|
59
|
+
else
|
60
|
+
NO_DIRECTIVES
|
61
|
+
end
|
62
|
+
when Module
|
63
|
+
dirs = nil
|
64
|
+
schema_member.ancestors.reverse_each do |ancestor|
|
65
|
+
if ancestor.respond_to?(:own_directives) &&
|
66
|
+
(anc_dirs = ancestor.own_directives).any?
|
67
|
+
dirs ||= []
|
68
|
+
merge_directives(dirs, anc_dirs)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
if directives
|
54
72
|
dirs ||= []
|
55
|
-
merge_directives(dirs,
|
73
|
+
merge_directives(dirs, directives)
|
56
74
|
end
|
75
|
+
dirs || NO_DIRECTIVES
|
76
|
+
when HasDirectives
|
77
|
+
directives || NO_DIRECTIVES
|
78
|
+
else
|
79
|
+
raise "Invariant: how could #{schema_member} not be a Class, Module, or instance of HasDirectives?"
|
57
80
|
end
|
58
|
-
|
59
|
-
|
60
|
-
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Modify `target` by adding items from `dirs` such that:
|
86
|
+
# - Any name conflict is overriden by the incoming member of `dirs`
|
87
|
+
# - Any other member of `dirs` is appended
|
88
|
+
# @param target [Array<GraphQL::Schema::Directive>]
|
89
|
+
# @param dirs [Array<GraphQL::Schema::Directive>]
|
90
|
+
# @return [void]
|
91
|
+
def merge_directives(target, dirs)
|
92
|
+
dirs.each do |dir|
|
93
|
+
if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name })
|
94
|
+
target.slice!(idx)
|
95
|
+
target.insert(idx, dir)
|
96
|
+
else
|
97
|
+
target << dir
|
98
|
+
end
|
61
99
|
end
|
62
|
-
|
63
|
-
when HasDirectives
|
64
|
-
@own_directives || NO_DIRECTIVES
|
65
|
-
else
|
66
|
-
raise "Invariant: how could #{self} not be a Class, Module, or instance of HasDirectives?"
|
100
|
+
nil
|
67
101
|
end
|
68
102
|
end
|
69
103
|
|
104
|
+
|
70
105
|
protected
|
71
106
|
|
72
107
|
def own_directives
|
73
108
|
@own_directives
|
74
109
|
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
# Modify `target` by adding items from `dirs` such that:
|
79
|
-
# - Any name conflict is overriden by the incoming member of `dirs`
|
80
|
-
# - Any other member of `dirs` is appended
|
81
|
-
# @param target [Array<GraphQL::Schema::Directive>]
|
82
|
-
# @param dirs [Array<GraphQL::Schema::Directive>]
|
83
|
-
# @return [void]
|
84
|
-
def merge_directives(target, dirs)
|
85
|
-
dirs.each do |dir|
|
86
|
-
if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name })
|
87
|
-
target.slice!(idx)
|
88
|
-
target.insert(idx, dir)
|
89
|
-
else
|
90
|
-
target << dir
|
91
|
-
end
|
92
|
-
end
|
93
|
-
nil
|
94
|
-
end
|
95
110
|
end
|
96
111
|
end
|
97
112
|
end
|
@@ -91,7 +91,7 @@ module GraphQL
|
|
91
91
|
resolver_fields = all_field_definitions
|
92
92
|
Class.new(object_class) do
|
93
93
|
graphql_name("#{resolver_name}Payload")
|
94
|
-
description("Autogenerated return type of #{resolver_name}")
|
94
|
+
description("Autogenerated return type of #{resolver_name}.")
|
95
95
|
resolver_fields.each do |f|
|
96
96
|
# Reattach the already-defined field here
|
97
97
|
# (The field's `.owner` will still point to the mutation, not the object type, I think)
|
@@ -11,6 +11,9 @@ module GraphQL
|
|
11
11
|
# @return [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
|
12
12
|
attr_reader :abstract_type
|
13
13
|
|
14
|
+
# @return [Hash]
|
15
|
+
attr_reader :options
|
16
|
+
|
14
17
|
# Called when an object is hooked up to an abstract type, such as {Schema::Union.possible_types}
|
15
18
|
# or {Schema::Object.implements} (for interfaces).
|
16
19
|
#
|