graphql 2.3.5 → 2.3.14
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_generator.rb +46 -0
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
- data/lib/graphql/analysis/visitor.rb +283 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +2 -0
- data/lib/graphql/dataloader/source.rb +5 -2
- data/lib/graphql/dataloader.rb +4 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/runtime.rb +29 -25
- data/lib/graphql/execution/interpreter.rb +3 -1
- data/lib/graphql/execution/lookahead.rb +10 -10
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +6 -11
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language/document_from_schema_definition.rb +19 -26
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/language/parser.rb +9 -1
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +0 -1
- data/lib/graphql/query/context.rb +7 -1
- data/lib/graphql/query/null_context.rb +2 -2
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query.rb +26 -7
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +129 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +1 -0
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +19 -5
- data/lib/graphql/schema/build_from_definition.rb +8 -1
- data/lib/graphql/schema/directive/flagged.rb +1 -1
- data/lib/graphql/schema/directive.rb +2 -0
- data/lib/graphql/schema/enum.rb +51 -20
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +85 -39
- data/lib/graphql/schema/has_single_input_argument.rb +2 -1
- data/lib/graphql/schema/input_object.rb +8 -7
- data/lib/graphql/schema/interface.rb +20 -4
- data/lib/graphql/schema/introspection_system.rb +5 -16
- data/lib/graphql/schema/member/has_arguments.rb +14 -9
- data/lib/graphql/schema/member/has_fields.rb +8 -6
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/resolver.rb +5 -5
- data/lib/graphql/schema/subset.rb +509 -0
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/types_migration.rb +187 -0
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator.rb +2 -0
- data/lib/graphql/schema/warden.rb +89 -5
- data/lib/graphql/schema.rb +109 -53
- data/lib/graphql/static_validation/base_visitor.rb +6 -5
- data/lib/graphql/static_validation/literal_validator.rb +4 -4
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +7 -7
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +2 -2
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions.rb +3 -3
- data/lib/graphql/testing/helpers.rb +8 -5
- data/lib/graphql/types/relay/connection_behaviors.rb +10 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +10 -0
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -0
- metadata +31 -13
- data/lib/graphql/analysis/ast/analyzer.rb +0 -91
- data/lib/graphql/analysis/ast/field_usage.rb +0 -84
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -185
- data/lib/graphql/analysis/ast/visitor.rb +0 -284
- data/lib/graphql/analysis/ast.rb +0 -94
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -135,10 +135,11 @@ module GraphQL
|
|
135
135
|
|
136
136
|
def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
|
137
137
|
warden = Warden.from_context(context)
|
138
|
+
skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Subset)
|
138
139
|
for ancestor in ancestors
|
139
140
|
if ancestor.respond_to?(:own_arguments) &&
|
140
141
|
(a = ancestor.own_arguments[argument_name]) &&
|
141
|
-
(a = Warden.visible_entry?(:visible_argument?, a, context, warden))
|
142
|
+
(skip_visible || (a = Warden.visible_entry?(:visible_argument?, a, context, warden)))
|
142
143
|
return a
|
143
144
|
end
|
144
145
|
end
|
@@ -197,16 +198,20 @@ module GraphQL
|
|
197
198
|
end
|
198
199
|
|
199
200
|
def all_argument_definitions
|
200
|
-
|
201
|
-
|
202
|
-
|
201
|
+
if own_arguments.any?
|
202
|
+
all_defns = own_arguments.values
|
203
|
+
all_defns.flatten!
|
204
|
+
all_defns
|
205
|
+
else
|
206
|
+
EmptyObjects::EMPTY_ARRAY
|
207
|
+
end
|
203
208
|
end
|
204
209
|
|
205
210
|
# @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
|
206
211
|
def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
|
207
212
|
warden = Warden.from_context(context)
|
208
|
-
if (arg_config = own_arguments[argument_name]) && (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden))
|
209
|
-
visible_arg
|
213
|
+
if (arg_config = own_arguments[argument_name]) && ((context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Subset)) || (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden)))
|
214
|
+
visible_arg || arg_config
|
210
215
|
elsif defined?(@resolver_class) && @resolver_class
|
211
216
|
@resolver_class.get_field_argument(argument_name, context)
|
212
217
|
else
|
@@ -230,7 +235,7 @@ module GraphQL
|
|
230
235
|
# @return [Interpreter::Arguments, Execution::Lazy<Interpreter::Arguments>]
|
231
236
|
def coerce_arguments(parent_object, values, context, &block)
|
232
237
|
# Cache this hash to avoid re-merging it
|
233
|
-
arg_defns = context.
|
238
|
+
arg_defns = context.types.arguments(self)
|
234
239
|
total_args_count = arg_defns.size
|
235
240
|
|
236
241
|
finished_args = nil
|
@@ -364,8 +369,8 @@ module GraphQL
|
|
364
369
|
end
|
365
370
|
|
366
371
|
if !(
|
367
|
-
context.
|
368
|
-
context.
|
372
|
+
context.types.possible_types(argument.loads).include?(application_object_type) ||
|
373
|
+
context.types.loadable?(argument.loads, context)
|
369
374
|
)
|
370
375
|
err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
|
371
376
|
application_object = load_application_object_failed(err)
|
@@ -99,11 +99,12 @@ module GraphQL
|
|
99
99
|
module InterfaceMethods
|
100
100
|
def get_field(field_name, context = GraphQL::Query::NullContext.instance)
|
101
101
|
warden = Warden.from_context(context)
|
102
|
+
skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Subset)
|
102
103
|
for ancestor in ancestors
|
103
104
|
if ancestor.respond_to?(:own_fields) &&
|
104
105
|
(f_entry = ancestor.own_fields[field_name]) &&
|
105
|
-
(
|
106
|
-
return
|
106
|
+
(skip_visible || (f_entry = Warden.visible_entry?(:visible_field?, f_entry, context, warden)))
|
107
|
+
return f_entry
|
107
108
|
end
|
108
109
|
end
|
109
110
|
nil
|
@@ -120,7 +121,7 @@ module GraphQL
|
|
120
121
|
# Choose the most local definition that passes `.visible?` --
|
121
122
|
# stop checking for fields by name once one has been found.
|
122
123
|
if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
|
123
|
-
visible_fields[field_name] = f
|
124
|
+
visible_fields[field_name] = f.ensure_loaded
|
124
125
|
end
|
125
126
|
end
|
126
127
|
end
|
@@ -134,13 +135,14 @@ module GraphQL
|
|
134
135
|
# Objects need to check that the interface implementation is visible, too
|
135
136
|
warden = Warden.from_context(context)
|
136
137
|
ancs = ancestors
|
138
|
+
skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Subset)
|
137
139
|
i = 0
|
138
140
|
while (ancestor = ancs[i])
|
139
141
|
if ancestor.respond_to?(:own_fields) &&
|
140
142
|
visible_interface_implementation?(ancestor, context, warden) &&
|
141
143
|
(f_entry = ancestor.own_fields[field_name]) &&
|
142
|
-
(
|
143
|
-
return
|
144
|
+
(skip_visible || (f_entry = Warden.visible_entry?(:visible_field?, f_entry, context, warden)))
|
145
|
+
return (skip_visible ? f_entry : f_entry.ensure_loaded)
|
144
146
|
end
|
145
147
|
i += 1
|
146
148
|
end
|
@@ -159,7 +161,7 @@ module GraphQL
|
|
159
161
|
# Choose the most local definition that passes `.visible?` --
|
160
162
|
# stop checking for fields by name once one has been found.
|
161
163
|
if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
|
162
|
-
visible_fields[field_name] = f
|
164
|
+
visible_fields[field_name] = f.ensure_loaded
|
163
165
|
end
|
164
166
|
end
|
165
167
|
end
|
@@ -7,7 +7,11 @@ module GraphQL
|
|
7
7
|
module HasUnresolvedTypeError
|
8
8
|
private
|
9
9
|
def add_unresolved_type_error(child_class)
|
10
|
-
child_class.
|
10
|
+
if child_class.name # Don't set this for anonymous classes
|
11
|
+
child_class.const_set(:UnresolvedTypeError, Class.new(GraphQL::UnresolvedTypeError))
|
12
|
+
else
|
13
|
+
child_class.const_set(:UnresolvedTypeError, UnresolvedTypeError)
|
14
|
+
end
|
11
15
|
end
|
12
16
|
end
|
13
17
|
end
|
@@ -25,6 +25,7 @@ module GraphQL
|
|
25
25
|
extend GraphQL::Schema::Member::HasValidators
|
26
26
|
include Schema::Member::HasPath
|
27
27
|
extend Schema::Member::HasPath
|
28
|
+
extend Schema::Member::HasDirectives
|
28
29
|
|
29
30
|
# @param object [Object] The application object that this field is being resolved on
|
30
31
|
# @param context [GraphQL::Query::Context]
|
@@ -35,7 +36,7 @@ module GraphQL
|
|
35
36
|
@field = field
|
36
37
|
# Since this hash is constantly rebuilt, cache it for this call
|
37
38
|
@arguments_by_keyword = {}
|
38
|
-
|
39
|
+
context.types.arguments(self.class).each do |arg|
|
39
40
|
@arguments_by_keyword[arg.keyword] = arg
|
40
41
|
end
|
41
42
|
@prepared_arguments = nil
|
@@ -151,7 +152,7 @@ module GraphQL
|
|
151
152
|
# @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.)
|
152
153
|
def authorized?(**inputs)
|
153
154
|
arg_owner = @field # || self.class
|
154
|
-
args =
|
155
|
+
args = context.types.arguments(arg_owner)
|
155
156
|
authorize_arguments(args, inputs)
|
156
157
|
end
|
157
158
|
|
@@ -168,7 +169,7 @@ module GraphQL
|
|
168
169
|
private
|
169
170
|
|
170
171
|
def authorize_arguments(args, inputs)
|
171
|
-
args.
|
172
|
+
args.each do |argument|
|
172
173
|
arg_keyword = argument.keyword
|
173
174
|
if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value)
|
174
175
|
auth_result = argument.authorized?(self, arg_value, context)
|
@@ -181,10 +182,9 @@ module GraphQL
|
|
181
182
|
elsif auth_result == false
|
182
183
|
return auth_result
|
183
184
|
end
|
184
|
-
else
|
185
|
-
true
|
186
185
|
end
|
187
186
|
end
|
187
|
+
true
|
188
188
|
end
|
189
189
|
|
190
190
|
def load_arguments(args)
|
@@ -0,0 +1,509 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
# This class filters the types, fields, arguments, enum values, and directives in a schema
|
6
|
+
# based on the given `context`.
|
7
|
+
#
|
8
|
+
# It's like {Warden}, but has some differences:
|
9
|
+
#
|
10
|
+
# - It doesn't use {Schema}'s top-level caches (eg {Schema.references_to}, {Schema.possible_types}, {Schema.types})
|
11
|
+
# - It doesn't hide Interface or Union types when all their possible types are hidden. (Instead, those types should implement `.visible?` to hide in that case.)
|
12
|
+
# - It checks `.visible?` on root introspection types
|
13
|
+
#
|
14
|
+
# In the future, {Subset} will support lazy-loading types as needed during execution and multi-request caching of subsets.
|
15
|
+
#
|
16
|
+
# @see Schema::TypesMigration for a helper class in adopting this filter
|
17
|
+
class Subset
|
18
|
+
# @return [Schema::Subset]
|
19
|
+
def self.from_context(ctx, schema)
|
20
|
+
if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self)
|
21
|
+
types
|
22
|
+
else
|
23
|
+
# TODO use a cached instance from the schema
|
24
|
+
self.new(context: ctx, schema: schema)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.pass_thru(context:, schema:)
|
29
|
+
subset = self.new(context: context, schema: schema)
|
30
|
+
subset.instance_variable_set(:@cached_visible, Hash.new { |h,k| h[k] = true })
|
31
|
+
subset
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(context:, schema:)
|
35
|
+
@context = context
|
36
|
+
@schema = schema
|
37
|
+
@all_types = {}
|
38
|
+
@all_types_loaded = false
|
39
|
+
@unvisited_types = []
|
40
|
+
@referenced_types = Hash.new { |h, type_defn| h[type_defn] = [] }.compare_by_identity
|
41
|
+
@cached_directives = {}
|
42
|
+
@all_directives = nil
|
43
|
+
@cached_visible = Hash.new { |h, member|
|
44
|
+
h[member] = @schema.visible?(member, @context)
|
45
|
+
}.compare_by_identity
|
46
|
+
|
47
|
+
@cached_visible_fields = Hash.new { |h, owner|
|
48
|
+
h[owner] = Hash.new do |h2, field|
|
49
|
+
h2[field] = if @cached_visible[field] &&
|
50
|
+
(ret_type = field.type.unwrap) &&
|
51
|
+
@cached_visible[ret_type] &&
|
52
|
+
(owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner))
|
53
|
+
|
54
|
+
if !field.introspection?
|
55
|
+
# The problem is that some introspection fields may have references
|
56
|
+
# to non-custom introspection types.
|
57
|
+
# If those were added here, they'd cause a DuplicateNamesError.
|
58
|
+
# This is basically a bug -- those fields _should_ reference the custom types.
|
59
|
+
add_type(ret_type, field)
|
60
|
+
end
|
61
|
+
true
|
62
|
+
else
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end.compare_by_identity
|
66
|
+
}.compare_by_identity
|
67
|
+
|
68
|
+
@cached_visible_arguments = Hash.new do |h, arg|
|
69
|
+
h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
|
70
|
+
add_type(arg_type, arg)
|
71
|
+
true
|
72
|
+
else
|
73
|
+
false
|
74
|
+
end
|
75
|
+
end.compare_by_identity
|
76
|
+
|
77
|
+
@cached_parent_fields = Hash.new do |h, type|
|
78
|
+
h[type] = Hash.new do |h2, field_name|
|
79
|
+
h2[field_name] = type.get_field(field_name, @context)
|
80
|
+
end
|
81
|
+
end.compare_by_identity
|
82
|
+
|
83
|
+
@cached_parent_arguments = Hash.new do |h, arg_owner|
|
84
|
+
h[arg_owner] = Hash.new do |h2, arg_name|
|
85
|
+
h2[arg_name] = arg_owner.get_argument(arg_name, @context)
|
86
|
+
end
|
87
|
+
end.compare_by_identity
|
88
|
+
|
89
|
+
@cached_possible_types = Hash.new do |h, type|
|
90
|
+
h[type] = case type.kind.name
|
91
|
+
when "INTERFACE"
|
92
|
+
load_all_types
|
93
|
+
pts = []
|
94
|
+
@unfiltered_interface_type_memberships[type].each { |itm|
|
95
|
+
if @cached_visible[itm] && (ot = itm.object_type) && @cached_visible[ot] && referenced?(ot)
|
96
|
+
pts << ot
|
97
|
+
end
|
98
|
+
}
|
99
|
+
pts
|
100
|
+
when "UNION"
|
101
|
+
pts = []
|
102
|
+
type.type_memberships.each { |tm|
|
103
|
+
if @cached_visible[tm] &&
|
104
|
+
(ot = tm.object_type) &&
|
105
|
+
@cached_visible[ot] &&
|
106
|
+
referenced?(ot)
|
107
|
+
pts << ot
|
108
|
+
end
|
109
|
+
}
|
110
|
+
pts
|
111
|
+
when "OBJECT"
|
112
|
+
load_all_types
|
113
|
+
if @all_types[type.graphql_name] == type
|
114
|
+
[type]
|
115
|
+
else
|
116
|
+
EmptyObjects::EMPTY_ARRAY
|
117
|
+
end
|
118
|
+
else
|
119
|
+
GraphQL::EmptyObjects::EMPTY_ARRAY
|
120
|
+
end
|
121
|
+
end.compare_by_identity
|
122
|
+
|
123
|
+
@cached_enum_values = Hash.new do |h, enum_t|
|
124
|
+
values = non_duplicate_items(enum_t.all_enum_value_definitions, @cached_visible)
|
125
|
+
if values.size == 0
|
126
|
+
raise GraphQL::Schema::Enum::MissingValuesError.new(enum_t)
|
127
|
+
end
|
128
|
+
h[enum_t] = values
|
129
|
+
end.compare_by_identity
|
130
|
+
|
131
|
+
@cached_fields = Hash.new do |h, owner|
|
132
|
+
h[owner] = non_duplicate_items(owner.all_field_definitions.each(&:ensure_loaded), @cached_visible_fields[owner])
|
133
|
+
end.compare_by_identity
|
134
|
+
|
135
|
+
@cached_arguments = Hash.new do |h, owner|
|
136
|
+
h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
|
137
|
+
end.compare_by_identity
|
138
|
+
end
|
139
|
+
|
140
|
+
def field_on_visible_interface?(field, owner)
|
141
|
+
ints = owner.interface_type_memberships.map(&:abstract_type)
|
142
|
+
field_name = field.graphql_name
|
143
|
+
filtered_ints = interfaces(owner)
|
144
|
+
any_interface_has_field = false
|
145
|
+
any_interface_has_visible_field = false
|
146
|
+
ints.each do |int_t|
|
147
|
+
if (_int_f_defn = @cached_parent_fields[int_t][field_name])
|
148
|
+
any_interface_has_field = true
|
149
|
+
|
150
|
+
if filtered_ints.include?(int_t) # TODO cycles, or maybe not necessary since previously checked? && @cached_visible_fields[owner][field]
|
151
|
+
any_interface_has_visible_field = true
|
152
|
+
break
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
if any_interface_has_field
|
158
|
+
any_interface_has_visible_field
|
159
|
+
else
|
160
|
+
true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def type(type_name)
|
165
|
+
t = if (loaded_t = @all_types[type_name])
|
166
|
+
loaded_t
|
167
|
+
elsif !@all_types_loaded
|
168
|
+
load_all_types
|
169
|
+
@all_types[type_name]
|
170
|
+
end
|
171
|
+
if t
|
172
|
+
if t.is_a?(Array)
|
173
|
+
vis_t = nil
|
174
|
+
t.each do |t_defn|
|
175
|
+
if @cached_visible[t_defn]
|
176
|
+
if vis_t.nil?
|
177
|
+
vis_t = t_defn
|
178
|
+
else
|
179
|
+
raise_duplicate_definition(vis_t, t_defn)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
vis_t
|
184
|
+
else
|
185
|
+
if t && @cached_visible[t]
|
186
|
+
t
|
187
|
+
else
|
188
|
+
nil
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def field(owner, field_name)
|
195
|
+
f = if owner.kind.fields? && (field = @cached_parent_fields[owner][field_name])
|
196
|
+
field
|
197
|
+
elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
|
198
|
+
entry_point_field
|
199
|
+
elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
|
200
|
+
dynamic_field
|
201
|
+
else
|
202
|
+
nil
|
203
|
+
end
|
204
|
+
if f.is_a?(Array)
|
205
|
+
visible_f = nil
|
206
|
+
f.each do |f_defn|
|
207
|
+
if @cached_visible_fields[owner][f_defn]
|
208
|
+
|
209
|
+
if visible_f.nil?
|
210
|
+
visible_f = f_defn
|
211
|
+
else
|
212
|
+
raise_duplicate_definition(visible_f, f_defn)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
visible_f.ensure_loaded
|
217
|
+
elsif f && @cached_visible_fields[owner][f.ensure_loaded]
|
218
|
+
f
|
219
|
+
else
|
220
|
+
nil
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def fields(owner)
|
225
|
+
@cached_fields[owner]
|
226
|
+
end
|
227
|
+
|
228
|
+
def arguments(owner)
|
229
|
+
@cached_arguments[owner]
|
230
|
+
end
|
231
|
+
|
232
|
+
def argument(owner, arg_name)
|
233
|
+
arg = @cached_parent_arguments[owner][arg_name]
|
234
|
+
if arg.is_a?(Array)
|
235
|
+
visible_arg = nil
|
236
|
+
arg.each do |arg_defn|
|
237
|
+
if @cached_visible_arguments[arg_defn]
|
238
|
+
if visible_arg.nil?
|
239
|
+
visible_arg = arg_defn
|
240
|
+
else
|
241
|
+
raise_duplicate_definition(visible_arg, arg_defn)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
visible_arg
|
246
|
+
else
|
247
|
+
if arg && @cached_visible_arguments[arg]
|
248
|
+
arg
|
249
|
+
else
|
250
|
+
nil
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def possible_types(type)
|
256
|
+
@cached_possible_types[type]
|
257
|
+
end
|
258
|
+
|
259
|
+
def interfaces(obj_or_int_type)
|
260
|
+
ints = obj_or_int_type.interface_type_memberships
|
261
|
+
.select { |itm| @cached_visible[itm] && @cached_visible[itm.abstract_type] }
|
262
|
+
.map!(&:abstract_type)
|
263
|
+
ints.uniq! # Remove any duplicate interfaces implemented via other interfaces
|
264
|
+
ints
|
265
|
+
end
|
266
|
+
|
267
|
+
def query_root
|
268
|
+
add_if_visible(@schema.query)
|
269
|
+
end
|
270
|
+
|
271
|
+
def mutation_root
|
272
|
+
add_if_visible(@schema.mutation)
|
273
|
+
end
|
274
|
+
|
275
|
+
def subscription_root
|
276
|
+
add_if_visible(@schema.subscription)
|
277
|
+
end
|
278
|
+
|
279
|
+
def all_types
|
280
|
+
load_all_types
|
281
|
+
@all_types.values
|
282
|
+
end
|
283
|
+
|
284
|
+
def all_types_h
|
285
|
+
load_all_types
|
286
|
+
@all_types
|
287
|
+
end
|
288
|
+
|
289
|
+
def enum_values(owner)
|
290
|
+
@cached_enum_values[owner]
|
291
|
+
end
|
292
|
+
|
293
|
+
def directive_exists?(dir_name)
|
294
|
+
if (dir = @schema.directives[dir_name]) && @cached_visible[dir]
|
295
|
+
!!dir
|
296
|
+
else
|
297
|
+
load_all_types
|
298
|
+
!!@cached_directives[dir_name]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def directives
|
303
|
+
@all_directives ||= begin
|
304
|
+
load_all_types
|
305
|
+
dirs = []
|
306
|
+
@schema.directives.each do |name, dir_defn|
|
307
|
+
if !@cached_directives[name] && @cached_visible[dir_defn]
|
308
|
+
dirs << dir_defn
|
309
|
+
end
|
310
|
+
end
|
311
|
+
dirs.concat(@cached_directives.values)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def loadable?(t, _ctx)
|
316
|
+
!@all_types[t.graphql_name] && @cached_visible[t]
|
317
|
+
end
|
318
|
+
|
319
|
+
def loaded_types
|
320
|
+
@all_types.values
|
321
|
+
end
|
322
|
+
|
323
|
+
def reachable_type?(name)
|
324
|
+
load_all_types
|
325
|
+
!!@all_types[name]
|
326
|
+
end
|
327
|
+
|
328
|
+
private
|
329
|
+
|
330
|
+
def add_if_visible(t)
|
331
|
+
(t && @cached_visible[t]) ? (add_type(t, true); t) : nil
|
332
|
+
end
|
333
|
+
|
334
|
+
def add_type(t, by_member)
|
335
|
+
if t && @cached_visible[t]
|
336
|
+
n = t.graphql_name
|
337
|
+
if (prev_t = @all_types[n])
|
338
|
+
if !prev_t.equal?(t)
|
339
|
+
raise_duplicate_definition(prev_t, t)
|
340
|
+
end
|
341
|
+
false
|
342
|
+
else
|
343
|
+
@referenced_types[t] << by_member
|
344
|
+
@all_types[n] = t
|
345
|
+
@unvisited_types << t
|
346
|
+
true
|
347
|
+
end
|
348
|
+
else
|
349
|
+
false
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def non_duplicate_items(definitions, visibility_cache)
|
354
|
+
non_dups = []
|
355
|
+
definitions.each do |defn|
|
356
|
+
if visibility_cache[defn]
|
357
|
+
if (dup_defn = non_dups.find { |d| d.graphql_name == defn.graphql_name })
|
358
|
+
raise_duplicate_definition(dup_defn, defn)
|
359
|
+
end
|
360
|
+
non_dups << defn
|
361
|
+
end
|
362
|
+
end
|
363
|
+
non_dups
|
364
|
+
end
|
365
|
+
|
366
|
+
def raise_duplicate_definition(first_defn, second_defn)
|
367
|
+
raise DuplicateNamesError.new(duplicated_name: first_defn.path, duplicated_definition_1: first_defn.inspect, duplicated_definition_2: second_defn.inspect)
|
368
|
+
end
|
369
|
+
|
370
|
+
def referenced?(t)
|
371
|
+
load_all_types
|
372
|
+
@referenced_types[t].any? { |reference| (reference == true) || @cached_visible[reference] }
|
373
|
+
end
|
374
|
+
|
375
|
+
def load_all_types
|
376
|
+
return if @all_types_loaded
|
377
|
+
@all_types_loaded = true
|
378
|
+
entry_point_types = [
|
379
|
+
query_root,
|
380
|
+
mutation_root,
|
381
|
+
subscription_root,
|
382
|
+
*@schema.introspection_system.types.values,
|
383
|
+
]
|
384
|
+
|
385
|
+
# Don't include any orphan_types whose interfaces aren't visible.
|
386
|
+
@schema.orphan_types.each do |orphan_type|
|
387
|
+
if @cached_visible[orphan_type] &&
|
388
|
+
orphan_type.interface_type_memberships.any? { |tm| @cached_visible[tm] && @cached_visible[tm.abstract_type] }
|
389
|
+
entry_point_types << orphan_type
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
@schema.directives.each do |_dir_name, dir_class|
|
394
|
+
if @cached_visible[dir_class]
|
395
|
+
arguments(dir_class).each do |arg|
|
396
|
+
entry_point_types << arg.type.unwrap
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
entry_point_types.compact! # TODO why is this necessary?!
|
402
|
+
entry_point_types.flatten! # handle multiple defns
|
403
|
+
entry_point_types.each { |t| add_type(t, true) }
|
404
|
+
|
405
|
+
@unfiltered_interface_type_memberships = Hash.new { |h, k| h[k] = [] }.compare_by_identity
|
406
|
+
@add_possible_types = Set.new
|
407
|
+
|
408
|
+
while @unvisited_types.any?
|
409
|
+
while t = @unvisited_types.pop
|
410
|
+
# These have already been checked for `.visible?`
|
411
|
+
visit_type(t)
|
412
|
+
end
|
413
|
+
@add_possible_types.each do |int_t|
|
414
|
+
itms = @unfiltered_interface_type_memberships[int_t]
|
415
|
+
itms.each do |itm|
|
416
|
+
if @cached_visible[itm] && (obj_type = itm.object_type) && @cached_visible[obj_type]
|
417
|
+
add_type(obj_type, itm)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
@add_possible_types.clear
|
422
|
+
end
|
423
|
+
|
424
|
+
@all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) }
|
425
|
+
nil
|
426
|
+
end
|
427
|
+
|
428
|
+
def visit_type(type)
|
429
|
+
visit_directives(type)
|
430
|
+
case type.kind.name
|
431
|
+
when "OBJECT", "INTERFACE"
|
432
|
+
if type.kind.object?
|
433
|
+
type.interface_type_memberships.each do |itm|
|
434
|
+
@unfiltered_interface_type_memberships[itm.abstract_type] << itm
|
435
|
+
end
|
436
|
+
# recurse into visible implemented interfaces
|
437
|
+
interfaces(type).each do |interface|
|
438
|
+
add_type(interface, type)
|
439
|
+
end
|
440
|
+
else
|
441
|
+
type.orphan_types.each { |t| add_type(t, type)}
|
442
|
+
end
|
443
|
+
|
444
|
+
# recurse into visible fields
|
445
|
+
t_f = type.all_field_definitions
|
446
|
+
t_f.each do |field|
|
447
|
+
field.ensure_loaded
|
448
|
+
if @cached_visible[field]
|
449
|
+
visit_directives(field)
|
450
|
+
field_type = field.type.unwrap
|
451
|
+
if field_type.kind.interface?
|
452
|
+
@add_possible_types.add(field_type)
|
453
|
+
end
|
454
|
+
add_type(field_type, field)
|
455
|
+
|
456
|
+
# recurse into visible arguments
|
457
|
+
arguments(field).each do |argument|
|
458
|
+
visit_directives(argument)
|
459
|
+
add_type(argument.type.unwrap, argument)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
when "INPUT_OBJECT"
|
464
|
+
# recurse into visible arguments
|
465
|
+
arguments(type).each do |argument|
|
466
|
+
visit_directives(argument)
|
467
|
+
add_type(argument.type.unwrap, argument)
|
468
|
+
end
|
469
|
+
when "UNION"
|
470
|
+
# recurse into visible possible types
|
471
|
+
type.type_memberships.each do |tm|
|
472
|
+
if @cached_visible[tm]
|
473
|
+
obj_t = tm.object_type
|
474
|
+
if obj_t.is_a?(String)
|
475
|
+
obj_t = Member::BuildType.constantize(obj_t)
|
476
|
+
tm.object_type = obj_t
|
477
|
+
end
|
478
|
+
if @cached_visible[obj_t]
|
479
|
+
add_type(obj_t, tm)
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
483
|
+
when "ENUM"
|
484
|
+
enum_values(type).each do |val|
|
485
|
+
visit_directives(val)
|
486
|
+
end
|
487
|
+
when "SCALAR"
|
488
|
+
# pass
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def visit_directives(member)
|
493
|
+
member.directives.each { |dir|
|
494
|
+
dir_class = dir.class
|
495
|
+
if @cached_visible[dir_class]
|
496
|
+
dir_name = dir_class.graphql_name
|
497
|
+
if (existing_dir = @cached_directives[dir_name])
|
498
|
+
if existing_dir != dir_class
|
499
|
+
raise ArgumentError, "Two directives for `@#{dir_name}`: #{existing_dir}, #{dir.class}"
|
500
|
+
end
|
501
|
+
else
|
502
|
+
@cached_directives[dir.graphql_name] = dir_class
|
503
|
+
end
|
504
|
+
end
|
505
|
+
}
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
@@ -5,13 +5,13 @@ module GraphQL
|
|
5
5
|
module TypeExpression
|
6
6
|
# Fetch a type from a type map by its AST specification.
|
7
7
|
# Return `nil` if not found.
|
8
|
-
# @param type_owner [#
|
8
|
+
# @param type_owner [#type] A thing for looking up types by name
|
9
9
|
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
10
10
|
# @return [Class, GraphQL::Schema::NonNull, GraphQL::Schema:List]
|
11
11
|
def self.build_type(type_owner, ast_node)
|
12
12
|
case ast_node
|
13
13
|
when GraphQL::Language::Nodes::TypeName
|
14
|
-
type_owner.
|
14
|
+
type_owner.type(ast_node.name) # rubocop:disable Development/ContextIsPassedCop -- this is a `context` or `warden`, it's already query-aware
|
15
15
|
when GraphQL::Language::Nodes::NonNullType
|
16
16
|
ast_inner_type = ast_node.of_type
|
17
17
|
inner_type = build_type(type_owner, ast_inner_type)
|