graphql 1.7.7 → 1.7.8
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/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
|