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.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/query_complexity.rb +15 -6
- data/lib/graphql/analysis/query_depth.rb +11 -10
- data/lib/graphql/directive.rb +2 -6
- data/lib/graphql/directive/include_directive.rb +0 -4
- data/lib/graphql/directive/skip_directive.rb +0 -4
- data/lib/graphql/execution/directive_checks.rb +22 -13
- data/lib/graphql/execution_error.rb +7 -0
- data/lib/graphql/internal_representation/node.rb +20 -2
- data/lib/graphql/internal_representation/rewrite.rb +66 -20
- data/lib/graphql/language/generation.rb +22 -8
- data/lib/graphql/language/nodes.rb +48 -20
- data/lib/graphql/language/parser.rb +436 -423
- data/lib/graphql/language/parser.y +22 -19
- data/lib/graphql/language/parser_tests.rb +131 -2
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -0
- data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
- data/lib/graphql/query/serial_execution/value_resolution.rb +4 -1
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/static_validation/message.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +10 -12
- data/spec/graphql/directive_spec.rb +139 -1
- data/spec/graphql/execution_error_spec.rb +63 -3
- data/spec/graphql/introspection/type_type_spec.rb +2 -0
- data/spec/graphql/language/generation_spec.rb +55 -7
- data/spec/graphql/query/executor_spec.rb +4 -2
- data/spec/graphql/schema/catchall_middleware_spec.rb +1 -0
- data/spec/graphql/schema/printer_spec.rb +1 -1
- data/spec/graphql/schema/timeout_middleware_spec.rb +10 -5
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +6 -6
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +3 -3
- data/spec/graphql/static_validation/validator_spec.rb +1 -1
- data/spec/support/dairy_app.rb +8 -0
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca81d77891fe081da31667ba3d1a8676ff489ff5
|
4
|
+
data.tar.gz: e2b4a12d439ce76943470264e60d6007ae7f61fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
37
|
-
memo[:
|
38
|
-
|
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
|
data/lib/graphql/directive.rb
CHANGED
@@ -7,9 +7,9 @@ module GraphQL
|
|
7
7
|
#
|
8
8
|
class Directive
|
9
9
|
include GraphQL::Define::InstanceDefinable
|
10
|
-
accepts_definitions :locations, :name, :description,
|
10
|
+
accepts_definitions :locations, :name, :description, argument: GraphQL::Define::AssignArgument
|
11
11
|
|
12
|
-
lazy_defined_attr_accessor :locations, :arguments, :name, :description
|
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?(
|
14
|
-
|
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?(
|
28
|
-
|
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 ...
|
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
|
-
|
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:
|
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
|
-
|
121
|
-
|
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
|
-
|
143
|
-
|
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
|
-
#
|
153
|
-
|
154
|
-
|
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,
|
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
|
170
|
-
|
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,
|
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 `
|
177
|
-
|
178
|
-
|
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}
|
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}
|
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 <<
|
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}
|
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
|
-
"{
|
133
|
+
"{#{node.map { |k, v| "#{k}: #{generate(v)}" }.join(", ")}}"
|
120
134
|
else
|
121
135
|
raise TypeError
|
122
136
|
end
|