graphql 1.7.7 → 1.7.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/enum_type.rb +1 -1
- data/lib/graphql/field.rb +10 -1
- data/lib/graphql/input_object_type.rb +3 -1
- data/lib/graphql/introspection/arguments_field.rb +1 -0
- data/lib/graphql/introspection/enum_values_field.rb +1 -0
- data/lib/graphql/introspection/fields_field.rb +1 -0
- data/lib/graphql/introspection/input_fields_field.rb +1 -0
- data/lib/graphql/introspection/interfaces_field.rb +1 -0
- data/lib/graphql/introspection/of_type_field.rb +1 -0
- data/lib/graphql/introspection/possible_types_field.rb +1 -0
- data/lib/graphql/introspection/schema_field.rb +1 -0
- data/lib/graphql/introspection/type_by_name_field.rb +1 -0
- data/lib/graphql/introspection/typename_field.rb +1 -0
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/language/document_from_schema_definition.rb +129 -37
- data/lib/graphql/language/generation.rb +3 -182
- data/lib/graphql/language/nodes.rb +12 -2
- data/lib/graphql/language/parser.rb +63 -55
- data/lib/graphql/language/parser.y +2 -1
- data/lib/graphql/language/printer.rb +351 -0
- data/lib/graphql/object_type.rb +1 -1
- data/lib/graphql/query/arguments.rb +27 -9
- data/lib/graphql/query/literal_input.rb +4 -1
- data/lib/graphql/schema/printer.rb +33 -266
- data/lib/graphql/tracing/scout_tracing.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/language/document_from_schema_definition_spec.rb +729 -296
- data/spec/graphql/language/generation_spec.rb +21 -186
- data/spec/graphql/language/nodes_spec.rb +21 -0
- data/spec/graphql/language/printer_spec.rb +203 -0
- data/spec/graphql/query/arguments_spec.rb +33 -11
- data/spec/graphql/schema/build_from_definition_spec.rb +13 -4
- data/spec/graphql/schema/printer_spec.rb +14 -14
- metadata +5 -2
@@ -14,189 +14,10 @@ module GraphQL
|
|
14
14
|
#
|
15
15
|
# @param node [GraphQL::Language::Nodes::AbstractNode] an AST node to recursively stringify
|
16
16
|
# @param indent [String] Whitespace to add to each printed node
|
17
|
+
# @param printer [GraphQL::Language::Printer] An optional custom printer for printing AST nodes. Defaults to GraphQL::Language::Printer
|
17
18
|
# @return [String] Valid GraphQL for `node`
|
18
|
-
def generate(node, indent: "")
|
19
|
-
|
20
|
-
when Nodes::Document
|
21
|
-
node.definitions.map { |d| generate(d) }.join("\n\n")
|
22
|
-
when Nodes::Argument
|
23
|
-
"#{node.name}: #{generate(node.value)}".dup
|
24
|
-
when Nodes::Directive
|
25
|
-
out = "@#{node.name}".dup
|
26
|
-
out << "(#{node.arguments.map { |a| generate(a) }.join(", ")})" if node.arguments.any?
|
27
|
-
out
|
28
|
-
when Nodes::Enum
|
29
|
-
"#{node.name}".dup
|
30
|
-
when Nodes::NullValue
|
31
|
-
"null".dup
|
32
|
-
when Nodes::Field
|
33
|
-
out = "#{indent}".dup
|
34
|
-
out << "#{node.alias}: " if node.alias
|
35
|
-
out << "#{node.name}"
|
36
|
-
out << "(#{node.arguments.map { |a| generate(a) }.join(", ")})" if node.arguments.any?
|
37
|
-
out << generate_directives(node.directives)
|
38
|
-
out << generate_selections(node.selections, indent: indent)
|
39
|
-
out
|
40
|
-
when Nodes::FragmentDefinition
|
41
|
-
out = "#{indent}fragment #{node.name}".dup
|
42
|
-
if node.type
|
43
|
-
out << " on #{generate(node.type)}"
|
44
|
-
end
|
45
|
-
out << generate_directives(node.directives)
|
46
|
-
out << generate_selections(node.selections, indent: indent)
|
47
|
-
out
|
48
|
-
when Nodes::FragmentSpread
|
49
|
-
out = "#{indent}...#{node.name}".dup
|
50
|
-
out << generate_directives(node.directives)
|
51
|
-
out
|
52
|
-
when Nodes::InlineFragment
|
53
|
-
out = "#{indent}...".dup
|
54
|
-
if node.type
|
55
|
-
out << " on #{generate(node.type)}"
|
56
|
-
end
|
57
|
-
out << generate_directives(node.directives)
|
58
|
-
out << generate_selections(node.selections, indent: indent)
|
59
|
-
out
|
60
|
-
when Nodes::InputObject
|
61
|
-
generate(node.to_h)
|
62
|
-
when Nodes::ListType
|
63
|
-
"[#{generate(node.of_type)}]".dup
|
64
|
-
when Nodes::NonNullType
|
65
|
-
"#{generate(node.of_type)}!".dup
|
66
|
-
when Nodes::OperationDefinition
|
67
|
-
out = "#{indent}#{node.operation_type}".dup
|
68
|
-
out << " #{node.name}" if node.name
|
69
|
-
out << "(#{node.variables.map { |v| generate(v) }.join(", ")})" if node.variables.any?
|
70
|
-
out << generate_directives(node.directives)
|
71
|
-
out << generate_selections(node.selections, indent: indent)
|
72
|
-
out
|
73
|
-
when Nodes::TypeName
|
74
|
-
"#{node.name}".dup
|
75
|
-
when Nodes::VariableDefinition
|
76
|
-
out = "$#{node.name}: #{generate(node.type)}".dup
|
77
|
-
out << " = #{generate(node.default_value)}" unless node.default_value.nil?
|
78
|
-
out
|
79
|
-
when Nodes::VariableIdentifier
|
80
|
-
"$#{node.name}".dup
|
81
|
-
when Nodes::SchemaDefinition
|
82
|
-
if (node.query.nil? || node.query == 'Query') &&
|
83
|
-
(node.mutation.nil? || node.mutation == 'Mutation') &&
|
84
|
-
(node.subscription.nil? || node.subscription == 'Subscription')
|
85
|
-
return
|
86
|
-
end
|
87
|
-
|
88
|
-
out = "schema {\n".dup
|
89
|
-
out << " query: #{node.query}\n" if node.query
|
90
|
-
out << " mutation: #{node.mutation}\n" if node.mutation
|
91
|
-
out << " subscription: #{node.subscription}\n" if node.subscription
|
92
|
-
out << "}"
|
93
|
-
when Nodes::ScalarTypeDefinition
|
94
|
-
out = generate_description(node)
|
95
|
-
out << "scalar #{node.name}"
|
96
|
-
out << generate_directives(node.directives)
|
97
|
-
when Nodes::ObjectTypeDefinition
|
98
|
-
out = generate_description(node)
|
99
|
-
out << "type #{node.name}"
|
100
|
-
out << generate_directives(node.directives)
|
101
|
-
out << " implements " << node.interfaces.map(&:name).join(", ") unless node.interfaces.empty?
|
102
|
-
out << generate_field_definitions(node.fields)
|
103
|
-
when Nodes::InputValueDefinition
|
104
|
-
out = "#{node.name}: #{generate(node.type)}".dup
|
105
|
-
out << " = #{generate(node.default_value)}" unless node.default_value.nil?
|
106
|
-
out << generate_directives(node.directives)
|
107
|
-
when Nodes::FieldDefinition
|
108
|
-
out = node.name.dup
|
109
|
-
unless node.arguments.empty?
|
110
|
-
out << "(" << node.arguments.map{ |arg| generate(arg) }.join(", ") << ")"
|
111
|
-
end
|
112
|
-
out << ": #{generate(node.type)}"
|
113
|
-
out << generate_directives(node.directives)
|
114
|
-
when Nodes::InterfaceTypeDefinition
|
115
|
-
out = generate_description(node)
|
116
|
-
out << "interface #{node.name}"
|
117
|
-
out << generate_directives(node.directives)
|
118
|
-
out << generate_field_definitions(node.fields)
|
119
|
-
when Nodes::UnionTypeDefinition
|
120
|
-
out = generate_description(node)
|
121
|
-
out << "union #{node.name}"
|
122
|
-
out << generate_directives(node.directives)
|
123
|
-
out << " = " + node.types.map(&:name).join(" | ")
|
124
|
-
when Nodes::EnumTypeDefinition
|
125
|
-
out = generate_description(node)
|
126
|
-
out << "enum #{node.name}#{generate_directives(node.directives)} {\n"
|
127
|
-
node.values.each.with_index do |value, i|
|
128
|
-
out << generate_description(value, indent: ' ', first_in_block: i == 0)
|
129
|
-
out << generate(value)
|
130
|
-
end
|
131
|
-
out << "}"
|
132
|
-
when Nodes::EnumValueDefinition
|
133
|
-
out = " #{node.name}".dup
|
134
|
-
out << generate_directives(node.directives)
|
135
|
-
out << "\n"
|
136
|
-
when Nodes::InputObjectTypeDefinition
|
137
|
-
out = generate_description(node)
|
138
|
-
out << "input #{node.name}"
|
139
|
-
out << generate_directives(node.directives)
|
140
|
-
out << " {\n"
|
141
|
-
node.fields.each.with_index do |field, i|
|
142
|
-
out << generate_description(field, indent: ' ', first_in_block: i == 0)
|
143
|
-
out << " #{generate(field)}\n"
|
144
|
-
end
|
145
|
-
out << "}"
|
146
|
-
when Nodes::DirectiveDefinition
|
147
|
-
out = generate_description(node)
|
148
|
-
out << "directive @#{node.name}"
|
149
|
-
out << "(#{node.arguments.map { |a| generate(a) }.join(", ")})" if node.arguments.any?
|
150
|
-
out << " on #{node.locations.join(' | ')}"
|
151
|
-
when Nodes::AbstractNode
|
152
|
-
node.to_query_string(indent: indent)
|
153
|
-
when FalseClass, Float, Integer, NilClass, String, TrueClass
|
154
|
-
GraphQL::Language.serialize(node)
|
155
|
-
when Array
|
156
|
-
"[#{node.map { |v| generate(v) }.join(", ")}]".dup
|
157
|
-
when Hash
|
158
|
-
"{#{node.map { |k, v| "#{k}: #{generate(v)}" }.join(", ")}}".dup
|
159
|
-
else
|
160
|
-
raise TypeError
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
private
|
165
|
-
|
166
|
-
def generate_directives(directives)
|
167
|
-
if directives.any?
|
168
|
-
directives.map { |d| " #{generate(d)}" }.join
|
169
|
-
else
|
170
|
-
""
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def generate_selections(selections, indent: "")
|
175
|
-
if selections.any?
|
176
|
-
out = " {\n".dup
|
177
|
-
selections.each do |selection|
|
178
|
-
out << generate(selection, indent: indent + " ") << "\n"
|
179
|
-
end
|
180
|
-
out << "#{indent}}"
|
181
|
-
else
|
182
|
-
""
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def generate_description(node, indent: '', first_in_block: true)
|
187
|
-
return ''.dup unless node.description
|
188
|
-
|
189
|
-
description = indent != '' && !first_in_block ? "\n".dup : "".dup
|
190
|
-
description << GraphQL::Language::Comments.commentize(node.description, indent: indent)
|
191
|
-
end
|
192
|
-
|
193
|
-
def generate_field_definitions(fields)
|
194
|
-
out = " {\n".dup
|
195
|
-
fields.each.with_index do |field, i|
|
196
|
-
out << generate_description(field, indent: ' ', first_in_block: i == 0)
|
197
|
-
out << " #{generate(field)}\n"
|
198
|
-
end
|
199
|
-
out << "}"
|
19
|
+
def generate(node, indent: "", printer: GraphQL::Language::Printer.new)
|
20
|
+
printer.print(node, indent: indent)
|
200
21
|
end
|
201
22
|
end
|
202
23
|
end
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
8
8
|
# - `children` returns all AST nodes attached to this one. Used for tree traversal.
|
9
9
|
# - `scalars` returns all scalar (Ruby) values attached to this one. Used for comparing nodes.
|
10
10
|
# - `to_query_string` turns an AST node into a GraphQL string
|
11
|
+
|
11
12
|
class AbstractNode
|
12
13
|
attr_accessor :line, :col, :filename
|
13
14
|
|
@@ -77,8 +78,8 @@ module GraphQL
|
|
77
78
|
[line, col]
|
78
79
|
end
|
79
80
|
|
80
|
-
def to_query_string
|
81
|
-
|
81
|
+
def to_query_string(printer: GraphQL::Language::Printer.new)
|
82
|
+
printer.print(self)
|
82
83
|
end
|
83
84
|
end
|
84
85
|
|
@@ -164,6 +165,15 @@ module GraphQL
|
|
164
165
|
# document.to_query_string
|
165
166
|
# # { ... }
|
166
167
|
#
|
168
|
+
# @example Creating a custom string from a document
|
169
|
+
# class VariableScrubber < GraphQL::Language::Printer
|
170
|
+
# def print_argument(arg)
|
171
|
+
# "#{arg.name}: <HIDDEN>"
|
172
|
+
# end
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# document.to_query_string(printer: VariableSrubber.new)
|
176
|
+
#
|
167
177
|
class Document < AbstractNode
|
168
178
|
attr_accessor :definitions
|
169
179
|
child_attributes :definitions
|
@@ -12,7 +12,7 @@ module GraphQL
|
|
12
12
|
module Language
|
13
13
|
class Parser < Racc::Parser
|
14
14
|
|
15
|
-
module_eval(<<'...end parser.y/module_eval...', 'parser.y',
|
15
|
+
module_eval(<<'...end parser.y/module_eval...', 'parser.y', 373)
|
16
16
|
|
17
17
|
def initialize(query_string, filename:, tracer: Tracing::NullTracer)
|
18
18
|
@query_string = query_string
|
@@ -425,30 +425,30 @@ racc_action_pointer = [
|
|
425
425
|
nil, nil, nil, nil ]
|
426
426
|
|
427
427
|
racc_action_default = [
|
428
|
-
-
|
429
|
-
-11, -12, -13, -109, -111, -112, -113, -
|
430
|
-
-120, -121, -122, -123, -
|
431
|
-
-
|
428
|
+
-146, -146, -1, -2, -3, -5, -6, -7, -14, -146,
|
429
|
+
-11, -12, -13, -109, -111, -112, -113, -146, -118, -119,
|
430
|
+
-120, -121, -122, -123, -146, -146, -146, -146, -146, -146,
|
431
|
+
-146, -146, -4, -16, -15, -37, -38, -39, -40, -41,
|
432
432
|
-42, -43, -44, -45, -46, -47, -48, -49, -50, -51,
|
433
|
-
-52, -53, -
|
434
|
-
-
|
435
|
-
244, -100, -
|
436
|
-
-
|
437
|
-
-
|
438
|
-
-28, -64, -65, -
|
439
|
-
-100, -
|
440
|
-
-127, -
|
441
|
-
-
|
442
|
-
-
|
443
|
-
-
|
444
|
-
-
|
433
|
+
-52, -53, -146, -10, -30, -32, -33, -34, -64, -100,
|
434
|
+
-146, -110, -146, -100, -126, -100, -100, -100, -100, -146,
|
435
|
+
244, -100, -146, -9, -31, -100, -146, -146, -101, -102,
|
436
|
+
-146, -100, -146, -146, -146, -146, -115, -146, -124, -100,
|
437
|
+
-146, -146, -146, -146, -146, -131, -146, -146, -18, -146,
|
438
|
+
-28, -64, -65, -146, -67, -146, -103, -64, -105, -21,
|
439
|
+
-100, -146, -146, -107, -100, -114, -116, -146, -146, -59,
|
440
|
+
-127, -134, -146, -146, -146, -146, -146, -8, -17, -19,
|
441
|
+
-146, -29, -35, -100, -66, -68, -79, -104, -22, -146,
|
442
|
+
-146, -26, -146, -146, -117, -134, -60, -131, -135, -146,
|
443
|
+
-138, -140, -54, -55, -56, -57, -58, -100, -62, -146,
|
444
|
+
-146, -129, -146, -146, -146, -146, -28, -69, -70, -71,
|
445
445
|
-72, -73, -74, -75, -76, -77, -78, -80, -81, -82,
|
446
|
-
-83, -
|
447
|
-
-
|
448
|
-
-132, -
|
449
|
-
-
|
450
|
-
-20, -
|
451
|
-
-
|
446
|
+
-83, -146, -146, -146, -99, -106, -23, -27, -108, -146,
|
447
|
+
-146, -136, -137, -146, -61, -63, -141, -146, -130, -142,
|
448
|
+
-132, -143, -144, -24, -36, -84, -85, -146, -87, -89,
|
449
|
+
-146, -91, -146, -146, -96, -125, -146, -139, -24, -146,
|
450
|
+
-20, -146, -86, -88, -90, -92, -146, -79, -95, -97,
|
451
|
+
-146, -100, -100, -145, -25, -146, -79, -80, -93, -146,
|
452
452
|
-133, -128, -94, -98 ]
|
453
453
|
|
454
454
|
racc_goto_table = [
|
@@ -656,19 +656,20 @@ racc_reduce_table = [
|
|
656
656
|
0, 100, :_reduce_131,
|
657
657
|
3, 100, :_reduce_132,
|
658
658
|
5, 101, :_reduce_133,
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
6,
|
667
|
-
6,
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
659
|
+
0, 97, :_reduce_134,
|
660
|
+
1, 97, :_reduce_135,
|
661
|
+
2, 97, :_reduce_136,
|
662
|
+
6, 92, :_reduce_137,
|
663
|
+
1, 102, :_reduce_138,
|
664
|
+
3, 102, :_reduce_139,
|
665
|
+
5, 93, :_reduce_140,
|
666
|
+
6, 94, :_reduce_141,
|
667
|
+
6, 95, :_reduce_142,
|
668
|
+
6, 87, :_reduce_143,
|
669
|
+
1, 103, :_reduce_144,
|
670
|
+
3, 103, :_reduce_145 ]
|
671
|
+
|
672
|
+
racc_reduce_n = 146
|
672
673
|
|
673
674
|
racc_shift_n = 244
|
674
675
|
|
@@ -1582,81 +1583,88 @@ module_eval(<<'.,.,', 'parser.y', 326)
|
|
1582
1583
|
|
1583
1584
|
module_eval(<<'.,.,', 'parser.y', 330)
|
1584
1585
|
def _reduce_134(val, _values, result)
|
1585
|
-
return [
|
1586
|
+
return []
|
1586
1587
|
result
|
1587
1588
|
end
|
1588
1589
|
.,.,
|
1589
1590
|
|
1590
1591
|
module_eval(<<'.,.,', 'parser.y', 331)
|
1591
1592
|
def _reduce_135(val, _values, result)
|
1592
|
-
val[0]
|
1593
|
+
return [val[0]]
|
1593
1594
|
result
|
1594
1595
|
end
|
1595
1596
|
.,.,
|
1596
1597
|
|
1597
|
-
module_eval(<<'.,.,', 'parser.y',
|
1598
|
+
module_eval(<<'.,.,', 'parser.y', 332)
|
1598
1599
|
def _reduce_136(val, _values, result)
|
1599
|
-
|
1600
|
-
|
1600
|
+
val[0] << val[1]
|
1601
1601
|
result
|
1602
1602
|
end
|
1603
1603
|
.,.,
|
1604
1604
|
|
1605
|
-
module_eval(<<'.,.,', 'parser.y',
|
1605
|
+
module_eval(<<'.,.,', 'parser.y', 336)
|
1606
1606
|
def _reduce_137(val, _values, result)
|
1607
|
-
|
1607
|
+
return make_node(:InterfaceTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]), position_source: val[0])
|
1608
|
+
|
1608
1609
|
result
|
1609
1610
|
end
|
1610
1611
|
.,.,
|
1611
1612
|
|
1612
1613
|
module_eval(<<'.,.,', 'parser.y', 340)
|
1613
1614
|
def _reduce_138(val, _values, result)
|
1614
|
-
|
1615
|
+
return [make_node(:TypeName, name: val[0])]
|
1615
1616
|
result
|
1616
1617
|
end
|
1617
1618
|
.,.,
|
1618
1619
|
|
1619
|
-
module_eval(<<'.,.,', 'parser.y',
|
1620
|
+
module_eval(<<'.,.,', 'parser.y', 341)
|
1620
1621
|
def _reduce_139(val, _values, result)
|
1621
|
-
|
1622
|
-
|
1622
|
+
val[0] << make_node(:TypeName, name: val[2])
|
1623
1623
|
result
|
1624
1624
|
end
|
1625
1625
|
.,.,
|
1626
1626
|
|
1627
|
-
module_eval(<<'.,.,', 'parser.y',
|
1627
|
+
module_eval(<<'.,.,', 'parser.y', 345)
|
1628
1628
|
def _reduce_140(val, _values, result)
|
1629
|
-
|
1629
|
+
return make_node(:UnionTypeDefinition, name: val[1], directives: val[2], types: val[4], description: get_description(val[0]), position_source: val[0])
|
1630
1630
|
|
1631
1631
|
result
|
1632
1632
|
end
|
1633
1633
|
.,.,
|
1634
1634
|
|
1635
|
-
module_eval(<<'.,.,', 'parser.y',
|
1635
|
+
module_eval(<<'.,.,', 'parser.y', 350)
|
1636
1636
|
def _reduce_141(val, _values, result)
|
1637
|
-
|
1637
|
+
return make_node(:EnumTypeDefinition, name: val[1], directives: val[2], values: val[4], description: get_description(val[0]), position_source: val[0])
|
1638
1638
|
|
1639
1639
|
result
|
1640
1640
|
end
|
1641
1641
|
.,.,
|
1642
1642
|
|
1643
|
-
module_eval(<<'.,.,', 'parser.y',
|
1643
|
+
module_eval(<<'.,.,', 'parser.y', 355)
|
1644
1644
|
def _reduce_142(val, _values, result)
|
1645
|
-
return make_node(:
|
1645
|
+
return make_node(:InputObjectTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]), position_source: val[0])
|
1646
1646
|
|
1647
1647
|
result
|
1648
1648
|
end
|
1649
1649
|
.,.,
|
1650
1650
|
|
1651
|
-
module_eval(<<'.,.,', 'parser.y',
|
1651
|
+
module_eval(<<'.,.,', 'parser.y', 360)
|
1652
1652
|
def _reduce_143(val, _values, result)
|
1653
|
-
|
1653
|
+
return make_node(:DirectiveDefinition, name: val[2], arguments: val[3], locations: val[5], description: get_description(val[0]), position_source: val[0])
|
1654
|
+
|
1654
1655
|
result
|
1655
1656
|
end
|
1656
1657
|
.,.,
|
1657
1658
|
|
1658
1659
|
module_eval(<<'.,.,', 'parser.y', 364)
|
1659
1660
|
def _reduce_144(val, _values, result)
|
1661
|
+
return [val[0].to_s]
|
1662
|
+
result
|
1663
|
+
end
|
1664
|
+
.,.,
|
1665
|
+
|
1666
|
+
module_eval(<<'.,.,', 'parser.y', 365)
|
1667
|
+
def _reduce_145(val, _values, result)
|
1660
1668
|
val[0] << val[2].to_s
|
1661
1669
|
result
|
1662
1670
|
end
|
@@ -328,7 +328,8 @@ rule
|
|
328
328
|
}
|
329
329
|
|
330
330
|
field_definition_list:
|
331
|
-
|
331
|
+
/* none */ { return [] }
|
332
|
+
| field_definition { return [val[0]] }
|
332
333
|
| field_definition_list field_definition { val[0] << val[1] }
|
333
334
|
|
334
335
|
interface_type_definition:
|
@@ -0,0 +1,351 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Language
|
4
|
+
class Printer
|
5
|
+
# Turn an arbitrary AST node back into a string.
|
6
|
+
#
|
7
|
+
# @example Turning a document into a query string
|
8
|
+
# document = GraphQL.parse(query_string)
|
9
|
+
# GraphQL::Language::Printer.new.print(document)
|
10
|
+
# # => "{ ... }"
|
11
|
+
#
|
12
|
+
#
|
13
|
+
# @example Building a custom printer
|
14
|
+
#
|
15
|
+
# class MyPrinter < GraphQL::Language::Printer
|
16
|
+
# def print_argument(arg)
|
17
|
+
# "#{arg.name}: <HIDDEN>"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# MyPrinter.new.print(document)
|
22
|
+
# # => "mutation { pay(creditCard: <HIDDEN>) { success } }"
|
23
|
+
#
|
24
|
+
#
|
25
|
+
# @param indent [String] Whitespace to add to the printed node
|
26
|
+
# @return [String] Valid GraphQL for `node`
|
27
|
+
def print(node, indent: "")
|
28
|
+
print_node(node, indent: indent)
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def print_document(document)
|
34
|
+
document.definitions.map { |d| print_node(d) }.join("\n\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
def print_argument(argument)
|
38
|
+
"#{argument.name}: #{print_node(argument.value)}".dup
|
39
|
+
end
|
40
|
+
|
41
|
+
def print_directive(directive)
|
42
|
+
out = "@#{directive.name}".dup
|
43
|
+
|
44
|
+
if directive.arguments.any?
|
45
|
+
out << "(#{directive.arguments.map { |a| print_argument(a) }.join(", ")})"
|
46
|
+
end
|
47
|
+
|
48
|
+
out
|
49
|
+
end
|
50
|
+
|
51
|
+
def print_enum(enum)
|
52
|
+
"#{enum.name}".dup
|
53
|
+
end
|
54
|
+
|
55
|
+
def print_null_value
|
56
|
+
"null".dup
|
57
|
+
end
|
58
|
+
|
59
|
+
def print_field(field, indent: "")
|
60
|
+
out = "#{indent}".dup
|
61
|
+
out << "#{field.alias}: " if field.alias
|
62
|
+
out << "#{field.name}"
|
63
|
+
out << "(#{field.arguments.map { |a| print_argument(a) }.join(", ")})" if field.arguments.any?
|
64
|
+
out << print_directives(field.directives)
|
65
|
+
out << print_selections(field.selections, indent: indent)
|
66
|
+
out
|
67
|
+
end
|
68
|
+
|
69
|
+
def print_fragment_definition(fragment_def, indent: "")
|
70
|
+
out = "#{indent}fragment #{fragment_def.name}".dup
|
71
|
+
if fragment_def.type
|
72
|
+
out << " on #{print_node(fragment_def.type)}"
|
73
|
+
end
|
74
|
+
out << print_directives(fragment_def.directives)
|
75
|
+
out << print_selections(fragment_def.selections, indent: indent)
|
76
|
+
out
|
77
|
+
end
|
78
|
+
|
79
|
+
def print_fragment_spread(fragment_spread, indent: "")
|
80
|
+
out = "#{indent}...#{fragment_spread.name}".dup
|
81
|
+
out << print_directives(fragment_spread.directives)
|
82
|
+
out
|
83
|
+
end
|
84
|
+
|
85
|
+
def print_inline_fragment(inline_fragment, indent: "")
|
86
|
+
out = "#{indent}...".dup
|
87
|
+
if inline_fragment.type
|
88
|
+
out << " on #{print_node(inline_fragment.type)}"
|
89
|
+
end
|
90
|
+
out << print_directives(inline_fragment.directives)
|
91
|
+
out << print_selections(inline_fragment.selections, indent: indent)
|
92
|
+
out
|
93
|
+
end
|
94
|
+
|
95
|
+
def print_input_object(input_object)
|
96
|
+
print_node(input_object.to_h)
|
97
|
+
end
|
98
|
+
|
99
|
+
def print_list_type(list_type)
|
100
|
+
"[#{print_node(list_type.of_type)}]".dup
|
101
|
+
end
|
102
|
+
|
103
|
+
def print_non_null_type(non_null_type)
|
104
|
+
"#{print_node(non_null_type.of_type)}!".dup
|
105
|
+
end
|
106
|
+
|
107
|
+
def print_operation_definition(operation_definition, indent: "")
|
108
|
+
out = "#{indent}#{operation_definition.operation_type}".dup
|
109
|
+
out << " #{operation_definition.name}" if operation_definition.name
|
110
|
+
|
111
|
+
if operation_definition.variables.any?
|
112
|
+
out << "(#{operation_definition.variables.map { |v| print_variable_definition(v) }.join(", ")})"
|
113
|
+
end
|
114
|
+
|
115
|
+
out << print_directives(operation_definition.directives)
|
116
|
+
out << print_selections(operation_definition.selections, indent: indent)
|
117
|
+
out
|
118
|
+
end
|
119
|
+
|
120
|
+
def print_type_name(type_name)
|
121
|
+
"#{type_name.name}".dup
|
122
|
+
end
|
123
|
+
|
124
|
+
def print_variable_definition(variable_definition)
|
125
|
+
out = "$#{variable_definition.name}: #{print_node(variable_definition.type)}".dup
|
126
|
+
out << " = #{print_node(variable_definition.default_value)}" unless variable_definition.default_value.nil?
|
127
|
+
out
|
128
|
+
end
|
129
|
+
|
130
|
+
def print_variable_identifier(variable_identifier)
|
131
|
+
"$#{variable_identifier.name}".dup
|
132
|
+
end
|
133
|
+
|
134
|
+
def print_schema_definition(schema)
|
135
|
+
if (schema.query.nil? || schema.query == 'Query') &&
|
136
|
+
(schema.mutation.nil? || schema.mutation == 'Mutation') &&
|
137
|
+
(schema.subscription.nil? || schema.subscription == 'Subscription')
|
138
|
+
return
|
139
|
+
end
|
140
|
+
|
141
|
+
out = "schema {\n".dup
|
142
|
+
out << " query: #{schema.query}\n" if schema.query
|
143
|
+
out << " mutation: #{schema.mutation}\n" if schema.mutation
|
144
|
+
out << " subscription: #{schema.subscription}\n" if schema.subscription
|
145
|
+
out << "}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def print_scalar_type_definition(scalar_type)
|
149
|
+
out = print_description(scalar_type)
|
150
|
+
out << "scalar #{scalar_type.name}"
|
151
|
+
out << print_directives(scalar_type.directives)
|
152
|
+
end
|
153
|
+
|
154
|
+
def print_object_type_definition(object_type)
|
155
|
+
out = print_description(object_type)
|
156
|
+
out << "type #{object_type.name}"
|
157
|
+
out << print_directives(object_type.directives)
|
158
|
+
out << " implements " << object_type.interfaces.map(&:name).join(", ") unless object_type.interfaces.empty?
|
159
|
+
out << print_field_definitions(object_type.fields)
|
160
|
+
end
|
161
|
+
|
162
|
+
def print_input_value_definition(input_value)
|
163
|
+
out = "#{input_value.name}: #{print_node(input_value.type)}".dup
|
164
|
+
out << " = #{print_node(input_value.default_value)}" unless input_value.default_value.nil?
|
165
|
+
out << print_directives(input_value.directives)
|
166
|
+
end
|
167
|
+
|
168
|
+
def print_arguments(arguments, indent: "")
|
169
|
+
if arguments.all?{ |arg| !arg.description }
|
170
|
+
return "(#{arguments.map{ |arg| print_input_value_definition(arg) }.join(", ")})"
|
171
|
+
end
|
172
|
+
|
173
|
+
out = "(\n".dup
|
174
|
+
out << arguments.map.with_index{ |arg, i|
|
175
|
+
"#{print_description(arg, indent: " " + indent, first_in_block: i == 0)} #{indent}"\
|
176
|
+
"#{print_input_value_definition(arg)}"
|
177
|
+
}.join("\n")
|
178
|
+
out << "\n#{indent})"
|
179
|
+
end
|
180
|
+
|
181
|
+
def print_field_definition(field)
|
182
|
+
out = field.name.dup
|
183
|
+
unless field.arguments.empty?
|
184
|
+
out << print_arguments(field.arguments, indent: " ")
|
185
|
+
end
|
186
|
+
out << ": #{print_node(field.type)}"
|
187
|
+
out << print_directives(field.directives)
|
188
|
+
end
|
189
|
+
|
190
|
+
def print_interface_type_definition(interface_type)
|
191
|
+
out = print_description(interface_type)
|
192
|
+
out << "interface #{interface_type.name}"
|
193
|
+
out << print_directives(interface_type.directives)
|
194
|
+
out << print_field_definitions(interface_type.fields)
|
195
|
+
end
|
196
|
+
|
197
|
+
def print_union_type_definition(union_type)
|
198
|
+
out = print_description(union_type)
|
199
|
+
out << "union #{union_type.name}"
|
200
|
+
out << print_directives(union_type.directives)
|
201
|
+
out << " = " + union_type.types.map(&:name).join(" | ")
|
202
|
+
end
|
203
|
+
|
204
|
+
def print_enum_type_definition(enum_type)
|
205
|
+
out = print_description(enum_type)
|
206
|
+
out << "enum #{enum_type.name}#{print_directives(enum_type.directives)} {\n"
|
207
|
+
enum_type.values.each.with_index do |value, i|
|
208
|
+
out << print_description(value, indent: ' ', first_in_block: i == 0)
|
209
|
+
out << print_enum_value_definition(value)
|
210
|
+
end
|
211
|
+
out << "}"
|
212
|
+
end
|
213
|
+
|
214
|
+
def print_enum_value_definition(enum_value)
|
215
|
+
out = " #{enum_value.name}".dup
|
216
|
+
out << print_directives(enum_value.directives)
|
217
|
+
out << "\n"
|
218
|
+
end
|
219
|
+
|
220
|
+
def print_input_object_type_definition(input_object_type)
|
221
|
+
out = print_description(input_object_type)
|
222
|
+
out << "input #{input_object_type.name}"
|
223
|
+
out << print_directives(input_object_type.directives)
|
224
|
+
out << " {\n"
|
225
|
+
input_object_type.fields.each.with_index do |field, i|
|
226
|
+
out << print_description(field, indent: ' ', first_in_block: i == 0)
|
227
|
+
out << " #{print_input_value_definition(field)}\n"
|
228
|
+
end
|
229
|
+
out << "}"
|
230
|
+
end
|
231
|
+
|
232
|
+
def print_directive_definition(directive)
|
233
|
+
out = print_description(directive)
|
234
|
+
out << "directive @#{directive.name}"
|
235
|
+
|
236
|
+
if directive.arguments.any?
|
237
|
+
out << print_arguments(directive.arguments)
|
238
|
+
end
|
239
|
+
|
240
|
+
out << " on #{directive.locations.join(' | ')}"
|
241
|
+
end
|
242
|
+
|
243
|
+
def print_description(node, indent: "", first_in_block: true)
|
244
|
+
return ''.dup unless node.description
|
245
|
+
|
246
|
+
description = indent != '' && !first_in_block ? "\n".dup : "".dup
|
247
|
+
description << GraphQL::Language::Comments.commentize(node.description, indent: indent)
|
248
|
+
end
|
249
|
+
|
250
|
+
def print_field_definitions(fields)
|
251
|
+
out = " {\n".dup
|
252
|
+
fields.each.with_index do |field, i|
|
253
|
+
out << print_description(field, indent: ' ', first_in_block: i == 0)
|
254
|
+
out << " #{print_field_definition(field)}\n"
|
255
|
+
end
|
256
|
+
out << "}"
|
257
|
+
end
|
258
|
+
|
259
|
+
def print_directives(directives)
|
260
|
+
if directives.any?
|
261
|
+
directives.map { |d| " #{print_directive(d)}" }.join
|
262
|
+
else
|
263
|
+
""
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def print_selections(selections, indent: "")
|
268
|
+
if selections.any?
|
269
|
+
out = " {\n".dup
|
270
|
+
selections.each do |selection|
|
271
|
+
out << print_node(selection, indent: indent + " ") << "\n"
|
272
|
+
end
|
273
|
+
out << "#{indent}}"
|
274
|
+
else
|
275
|
+
""
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def print_node(node, indent: "")
|
280
|
+
case node
|
281
|
+
when Nodes::Document
|
282
|
+
print_document(node)
|
283
|
+
when Nodes::Argument
|
284
|
+
print_argument(node)
|
285
|
+
when Nodes::Directive
|
286
|
+
print_directive(node)
|
287
|
+
when Nodes::Enum
|
288
|
+
print_enum(node)
|
289
|
+
when Nodes::NullValue
|
290
|
+
print_null_value
|
291
|
+
when Nodes::Field
|
292
|
+
print_field(node, indent: indent)
|
293
|
+
when Nodes::FragmentDefinition
|
294
|
+
print_fragment_definition(node, indent: indent)
|
295
|
+
when Nodes::FragmentSpread
|
296
|
+
print_fragment_spread(node, indent: indent)
|
297
|
+
when Nodes::InlineFragment
|
298
|
+
print_inline_fragment(node, indent: indent)
|
299
|
+
when Nodes::InputObject
|
300
|
+
print_input_object(node)
|
301
|
+
when Nodes::ListType
|
302
|
+
print_list_type(node)
|
303
|
+
when Nodes::NonNullType
|
304
|
+
print_non_null_type(node)
|
305
|
+
when Nodes::OperationDefinition
|
306
|
+
print_operation_definition(node, indent: indent)
|
307
|
+
when Nodes::TypeName
|
308
|
+
print_type_name(node)
|
309
|
+
when Nodes::VariableDefinition
|
310
|
+
print_variable_definition(node)
|
311
|
+
when Nodes::VariableIdentifier
|
312
|
+
print_variable_identifier(node)
|
313
|
+
when Nodes::SchemaDefinition
|
314
|
+
print_schema_definition(node)
|
315
|
+
when Nodes::ScalarTypeDefinition
|
316
|
+
print_scalar_type_definition(node)
|
317
|
+
when Nodes::ObjectTypeDefinition
|
318
|
+
print_object_type_definition(node)
|
319
|
+
when Nodes::InputValueDefinition
|
320
|
+
print_input_value_definition(node)
|
321
|
+
when Nodes::FieldDefinition
|
322
|
+
print_field_definition(node)
|
323
|
+
when Nodes::InterfaceTypeDefinition
|
324
|
+
print_interface_type_definition(node)
|
325
|
+
when Nodes::UnionTypeDefinition
|
326
|
+
print_union_type_definition(node)
|
327
|
+
when Nodes::EnumTypeDefinition
|
328
|
+
print_enum_type_definition(node)
|
329
|
+
when Nodes::EnumValueDefinition
|
330
|
+
print_enum_value_definition(node)
|
331
|
+
when Nodes::InputObjectTypeDefinition
|
332
|
+
print_input_object_type_definition(node)
|
333
|
+
when Nodes::DirectiveDefinition
|
334
|
+
print_directive_definition(node)
|
335
|
+
when FalseClass, Float, Integer, NilClass, String, TrueClass, Symbol
|
336
|
+
GraphQL::Language.serialize(node)
|
337
|
+
when Array
|
338
|
+
"[#{node.map { |v| print_node(v) }.join(", ")}]".dup
|
339
|
+
when Hash
|
340
|
+
"{#{node.map { |k, v| "#{k}: #{print_node(v)}" }.join(", ")}}".dup
|
341
|
+
else
|
342
|
+
raise TypeError
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
private
|
347
|
+
|
348
|
+
attr_reader :node
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|