graphql 0.18.14 → 0.18.15

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/query_complexity.rb +15 -6
  3. data/lib/graphql/analysis/query_depth.rb +11 -10
  4. data/lib/graphql/directive.rb +2 -6
  5. data/lib/graphql/directive/include_directive.rb +0 -4
  6. data/lib/graphql/directive/skip_directive.rb +0 -4
  7. data/lib/graphql/execution/directive_checks.rb +22 -13
  8. data/lib/graphql/execution_error.rb +7 -0
  9. data/lib/graphql/internal_representation/node.rb +20 -2
  10. data/lib/graphql/internal_representation/rewrite.rb +66 -20
  11. data/lib/graphql/language/generation.rb +22 -8
  12. data/lib/graphql/language/nodes.rb +48 -20
  13. data/lib/graphql/language/parser.rb +436 -423
  14. data/lib/graphql/language/parser.y +22 -19
  15. data/lib/graphql/language/parser_tests.rb +131 -2
  16. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -0
  17. data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
  18. data/lib/graphql/query/serial_execution/value_resolution.rb +4 -1
  19. data/lib/graphql/schema/printer.rb +1 -1
  20. data/lib/graphql/static_validation/message.rb +1 -1
  21. data/lib/graphql/version.rb +1 -1
  22. data/readme.md +10 -12
  23. data/spec/graphql/directive_spec.rb +139 -1
  24. data/spec/graphql/execution_error_spec.rb +63 -3
  25. data/spec/graphql/introspection/type_type_spec.rb +2 -0
  26. data/spec/graphql/language/generation_spec.rb +55 -7
  27. data/spec/graphql/query/executor_spec.rb +4 -2
  28. data/spec/graphql/schema/catchall_middleware_spec.rb +1 -0
  29. data/spec/graphql/schema/printer_spec.rb +1 -1
  30. data/spec/graphql/schema/timeout_middleware_spec.rb +10 -5
  31. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +6 -6
  32. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -4
  33. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +2 -2
  34. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +2 -2
  35. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +3 -3
  36. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +2 -2
  37. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +3 -3
  38. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +2 -2
  39. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +2 -2
  40. data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +1 -1
  41. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +3 -3
  42. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +2 -2
  43. data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +1 -1
  44. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +3 -3
  45. data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +1 -1
  46. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +4 -4
  47. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +4 -4
  48. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +3 -3
  49. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +3 -3
  50. data/spec/graphql/static_validation/validator_spec.rb +1 -1
  51. data/spec/support/dairy_app.rb +8 -0
  52. metadata +30 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d8692b90c4aa941fe5a1139719790eb1ba55c92e
4
- data.tar.gz: 84c924ce93a593f26a7419f9ed69e5220380201c
3
+ metadata.gz: ca81d77891fe081da31667ba3d1a8676ff489ff5
4
+ data.tar.gz: e2b4a12d439ce76943470264e60d6007ae7f61fb
5
5
  SHA512:
6
- metadata.gz: 26a2968e15530d07b78b293f41bc103fdc3026036de39fd84e0f48434659d23558d806e67b7d07288cd5eb9b6f08cfbb06e76d809b865d35c70355cd7ad51ae7
7
- data.tar.gz: 9c3401de8f427e468db33e2403b729ec5ccc291b53e95d8d853ef86e28744cfec291c5df778f34c8497149d82f4aa3054ddb0ff8059516e973f0fbc2441e999d
6
+ metadata.gz: d18944b50b350786c710bc9e9ca3f9e463ccc8a6d835fa3b24e2442120e4b41280828ec59ad4e916b5aa3bea5f0566b9fb9bb6a462eaa75dcc7397d9099f5892
7
+ data.tar.gz: 78b22a36ffbed73b1b512c8b351cc55f99a8484139ac71fd1f8f4dc2a9f7682b68c681e5a16d8b7776500b24cd721ac576519566dae87c0bb94c97536651eca6
@@ -18,10 +18,13 @@ module GraphQL
18
18
  # State for the query complexity calcuation:
19
19
  # - `query` is needed for variables, then passed to handler
20
20
  # - `complexities_on_type` holds complexity scores for each type in an IRep node
21
+ # - `skip_depth` increments for each skipped node, then decrements on the way out.
22
+ # While it's greater than `0`, we're visiting a skipped part of the query.
21
23
  def initial_value(query)
