graphql 1.5.10 → 1.5.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: 0e87f7a05371764ee8744616a781f61f103f761c
4
- data.tar.gz: e8d2eafe80fbe962c1dd5c017201b82b86def0ad
3
+ metadata.gz: bb2841b1c8677bc9188757eca08b2f365f0e0af8
4
+ data.tar.gz: 12bc61f9684e3bae2a4d06c22f9378064fcf191d
5
5
  SHA512:
6
- metadata.gz: b83d8c335d98f4149a6ce531b6dc61254fc96bbb5fec35beb2c0fece8eb4fff58693cd7bd48fb89511da84d69cfcec5e639b64372add0c8070c5a9c871804af0
7
- data.tar.gz: ef672d60a38c0e1b5ec87af598d7ec221e36c6511ed92f72840bb32be813a0d434d9a0580d2e57e18ad8ad48f486f150d36b72399dbba347a4915686148b1c4e
6
+ metadata.gz: 7859ae0afed299edb29dedc59d014f057b85dde4e3547a300d312c0ba137f23cc23d6c70c664a3afd6985c3b393483aa012c8e7ba6e70e0713ad203163dbc089
7
+ data.tar.gz: 61ab9c38a66e013b09bec6f5865aae178110ba99b230b2a94b325ea1afef7b1bb9beb98744c52cee0d8b2c2813b377a39dbef13c920bf319cddfeef1ba2f3e07
@@ -9,7 +9,7 @@ class Functions::<%= name %> < GraphQL::Function
9
9
  # end
10
10
 
11
11
  # add arguments by type:
12
- # argument :id, !GraphQL::ID_TYPE
12
+ # argument :id, !types.ID
13
13
 
14
14
  # Resolve function:
15
15
  def call(obj, args, ctx)
@@ -29,6 +29,5 @@
29
29
  }
30
30
  <% end %><% if options[:batch] %>
31
31
  # GraphQL::Batch setup:
32
- lazy_resolve(Promise, :sync)
33
- instrument(:query, GraphQL::Batch::Setup)
32
+ use GraphQL::Batch
34
33
  <% end %>end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  require 'rails/generators/base'
3
3
  require 'graphql'
4
+ require 'active_support'
5
+ require 'active_support/core_ext/string/inflections'
4
6
 
5
7
  module Graphql
6
8
  module Generators
@@ -39,10 +41,10 @@ module Graphql
39
41
  when "Int", "Float", "Boolean", "String", "ID"
40
42
  "types.#{type_expression}"
41
43
  else
42
- "Types::#{type_expression}Type"
44
+ "Types::#{type_expression.camelize}Type"
43
45
  end
44
46
  when :graphql
45
- type_expression
47
+ type_expression.camelize
46
48
  else
47
49
  raise "Unexpected normalize mode: #{mode}"
48
50
  end
@@ -18,7 +18,7 @@ module GraphQL
18
18
 
19
19
  irep = query.internal_representation
20
20
 
21
- irep.each do |name, op_node|
21
+ irep.operation_definitions.each do |name, op_node|
22
22
  reduce_node(op_node, reducer_states)
23
23
  end
24
24
 
@@ -18,7 +18,7 @@ module GraphQL
18
18
  #
19
19
  # argument :id, GraphQL::ID_TYPE
20
20
  #
21
- # def resolve(obj, args, ctx)
21
+ # def call(obj, args, ctx)
22
22
  # @model.find(args.id)
23
23
  # end
24
24
  # end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/internal_representation/document"
2
3
  require "graphql/internal_representation/node"
3
4
  require "graphql/internal_representation/print"
4
5
  require "graphql/internal_representation/rewrite"
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module InternalRepresentation
4
+ class Document
5
+ # @return [Hash<String, Node>] Operation Nodes of this query
6
+ attr_reader :operation_definitions
7
+
8
+ # @return [Hash<String, Node>] Fragment definition Nodes of this query
9
+ attr_reader :fragment_definitions
10
+
11
+ def initialize
12
+ @operation_definitions = {}
13
+ @fragment_definitions = {}
14
+ end
15
+
16
+ def [](key)
17
+ warn "#{self.class}#[] is deprecated; use `operation_definitions[]` instead"
18
+ operation_definitions[key]
19
+ end
20
+
21
+ def each(&block)
22
+ warn "#{self.class}#each is deprecated; use `operation_definitions.each` instead"
23
+ operation_definitions.each(&block)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -18,11 +18,17 @@ module GraphQL
18
18
 
