graphql 2.1.0 → 2.1.2
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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +2 -0
- data/lib/graphql/analysis/ast/analyzer.rb +7 -0
- data/lib/graphql/analysis/ast/query_depth.rb +7 -2
- data/lib/graphql/analysis/ast/visitor.rb +2 -2
- data/lib/graphql/analysis/ast.rb +15 -11
- data/lib/graphql/dataloader/source.rb +7 -0
- data/lib/graphql/dataloader.rb +38 -10
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +90 -251
- data/lib/graphql/execution/interpreter.rb +0 -6
- data/lib/graphql/execution/lookahead.rb +1 -1
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/language/block_string.rb +28 -16
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +30 -19
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/printer.rb +88 -27
- data/lib/graphql/language/sanitized_printer.rb +6 -1
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +2 -0
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +32 -98
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field.rb +7 -4
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/introspection_system.rb +2 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +19 -4
- data/lib/graphql/schema/member/has_fields.rb +4 -1
- data/lib/graphql/schema/member/has_interfaces.rb +21 -7
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
- data/lib/graphql/schema/resolver.rb +4 -0
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/warden.rb +84 -55
- data/lib/graphql/schema.rb +123 -36
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +1 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -2
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -1
- metadata +9 -4
@@ -21,6 +21,10 @@ module GraphQL
|
|
21
21
|
# @see {GraphQL::Schema::Mutation} for an example, it's basically the same.
|
22
22
|
#
|
23
23
|
class RelayClassicMutation < GraphQL::Schema::Mutation
|
24
|
+
include GraphQL::Schema::HasSingleInputArgument
|
25
|
+
|
26
|
+
argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
|
27
|
+
|
24
28
|
# The payload should always include this field
|
25
29
|
field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.")
|
26
30
|
# Relay classic default:
|
@@ -31,34 +35,14 @@ module GraphQL
|
|
31
35
|
def resolve_with_support(**inputs)
|
32
36
|
input = inputs[:input].to_kwargs
|
33
37
|
|
34
|
-
new_extras = field ? field.extras : []
|
35
|
-
all_extras = self.class.extras + new_extras
|
36
|
-
|
37
|
-
# Transfer these from the top-level hash to the
|
38
|
-
# shortcutted `input:` object
|
39
|
-
all_extras.each do |ext|
|
40
|
-
# It's possible that the `extra` was not passed along by this point,
|
41
|
-
# don't re-add it if it wasn't given here.
|
42
|
-
if inputs.key?(ext)
|
43
|
-
input[ext] = inputs[ext]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
38
|
if input
|
48
39
|
# This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
|
49
40
|
input_kwargs = input.to_h
|
50
41
|
client_mutation_id = input_kwargs.delete(:client_mutation_id)
|
51
|
-
|
52
|
-
# Relay Classic Mutations with no `argument`s
|
53
|
-
# don't require `input:`
|
54
|
-
input_kwargs = {}
|
42
|
+
inputs[:input] = input_kwargs
|
55
43
|
end
|
56
44
|
|
57
|
-
return_value =
|
58
|
-
super(**input_kwargs)
|
59
|
-
else
|
60
|
-
super()
|
61
|
-
end
|
45
|
+
return_value = super(**inputs)
|
62
46
|
|
63
47
|
context.query.after_lazy(return_value) do |return_hash|
|
64
48
|
# It might be an error
|
@@ -68,112 +52,6 @@ module GraphQL
|
|
68
52
|
return_hash
|
69
53
|
end
|
70
54
|
end
|
71
|
-
|
72
|
-
class << self
|
73
|
-
def dummy
|
74
|
-
@dummy ||= begin
|
75
|
-
d = Class.new(GraphQL::Schema::Resolver)
|
76
|
-
d.argument_class(self.argument_class)
|
77
|
-
# TODO make this lazier?
|
78
|
-
d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
|
79
|
-
d
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def field_arguments(context = GraphQL::Query::NullContext)
|
84
|
-
dummy.arguments(context)
|
85
|
-
end
|
86
|
-
|
87
|
-
def get_field_argument(name, context = GraphQL::Query::NullContext)
|
88
|
-
dummy.get_argument(name, context)
|
89
|
-
end
|
90
|
-
|
91
|
-
def own_field_arguments
|
92
|
-
dummy.own_arguments
|
93
|
-
end
|
94
|
-
|
95
|
-
def all_field_argument_definitions
|
96
|
-
dummy.all_argument_definitions
|
97
|
-
end
|
98
|
-
|
99
|
-
# Also apply this argument to the input type:
|
100
|
-
def argument(*args, own_argument: false, **kwargs, &block)
|
101
|
-
it = input_type # make sure any inherited arguments are already added to it
|
102
|
-
arg = super(*args, **kwargs, &block)
|
103
|
-
|
104
|
-
# This definition might be overriding something inherited;
|
105
|
-
# if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
|
106
|
-
prev_args = it.own_arguments[arg.graphql_name]
|
107
|
-
case prev_args
|
108
|
-
when GraphQL::Schema::Argument
|
109
|
-
if prev_args.owner != self
|
110
|
-
it.own_arguments.delete(arg.graphql_name)
|
111
|
-
end
|
112
|
-
when Array
|
113
|
-
prev_args.reject! { |a| a.owner != self }
|
114
|
-
if prev_args.empty?
|
115
|
-
it.own_arguments.delete(arg.graphql_name)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
it.add_argument(arg)
|
120
|
-
arg
|
121
|
-
end
|
122
|
-
|
123
|
-
# The base class for generated input object types
|
124
|
-
# @param new_class [Class] The base class to use for generating input object definitions
|
125
|
-
# @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
|
126
|
-
def input_object_class(new_class = nil)
|
127
|
-
if new_class
|
128
|
-
@input_object_class = new_class
|
129
|
-
end
|
130
|
-
@input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
|
131
|
-
end
|
132
|
-
|
133
|
-
# @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type
|
134
|
-
# @return [Class] The generated {Schema::InputObject} class for this mutation's `input`
|
135
|
-
def input_type(new_input_type = nil)
|
136
|
-
if new_input_type
|
137
|
-
@input_type = new_input_type
|
138
|
-
end
|
139
|
-
@input_type ||= generate_input_type
|
140
|
-
end
|
141
|
-
|
142
|
-
private
|
143
|
-
|
144
|
-
# Generate the input type for the `input:` argument
|
145
|
-
# To customize how input objects are generated, override this method
|
146
|
-
# @return [Class] a subclass of {.input_object_class}
|
147
|
-
def generate_input_type
|
148
|
-
mutation_args = all_argument_definitions
|
149
|
-
mutation_class = self
|
150
|
-
Class.new(input_object_class) do
|
151
|
-
class << self
|
152
|
-
def default_graphql_name
|
153
|
-
"#{self.mutation.graphql_name}Input"
|
154
|
-
end
|
155
|
-
|
156
|
-
def description(new_desc = nil)
|
157
|
-
super || "Autogenerated input type of #{self.mutation.graphql_name}"
|
158
|
-
end
|
159
|
-
end
|
160
|
-
mutation(mutation_class)
|
161
|
-
# these might be inherited:
|
162
|
-
mutation_args.each do |arg|
|
163
|
-
add_argument(arg)
|
164
|
-
end
|
165
|
-
argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
private
|
171
|
-
|
172
|
-
def authorize_arguments(args, values)
|
173
|
-
# remove the `input` wrapper to match values
|
174
|
-
input_args = args["input"].type.unwrap.arguments(context)
|
175
|
-
super(input_args, values)
|
176
|
-
end
|
177
55
|
end
|
178
56
|
end
|
179
57
|
end
|
@@ -19,9 +19,9 @@ module GraphQL
|
|
19
19
|
|
20
20
|
def specified_by_url(new_url = nil)
|
21
21
|
if new_url
|
22
|
-
|
23
|
-
elsif
|
24
|
-
|
22
|
+
directive(GraphQL::Schema::Directive::SpecifiedBy, url: new_url)
|
23
|
+
elsif (directive = directives.find { |dir| dir.graphql_name == "specifiedBy" })
|
24
|
+
directive.arguments[:url] # rubocop:disable Development/ContextIsPassedCop
|
25
25
|
elsif superclass.respond_to?(:specified_by_url)
|
26
26
|
superclass.specified_by_url
|
27
27
|
else
|
@@ -60,6 +60,7 @@ module GraphQL
|
|
60
60
|
def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
|
61
61
|
def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
|
62
62
|
def arguments(owner, ctx); owner.arguments(ctx); end
|
63
|
+
def loadable?(type, ctx); type.visible?(ctx); end
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
@@ -84,6 +85,7 @@ module GraphQL
|
|
84
85
|
def fields(type_defn); type_defn.all_field_definitions; end # rubocop:disable Development/ContextIsPassedCop
|
85
86
|
def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
|
86
87
|
def reachable_type?(type_name); true; end
|
88
|
+
def loadable?(type, _ctx); true; end
|
87
89
|
def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
|
88
90
|
def possible_types(type_defn); @schema.possible_types(type_defn); end
|
89
91
|
def interfaces(obj_type); obj_type.interfaces; end
|
@@ -122,6 +124,11 @@ module GraphQL
|
|
122
124
|
end
|
123
125
|
end
|
124
126
|
|
127
|
+
# @return [Boolean] True if this type is used for `loads:` but not in the schema otherwise and not _explicitly_ hidden.
|
128
|
+
def loadable?(type, _ctx)
|
129
|
+
!reachable_type_set.include?(type) && visible_type?(type)
|
130
|
+
end
|
131
|
+
|
125
132
|
# @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
|
126
133
|
def get_type(type_name)
|
127
134
|
@visible_types ||= read_through do |name|
|
@@ -188,7 +195,16 @@ module GraphQL
|
|
188
195
|
# @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
|
189
196
|
# @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
|
190
197
|
def arguments(argument_owner, ctx = nil)
|
191
|
-
@visible_arguments ||= read_through { |o|
|
198
|
+
@visible_arguments ||= read_through { |o|
|
199
|
+
args = o.arguments(@context)
|
200
|
+
if args.any?
|
201
|
+
args = args.values
|
202
|
+
args.select! { |a| visible_argument?(a, @context) }
|
203
|
+
args
|
204
|
+
else
|
205
|
+
EmptyObjects::EMPTY_ARRAY
|
206
|
+
end
|
207
|
+
}
|
192
208
|
@visible_arguments[argument_owner]
|
193
209
|
end
|
194
210
|
|
@@ -211,7 +227,13 @@ module GraphQL
|
|
211
227
|
|
212
228
|
# @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
|
213
229
|
def interfaces(obj_type)
|
214
|
-
@visible_interfaces ||= read_through { |t|
|
230
|
+
@visible_interfaces ||= read_through { |t|
|
231
|
+
ints = t.interfaces(@context)
|
232
|
+
if ints.any?
|
233
|
+
ints.select! { |i| visible_type?(i) }
|
234
|
+
end
|
235
|
+
ints
|
236
|
+
}
|
215
237
|
@visible_interfaces[obj_type]
|
216
238
|
end
|
217
239
|
|
@@ -267,11 +289,19 @@ module GraphQL
|
|
267
289
|
next true if root_type?(type_defn) || type_defn.introspection?
|
268
290
|
|
269
291
|
if type_defn.kind.union?
|
270
|
-
|
292
|
+
possible_types(type_defn).any? && (referenced?(type_defn) || orphan_type?(type_defn))
|
271
293
|
elsif type_defn.kind.interface?
|
272
|
-
|
294
|
+
possible_types(type_defn).any?
|
273
295
|
else
|
274
|
-
referenced?(type_defn)
|
296
|
+
if referenced?(type_defn)
|
297
|
+
true
|
298
|
+
elsif type_defn.kind.object?
|
299
|
+
# Show this object if it belongs to ...
|
300
|
+
interfaces(type_defn).any? { |t| referenced?(t) } || # an interface which is referenced in the schema
|
301
|
+
union_memberships(type_defn).any? { |t| referenced?(t) || orphan_type?(t) } # or a union which is referenced or added via orphan_types
|
302
|
+
else
|
303
|
+
false
|
304
|
+
end
|
275
305
|
end
|
276
306
|
end
|
277
307
|
|
@@ -338,23 +368,14 @@ module GraphQL
|
|
338
368
|
@schema.orphan_types.include?(type_defn)
|
339
369
|
end
|
340
370
|
|
341
|
-
def visible_abstract_type?(type_defn)
|
342
|
-
type_defn.kind.object? && (
|
343
|
-
interfaces(type_defn).any? ||
|
344
|
-
union_memberships(type_defn).any?
|
345
|
-
)
|
346
|
-
end
|
347
|
-
|
348
|
-
def visible_possible_types?(type_defn)
|
349
|
-
possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
|
350
|
-
end
|
351
|
-
|
352
371
|
def visible?(member)
|
353
372
|
@visibility_cache[member]
|
354
373
|
end
|
355
374
|
|
356
375
|
def read_through
|
357
|
-
Hash.new { |h, k| h[k] = yield(k) }
|
376
|
+
h = Hash.new { |h, k| h[k] = yield(k) }
|
377
|
+
h.compare_by_identity
|
378
|
+
h
|
358
379
|
end
|
359
380
|
|
360
381
|
def reachable_type_set
|
@@ -385,54 +406,62 @@ module GraphQL
|
|
385
406
|
end
|
386
407
|
end
|
387
408
|
|
409
|
+
included_interface_possible_types_set = Set.new
|
410
|
+
|
388
411
|
until unvisited_types.empty?
|
389
412
|
type = unvisited_types.pop
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
413
|
+
visit_type(type, unvisited_types, @reachable_type_set, rt_hash, included_interface_possible_types_set, include_interface_possible_types: false)
|
414
|
+
end
|
415
|
+
|
416
|
+
@reachable_type_set
|
417
|
+
end
|
418
|
+
|
419
|
+
def visit_type(type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types:)
|
420
|
+
if visited_type_set.add?(type) || (include_interface_possible_types && type.kind.interface? && included_interface_possible_types_set.add?(type))
|
421
|
+
type_by_name = type_by_name_hash[type.graphql_name] ||= type
|
422
|
+
if type_by_name != type
|
423
|
+
name_1, name_2 = [type.inspect, type_by_name.inspect].sort
|
424
|
+
raise DuplicateNamesError.new(
|
425
|
+
duplicated_name: type.graphql_name, duplicated_definition_1: name_1, duplicated_definition_2: name_2
|
426
|
+
)
|
427
|
+
end
|
428
|
+
if type.kind.input_object?
|
429
|
+
# recurse into visible arguments
|
430
|
+
arguments(type).each do |argument|
|
431
|
+
argument_type = argument.type.unwrap
|
432
|
+
unvisited_types << argument_type
|
396
433
|
end
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
# recurse into visible
|
405
|
-
|
406
|
-
unvisited_types <<
|
434
|
+
elsif type.kind.union?
|
435
|
+
# recurse into visible possible types
|
436
|
+
possible_types(type).each do |possible_type|
|
437
|
+
unvisited_types << possible_type
|
438
|
+
end
|
439
|
+
elsif type.kind.fields?
|
440
|
+
if type.kind.object?
|
441
|
+
# recurse into visible implemented interfaces
|
442
|
+
interfaces(type).each do |interface|
|
443
|
+
unvisited_types << interface
|
407
444
|
end
|
408
|
-
elsif
|
409
|
-
|
410
|
-
|
411
|
-
possible_types(type).each do |possible_type|
|
412
|
-
unvisited_types << possible_type
|
413
|
-
end
|
414
|
-
elsif type.kind.object?
|
415
|
-
# recurse into visible implemented interfaces
|
416
|
-
interfaces(type).each do |interface|
|
417
|
-
unvisited_types << interface
|
418
|
-
end
|
445
|
+
elsif include_interface_possible_types
|
446
|
+
possible_types(type).each do |pt|
|
447
|
+
unvisited_types << pt
|
419
448
|
end
|
449
|
+
end
|
450
|
+
# Don't visit interface possible types -- it's not enough to justify visibility
|
420
451
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
452
|
+
# recurse into visible fields
|
453
|
+
fields(type).each do |field|
|
454
|
+
field_type = field.type.unwrap
|
455
|
+
# In this case, if it's an interface, we want to include
|
456
|
+
visit_type(field_type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types: true)
|
457
|
+
# recurse into visible arguments
|
458
|
+
arguments(field).each do |argument|
|
459
|
+
argument_type = argument.type.unwrap
|
460
|
+
unvisited_types << argument_type
|
430
461
|
end
|
431
462
|
end
|
432
463
|
end
|
433
464
|
end
|
434
|
-
|
435
|
-
@reachable_type_set
|
436
465
|
end
|
437
466
|
end
|
438
467
|
end
|
data/lib/graphql/schema.rb
CHANGED
@@ -37,10 +37,12 @@ require "graphql/schema/directive/skip"
|
|
37
37
|
require "graphql/schema/directive/feature"
|
38
38
|
require "graphql/schema/directive/flagged"
|
39
39
|
require "graphql/schema/directive/transform"
|
40
|
+
require "graphql/schema/directive/specified_by"
|
40
41
|
require "graphql/schema/type_membership"
|
41
42
|
|
42
43
|
require "graphql/schema/resolver"
|
43
44
|
require "graphql/schema/mutation"
|
45
|
+
require "graphql/schema/has_single_input_argument"
|
44
46
|
require "graphql/schema/relay_classic_mutation"
|
45
47
|
require "graphql/schema/subscription"
|
46
48
|
|
@@ -144,6 +146,19 @@ module GraphQL
|
|
144
146
|
@subscriptions = new_implementation
|
145
147
|
end
|
146
148
|
|
149
|
+
# @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
|
150
|
+
def default_trace_mode(new_mode = nil)
|
151
|
+
if new_mode
|
152
|
+
@default_trace_mode = new_mode
|
153
|
+
elsif defined?(@default_trace_mode)
|
154
|
+
@default_trace_mode
|
155
|
+
elsif superclass.respond_to?(:default_trace_mode)
|
156
|
+
superclass.default_trace_mode
|
157
|
+
else
|
158
|
+
:default
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
147
162
|
def trace_class(new_class = nil)
|
148
163
|
if new_class
|
149
164
|
trace_mode(:default, new_class)
|
@@ -155,42 +170,66 @@ module GraphQL
|
|
155
170
|
end
|
156
171
|
|
157
172
|
# @return [Class] Return the trace class to use for this mode, looking one up on the superclass if this Schema doesn't have one defined.
|
158
|
-
def trace_class_for(mode)
|
159
|
-
|
160
|
-
|
161
|
-
case mode
|
162
|
-
when :default
|
163
|
-
superclass_base_class = if superclass.respond_to?(:trace_class_for)
|
164
|
-
superclass.trace_class_for(mode)
|
165
|
-
else
|
166
|
-
GraphQL::Tracing::Trace
|
167
|
-
end
|
168
|
-
Class.new(superclass_base_class)
|
169
|
-
when :default_backtrace
|
170
|
-
schema_base_class = trace_class_for(:default)
|
171
|
-
Class.new(schema_base_class) do
|
172
|
-
include(GraphQL::Backtrace::Trace)
|
173
|
-
end
|
174
|
-
else
|
175
|
-
mods = trace_modules_for(mode)
|
176
|
-
Class.new(trace_class_for(:default)) do
|
177
|
-
mods.any? && include(*mods)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
173
|
+
def trace_class_for(mode, build: false)
|
174
|
+
own_trace_modes[mode] ||
|
175
|
+
(superclass.respond_to?(:trace_class_for) ? superclass.trace_class_for(mode) : nil)
|
181
176
|
end
|
182
177
|
|
183
178
|
# Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
|
184
|
-
#
|
179
|
+
# {default_trace_mode} is used when no `trace_mode: ...` is requested.
|
180
|
+
#
|
181
|
+
# When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
|
182
|
+
# unless `trace_mode` is explicitly given. (This class will not recieve any default trace modules.)
|
183
|
+
#
|
184
|
+
# Subclasses of the schema will use `trace_class` as a base class for this mode and those
|
185
|
+
# subclass also will _not_ receive default tracing modules.
|
186
|
+
#
|
185
187
|
# @param mode_name [Symbol]
|
186
188
|
# @param trace_class [Class] subclass of GraphQL::Tracing::Trace
|
187
189
|
# @return void
|
188
190
|
def trace_mode(mode_name, trace_class)
|
189
|
-
|
190
|
-
@trace_modes[mode_name] = trace_class
|
191
|
+
own_trace_modes[mode_name] = trace_class
|
191
192
|
nil
|
192
193
|
end
|
193
194
|
|
195
|
+
def own_trace_modes
|
196
|
+
@own_trace_modes ||= {}
|
197
|
+
end
|
198
|
+
|
199
|
+
module DefaultTraceClass
|
200
|
+
end
|
201
|
+
|
202
|
+
private_constant :DefaultTraceClass
|
203
|
+
|
204
|
+
def build_trace_mode(mode)
|
205
|
+
case mode
|
206
|
+
when :default
|
207
|
+
# Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
|
208
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || GraphQL::Tracing::Trace
|
209
|
+
Class.new(base_class) do
|
210
|
+
include DefaultTraceClass
|
211
|
+
end
|
212
|
+
when :default_backtrace
|
213
|
+
schema_base_class = trace_class_for(:default)
|
214
|
+
Class.new(schema_base_class) do
|
215
|
+
include(GraphQL::Backtrace::Trace)
|
216
|
+
end
|
217
|
+
else
|
218
|
+
# First, see if the superclass has a custom-defined class for this.
|
219
|
+
# Then, if it doesn't, use this class's default trace
|
220
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default)
|
221
|
+
# Prepare the default trace class if it hasn't been initialized yet
|
222
|
+
base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
|
223
|
+
mods = trace_modules_for(mode)
|
224
|
+
if base_class < DefaultTraceClass
|
225
|
+
mods = trace_modules_for(:default) + mods
|
226
|
+
end
|
227
|
+
Class.new(base_class) do
|
228
|
+
mods.any? && include(*mods)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
194
233
|
def own_trace_modules
|
195
234
|
@own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
|
196
235
|
end
|
@@ -701,9 +740,10 @@ module GraphQL
|
|
701
740
|
|
702
741
|
attr_writer :max_depth
|
703
742
|
|
704
|
-
def max_depth(new_max_depth = nil)
|
743
|
+
def max_depth(new_max_depth = nil, count_introspection_fields: true)
|
705
744
|
if new_max_depth
|
706
745
|
@max_depth = new_max_depth
|
746
|
+
@count_introspection_fields = count_introspection_fields
|
707
747
|
elsif defined?(@max_depth)
|
708
748
|
@max_depth
|
709
749
|
else
|
@@ -711,6 +751,14 @@ module GraphQL
|
|
711
751
|
end
|
712
752
|
end
|
713
753
|
|
754
|
+
def count_introspection_fields
|
755
|
+
if defined?(@count_introspection_fields)
|
756
|
+
@count_introspection_fields
|
757
|
+
else
|
758
|
+
find_inherited_value(:count_introspection_fields, true)
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
714
762
|
def disable_introspection_entry_points
|
715
763
|
@disable_introspection_entry_points = true
|
716
764
|
# TODO: this clears the cache made in `def types`. But this is not a great solution.
|
@@ -760,7 +808,16 @@ module GraphQL
|
|
760
808
|
own_orphan_types.concat(new_orphan_types.flatten)
|
761
809
|
end
|
762
810
|
|
763
|
-
find_inherited_value(:orphan_types,
|
811
|
+
inherited_ot = find_inherited_value(:orphan_types, nil)
|
812
|
+
if inherited_ot
|
813
|
+
if own_orphan_types.any?
|
814
|
+
inherited_ot + own_orphan_types
|
815
|
+
else
|
816
|
+
inherited_ot
|
817
|
+
end
|
818
|
+
else
|
819
|
+
own_orphan_types
|
820
|
+
end
|
764
821
|
end
|
765
822
|
|
766
823
|
def default_execution_strategy
|
@@ -864,8 +921,14 @@ module GraphQL
|
|
864
921
|
|
865
922
|
def inherited(child_class)
|
866
923
|
if self == GraphQL::Schema
|
924
|
+
child_class.own_trace_modes[:default] = child_class.build_trace_mode(:default)
|
867
925
|
child_class.directives(default_directives.values)
|
868
926
|
end
|
927
|
+
# Make sure the child class has these built out, so that
|
928
|
+
# subclasses can be modified by later calls to `trace_with`
|
929
|
+
own_trace_modes.each do |name, _class|
|
930
|
+
child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
|
931
|
+
end
|
869
932
|
child_class.singleton_class.prepend(ResolveTypeWithType)
|
870
933
|
super
|
871
934
|
end
|
@@ -962,7 +1025,12 @@ module GraphQL
|
|
962
1025
|
new_directives.flatten.each { |d| directive(d) }
|
963
1026
|
end
|
964
1027
|
|
965
|
-
find_inherited_value(:directives, default_directives)
|
1028
|
+
inherited_dirs = find_inherited_value(:directives, default_directives)
|
1029
|
+
if own_directives.any?
|
1030
|
+
inherited_dirs.merge(own_directives)
|
1031
|
+
else
|
1032
|
+
inherited_dirs
|
1033
|
+
end
|
966
1034
|
end
|
967
1035
|
|
968
1036
|
# Attach a single directive to this schema
|
@@ -978,11 +1046,13 @@ module GraphQL
|
|
978
1046
|
"skip" => GraphQL::Schema::Directive::Skip,
|
979
1047
|
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
980
1048
|
"oneOf" => GraphQL::Schema::Directive::OneOf,
|
1049
|
+
"specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
|
981
1050
|
}.freeze
|
982
1051
|
end
|
983
1052
|
|
984
1053
|
def tracer(new_tracer)
|
985
|
-
|
1054
|
+
default_trace = trace_class_for(:default)
|
1055
|
+
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
986
1056
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
987
1057
|
end
|
988
1058
|
|
@@ -1004,10 +1074,20 @@ module GraphQL
|
|
1004
1074
|
if mode.is_a?(Array)
|
1005
1075
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
1006
1076
|
else
|
1007
|
-
tc =
|
1077
|
+
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
1008
1078
|
tc.include(trace_mod)
|
1009
|
-
|
1010
|
-
|
1079
|
+
own_trace_modules[mode] << trace_mod
|
1080
|
+
|
1081
|
+
if mode == :default
|
1082
|
+
# This module is being added as a default tracer. If any other mode classes
|
1083
|
+
# have already been created, but get their default behavior from a superclass,
|
1084
|
+
# Then mix this into this schema's subclass.
|
1085
|
+
# (But don't mix it into mode classes that aren't default-based.)
|
1086
|
+
own_trace_modes.each do |other_mode_name, other_mode_class|
|
1087
|
+
if other_mode_class < DefaultTraceClass && !(other_mode_class < trace_mod)
|
1088
|
+
other_mode_class.include(trace_mod)
|
1089
|
+
end
|
1090
|
+
end
|
1011
1091
|
end
|
1012
1092
|
t_opts = trace_options_for(mode)
|
1013
1093
|
t_opts.merge!(options)
|
@@ -1030,6 +1110,8 @@ module GraphQL
|
|
1030
1110
|
|
1031
1111
|
# Create a trace instance which will include the trace modules specified for the optional mode.
|
1032
1112
|
#
|
1113
|
+
# If no `mode:` is given, then {default_trace_mode} will be used.
|
1114
|
+
#
|
1033
1115
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
1034
1116
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1035
1117
|
# @return [Tracing::Trace]
|
@@ -1040,14 +1122,19 @@ module GraphQL
|
|
1040
1122
|
trace_mode = if mode
|
1041
1123
|
mode
|
1042
1124
|
elsif target && target.context[:backtrace]
|
1043
|
-
:
|
1125
|
+
if default_trace_mode != :default
|
1126
|
+
raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
|
1127
|
+
else
|
1128
|
+
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
1129
|
+
:default_backtrace
|
1130
|
+
end
|
1044
1131
|
else
|
1045
|
-
|
1132
|
+
default_trace_mode
|
1046
1133
|
end
|
1047
1134
|
|
1048
1135
|
base_trace_options = trace_options_for(trace_mode)
|
1049
1136
|
trace_options = base_trace_options.merge(options)
|
1050
|
-
trace_class_for_mode = trace_class_for(trace_mode)
|
1137
|
+
trace_class_for_mode = trace_class_for(trace_mode) || raise(ArgumentError, "#{self} has no trace class for mode: #{trace_mode.inspect}")
|
1051
1138
|
trace_class_for_mode.new(**trace_options)
|
1052
1139
|
end
|
1053
1140
|
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
module StaticValidation
|
4
4
|
# Default rules for {GraphQL::StaticValidation::Validator}
|
5
5
|
#
|
6
|
-
# Order is important here. Some validators
|
6
|
+
# Order is important here. Some validators skip later hooks.
|
7
7
|
# which stops the visit on that node. That way it doesn't try to find fields on types that
|
8
8
|
# don't exist, etc.
|
9
9
|
ALL_RULES = [
|