graphql 1.9.16 → 1.9.21
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.
- checksums.yaml +4 -4
- data/lib/graphql.rb +8 -0
- data/lib/graphql/argument.rb +2 -2
- data/lib/graphql/define/assign_object_field.rb +2 -2
- data/lib/graphql/define/defined_object_proxy.rb +3 -0
- data/lib/graphql/define/instance_definable.rb +14 -3
- data/lib/graphql/execution/errors.rb +15 -14
- data/lib/graphql/execution/execute.rb +1 -1
- data/lib/graphql/execution/interpreter/runtime.rb +39 -17
- data/lib/graphql/execution/multiplex.rb +3 -3
- data/lib/graphql/introspection/entry_points.rb +2 -1
- data/lib/graphql/introspection/schema_type.rb +2 -1
- data/lib/graphql/language/document_from_schema_definition.rb +9 -3
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/query.rb +7 -1
- data/lib/graphql/query/context.rb +31 -9
- data/lib/graphql/query/null_context.rb +4 -0
- data/lib/graphql/query/variables.rb +3 -1
- data/lib/graphql/relay/node.rb +2 -2
- data/lib/graphql/schema.rb +58 -7
- data/lib/graphql/schema/argument.rb +4 -0
- data/lib/graphql/schema/base_64_bp.rb +3 -2
- data/lib/graphql/schema/build_from_definition.rb +26 -9
- data/lib/graphql/schema/directive.rb +7 -1
- data/lib/graphql/schema/introspection_system.rb +4 -1
- data/lib/graphql/schema/loader.rb +9 -3
- data/lib/graphql/schema/member/has_fields.rb +1 -4
- data/lib/graphql/schema/mutation.rb +1 -1
- data/lib/graphql/schema/object.rb +6 -4
- data/lib/graphql/schema/possible_types.rb +3 -3
- data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
- data/lib/graphql/schema/resolver.rb +1 -1
- data/lib/graphql/schema/subscription.rb +5 -5
- data/lib/graphql/schema/type_membership.rb +34 -0
- data/lib/graphql/schema/union.rb +26 -6
- data/lib/graphql/schema/warden.rb +77 -3
- data/lib/graphql/subscriptions.rb +2 -2
- data/lib/graphql/subscriptions/subscription_root.rb +10 -2
- data/lib/graphql/union_type.rb +58 -23
- data/lib/graphql/version.rb +1 -1
- metadata +6 -5
@@ -35,10 +35,12 @@ module GraphQL
|
|
35
35
|
# @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy]
|
36
36
|
# @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false`
|
37
37
|
def authorized_new(object, context)
|
38
|
-
auth_val =
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
auth_val = context.query.with_error_handling do
|
39
|
+
begin
|
40
|
+
authorized?(object, context)
|
41
|
+
rescue GraphQL::UnauthorizedError => err
|
42
|
+
context.schema.unauthorized_object(err)
|
43
|
+
end
|
42
44
|
end
|
43
45
|
|
44
46
|
context.schema.after_lazy(auth_val) do |is_authorized|
|
@@ -20,12 +20,12 @@ module GraphQL
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def possible_types(type_defn)
|
23
|
+
def possible_types(type_defn, ctx)
|
24
24
|
case type_defn
|
25
25
|
when Module
|
26
|
-
possible_types(type_defn.graphql_definition)
|
26
|
+
possible_types(type_defn.graphql_definition, ctx)
|
27
27
|
when GraphQL::UnionType
|
28
|
-
type_defn.possible_types
|
28
|
+
type_defn.possible_types(ctx)
|
29
29
|
when GraphQL::InterfaceType
|
30
30
|
@interface_implementers[type_defn]
|
31
31
|
when GraphQL::BaseType
|
@@ -76,7 +76,7 @@ module GraphQL
|
|
76
76
|
context.schema.after_lazy(load_arguments_val) do |loaded_args|
|
77
77
|
# Then call `authorized?`, which may raise or may return a lazy object
|
78
78
|
authorized_val = if loaded_args.any?
|
79
|
-
authorized?(loaded_args)
|
79
|
+
authorized?(**loaded_args)
|
80
80
|
else
|
81
81
|
authorized?
|
82
82
|
end
|
@@ -39,12 +39,12 @@ module GraphQL
|
|
39
39
|
def resolve(**args)
|
40
40
|
# Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever
|
41
41
|
# have an unexpected `@mode`
|
42
|
-
public_send("resolve_#{@mode}", args)
|
42
|
+
public_send("resolve_#{@mode}", **args)
|
43
43
|
end
|
44
44
|
|
45
45
|
# Wrap the user-defined `#subscribe` hook
|
46
|
-
def resolve_subscribe(args)
|
47
|
-
ret_val = args.any? ? subscribe(args) : subscribe
|
46
|
+
def resolve_subscribe(**args)
|
47
|
+
ret_val = args.any? ? subscribe(**args) : subscribe
|
48
48
|
if ret_val == :no_response
|
49
49
|
context.skip
|
50
50
|
else
|
@@ -62,8 +62,8 @@ module GraphQL
|
|
62
62
|
end
|
63
63
|
|
64
64
|
# Wrap the user-provided `#update` hook
|
65
|
-
def resolve_update(args)
|
66
|
-
ret_val = args.any? ? update(args) : update
|
65
|
+
def resolve_update(**args)
|
66
|
+
ret_val = args.any? ? update(**args) : update
|
67
67
|
if ret_val == :no_update
|
68
68
|
raise NoUpdateError
|
69
69
|
else
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
# This class joins an object type to an abstract type (interface or union) of which
|
6
|
+
# it is a member.
|
7
|
+
#
|
8
|
+
# TODO: Not yet implemented for interfaces.
|
9
|
+
class TypeMembership
|
10
|
+
# @return [Class<GraphQL::Schema::Object>]
|
11
|
+
attr_reader :object_type
|
12
|
+
|
13
|
+
# @return [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
|
14
|
+
attr_reader :abstract_type
|
15
|
+
|
16
|
+
# Called when an object is hooked up to an abstract type, such as {Schema::Union.possible_types}
|
17
|
+
# or {Schema::Object.implements} (for interfaces).
|
18
|
+
#
|
19
|
+
# @param abstract_type [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
|
20
|
+
# @param object_type [Class<GraphQL::Schema::Object>]
|
21
|
+
# @param options [Hash] Any options passed to `.possible_types` or `.implements`
|
22
|
+
def initialize(abstract_type, object_type, **options)
|
23
|
+
@abstract_type = abstract_type
|
24
|
+
@object_type = object_type
|
25
|
+
@options = options
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Boolean] if false, {#object_type} will be treated as _not_ a member of {#abstract_type}
|
29
|
+
def visible?(_ctx)
|
30
|
+
true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/graphql/schema/union.rb
CHANGED
@@ -5,13 +5,19 @@ module GraphQL
|
|
5
5
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
6
6
|
|
7
7
|
class << self
|
8
|
-
def possible_types(*types)
|
8
|
+
def possible_types(*types, context: GraphQL::Query::NullContext, **options)
|
9
9
|
if types.any?
|
10
|
-
|
10
|
+
types.each do |t|
|
11
|
+
type_memberships << type_membership_class.new(self, t, **options)
|
12
|
+
end
|
11
13
|
else
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
visible_types = []
|
15
|
+
type_memberships.each do |type_membership|
|
16
|
+
if type_membership.visible?(context)
|
17
|
+
visible_types << type_membership.object_type
|
18
|
+
end
|
19
|
+
end
|
20
|
+
visible_types
|
15
21
|
end
|
16
22
|
end
|
17
23
|
|
@@ -19,7 +25,7 @@ module GraphQL
|
|
19
25
|
type_defn = GraphQL::UnionType.new
|
20
26
|
type_defn.name = graphql_name
|
21
27
|
type_defn.description = description
|
22
|
-
type_defn.
|
28
|
+
type_defn.type_memberships = type_memberships
|
23
29
|
if respond_to?(:resolve_type)
|
24
30
|
type_defn.resolve_type = method(:resolve_type)
|
25
31
|
end
|
@@ -27,9 +33,23 @@ module GraphQL
|
|
27
33
|
type_defn
|
28
34
|
end
|
29
35
|
|
36
|
+
def type_membership_class(membership_class = nil)
|
37
|
+
if membership_class
|
38
|
+
@type_membership_class = membership_class
|
39
|
+
else
|
40
|
+
@type_membership_class || find_inherited_value(:type_membership_class, GraphQL::Schema::TypeMembership)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
30
44
|
def kind
|
31
45
|
GraphQL::TypeKinds::UNION
|
32
46
|
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def type_memberships
|
51
|
+
@type_memberships ||= []
|
52
|
+
end
|
33
53
|
end
|
34
54
|
end
|
35
55
|
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
2
5
|
module GraphQL
|
3
6
|
class Schema
|
4
7
|
# Restrict access to a {GraphQL::Schema} with a user-defined filter.
|
@@ -40,7 +43,8 @@ module GraphQL
|
|
40
43
|
# @param deep_check [Boolean]
|
41
44
|
def initialize(filter, context:, schema:)
|
42
45
|
@schema = schema
|
43
|
-
@
|
46
|
+
@context = context
|
47
|
+
@visibility_cache = read_through { |m| filter.call(m, @context) }
|
44
48
|
end
|
45
49
|
|
46
50
|
# @return [Array<GraphQL::BaseType>] Visible types in the schema
|
@@ -62,6 +66,17 @@ module GraphQL
|
|
62
66
|
@visible_types[type_name]
|
63
67
|
end
|
64
68
|
|
69
|
+
# @return [Array<GraphQL::BaseType>] Visible and reachable types in the schema
|
70
|
+
def reachable_types
|
71
|
+
@reachable_types ||= reachable_type_set.to_a
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return Boolean True if the type is visible and reachable in the schema
|
75
|
+
def reachable_type?(type_name)
|
76
|
+
type = get_type(type_name)
|
77
|
+
type && reachable_type_set.include?(type)
|
78
|
+
end
|
79
|
+
|
65
80
|
# @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`, if it exists
|
66
81
|
def get_field(parent_type, field_name)
|
67
82
|
|
@@ -81,7 +96,7 @@ module GraphQL
|
|
81
96
|
|
82
97
|
# @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
|
83
98
|
def possible_types(type_defn)
|
84
|
-
@visible_possible_types ||= read_through { |type_defn| @schema.possible_types(type_defn).select { |t| visible_type?(t) } }
|
99
|
+
@visible_possible_types ||= read_through { |type_defn| @schema.possible_types(type_defn, @context).select { |t| visible_type?(t) } }
|
85
100
|
@visible_possible_types[type_defn]
|
86
101
|
end
|
87
102
|
|
@@ -170,7 +185,7 @@ module GraphQL
|
|
170
185
|
end
|
171
186
|
|
172
187
|
def visible_possible_types?(type_defn)
|
173
|
-
@schema.possible_types(type_defn).any? { |t| visible_type?(t) }
|
188
|
+
@schema.possible_types(type_defn, @context).any? { |t| visible_type?(t) }
|
174
189
|
end
|
175
190
|
|
176
191
|
def visible?(member)
|
@@ -180,6 +195,65 @@ module GraphQL
|
|
180
195
|
def read_through
|
181
196
|
Hash.new { |h, k| h[k] = yield(k) }
|
182
197
|
end
|
198
|
+
|
199
|
+
def reachable_type_set
|
200
|
+
return @reachable_type_set if defined?(@reachable_type_set)
|
201
|
+
|
202
|
+
@reachable_type_set = Set.new
|
203
|
+
|
204
|
+
unvisited_types = []
|
205
|
+
['query', 'mutation', 'subscription'].each do |op_name|
|
206
|
+
root_type = root_type_for_operation(op_name)
|
207
|
+
unvisited_types << root_type if root_type
|
208
|
+
end
|
209
|
+
unvisited_types.concat(@schema.introspection_system.object_types)
|
210
|
+
@schema.orphan_types.each do |orphan_type|
|
211
|
+
unvisited_types << orphan_type.graphql_definition if get_type(orphan_type.graphql_name)
|
212
|
+
end
|
213
|
+
|
214
|
+
until unvisited_types.empty?
|
215
|
+
type = unvisited_types.pop
|
216
|
+
if @reachable_type_set.add?(type)
|
217
|
+
if type.kind.input_object?
|
218
|
+
# recurse into visible arguments
|
219
|
+
arguments(type).each do |argument|
|
220
|
+
argument_type = argument.type.unwrap
|
221
|
+
unvisited_types << argument_type
|
222
|
+
end
|
223
|
+
elsif type.kind.union?
|
224
|
+
# recurse into visible possible types
|
225
|
+
possible_types(type).each do |possible_type|
|
226
|
+
unvisited_types << possible_type
|
227
|
+
end
|
228
|
+
elsif type.kind.fields?
|
229
|
+
if type.kind.interface?
|
230
|
+
# recurse into visible possible types
|
231
|
+
possible_types(type).each do |possible_type|
|
232
|
+
unvisited_types << possible_type
|
233
|
+
end
|
234
|
+
elsif type.kind.object?
|
235
|
+
# recurse into visible implemented interfaces
|
236
|
+
interfaces(type).each do |interface|
|
237
|
+
unvisited_types << interface
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# recurse into visible fields
|
242
|
+
fields(type).each do |field|
|
243
|
+
field_type = field.type.unwrap
|
244
|
+
unvisited_types << field_type
|
245
|
+
# recurse into visible arguments
|
246
|
+
arguments(field).each do |argument|
|
247
|
+
argument_type = argument.type.unwrap
|
248
|
+
unvisited_types << argument_type
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
@reachable_type_set
|
256
|
+
end
|
183
257
|
end
|
184
258
|
end
|
185
259
|
end
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
20
20
|
def self.use(defn, options = {})
|
21
21
|
schema = defn.target
|
22
22
|
options[:schema] = schema
|
23
|
-
schema.subscriptions = self.new(options)
|
23
|
+
schema.subscriptions = self.new(**options)
|
24
24
|
instrumentation = Subscriptions::Instrumentation.new(schema: schema)
|
25
25
|
defn.instrument(:field, instrumentation)
|
26
26
|
defn.instrument(:query, instrumentation)
|
@@ -90,7 +90,7 @@ module GraphQL
|
|
90
90
|
operation_name = query_data.fetch(:operation_name)
|
91
91
|
# Re-evaluate the saved query
|
92
92
|
result = @schema.execute(
|
93
|
-
{
|
93
|
+
**{
|
94
94
|
query: query_string,
|
95
95
|
context: context,
|
96
96
|
subscription_topic: event.topic,
|
@@ -40,7 +40,7 @@ module GraphQL
|
|
40
40
|
# for the backend to register:
|
41
41
|
events << Subscriptions::Event.new(
|
42
42
|
name: field.name,
|
43
|
-
arguments: arguments,
|
43
|
+
arguments: arguments_without_field_extras(arguments: arguments),
|
44
44
|
context: context,
|
45
45
|
field: field,
|
46
46
|
)
|
@@ -48,7 +48,7 @@ module GraphQL
|
|
48
48
|
value
|
49
49
|
elsif context.query.subscription_topic == Subscriptions::Event.serialize(
|
50
50
|
field.name,
|
51
|
-
arguments,
|
51
|
+
arguments_without_field_extras(arguments: arguments),
|
52
52
|
field,
|
53
53
|
scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
|
54
54
|
)
|
@@ -60,6 +60,14 @@ module GraphQL
|
|
60
60
|
context.skip
|
61
61
|
end
|
62
62
|
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def arguments_without_field_extras(arguments:)
|
67
|
+
arguments.dup.tap do |event_args|
|
68
|
+
field.extras.each { |k| event_args.delete(k) }
|
69
|
+
end
|
70
|
+
end
|
63
71
|
end
|
64
72
|
end
|
65
73
|
end
|
data/lib/graphql/union_type.rb
CHANGED
@@ -24,22 +24,34 @@ module GraphQL
|
|
24
24
|
# }
|
25
25
|
#
|
26
26
|
class UnionType < GraphQL::BaseType
|
27
|
-
|
28
|
-
|
27
|
+
# Rubocop was unhappy about the syntax when this was a proc literal
|
28
|
+
class AcceptPossibleTypesDefinition
|
29
|
+
def self.call(target, possible_types, options = {})
|
30
|
+
target.add_possible_types(possible_types, **options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
accepts_definitions :resolve_type, :type_membership_class,
|
35
|
+
possible_types: AcceptPossibleTypesDefinition
|
36
|
+
ensure_defined :possible_types, :resolve_type, :resolve_type_proc, :type_membership_class
|
29
37
|
|
30
38
|
attr_accessor :resolve_type_proc
|
39
|
+
attr_reader :type_memberships
|
40
|
+
attr_accessor :type_membership_class
|
31
41
|
|
32
42
|
def initialize
|
33
43
|
super
|
34
|
-
@
|
35
|
-
@
|
44
|
+
@type_membership_class = GraphQL::Schema::TypeMembership
|
45
|
+
@type_memberships = []
|
46
|
+
@cached_possible_types = nil
|
36
47
|
@resolve_type_proc = nil
|
37
48
|
end
|
38
49
|
|
39
50
|
def initialize_copy(other)
|
40
51
|
super
|
41
|
-
@
|
42
|
-
@
|
52
|
+
@type_membership_class = other.type_membership_class
|
53
|
+
@type_memberships = other.type_memberships.dup
|
54
|
+
@cached_possible_types = nil
|
43
55
|
end
|
44
56
|
|
45
57
|
def kind
|
@@ -47,33 +59,44 @@ module GraphQL
|
|
47
59
|
end
|
48
60
|
|
49
61
|
# @return [Boolean] True if `child_type_defn` is a member of this {UnionType}
|
50
|
-
def include?(child_type_defn)
|
51
|
-
possible_types.include?(child_type_defn)
|
52
|
-
end
|
53
|
-
|
54
|
-
def possible_types=(new_possible_types)
|
55
|
-
@clean_possible_types = nil
|
56
|
-
@dirty_possible_types = new_possible_types
|
62
|
+
def include?(child_type_defn, ctx = GraphQL::Query::NullContext)
|
63
|
+
possible_types(ctx).include?(child_type_defn)
|
57
64
|
end
|
58
65
|
|
59
66
|
# @return [Array<GraphQL::ObjectType>] Types which may be found in this union
|
60
|
-
def possible_types
|
61
|
-
|
62
|
-
if
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
+
def possible_types(ctx = GraphQL::Query::NullContext)
|
68
|
+
if ctx == GraphQL::Query::NullContext
|
69
|
+
# Only cache the default case; if we cached for every `ctx`, it would be a memory leak
|
70
|
+
# (The warden should cache calls to this method, so it's called only once per query,
|
71
|
+
# unless user code calls it directly.)
|
72
|
+
@cached_possible_types ||= possible_types_for_context(ctx)
|
73
|
+
else
|
74
|
+
possible_types_for_context(ctx)
|
67
75
|
end
|
68
76
|
end
|
69
77
|
|
78
|
+
def possible_types=(types)
|
79
|
+
# This is a re-assignment, so clear the previous values
|
80
|
+
@type_memberships = []
|
81
|
+
@cached_possible_types = nil
|
82
|
+
add_possible_types(types, **{})
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_possible_types(types, **options)
|
86
|
+
@type_memberships ||= []
|
87
|
+
Array(types).each { |t|
|
88
|
+
@type_memberships << self.type_membership_class.new(self, t, **options)
|
89
|
+
}
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
|
70
93
|
# Get a possible type of this {UnionType} by type name
|
71
94
|
# @param type_name [String]
|
72
95
|
# @param ctx [GraphQL::Query::Context] The context for the current query
|
73
96
|
# @return [GraphQL::ObjectType, nil] The type named `type_name` if it exists and is a member of this {UnionType}, (else `nil`)
|
74
97
|
def get_possible_type(type_name, ctx)
|
75
98
|
type = ctx.query.get_type(type_name)
|
76
|
-
type if type && ctx.query.schema.possible_types(self).include?(type)
|
99
|
+
type if type && ctx.query.schema.possible_types(self, ctx).include?(type)
|
77
100
|
end
|
78
101
|
|
79
102
|
# Check if a type is a possible type of this {UnionType}
|
@@ -93,8 +116,20 @@ module GraphQL
|
|
93
116
|
@resolve_type_proc = new_resolve_type_proc
|
94
117
|
end
|
95
118
|
|
96
|
-
|
119
|
+
def type_memberships=(type_memberships)
|
120
|
+
@type_memberships = type_memberships
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
97
124
|
|
98
|
-
|
125
|
+
def possible_types_for_context(ctx)
|
126
|
+
visible_types = []
|
127
|
+
@type_memberships.each do |type_membership|
|
128
|
+
if type_membership.visible?(ctx)
|
129
|
+
visible_types << BaseType.resolve_related_type(type_membership.object_type)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
visible_types
|
133
|
+
end
|
99
134
|
end
|
100
135
|
end
|