19
19
  NO_DIRECTIVES = [].freeze
20
20
 
21
- # @return [Hash<String, Node>] Roots of this query
22
- attr_reader :operations
21
+ # @return InternalRepresentation::Document
22
+ attr_reader :document
23
23
 
24
24
  def initialize
25
- @operations = {}
25
+ @document = InternalRepresentation::Document.new
26
+ end
27
+
28
+ # @return [Hash<String, Node>] Roots of this query
29
+ def operations
30
+ warn "#{self.class}#operations is deprecated; use `document.operation_definitions` instead"
31
+ document.operation_definitions
26
32
  end
27
33
 
28
34
  def validate(context)
@@ -39,14 +45,13 @@ module GraphQL
39
45
  # Array<Scope>
40
46
  scopes_stack = []
41
47
 
42
- fragment_definitions = {}
43
48
  skip_nodes = Set.new
44
49
 
45
- visit_op = VisitDefinition.new(context, @operations, nodes_stack, scopes_stack)
50
+ visit_op = VisitDefinition.new(context, @document.operation_definitions, nodes_stack, scopes_stack)
46
51
  visitor[Nodes::OperationDefinition].enter << visit_op.method(:enter)
47
52
  visitor[Nodes::OperationDefinition].leave << visit_op.method(:leave)
48
53
 
49
- visit_frag = VisitDefinition.new(context, fragment_definitions, nodes_stack, scopes_stack)
54
+ visit_frag = VisitDefinition.new(context, @document.fragment_definitions, nodes_stack, scopes_stack)
50
55
  visitor[Nodes::FragmentDefinition].enter << visit_frag.method(:enter)
51
56
  visitor[Nodes::FragmentDefinition].leave << visit_frag.method(:leave)
52
57
 
@@ -137,7 +142,7 @@ module GraphQL
137
142
  # can be shared between its usages.
138
143
  context.on_dependency_resolve do |defn_ast_node, spread_ast_nodes, frag_ast_node|
139
144
  frag_name = frag_ast_node.name
140
- fragment_node = fragment_definitions[frag_name]
145
+ fragment_node = @document.fragment_definitions[frag_name]
141
146
 
142
147
  if fragment_node
143
148
  spread_ast_nodes.each do |spread_ast_node|
@@ -39,25 +39,26 @@ module GraphQL
39
39
  def to_s
40
40
  "[#{of_type.to_s}]"
41
41
  end
42
+ alias_method :inspect, :to_s
42
43
 
43
44
  def coerce_result(value, ctx = nil)
44
45
  if ctx.nil?
45
46
  warn_deprecated_coerce("coerce_isolated_result")
46
47
  ctx = GraphQL::Query::NullContext
47
48
  end
48
- Array(value).map { |item| item.nil? ? nil : of_type.coerce_result(item, ctx) }
49
+ ensure_array(value).map { |item| item.nil? ? nil : of_type.coerce_result(item, ctx) }
49
50
  end
50
51
 
51
52
  private
52
53
 
53
54
  def coerce_non_null_input(value, ctx)
54
- Array(value).map { |item| of_type.coerce_input(item, ctx) }
55
+ ensure_array(value).map { |item| of_type.coerce_input(item, ctx) }
55
56
  end
56
57
 
57
58
  def validate_non_null_input(value, ctx)
58
59
  result = GraphQL::Query::InputValidationResult.new
59
60
 
60
- Array(value).each_with_index do |item, index|
61
+ ensure_array(value).each_with_index do |item, index|
61
62
  item_result = of_type.validate_input(item, ctx)
62
63
  if !item_result.valid?
63
64
  result.merge_result!(index, item_result)
@@ -66,5 +67,9 @@ module GraphQL
66
67
 
67
68
  result
68
69
  end
70
+
71
+ def ensure_array(value)
72
+ value.is_a?(Array) ? value : [value]
73
+ end
69
74
  end
70
75
  end
@@ -61,5 +61,6 @@ module GraphQL
61
61
  def to_s
62
62
  "#{of_type.to_s}!"
63
63
  end
64
+ alias_method :inspect, :to_s
64
65
  end
65
66
  end
