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 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