graphql 2.4.1 → 2.4.4
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/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
|