@@ -27,7 +27,7 @@ module GraphQL
27
27
  end
28
28
  end
29
29
 
30
- attr_reader :schema, :document, :context, :fragments, :operations, :root_value, :query_string, :warden, :provided_variables
30
+ attr_reader :schema, :document, :context, :fragments, :operations, :root_value, :query_string, :warden, :provided_variables, :operation_name
31
31
 
32
32
  # Prepare query `query_string` on `schema`
33
33
  # @param schema [GraphQL::Schema]
@@ -83,13 +83,17 @@ module GraphQL
83
83
  @ast_variables = []
84
84
  @mutation = false
85
85
  operation_name_error = nil
86
+ @operation_name = nil
87
+
86
88
  if @operations.any?
87
- @selected_operation = find_operation(@operations, operation_name)
88
- if @selected_operation.nil?
89
+ selected_operation = find_operation(@operations, operation_name)
90
+ if selected_operation.nil?
89
91
  operation_name_error = GraphQL::Query::OperationNameMissingError.new(operation_name)
90
92
  else
91
- @ast_variables = @selected_operation.variables
92
- @mutation = @selected_operation.operation_type == "mutation"
93
+ @operation_name = selected_operation.name
94
+ @ast_variables = selected_operation.variables
95
+ @mutation = selected_operation.operation_type == "mutation"
96
+ @selected_operation = selected_operation
93
97
  end
94
98
  end
95
99
 
@@ -155,7 +159,7 @@ module GraphQL
155
159
  end
156
160
 
157
161
  def irep_selection
158
- @selection ||= internal_representation[selected_operation.name]
162
+ @selection ||= internal_representation.operation_definitions[selected_operation.name]
159
163
  end
160
164
 
161
165
  # Node-level cache for calculating arguments. Used during execution and query analysis.
@@ -399,9 +399,10 @@ module GraphQL
399
399
  # Create schema from an IDL schema.
400
400
  # @param definition_string [String] A schema definition string
401
401
  # @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
402
+ # @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
402
403
  # @return [GraphQL::Schema] the schema described by `document`
403
- def self.from_definition(string, default_resolve: BuildFromDefinition::DefaultResolve)
404
- GraphQL::Schema::BuildFromDefinition.from_definition(string, default_resolve: default_resolve)
404
+ def self.from_definition(string, default_resolve: BuildFromDefinition::DefaultResolve, parser: BuildFromDefinition::DefaultParser)
405
+ GraphQL::Schema::BuildFromDefinition.from_definition(string, default_resolve: default_resolve, parser: parser)
405
406
  end
406
407
 
407
408
  # Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
@@ -3,12 +3,15 @@ module GraphQL
3
3
  class Schema
4
4
  module BuildFromDefinition
5
5
  class << self
6
- def from_definition(definition_string, default_resolve:)
7
- document = GraphQL::parse(definition_string)
6
+ def from_definition(definition_string, default_resolve:, parser: DefaultParser)
7
+ document = parser.parse(definition_string)
8
8
  Builder.build(document, default_resolve: default_resolve)
9
9
  end
10
10
  end
11
11
 
12
+ # @api private
13
+ DefaultParser = GraphQL::Language::Parser
14
+
12
15
  # @api private
13
16
  module DefaultResolve
14
17
  def self.call(type, field, obj, args, ctx)
@@ -12,17 +12,19 @@ module GraphQL
12
12
  end
13
13
 
14
14
  def initialize
15
+ @node_paths = {}
16
+
15
17
  # { name => node } pairs for fragments
16
18
  @fragment_definitions = {}
17
19
 
18
20
  # This tracks dependencies from fragment to Node where it was used
19
- # { frag_name => [dependent_node, dependent_node]}
21
+ # { fragment_definition_node => [dependent_node, dependent_node]}
20
22
  @dependent_definitions = Hash.new { |h, k| h[k] = Set.new }
21
23
 
22
24
  # First-level usages of spreads within definitions
23
25
  # (When a key has an empty list as its value,
24
26
  # we can resolve that key's depenedents)
25
- # { string => [node, node ...] }
27
+ # { definition_node => [node, node ...] }
26
28
  @immediate_dependencies = Hash.new { |h, k| h[k] = Set.new }
27
29
  end
28
30
 
@@ -38,8 +40,19 @@ module GraphQL
38
40
  # this node is the one who depends on it