22
24
  {
23
25
  query: query,
24
26
  complexities_on_type: [TypeComplexity.new],
27
+ skip_depth: 0,
25
28
  }
26
29
  end
27
30
 
@@ -29,16 +32,22 @@ module GraphQL
29
32
  def call(memo, visit_type, irep_node)
30
33
  if irep_node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
31
34
  if visit_type == :enter
32
- memo[:complexities_on_type].push(TypeComplexity.new)
35
+ if irep_node.skipped?
36
+ memo[:skip_depth] += 1
37
+ elsif memo[:skip_depth] == 0
38
+ memo[:complexities_on_type].push(TypeComplexity.new)
39
+ end
33
40
  else
34
- type_complexities = memo[:complexities_on_type].pop
35
- own_complexity = if GraphQL::Execution::DirectiveChecks.skip?(irep_node, memo[:query])
36
- 0
41
+ if memo[:skip_depth] > 0
42
+ if irep_node.skipped?
43
+ memo[:skip_depth] -= 1
44
+ end
37
45
  else
46
+ type_complexities = memo[:complexities_on_type].pop
38
47
  child_complexity = type_complexities.max_possible_complexity
39
- get_complexity(irep_node, memo[:query], child_complexity)
48
+ own_complexity = get_complexity(irep_node, memo[:query], child_complexity)
49
+ memo[:complexities_on_type].last.merge(irep_node.definitions, own_complexity)
40
50
  end
41
- memo[:complexities_on_type].last.merge(irep_node.definitions, own_complexity)
42
51
  end
43
52
  end
44
53
  memo
@@ -16,26 +16,27 @@ module GraphQL
16
16
  {
17
17
  max_depth: 0,
18
18
  current_depth: 0,
19
- skip_current_scope: false,
19
+ skip_depth: 0,
20
20
  query: query,
21
21
  }
22
22
  end
23
23
 
24
24
  def call(memo, visit_type, irep_node)
25
25
  if irep_node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
26
+ # Don't validate introspection fields or skipped nodes
27
+ not_validated_node = GraphQL::Schema::DYNAMIC_FIELDS.include?(irep_node.definition_name) || !irep_node.included?
26
28
  if visit_type == :enter
27
- if GraphQL::Schema::DYNAMIC_FIELDS.include?(irep_node.definition_name)
28
- # Don't validate introspection fields
29
- memo[:skip_current_scope] = true
30
- elsif memo[:skip_current_scope]
31
- # we're inside an introspection query
32
- elsif GraphQL::Execution::DirectiveChecks.include?(irep_node, memo[:query])
29
+ if not_validated_node
30
+ memo[:skip_depth] += 1
31
+ elsif memo[:skip_depth] > 0
32
+ # we're inside an introspection query or skipped node
33
+ else
33
34
  memo[:current_depth] += 1
34
35
  end
35
36
  else
36
- if GraphQL::Schema::DYNAMIC_FIELDS.include?(irep_node.definition_name)
37
- memo[:skip_current_scope] = false
38
- elsif GraphQL::Execution::DirectiveChecks.include?(irep_node, memo[:query])
37
+ if not_validated_node
38
+ memo[:skip_depth] -= 1
39
+ else
39
40
  if memo[:max_depth] < memo[:current_depth]
40
41
  memo[:max_depth] = memo[:current_depth]
41
42
  end
@@ -7,9 +7,9 @@ module GraphQL
7
7
  #
8
8
  class Directive
9
9
  include GraphQL::Define::InstanceDefinable
10
- accepts_definitions :locations, :name, :description, :include_proc, argument: GraphQL::Define::AssignArgument
10
+ accepts_definitions :locations, :name, :description, argument: GraphQL::Define::AssignArgument
11
11
 
12
- lazy_defined_attr_accessor :locations, :arguments, :name, :description, :include_proc
12
+ lazy_defined_attr_accessor :locations, :arguments, :name, :description
13
13
 
