graphql 0.18.10 → 0.18.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql.rb +1 -0
- data/lib/graphql/argument.rb +7 -11
- data/lib/graphql/base_type.rb +6 -0
- data/lib/graphql/boolean_type.rb +1 -4
- data/lib/graphql/enum_type.rb +3 -0
- data/lib/graphql/field.rb +14 -25
- data/lib/graphql/input_object_type.rb +3 -7
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/language/definition_slice.rb +29 -0
- data/lib/graphql/language/generation.rb +11 -0
- data/lib/graphql/language/nodes.rb +95 -8
- data/lib/graphql/language/token.rb +4 -0
- data/lib/graphql/schema.rb +5 -11
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/language/definition_slice_spec.rb +225 -0
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +16 -1
- 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: 1878fc3aef9f2315438da1dc338f7a6254ed83ca
|
4
|
+
data.tar.gz: 1f894a00b663c83b7b1bb33f2fda113dd8851f1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d14b02503088c21f6795c91a8b7eccfa7ed9d7cac10f06b388368ec3a1730e32e504166da603de22c2303a952af0778d1bb8190f0620df1bbb24dd9bb23a2c88
|
7
|
+
data.tar.gz: 790684129a750601ce3fb2cd71455ba1c6afece4f4be047f260161436e2cfca3f01c3d2cc9dd147f3f03eeb2bdac5d3c1bcb44acd420b40c99158e23eca650e7
|
data/lib/graphql.rb
CHANGED
data/lib/graphql/argument.rb
CHANGED
@@ -17,23 +17,19 @@ module GraphQL
|
|
17
17
|
class Argument
|
18
18
|
include GraphQL::Define::InstanceDefinable
|
19
19
|
accepts_definitions :name, :type, :description, :default_value
|
20
|
-
lazy_defined_attr_accessor :type, :description, :default_value
|
20
|
+
lazy_defined_attr_accessor :type, :description, :default_value, :name
|
21
21
|
|
22
|
-
#
|
23
|
-
|
24
|
-
ensure_defined
|
25
|
-
@name
|
26
|
-
end
|
27
|
-
|
28
|
-
attr_writer :name
|
22
|
+
# @!attribute name
|
23
|
+
# @return [String] The name of this argument on its {GraphQL::Field} or {GraphQL::InputObjectType}
|
29
24
|
|
30
|
-
|
25
|
+
# @param new_input_type [GraphQL::BaseType, Proc] Assign a new input type for this argument (if it's a proc, it will be called after schema initialization)
|
26
|
+
def type=(new_input_type)
|
31
27
|
ensure_defined
|
32
28
|
@clean_type = nil
|
33
|
-
@dirty_type =
|
29
|
+
@dirty_type = new_input_type
|
34
30
|
end
|
35
31
|
|
36
|
-
#
|
32
|
+
# @return [GraphQL::BaseType] the input type for this argument
|
37
33
|
def type
|
38
34
|
@clean_type ||= begin
|
39
35
|
ensure_defined
|
data/lib/graphql/base_type.rb
CHANGED
@@ -10,6 +10,12 @@ module GraphQL
|
|
10
10
|
|
11
11
|
lazy_defined_attr_accessor :name, :description
|
12
12
|
|
13
|
+
# @!attribute name
|
14
|
+
# @return [String] the name of this type, must be unique within a Schema
|
15
|
+
|
16
|
+
# @!attribute description
|
17
|
+
# @return [String, nil] a description for this type
|
18
|
+
|
13
19
|
# @param other [GraphQL::BaseType] compare to this object
|
14
20
|
# @return [Boolean] are these types equivalent? (incl. non-null, list)
|
15
21
|
def ==(other)
|
data/lib/graphql/boolean_type.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
GraphQL::BOOLEAN_TYPE = GraphQL::ScalarType.define do
|
2
|
-
# Everything else is nil
|
3
|
-
ALLOWED_INPUTS = [true, false]
|
4
|
-
|
5
2
|
name "Boolean"
|
6
3
|
description "Represents `true` or `false` values."
|
7
4
|
|
8
|
-
coerce_input -> (value) {
|
5
|
+
coerce_input -> (value) { (value == true || value == false) ? value : nil }
|
9
6
|
coerce_result -> (value) { !!value }
|
10
7
|
end
|
data/lib/graphql/enum_type.rb
CHANGED
@@ -63,17 +63,20 @@ module GraphQL
|
|
63
63
|
@values_by_value = {}
|
64
64
|
end
|
65
65
|
|
66
|
+
# @param new_values [Array<EnumValue>] The set of values contained in this type
|
66
67
|
def values=(new_values)
|
67
68
|
@values_by_name = {}
|
68
69
|
@values_by_value = {}
|
69
70
|
new_values.each { |enum_value| add_value(enum_value) }
|
70
71
|
end
|
71
72
|
|
73
|
+
# @param enum_value [EnumValue] A value to add to this type's set of values
|
72
74
|
def add_value(enum_value)
|
73
75
|
@values_by_name[enum_value.name] = enum_value
|
74
76
|
@values_by_value[enum_value.value] = enum_value
|
75
77
|
end
|
76
78
|
|
79
|
+
# @return [Hash<String => EnumValue>] `{name => value}` pairs contained in this type
|
77
80
|
def values
|
78
81
|
ensure_defined
|
79
82
|
@values_by_name
|
data/lib/graphql/field.rb
CHANGED
@@ -126,37 +126,23 @@ module GraphQL
|
|
126
126
|
argument: GraphQL::Define::AssignArgument
|
127
127
|
|
128
128
|
|
129
|
-
lazy_defined_attr_accessor :deprecation_reason, :description, :property, :hash_key, :mutation
|
129
|
+
lazy_defined_attr_accessor :name, :deprecation_reason, :description, :property, :hash_key, :mutation, :arguments, :complexity
|
130
130
|
|
131
|
-
#
|
131
|
+
# @!attribute [r] resolve_proc
|
132
|
+
# @return [<#call(obj, args,ctx)>] A proc-like object which can be called to return the field's value
|
132
133
|
attr_reader :resolve_proc
|
133
134
|
|
134
|
-
#
|
135
|
-
|
136
|
-
ensure_defined
|
137
|
-
@name
|
138
|
-
end
|
139
|
-
|
140
|
-
attr_writer :name
|
135
|
+
# @!attribute name
|
136
|
+
# @return [String] The name of this field on its {GraphQL::ObjectType} (or {GraphQL::InterfaceType})
|
141
137
|
|
142
|
-
#
|
143
|
-
|
144
|
-
ensure_defined
|
145
|
-
@arguments
|
146
|
-
end
|
147
|
-
|
148
|
-
attr_writer :arguments
|
138
|
+
# @!attribute arguments
|
139
|
+
# @return [Hash<String => GraphQL::Argument>] Map String argument names to their {GraphQL::Argument} implementations
|
149
140
|
|
150
141
|
# @!attribute mutation
|
151
142
|
# @return [GraphQL::Relay::Mutation, nil] The mutation this field was derived from, if it was derived from a mutation
|
152
143
|
|
153
|
-
#
|
154
|
-
|
155
|
-
ensure_defined
|
156
|
-
@complexity
|
157
|
-
end
|
158
|
-
|
159
|
-
attr_writer :complexity
|
144
|
+
# @!attribute complexity
|
145
|
+
# @return [Numeric, Proc] The complexity for this field (default: 1), as a constant or a proc like `-> (query_ctx, args, child_complexity) { } # Numeric`
|
160
146
|
|
161
147
|
def initialize
|
162
148
|
@complexity = 1
|
@@ -176,9 +162,12 @@ module GraphQL
|
|
176
162
|
resolve_proc.call(object, arguments, context)
|
177
163
|
end
|
178
164
|
|
179
|
-
|
165
|
+
# Provide a new callable for this field's resolve function. If `nil`,
|
166
|
+
# a new resolve proc will be build based on its {#name}, {#property} or {#hash_key}.
|
167
|
+
# @param new_resolve_proc [<#call(obj, args, ctx)>, nil]
|
168
|
+
def resolve=(new_resolve_proc)
|
180
169
|
ensure_defined
|
181
|
-
@resolve_proc =
|
170
|
+
@resolve_proc = new_resolve_proc || build_default_resolver
|
182
171
|
end
|
183
172
|
|
184
173
|
def type=(new_return_type)
|
@@ -29,19 +29,15 @@ module GraphQL
|
|
29
29
|
argument: GraphQL::Define::AssignArgument
|
30
30
|
)
|
31
31
|
|
32
|
-
lazy_defined_attr_accessor :mutation
|
32
|
+
lazy_defined_attr_accessor :mutation, :arguments
|
33
|
+
alias :input_fields :arguments
|
33
34
|
|
34
35
|
# @!attribute mutation
|
35
36
|
# @return [GraphQL::Relay::Mutation, nil] The mutation this field was derived from, if it was derived from a mutation
|
36
37
|
|
38
|
+
# @!attribute arguments
|
37
39
|
# @return [Hash<String => GraphQL::Argument>] Map String argument names to their {GraphQL::Argument} implementations
|
38
|
-
def arguments
|
39
|
-
ensure_defined
|
40
|
-
@arguments
|
41
|
-
end
|
42
|
-
attr_writer :arguments
|
43
40
|
|
44
|
-
alias :input_fields :arguments
|
45
41
|
|
46
42
|
def initialize
|
47
43
|
@arguments = {}
|
data/lib/graphql/language.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
module DefinitionSlice
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def slice(document, name)
|
7
|
+
definitions = {}
|
8
|
+
document.definitions.each { |d| definitions[d.name] = d }
|
9
|
+
names = find_definition_dependencies(definitions, name)
|
10
|
+
definitions = document.definitions.select { |d| names.include?(d.name) }
|
11
|
+
Nodes::Document.new(definitions: definitions)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def find_definition_dependencies(definitions, name)
|
17
|
+
names = Set.new([name])
|
18
|
+
visitor = Visitor.new(definitions[name])
|
19
|
+
visitor[Nodes::FragmentSpread] << -> (node, parent) {
|
20
|
+
if fragment = definitions[node.name]
|
21
|
+
names.merge(find_definition_dependencies(definitions, fragment.name))
|
22
|
+
end
|
23
|
+
}
|
24
|
+
visitor.visit
|
25
|
+
names
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,8 +1,19 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Language
|
3
|
+
# Exposes {.generate}, which turns AST nodes back into query strings.
|
3
4
|
module Generation
|
4
5
|
extend self
|
5
6
|
|
7
|
+
# Turn an AST node back into a string.
|
8
|
+
#
|
9
|
+
# @example Turning a document into a query
|
10
|
+
# document = GraphQL.parse(query_string)
|
11
|
+
# GraphQL::Language::Generation.generate(document)
|
12
|
+
# # => "{ ... }"
|
13
|
+
#
|
14
|
+
# @param node [GraphQL::Language::Nodes::AbstractNode] an AST node to recursively stringify
|
15
|
+
# @param indent [String] Whitespace to add to each printed node
|
16
|
+
# @return [String] Valid GraphQL for `node`
|
6
17
|
def generate(node, indent: "")
|
7
18
|
case node
|
8
19
|
when Nodes::Document
|
@@ -1,13 +1,18 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Language
|
3
3
|
module Nodes
|
4
|
-
# AbstractNode
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# {AbstractNode} is the base class for all nodes in a GraphQL AST.
|
5
|
+
#
|
6
|
+
# It provides some APIs for working with ASTs:
|
7
|
+
# - `children` returns all AST nodes attached to this one. Used for tree traversal.
|
8
|
+
# - `scalars` returns all scalar (Ruby) values attached to this one. Used for comparing nodes.
|
9
|
+
# - `to_query_string` turns an AST node into a GraphQL string
|
7
10
|
class AbstractNode
|
8
11
|
attr_accessor :line, :col
|
9
12
|
|
10
|
-
#
|
13
|
+
# Initialize a node by extracting its position,
|
14
|
+
# then calling the class's `initialize_node` method.
|
15
|
+
# @param options [Hash] Initial attributes for this node
|
11
16
|
def initialize(options={})
|
12
17
|
if options.key?(:position_source)
|
13
18
|
position_source = options.delete(:position_source)
|
@@ -25,6 +30,8 @@ module GraphQL
|
|
25
30
|
raise NotImplementedError
|
26
31
|
end
|
27
32
|
|
33
|
+
# Value equality
|
34
|
+
# @return [Boolean] True if `self` is equivalent to `other`
|
28
35
|
def eql?(other)
|
29
36
|
return true if equal?(other)
|
30
37
|
other.is_a?(self.class) &&
|
@@ -32,29 +39,34 @@ module GraphQL
|
|
32
39
|
other.children.eql?(self.children)
|
33
40
|
end
|
34
41
|
|
35
|
-
# @return [GraphQL::Language::Nodes::AbstractNode] all nodes in the tree below this one
|
42
|
+
# @return [Array<GraphQL::Language::Nodes::AbstractNode>] all nodes in the tree below this one
|
36
43
|
def children
|
37
44
|
self.class.child_attributes
|
38
45
|
.map { |attr_name| public_send(attr_name) }
|
39
46
|
.flatten
|
40
47
|
end
|
41
48
|
|
49
|
+
# @return [Array<Integer, Float, String, Boolean, Array>] Scalar values attached to this node
|
42
50
|
def scalars
|
43
51
|
self.class.scalar_attributes
|
44
52
|
.map { |attr_name| public_send(attr_name) }
|
45
53
|
end
|
46
54
|
|
47
55
|
class << self
|
56
|
+
# A node subclass inherits `scalar_attributes`
|
57
|
+
# and `child_attributes` from its parent
|
48
58
|
def inherited(subclass)
|
49
59
|
subclass.scalar_attributes(*@scalar_attributes)
|
50
60
|
subclass.child_attributes(*@child_attributes)
|
51
61
|
end
|
52
62
|
|
63
|
+
# define `attr_names` as places where scalars may be attached to this node
|
53
64
|
def scalar_attributes(*attr_names)
|
54
65
|
@scalar_attributes ||= []
|
55
66
|
@scalar_attributes += attr_names
|
56
67
|
end
|
57
68
|
|
69
|
+
# define `attr_names` as places where child nodes may be attached to this node
|
58
70
|
def child_attributes(*attr_names)
|
59
71
|
@child_attributes ||= []
|
60
72
|
@child_attributes += attr_names
|
@@ -70,6 +82,7 @@ module GraphQL
|
|
70
82
|
end
|
71
83
|
end
|
72
84
|
|
85
|
+
# Base class for non-null type names and list type names
|
73
86
|
class WrapperType < AbstractNode
|
74
87
|
attr_accessor :of_type
|
75
88
|
scalar_attributes :of_type
|
@@ -83,6 +96,7 @@ module GraphQL
|
|
83
96
|
end
|
84
97
|
end
|
85
98
|
|
99
|
+
# Base class for nodes whose only value is a name (no child nodes or other scalars)
|
86
100
|
class NameOnlyNode < AbstractNode
|
87
101
|
attr_accessor :name
|
88
102
|
scalar_attributes :name
|
@@ -96,11 +110,17 @@ module GraphQL
|
|
96
110
|
end
|
97
111
|
end
|
98
112
|
|
99
|
-
|
113
|
+
# A key-value pair for a field's inputs
|
100
114
|
class Argument < AbstractNode
|
101
115
|
attr_accessor :name, :value
|
102
116
|
scalar_attributes :name, :value
|
103
117
|
|
118
|
+
# @!attribute name
|
119
|
+
# @return [String] the key for this argument
|
120
|
+
|
121
|
+
# @!attribute value
|
122
|
+
# @return [String, Float, Integer, Boolean, Array, InputObject] The value passed for this key
|
123
|
+
|
104
124
|
def initialize_node(name: nil, value: nil)
|
105
125
|
@name = name
|
106
126
|
@value = value
|
@@ -122,22 +142,42 @@ module GraphQL
|
|
122
142
|
end
|
123
143
|
end
|
124
144
|
|
145
|
+
# This is the AST root for normal queries
|
146
|
+
#
|
147
|
+
# @example Deriving a document by parsing a string
|
148
|
+
# document = GraphQL.parse(query_string)
|
149
|
+
#
|
150
|
+
# @example Creating a string from a document
|
151
|
+
# document.to_query_string
|
152
|
+
# # { ... }
|
153
|
+
#
|
125
154
|
class Document < AbstractNode
|
126
155
|
attr_accessor :definitions
|
127
156
|
child_attributes :definitions
|
128
157
|
|
158
|
+
# @!attribute definitions
|
159
|
+
# @return [Array<OperationDefinition, FragmentDefinition>] top-level GraphQL units: operations or fragments
|
129
160
|
def initialize_node(definitions: [])
|
130
161
|
@definitions = definitions
|
131
162
|
end
|
163
|
+
|
164
|
+
def slice_definition(name)
|
165
|
+
GraphQL::Language::DefinitionSlice.slice(self, name)
|
166
|
+
end
|
132
167
|
end
|
133
168
|
|
169
|
+
# An enum value. The string is available as {#name}.
|
134
170
|
class Enum < NameOnlyNode; end
|
135
171
|
|
172
|
+
# A single selection in a GraphQL query.
|
136
173
|
class Field < AbstractNode
|
137
174
|
attr_accessor :name, :alias, :arguments, :directives, :selections
|
138
175
|
scalar_attributes :name, :alias
|
139
176
|
child_attributes :arguments, :directives, :selections
|
140
177
|
|
178
|
+
# @!attribute selections
|
179
|
+
# @return [Array<Nodes::Field>] Selections on this object (or empty array if this is a scalar field)
|
180
|
+
|
141
181
|
def initialize_node(name: nil, arguments: [], directives: [], selections: [], **kwargs)
|
142
182
|
@name = name
|
143
183
|
# oops, alias is a keyword:
|
@@ -148,11 +188,17 @@ module GraphQL
|
|
148
188
|
end
|
149
189
|
end
|
150
190
|
|
191
|
+
# A reusable fragment, defined at document-level.
|
151
192
|
class FragmentDefinition < AbstractNode
|
152
193
|
attr_accessor :name, :type, :directives, :selections
|
153
194
|
scalar_attributes :name, :type
|
154
195
|
child_attributes :directives, :selections
|
155
196
|
|
197
|
+
# @!attribute name
|
198
|
+
# @return [String] the identifier for this fragment, which may be applied with `...#{name}`
|
199
|
+
|
200
|
+
# @!attribute type
|
201
|
+
# @return [String] the type condition for this fragment (name of type which it may apply to)
|
156
202
|
def initialize_node(name: nil, type: nil, directives: [], selections: [])
|
157
203
|
@name = name
|
158
204
|
@type = type
|
@@ -161,22 +207,30 @@ module GraphQL
|
|
161
207
|
end
|
162
208
|
end
|
163
209
|
|
210
|
+
# Application of a named fragment in a selection
|
164
211
|
class FragmentSpread < AbstractNode
|
165
212
|
attr_accessor :name, :directives
|
166
213
|
scalar_attributes :name
|
167
214
|
child_attributes :directives
|
168
215
|
|
216
|
+
# @!attribute name
|
217
|
+
# @return [String] The identifier of the fragment to apply, corresponds with {FragmentDefinition#name}
|
218
|
+
|
169
219
|
def initialize_node(name: nil, directives: [])
|
170
220
|
@name = name
|
171
221
|
@directives = directives
|
172
222
|
end
|
173
223
|
end
|
174
224
|
|
225
|
+
# An unnamed fragment, defined directly in the query with `... { }`
|
175
226
|
class InlineFragment < AbstractNode
|
176
227
|
attr_accessor :type, :directives, :selections
|
177
228
|
scalar_attributes :type
|
178
229
|
child_attributes :directives, :selections
|
179
230
|
|
231
|
+
# @!attribute type
|
232
|
+
# @return [String, nil] Name of the type this fragment applies to, or `nil` if this fragment applies to any type
|
233
|
+
|
180
234
|
def initialize_node(type: nil, directives: [], selections: [])
|
181
235
|
@type = type
|
182
236
|
@directives = directives
|
@@ -184,14 +238,19 @@ module GraphQL
|
|
184
238
|
end
|
185
239
|
end
|
186
240
|
|
241
|
+
# A collection of key-value inputs which may be a field argument
|
187
242
|
class InputObject < AbstractNode
|
188
243
|
attr_accessor :arguments
|
189
244
|
child_attributes :arguments
|
190
245
|
|
246
|
+
# @!attribute arguments
|
247
|
+
# @return [Array<Nodes::Argument>] A list of key-value pairs inside this input object
|
248
|
+
|
191
249
|
def initialize_node(arguments: [])
|
192
250
|
@arguments = arguments
|
193
251
|
end
|
194
252
|
|
253
|
+
# @return [Hash<String, Any>] Recursively turn this input object into a Ruby Hash
|
195
254
|
def to_h(options={})
|
196
255
|
arguments.inject({}) do |memo, pair|
|
197
256
|
v = pair.value
|
@@ -202,15 +261,32 @@ module GraphQL
|
|
202
261
|
end
|
203
262
|
|
204
263
|
|
205
|
-
|
264
|
+
# A list type definition, denoted with `[...]` (used for variable type definitions)
|
206
265
|
class ListType < WrapperType; end
|
266
|
+
|
267
|
+
# A non-null type definition, denoted with `...!` (used for variable type definitions)
|
207
268
|
class NonNullType < WrapperType; end
|
208
269
|
|
270
|
+
# A query, mutation or subscription.
|
271
|
+
# May be anonymous or named.
|
272
|
+
# May be explicitly typed (eg `mutation { ... }`) or implicitly a query (eg `{ ... }`).
|
209
273
|
class OperationDefinition < AbstractNode
|
210
274
|
attr_accessor :operation_type, :name, :variables, :directives, :selections
|
211
275
|
scalar_attributes :operation_type, :name
|
212
276
|
child_attributes :variables, :directives, :selections
|
213
277
|
|
278
|
+
# @!attribute variables
|
279
|
+
# @return [Array<VariableDefinition>] Variable definitions for this operation
|
280
|
+
|
281
|
+
# @!attribute selections
|
282
|
+
# @return [Array<Field>] Root-level fields on this operation
|
283
|
+
|
284
|
+
# @!attribute operation_type
|
285
|
+
# @return [String, nil] The root type for this operation, or `nil` for implicit `"query"`
|
286
|
+
|
287
|
+
# @!attribute name
|
288
|
+
# @return [String, nil] The name for this operation, or `nil` if unnamed
|
289
|
+
|
214
290
|
def initialize_node(operation_type: nil, name: nil, variables: [], directives: [], selections: [])
|
215
291
|
@operation_type = operation_type
|
216
292
|
@name = name
|
@@ -220,12 +296,23 @@ module GraphQL
|
|
220
296
|
end
|
221
297
|
end
|
222
298
|
|
299
|
+
# A type name, used for variable definitions
|
223
300
|
class TypeName < NameOnlyNode; end
|
224
301
|
|
302
|
+
# An operation-level query variable
|
225
303
|
class VariableDefinition < AbstractNode
|
226
304
|
attr_accessor :name, :type, :default_value
|
227
305
|
scalar_attributes :name, :type, :default_value
|
228
306
|
|
307
|
+
# @!attribute default_value
|
308
|
+
# @return [String, Integer, Float, Boolean, Array] A Ruby value to use if no other value is provided
|
309
|
+
|
310
|
+
# @!attribute type
|
311
|
+
# @return [TypeName, NonNullType, ListType] The expected type of this value
|
312
|
+
|
313
|
+
# @!attribute name
|
314
|
+
# @return [String] The identifier for this variable, _without_ `$`
|
315
|
+
|
229
316
|
def initialize_node(name: nil, type: nil, default_value: nil)
|
230
317
|
@name = name
|
231
318
|
@type = type
|
@@ -233,9 +320,9 @@ module GraphQL
|
|
233
320
|
end
|
234
321
|
end
|
235
322
|
|
323
|
+
# Usage of a variable in a query. Name does _not_ include `$`.
|
236
324
|
class VariableIdentifier < NameOnlyNode; end
|
237
325
|
|
238
|
-
|
239
326
|
class SchemaDefinition < AbstractNode
|
240
327
|
attr_accessor :query, :mutation, :subscription
|
241
328
|
scalar_attributes :query, :mutation, :subscription
|
@@ -1,7 +1,11 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Language
|
3
|
+
# Emitted by the lexer and passed to the parser.
|
4
|
+
# Contains type, value and position data.
|
3
5
|
class Token
|
6
|
+
# @return [Symbol] The kind of token this is
|
4
7
|
attr_reader :name
|
8
|
+
|
5
9
|
def initialize(value:, name:, line:, col:)
|
6
10
|
@name = name
|
7
11
|
@value = value
|
data/lib/graphql/schema.rb
CHANGED
@@ -57,7 +57,7 @@ module GraphQL
|
|
57
57
|
:query, :mutation, :subscription,
|
58
58
|
:query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
|
59
59
|
:max_depth, :max_complexity,
|
60
|
-
:orphan_types,
|
60
|
+
:orphan_types, :node_identification,
|
61
61
|
:query_analyzers, :middleware
|
62
62
|
|
63
63
|
|
@@ -67,22 +67,16 @@ module GraphQL
|
|
67
67
|
|
68
68
|
attr_reader :directives, :static_validator
|
69
69
|
|
70
|
-
#
|
71
|
-
|
72
|
-
ensure_defined
|
73
|
-
@node_identification
|
74
|
-
end
|
70
|
+
# @!attribute node_identification
|
71
|
+
# @return [GraphQL::Relay::GlobalNodeIdentification] the node identification instance for this schema, when using Relay
|
75
72
|
|
76
73
|
def node_identification=(new_node_ident)
|
77
74
|
new_node_ident.schema = self
|
78
75
|
@node_identification = new_node_ident
|
79
76
|
end
|
80
77
|
|
81
|
-
#
|
82
|
-
|
83
|
-
ensure_defined
|
84
|
-
@middleware
|
85
|
-
end
|
78
|
+
# @!attribute [r] middleware
|
79
|
+
# @return [Array<#call>] Middlewares suitable for MiddlewareChain, applied to fields during execution
|
86
80
|
|
87
81
|
# @param query [GraphQL::ObjectType] the query root for the schema
|
88
82
|
# @param mutation [GraphQL::ObjectType] the mutation root for the schema
|
@@ -23,7 +23,7 @@ module GraphQL
|
|
23
23
|
|
24
24
|
field = parent_type.get_field(ast_field.name)
|
25
25
|
if field.nil?
|
26
|
-
context.errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'",
|
26
|
+
context.errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", ast_field, context: context)
|
27
27
|
return GraphQL::Language::Visitor::SKIP
|
28
28
|
end
|
29
29
|
end
|
data/lib/graphql/version.rb
CHANGED
@@ -0,0 +1,225 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GraphQL::Language::DefinitionSlice do
|
4
|
+
let(:document) { GraphQL::Language::Parser.parse(query_string) }
|
5
|
+
|
6
|
+
describe "anonymous query with no dependencies" do
|
7
|
+
let(:query_string) {%|
|
8
|
+
{
|
9
|
+
version
|
10
|
+
}
|
11
|
+
|}
|
12
|
+
|
13
|
+
it "is already the smallest slice" do
|
14
|
+
assert_equal document.to_query_string,
|
15
|
+
document.slice_definition(nil).to_query_string
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "anonymous mutation with no dependencies" do
|
20
|
+
let(:query_string) {%|
|
21
|
+
mutation {
|
22
|
+
ping {
|
23
|
+
message
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|}
|
27
|
+
|
28
|
+
it "is already the smallest slice" do
|
29
|
+
assert_equal document.to_query_string,
|
30
|
+
document.slice_definition(nil).to_query_string
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "anonymous fragment with no dependencies" do
|
35
|
+
let(:query_string) {%|
|
36
|
+
fragment on User {
|
37
|
+
name
|
38
|
+
}
|
39
|
+
|}
|
40
|
+
|
41
|
+
it "is already the smallest slice" do
|
42
|
+
assert_equal document.to_query_string,
|
43
|
+
document.slice_definition(nil).to_query_string
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "named query with no dependencies" do
|
48
|
+
let(:query_string) {%|
|
49
|
+
query getVersion {
|
50
|
+
version
|
51
|
+
}
|
52
|
+
|}
|
53
|
+
|
54
|
+
it "is already the smallest slice" do
|
55
|
+
assert_equal document.to_query_string,
|
56
|
+
document.slice_definition("getVersion").to_query_string
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "named fragment with no dependencies" do
|
61
|
+
let(:query_string) {%|
|
62
|
+
fragment profileFields on User {
|
63
|
+
firstName
|
64
|
+
lastName
|
65
|
+
}
|
66
|
+
|}
|
67
|
+
|
68
|
+
it "is already the smallest slice" do
|
69
|
+
assert_equal document.to_query_string,
|
70
|
+
document.slice_definition("profileFields").to_query_string
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "document with multiple queries but no subdependencies" do
|
75
|
+
let(:query_string) {%|
|
76
|
+
query getVersion {
|
77
|
+
version
|
78
|
+
}
|
79
|
+
|
80
|
+
query getTime {
|
81
|
+
time
|
82
|
+
}
|
83
|
+
|}
|
84
|
+
|
85
|
+
it "returns just the query definition" do
|
86
|
+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[0]]).to_query_string,
|
87
|
+
document.slice_definition("getVersion").to_query_string
|
88
|
+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[1]]).to_query_string,
|
89
|
+
document.slice_definition("getTime").to_query_string
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "document with multiple fragments but no subdependencies" do
|
94
|
+
let(:query_string) {%|
|
95
|
+
fragment profileFields on User {
|
96
|
+
firstName
|
97
|
+
lastName
|
98
|
+
}
|
99
|
+
|
100
|
+
fragment avatarFields on User {
|
101
|
+
avatarURL(size: 80)
|
102
|
+
}
|
103
|
+
|}
|
104
|
+
|
105
|
+
it "returns just the fragment definition" do
|
106
|
+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[0]]).to_query_string,
|
107
|
+
document.slice_definition("profileFields").to_query_string
|
108
|
+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[1]]).to_query_string,
|
109
|
+
document.slice_definition("avatarFields").to_query_string
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "query with missing spread" do
|
114
|
+
let(:query_string) {%|
|
115
|
+
query getUser {
|
116
|
+
viewer {
|
117
|
+
...profileFields
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|}
|
121
|
+
|
122
|
+
it "is ignored" do
|
123
|
+
assert_equal document.to_query_string,
|
124
|
+
document.slice_definition("getUser").to_query_string
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "query and fragment subdependency" do
|
129
|
+
let(:query_string) {%|
|
130
|
+
query getUser {
|
131
|
+
viewer {
|
132
|
+
...profileFields
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
fragment profileFields on User {
|
137
|
+
firstName
|
138
|
+
lastName
|
139
|
+
}
|
140
|
+
|}
|
141
|
+
|
142
|
+
it "returns query and fragment dependency" do
|
143
|
+
assert_equal document.to_query_string,
|
144
|
+
document.slice_definition("getUser").to_query_string
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "query and fragment nested subdependencies" do
|
149
|
+
let(:query_string) {%|
|
150
|
+
query getUser {
|
151
|
+
viewer {
|
152
|
+
...viewerInfo
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
fragment viewerInfo on User {
|
157
|
+
...profileFields
|
158
|
+
}
|
159
|
+
|
160
|
+
fragment profileFields on User {
|
161
|
+
firstName
|
162
|
+
lastName
|
163
|
+
...avatarFields
|
164
|
+
}
|
165
|
+
|
166
|
+
fragment avatarFields on User {
|
167
|
+
avatarURL(size: 80)
|
168
|
+
}
|
169
|
+
|}
|
170
|
+
|
171
|
+
it "returns query and all fragment dependencies" do
|
172
|
+
assert_equal document.to_query_string,
|
173
|
+
document.slice_definition("getUser").to_query_string
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "fragment subdependency referenced multiple times" do
|
178
|
+
let(:query_string) {%|
|
179
|
+
query getUser {
|
180
|
+
viewer {
|
181
|
+
...viewerInfo
|
182
|
+
...moreViewerInfo
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
fragment viewerInfo on User {
|
187
|
+
...profileFields
|
188
|
+
}
|
189
|
+
|
190
|
+
fragment moreViewerInfo on User {
|
191
|
+
...profileFields
|
192
|
+
}
|
193
|
+
|
194
|
+
fragment profileFields on User {
|
195
|
+
firstName
|
196
|
+
lastName
|
197
|
+
}
|
198
|
+
|}
|
199
|
+
|
200
|
+
it "is only returned once" do
|
201
|
+
assert_equal document.to_query_string,
|
202
|
+
document.slice_definition("getUser").to_query_string
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "query and unused fragment" do
|
207
|
+
let(:query_string) {%|
|
208
|
+
query getUser {
|
209
|
+
viewer {
|
210
|
+
id
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
fragment profileFields on User {
|
215
|
+
firstName
|
216
|
+
lastName
|
217
|
+
}
|
218
|
+
|}
|
219
|
+
|
220
|
+
it "returns just the query definition" do
|
221
|
+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[0]]).to_query_string,
|
222
|
+
document.slice_definition("getUser").to_query_string
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -26,6 +26,21 @@ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
|
|
26
26
|
assert_equal(expected_errors, error_messages)
|
27
27
|
end
|
28
28
|
|
29
|
+
describe "on objects" do
|
30
|
+
let(:query_string) { "query getStuff { notDefinedField }"}
|
31
|
+
|
32
|
+
it "finds invalid fields" do
|
33
|
+
expected_errors = [
|
34
|
+
{
|
35
|
+
"message"=>"Field 'notDefinedField' doesn't exist on type 'Query'",
|
36
|
+
"locations"=>[{"line"=>1, "column"=>18}],
|
37
|
+
"path"=>["query getStuff", "notDefinedField"],
|
38
|
+
}
|
39
|
+
]
|
40
|
+
assert_equal(expected_errors, errors)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
29
44
|
describe "on interfaces" do
|
30
45
|
let(:query_string) { "query getStuff { favoriteEdible { amountThatILikeIt } }"}
|
31
46
|
|
@@ -33,7 +48,7 @@ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
|
|
33
48
|
expected_errors = [
|
34
49
|
{
|
35
50
|
"message"=>"Field 'amountThatILikeIt' doesn't exist on type 'Edible'",
|
36
|
-
"locations"=>[{"line"=>1, "column"=>
|
51
|
+
"locations"=>[{"line"=>1, "column"=>35}],
|
37
52
|
"path"=>["query getStuff", "favoriteEdible", "amountThatILikeIt"],
|
38
53
|
}
|
39
54
|
]
|
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: 0.18.
|
4
|
+
version: 0.18.11
|
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-09-
|
11
|
+
date: 2016-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: codeclimate-test-reporter
|
@@ -304,6 +304,7 @@ files:
|
|
304
304
|
- lib/graphql/introspection/typename_field.rb
|
305
305
|
- lib/graphql/invalid_null_error.rb
|
306
306
|
- lib/graphql/language.rb
|
307
|
+
- lib/graphql/language/definition_slice.rb
|
307
308
|
- lib/graphql/language/generation.rb
|
308
309
|
- lib/graphql/language/lexer.rb
|
309
310
|
- lib/graphql/language/lexer.rl
|
@@ -413,6 +414,7 @@ files:
|
|
413
414
|
- spec/graphql/introspection/introspection_query_spec.rb
|
414
415
|
- spec/graphql/introspection/schema_type_spec.rb
|
415
416
|
- spec/graphql/introspection/type_type_spec.rb
|
417
|
+
- spec/graphql/language/definition_slice_spec.rb
|
416
418
|
- spec/graphql/language/equality_spec.rb
|
417
419
|
- spec/graphql/language/generation_spec.rb
|
418
420
|
- spec/graphql/language/nodes_spec.rb
|
@@ -524,6 +526,7 @@ test_files:
|
|
524
526
|
- spec/graphql/introspection/introspection_query_spec.rb
|
525
527
|
- spec/graphql/introspection/schema_type_spec.rb
|
526
528
|
- spec/graphql/introspection/type_type_spec.rb
|
529
|
+
- spec/graphql/language/definition_slice_spec.rb
|
527
530
|
- spec/graphql/language/equality_spec.rb
|
528
531
|
- spec/graphql/language/generation_spec.rb
|
529
532
|
- spec/graphql/language/nodes_spec.rb
|