39
41
  current_parent = nil
40
42
 
43
+ visitor[GraphQL::Language::Nodes::Document] << ->(node, prev_node) {
44
+ node.definitions.each do |definition|
45
+ case definition
46
+ when GraphQL::Language::Nodes::OperationDefinition
47
+ when GraphQL::Language::Nodes::FragmentDefinition
48
+ @fragment_definitions[definition.name] = definition
49
+ end
50
+ end
51
+ }
52
+
41
53
  visitor[GraphQL::Language::Nodes::OperationDefinition] << ->(node, prev_node) {
42
- current_parent = NodeWithPath.new(node, context.path)
54
+ @node_paths[node] = NodeWithPath.new(node, context.path)
55
+ current_parent = node
43
56
  }
44
57
 
45
58
  visitor[GraphQL::Language::Nodes::OperationDefinition].leave << ->(node, prev_node) {
@@ -47,7 +60,8 @@ module GraphQL
47
60
  }
48
61
 
49
62
  visitor[GraphQL::Language::Nodes::FragmentDefinition] << ->(node, prev_node) {
50
- current_parent = @fragment_definitions[node.name] = NodeWithPath.new(node, context.path)
63
+ @node_paths[node] = NodeWithPath.new(node, context.path)
64
+ current_parent = node
51
65
  }
52
66
 
53
67
  visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << ->(node, prev_node) {
@@ -55,9 +69,11 @@ module GraphQL
55
69
  }
56
70
 
57
71
  visitor[GraphQL::Language::Nodes::FragmentSpread] << ->(node, prev_node) {
72
+ @node_paths[node] = NodeWithPath.new(node, context.path)
73
+
58
74
  # Track both sides of the dependency
59
- @dependent_definitions[node.name] << current_parent
60
- @immediate_dependencies[current_parent.name] << NodeWithPath.new(node, context.path)
75
+ @dependent_definitions[@fragment_definitions[node.name]] << current_parent
76
+ @immediate_dependencies[current_parent] << node
61
77
  }
62
78
  end
63
79
 
@@ -113,40 +129,39 @@ module GraphQL
113
129
  # determine them at the end. This way, we can treat fragments with the
114
130
  # same name as if they were the same name. If _any_ of the fragments
115
131
  # with that name has a dependency, we record it.
116
- independent_fragment_names = @fragment_definitions.each_key.select { |name| !@immediate_dependencies.key?(name) }
132
+ independent_fragment_nodes = @fragment_definitions.values - @immediate_dependencies.keys
117
133
 
118
- while fragment_name = independent_fragment_names.pop
134
+ while fragment_node = independent_fragment_nodes.pop
119
135
  loops += 1
120
136
  if loops > max_loops
121
137
  raise("Resolution loops exceeded the number of definitions; infinite loop detected.")
122
138
  end
123
- fragment_node = @fragment_definitions[fragment_name]
124
139
  # Since it's independent, let's remove it from here.
125
140
  # That way, we can use the remainder to identify cycles
126
- @immediate_dependencies.delete(fragment_name)
127
- fragment_usages = @dependent_definitions[fragment_name]
141
+ @immediate_dependencies.delete(fragment_node)
142
+ fragment_usages = @dependent_definitions[fragment_node]
128
143
  if fragment_usages.none?
129
144
  # If we didn't record any usages during the visit,
130
145
  # then this fragment is unused.
131
- dependency_map.unused_dependencies << fragment_node
146
+ dependency_map.unused_dependencies << @node_paths[fragment_node]
132
147
  else
133
148
  fragment_usages.each do |definition_node|
134
149
  # Register the dependency AND second-order dependencies
135
150
  dependency_map[definition_node] << fragment_node
136
151
  dependency_map[definition_node].concat(dependency_map[fragment_node])
137
152
  # Since we've regestered it, remove it from our to-do list
138
- deps = @immediate_dependencies[definition_node.name]
153
+ deps = @immediate_dependencies[definition_node]
139
154
  # Can't find a way to _just_ delete from `deps` and return the deleted entries
140
- removed, remaining = deps.partition { |spread| spread.name == fragment_name }
141
- @immediate_dependencies[definition_node.name] = remaining
155
+ removed, remaining = deps.partition { |spread| spread.name == fragment_node.name }
156
+ @immediate_dependencies[definition_node] = remaining
142
157
  if block_given?