14
14
  LOCATIONS = [
15
15
  QUERY = :QUERY,
@@ -25,10 +25,6 @@ module GraphQL
25
25
  @arguments = {}
26
26
  end
27
27
 
28
- def include?(arguments)
29
- include_proc.call(arguments)
30
- end
31
-
32
28
  def to_s
33
29
  "<GraphQL::Directive #{name}>"
34
30
  end
@@ -3,8 +3,4 @@ GraphQL::Directive::IncludeDirective = GraphQL::Directive.define do
3
3
  description "Include this part of the query if `if` is true"
4
4
  locations([GraphQL::Directive::FIELD, GraphQL::Directive::FRAGMENT_SPREAD, GraphQL::Directive::INLINE_FRAGMENT])
5
5
  argument :if, !GraphQL::BOOLEAN_TYPE
6
-
7
- include_proc -> (arguments) {
8
- arguments["if"]
9
- }
10
6
  end
@@ -4,8 +4,4 @@ GraphQL::Directive::SkipDirective = GraphQL::Directive.define do
4
4
  locations([GraphQL::Directive::FIELD, GraphQL::Directive::FRAGMENT_SPREAD, GraphQL::Directive::INLINE_FRAGMENT])
5
5
 
6
6
  argument :if, !GraphQL::BOOLEAN_TYPE
7
-
8
- include_proc -> (arguments) {
9
- !arguments["if"]
10
- }
11
7
  end
@@ -10,22 +10,31 @@ module GraphQL
10
10
 
11
11
  # This covers `@include(if:)` & `@skip(if:)`
12
12
  # @return [Boolean] Should this node be skipped altogether?
13
- def skip?(irep_node, query)
14
- irep_node.directives.each do |directive_node|
15
- if directive_node.name == SKIP || directive_node.name == INCLUDE
16
- directive_defn = directive_node.definitions.first
17
- args = query.arguments_for(directive_node, directive_defn)
18
- if !directive_defn.include?(args)
19
- return true
20
- end
21
- end
22
- end
23
- false
13
+ def skip?(ast_node, query)
14
+ !include?(ast_node, query)
24
15
  end
25
16
 
26
17
  # @return [Boolean] Should this node be included in the query?
27
- def include?(irep_node, query)
28
- !skip?(irep_node, query)
18
+ def include?(directive_irep_nodes, query)
19
+ directive_irep_nodes.each do |directive_irep_node|
20
+ name = directive_irep_node.name
21
+ directive_defn = query.schema.directives[name]
22
+ case name
23
+ when SKIP
24
+ args = query.arguments_for(directive_irep_node, directive_defn)
25
+ if args['if'] == true
26
+ return false
27
+ end
28
+ when INCLUDE
29
+ args = query.arguments_for(directive_irep_node, directive_defn)
30
+ if args['if'] == false
31
+ return false
32
+ end
33
+ else
34
+ # Undefined directive, or one we don't care about
35
+ end
36
+ end
37
+ true
29
38
  end
30
39
  end
31
40
  end
@@ -6,6 +6,10 @@ module GraphQL
6
6
  # @return [GraphQL::Language::Nodes::Field] the field where the error occured
7
7
  attr_accessor :ast_node
8
8
 
9
+ # @return [String] an array describing the JSON-path into the execution
10
+ # response which corresponds to this error.
11
+ attr_accessor :path
12
+
9
13
  def initialize(message, ast_node: nil)
10
14
  @ast_node = ast_node
11
15
  super(message)
@@ -24,6 +28,9 @@ module GraphQL
24
28
  }
25
29
  ]
26
30
  end
31
+ if path
32
+ hash["path"] = path
33
+ end
27
34
  hash
28
35
  end
29
36
  end
@@ -3,7 +3,7 @@ require "set"
3
3
  module GraphQL
4
4
  module InternalRepresentation
5
5
  class Node
6
- def initialize(parent:, ast_node: nil, return_type: nil, name: nil, definition_name: nil, definitions: {}, children: {}, spreads: [], directives: Set.new)
6
+ def initialize(parent:, ast_node: nil, return_type: nil, name: nil, definition_name: nil, definitions: {}, children: {}, spreads: [], directives: Set.new, included: true)
7
7
  # Make sure these are kept in sync with #dup
8
8
  @ast_node = ast_node
9
9
  @return_type = return_type
@@ -14,6 +14,7 @@ module GraphQL
14
14
  @children = children
15
15
  @spreads = spreads
16
16
  @directives = directives
17
+ @included = included
17
18
  end
18
19
 
19
20
  # Note: by the time this gets out of the Rewrite phase, this will be empty -- it's emptied out when fragments are merged back in
@@ -58,6 +59,14 @@ module GraphQL
58
59
  # @return [Array<GraphQL::Query::Node>]
