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
data/lib/graphql/object_type.rb
CHANGED
@@ -32,7 +32,7 @@ module GraphQL
|
|
32
32
|
# @return [Hash<String => GraphQL::Field>] Map String fieldnames to their {GraphQL::Field} implementations
|
33
33
|
|
34
34
|
# @!attribute mutation
|
35
|
-
# @return [GraphQL::Relay::Mutation, nil] The mutation this
|
35
|
+
# @return [GraphQL::Relay::Mutation, nil] The mutation this object type was derived from, if it is an auto-generated payload type.
|
36
36
|
|
37
37
|
def initialize
|
38
38
|
super
|
@@ -12,8 +12,8 @@ module GraphQL
|
|
12
12
|
argument_owner.arguments_class = Class.new(self) do
|
13
13
|
self.argument_definitions = argument_definitions
|
14
14
|
|
15
|
-
def initialize(values)
|
16
|
-
super(values, argument_definitions: self.class.argument_definitions)
|
15
|
+
def initialize(values, defaults_used)
|
16
|
+
super(values, argument_definitions: self.class.argument_definitions, defaults_used: defaults_used)
|
17
17
|
end
|
18
18
|
|
19
19
|
argument_definitions.each do |_arg_name, arg_definition|
|
@@ -36,13 +36,16 @@ module GraphQL
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def initialize(values, argument_definitions:)
|
39
|
+
def initialize(values, argument_definitions:, defaults_used:)
|
40
40
|
@argument_values = values.inject({}) do |memo, (inner_key, inner_value)|
|
41
|
-
|
41
|
+
arg_name = inner_key.to_s
|
42
|
+
|
43
|
+
arg_defn = argument_definitions[arg_name]
|
44
|
+
arg_default_used = defaults_used.include?(arg_name)
|
42
45
|
|
43
46
|
arg_value = wrap_value(inner_value, arg_defn.type)
|
44
47
|
string_key = arg_defn.expose_as
|
45
|
-
memo[string_key] = ArgumentValue.new(string_key, arg_value, arg_defn)
|
48
|
+
memo[string_key] = ArgumentValue.new(string_key, arg_value, arg_defn, arg_default_used)
|
46
49
|
memo
|
47
50
|
end
|
48
51
|
end
|
@@ -61,6 +64,13 @@ module GraphQL
|
|
61
64
|
@argument_values.key?(key_s)
|
62
65
|
end
|
63
66
|
|
67
|
+
# @param key [String, Symbol] name of value to access
|
68
|
+
# @return [Boolean] true if the argument default was passed as the argument value to the resolver
|
69
|
+
def default_used?(key)
|
70
|
+
key_s = key.is_a?(String) ? key : key.to_s
|
71
|
+
@argument_values.fetch(key_s, NULL_ARGUMENT_VALUE).default_used?
|
72
|
+
end
|
73
|
+
|
64
74
|
# Get the hash of all values, with stringified keys
|
65
75
|
# @return [Hash] the stringified hash
|
66
76
|
def to_h
|
@@ -85,7 +95,7 @@ module GraphQL
|
|
85
95
|
end
|
86
96
|
end
|
87
97
|
|
88
|
-
NO_ARGS = self.new({}, argument_definitions: [])
|
98
|
+
NO_ARGS = self.new({}, argument_definitions: [], defaults_used: Set.new)
|
89
99
|
|
90
100
|
class << self
|
91
101
|
attr_accessor :argument_definitions
|
@@ -95,14 +105,22 @@ module GraphQL
|
|
95
105
|
|
96
106
|
class ArgumentValue
|
97
107
|
attr_reader :key, :value, :definition
|
98
|
-
|
108
|
+
attr_writer :default_used
|
109
|
+
|
110
|
+
def initialize(key, value, definition, default_used)
|
99
111
|
@key = key
|
100
112
|
@value = value
|
101
113
|
@definition = definition
|
114
|
+
@default_used = default_used
|
115
|
+
end
|
116
|
+
|
117
|
+
# @return [Boolean] true if the argument default was passed as the argument value to the resolver
|
118
|
+
def default_used?
|
119
|
+
@default_used
|
102
120
|
end
|
103
121
|
end
|
104
122
|
|
105
|
-
NULL_ARGUMENT_VALUE = ArgumentValue.new(nil, nil, nil)
|
123
|
+
NULL_ARGUMENT_VALUE = ArgumentValue.new(nil, nil, nil, nil)
|
106
124
|
|
107
125
|
def wrap_value(value, arg_defn_type)
|
108
126
|
if value.nil?
|
@@ -115,7 +133,7 @@ module GraphQL
|
|
115
133
|
wrap_value(value, arg_defn_type.of_type)
|
116
134
|
when GraphQL::InputObjectType
|
117
135
|
if value.is_a?(Hash)
|
118
|
-
arg_defn_type.arguments_class.new(value)
|
136
|
+
arg_defn_type.arguments_class.new(value, Set.new)
|
119
137
|
else
|
120
138
|
value
|
121
139
|
end
|
@@ -49,6 +49,8 @@ module GraphQL
|
|
49
49
|
def self.from_arguments(ast_arguments, argument_owner, variables)
|
50
50
|
context = variables ? variables.context : nil
|
51
51
|
values_hash = {}
|
52
|
+
defaults_used = Set.new
|
53
|
+
|
52
54
|
indexed_arguments = case ast_arguments
|
53
55
|
when Hash
|
54
56
|
ast_arguments
|
@@ -93,6 +95,7 @@ module GraphQL
|
|
93
95
|
# then add the default value.
|
94
96
|
if arg_defn.default_value? && !values_hash.key?(arg_name)
|
95
97
|
value = arg_defn.default_value
|
98
|
+
defaults_used << arg_name
|
96
99
|
# `context` isn't present when pre-calculating defaults
|
97
100
|
if context
|
98
101
|
value = arg_defn.prepare(value, context)
|
@@ -105,7 +108,7 @@ module GraphQL
|
|
105
108
|
end
|
106
109
|
end
|
107
110
|
|
108
|
-
argument_owner.arguments_class.new(values_hash)
|
111
|
+
argument_owner.arguments_class.new(values_hash, defaults_used)
|
109
112
|
end
|
110
113
|
end
|
111
114
|
end
|
@@ -36,7 +36,7 @@ module GraphQL
|
|
36
36
|
# printer = GraphQL::Schema::Printer.new(MySchema)
|
37
37
|
# puts printer.print_type(post_type)
|
38
38
|
#
|
39
|
-
class Printer
|
39
|
+
class Printer < GraphQL::Language::Printer
|
40
40
|
attr_reader :schema, :warden
|
41
41
|
|
42
42
|
# @param schema [GraphQL::Schema]
|
@@ -45,21 +45,32 @@ module GraphQL
|
|
45
45
|
# @param except [<#call(member, ctx)>]
|
46
46
|
# @param introspection [Boolean] Should include the introspection types in the string?
|
47
47
|
def initialize(schema, context: nil, only: nil, except: nil, introspection: false)
|
48
|
-
@
|
49
|
-
|
48
|
+
@document_from_schema = GraphQL::Language::DocumentFromSchemaDefinition.new(
|
49
|
+
schema,
|
50
|
+
context: context,
|
51
|
+
only: only,
|
52
|
+
except: except,
|
53
|
+
include_introspection_types: introspection,
|
54
|
+
)
|
55
|
+
|
56
|
+
@document = @document_from_schema.document
|
50
57
|
|
51
|
-
|
52
|
-
filter = GraphQL::Filter.new(except: blacklist)
|
53
|
-
@warden = GraphQL::Schema::Warden.new(filter, schema: @schema, context: @context)
|
58
|
+
@schema = schema
|
54
59
|
end
|
55
60
|
|
56
61
|
# Return the GraphQL schema string for the introspection type system
|
57
62
|
def self.print_introspection_schema
|
58
63
|
query_root = ObjectType.define(name: "Root")
|
59
64
|
schema = GraphQL::Schema.define(query: query_root)
|
60
|
-
|
61
|
-
|
62
|
-
|
65
|
+
|
66
|
+
introspection_schema_ast = GraphQL::Language::DocumentFromSchemaDefinition.new(
|
67
|
+
schema,
|
68
|
+
except: ->(member, _) { member.name == "Root" },
|
69
|
+
include_introspection_types: true,
|
70
|
+
include_built_in_directives: true,
|
71
|
+
).document
|
72
|
+
|
73
|
+
introspection_schema_ast.to_query_string(printer: IntrospectionPrinter.new)
|
63
74
|
end
|
64
75
|
|
65
76
|
# Return a GraphQL schema string for the defined types in the schema
|
@@ -74,277 +85,33 @@ module GraphQL
|
|
74
85
|
|
75
86
|
# Return a GraphQL schema string for the defined types in the schema
|
76
87
|
def print_schema
|
77
|
-
|
78
|
-
|
79
|
-
printable_types = warden.types.reject(&:default_scalar?)
|
80
|
-
|
81
|
-
type_definitions = printable_types
|
82
|
-
.sort_by(&:name)
|
83
|
-
.map { |type| print_type(type) }
|
84
|
-
|
85
|
-
[print_schema_definition].compact
|
86
|
-
.concat(directive_definitions)
|
87
|
-
.concat(type_definitions).join("\n\n")
|
88
|
+
print(@document)
|
88
89
|
end
|
89
90
|
|
90
91
|
def print_type(type)
|
91
|
-
|
92
|
+
node = @document_from_schema.build_object_type_node(type)
|
93
|
+
print(node)
|
92
94
|
end
|
93
95
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
IS_USER_DEFINED_MEMBER = ->(member) {
|
98
|
-
case member
|
99
|
-
when GraphQL::BaseType
|
100
|
-
!member.introspection?
|
101
|
-
when GraphQL::Directive
|
102
|
-
!member.default_directive?
|
103
|
-
else
|
104
|
-
true
|
105
|
-
end
|
106
|
-
}
|
107
|
-
|
108
|
-
private_constant :IS_USER_DEFINED_MEMBER
|
96
|
+
def print_directive(directive)
|
97
|
+
if directive.name == "deprecated"
|
98
|
+
reason = directive.arguments.find { |arg| arg.name == "reason" }
|
109
99
|
|
110
|
-
|
111
|
-
|
112
|
-
if only
|
113
|
-
->(m, ctx) { !only.call(m, ctx) }
|
114
|
-
elsif except
|
115
|
-
except
|
100
|
+
if reason.value == GraphQL::Directive::DEFAULT_DEPRECATION_REASON
|
101
|
+
"@deprecated"
|
116
102
|
else
|
117
|
-
|
103
|
+
"@deprecated(reason: \"#{reason.value}\")"
|
118
104
|
end
|
119
105
|
else
|
120
|
-
|
121
|
-
->(m, ctx) { !(IS_USER_DEFINED_MEMBER.call(m) && only.call(m, ctx)) }
|
122
|
-
elsif except
|
123
|
-
->(m, ctx) { !IS_USER_DEFINED_MEMBER.call(m) || except.call(m, ctx) }
|
124
|
-
else
|
125
|
-
->(m, ctx) { !IS_USER_DEFINED_MEMBER.call(m) }
|
126
|
-
end
|
106
|
+
super
|
127
107
|
end
|
128
108
|
end
|
129
109
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
(schema.subscription.nil? || schema.subscription.name == 'Subscription')
|
134
|
-
return
|
110
|
+
class IntrospectionPrinter < GraphQL::Language::Printer
|
111
|
+
def print_schema_definition(schema)
|
112
|
+
"schema {\n query: Root\n}"
|
135
113
|
end
|
136
|
-
|
137
|
-
operations = [:query, :mutation, :subscription].map do |operation_type|
|
138
|
-
object_type = schema.public_send(operation_type)
|
139
|
-
# Special treatment for the introspection schema, which prints `{ query: "Root" }`
|
140
|
-
if object_type && (warden.get_type(object_type.name) || (object_type.name == "Root" && schema.query == object_type))
|
141
|
-
" #{operation_type}: #{object_type.name}\n"
|
142
|
-
else
|
143
|
-
nil
|
144
|
-
end
|
145
|
-
end.compact.join
|
146
|
-
"schema {\n#{operations}}"
|
147
|
-
end
|
148
|
-
|
149
|
-
def print_directive(directive)
|
150
|
-
TypeKindPrinters::DirectivePrinter.print(warden, directive)
|
151
|
-
end
|
152
|
-
|
153
|
-
module TypeKindPrinters
|
154
|
-
module DeprecatedPrinter
|
155
|
-
def print_deprecated(field_or_enum_value)
|
156
|
-
return unless field_or_enum_value.deprecation_reason
|
157
|
-
|
158
|
-
case field_or_enum_value.deprecation_reason
|
159
|
-
when nil
|
160
|
-
''
|
161
|
-
when '', GraphQL::Directive::DEFAULT_DEPRECATION_REASON
|
162
|
-
' @deprecated'
|
163
|
-
else
|
164
|
-
" @deprecated(reason: #{field_or_enum_value.deprecation_reason.to_s.inspect})"
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
module DescriptionPrinter
|
170
|
-
def print_description(definition, indentation='', first_in_block=true)
|
171
|
-
return '' unless definition.description
|
172
|
-
|
173
|
-
description = indentation != '' && !first_in_block ? "\n".dup : "".dup
|
174
|
-
description << GraphQL::Language::Comments.commentize(definition.description, indent: indentation)
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
module ArgsPrinter
|
179
|
-
include DescriptionPrinter
|
180
|
-
def print_args(warden, field, indentation = '')
|
181
|
-
arguments = warden.arguments(field)
|
182
|
-
return if arguments.empty?
|
183
|
-
|
184
|
-
if arguments.all?{ |arg| !arg.description }
|
185
|
-
return "(#{arguments.map{ |arg| print_input_value(arg) }.join(", ")})"
|
186
|
-
end
|
187
|
-
|
188
|
-
out = "(\n".dup
|
189
|
-
out << arguments.sort_by(&:name).map.with_index{ |arg, i|
|
190
|
-
"#{print_description(arg, " #{indentation}", i == 0)} #{indentation}"\
|
191
|
-
"#{print_input_value(arg)}"
|
192
|
-
}.join("\n")
|
193
|
-
out << "\n#{indentation})"
|
194
|
-
end
|
195
|
-
|
196
|
-
def print_input_value(arg)
|
197
|
-
if arg.default_value?
|
198
|
-
default_string = " = #{print_value(arg.default_value, arg.type)}"
|
199
|
-
else
|
200
|
-
default_string = nil
|
201
|
-
end
|
202
|
-
|
203
|
-
"#{arg.name}: #{arg.type.to_s}#{default_string}"
|
204
|
-
end
|
205
|
-
|
206
|
-
def print_value(value, type)
|
207
|
-
case type
|
208
|
-
when FLOAT_TYPE
|
209
|
-
return 'null' if value.nil?
|
210
|
-
value.to_f.inspect
|
211
|
-
when INT_TYPE
|
212
|
-
return 'null' if value.nil?
|
213
|
-
value.to_i.inspect
|
214
|
-
when BOOLEAN_TYPE
|
215
|
-
return 'null' if value.nil?
|
216
|
-
(!!value).inspect
|
217
|
-
when ScalarType, ID_TYPE, STRING_TYPE
|
218
|
-
return 'null' if value.nil?
|
219
|
-
value.to_s.inspect
|
220
|
-
when EnumType
|
221
|
-
return 'null' if value.nil?
|
222
|
-
type.coerce_isolated_result(value)
|
223
|
-
when InputObjectType
|
224
|
-
return 'null' if value.nil?
|
225
|
-
fields = value.to_h.map{ |field_name, field_value|
|
226
|
-
field_type = type.input_fields.fetch(field_name.to_s).type
|
227
|
-
"#{field_name}: #{print_value(field_value, field_type)}"
|
228
|
-
}.join(", ")
|
229
|
-
"{#{fields}}"
|
230
|
-
when NonNullType
|
231
|
-
print_value(value, type.of_type)
|
232
|
-
when ListType
|
233
|
-
return 'null' if value.nil?
|
234
|
-
"[#{value.to_a.map{ |v| print_value(v, type.of_type) }.join(", ")}]"
|
235
|
-
else
|
236
|
-
raise NotImplementedError, "Unexpected value type #{type.inspect}"
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
module FieldPrinter
|
242
|
-
include DeprecatedPrinter
|
243
|
-
include ArgsPrinter
|
244
|
-
include DescriptionPrinter
|
245
|
-
def print_fields(warden, type)
|
246
|
-
fields = warden.fields(type)
|
247
|
-
fields.sort_by(&:name).map.with_index { |field, i|
|
248
|
-
"#{print_description(field, ' ', i == 0)}"\
|
249
|
-
" #{field.name}#{print_args(warden, field, ' ')}: #{field.type}#{print_deprecated(field)}"
|
250
|
-
}.join("\n")
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
class DirectivePrinter
|
255
|
-
extend ArgsPrinter
|
256
|
-
extend DescriptionPrinter
|
257
|
-
def self.print(warden, directive)
|
258
|
-
"#{print_description(directive)}"\
|
259
|
-
"directive @#{directive.name}#{print_args(warden, directive)} "\
|
260
|
-
"on #{directive.locations.join(' | ')}"
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
class ScalarPrinter
|
265
|
-
extend DescriptionPrinter
|
266
|
-
def self.print(warden, type)
|
267
|
-
"#{print_description(type)}"\
|
268
|
-
"scalar #{type.name}"
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
class ObjectPrinter
|
273
|
-
extend FieldPrinter
|
274
|
-
extend DescriptionPrinter
|
275
|
-
def self.print(warden, type)
|
276
|
-
interfaces = warden.interfaces(type)
|
277
|
-
if interfaces.any?
|
278
|
-
implementations = " implements #{interfaces.sort_by(&:name).map(&:to_s).join(", ")}"
|
279
|
-
else
|
280
|
-
implementations = nil
|
281
|
-
end
|
282
|
-
|
283
|
-
"#{print_description(type)}"\
|
284
|
-
"type #{type.name}#{implementations} {\n"\
|
285
|
-
"#{print_fields(warden, type)}\n"\
|
286
|
-
"}"
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
class InterfacePrinter
|
291
|
-
extend FieldPrinter
|
292
|
-
extend DescriptionPrinter
|
293
|
-
def self.print(warden, type)
|
294
|
-
"#{print_description(type)}"\
|
295
|
-
"interface #{type.name} {\n#{print_fields(warden, type)}\n}"
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
class UnionPrinter
|
300
|
-
extend DescriptionPrinter
|
301
|
-
def self.print(warden, type)
|
302
|
-
possible_types = warden.possible_types(type)
|
303
|
-
"#{print_description(type)}"\
|
304
|
-
"union #{type.name} = #{possible_types.sort_by(&:name).map(&:to_s).join(" | ")}"
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
class EnumPrinter
|
309
|
-
extend DeprecatedPrinter
|
310
|
-
extend DescriptionPrinter
|
311
|
-
def self.print(warden, type)
|
312
|
-
enum_values = warden.enum_values(type)
|
313
|
-
|
314
|
-
values = enum_values.sort_by(&:name).map.with_index { |v, i|
|
315
|
-
"#{print_description(v, ' ', i == 0)}"\
|
316
|
-
" #{v.name}#{print_deprecated(v)}"
|
317
|
-
}.join("\n")
|
318
|
-
|
319
|
-
"#{print_description(type)}"\
|
320
|
-
"enum #{type.name} {\n#{values}\n}"
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
class InputObjectPrinter
|
325
|
-
extend FieldPrinter
|
326
|
-
extend DescriptionPrinter
|
327
|
-
def self.print(warden, type)
|
328
|
-
arguments = warden.arguments(type)
|
329
|
-
fields = arguments.sort_by(&:name).map.with_index{ |field, i|
|
330
|
-
"#{print_description(field, " ", i == 0)}"\
|
331
|
-
" #{print_input_value(field)}"
|
332
|
-
}.join("\n")
|
333
|
-
"#{print_description(type)}"\
|
334
|
-
"input #{type.name} {\n#{fields}\n}"
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
STRATEGIES = {
|
339
|
-
GraphQL::TypeKinds::SCALAR => ScalarPrinter,
|
340
|
-
GraphQL::TypeKinds::OBJECT => ObjectPrinter,
|
341
|
-
GraphQL::TypeKinds::INTERFACE => InterfacePrinter,
|
342
|
-
GraphQL::TypeKinds::UNION => UnionPrinter,
|
343
|
-
GraphQL::TypeKinds::ENUM => EnumPrinter,
|
344
|
-
GraphQL::TypeKinds::INPUT_OBJECT => InputObjectPrinter,
|
345
|
-
}
|
346
114
|
end
|
347
|
-
private_constant :TypeKindPrinters
|
348
115
|
end
|
349
116
|
end
|
350
117
|
end
|