143
158
  yield(definition_node, removed, fragment_node)
144
159
  end
145
- if remaining.none? && definition_node.node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
160
+ if remaining.none? && definition_node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
146
161
  # If all of this definition's dependencies have
147
162
  # been resolved, we can now resolve its
148
163
  # own dependents.
149
- independent_fragment_names << definition_node.name
164
+ independent_fragment_nodes << definition_node
150
165
  end
151
166
  end
152
167
  end
@@ -155,21 +170,20 @@ module GraphQL
155
170
  # If any dependencies were _unmet_
156
171
  # (eg, spreads with no corresponding definition)
157
172
  # then they're still in there
158
- @immediate_dependencies.each do |defn_name, deps|
173
+ @immediate_dependencies.each do |defn_node, deps|
159
174
  deps.each do |spread|
160
175
  if @fragment_definitions[spread.name].nil?
161
- defn_node = @fragment_definitions[defn_name]
162
- dependency_map.unmet_dependencies[defn_node] << spread
176
+ dependency_map.unmet_dependencies[@node_paths[defn_node]] << @node_paths[spread]
163
177
  deps.delete(spread)
164
178
  end
165
179
  end
166
180
  if deps.none?
167
- @immediate_dependencies.delete(defn_name)
181
+ @immediate_dependencies.delete(defn_node)
168
182
  end
169
183
  end
170
184
 
171
185
  # Anything left in @immediate_dependencies is cyclical
172
- cyclical_nodes = @immediate_dependencies.keys.map { |n| @fragment_definitions[n] }
186
+ cyclical_nodes = @immediate_dependencies.keys.map { |n| @node_paths[n] }
173
187
  # @immediate_dependencies also includes operation names, but we don't care about
174
188
  # those. They became nil when we looked them up on `@fragment_definitions`, so remove them.
175
189
  cyclical_nodes.compact!
@@ -33,8 +33,8 @@ module GraphQL
33
33
 
34
34
  context.visitor.visit
35
35
  # Post-validation: allow validators to register handlers on rewritten query nodes
36
- rewrite_result = rewrite.operations
37
- GraphQL::InternalRepresentation::Visit.visit_each_node(rewrite_result, context.each_irep_node_handlers)
36
+ rewrite_result = rewrite.document
37
+ GraphQL::InternalRepresentation::Visit.visit_each_node(rewrite_result.operation_definitions, context.each_irep_node_handlers)
38
38
 
39
39
  {
40
40
  errors: context.errors,
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.5.10"
3
+ VERSION = "1.5.11"
4
4
  end
@@ -20,7 +20,7 @@ class Functions::FindRecord < GraphQL::Function
20
20
  # end
21
21
 
22
22
  # add arguments by type:
23
- # argument :id, !GraphQL::ID_TYPE
23
+ # argument :id, !types.ID
24
24
 
25
25
  # Resolve function:
26
26
  def call(obj, args, ctx)
@@ -178,8 +178,7 @@ DummySchema = GraphQL::Schema.define do
178
178
  }
179
179
 
180
180
  # GraphQL::Batch setup:
181
- lazy_resolve(Promise, :sync)
182
- instrument(:query, GraphQL::Batch::Setup)
181
+ use GraphQL::Batch
183
182
  end
184
183
  RUBY
185
184
  end
@@ -30,6 +30,15 @@ RUBY
30
30
  end
31
31
  end
32
32
 
33
+ test "it generates classifed file" do
34
+ run_generator(["page"])
35
+ assert_file "app/graphql/types/page_type.rb", <<-RUBY
36
+ Types::PageType = GraphQL::ObjectType.define do
37
+ name "Page"
38
+ end
39
+ RUBY
40
+ end
41
+
33
42
  test "it makes Relay nodes" do
34
43
  run_generator(["Page", "--node"])
35
44
  assert_file "app/graphql/types/page_type.rb", <<-RUBY
@@ -104,7 +104,7 @@ describe GraphQL::InternalRepresentation::Rewrite do
104
104
  }
105
105
 
106
106
  it "groups selections by object types which they apply to" do
107
- doc = rewrite_result["getPlant"]
107
+ doc = rewrite_result.operation_definitions["getPlant"]
108
108
  assert_equal nil, doc.definition
