graphql 2.0.13 → 2.0.15
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/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
|
#
|