graphql 0.18.10 → 0.18.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/graphql.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
|