109
109
 
110
110
  plant_scoped_selection = doc.scoped_children[schema.types["Query"]]["plant"]
@@ -126,7 +126,7 @@ describe GraphQL::InternalRepresentation::Rewrite do
126
126
  end
127
127
 
128
128
  it "tracks parent nodes" do
129
- doc = rewrite_result["getPlant"]
129
+ doc = rewrite_result.operation_definitions["getPlant"]
130
130
  assert_equal nil, doc.parent
131
131
 
132
132
  plant_selection = doc.typed_children[schema.types["Query"]]["plant"]
@@ -171,7 +171,7 @@ describe GraphQL::InternalRepresentation::Rewrite do
171
171
  }
172
172
 
173
173
  it "applies directives from all contexts" do
174
- doc = rewrite_result["getPlant"]
174
+ doc = rewrite_result.operation_definitions["getPlant"]
175
175
  plant_selection = doc.typed_children[schema.types["Query"]]["plant"]
176
176
  leaf_type_selection = plant_selection.typed_children[schema.types["Nut"]]["leafType"]
177
177
  # Only unskipped occurrences in the AST
@@ -208,7 +208,7 @@ describe GraphQL::InternalRepresentation::Rewrite do
208
208
  }
209
209
 
210
210
  it "applies spreads to their parents only" do
211
- doc = rewrite_result[nil]
211
+ doc = rewrite_result.operation_definitions[nil]
212
212
  plant_selection = doc.typed_children[schema.types["Query"]]["plant"]
213
213
  nut_habitat_selections = plant_selection.typed_children[schema.types["Nut"]]["habitats"].typed_children[schema.types["Habitat"]]
214
214
  assert_equal ["averageWeight", "residentName", "seasons"], nut_habitat_selections.keys.sort
@@ -286,4 +286,42 @@ describe GraphQL::InternalRepresentation::Rewrite do
286
286
  end
287
287
  end
288
288
  end
289
+
290
+ describe "fragment definition without query" do
291
+ let(:query_string) {
292
+ <<-GRAPHQL
293
+ fragment NutFields on Nut {
294
+ leafType
295
+ ... TreeFields
296
+ }
297
+
298
+ fragment TreeFields on Tree {
299
+ habitats {
300
+ ... HabitatFields
301
+ }
302
+ }
303
+
304
+ fragment HabitatFields on Habitat {
305
+ seasons
306
+ }
307
+ GRAPHQL
308
+ }
309
+
310
+ let(:validator) {
311
+ rules = GraphQL::StaticValidation::ALL_RULES - [
312
+ GraphQL::StaticValidation::FragmentsAreUsed
313
+ ]
314
+ GraphQL::StaticValidation::Validator.new(schema: schema, rules: rules)
315
+ }
316
+
317
+ it "tracks fragment definitions without operation" do
318
+ doc = rewrite_result.fragment_definitions["NutFields"]
319
+
320
+ assert doc.typed_children[schema.types["Nut"]]["leafType"]
321
+
322
+ assert nut_selections = doc.typed_children[schema.types["Nut"]]
323
+ habitats_selections = nut_selections["habitats"].typed_children[schema.types["Habitat"]]
324
+ assert_equal ["seasons"], habitats_selections.keys
325
+ end
326
+ end
289
327
  end
@@ -8,6 +8,10 @@ describe GraphQL::ListType do
8
8
  assert_equal([1.0, 2.0, 3.0].inspect, float_list.coerce_isolated_input([1, 2, 3]).inspect)
9
9
  end
10
10
 
11
+ it "converts items that are not lists into lists" do
12
+ assert_equal([1.0].inspect, float_list.coerce_isolated_input(1.0).inspect)
13
+ end
14
+
11
15
  describe "validate_input with bad input" do
12
16
  let(:bad_num) { "bad_num" }
13
17
  let(:result) { float_list.validate_isolated_input([bad_num, 2.0, 3.0]) }
@@ -30,4 +34,20 @@ describe GraphQL::ListType do
30
34
  assert_equal(actual, expected)
31
35
  end
32
36
  end
