graphql 2.4.1 → 2.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/execution/lookahead.rb +8 -1
- data/lib/graphql/schema/visibility/profile.rb +59 -235
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +214 -9
- data/lib/graphql/schema.rb +53 -3
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +10 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +1 -0
- data/lib/graphql/testing/helpers.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: caa82217695402eca9cd3782abf5deb4b27dd61d8aa3c16e7c4a555e1c09634a
|
4
|
+
data.tar.gz: a39b5821df1a8656616fd1831878c365c930289c0f94d733b788ab4298f24a96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1181acff2ccf066def91b1f39644b449fe715d3cfa18437c7edf74dfa5af01a45cbd1e00db2413810917fc290d1d3abc8bcc0620354458721d4df184973beb17
|
7
|
+
data.tar.gz: 61bcb56a34d0b0beb61672e3bae868dc5bd7a9caf351d85e0a78afd16c12347a076767e63ce55cc468c6edc5247caa10e2ccace32447b54c9dbc4f864846db5b
|
@@ -56,7 +56,14 @@ module GraphQL
|
|
56
56
|
else
|
57
57
|
@arguments = if @field
|
58
58
|
@query.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
|
59
|
-
|
59
|
+
case args
|
60
|
+
when Execution::Interpreter::Arguments
|
61
|
+
args.keyword_arguments
|
62
|
+
when GraphQL::ExecutionError
|
63
|
+
EmptyObjects::EMPTY_HASH
|
64
|
+
else
|
65
|
+
args
|
66
|
+
end
|
60
67
|
end
|
61
68
|
else
|
62
69
|
nil
|
@@ -38,38 +38,17 @@ module GraphQL
|
|
38
38
|
@all_types = {}
|
39
39
|
@all_types_loaded = false
|
40
40
|
@unvisited_types = []
|
41
|
-
@referenced_types = Hash.new { |h, type_defn| h[type_defn] = [] }.compare_by_identity
|
42
|
-
@cached_directives = {}
|
43
41
|
@all_directives = nil
|
44
|
-
@cached_visible = Hash.new { |h, member|
|
45
|
-
h[member] = @schema.visible?(member, @context)
|
46
|
-
}.compare_by_identity
|
42
|
+
@cached_visible = Hash.new { |h, member| h[member] = @schema.visible?(member, @context) }.compare_by_identity
|
47
43
|
|
48
44
|
@cached_visible_fields = Hash.new { |h, owner|
|
49
45
|
h[owner] = Hash.new do |h2, field|
|
50
|
-
h2[field] =
|
51
|
-
(ret_type = field.type.unwrap) &&
|
52
|
-
@cached_visible[ret_type] &&
|
53
|
-
(owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner))
|
54
|
-
|
55
|
-
if !field.introspection?
|
56
|
-
# The problem is that some introspection fields may have references
|
57
|
-
# to non-custom introspection types.
|
58
|
-
# If those were added here, they'd cause a DuplicateNamesError.
|
59
|
-
# This is basically a bug -- those fields _should_ reference the custom types.
|
60
|
-
add_type(ret_type, field)
|
61
|
-
end
|
62
|
-
true
|
63
|
-
else
|
64
|
-
false
|
65
|
-
end
|
46
|
+
h2[field] = visible_field_for(owner, field)
|
66
47
|
end.compare_by_identity
|
67
48
|
}.compare_by_identity
|
68
49
|
|
69
50
|
@cached_visible_arguments = Hash.new do |h, arg|
|
70
51
|
h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
|
71
|
-
add_type(arg_type, arg)
|
72
|
-
arg.validate_default_value
|
73
52
|
true
|
74
53
|
else
|
75
54
|
false
|
@@ -88,39 +67,7 @@ module GraphQL
|
|
88
67
|
end
|
89
68
|
end.compare_by_identity
|
90
69
|
|
91
|
-
@cached_possible_types = Hash.new
|
92
|
-
h[type] = case type.kind.name
|
93
|
-
when "INTERFACE"
|
94
|
-
load_all_types
|
95
|
-
pts = []
|
96
|
-
@unfiltered_interface_type_memberships[type].each { |itm|
|
97
|
-
if @cached_visible[itm] && (ot = itm.object_type) && @cached_visible[ot] && referenced?(ot)
|
98
|
-
pts << ot
|
99
|
-
end
|
100
|
-
}
|
101
|
-
pts
|
102
|
-
when "UNION"
|
103
|
-
pts = []
|
104
|
-
type.type_memberships.each { |tm|
|
105
|
-
if @cached_visible[tm] &&
|
106
|
-
(ot = tm.object_type) &&
|
107
|
-
@cached_visible[ot] &&
|
108
|
-
referenced?(ot)
|
109
|
-
pts << ot
|
110
|
-
end
|
111
|
-
}
|
112
|
-
pts
|
113
|
-
when "OBJECT"
|
114
|
-
load_all_types
|
115
|
-
if @all_types[type.graphql_name] == type
|
116
|
-
[type]
|
117
|
-
else
|
118
|
-
EmptyObjects::EMPTY_ARRAY
|
119
|
-
end
|
120
|
-
else
|
121
|
-
GraphQL::EmptyObjects::EMPTY_ARRAY
|
122
|
-
end
|
123
|
-
end.compare_by_identity
|
70
|
+
@cached_possible_types = Hash.new { |h, type| h[type] = possible_types_for(type) }.compare_by_identity
|
124
71
|
|
125
72
|
@cached_enum_values = Hash.new do |h, enum_t|
|
126
73
|
values = non_duplicate_items(enum_t.enum_values(@context), @cached_visible)
|
@@ -164,17 +111,12 @@ module GraphQL
|
|
164
111
|
end
|
165
112
|
|
166
113
|
def type(type_name)
|
167
|
-
t =
|
168
|
-
loaded_t
|
169
|
-
elsif !@all_types_loaded
|
170
|
-
load_all_types
|
171
|
-
@all_types[type_name]
|
172
|
-
end
|
114
|
+
t = @schema.visibility.get_type(type_name) # rubocop:disable ContextIsPassedCop
|
173
115
|
if t
|
174
116
|
if t.is_a?(Array)
|
175
117
|
vis_t = nil
|
176
118
|
t.each do |t_defn|
|
177
|
-
if @cached_visible[t_defn]
|
119
|
+
if @cached_visible[t_defn] && referenced?(t_defn)
|
178
120
|
if vis_t.nil?
|
179
121
|
vis_t = t_defn
|
180
122
|
else
|
@@ -184,7 +126,7 @@ module GraphQL
|
|
184
126
|
end
|
185
127
|
vis_t
|
186
128
|
else
|
187
|
-
if t && @cached_visible[t]
|
129
|
+
if t && @cached_visible[t] && referenced?(t)
|
188
130
|
t
|
189
131
|
else
|
190
132
|
nil
|
@@ -267,15 +209,15 @@ module GraphQL
|
|
267
209
|
end
|
268
210
|
|
269
211
|
def query_root
|
270
|
-
|
212
|
+
((t = @schema.query) && @cached_visible[t]) ? t : nil
|
271
213
|
end
|
272
214
|
|
273
215
|
def mutation_root
|
274
|
-
|
216
|
+
((t = @schema.mutation) && @cached_visible[t]) ? t : nil
|
275
217
|
end
|
276
218
|
|
277
219
|
def subscription_root
|
278
|
-
|
220
|
+
((t = @schema.subscription) && @cached_visible[t]) ? t : nil
|
279
221
|
end
|
280
222
|
|
281
223
|
def all_types
|
@@ -293,28 +235,15 @@ module GraphQL
|
|
293
235
|
end
|
294
236
|
|
295
237
|
def directive_exists?(dir_name)
|
296
|
-
|
297
|
-
!!dir
|
298
|
-
else
|
299
|
-
load_all_types
|
300
|
-
!!@cached_directives[dir_name]
|
301
|
-
end
|
238
|
+
directives.any? { |d| d.graphql_name == dir_name }
|
302
239
|
end
|
303
240
|
|
304
241
|
def directives
|
305
|
-
@all_directives ||=
|
306
|
-
load_all_types
|
307
|
-
dirs = []
|
308
|
-
@schema.directives.each do |name, dir_defn|
|
309
|
-
if !@cached_directives[name] && @cached_visible[dir_defn]
|
310
|
-
dirs << dir_defn
|
311
|
-
end
|
312
|
-
end
|
313
|
-
dirs.concat(@cached_directives.values)
|
314
|
-
end
|
242
|
+
@all_directives ||= @schema.visibility.all_directives.select { |dir| @cached_visible[dir] }
|
315
243
|
end
|
316
244
|
|
317
245
|
def loadable?(t, _ctx)
|
246
|
+
load_all_types
|
318
247
|
!@all_types[t.graphql_name] && @cached_visible[t]
|
319
248
|
end
|
320
249
|
|
@@ -322,9 +251,9 @@ module GraphQL
|
|
322
251
|
@all_types.values
|
323
252
|
end
|
324
253
|
|
325
|
-
def reachable_type?(
|
254
|
+
def reachable_type?(type_name)
|
326
255
|
load_all_types
|
327
|
-
!!@all_types[
|
256
|
+
!!@all_types[type_name]
|
328
257
|
end
|
329
258
|
|
330
259
|
def visible_enum_value?(enum_value, _ctx = nil)
|
@@ -333,29 +262,6 @@ module GraphQL
|
|
333
262
|
|
334
263
|
private
|
335
264
|
|
336
|
-
def add_if_visible(t)
|
337
|
-
(t && @cached_visible[t]) ? (add_type(t, true); t) : nil
|
338
|
-
end
|
339
|
-
|
340
|
-
def add_type(t, by_member)
|
341
|
-
if t && @cached_visible[t]
|
342
|
-
n = t.graphql_name
|
343
|
-
if (prev_t = @all_types[n])
|
344
|
-
if !prev_t.equal?(t)
|
345
|
-
raise_duplicate_definition(prev_t, t)
|
346
|
-
end
|
347
|
-
false
|
348
|
-
else
|
349
|
-
@referenced_types[t] << by_member
|
350
|
-
@all_types[n] = t
|
351
|
-
@unvisited_types << t
|
352
|
-
true
|
353
|
-
end
|
354
|
-
else
|
355
|
-
false
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
265
|
def non_duplicate_items(definitions, visibility_cache)
|
360
266
|
non_dups = []
|
361
267
|
definitions.each do |defn|
|
@@ -373,153 +279,71 @@ module GraphQL
|
|
373
279
|
raise DuplicateNamesError.new(duplicated_name: first_defn.path, duplicated_definition_1: first_defn.inspect, duplicated_definition_2: second_defn.inspect)
|
374
280
|
end
|
375
281
|
|
376
|
-
def referenced?(t)
|
377
|
-
load_all_types
|
378
|
-
@referenced_types[t].any? { |reference| (reference == true) || @cached_visible[reference] }
|
379
|
-
end
|
380
|
-
|
381
282
|
def load_all_types
|
382
283
|
return if @all_types_loaded
|
383
284
|
@all_types_loaded = true
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
# Don't include any orphan_types whose interfaces aren't visible.
|
392
|
-
@schema.orphan_types.each do |orphan_type|
|
393
|
-
if @cached_visible[orphan_type] &&
|
394
|
-
orphan_type.interface_type_memberships.any? { |tm| @cached_visible[tm] && @cached_visible[tm.abstract_type] }
|
395
|
-
entry_point_types << orphan_type
|
396
|
-
end
|
397
|
-
end
|
398
|
-
|
399
|
-
@schema.directives.each do |_dir_name, dir_class|
|
400
|
-
if @cached_visible[dir_class]
|
401
|
-
arguments(dir_class).each do |arg|
|
402
|
-
entry_point_types << arg.type.unwrap
|
403
|
-
end
|
404
|
-
end
|
405
|
-
end
|
406
|
-
|
407
|
-
entry_point_types.compact! # TODO why is this necessary?!
|
408
|
-
entry_point_types.flatten! # handle multiple defns
|
409
|
-
entry_point_types.each { |t| add_type(t, true) }
|
410
|
-
|
411
|
-
@unfiltered_interface_type_memberships = Hash.new { |h, k| h[k] = [] }.compare_by_identity
|
412
|
-
@add_possible_types = Set.new
|
413
|
-
@late_types = []
|
414
|
-
|
415
|
-
while @unvisited_types.any? || @late_types.any?
|
416
|
-
while t = @unvisited_types.pop
|
417
|
-
# These have already been checked for `.visible?`
|
418
|
-
visit_type(t)
|
419
|
-
end
|
420
|
-
@add_possible_types.each do |int_t|
|
421
|
-
itms = @unfiltered_interface_type_memberships[int_t]
|
422
|
-
itms.each do |itm|
|
423
|
-
if @cached_visible[itm] && (obj_type = itm.object_type) && @cached_visible[obj_type]
|
424
|
-
add_type(obj_type, itm)
|
285
|
+
visit = Visibility::Visit.new(@schema) do |member|
|
286
|
+
if member.is_a?(Module) && member.respond_to?(:kind)
|
287
|
+
if @cached_visible[member]
|
288
|
+
type_name = member.graphql_name
|
289
|
+
if (prev_t = @all_types[type_name]) && !prev_t.equal?(member)
|
290
|
+
raise_duplicate_definition(member, prev_t)
|
425
291
|
end
|
292
|
+
@all_types[type_name] = member
|
293
|
+
true
|
294
|
+
else
|
295
|
+
false
|
426
296
|
end
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
while (union_tm = @late_types.shift)
|
431
|
-
late_obj_t = union_tm.object_type
|
432
|
-
obj_t = @all_types[late_obj_t.graphql_name] || raise("Failed to resolve #{late_obj_t.graphql_name.inspect} from #{union_tm.inspect}")
|
433
|
-
union_tm.abstract_type.assign_type_membership_object_type(obj_t)
|
297
|
+
else
|
298
|
+
@cached_visible[member]
|
434
299
|
end
|
435
300
|
end
|
436
|
-
|
301
|
+
visit.visit_each
|
437
302
|
@all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) }
|
438
303
|
nil
|
439
304
|
end
|
440
305
|
|
441
|
-
def
|
442
|
-
|
443
|
-
|
444
|
-
when "OBJECT", "INTERFACE"
|
445
|
-
if type.kind.object?
|
446
|
-
type.interface_type_memberships.each do |itm|
|
447
|
-
@unfiltered_interface_type_memberships[itm.abstract_type] << itm
|
448
|
-
end
|
449
|
-
# recurse into visible implemented interfaces
|
450
|
-
interfaces(type).each do |interface|
|
451
|
-
add_type(interface, type)
|
452
|
-
end
|
453
|
-
else
|
454
|
-
type.orphan_types.each { |t| add_type(t, type)}
|
455
|
-
end
|
456
|
-
|
457
|
-
# recurse into visible fields
|
458
|
-
t_f = type.all_field_definitions
|
459
|
-
t_f.each do |field|
|
460
|
-
field.ensure_loaded
|
461
|
-
if @cached_visible[field]
|
462
|
-
visit_directives(field)
|
463
|
-
field_type = field.type.unwrap
|
464
|
-
if field_type.kind.interface?
|
465
|
-
@add_possible_types.add(field_type)
|
466
|
-
end
|
467
|
-
add_type(field_type, field)
|
306
|
+
def referenced?(type_defn)
|
307
|
+
@schema.visibility.all_references[type_defn].any? { |r| r == true || @cached_visible[r] }
|
308
|
+
end
|
468
309
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
310
|
+
def possible_types_for(type)
|
311
|
+
case type.kind.name
|
312
|
+
when "INTERFACE"
|
313
|
+
pts = []
|
314
|
+
@schema.visibility.all_interface_type_memberships[type].each do |itm|
|
315
|
+
if @cached_visible[itm] && (ot = itm.object_type) && @cached_visible[ot] && referenced?(ot)
|
316
|
+
pts << ot
|
474
317
|
end
|
475
318
|
end
|
476
|
-
|
477
|
-
# recurse into visible arguments
|
478
|
-
arguments(type).each do |argument|
|
479
|
-
visit_directives(argument)
|
480
|
-
add_type(argument.type.unwrap, argument)
|
481
|
-
end
|
319
|
+
pts
|
482
320
|
when "UNION"
|
483
|
-
|
484
|
-
type.type_memberships.each
|
485
|
-
if @cached_visible[tm]
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
if obj_t.is_a?(String)
|
491
|
-
obj_t = Member::BuildType.constantize(obj_t)
|
492
|
-
tm.object_type = obj_t
|
493
|
-
end
|
494
|
-
if @cached_visible[obj_t]
|
495
|
-
add_type(obj_t, tm)
|
496
|
-
end
|
497
|
-
end
|
321
|
+
pts = []
|
322
|
+
type.type_memberships.each { |tm|
|
323
|
+
if @cached_visible[tm] &&
|
324
|
+
(ot = tm.object_type) &&
|
325
|
+
@cached_visible[ot] &&
|
326
|
+
referenced?(ot)
|
327
|
+
pts << ot
|
498
328
|
end
|
329
|
+
}
|
330
|
+
pts
|
331
|
+
when "OBJECT"
|
332
|
+
if @cached_visible[type]
|
333
|
+
[type]
|
334
|
+
else
|
335
|
+
EmptyObjects::EMPTY_ARRAY
|
499
336
|
end
|
500
|
-
|
501
|
-
|
502
|
-
visit_directives(val)
|
503
|
-
end
|
504
|
-
when "SCALAR"
|
505
|
-
# pass
|
337
|
+
else
|
338
|
+
GraphQL::EmptyObjects::EMPTY_ARRAY
|
506
339
|
end
|
507
340
|
end
|
508
341
|
|
509
|
-
def
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
if (existing_dir = @cached_directives[dir_name])
|
515
|
-
if existing_dir != dir_class
|
516
|
-
raise ArgumentError, "Two directives for `@#{dir_name}`: #{existing_dir}, #{dir.class}"
|
517
|
-
end
|
518
|
-
else
|
519
|
-
@cached_directives[dir.graphql_name] = dir_class
|
520
|
-
end
|
521
|
-
end
|
522
|
-
}
|
342
|
+
def visible_field_for(owner, field)
|
343
|
+
@cached_visible[field] &&
|
344
|
+
(ret_type = field.type.unwrap) &&
|
345
|
+
@cached_visible[ret_type] &&
|
346
|
+
(owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner))
|
523
347
|
end
|
524
348
|
end
|
525
349
|
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Visibility
|
5
|
+
class Visit
|
6
|
+
def initialize(schema, &visit_block)
|
7
|
+
@schema = schema
|
8
|
+
@late_bound_types = nil
|
9
|
+
@unvisited_types = nil
|
10
|
+
# These accumulate between calls to prevent re-visiting the same types
|
11
|
+
@visited_types = Set.new.compare_by_identity
|
12
|
+
@visited_directives = Set.new.compare_by_identity
|
13
|
+
@visit_block = visit_block
|
14
|
+
end
|
15
|
+
|
16
|
+
def entry_point_types
|
17
|
+
ept = [
|
18
|
+
@schema.query,
|
19
|
+
@schema.mutation,
|
20
|
+
@schema.subscription,
|
21
|
+
*@schema.introspection_system.types.values,
|
22
|
+
*@schema.orphan_types,
|
23
|
+
]
|
24
|
+
ept.compact!
|
25
|
+
ept
|
26
|
+
end
|
27
|
+
|
28
|
+
def entry_point_directives
|
29
|
+
@schema.directives.values
|
30
|
+
end
|
31
|
+
|
32
|
+
def visit_each(types: entry_point_types, directives: entry_point_directives)
|
33
|
+
@unvisited_types && raise("Can't re-enter `visit_each` on this Visit (another visit is already in progress)")
|
34
|
+
@unvisited_types = types
|
35
|
+
@late_bound_types = []
|
36
|
+
directives_to_visit = directives
|
37
|
+
|
38
|
+
while @unvisited_types.any? || @late_bound_types.any?
|
39
|
+
while (type = @unvisited_types.pop)
|
40
|
+
if @visited_types.add?(type) && @visit_block.call(type)
|
41
|
+
directives_to_visit.concat(type.directives)
|
42
|
+
case type.kind.name
|
43
|
+
when "OBJECT", "INTERFACE"
|
44
|
+
type.interface_type_memberships.each do |itm|
|
45
|
+
append_unvisited_type(type, itm.abstract_type)
|
46
|
+
end
|
47
|
+
if type.kind.interface?
|
48
|
+
type.orphan_types.each do |orphan_type|
|
49
|
+
append_unvisited_type(type, orphan_type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
type.all_field_definitions.each do |field|
|
54
|
+
field.ensure_loaded
|
55
|
+
if @visit_block.call(field)
|
56
|
+
directives_to_visit.concat(field.directives)
|
57
|
+
append_unvisited_type(type, field.type.unwrap)
|
58
|
+
field.all_argument_definitions.each do |argument|
|
59
|
+
if @visit_block.call(argument)
|
60
|
+
directives_to_visit.concat(argument.directives)
|
61
|
+
append_unvisited_type(field, argument.type.unwrap)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
when "INPUT_OBJECT"
|
67
|
+
type.all_argument_definitions.each do |argument|
|
68
|
+
if @visit_block.call(argument)
|
69
|
+
directives_to_visit.concat(argument.directives)
|
70
|
+
append_unvisited_type(type, argument.type.unwrap)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
when "UNION"
|
74
|
+
type.type_memberships.each do |tm|
|
75
|
+
append_unvisited_type(type, tm.object_type)
|
76
|
+
end
|
77
|
+
when "ENUM"
|
78
|
+
type.all_enum_value_definitions.each do |val|
|
79
|
+
if @visit_block.call(val)
|
80
|
+
directives_to_visit.concat(val.directives)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
when "SCALAR"
|
84
|
+
# pass -- nothing else to visit
|
85
|
+
else
|
86
|
+
raise "Invariant: unhandled type kind: #{type.kind.inspect}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
directives_to_visit.each do |dir|
|
92
|
+
dir_class = dir.is_a?(Class) ? dir : dir.class
|
93
|
+
if @visited_directives.add?(dir_class) && @visit_block.call(dir_class)
|
94
|
+
dir_class.all_argument_definitions.each do |arg_defn|
|
95
|
+
if @visit_block.call(arg_defn)
|
96
|
+
directives_to_visit.concat(arg_defn.directives)
|
97
|
+
append_unvisited_type(dir_class, arg_defn.type.unwrap)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
missed_late_types_streak = 0
|
104
|
+
while (owner, late_type = @late_bound_types.shift)
|
105
|
+
if (late_type.is_a?(String) && (type = Member::BuildType.constantize(type))) ||
|
106
|
+
(late_type.is_a?(LateBoundType) && (type = @visited_types.find { |t| t.graphql_name == late_type.graphql_name }))
|
107
|
+
missed_late_types_streak = 0 # might succeed next round
|
108
|
+
update_type_owner(owner, type)
|
109
|
+
append_unvisited_type(owner, type)
|
110
|
+
else
|
111
|
+
# Didn't find it -- keep trying
|
112
|
+
missed_late_types_streak += 1
|
113
|
+
@late_bound_types << [owner, late_type]
|
114
|
+
if missed_late_types_streak == @late_bound_types.size
|
115
|
+
raise UnresolvedLateBoundTypeError.new(type: late_type)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
@unvisited_types = nil
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def append_unvisited_type(owner, type)
|
128
|
+
if type.is_a?(LateBoundType) || type.is_a?(String)
|
129
|
+
@late_bound_types << [owner, type]
|
130
|
+
else
|
131
|
+
@unvisited_types << type
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def update_type_owner(owner, type)
|
136
|
+
case owner
|
137
|
+
when Module
|
138
|
+
if owner.kind.union?
|
139
|
+
owner.assign_type_membership_object_type(type)
|
140
|
+
elsif type.kind.interface?
|
141
|
+
new_interfaces = []
|
142
|
+
owner.interfaces.each do |int_t|
|
143
|
+
if int_t.is_a?(String) && int_t == type.graphql_name
|
144
|
+
new_interfaces << type
|
145
|
+
elsif int_t.is_a?(LateBoundType) && int_t.graphql_name == type.graphql_name
|
146
|
+
new_interfaces << type
|
147
|
+
else
|
148
|
+
# Don't re-add proper interface definitions,
|
149
|
+
# they were probably already added, maybe with options.
|
150
|
+
end
|
151
|
+
end
|
152
|
+
owner.implements(*new_interfaces)
|
153
|
+
new_interfaces.each do |int|
|
154
|
+
pt = @possible_types[int] ||= []
|
155
|
+
if !pt.include?(owner) && owner.is_a?(Class)
|
156
|
+
pt << owner
|
157
|
+
end
|
158
|
+
int.interfaces.each do |indirect_int|
|
159
|
+
if indirect_int.is_a?(LateBoundType) && (indirect_int_type = get_type(indirect_int.graphql_name)) # rubocop:disable ContextIsPassedCop
|
160
|
+
update_type_owner(owner, indirect_int_type)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
when GraphQL::Schema::Argument, GraphQL::Schema::Field
|
166
|
+
orig_type = owner.type
|
167
|
+
# Apply list/non-null wrapper as needed
|
168
|
+
if orig_type.respond_to?(:of_type)
|
169
|
+
transforms = []
|
170
|
+
while (orig_type.respond_to?(:of_type))
|
171
|
+
if orig_type.kind.non_null?
|
172
|
+
transforms << :to_non_null_type
|
173
|
+
elsif orig_type.kind.list?
|
174
|
+
transforms << :to_list_type
|
175
|
+
else
|
176
|
+
raise "Invariant: :of_type isn't non-null or list"
|
177
|
+
end
|
178
|
+
orig_type = orig_type.of_type
|
179
|
+
end
|
180
|
+
transforms.reverse_each { |t| type = type.public_send(t) }
|
181
|
+
end
|
182
|
+
owner.type = type
|
183
|
+
else
|
184
|
+
raise "Unexpected update: #{owner.inspect} #{type.inspect}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "graphql/schema/visibility/profile"
|
3
3
|
require "graphql/schema/visibility/migration"
|
4
|
+
require "graphql/schema/visibility/visit"
|
4
5
|
|
5
6
|
module GraphQL
|
6
7
|
class Schema
|
@@ -13,24 +14,115 @@ module GraphQL
|
|
13
14
|
# @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
|
14
15
|
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails) ? Rails.env.production? : nil), migration_errors: false)
|
15
16
|
schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
|
17
|
+
if preload
|
18
|
+
schema.visibility.preload
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
22
|
def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
|
19
23
|
@schema = schema
|
20
24
|
schema.use_visibility_profile = true
|
21
|
-
if migration_errors
|
22
|
-
|
25
|
+
schema.visibility_profile_class = if migration_errors
|
26
|
+
Visibility::Migration
|
27
|
+
else
|
28
|
+
Visibility::Profile
|
23
29
|
end
|
30
|
+
@preload = preload
|
24
31
|
@profiles = profiles
|
25
32
|
@cached_profiles = {}
|
26
33
|
@dynamic = dynamic
|
27
34
|
@migration_errors = migration_errors
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
35
|
+
# Top-level type caches:
|
36
|
+
@visit = nil
|
37
|
+
@interface_type_memberships = nil
|
38
|
+
@directives = nil
|
39
|
+
@types = nil
|
40
|
+
@references = nil
|
41
|
+
@loaded_all = false
|
42
|
+
end
|
43
|
+
|
44
|
+
def all_directives
|
45
|
+
load_all
|
46
|
+
@directives
|
47
|
+
end
|
48
|
+
|
49
|
+
def all_interface_type_memberships
|
50
|
+
load_all
|
51
|
+
@interface_type_memberships
|
52
|
+
end
|
53
|
+
|
54
|
+
def all_references
|
55
|
+
load_all
|
56
|
+
@references
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_type(type_name)
|
60
|
+
load_all
|
61
|
+
@types[type_name]
|
62
|
+
end
|
63
|
+
|
64
|
+
def preload?
|
65
|
+
@preload
|
66
|
+
end
|
67
|
+
|
68
|
+
def preload
|
69
|
+
# Traverse the schema now (and in the *_configured hooks below)
|
70
|
+
# To make sure things are loaded during boot
|
71
|
+
@preloaded_types = Set.new
|
72
|
+
types_to_visit = [
|
73
|
+
@schema.query,
|
74
|
+
@schema.mutation,
|
75
|
+
@schema.subscription,
|
76
|
+
*@schema.introspection_system.types.values,
|
77
|
+
*@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
|
78
|
+
*@schema.orphan_types,
|
79
|
+
]
|
80
|
+
# Root types may have been nil:
|
81
|
+
types_to_visit.compact!
|
82
|
+
ensure_all_loaded(types_to_visit)
|
83
|
+
@profiles.each do |profile_name, example_ctx|
|
84
|
+
example_ctx[:visibility_profile] = profile_name
|
85
|
+
prof = profile_for(example_ctx, profile_name)
|
86
|
+
prof.all_types # force loading
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @api private
|
91
|
+
def query_configured(query_type)
|
92
|
+
if @preload
|
93
|
+
ensure_all_loaded([query_type])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# @api private
|
98
|
+
def mutation_configured(mutation_type)
|
99
|
+
if @preload
|
100
|
+
ensure_all_loaded([mutation_type])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# @api private
|
105
|
+
def subscription_configured(subscription_type)
|
106
|
+
if @preload
|
107
|
+
ensure_all_loaded([subscription_type])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# @api private
|
112
|
+
def orphan_types_configured(orphan_types)
|
113
|
+
if @preload
|
114
|
+
ensure_all_loaded(orphan_types)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# @api private
|
119
|
+
def introspection_system_configured(introspection_system)
|
120
|
+
if @preload
|
121
|
+
introspection_types = [
|
122
|
+
*@schema.introspection_system.types.values,
|
123
|
+
*@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
|
124
|
+
]
|
125
|
+
ensure_all_loaded(introspection_types)
|
34
126
|
end
|
35
127
|
end
|
36
128
|
|
@@ -57,7 +149,11 @@ module GraphQL
|
|
57
149
|
if @profiles.any?
|
58
150
|
if visibility_profile.nil?
|
59
151
|
if @dynamic
|
60
|
-
|
152
|
+
if context.is_a?(Query::NullContext)
|
153
|
+
top_level_profile
|
154
|
+
else
|
155
|
+
@schema.visibility_profile_class.new(context: context, schema: @schema)
|
156
|
+
end
|
61
157
|
elsif @profiles.any?
|
62
158
|
raise ArgumentError, "#{@schema} expects a visibility profile, but `visibility_profile:` wasn't passed. Provide a `visibility_profile:` value or add `dynamic: true` to your visibility configuration."
|
63
159
|
end
|
@@ -66,10 +162,119 @@ module GraphQL
|
|
66
162
|
else
|
67
163
|
@cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: context, schema: @schema)
|
68
164
|
end
|
165
|
+
elsif context.is_a?(Query::NullContext)
|
166
|
+
top_level_profile
|
69
167
|
else
|
70
168
|
@schema.visibility_profile_class.new(context: context, schema: @schema)
|
71
169
|
end
|
72
170
|
end
|
171
|
+
|
172
|
+
attr_reader :top_level
|
173
|
+
|
174
|
+
# @api private
|
175
|
+
attr_reader :unfiltered_interface_type_memberships
|
176
|
+
|
177
|
+
def top_level_profile(refresh: false)
|
178
|
+
if refresh
|
179
|
+
@top_level_profile = nil
|
180
|
+
end
|
181
|
+
@top_level_profile ||= @schema.visibility_profile_class.new(context: Query::NullContext.instance, schema: @schema)
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def ensure_all_loaded(types_to_visit)
|
187
|
+
while (type = types_to_visit.shift)
|
188
|
+
if type.kind.fields? && @preloaded_types.add?(type)
|
189
|
+
type.all_field_definitions.each do |field_defn|
|
190
|
+
field_defn.ensure_loaded
|
191
|
+
types_to_visit << field_defn.type.unwrap
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
top_level_profile(refresh: true)
|
196
|
+
nil
|
197
|
+
end
|
198
|
+
|
199
|
+
def load_all(types: nil)
|
200
|
+
if @visit.nil?
|
201
|
+
# Set up the visit system
|
202
|
+
@interface_type_memberships = Hash.new { |h, interface_type| h[interface_type] = [] }.compare_by_identity
|
203
|
+
@directives = []
|
204
|
+
@types = {} # String => Module
|
205
|
+
@references = Hash.new { |h, member| h[member] = [] }.compare_by_identity
|
206
|
+
@unions_for_references = Set.new
|
207
|
+
@visit = Visibility::Visit.new(@schema) do |member|
|
208
|
+
if member.is_a?(Module)
|
209
|
+
type_name = member.graphql_name
|
210
|
+
if (prev_t = @types[type_name])
|
211
|
+
if prev_t.is_a?(Array)
|
212
|
+
prev_t << member
|
213
|
+
else
|
214
|
+
@types[type_name] = [member, prev_t]
|
215
|
+
end
|
216
|
+
else
|
217
|
+
@types[member.graphql_name] = member
|
218
|
+
end
|
219
|
+
if member < GraphQL::Schema::Directive
|
220
|
+
@directives << member
|
221
|
+
elsif member.respond_to?(:interface_type_memberships)
|
222
|
+
member.interface_type_memberships.each do |itm|
|
223
|
+
@references[itm.abstract_type] << member
|
224
|
+
@interface_type_memberships[itm.abstract_type] << itm
|
225
|
+
end
|
226
|
+
elsif member < GraphQL::Schema::Union
|
227
|
+
@unions_for_references << member
|
228
|
+
end
|
229
|
+
elsif member.is_a?(GraphQL::Schema::Argument)
|
230
|
+
member.validate_default_value
|
231
|
+
@references[member.type.unwrap] << member
|
232
|
+
elsif member.is_a?(GraphQL::Schema::Field)
|
233
|
+
@references[member.type.unwrap] << member
|
234
|
+
end
|
235
|
+
true
|
236
|
+
end
|
237
|
+
|
238
|
+
@schema.root_types.each do |t|
|
239
|
+
@references[t] << true
|
240
|
+
end
|
241
|
+
|
242
|
+
@schema.introspection_system.types.each_value do |t|
|
243
|
+
@references[t] << true
|
244
|
+
end
|
245
|
+
@visit.visit_each(types: []) # visit default directives
|
246
|
+
end
|
247
|
+
|
248
|
+
if types
|
249
|
+
@visit.visit_each(types: types, directives: [])
|
250
|
+
elsif @loaded_all == false
|
251
|
+
@loaded_all = true
|
252
|
+
@visit.visit_each
|
253
|
+
else
|
254
|
+
# already loaded all
|
255
|
+
return
|
256
|
+
end
|
257
|
+
|
258
|
+
# TODO: somehow don't iterate over all these,
|
259
|
+
# only the ones that may have been modified
|
260
|
+
@interface_type_memberships.each do |int_type, type_memberships|
|
261
|
+
referers = @references[int_type].select { |r| r.is_a?(GraphQL::Schema::Field) }
|
262
|
+
if referers.any?
|
263
|
+
type_memberships.each do |type_membership|
|
264
|
+
implementor_type = type_membership.object_type
|
265
|
+
# Add new items only:
|
266
|
+
@references[implementor_type] |= referers
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
@unions_for_references.each do |union_type|
|
272
|
+
refs = @references[union_type]
|
273
|
+
union_type.all_possible_types.each do |object_type|
|
274
|
+
@references[object_type] |= refs # Add new items
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
73
278
|
end
|
74
279
|
end
|
75
280
|
end
|
data/lib/graphql/schema.rb
CHANGED
@@ -445,7 +445,17 @@ module GraphQL
|
|
445
445
|
dup_defn = new_query_object || yield
|
446
446
|
raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
|
447
447
|
elsif use_visibility_profile?
|
448
|
-
|
448
|
+
if block_given?
|
449
|
+
if visibility.preload?
|
450
|
+
@query_object = lazy_load_block.call
|
451
|
+
self.visibility.query_configured(@query_object)
|
452
|
+
else
|
453
|
+
@query_object = lazy_load_block
|
454
|
+
end
|
455
|
+
else
|
456
|
+
@query_object = new_query_object
|
457
|
+
self.visibility.query_configured(@query_object)
|
458
|
+
end
|
449
459
|
else
|
450
460
|
@query_object = new_query_object || lazy_load_block.call
|
451
461
|
add_type_and_traverse(@query_object, root: true)
|
@@ -453,6 +463,8 @@ module GraphQL
|
|
453
463
|
nil
|
454
464
|
elsif @query_object.is_a?(Proc)
|
455
465
|
@query_object = @query_object.call
|
466
|
+
self.visibility&.query_configured(@query_object)
|
467
|
+
@query_object
|
456
468
|
else
|
457
469
|
@query_object || find_inherited_value(:query)
|
458
470
|
end
|
@@ -472,7 +484,17 @@ module GraphQL
|
|
472
484
|
dup_defn = new_mutation_object || yield
|
473
485
|
raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
|
474
486
|
elsif use_visibility_profile?
|
475
|
-
|
487
|
+
if block_given?
|
488
|
+
if visibility.preload?
|
489
|
+
@mutation_object = lazy_load_block.call
|
490
|
+
self.visibility.mutation_configured(@mutation_object)
|
491
|
+
else
|
492
|
+
@mutation_object = lazy_load_block
|
493
|
+
end
|
494
|
+
else
|
495
|
+
@mutation_object = new_mutation_object
|
496
|
+
self.visibility.mutation_configured(@mutation_object)
|
497
|
+
end
|
476
498
|
else
|
477
499
|
@mutation_object = new_mutation_object || lazy_load_block.call
|
478
500
|
add_type_and_traverse(@mutation_object, root: true)
|
@@ -480,6 +502,8 @@ module GraphQL
|
|
480
502
|
nil
|
481
503
|
elsif @mutation_object.is_a?(Proc)
|
482
504
|
@mutation_object = @mutation_object.call
|
505
|
+
self.visibility&.mutation_configured(@mutation_object)
|
506
|
+
@mutation_object
|
483
507
|
else
|
484
508
|
@mutation_object || find_inherited_value(:mutation)
|
485
509
|
end
|
@@ -499,7 +523,17 @@ module GraphQL
|
|
499
523
|
dup_defn = new_subscription_object || yield
|
500
524
|
raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
501
525
|
elsif use_visibility_profile?
|
502
|
-
|
526
|
+
if block_given?
|
527
|
+
if visibility.preload?
|
528
|
+
@subscription_object = lazy_load_block.call
|
529
|
+
visibility.subscription_configured(@subscription_object)
|
530
|
+
else
|
531
|
+
@subscription_object = lazy_load_block
|
532
|
+
end
|
533
|
+
else
|
534
|
+
@subscription_object = new_subscription_object
|
535
|
+
self.visibility.subscription_configured(@subscription_object)
|
536
|
+
end
|
503
537
|
add_subscription_extension_if_necessary
|
504
538
|
else
|
505
539
|
@subscription_object = new_subscription_object || lazy_load_block.call
|
@@ -510,6 +544,7 @@ module GraphQL
|
|
510
544
|
elsif @subscription_object.is_a?(Proc)
|
511
545
|
@subscription_object = @subscription_object.call
|
512
546
|
add_subscription_extension_if_necessary
|
547
|
+
self.visibility.subscription_configured(@subscription_object)
|
513
548
|
@subscription_object
|
514
549
|
else
|
515
550
|
@subscription_object || find_inherited_value(:subscription)
|
@@ -695,20 +730,27 @@ module GraphQL
|
|
695
730
|
type.fields(context)
|
696
731
|
end
|
697
732
|
|
733
|
+
# Pass a custom introspection module here to use it for this schema.
|
734
|
+
# @param new_introspection_namespace [Module] If given, use this module for custom introspection on the schema
|
735
|
+
# @return [Module, nil] The configured namespace, if there is one
|
698
736
|
def introspection(new_introspection_namespace = nil)
|
699
737
|
if new_introspection_namespace
|
700
738
|
@introspection = new_introspection_namespace
|
701
739
|
# reset this cached value:
|
702
740
|
@introspection_system = nil
|
741
|
+
introspection_system
|
742
|
+
@introspection
|
703
743
|
else
|
704
744
|
@introspection || find_inherited_value(:introspection)
|
705
745
|
end
|
706
746
|
end
|
707
747
|
|
748
|
+
# @return [Schema::IntrospectionSystem] Based on {introspection}
|
708
749
|
def introspection_system
|
709
750
|
if !@introspection_system
|
710
751
|
@introspection_system = Schema::IntrospectionSystem.new(self)
|
711
752
|
@introspection_system.resolve_late_bindings
|
753
|
+
self.visibility&.introspection_system_configured(@introspection_system)
|
712
754
|
end
|
713
755
|
@introspection_system
|
714
756
|
end
|
@@ -952,6 +994,13 @@ module GraphQL
|
|
952
994
|
end
|
953
995
|
end
|
954
996
|
|
997
|
+
# Tell the schema about these types so that they can be registered as implementations of interfaces in the schema.
|
998
|
+
#
|
999
|
+
# This method must be used when an object type is connected to the schema as an interface implementor but
|
1000
|
+
# not as a return type of a field. In that case, if the object type isn't registered here, GraphQL-Ruby won't be able to find it.
|
1001
|
+
#
|
1002
|
+
# @param new_orphan_types [Array<Class<GraphQL::Schema::Object>>] Object types to register as implementations of interfaces in the schema.
|
1003
|
+
# @return [Array<Class<GraphQL::Schema::Object>>] All previously-registered orphan types for this schema
|
955
1004
|
def orphan_types(*new_orphan_types)
|
956
1005
|
if new_orphan_types.any?
|
957
1006
|
new_orphan_types = new_orphan_types.flatten
|
@@ -968,6 +1017,7 @@ module GraphQL
|
|
968
1017
|
end
|
969
1018
|
add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
|
970
1019
|
own_orphan_types.concat(new_orphan_types.flatten)
|
1020
|
+
self.visibility&.orphan_types_configured(new_orphan_types)
|
971
1021
|
end
|
972
1022
|
|
973
1023
|
inherited_ot = find_inherited_value(:orphan_types, nil)
|
@@ -14,7 +14,9 @@ module GraphQL
|
|
14
14
|
node_name: parent_type.graphql_name
|
15
15
|
))
|
16
16
|
else
|
17
|
-
|
17
|
+
possible_fields = possible_fields(context, parent_type)
|
18
|
+
suggestion = context.did_you_mean_suggestion(node.name, possible_fields)
|
19
|
+
message = "Field '#{node.name}' doesn't exist on type '#{parent_type.graphql_name}'#{suggestion}"
|
18
20
|
add_error(GraphQL::StaticValidation::FieldsAreDefinedOnTypeError.new(
|
19
21
|
message,
|
20
22
|
nodes: node,
|
@@ -26,6 +28,13 @@ module GraphQL
|
|
26
28
|
super
|
27
29
|
end
|
28
30
|
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def possible_fields(context, parent_type)
|
35
|
+
return EmptyObjects::EMPTY_ARRAY if parent_type.kind.leaf?
|
36
|
+
context.types.fields(parent_type).map(&:graphql_name)
|
37
|
+
end
|
29
38
|
end
|
30
39
|
end
|
31
40
|
end
|
@@ -25,7 +25,7 @@ module GraphQL
|
|
25
25
|
def validate_field_selections(ast_node, resolved_type)
|
26
26
|
msg = if resolved_type.nil?
|
27
27
|
nil
|
28
|
-
elsif
|
28
|
+
elsif ast_node.selections.any? && resolved_type.kind.leaf?
|
29
29
|
selection_strs = ast_node.selections.map do |n|
|
30
30
|
case n
|
31
31
|
when GraphQL::Language::Nodes::InlineFragment
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
38
38
|
raise "Invariant: unexpected selection node: #{n}"
|
39
39
|
end
|
40
40
|
end
|
41
|
-
"Selections can't be made on
|
41
|
+
"Selections can't be made on #{resolved_type.kind.name.sub("_", " ").downcase}s (%{node_name} returns #{resolved_type.graphql_name} but has selections [#{selection_strs.join(", ")}])"
|
42
42
|
elsif resolved_type.kind.fields? && ast_node.selections.empty?
|
43
43
|
"Field must have selections (%{node_name} returns #{resolved_type.graphql_name} but has no selections. Did you mean '#{ast_node.name} { ... }'?)"
|
44
44
|
else
|
@@ -29,6 +29,7 @@ module GraphQL
|
|
29
29
|
@visitor = visitor_class.new(document, self)
|
30
30
|
end
|
31
31
|
|
32
|
+
# TODO stop using def_delegators because of Array allocations
|
32
33
|
def_delegators :@visitor,
|
33
34
|
:path, :type_definition, :field_definition, :argument_definition,
|
34
35
|
:parent_type_definition, :directive_definition, :object_types, :dependencies
|
@@ -92,7 +92,7 @@ module GraphQL
|
|
92
92
|
end
|
93
93
|
graphql_result
|
94
94
|
else
|
95
|
-
unfiltered_type =
|
95
|
+
unfiltered_type = schema.use_visibility_profile? ? schema.visibility.get_type(type_name) : schema.get_type(type_name) # rubocop:disable ContextIsPassedCop
|
96
96
|
if unfiltered_type
|
97
97
|
raise TypeNotVisibleError.new(type_name: type_name)
|
98
98
|
else
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.4.
|
4
|
+
version: 2.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -504,6 +504,7 @@ files:
|
|
504
504
|
- lib/graphql/schema/visibility.rb
|
505
505
|
- lib/graphql/schema/visibility/migration.rb
|
506
506
|
- lib/graphql/schema/visibility/profile.rb
|
507
|
+
- lib/graphql/schema/visibility/visit.rb
|
507
508
|
- lib/graphql/schema/warden.rb
|
508
509
|
- lib/graphql/schema/wrapper.rb
|
509
510
|
- lib/graphql/static_validation.rb
|