graphql 1.2.4 → 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/define/assign_object_field.rb +2 -2
- data/lib/graphql/define/instance_definable.rb +12 -15
- data/lib/graphql/field.rb +9 -2
- data/lib/graphql/input_object_type.rb +1 -2
- data/lib/graphql/language/visitor.rb +1 -10
- data/lib/graphql/query.rb +1 -0
- data/lib/graphql/query/arguments.rb +14 -10
- data/lib/graphql/relay/connection_field.rb +1 -1
- data/lib/graphql/relay/mutation.rb +7 -1
- data/lib/graphql/relay/node.rb +2 -6
- data/lib/graphql/schema.rb +16 -15
- data/lib/graphql/schema/instrumented_field_map.rb +14 -1
- data/lib/graphql/schema/validation.rb +1 -1
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/message.rb +7 -8
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +38 -0
- data/lib/graphql/static_validation/type_stack.rb +35 -12
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/define/instance_definable_spec.rb +16 -3
- data/spec/graphql/field_spec.rb +30 -0
- data/spec/graphql/input_object_type_spec.rb +12 -0
- data/spec/graphql/query/variables_spec.rb +45 -6
- data/spec/graphql/relay/connection_field_spec.rb +22 -0
- data/spec/graphql/relay/mutation_spec.rb +42 -0
- data/spec/graphql/schema_spec.rb +20 -2
- data/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb +180 -0
- data/spec/support/star_wars_schema.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b49f51c993d3f37fb6596a0547175b9cb1e6dfe0
|
4
|
+
data.tar.gz: 4e639bf70ca713dd8a795690a30b2eafca16c5be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ba6eec79d60b4e4fac5a3779dce895c9e757e3bd923b64d6d77b2702638ed2dcb6b57ae914a6be2e582cff6cad29a3f3f18662e90b7ff5076f56610c75c2519
|
7
|
+
data.tar.gz: 39e999428ae079e15e942852e295518bb242b6c4dcb65ca32f31cdcde7d2518427f34dbd39d5b92a8e72d70762f85d0e85079d4efe4d670bf2ea674f98e542f8
|
@@ -16,13 +16,13 @@ module GraphQL
|
|
16
16
|
# Figure out how to find or initialize the field instance:
|
17
17
|
if type_or_field.is_a?(GraphQL::Field)
|
18
18
|
field = type_or_field
|
19
|
-
field.name
|
19
|
+
field = field.redefine(name: name_s)
|
20
20
|
elsif block_given?
|
21
21
|
field = GraphQL::Field.define(kwargs, &block)
|
22
22
|
elsif field.nil?
|
23
23
|
field = GraphQL::Field.define(kwargs)
|
24
24
|
elsif field.is_a?(GraphQL::Field)
|
25
|
-
field.name
|
25
|
+
field = field.redefine(name: name_s)
|
26
26
|
else
|
27
27
|
raise("Couldn't find a field argument, received: #{field || type_or_field}")
|
28
28
|
end
|
@@ -100,7 +100,7 @@ module GraphQL
|
|
100
100
|
def redefine(**kwargs, &block)
|
101
101
|
ensure_defined
|
102
102
|
new_instance = self.class.new
|
103
|
-
applied_definitions.each { |defn| defn.
|
103
|
+
applied_definitions.each { |defn| new_instance.define(defn.define_keywords, &defn.define_proc) }
|
104
104
|
new_instance.define(**kwargs, &block)
|
105
105
|
new_instance
|
106
106
|
end
|
@@ -116,7 +116,16 @@ module GraphQL
|
|
116
116
|
if @pending_definition
|
117
117
|
defn = @pending_definition
|
118
118
|
@pending_definition = nil
|
119
|
-
|
119
|
+
defn_proxy = DefinedObjectProxy.new(self)
|
120
|
+
# Apply definition from `define(...)` kwargs
|
121
|
+
defn.define_keywords.each do |keyword, value|
|
122
|
+
defn_proxy.public_send(keyword, value)
|
123
|
+
end
|
124
|
+
# and/or apply definition from `define { ... }` block
|
125
|
+
if defn.define_proc
|
126
|
+
defn_proxy.instance_eval(&defn.define_proc)
|
127
|
+
end
|
128
|
+
|
120
129
|
applied_definitions << defn
|
121
130
|
end
|
122
131
|
nil
|
@@ -126,24 +135,12 @@ module GraphQL
|
|
126
135
|
@applied_definitions ||= []
|
127
136
|
end
|
128
137
|
|
129
|
-
|
130
138
|
class Definition
|
139
|
+
attr_reader :define_keywords, :define_proc
|
131
140
|
def initialize(define_keywords, define_proc)
|
132
141
|
@define_keywords = define_keywords
|
133
142
|
@define_proc = define_proc
|
134
143
|
end
|
135
|
-
|
136
|
-
def apply(instance)
|
137
|
-
defn_proxy = DefinedObjectProxy.new(instance)
|
138
|
-
|
139
|
-
@define_keywords.each do |keyword, value|
|
140
|
-
defn_proxy.public_send(keyword, value)
|
141
|
-
end
|
142
|
-
|
143
|
-
if @define_proc
|
144
|
-
defn_proxy.instance_eval(&@define_proc)
|
145
|
-
end
|
146
|
-
end
|
147
144
|
end
|
148
145
|
|
149
146
|
module ClassMethods
|
data/lib/graphql/field.rb
CHANGED
@@ -123,13 +123,19 @@ module GraphQL
|
|
123
123
|
accepts_definitions :name, :description, :deprecation_reason,
|
124
124
|
:resolve, :type, :arguments,
|
125
125
|
:property, :hash_key, :complexity, :mutation,
|
126
|
+
:relay_node_field,
|
126
127
|
argument: GraphQL::Define::AssignArgument
|
127
128
|
|
128
129
|
|
129
130
|
attr_accessor :name, :deprecation_reason, :description, :property, :hash_key, :mutation, :arguments, :complexity
|
131
|
+
|
132
|
+
# @return [Boolean] True if this is the Relay find-by-id field
|
133
|
+
attr_accessor :relay_node_field
|
134
|
+
|
130
135
|
ensure_defined(
|
131
|
-
:name, :deprecation_reason, :description, :property, :hash_key, :mutation, :arguments, :complexity,
|
132
|
-
:resolve, :resolve=, :type, :type=, :name=, :property=, :hash_key
|
136
|
+
:name, :deprecation_reason, :description, :description=, :property, :hash_key, :mutation, :arguments, :complexity,
|
137
|
+
:resolve, :resolve=, :type, :type=, :name=, :property=, :hash_key=,
|
138
|
+
:relay_node_field,
|
133
139
|
)
|
134
140
|
|
135
141
|
# @!attribute [r] resolve_proc
|
@@ -152,6 +158,7 @@ module GraphQL
|
|
152
158
|
@complexity = 1
|
153
159
|
@arguments = {}
|
154
160
|
@resolve_proc = build_default_resolver
|
161
|
+
@relay_node_field = false
|
155
162
|
end
|
156
163
|
|
157
164
|
# Get a value for this field
|
@@ -30,9 +30,8 @@ module GraphQL
|
|
30
30
|
)
|
31
31
|
|
32
32
|
attr_accessor :mutation, :arguments
|
33
|
-
alias :input_fields :arguments
|
34
|
-
|
35
33
|
ensure_defined(:mutation, :arguments)
|
34
|
+
alias :input_fields :arguments
|
36
35
|
|
37
36
|
# @!attribute mutation
|
38
37
|
# @return [GraphQL::Relay::Mutation, nil] The mutation this field was derived from, if it was derived from a mutation
|
@@ -17,16 +17,9 @@ module GraphQL
|
|
17
17
|
# node right away
|
18
18
|
SKIP = :_skip
|
19
19
|
|
20
|
-
# @return [Array<Proc>] Hooks to call when entering _any_ node
|
21
|
-
attr_reader :enter
|
22
|
-
# @return [Array<Proc>] Hooks to call when leaving _any_ node
|
23
|
-
attr_reader :leave
|
24
|
-
|
25
20
|
def initialize(document)
|
26
21
|
@document = document
|
27
22
|
@visitors = {}
|
28
|
-
@enter = []
|
29
|
-
@leave = []
|
30
23
|
end
|
31
24
|
|
32
25
|
# Get a {NodeVisitor} for `node_class`
|
@@ -50,20 +43,18 @@ module GraphQL
|
|
50
43
|
def visit_node(node, parent)
|
51
44
|
begin_hooks_ok = begin_visit(node, parent)
|
52
45
|
if begin_hooks_ok
|
53
|
-
node.children.
|
46
|
+
node.children.each { |child| visit_node(child, node) }
|
54
47
|
end
|
55
48
|
end_visit(node, parent)
|
56
49
|
end
|
57
50
|
|
58
51
|
def begin_visit(node, parent)
|
59
|
-
self.class.apply_hooks(enter, node, parent)
|
60
52
|
node_visitor = self[node.class]
|
61
53
|
self.class.apply_hooks(node_visitor.enter, node, parent)
|
62
54
|
end
|
63
55
|
|
64
56
|
# Should global `leave` visitors come first or last?
|
65
57
|
def end_visit(node, parent)
|
66
|
-
self.class.apply_hooks(leave, node, parent)
|
67
58
|
node_visitor = self[node.class]
|
68
59
|
self.class.apply_hooks(node_visitor.leave, node, parent)
|
69
60
|
end
|
data/lib/graphql/query.rb
CHANGED
@@ -60,19 +60,23 @@ module GraphQL
|
|
60
60
|
NULL_ARGUMENT_VALUE = ArgumentValue.new(nil, nil, nil)
|
61
61
|
|
62
62
|
def wrap_value(value, arg_defn_type)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
63
|
+
if value.nil?
|
64
|
+
nil
|
65
|
+
else
|
66
|
+
case arg_defn_type
|
67
|
+
when GraphQL::ListType
|
68
|
+
value.map { |item| wrap_value(item, arg_defn_type.of_type) }
|
69
|
+
when GraphQL::NonNullType
|
70
|
+
wrap_value(value, arg_defn_type.of_type)
|
71
|
+
when GraphQL::InputObjectType
|
72
|
+
if value.is_a?(Hash)
|
73
|
+
self.class.new(value, argument_definitions: arg_defn_type.arguments)
|
74
|
+
else
|
75
|
+
value
|
76
|
+
end
|
71
77
|
else
|
72
78
|
value
|
73
79
|
end
|
74
|
-
else
|
75
|
-
value
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
@@ -27,7 +27,7 @@ module GraphQL
|
|
27
27
|
# Turn A GraphQL::Field into a connection by:
|
28
28
|
# - Merging in the default arguments
|
29
29
|
# - Transforming its resolve function to return a connection object
|
30
|
-
# @param [GraphQL::Field] A field which returns nodes to be wrapped as a connection
|
30
|
+
# @param underlying_field [GraphQL::Field] A field which returns nodes to be wrapped as a connection
|
31
31
|
# @param max_page_size [Integer] The maximum number of nodes which may be requested (if a larger page is requested, it is limited to this number)
|
32
32
|
# @return [GraphQL::Field] The same field, modified to resolve to a connection object
|
33
33
|
def self.create(underlying_field, max_page_size: nil)
|
@@ -148,7 +148,7 @@ module GraphQL
|
|
148
148
|
attr_reader :client_mutation_id
|
149
149
|
def initialize(client_mutation_id:, result:)
|
150
150
|
@client_mutation_id = client_mutation_id
|
151
|
-
result.each do |key, value|
|
151
|
+
result && result.each do |key, value|
|
152
152
|
self.public_send("#{key}=", value)
|
153
153
|
end
|
154
154
|
end
|
@@ -175,6 +175,12 @@ module GraphQL
|
|
175
175
|
|
176
176
|
def call(obj, args, ctx)
|
177
177
|
mutation_result = @resolve.call(obj, args[:input], ctx)
|
178
|
+
|
179
|
+
if mutation_result.is_a?(GraphQL::ExecutionError)
|
180
|
+
ctx.add_error(mutation_result)
|
181
|
+
mutation_result = nil
|
182
|
+
end
|
183
|
+
|
178
184
|
if @wrap_result
|
179
185
|
@mutation.result_class.new(client_mutation_id: args[:input][:clientMutationId], result: mutation_result)
|
180
186
|
else
|
data/lib/graphql/relay/node.rb
CHANGED
@@ -7,17 +7,13 @@ module GraphQL
|
|
7
7
|
# We have to define it fresh each time because
|
8
8
|
# its name will be modified and its description
|
9
9
|
# _may_ be modified.
|
10
|
-
|
10
|
+
GraphQL::Field.define do
|
11
11
|
type(GraphQL::Relay::Node.interface)
|
12
12
|
description("Fetches an object given its ID.")
|
13
13
|
argument(:id, !types.ID, "ID of the object.")
|
14
14
|
resolve(GraphQL::Relay::Node::FindNode)
|
15
|
+
relay_node_field(true)
|
15
16
|
end
|
16
|
-
|
17
|
-
# This is used to identify generated fields in the schema
|
18
|
-
node_field.metadata[:relay_node_field] = true
|
19
|
-
|
20
|
-
node_field
|
21
17
|
end
|
22
18
|
|
23
19
|
# @return [GraphQL::InterfaceType] The interface which all Relay types must implement
|
data/lib/graphql/schema.rb
CHANGED
@@ -109,27 +109,24 @@ module GraphQL
|
|
109
109
|
ensure_defined
|
110
110
|
all_types = orphan_types + [query, mutation, subscription, GraphQL::Introspection::SchemaType]
|
111
111
|
@types = GraphQL::Schema::ReduceTypes.reduce(all_types.compact)
|
112
|
-
|
113
|
-
@instrumented_field_map = InstrumentedFieldMap.new(self)
|
114
|
-
field_instrumenters = @instrumenters[:field]
|
115
|
-
types.each do |type_name, type|
|
116
|
-
if type.kind.fields?
|
117
|
-
type.all_fields.each do |field_defn|
|
118
|
-
|
119
|
-
instrumented_field_defn = field_instrumenters.reduce(field_defn) do |defn, inst|
|
120
|
-
inst.instrument(type, defn)
|
121
|
-
end
|
122
|
-
|
123
|
-
@instrumented_field_map.set(type.name, field_defn.name, instrumented_field_defn)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
112
|
+
build_instrumented_field_map
|
127
113
|
# Assert that all necessary configs are present:
|
128
114
|
validation_error = Validation.validate(self)
|
129
115
|
validation_error && raise(NotImplementedError, validation_error)
|
130
116
|
nil
|
131
117
|
end
|
132
118
|
|
119
|
+
# Attach `instrumenter` to this schema for instrumenting events of `instrumentation_type`.
|
120
|
+
# @param instrumentation_type [Symbol]
|
121
|
+
# @param instrumenter
|
122
|
+
# @return [void]
|
123
|
+
def instrument(instrumentation_type, instrumenter)
|
124
|
+
@instrumenters[instrumentation_type] << instrumenter
|
125
|
+
if instrumentation_type == :field
|
126
|
+
build_instrumented_field_map
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
133
130
|
|
134
131
|
# @see [GraphQL::Schema::Warden] Restricted access to members of a schema
|
135
132
|
# @return [GraphQL::Schema::TypeMap] `{ name => type }` pairs of types in this schema
|
@@ -289,5 +286,9 @@ module GraphQL
|
|
289
286
|
middleware
|
290
287
|
end
|
291
288
|
end
|
289
|
+
|
290
|
+
def build_instrumented_field_map
|
291
|
+
@instrumented_field_map = InstrumentedFieldMap.new(self, @instrumenters[:field])
|
292
|
+
end
|
292
293
|
end
|
293
294
|
end
|
@@ -6,8 +6,21 @@ module GraphQL
|
|
6
6
|
#
|
7
7
|
# The catch is, the fields in this map _may_ have been modified by being instrumented.
|
8
8
|
class InstrumentedFieldMap
|
9
|
-
|
9
|
+
# Build a map using types from `schema` and instrumenters in `field_instrumenters`
|
10
|
+
# @param schema [GraphQL::Schema]
|
11
|
+
# @param field_instrumenters [Array<#instrument(type, field)>]
|
12
|
+
def initialize(schema, field_instrumenters)
|
10
13
|
@storage = Hash.new { |h, k| h[k] = {} }
|
14
|
+
schema.types.each do |type_name, type|
|
15
|
+
if type.kind.fields?
|
16
|
+
type.all_fields.each do |field_defn|
|
17
|
+
instrumented_field_defn = field_instrumenters.reduce(field_defn) do |defn, inst|
|
18
|
+
inst.instrument(type, defn)
|
19
|
+
end
|
20
|
+
self.set(type.name, field_defn.name, instrumented_field_defn)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
11
24
|
end
|
12
25
|
|
13
26
|
def set(type_name, field_name, field)
|
@@ -139,7 +139,7 @@ module GraphQL
|
|
139
139
|
}
|
140
140
|
|
141
141
|
SCHEMA_CAN_FETCH_IDS = ->(schema) {
|
142
|
-
has_node_field = schema.query && schema.query.all_fields.any?
|
142
|
+
has_node_field = schema.query && schema.query.all_fields.any?(&:relay_node_field)
|
143
143
|
if has_node_field && schema.object_from_id_proc.nil?
|
144
144
|
"schema contains `node(id:...)` field, so you must define a `object_from_id (id, ctx) -> { ... }` function"
|
145
145
|
else
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
8
8
|
ALL_RULES = [
|
9
9
|
GraphQL::StaticValidation::DirectivesAreDefined,
|
10
10
|
GraphQL::StaticValidation::DirectivesAreInValidLocations,
|
11
|
+
GraphQL::StaticValidation::UniqueDirectivesPerLocation,
|
11
12
|
GraphQL::StaticValidation::FragmentsAreFinite,
|
12
13
|
GraphQL::StaticValidation::FragmentsAreNamed,
|
13
14
|
GraphQL::StaticValidation::FragmentsAreUsed,
|
@@ -1,23 +1,22 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module StaticValidation
|
3
3
|
# Generates GraphQL-compliant validation message.
|
4
|
-
# Only supports one "location", too bad :(
|
5
4
|
class Message
|
6
5
|
# Convenience for validators
|
7
6
|
module MessageHelper
|
8
7
|
# Error `message` is located at `node`
|
9
|
-
def message(message,
|
8
|
+
def message(message, nodes, context: nil, path: nil)
|
10
9
|
path ||= context.path
|
11
|
-
|
10
|
+
nodes = Array(nodes)
|
11
|
+
GraphQL::StaticValidation::Message.new(message, nodes: nodes, path: path)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
attr_reader :message, :
|
15
|
+
attr_reader :message, :path
|
16
16
|
|
17
|
-
def initialize(message,
|
17
|
+
def initialize(message, path: [], nodes: [])
|
18
18
|
@message = message
|
19
|
-
@
|
20
|
-
@col = col
|
19
|
+
@nodes = nodes
|
21
20
|
@path = path
|
22
21
|
end
|
23
22
|
|
@@ -33,7 +32,7 @@ module GraphQL
|
|
33
32
|
private
|
34
33
|
|
35
34
|
def locations
|
36
|
-
@
|
35
|
+
@nodes.map{|node| {"line" => node.line, "column" => node.col}}
|
37
36
|
end
|
38
37
|
end
|
39
38
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module StaticValidation
|
3
|
+
class UniqueDirectivesPerLocation
|
4
|
+
include GraphQL::StaticValidation::Message::MessageHelper
|
5
|
+
|
6
|
+
NODES_WITH_DIRECTIVES = GraphQL::Language::Nodes.constants
|
7
|
+
.map{|c| GraphQL::Language::Nodes.const_get(c)}
|
8
|
+
.select{|c| c.is_a?(Class) && c.instance_methods.include?(:directives)}
|
9
|
+
|
10
|
+
def validate(context)
|
11
|
+
NODES_WITH_DIRECTIVES.each do |node_class|
|
12
|
+
context.visitor[node_class] << ->(node, _) {
|
13
|
+
validate_directives(node, context) unless node.directives.empty?
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def validate_directives(node, context)
|
21
|
+
used_directives = {}
|
22
|
+
|
23
|
+
node.directives.each do |ast_directive|
|
24
|
+
directive_name = ast_directive.name
|
25
|
+
if used_directives[directive_name]
|
26
|
+
context.errors << message(
|
27
|
+
"The directive \"#{directive_name}\" can only be used once at this location.",
|
28
|
+
[used_directives[directive_name], ast_directive],
|
29
|
+
context: context
|
30
|
+
)
|
31
|
+
else
|
32
|
+
used_directives[directive_name] = ast_directive
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -41,18 +41,15 @@ module GraphQL
|
|
41
41
|
@directive_definitions = []
|
42
42
|
@argument_definitions = []
|
43
43
|
@path = []
|
44
|
-
|
45
|
-
|
44
|
+
|
45
|
+
PUSH_STRATEGIES.each do |node_class, strategy|
|
46
|
+
visitor[node_class].enter << EnterWithStrategy.new(self, strategy)
|
47
|
+
visitor[node_class].leave << LeaveWithStrategy.new(self, strategy)
|
48
|
+
end
|
46
49
|
end
|
47
50
|
|
48
51
|
private
|
49
52
|
|
50
|
-
# Look up strategies by name and use singleton instance to push and pop
|
51
|
-
PUSH_STRATEGIES = Hash.new do |hash, key|
|
52
|
-
node_class_name = key.name.split("::").last
|
53
|
-
strategy_key = "#{node_class_name}Strategy"
|
54
|
-
hash[key] = const_defined?(strategy_key) ? const_get(strategy_key) : NullStrategy
|
55
|
-
end
|
56
53
|
|
57
54
|
module FragmentWithTypeStrategy
|
58
55
|
def push(stack, node)
|
@@ -182,10 +179,36 @@ module GraphQL
|
|
182
179
|
end
|
183
180
|
end
|
184
181
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
182
|
+
PUSH_STRATEGIES = {
|
183
|
+
GraphQL::Language::Nodes::FragmentDefinition => FragmentDefinitionStrategy,
|
184
|
+
GraphQL::Language::Nodes::InlineFragment => InlineFragmentStrategy,
|
185
|
+
GraphQL::Language::Nodes::FragmentSpread => FragmentSpreadStrategy,
|
186
|
+
GraphQL::Language::Nodes::Argument => ArgumentStrategy,
|
187
|
+
GraphQL::Language::Nodes::Field => FieldStrategy,
|
188
|
+
GraphQL::Language::Nodes::Directive => DirectiveStrategy,
|
189
|
+
GraphQL::Language::Nodes::OperationDefinition => OperationDefinitionStrategy,
|
190
|
+
}
|
191
|
+
|
192
|
+
class EnterWithStrategy
|
193
|
+
def initialize(stack, strategy)
|
194
|
+
@stack = stack
|
195
|
+
@strategy = strategy
|
196
|
+
end
|
197
|
+
|
198
|
+
def call(node, parent)
|
199
|
+
@strategy.push(@stack, node)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class LeaveWithStrategy
|
204
|
+
def initialize(stack, strategy)
|
205
|
+
@stack = stack
|
206
|
+
@strategy = strategy
|
207
|
+
end
|
208
|
+
|
209
|
+
def call(node, parent)
|
210
|
+
@strategy.pop(@stack, node)
|
211
|
+
end
|
189
212
|
end
|
190
213
|
end
|
191
214
|
end
|
data/lib/graphql/version.rb
CHANGED
@@ -18,6 +18,10 @@ module Garden
|
|
18
18
|
# definition added later:
|
19
19
|
attr_accessor :height
|
20
20
|
ensure_defined(:height)
|
21
|
+
|
22
|
+
def color
|
23
|
+
metadata[:color]
|
24
|
+
end
|
21
25
|
end
|
22
26
|
end
|
23
27
|
|
@@ -84,15 +88,24 @@ describe GraphQL::Define::InstanceDefinable do
|
|
84
88
|
name "Renamed Red Arugula"
|
85
89
|
end
|
86
90
|
|
87
|
-
assert_equal :green, arugula.
|
91
|
+
assert_equal :green, arugula.color
|
88
92
|
assert_equal "Arugula", arugula.name
|
89
93
|
|
90
|
-
assert_equal :red, red_arugula.
|
94
|
+
assert_equal :red, red_arugula.color
|
91
95
|
assert_equal "Arugula", red_arugula.name
|
92
96
|
|
93
|
-
assert_equal :red, renamed_red_arugula.
|
97
|
+
assert_equal :red, renamed_red_arugula.color
|
94
98
|
assert_equal "Renamed Red Arugula", renamed_red_arugula.name
|
95
99
|
end
|
100
|
+
|
101
|
+
it "can be chained several times" do
|
102
|
+
arugula_1 = Garden::Vegetable.define(name: "Arugula") { color :green }
|
103
|
+
arugula_2 = arugula_1.redefine { color :red }
|
104
|
+
arugula_3 = arugula_2.redefine { plant_between(1..3) }
|
105
|
+
assert_equal ["Arugula", :green], [arugula_1.name, arugula_1.color]
|
106
|
+
assert_equal ["Arugula", :red], [arugula_2.name, arugula_2.color]
|
107
|
+
assert_equal ["Arugula", :red], [arugula_3.name, arugula_3.color]
|
108
|
+
end
|
96
109
|
end
|
97
110
|
|
98
111
|
describe "#metadata" do
|
data/spec/graphql/field_spec.rb
CHANGED
@@ -120,4 +120,34 @@ describe GraphQL::Field do
|
|
120
120
|
assert_equal [:cheeses, :milks], similar_cheese_field.metadata[:joins]
|
121
121
|
end
|
122
122
|
end
|
123
|
+
|
124
|
+
describe "reusing a GraphQL::Field" do
|
125
|
+
let(:schema) {
|
126
|
+
int_field = GraphQL::Field.define do
|
127
|
+
type types.Int
|
128
|
+
argument :value, types.Int
|
129
|
+
resolve -> (obj, args, ctx) { args[:value] }
|
130
|
+
end
|
131
|
+
|
132
|
+
query_type = GraphQL::ObjectType.define do
|
133
|
+
name "Query"
|
134
|
+
field :int, int_field
|
135
|
+
field :int2, int_field
|
136
|
+
field :int3, field: int_field
|
137
|
+
end
|
138
|
+
|
139
|
+
GraphQL::Schema.define do
|
140
|
+
query(query_type)
|
141
|
+
end
|
142
|
+
}
|
143
|
+
|
144
|
+
it "can be used in two places" do
|
145
|
+
res = schema.execute %|{ int(value: 1) int2(value: 2) int3(value: 3) }|
|
146
|
+
assert_equal({ "int" => 1, "int2" => 2, "int3" => 3}, res["data"], "It works in queries")
|
147
|
+
|
148
|
+
res = schema.execute %|{ __type(name: "Query") { fields { name } } }|
|
149
|
+
query_field_names = res["data"]["__type"]["fields"].map { |f| f["name"] }
|
150
|
+
assert_equal ["int", "int2", "int3"], query_field_names, "It works in introspection"
|
151
|
+
end
|
152
|
+
end
|
123
153
|
end
|
@@ -10,6 +10,18 @@ describe GraphQL::InputObjectType do
|
|
10
10
|
assert(DairyProductInputType.input_fields["fatContent"])
|
11
11
|
end
|
12
12
|
|
13
|
+
describe "on a type unused by the schema" do
|
14
|
+
it "has input fields" do
|
15
|
+
UnreachedInputType = GraphQL::InputObjectType.define do
|
16
|
+
name 'UnreachedInputType'
|
17
|
+
description 'An input object type not directly used in the schema.'
|
18
|
+
|
19
|
+
input_field :field, types.String
|
20
|
+
end
|
21
|
+
assert(UnreachedInputType.input_fields['field'])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
13
25
|
describe "input validation" do
|
14
26
|
it "Accepts anything that yields key-value pairs to #all?" do
|
15
27
|
values_obj = MinimumInputObject.new({"source" => "COW", "fatContent" => 0.4})
|
@@ -2,30 +2,69 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe GraphQL::Query::Variables do
|
4
4
|
let(:query_string) {%|
|
5
|
-
query getCheese($animals: [DairyAnimal], $int: Int, $intWithDefault: Int = 10) {
|
5
|
+
query getCheese($animals: [DairyAnimal!], $int: Int, $intWithDefault: Int = 10) {
|
6
6
|
cheese(id: 1) {
|
7
7
|
similarCheese(source: $animals)
|
8
8
|
}
|
9
9
|
}
|
10
10
|
|}
|
11
11
|
let(:ast_variables) { GraphQL.parse(query_string).definitions.first.variables }
|
12
|
+
let(:schema) { DummySchema }
|
12
13
|
let(:variables) { GraphQL::Query::Variables.new(
|
13
|
-
|
14
|
-
GraphQL::Schema::Warden.new(
|
14
|
+
schema,
|
15
|
+
GraphQL::Schema::Warden.new(schema, GraphQL::Query::NullExcept),
|
15
16
|
ast_variables,
|
16
17
|
provided_variables)
|
17
18
|
}
|
18
19
|
|
19
20
|
describe "#initialize" do
|
20
21
|
describe "coercing inputs" do
|
21
|
-
let(:provided_variables) {
|
22
|
-
|
23
|
-
}
|
22
|
+
let(:provided_variables) { { "animals" => "YAK" } }
|
23
|
+
|
24
24
|
it "coerces single items into one-element lists" do
|
25
25
|
assert_equal ["YAK"], variables["animals"]
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
describe "nullable variables" do
|
30
|
+
let(:schema) { GraphQL::Schema.from_definition(%|
|
31
|
+
type Query {
|
32
|
+
thingsCount(ids: [ID!]): Int!
|
33
|
+
}
|
34
|
+
|)
|
35
|
+
}
|
36
|
+
let(:query_string) {%|
|
37
|
+
query getThingsCount($ids: [ID!]) {
|
38
|
+
thingsCount(ids: $ids)
|
39
|
+
}
|
40
|
+
|}
|
41
|
+
let(:result) {
|
42
|
+
schema.execute(query_string, variables: provided_variables, root_value: OpenStruct.new(thingsCount: 1))
|
43
|
+
}
|
44
|
+
|
45
|
+
describe "when they are present, but null" do
|
46
|
+
let(:provided_variables) { { "ids" => nil } }
|
47
|
+
it "ignores them" do
|
48
|
+
assert_equal 1, result["data"]["thingsCount"]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "when they are not present" do
|
53
|
+
let(:provided_variables) { {} }
|
54
|
+
it "ignores them" do
|
55
|
+
assert_equal 1, result["data"]["thingsCount"]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "when a nullable list has a null in it" do
|
60
|
+
let(:provided_variables) { { "ids" => [nil] } }
|
61
|
+
it "returns an error" do
|
62
|
+
assert_equal 1, result["errors"].length
|
63
|
+
assert_equal nil, result["data"]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
29
68
|
describe "coercing null" do
|
30
69
|
let(:provided_variables) {
|
31
70
|
{"int" => nil, "intWithDefault" => nil}
|
@@ -9,4 +9,26 @@ describe GraphQL::Relay::ConnectionField do
|
|
9
9
|
|
10
10
|
assert_equal ["tests"], test_type.fields.keys
|
11
11
|
end
|
12
|
+
|
13
|
+
it "leaves the original field untouched" do
|
14
|
+
test_type = nil
|
15
|
+
|
16
|
+
test_field = GraphQL::Field.define do
|
17
|
+
type(test_type.connection_type)
|
18
|
+
property(:something)
|
19
|
+
end
|
20
|
+
|
21
|
+
test_type = GraphQL::ObjectType.define do
|
22
|
+
name "Test"
|
23
|
+
connection :tests, test_field
|
24
|
+
end
|
25
|
+
|
26
|
+
conn_field = test_type.fields["tests"]
|
27
|
+
|
28
|
+
assert_equal 0, test_field.arguments.length
|
29
|
+
assert_equal 4, conn_field.arguments.length
|
30
|
+
|
31
|
+
assert_instance_of GraphQL::Field::Resolve::MethodResolve, test_field.resolve_proc
|
32
|
+
assert_instance_of GraphQL::Relay::ConnectionResolve, conn_field.resolve_proc
|
33
|
+
end
|
12
34
|
end
|
@@ -77,6 +77,23 @@ describe GraphQL::Relay::Mutation do
|
|
77
77
|
assert_equal IntroduceShipMutation, IntroduceShipMutation.result_class.mutation
|
78
78
|
end
|
79
79
|
|
80
|
+
describe "aliased methods" do
|
81
|
+
describe "on an unreached mutation" do
|
82
|
+
it 'still ensures definitions' do
|
83
|
+
UnreachedMutation = GraphQL::Relay::Mutation.define do
|
84
|
+
name 'UnreachedMutation'
|
85
|
+
description 'A mutation type not directly used in the schema.'
|
86
|
+
|
87
|
+
input_field :input, types.String
|
88
|
+
return_field :return, types.String
|
89
|
+
end
|
90
|
+
|
91
|
+
assert UnreachedMutation.input_fields['input']
|
92
|
+
assert UnreachedMutation.return_fields['return']
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
80
97
|
describe "providing a return type" do
|
81
98
|
let(:custom_return_type) {
|
82
99
|
GraphQL::ObjectType.define do
|
@@ -143,4 +160,29 @@ describe GraphQL::Relay::Mutation do
|
|
143
160
|
assert_equal "String", input.arguments['stringDefault'].default_value
|
144
161
|
end
|
145
162
|
end
|
163
|
+
|
164
|
+
describe "handling errors" do
|
165
|
+
it "supports returning an error in resolve" do
|
166
|
+
result = star_wars_query(query_string, "clientMutationId" => "5678", "shipName" => "Millennium Falcon")
|
167
|
+
|
168
|
+
expected = {
|
169
|
+
"data" => {
|
170
|
+
"introduceShip" => {
|
171
|
+
"clientMutationId" => "5678",
|
172
|
+
"shipEdge" => nil,
|
173
|
+
"faction" => nil,
|
174
|
+
}
|
175
|
+
},
|
176
|
+
"errors" => [
|
177
|
+
{
|
178
|
+
"message" => "Sorry, Millennium Falcon ship is reserved",
|
179
|
+
"locations" => [ { "line" => 3 , "column" => 7}],
|
180
|
+
"path" => ["introduceShip"]
|
181
|
+
}
|
182
|
+
]
|
183
|
+
}
|
184
|
+
|
185
|
+
assert_equal(expected, result)
|
186
|
+
end
|
187
|
+
end
|
146
188
|
end
|
data/spec/graphql/schema_spec.rb
CHANGED
@@ -217,6 +217,7 @@ type Query {
|
|
217
217
|
end
|
218
218
|
|
219
219
|
def after_query(query)
|
220
|
+
@counts << :end
|
220
221
|
end
|
221
222
|
end
|
222
223
|
|
@@ -228,7 +229,7 @@ type Query {
|
|
228
229
|
name "Query"
|
229
230
|
field :int, types.Int do
|
230
231
|
argument :value, types.Int
|
231
|
-
resolve -> (obj, args, ctx) { args[:value] }
|
232
|
+
resolve -> (obj, args, ctx) { args[:value] == 13 ? raise("13 is unlucky") : args[:value] }
|
232
233
|
end
|
233
234
|
end
|
234
235
|
}
|
@@ -250,7 +251,24 @@ type Query {
|
|
250
251
|
it "can wrap query execution" do
|
251
252
|
schema.execute("query getInt($val: Int = 5){ int(value: $val) } ")
|
252
253
|
schema.execute("query getInt($val: Int = 5, $val2: Int = 3){ int(value: $val) int2: int(value: $val2) } ")
|
253
|
-
assert_equal [1, 2], variable_counter.counts
|
254
|
+
assert_equal [1, :end, 2, :end], variable_counter.counts
|
255
|
+
end
|
256
|
+
|
257
|
+
it "runs even when a runtime error occurs" do
|
258
|
+
schema.execute("query getInt($val: Int = 5){ int(value: $val) } ")
|
259
|
+
assert_raises(RuntimeError) {
|
260
|
+
schema.execute("query getInt($val: Int = 13){ int(value: $val) } ")
|
261
|
+
}
|
262
|
+
assert_equal [1, :end, 1, :end], variable_counter.counts
|
263
|
+
end
|
264
|
+
|
265
|
+
it "can be applied after the fact" do
|
266
|
+
res = schema.execute("query { int(value: 2) } ")
|
267
|
+
assert_equal 6, res["data"]["int"]
|
268
|
+
|
269
|
+
schema.instrument(:field, MultiplyInstrumenter.new(4))
|
270
|
+
res = schema.execute("query { int(value: 2) } ")
|
271
|
+
assert_equal 24, res["data"]["int"]
|
254
272
|
end
|
255
273
|
end
|
256
274
|
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GraphQL::StaticValidation::UniqueDirectivesPerLocation do
|
4
|
+
include StaticValidationHelpers
|
5
|
+
|
6
|
+
let(:schema) { GraphQL::Schema.from_definition("
|
7
|
+
type Query {
|
8
|
+
type: Type
|
9
|
+
}
|
10
|
+
|
11
|
+
type Type {
|
12
|
+
field: String
|
13
|
+
}
|
14
|
+
|
15
|
+
directive @A on FIELD
|
16
|
+
directive @B on FIELD
|
17
|
+
") }
|
18
|
+
|
19
|
+
describe "query with no directives" do
|
20
|
+
let(:query_string) {"
|
21
|
+
{
|
22
|
+
type {
|
23
|
+
field
|
24
|
+
}
|
25
|
+
}
|
26
|
+
"}
|
27
|
+
|
28
|
+
it "passes rule" do
|
29
|
+
assert_equal [], errors
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "query with unique directives in different locations" do
|
34
|
+
let(:query_string) {"
|
35
|
+
{
|
36
|
+
type @A {
|
37
|
+
field @B
|
38
|
+
}
|
39
|
+
}
|
40
|
+
"}
|
41
|
+
|
42
|
+
it "passes rule" do
|
43
|
+
assert_equal [], errors
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "query with unique directives in same locations" do
|
48
|
+
let(:query_string) {"
|
49
|
+
{
|
50
|
+
type @A @B {
|
51
|
+
field @A @B
|
52
|
+
}
|
53
|
+
}
|
54
|
+
"}
|
55
|
+
|
56
|
+
it "passes rule" do
|
57
|
+
assert_equal [], errors
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "query with same directives in different locations" do
|
62
|
+
let(:query_string) {"
|
63
|
+
{
|
64
|
+
type @A {
|
65
|
+
field @A
|
66
|
+
}
|
67
|
+
}
|
68
|
+
"}
|
69
|
+
|
70
|
+
it "passes rule" do
|
71
|
+
assert_equal [], errors
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "query with same directives in similar locations" do
|
76
|
+
let(:query_string) {"
|
77
|
+
{
|
78
|
+
type {
|
79
|
+
field @A
|
80
|
+
field @A
|
81
|
+
}
|
82
|
+
}
|
83
|
+
"}
|
84
|
+
|
85
|
+
it "passes rule" do
|
86
|
+
assert_equal [], errors
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "query with duplicate directives in one location" do
|
91
|
+
let(:query_string) {"
|
92
|
+
{
|
93
|
+
type {
|
94
|
+
field @A @A
|
95
|
+
}
|
96
|
+
}
|
97
|
+
"}
|
98
|
+
|
99
|
+
it "fails rule" do
|
100
|
+
assert_includes errors, {
|
101
|
+
"message" => 'The directive "A" can only be used once at this location.',
|
102
|
+
"locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 20 }],
|
103
|
+
"fields" => ["query", "type", "field"],
|
104
|
+
}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
describe "query with many duplicate directives in one location" do
|
110
|
+
let(:query_string) {"
|
111
|
+
{
|
112
|
+
type {
|
113
|
+
field @A @A @A
|
114
|
+
}
|
115
|
+
}
|
116
|
+
"}
|
117
|
+
|
118
|
+
it "fails rule" do
|
119
|
+
assert_includes errors, {
|
120
|
+
"message" => 'The directive "A" can only be used once at this location.',
|
121
|
+
"locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 20 }],
|
122
|
+
"fields" => ["query", "type", "field"],
|
123
|
+
}
|
124
|
+
|
125
|
+
assert_includes errors, {
|
126
|
+
"message" => 'The directive "A" can only be used once at this location.',
|
127
|
+
"locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 23 }],
|
128
|
+
"fields" => ["query", "type", "field"],
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "query with different duplicate directives in one location" do
|
134
|
+
let(:query_string) {"
|
135
|
+
{
|
136
|
+
type {
|
137
|
+
field @A @B @A @B
|
138
|
+
}
|
139
|
+
}
|
140
|
+
"}
|
141
|
+
|
142
|
+
it "fails rule" do
|
143
|
+
assert_includes errors, {
|
144
|
+
"message" => 'The directive "A" can only be used once at this location.',
|
145
|
+
"locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 23 }],
|
146
|
+
"fields" => ["query", "type", "field"],
|
147
|
+
}
|
148
|
+
|
149
|
+
assert_includes errors, {
|
150
|
+
"message" => 'The directive "B" can only be used once at this location.',
|
151
|
+
"locations" => [{ "line" => 4, "column" => 20 }, { "line" => 4, "column" => 26 }],
|
152
|
+
"fields" => ["query", "type", "field"],
|
153
|
+
}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "query with duplicate directives in many locations" do
|
158
|
+
let(:query_string) {"
|
159
|
+
{
|
160
|
+
type @A @A {
|
161
|
+
field @A @A
|
162
|
+
}
|
163
|
+
}
|
164
|
+
"}
|
165
|
+
|
166
|
+
it "fails rule" do
|
167
|
+
assert_includes errors, {
|
168
|
+
"message" => 'The directive "A" can only be used once at this location.',
|
169
|
+
"locations" => [{ "line" => 3, "column" => 14 }, { "line" => 3, "column" => 17 }],
|
170
|
+
"fields" => ["query", "type"],
|
171
|
+
}
|
172
|
+
|
173
|
+
assert_includes errors, {
|
174
|
+
"message" => 'The directive "A" can only be used once at this location.',
|
175
|
+
"locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 20 }],
|
176
|
+
"fields" => ["query", "type", "field"],
|
177
|
+
}
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -157,6 +157,7 @@ IntroduceShipMutation = GraphQL::Relay::Mutation.define do
|
|
157
157
|
# Here's the mutation operation:
|
158
158
|
resolve ->(root_obj, inputs, ctx) {
|
159
159
|
faction_id = inputs["factionId"]
|
160
|
+
return GraphQL::ExecutionError.new("Sorry, Millennium Falcon ship is reserved") if inputs["shipName"] == 'Millennium Falcon'
|
160
161
|
ship = STAR_WARS_DATA.create_ship(inputs["shipName"], faction_id)
|
161
162
|
faction = STAR_WARS_DATA["Faction"][faction_id]
|
162
163
|
connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(faction.ships)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-11-
|
11
|
+
date: 2016-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: codeclimate-test-reporter
|
@@ -407,6 +407,7 @@ files:
|
|
407
407
|
- lib/graphql/static_validation/rules/mutation_root_exists.rb
|
408
408
|
- lib/graphql/static_validation/rules/required_arguments_are_present.rb
|
409
409
|
- lib/graphql/static_validation/rules/subscription_root_exists.rb
|
410
|
+
- lib/graphql/static_validation/rules/unique_directives_per_location.rb
|
410
411
|
- lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb
|
411
412
|
- lib/graphql/static_validation/rules/variable_usages_are_allowed.rb
|
412
413
|
- lib/graphql/static_validation/rules/variables_are_input_types.rb
|
@@ -504,6 +505,7 @@ files:
|
|
504
505
|
- spec/graphql/static_validation/rules/mutation_root_exists_spec.rb
|
505
506
|
- spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb
|
506
507
|
- spec/graphql/static_validation/rules/subscription_root_exists_spec.rb
|
508
|
+
- spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb
|
507
509
|
- spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb
|
508
510
|
- spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb
|
509
511
|
- spec/graphql/static_validation/rules/variables_are_input_types_spec.rb
|
@@ -628,6 +630,7 @@ test_files:
|
|
628
630
|
- spec/graphql/static_validation/rules/mutation_root_exists_spec.rb
|
629
631
|
- spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb
|
630
632
|
- spec/graphql/static_validation/rules/subscription_root_exists_spec.rb
|
633
|
+
- spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb
|
631
634
|
- spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb
|
632
635
|
- spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb
|
633
636
|
- spec/graphql/static_validation/rules/variables_are_input_types_spec.rb
|