graphql 1.12.25 → 1.13.0
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/mutation_generator.rb +1 -1
- data/lib/generators/graphql/type_generator.rb +0 -1
- data/lib/graphql/analysis/ast/field_usage.rb +4 -8
- data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
- data/lib/graphql/analysis/ast/visitor.rb +4 -4
- data/lib/graphql/backtrace/table.rb +1 -1
- data/lib/graphql/dataloader.rb +55 -22
- data/lib/graphql/directive.rb +0 -4
- data/lib/graphql/enum_type.rb +5 -1
- data/lib/graphql/execution/errors.rb +1 -0
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
- data/lib/graphql/execution/interpreter/runtime.rb +20 -12
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/input_value_type.rb +4 -4
- data/lib/graphql/introspection/schema_type.rb +2 -2
- data/lib/graphql/introspection/type_type.rb +10 -10
- data/lib/graphql/language/block_string.rb +0 -4
- data/lib/graphql/language/document_from_schema_definition.rb +4 -2
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/nodes.rb +3 -14
- data/lib/graphql/language/parser.rb +442 -434
- data/lib/graphql/language/parser.y +5 -4
- data/lib/graphql/language/printer.rb +6 -1
- data/lib/graphql/language/sanitized_printer.rb +5 -5
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/arguments_cache.rb +1 -1
- data/lib/graphql/query/context.rb +5 -2
- data/lib/graphql/query/literal_input.rb +1 -1
- data/lib/graphql/query/null_context.rb +12 -7
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/schema/addition.rb +37 -28
- data/lib/graphql/schema/argument.rb +11 -9
- data/lib/graphql/schema/build_from_definition.rb +12 -13
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +2 -6
- data/lib/graphql/schema/enum.rb +57 -9
- data/lib/graphql/schema/enum_value.rb +5 -1
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +92 -18
- data/lib/graphql/schema/find_inherited_value.rb +1 -0
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +11 -13
- data/lib/graphql/schema/interface.rb +8 -19
- data/lib/graphql/schema/member/accepts_definition.rb +8 -1
- data/lib/graphql/schema/member/build_type.rb +0 -4
- data/lib/graphql/schema/member/has_arguments.rb +55 -13
- data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +76 -18
- data/lib/graphql/schema/member/has_interfaces.rb +90 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/object.rb +7 -74
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +29 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
- data/lib/graphql/schema/resolver.rb +19 -5
- data/lib/graphql/schema/subscription.rb +11 -1
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +18 -4
- data/lib/graphql/schema/union.rb +6 -1
- data/lib/graphql/schema/validator/format_validator.rb +0 -4
- data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +87 -15
- data/lib/graphql/static_validation/base_visitor.rb +5 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -15
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -3
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +6 -4
- data/lib/graphql/subscriptions/event.rb +20 -12
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/types/relay/has_node_field.rb +1 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +9 -31
- metadata +14 -6
@@ -4,8 +4,6 @@ module GraphQL
|
|
4
4
|
class Schema
|
5
5
|
# This class joins an object type to an abstract type (interface or union) of which
|
6
6
|
# it is a member.
|
7
|
-
#
|
8
|
-
# TODO: Not yet implemented for interfaces.
|
9
7
|
class TypeMembership
|
10
8
|
# @return [Class<GraphQL::Schema::Object>]
|
11
9
|
attr_accessor :object_type
|
@@ -26,9 +24,25 @@ module GraphQL
|
|
26
24
|
end
|
27
25
|
|
28
26
|
# @return [Boolean] if false, {#object_type} will be treated as _not_ a member of {#abstract_type}
|
29
|
-
def visible?(
|
30
|
-
|
27
|
+
def visible?(ctx)
|
28
|
+
warden = Warden.from_context(ctx)
|
29
|
+
(@object_type.respond_to?(:visible?) ? warden.visible_type?(@object_type, ctx) : true) &&
|
30
|
+
(@abstract_type.respond_to?(:visible?) ? warden.visible_type?(@abstract_type, ctx) : true)
|
31
31
|
end
|
32
|
+
|
33
|
+
def graphql_name
|
34
|
+
"#{@object_type.graphql_name}.#{@abstract_type.kind.interface? ? "implements" : "belongsTo" }.#{@abstract_type.graphql_name}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def path
|
38
|
+
graphql_name
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
"#<#{self.class} #{@object_type.inspect} => #{@abstract_type.inspect}>"
|
43
|
+
end
|
44
|
+
|
45
|
+
alias :type_class :itself
|
32
46
|
end
|
33
47
|
end
|
34
48
|
end
|
data/lib/graphql/schema/union.rb
CHANGED
@@ -19,8 +19,9 @@ module GraphQL
|
|
19
19
|
end
|
20
20
|
else
|
21
21
|
visible_types = []
|
22
|
+
warden = Warden.from_context(context)
|
22
23
|
type_memberships.each do |type_membership|
|
23
|
-
if
|
24
|
+
if warden.visible_type_membership?(type_membership, context)
|
24
25
|
visible_types << type_membership.object_type
|
25
26
|
end
|
26
27
|
end
|
@@ -28,6 +29,10 @@ module GraphQL
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
32
|
+
def all_possible_types
|
33
|
+
type_memberships.map(&:object_type)
|
34
|
+
end
|
35
|
+
|
31
36
|
def to_graphql
|
32
37
|
type_defn = GraphQL::UnionType.new
|
33
38
|
type_defn.name = graphql_name
|
@@ -18,10 +18,6 @@ module GraphQL
|
|
18
18
|
# # It's pretty hard to come up with a legitimate use case for `without:`
|
19
19
|
#
|
20
20
|
class FormatValidator < Validator
|
21
|
-
if !String.method_defined?(:match?)
|
22
|
-
using GraphQL::StringMatchBackport
|
23
|
-
end
|
24
|
-
|
25
21
|
# @param with [RegExp, nil]
|
26
22
|
# @param without [Regexp, nil]
|
27
23
|
# @param message [String]
|
@@ -37,6 +37,50 @@ module GraphQL
|
|
37
37
|
#
|
38
38
|
# @api private
|
39
39
|
class Warden
|
40
|
+
def self.from_context(context)
|
41
|
+
(context.respond_to?(:warden) && context.warden) || PassThruWarden
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param visibility_method [Symbol] a Warden method to call for this entry
|
45
|
+
# @param entry [Object, Array<Object>] One or more definitions for a given name in a GraphQL Schema
|
46
|
+
# @param context [GraphQL::Query::Context]
|
47
|
+
# @param warden [Warden]
|
48
|
+
# @return [Object] `entry` or one of `entry`'s items if exactly one of them is visible for this context
|
49
|
+
# @return [nil] If neither `entry` nor any of `entry`'s items are visible for this context
|
50
|
+
def self.visible_entry?(visibility_method, entry, context, warden = Warden.from_context(context))
|
51
|
+
if entry.is_a?(Array)
|
52
|
+
visible_item = nil
|
53
|
+
entry.each do |item|
|
54
|
+
if warden.public_send(visibility_method, item, context)
|
55
|
+
if visible_item.nil?
|
56
|
+
visible_item = item
|
57
|
+
else
|
58
|
+
raise Schema::DuplicateNamesError, "Found two visible definitions for `#{item.path}`: #{visible_item.inspect}, #{item.inspect}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
visible_item
|
63
|
+
elsif warden.public_send(visibility_method, entry, context)
|
64
|
+
entry
|
65
|
+
else
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# This is used when a caller provides a Hash for context.
|
71
|
+
# We want to call the schema's hooks, but we don't have a full-blown warden.
|
72
|
+
# The `context` arguments to these methods exist purely to simplify the code that
|
73
|
+
# calls methods on this object, so it will have everything it needs.
|
74
|
+
class PassThruWarden
|
75
|
+
class << self
|
76
|
+
def visible_field?(field, ctx); field.visible?(ctx); end
|
77
|
+
def visible_argument?(arg, ctx); arg.visible?(ctx); end
|
78
|
+
def visible_type?(type, ctx); type.visible?(ctx); end
|
79
|
+
def visible_enum_value?(ev, ctx); ev.visible?(ctx); end
|
80
|
+
def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
40
84
|
# @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
|
41
85
|
# @param context [GraphQL::Query::Context]
|
42
86
|
# @param schema [GraphQL::Schema]
|
@@ -54,8 +98,8 @@ module GraphQL
|
|
54
98
|
def types
|
55
99
|
@types ||= begin
|
56
100
|
vis_types = {}
|
57
|
-
@schema.types.each do |n, t|
|
58
|
-
if
|
101
|
+
@schema.types(@context).each do |n, t|
|
102
|
+
if visible_and_reachable_type?(t)
|
59
103
|
vis_types[n] = t
|
60
104
|
end
|
61
105
|
end
|
@@ -66,8 +110,8 @@ module GraphQL
|
|
66
110
|
# @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
|
67
111
|
def get_type(type_name)
|
68
112
|
@visible_types ||= read_through do |name|
|
69
|
-
type_defn = @schema.get_type(name)
|
70
|
-
if type_defn &&
|
113
|
+
type_defn = @schema.get_type(name, @context)
|
114
|
+
if type_defn && visible_and_reachable_type?(type_defn)
|
71
115
|
type_defn
|
72
116
|
else
|
73
117
|
nil
|
@@ -84,7 +128,7 @@ module GraphQL
|
|
84
128
|
|
85
129
|
# @return Boolean True if the type is visible and reachable in the schema
|
86
130
|
def reachable_type?(type_name)
|
87
|
-
type = get_type(type_name)
|
131
|
+
type = get_type(type_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
|
88
132
|
type && reachable_type_set.include?(type)
|
89
133
|
end
|
90
134
|
|
@@ -92,8 +136,8 @@ module GraphQL
|
|
92
136
|
def get_field(parent_type, field_name)
|
93
137
|
@visible_parent_fields ||= read_through do |type|
|
94
138
|
read_through do |f_name|
|
95
|
-
field_defn = @schema.get_field(type, f_name)
|
96
|
-
if field_defn && visible_field?(
|
139
|
+
field_defn = @schema.get_field(type, f_name, @context)
|
140
|
+
if field_defn && visible_field?(field_defn, nil, type)
|
97
141
|
field_defn
|
98
142
|
else
|
99
143
|
nil
|
@@ -106,15 +150,15 @@ module GraphQL
|
|
106
150
|
|
107
151
|
# @return [GraphQL::Argument, nil] The argument named `argument_name` on `parent_type`, if it exists and is visible
|
108
152
|
def get_argument(parent_type, argument_name)
|
109
|
-
argument = parent_type.get_argument(argument_name)
|
110
|
-
return argument if argument && visible_argument?(argument)
|
153
|
+
argument = parent_type.get_argument(argument_name, @context)
|
154
|
+
return argument if argument && visible_argument?(argument, @context)
|
111
155
|
end
|
112
156
|
|
113
157
|
# @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
|
114
158
|
def possible_types(type_defn)
|
115
159
|
@visible_possible_types ||= read_through { |type_defn|
|
116
160
|
pt = @schema.possible_types(type_defn, @context)
|
117
|
-
pt.select { |t|
|
161
|
+
pt.select { |t| visible_and_reachable_type?(t) }
|
118
162
|
}
|
119
163
|
@visible_possible_types[type_defn]
|
120
164
|
end
|
@@ -122,26 +166,31 @@ module GraphQL
|
|
122
166
|
# @param type_defn [GraphQL::ObjectType, GraphQL::InterfaceType]
|
123
167
|
# @return [Array<GraphQL::Field>] Fields on `type_defn`
|
124
168
|
def fields(type_defn)
|
125
|
-
@visible_fields ||= read_through { |t| @schema.get_fields(t
|
169
|
+
@visible_fields ||= read_through { |t| @schema.get_fields(t, @context).values }
|
126
170
|
@visible_fields[type_defn]
|
127
171
|
end
|
128
172
|
|
129
173
|
# @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
|
130
174
|
# @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
|
131
175
|
def arguments(argument_owner)
|
132
|
-
@visible_arguments ||= read_through { |o| o.arguments.each_value.select { |a| visible_argument?(a) } }
|
176
|
+
@visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a) } }
|
133
177
|
@visible_arguments[argument_owner]
|
134
178
|
end
|
135
179
|
|
136
180
|
# @return [Array<GraphQL::EnumType::EnumValue>] Visible members of `enum_defn`
|
137
181
|
def enum_values(enum_defn)
|
138
|
-
@
|
139
|
-
@
|
182
|
+
@visible_enum_arrays ||= read_through { |e| e.enum_values(@context) }
|
183
|
+
@visible_enum_arrays[enum_defn]
|
184
|
+
end
|
185
|
+
|
186
|
+
def visible_enum_value?(enum_value, _ctx = nil)
|
187
|
+
@visible_enum_values ||= read_through { |ev| visible?(ev) }
|
188
|
+
@visible_enum_values[enum_value]
|
140
189
|
end
|
141
190
|
|
142
191
|
# @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
|
143
192
|
def interfaces(obj_type)
|
144
|
-
@visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i|
|
193
|
+
@visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
|
145
194
|
@visible_interfaces[obj_type]
|
146
195
|
end
|
147
196
|
|
@@ -158,25 +207,52 @@ module GraphQL
|
|
158
207
|
end
|
159
208
|
end
|
160
209
|
|
161
|
-
|
162
|
-
|
163
|
-
def union_memberships(obj_type)
|
164
|
-
@unions ||= read_through { |obj_type| @schema.union_memberships(obj_type).select { |u| visible?(u) } }
|
165
|
-
@unions[obj_type]
|
166
|
-
end
|
167
|
-
|
168
|
-
def visible_argument?(arg_defn)
|
169
|
-
visible?(arg_defn) && visible_type?(arg_defn.type.unwrap)
|
170
|
-
end
|
171
|
-
|
172
|
-
def visible_field?(owner_type, field_defn)
|
210
|
+
# @param owner [Class, Module] If provided, confirm that field has the given owner.
|
211
|
+
def visible_field?(field_defn, _ctx = nil, owner = field_defn.owner)
|
173
212
|
# This field is visible in its own right
|
174
213
|
visible?(field_defn) &&
|
175
214
|
# This field's return type is visible
|
176
|
-
|
215
|
+
visible_and_reachable_type?(field_defn.type.unwrap) &&
|
177
216
|
# This field is either defined on this object type,
|
178
217
|
# or the interface it's inherited from is also visible
|
179
|
-
((field_defn.respond_to?(:owner) && field_defn.owner ==
|
218
|
+
((field_defn.respond_to?(:owner) && field_defn.owner == owner) || field_on_visible_interface?(field_defn, owner))
|
219
|
+
end
|
220
|
+
|
221
|
+
def visible_argument?(arg_defn, _ctx = nil)
|
222
|
+
visible?(arg_defn) && visible_and_reachable_type?(arg_defn.type.unwrap)
|
223
|
+
end
|
224
|
+
|
225
|
+
def visible_type?(type_defn, _ctx = nil)
|
226
|
+
@type_visibility ||= read_through { |type_defn| visible?(type_defn) }
|
227
|
+
@type_visibility[type_defn]
|
228
|
+
end
|
229
|
+
|
230
|
+
def visible_type_membership?(type_membership, _ctx = nil)
|
231
|
+
visible?(type_membership)
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def visible_and_reachable_type?(type_defn)
|
237
|
+
@visible_and_reachable_type ||= read_through do |type_defn|
|
238
|
+
next false unless visible_type?(type_defn)
|
239
|
+
next true if root_type?(type_defn) || type_defn.introspection?
|
240
|
+
|
241
|
+
if type_defn.kind.union?
|
242
|
+
visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
|
243
|
+
elsif type_defn.kind.interface?
|
244
|
+
visible_possible_types?(type_defn)
|
245
|
+
else
|
246
|
+
referenced?(type_defn) || visible_abstract_type?(type_defn)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
@visible_and_reachable_type[type_defn]
|
251
|
+
end
|
252
|
+
|
253
|
+
def union_memberships(obj_type)
|
254
|
+
@unions ||= read_through { |obj_type| @schema.union_memberships(obj_type).select { |u| visible?(u) } }
|
255
|
+
@unions[obj_type]
|
180
256
|
end
|
181
257
|
|
182
258
|
# We need this to tell whether a field was inherited by an interface
|
@@ -195,10 +271,10 @@ module GraphQL
|
|
195
271
|
any_interface_has_visible_field = false
|
196
272
|
ints = unfiltered_interfaces(type_defn)
|
197
273
|
ints.each do |interface_type|
|
198
|
-
if (iface_field_defn = interface_type.get_field(field_defn.graphql_name))
|
274
|
+
if (iface_field_defn = interface_type.get_field(field_defn.graphql_name, @context))
|
199
275
|
any_interface_has_field = true
|
200
276
|
|
201
|
-
if interfaces(type_defn).include?(interface_type) && visible_field?(
|
277
|
+
if interfaces(type_defn).include?(interface_type) && visible_field?(iface_field_defn, nil, interface_type)
|
202
278
|
any_interface_has_visible_field = true
|
203
279
|
end
|
204
280
|
end
|
@@ -215,23 +291,6 @@ module GraphQL
|
|
215
291
|
end
|
216
292
|
end
|
217
293
|
|
218
|
-
def visible_type?(type_defn)
|
219
|
-
@type_visibility ||= read_through do |type_defn|
|
220
|
-
next false unless visible?(type_defn)
|
221
|
-
next true if root_type?(type_defn) || type_defn.introspection?
|
222
|
-
|
223
|
-
if type_defn.kind.union?
|
224
|
-
visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
|
225
|
-
elsif type_defn.kind.interface?
|
226
|
-
visible_possible_types?(type_defn)
|
227
|
-
else
|
228
|
-
referenced?(type_defn) || visible_abstract_type?(type_defn)
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
@type_visibility[type_defn]
|
233
|
-
end
|
234
|
-
|
235
294
|
def root_type?(type_defn)
|
236
295
|
@query == type_defn ||
|
237
296
|
@mutation == type_defn ||
|
@@ -259,7 +318,7 @@ module GraphQL
|
|
259
318
|
end
|
260
319
|
|
261
320
|
def visible_possible_types?(type_defn)
|
262
|
-
possible_types(type_defn).any? { |t|
|
321
|
+
possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
|
263
322
|
end
|
264
323
|
|
265
324
|
def visible?(member)
|
@@ -274,6 +333,7 @@ module GraphQL
|
|
274
333
|
return @reachable_type_set if defined?(@reachable_type_set)
|
275
334
|
|
276
335
|
@reachable_type_set = Set.new
|
336
|
+
rt_hash = {}
|
277
337
|
|
278
338
|
unvisited_types = []
|
279
339
|
['query', 'mutation', 'subscription'].each do |op_name|
|
@@ -283,16 +343,16 @@ module GraphQL
|
|
283
343
|
unvisited_types.concat(@schema.introspection_system.types.values)
|
284
344
|
|
285
345
|
directives.each do |dir_class|
|
286
|
-
dir_class.
|
346
|
+
arguments(dir_class).each do |arg_defn|
|
287
347
|
arg_t = arg_defn.type.unwrap
|
288
|
-
if get_type(arg_t.graphql_name)
|
348
|
+
if get_type(arg_t.graphql_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
|
289
349
|
unvisited_types << arg_t
|
290
350
|
end
|
291
351
|
end
|
292
352
|
end
|
293
353
|
|
294
354
|
@schema.orphan_types.each do |orphan_type|
|
295
|
-
if get_type(orphan_type.graphql_name)
|
355
|
+
if get_type(orphan_type.graphql_name) == orphan_type # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
|
296
356
|
unvisited_types << orphan_type
|
297
357
|
end
|
298
358
|
end
|
@@ -300,6 +360,10 @@ module GraphQL
|
|
300
360
|
until unvisited_types.empty?
|
301
361
|
type = unvisited_types.pop
|
302
362
|
if @reachable_type_set.add?(type)
|
363
|
+
type_by_name = rt_hash[type.graphql_name] ||= type
|
364
|
+
if type_by_name != type
|
365
|
+
raise DuplicateNamesError, "Found two visible type definitions for `#{type.graphql_name}`: #{type.inspect}, #{type_by_name.inspect}"
|
366
|
+
end
|
303
367
|
if type.kind.input_object?
|
304
368
|
# recurse into visible arguments
|
305
369
|
arguments(type).each do |argument|
|
data/lib/graphql/schema.rb
CHANGED
@@ -92,6 +92,8 @@ module GraphQL
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
class DuplicateNamesError < GraphQL::Error; end
|
96
|
+
|
95
97
|
class UnresolvedLateBoundTypeError < GraphQL::Error
|
96
98
|
attr_reader :type
|
97
99
|
def initialize(type:)
|
@@ -996,16 +998,58 @@ module GraphQL
|
|
996
998
|
# Build a map of `{ name => type }` and return it
|
997
999
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
998
1000
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
999
|
-
def types
|
1000
|
-
non_introspection_types.merge(introspection_system.types)
|
1001
|
+
def types(context = GraphQL::Query::NullContext)
|
1002
|
+
all_types = non_introspection_types.merge(introspection_system.types)
|
1003
|
+
visible_types = {}
|
1004
|
+
all_types.each do |k, v|
|
1005
|
+
visible_types[k] =if v.is_a?(Array)
|
1006
|
+
visible_t = nil
|
1007
|
+
v.each do |t|
|
1008
|
+
if t.visible?(context)
|
1009
|
+
if visible_t.nil?
|
1010
|
+
visible_t = t
|
1011
|
+
else
|
1012
|
+
raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}"
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
end
|
1016
|
+
visible_t
|
1017
|
+
else
|
1018
|
+
v
|
1019
|
+
end
|
1020
|
+
end
|
1021
|
+
visible_types
|
1001
1022
|
end
|
1002
1023
|
|
1003
1024
|
# @param type_name [String]
|
1004
1025
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
1005
|
-
def get_type(type_name)
|
1006
|
-
own_types[type_name]
|
1007
|
-
|
1008
|
-
|
1026
|
+
def get_type(type_name, context = GraphQL::Query::NullContext)
|
1027
|
+
local_entry = own_types[type_name]
|
1028
|
+
type_defn = case local_entry
|
1029
|
+
when nil
|
1030
|
+
nil
|
1031
|
+
when Array
|
1032
|
+
visible_t = nil
|
1033
|
+
warden = Warden.from_context(context)
|
1034
|
+
local_entry.each do |t|
|
1035
|
+
if warden.visible_type?(t, context)
|
1036
|
+
if visible_t.nil?
|
1037
|
+
visible_t = t
|
1038
|
+
else
|
1039
|
+
raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}"
|
1040
|
+
end
|
1041
|
+
end
|
1042
|
+
end
|
1043
|
+
visible_t
|
1044
|
+
when Module
|
1045
|
+
local_entry
|
1046
|
+
else
|
1047
|
+
raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
type_defn ||
|
1051
|
+
introspection_system.types[type_name] || # todo context-specific introspection?
|
1052
|
+
(superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
|
1009
1053
|
end
|
1010
1054
|
|
1011
1055
|
# @api private
|
@@ -1182,19 +1226,19 @@ module GraphQL
|
|
1182
1226
|
GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
|
1183
1227
|
end
|
1184
1228
|
|
1185
|
-
def get_field(type_or_name, field_name)
|
1229
|
+
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
|
1186
1230
|
parent_type = case type_or_name
|
1187
1231
|
when LateBoundType
|
1188
|
-
get_type(type_or_name.name)
|
1232
|
+
get_type(type_or_name.name, context)
|
1189
1233
|
when String
|
1190
|
-
get_type(type_or_name)
|
1234
|
+
get_type(type_or_name, context)
|
1191
1235
|
when Module
|
1192
1236
|
type_or_name
|
1193
1237
|
else
|
1194
1238
|
raise ArgumentError, "unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
|
1195
1239
|
end
|
1196
1240
|
|
1197
|
-
if parent_type.kind.fields? && (field = parent_type.get_field(field_name))
|
1241
|
+
if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
|
1198
1242
|
field
|
1199
1243
|
elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
|
1200
1244
|
entry_point_field
|
@@ -1205,8 +1249,8 @@ module GraphQL
|
|
1205
1249
|
end
|
1206
1250
|
end
|
1207
1251
|
|
1208
|
-
def get_fields(type)
|
1209
|
-
type.fields
|
1252
|
+
def get_fields(type, context = GraphQL::Query::NullContext)
|
1253
|
+
type.fields(context)
|
1210
1254
|
end
|
1211
1255
|
|
1212
1256
|
def introspection(new_introspection_namespace = nil)
|
@@ -1405,7 +1449,6 @@ module GraphQL
|
|
1405
1449
|
if new_orphan_types.any?
|
1406
1450
|
new_orphan_types = new_orphan_types.flatten
|
1407
1451
|
add_type_and_traverse(new_orphan_types, root: false)
|
1408
|
-
@orphan_types = new_orphan_types
|
1409
1452
|
own_orphan_types.concat(new_orphan_types.flatten)
|
1410
1453
|
end
|
1411
1454
|
|
@@ -1715,7 +1758,7 @@ module GraphQL
|
|
1715
1758
|
if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
|
1716
1759
|
GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
|
1717
1760
|
else
|
1718
|
-
subscription.
|
1761
|
+
subscription.all_field_definitions.each do |field|
|
1719
1762
|
field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
|
1720
1763
|
end
|
1721
1764
|
end
|
@@ -1737,7 +1780,36 @@ module GraphQL
|
|
1737
1780
|
end
|
1738
1781
|
new_types = Array(t)
|
1739
1782
|
addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
|
1740
|
-
|
1783
|
+
addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
|
1784
|
+
if (prev_entry = own_types[name])
|
1785
|
+
prev_entries = case prev_entry
|
1786
|
+
when Array
|
1787
|
+
prev_entry
|
1788
|
+
when Module
|
1789
|
+
own_types[name] = [prev_entry]
|
1790
|
+
else
|
1791
|
+
raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
|
1792
|
+
end
|
1793
|
+
|
1794
|
+
case types_entry
|
1795
|
+
when Array
|
1796
|
+
prev_entries.concat(types_entry)
|
1797
|
+
prev_entries.uniq! # in case any are being re-visited
|
1798
|
+
when Module
|
1799
|
+
if !prev_entries.include?(types_entry)
|
1800
|
+
prev_entries << types_entry
|
1801
|
+
end
|
1802
|
+
else
|
1803
|
+
raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
|
1804
|
+
end
|
1805
|
+
else
|
1806
|
+
if types_entry.is_a?(Array)
|
1807
|
+
types_entry.uniq!
|
1808
|
+
end
|
1809
|
+
own_types[name] = types_entry
|
1810
|
+
end
|
1811
|
+
end
|
1812
|
+
|
1741
1813
|
own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
|
1742
1814
|
own_union_memberships.merge!(addition.union_memberships)
|
1743
1815
|
|
@@ -94,7 +94,7 @@ module GraphQL
|
|
94
94
|
|
95
95
|
def on_field(node, parent)
|
96
96
|
parent_type = @object_types.last
|
97
|
-
field_definition = @schema.get_field(parent_type, node.name)
|
97
|
+
field_definition = @schema.get_field(parent_type, node.name, @context.query.context)
|
98
98
|
@field_definitions.push(field_definition)
|
99
99
|
if !field_definition.nil?
|
100
100
|
next_object_type = field_definition.type.unwrap
|
@@ -120,14 +120,14 @@ module GraphQL
|
|
120
120
|
argument_defn = if (arg = @argument_definitions.last)
|
121
121
|
arg_type = arg.type.unwrap
|
122
122
|
if arg_type.kind.input_object?
|
123
|
-
arg_type
|
123
|
+
@context.warden.get_argument(arg_type, node.name)
|
124
124
|
else
|
125
125
|
nil
|
126
126
|
end
|
127
127
|
elsif (directive_defn = @directive_definitions.last)
|
128
|
-
directive_defn
|
128
|
+
@context.warden.get_argument(directive_defn, node.name)
|
129
129
|
elsif (field_defn = @field_definitions.last)
|
130
|
-
field_defn
|
130
|
+
@context.warden.get_argument(field_defn, node.name)
|
131
131
|
else
|
132
132
|
nil
|
133
133
|
end
|
@@ -187,7 +187,7 @@ module GraphQL
|
|
187
187
|
|
188
188
|
def on_fragment_with_type(node)
|
189
189
|
object_type = if node.type
|
190
|
-
@
|
190
|
+
@context.warden.get_type(node.type.name)
|
191
191
|
else
|
192
192
|
@object_types.last
|
193
193
|
end
|
@@ -95,7 +95,7 @@ module GraphQL
|
|
95
95
|
def required_input_fields_are_present(type, ast_node)
|
96
96
|
# TODO - would be nice to use these to create an error message so the caller knows
|
97
97
|
# that required fields are missing
|
98
|
-
required_field_names =
|
98
|
+
required_field_names = @warden.arguments(type)
|
99
99
|
.select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
|
100
100
|
.map(&:name)
|
101
101
|
|
@@ -15,7 +15,7 @@ module GraphQL
|
|
15
15
|
if @context.schema.error_bubbling || context.errors.none? { |err| err.path.take(@path.size) == @path }
|
16
16
|
parent_defn = parent_definition(parent)
|
17
17
|
|
18
|
-
if parent_defn && (arg_defn = parent_defn
|
18
|
+
if parent_defn && (arg_defn = context.warden.get_argument(parent_defn, node.name))
|
19
19
|
validation_result = context.validate_literal(node.value, arg_defn.type)
|
20
20
|
if !validation_result.valid?
|
21
21
|
kind_of_node = node_type(parent)
|
@@ -50,15 +50,15 @@ module GraphQL
|
|
50
50
|
@arg_conflicts = nil
|
51
51
|
|
52
52
|
yield
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
|
54
|
+
field_conflicts.each_value { |error| add_error(error) }
|
55
|
+
arg_conflicts.each_value { |error| add_error(error) }
|
56
56
|
end
|
57
57
|
|
58
58
|
def conflicts_within_selection_set(node, parent_type)
|
59
59
|
return if parent_type.nil?
|
60
60
|
|
61
|
-
fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents:
|
61
|
+
fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: [])
|
62
62
|
|
63
63
|
# (A) Find find all conflicts "within" the fields of this selection set.
|
64
64
|
find_conflicts_within(fields)
|
@@ -198,14 +198,10 @@ module GraphQL
|
|
198
198
|
response_keys.each do |key, fields|
|
199
199
|
next if fields.size < 2
|
200
200
|
# find conflicts within nodes
|
201
|
-
i
|
202
|
-
|
203
|
-
j = i + 1
|
204
|
-
while j < fields.size
|
201
|
+
for i in 0..fields.size - 1
|
202
|
+
for j in i + 1..fields.size - 1
|
205
203
|
find_conflict(key, fields[i], fields[j])
|
206
|
-
j += 1
|
207
204
|
end
|
208
|
-
i += 1
|
209
205
|
end
|
210
206
|
end
|
211
207
|
end
|
@@ -247,9 +243,7 @@ module GraphQL
|
|
247
243
|
end
|
248
244
|
|
249
245
|
def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive:)
|
250
|
-
return if field1.definition.nil? ||
|
251
|
-
field2.definition.nil? ||
|
252
|
-
(field1.node.selections.empty? && field2.node.selections.empty?)
|
246
|
+
return if field1.definition.nil? || field2.definition.nil?
|
253
247
|
|
254
248
|
return_type1 = field1.definition.type.unwrap
|
255
249
|
return_type2 = field2.definition.type.unwrap
|
@@ -329,7 +323,6 @@ module GraphQL
|
|
329
323
|
if node.selections.empty?
|
330
324
|
NO_SELECTIONS
|
331
325
|
else
|
332
|
-
parents ||= []
|
333
326
|
fields, fragment_spreads = find_fields_and_fragments(node.selections, owner_type: owner_type, parents: parents, fields: [], fragment_spreads: [])
|
334
327
|
response_keys = fields.group_by { |f| f.node.alias || f.node.name }
|
335
328
|
[response_keys, fragment_spreads]
|
@@ -340,7 +333,7 @@ module GraphQL
|
|
340
333
|
selections.each do |node|
|
341
334
|
case node
|
342
335
|
when GraphQL::Language::Nodes::Field
|
343
|
-
definition = context.
|
336
|
+
definition = context.query.get_field(owner_type, node.name)
|
344
337
|
fields << Field.new(node, definition, owner_type, parents)
|
345
338
|
when GraphQL::Language::Nodes::InlineFragment
|
346
339
|
fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
|
@@ -16,10 +16,8 @@ module GraphQL
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def assert_required_args(ast_node, defn)
|
19
|
-
args = defn.arguments
|
20
|
-
return if args.empty?
|
21
19
|
present_argument_names = ast_node.arguments.map(&:name)
|
22
|
-
required_argument_names =
|
20
|
+
required_argument_names = context.warden.arguments(defn)
|
23
21
|
.select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
|
24
22
|
.map(&:name)
|
25
23
|
|