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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e1f410073bc1513435fe5551135544b6925a01d3
4
- data.tar.gz: a0d737582d9918e19891a00c88e3d6829f1cc1c8
3
+ metadata.gz: 1878fc3aef9f2315438da1dc338f7a6254ed83ca
4
+ data.tar.gz: 1f894a00b663c83b7b1bb33f2fda113dd8851f1e
5
5
  SHA512:
6
- metadata.gz: 3beaf630fe3303784f85f3ae17ac89b427378ce5eeec8d7b6d7c76a6bfc699dc85e58ca87521a62b20e4b7c0c937411f0ce48cc1a40638e86b3b81991dea2964
7
- data.tar.gz: 52384d58bf87b5f18fb898ce2930d91d82e537269567d3d40ab0cd31a29367355d0048d4396c6002c123ee66d8f994ff347c01e2d342ae73561bd6d04807223e
6
+ metadata.gz: d14b02503088c21f6795c91a8b7eccfa7ed9d7cac10f06b388368ec3a1730e32e504166da603de22c2303a952af0778d1bb8190f0620df1bbb24dd9bb23a2c88
7
+ data.tar.gz: 790684129a750601ce3fb2cd71455ba1c6afece4f4be047f260161436e2cfca3f01c3d2cc9dd147f3f03eeb2bdac5d3c1bcb44acd420b40c99158e23eca650e7
data/lib/graphql.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "json"
2
2
  require "set"
3
3
  require "singleton"
4
+ require "forwardable"
4
5
 
5
6
  module GraphQL
6
7
  class Error < StandardError
@@ -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
- # @return [String] The name of this argument on its {GraphQL::Field} or {GraphQL::InputObjectType}
23
- def name
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
- def type=(new_return_type)
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 = new_return_type
29
+ @dirty_type = new_input_type
34
30
  end
35
31
 
36
- # Get the return type for this field.
32
+ # @return [GraphQL::BaseType] the input type for this argument
37
33
  def type
38
34
  @clean_type ||= begin
39
35
  ensure_defined
@@ -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)
@@ -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) { ALLOWED_INPUTS.include?(value) ? value : nil }
5
+ coerce_input -> (value) { (value == true || value == false) ? value : nil }
9
6
  coerce_result -> (value) { !!value }
10
7
  end
@@ -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
- # @return [<#call(obj, args,ctx)>] A proc-like object which can be called to return the field's value
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
- # @return [String] The name of this field on its {GraphQL::ObjectType} (or {GraphQL::InterfaceType})
135
- def name
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
- # @return [Hash<String => GraphQL::Argument>] Map String argument names to their {GraphQL::Argument} implementations
143
- def arguments
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
- # @return [Numeric, Proc] The complexity for this field (default: 1), as a constant or a proc like `-> (query_ctx, args, child_complexity) { } # Numeric`
154
- def complexity
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
- def resolve=(resolve_proc)
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 = resolve_proc || build_default_resolver
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 = {}
@@ -1,3 +1,4 @@
1
+ require "graphql/language/definition_slice"
1
2
  require "graphql/language/generation"
2
3
  require "graphql/language/lexer"
3
4
  require "graphql/language/nodes"
@@ -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 creates classes who:
5
- # - require their keyword arguments, throw ArgumentError if they don't match
6
- # - expose accessors for keyword arguments
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
- # @param options [Hash] Must contain all attributes defined by {required_attrs}, may also include `position_source`
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
@@ -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
- # @return [GraphQL::Relay::GlobalNodeIdentification] the node identification instance for this schema, when using Relay
71
- def node_identification
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
- # @return [Array<#call>] Middlewares suitable for MiddlewareChain, applied to fields during execution
82
- def middleware
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}'", parent, context: context)
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
@@ -1,3 +1,3 @@
1
1
  module GraphQL
2
- VERSION = "0.18.10"
2
+ VERSION = "0.18.11"
3
3
  end
@@ -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"=>18}],
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.10
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-09 00:00:00.000000000 Z
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