37
+
38
+ describe "list of input objects" do
39
+ let(:input_object) do
40
+ GraphQL::InputObjectType.define do
41
+ name "SomeInputObjectType"
42
+ argument :float, !types.Float
43
+ end
44
+ end
45
+
46
+ let(:input_object_list) { input_object.to_list_type }
47
+
48
+ it "converts hashes into lists of hashes" do
49
+ hash = { 'float' => 1.0 }
50
+ assert_equal([hash].inspect, input_object_list.coerce_isolated_input(hash).map(&:to_h).inspect)
51
+ end
52
+ end
33
53
  end
@@ -58,6 +58,27 @@ describe GraphQL::Query::Executor do
58
58
  end
59
59
  end
60
60
 
61
+ describe "operation and fragment defintions of the same name" do
62
+ let(:query_string) { %|
63
+ query Cheese { cheese(id: 1) { ...Cheese } }
64
+ query MoreCheese { cheese(id: 2) { ...Cheese } }
65
+ fragment Cheese on Cheese { flavor }
66
+ |}
67
+
68
+ let(:operation_name) { "Cheese" }
69
+
70
+ it "runs the named operation" do
71
+ expected = {
72
+ "data" => {
73
+ "cheese" => {
74
+ "flavor" => "Brie"
75
+ }
76
+ }
77
+ }
78
+ assert_equal(expected, result)
79
+ end
80
+ end
81
+
61
82
 
62
83
  describe "execution order" do
63
84
  let(:query_string) {%|
@@ -57,6 +57,32 @@ describe GraphQL::Query do
57
57
  end
58
58
  end
59
59
 
60
+ describe "operation_name" do
61
+ describe "when provided" do
62
+ let(:query_string) { <<-GRAPHQL
63
+ query q1 { cheese(id: 1) { flavor } }
64
+ query q2 { cheese(id: 2) { flavor } }
65
+ GRAPHQL
66
+ }
67
+ let(:operation_name) { "q2" }
68
+
69
+ it "returns the provided name" do
70
+ assert_equal "q2", query.operation_name
71
+ end
72
+ end
73
+
74
+ describe "when inferred" do
75
+ let(:query_string) { <<-GRAPHQL
76
+ query q3 { cheese(id: 3) { flavor } }
77
+ GRAPHQL
78
+ }
79
+
80
+ it "returns the inferred name" do
81
+ assert_equal "q3", query.operation_name
82
+ end
83
+ end
84
+ end
85
+
60
86
  describe "when passed a document instance" do
61
87
  let(:query) { GraphQL::Query.new(
62
88
  schema,
@@ -529,4 +555,14 @@ describe GraphQL::Query do
529
555
  assert_equal([nil], expected_args.first['id'])
530
556
  end
531
557
  end
558
+
559
+ describe '#internal_representation' do
560
+ it "includes all definition roots" do
561
+ assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.operation_definitions["getFlavor"]
562
+ assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.fragment_definitions["cheeseFields"]
563
+ assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.fragment_definitions["edibleFields"]
564
+ assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.fragment_definitions["milkFields"]
565
+ assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.fragment_definitions["dairyFields"]
566
+ end
567
+ end
532
568
  end
@@ -813,5 +813,21 @@ SCHEMA
813
813
  assert_equal(result.to_json, '{"data":{"allTodos":[{"text":"Pay the bills.","from_context":null},{"text":"Buy Milk","from_context":"bar"}]}}')
814
814
  end
815
815
  end
816
+
817
+ describe "custom parser behavior" do
818
+ module BadParser
819
+ ParseError = Class.new(StandardError)
820
+
821
+ def self.parse(string)
822
+ raise ParseError
823
+ end
824
+ end
825
+
826
+ it 'accepts a parser callable' do
827
+ assert_raises(BadParser::ParseError) do
828
+ GraphQL::Schema.from_definition(schema_defn, parser: BadParser)
829
+ end
830
+ end
831
+ end
816
832
  end
817
833
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.10
4
+ version: 1.5.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: 2017-04-25 00:00:00.000000000 Z
11
+ date: 2017-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -362,6 +362,7 @@ files:
362
362
  - lib/graphql/int_type.rb
363
363
  - lib/graphql/interface_type.rb
364
364
  - lib/graphql/internal_representation.rb
365
+ - lib/graphql/internal_representation/document.rb
365
366
  - lib/graphql/internal_representation/node.rb
366
367
  - lib/graphql/internal_representation/print.rb
367
368
  - lib/graphql/internal_representation/rewrite.rb