59
60
  attr_reader :children
60
61
 
62
+ # @return [Boolean] false if every field for this node included `@skip(if: true)`
63
+ attr_accessor :included
64
+ alias :included? :included
65
+
66
+ def skipped?
67
+ !@included
68
+ end
69
+
61
70
  # @return [GraphQL::InternalRepresentation::Node] The node which this node is a child of
62
71
  attr_reader :parent
63
72
 
@@ -72,9 +81,18 @@ module GraphQL
72
81
  end
73
82
  end
74
83
 
84
+ def path
85
+ path = parent ? parent.path : []
86
+ path << name if name
87
+ path << @index if @index
88
+ path
89
+ end
90
+
91
+ attr_writer :index
92
+
75
93
  def inspect(indent = 0)
76
94
  own_indent = " " * indent
77
- self_inspect = "#{own_indent}<Node #{name} (#{definition_name}: {#{definitions.keys.join("|")}} -> #{return_type})>"
95
+ self_inspect = "#{own_indent}<Node #{name} #{skipped? ? "(skipped)" : ""}(#{definition_name}: {#{definitions.keys.join("|")}} -> #{return_type})>"
78
96
  if children.any?
79
97
  self_inspect << " {\n#{children.values.map { |n| n.inspect(indent + 2)}.join("\n")}\n#{own_indent}}"
80
98
  end
@@ -24,7 +24,7 @@ module GraphQL
24
24
  # This tracks dependencies from fragment to Node where it was used
25
25
  # { frag_name => [dependent_node, dependent_node]}
26
26
  @fragment_spreads = Hash.new { |h, k| h[k] = []}
27
- # [Nodes::Directive ... ] directive affecting the current scope
27
+ # [[Nodes::Directive ...]] directive affecting the current scope
28
28
  @parent_directives = []
29
29
  end
30
30
 
@@ -55,6 +55,7 @@ module GraphQL
55
55
  name: node_name,
56
56
  definition_name: ast_node.name,
57
57
  parent: parent_node,
58
+ included: false, # may be set to true on leaving the node
58
59
  )
59
60
  end
60
61
  object_type = context.parent_type_definition.unwrap
@@ -64,20 +65,21 @@ module GraphQL
64
65
  }
65
66
 
66
67
  visitor[Nodes::InlineFragment].enter << -> (ast_node, prev_ast_node) {
67
- @parent_directives.push([])
68
+ @parent_directives.push(InlineFragmentDirectives.new)
68
69
  }
69
70
 
70
71
  visitor[Nodes::Directive].enter << -> (ast_node, prev_ast_node) {
71
72
  # It could be a query error where a directive is somewhere it shouldn't be
72
73
  if @parent_directives.any?
73
- @parent_directives.last << Node.new(
74
+ directive_irep_node = Node.new(
74
75
  name: ast_node.name,
75
76
  definition_name: ast_node.name,
76
77
  ast_node: ast_node,
77
- definitions: [context.directive_definition],
78
+ definitions: {context.directive_definition => context.directive_definition},
78
79
  # This isn't used, the directive may have many parents in the case of inline fragment
79
80
  parent: nil,
80
81
  )
82
+ @parent_directives.last.push(directive_irep_node)
81
83
  end
82
84
  }
83
85
 
@@ -88,6 +90,7 @@ module GraphQL
88
90
  parent: parent_node,
89
91
  name: ast_node.name,
90
92
  ast_node: ast_node,
93
+ included: false, # this may be set to true on leaving the node
91
94
  )
92
95
  # The parent node has a reference to the fragment
93
96
  parent_node.spreads.push(spread_node)
@@ -117,8 +120,9 @@ module GraphQL
117
120
  # so that they can be applied to fields when
118
121
  # the fragment is merged in later
119
122
  spread_node = @nodes.pop
120
- spread_node.directives.merge(@parent_directives.flatten)
121
- @parent_directives.pop
123
+ applicable_directives = pop_applicable_directives(@parent_directives)
124
+ spread_node.included ||= GraphQL::Execution::DirectiveChecks.include?(applicable_directives, context.query)
125
+ spread_node.directives.merge(applicable_directives)
122
126
  }
123
127
 
124
128
  visitor[Nodes::FragmentDefinition].leave << -> (ast_node, prev_ast_node) {
@@ -139,8 +143,9 @@ module GraphQL
139
143
  # and record any directives that were visited
140
144
  # during this field & before it (eg, inline fragments)
141
145
  field_node = @nodes.pop
142
- field_node.directives.merge(@parent_directives.flatten)
143
- @parent_directives.pop
146
+ applicable_directives = pop_applicable_directives(@parent_directives)
147
+ field_node.directives.merge(applicable_directives)
148
+ field_node.included ||= GraphQL::Execution::DirectiveChecks.include?(applicable_directives, context.query)
144
149
  }
145
150
 
146
151
  visitor[Nodes::Document].leave << -> (ast_node, prev_ast_node) {
@@ -149,12 +154,14 @@ module GraphQL
149
154
  while fragment_node = @independent_fragments.pop
150
155
  fragment_usages = @fragment_spreads[fragment_node.name]
151
156
  while dependent_node = fragment_usages.pop
152
- # remove self from dependent_node.spreads
153
- rejected_spread_nodes = dependent_node.spreads.select { |spr| spr.name == fragment_node.name }
154
- rejected_spread_nodes.each { |r_node| dependent_node.spreads.delete(r_node) }
157
+ # Find the spreads for this reference
158
+ resolved_spread_nodes = dependent_node.spreads.select { |spr| spr.name == fragment_node.name }
159
+ spread_is_included = resolved_spread_nodes.any?(&:included?)
160
+ # Since we're going to resolve them, remove them from the dependcies
161
+ resolved_spread_nodes.each { |r_node| dependent_node.spreads.delete(r_node) }
155
162
 
156
163
  # resolve the dependency (merge into dependent node)
157
- deep_merge(dependent_node, fragment_node, rejected_spread_nodes.first.directives)
164
+ deep_merge(dependent_node, fragment_node, spread_is_included)
158
165
  owner = dependent_node.owner
159
166
  if owner.ast_node.is_a?(Nodes::FragmentDefinition) && !any_fragment_spreads?(owner)
160
167
  @independent_fragments.push(owner)
@@ -166,27 +173,66 @@ module GraphQL
166
173
 
167
174
  private
168
175
 
169
- # Merge the chilren from `fragment_node` into `parent_node`. Merge `directives` into each of those fields.
170
- def deep_merge(parent_node, fragment_node, directives)
176
+ # Merge the children from `fragment_node` into `parent_node`.
177
+ # This is an implementation of "fragment inlining"
178
+ def deep_merge(parent_node, fragment_node, included)
171
179
  fragment_node.children.each do |name, child_node|
172
- deep_merge_child(parent_node, name, child_node, directives)
180
+ deep_merge_child(parent_node, name, child_node, included)
173
181
  end
174
182
  end
175
183
 
176
- # Merge `node` into `parent_node`'s children, as `name`, applying `extra_directives`
177
- def deep_merge_child(parent_node, name, node, extra_directives)
178
- child_node = parent_node.children[name] ||= node.dup
184
+ # Merge `node` into `parent_node`'s children, as `name`, applying `extra_included`
185
+ # `extra_included` comes from the spread node:
186
+ # - If the spread was included, first-level children should be included if _either_ node was included
187
+ # - If the spread was _not_ included, first-level children should be included if _a pre-existing_ node was included
188
+ # (A copied node should be excluded)
189
+ def deep_merge_child(parent_node, name, node, extra_included)
190
+ child_node = parent_node.children[name]
191
+ previously_included = child_node.nil? ? false : child_node.included?
192
+ next_included = extra_included ? (previously_included || node.included?) : previously_included
193
+
194
+ if child_node.nil?
195
+ child_node = parent_node.children[name] = node.dup
196
+ end
197
+
179
198
  child_node.definitions.merge!(node.definitions)
199
+
200
+ child_node.included = next_included
201
+
202
+
203
+
180
204
  node.children.each do |merge_child_name, merge_child_node|
181
- deep_merge_child(child_node, merge_child_name, merge_child_node, [])
205
+ deep_merge_child(child_node, merge_child_name, merge_child_node, node.included)
182
206
  end
183
- child_node.directives.merge(extra_directives)
184
207
  end
185
208
 
186
209
  # return true if node or _any_ children have a fragment spread
187
210
  def any_fragment_spreads?(node)
188
211
  node.spreads.any? || node.children.any? { |name, node| any_fragment_spreads?(node) }
189
212
  end
213
+
214
+ # pop off own directives,
215
+ # then check the last one to see if it's directives
216
+ # from an inline fragment. If it is, add them in
217
+ # @return [Array<Node>]
218
+ def pop_applicable_directives(directive_stack)
219
+ own_directives = directive_stack.pop
220
+ if directive_stack.last.is_a?(InlineFragmentDirectives)
221
+ own_directives = directive_stack.last + own_directives
222
+ end
223
+ own_directives
224
+ end
225
+
226
+
227
+ # It's an array, but can be identified with `is_a?`
228
+ class InlineFragmentDirectives
229
+ extend Forwardable
230
+ def initialize
231
+ @storage = []
232
+ end
233
+
234
+ def_delegators :@storage, :push, :+
235
+ end
190
236
  end
191
237
  end
192
238
  end
@@ -21,7 +21,9 @@ module GraphQL
21
21
  when Nodes::Argument
22
22
  "#{node.name}: #{generate(node.value)}"
23
23
  when Nodes::Directive
24
- "@#{node.name}(#{node.arguments.map { |a| generate(a) }.join(", ")})"
24
+ out = "@#{node.name}"
25
+ out << "(#{node.arguments.map { |a| generate(a) }.join(", ")})" if node.arguments.any?
26
+ out
25
27
  when Nodes::Enum
26
28
  "#{node.name}"
27
29
  when Nodes::Field
@@ -77,34 +79,46 @@ module GraphQL
77
79
  out << " subscription: #{node.subscription}\n" if node.subscription
78
80
  out << "}"
79
81
  when Nodes::ScalarTypeDefinition
80
- "scalar #{node.name}"
82
+ out = "scalar #{node.name}"
83
+ out << generate_directives(node.directives)
81
84
  when Nodes::ObjectTypeDefinition
82
85
  out = "type #{node.name}"
86
+ out << generate_directives(node.directives)
83
87
  out << " implements " << node.interfaces.join(", ") unless node.interfaces.empty?
84
88
  out << generate_field_definitions(node.fields)
85
89
  when Nodes::InputValueDefinition
86
90
  out = "#{node.name}: #{generate(node.type)}"
87
91
  out << " = #{generate(node.default_value)}" unless node.default_value.nil?
88
- out
92
+ out << generate_directives(node.directives)
89
93
  when Nodes::FieldDefinition
90
94
  out = node.name.dup
91
95
  unless node.arguments.empty?
92
96
  out << "(" << node.arguments.map{ |arg| generate(arg) }.join(", ") << ")"
93
97
  end
94
98
  out << ": #{generate(node.type)}"
99
+ out << generate_directives(node.directives)
95
100
  when Nodes::InterfaceTypeDefinition
96
101
  out = "interface #{node.name}"
102
+ out << generate_directives(node.directives)
97
103
  out << generate_field_definitions(node.fields)
98
104
  when Nodes::UnionTypeDefinition
99
- "union #{node.name} = " + node.types.join(" | ")
105
+ out = "union #{node.name}"
106
+ out << generate_directives(node.directives)
107
+ out << " = " + node.types.join(" | ")
100
108
  when Nodes::EnumTypeDefinition
101
- out = "enum #{node.name} {\n"
109
+ out = "enum #{node.name}#{generate_directives(node.directives)} {\n"
102
110
  node.values.each do |value|
103
- out << " #{value}\n"
111
+ out << generate(value)
104
112
  end
105
113
  out << "}"
114
+ when Nodes::EnumValueDefinition
115
+ out = " #{node.name}"
116
+ out << generate_directives(node.directives)
117
+ out << "\n"
106
118
  when Nodes::InputObjectTypeDefinition
107
- out = "input #{node.name} {\n"
119
+ out = "input #{node.name}"
120
+ out << generate_directives(node.directives)
121
+ out << " {\n"
108
122
  node.fields.each do |field|
109
123
  out << " #{generate(field)}\n"
110
124
  end
@@ -116,7 +130,7 @@ module GraphQL
116
130
  when Array
117
131
  "[#{node.map { |v| generate(v) }.join(", ")}]"
118
132
  when Hash
119
- "{ #{node.map { |k, v| "#{k}: #{generate(v)}" }.join(", ")} }"
133
+ "{#{node.map { |k, v| "#{k}: #{generate(v)}" }.join(", ")}}"
120
134
  else
121
135
  raise TypeError
122
136
  end