graphql 1.8.6 → 1.8.7
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 +5 -5
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/templates/base_enum.erb +3 -1
- data/lib/generators/graphql/templates/base_input_object.erb +3 -1
- data/lib/generators/graphql/templates/base_interface.erb +4 -2
- data/lib/generators/graphql/templates/base_object.erb +3 -1
- data/lib/generators/graphql/templates/base_union.erb +3 -1
- data/lib/generators/graphql/templates/enum.erb +5 -3
- data/lib/generators/graphql/templates/interface.erb +6 -4
- data/lib/generators/graphql/templates/loader.erb +14 -12
- data/lib/generators/graphql/templates/mutation.erb +9 -6
- data/lib/generators/graphql/templates/mutation_type.erb +8 -6
- data/lib/generators/graphql/templates/object.erb +6 -4
- data/lib/generators/graphql/templates/query_type.erb +13 -11
- data/lib/generators/graphql/templates/union.erb +5 -3
- data/lib/graphql/argument.rb +19 -2
- data/lib/graphql/base_type.rb +1 -1
- data/lib/graphql/define/instance_definable.rb +2 -0
- data/lib/graphql/enum_type.rb +1 -0
- data/lib/graphql/execution/instrumentation.rb +28 -20
- data/lib/graphql/field.rb +3 -3
- data/lib/graphql/input_object_type.rb +2 -1
- data/lib/graphql/query.rb +1 -9
- data/lib/graphql/query/variables.rb +1 -18
- data/lib/graphql/relay.rb +0 -1
- data/lib/graphql/relay/mongo_relation_connection.rb +10 -0
- data/lib/graphql/relay/mutation.rb +2 -1
- data/lib/graphql/schema.rb +5 -4
- data/lib/graphql/schema/base_64_bp.rb +25 -0
- data/lib/graphql/schema/base_64_encoder.rb +5 -1
- data/lib/graphql/schema/field.rb +52 -22
- data/lib/graphql/schema/interface.rb +2 -0
- data/lib/graphql/schema/list.rb +3 -15
- data/lib/graphql/schema/member.rb +8 -4
- data/lib/graphql/schema/member/base_dsl_methods.rb +12 -6
- data/lib/graphql/schema/member/has_arguments.rb +7 -0
- data/lib/graphql/schema/member/relay_shortcuts.rb +47 -0
- data/lib/graphql/schema/member/scoped.rb +21 -0
- data/lib/graphql/schema/non_null.rb +8 -17
- data/lib/graphql/schema/resolver.rb +99 -76
- data/lib/graphql/schema/unique_within_type.rb +4 -1
- data/lib/graphql/schema/wrapper.rb +29 -0
- data/lib/graphql/static_validation/validation_context.rb +1 -3
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/type_kinds.rb +32 -8
- data/lib/graphql/types/relay/base_connection.rb +17 -3
- data/lib/graphql/types/relay/base_edge.rb +12 -0
- data/lib/graphql/version.rb +1 -1
- data/spec/generators/graphql/enum_generator_spec.rb +8 -6
- data/spec/generators/graphql/install_generator_spec.rb +24 -20
- data/spec/generators/graphql/interface_generator_spec.rb +6 -4
- data/spec/generators/graphql/loader_generator_spec.rb +30 -26
- data/spec/generators/graphql/mutation_generator_spec.rb +18 -13
- data/spec/generators/graphql/object_generator_spec.rb +12 -6
- data/spec/generators/graphql/union_generator_spec.rb +10 -4
- data/spec/graphql/authorization_spec.rb +55 -14
- data/spec/graphql/input_object_type_spec.rb +11 -0
- data/spec/graphql/language/generation_spec.rb +8 -6
- data/spec/graphql/language/nodes_spec.rb +8 -6
- data/spec/graphql/relay/connection_instrumentation_spec.rb +1 -1
- data/spec/graphql/relay/mongo_relation_connection_spec.rb +54 -0
- data/spec/graphql/schema/field_spec.rb +9 -0
- data/spec/graphql/schema/list_spec.rb +46 -0
- data/spec/graphql/schema/member/scoped_spec.rb +161 -0
- data/spec/graphql/schema/non_null_spec.rb +46 -0
- data/spec/graphql/schema/object_spec.rb +15 -0
- data/spec/graphql/schema/resolver_spec.rb +165 -25
- data/spec/graphql/subscriptions/serialize_spec.rb +0 -22
- data/spec/graphql/subscriptions_spec.rb +19 -31
- data/spec/support/global_id.rb +23 -0
- data/spec/support/lazy_helpers.rb +1 -1
- data/spec/support/star_trek/data.rb +19 -2
- data/spec/support/star_trek/schema.rb +37 -22
- data/spec/support/star_wars/schema.rb +24 -17
- metadata +220 -208
@@ -2,20 +2,19 @@
|
|
2
2
|
|
3
3
|
module GraphQL
|
4
4
|
class Schema
|
5
|
+
# Represents a non null type in the schema.
|
5
6
|
# Wraps a {Schema::Member} when it is required.
|
6
7
|
# @see {Schema::Member::TypeSystemHelpers#to_non_null_type}
|
7
|
-
class NonNull
|
8
|
-
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
9
|
-
include GraphQL::Schema::Member::TypeSystemHelpers
|
10
|
-
attr_reader :of_type
|
11
|
-
def initialize(of_type)
|
12
|
-
@of_type = of_type
|
13
|
-
end
|
14
|
-
|
8
|
+
class NonNull < GraphQL::Schema::Wrapper
|
15
9
|
def to_graphql
|
16
10
|
@of_type.graphql_definition.to_non_null_type
|
17
11
|
end
|
18
12
|
|
13
|
+
# @return [GraphQL::TypeKinds::NON_NULL]
|
14
|
+
def kind
|
15
|
+
GraphQL::TypeKinds::NON_NULL
|
16
|
+
end
|
17
|
+
|
19
18
|
# @return [true]
|
20
19
|
def non_null?
|
21
20
|
true
|
@@ -25,15 +24,7 @@ module GraphQL
|
|
25
24
|
def list?
|
26
25
|
@of_type.list?
|
27
26
|
end
|
28
|
-
|
29
|
-
def kind
|
30
|
-
GraphQL::TypeKinds::NON_NULL
|
31
|
-
end
|
32
|
-
|
33
|
-
def unwrap
|
34
|
-
@of_type.unwrap
|
35
|
-
end
|
36
|
-
|
27
|
+
|
37
28
|
def to_type_signature
|
38
29
|
"#{@of_type.to_type_signature}!"
|
39
30
|
end
|
@@ -48,25 +48,50 @@ module GraphQL
|
|
48
48
|
# the user-defined `#resolve` method.
|
49
49
|
# @api private
|
50
50
|
def resolve_with_support(**args)
|
51
|
-
# First call the
|
52
|
-
|
53
|
-
|
51
|
+
# First call the ready? hook which may raise
|
52
|
+
ready_val = if args.any?
|
53
|
+
ready?(**args)
|
54
54
|
else
|
55
|
-
|
55
|
+
ready?
|
56
56
|
end
|
57
|
-
context.schema.after_lazy(
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
57
|
+
context.schema.after_lazy(ready_val) do |is_ready, ready_early_return|
|
58
|
+
if ready_early_return
|
59
|
+
if is_ready != false
|
60
|
+
raise "Unexpected result from #ready? (expected `true`, `false` or `[false, {...}]`): [#{authorized_result.inspect}, #{ready_early_return.inspect}]"
|
61
|
+
else
|
62
|
+
ready_early_return
|
63
|
+
end
|
64
|
+
elsif is_ready
|
65
|
+
# Then call each prepare hook, which may return a different value
|
66
|
+
# for that argument, or may return a lazy object
|
67
|
+
load_arguments_val = load_arguments(args)
|
68
|
+
context.schema.after_lazy(load_arguments_val) do |loaded_args|
|
69
|
+
# Then call `authorized?`, which may raise or may return a lazy object
|
70
|
+
authorized_val = if loaded_args.any?
|
71
|
+
authorized?(loaded_args)
|
68
72
|
else
|
69
|
-
|
73
|
+
authorized?
|
74
|
+
end
|
75
|
+
authorized?(loaded_args)
|
76
|
+
context.schema.after_lazy(authorized_val) do |(authorized_result, early_return)|
|
77
|
+
# If the `authorized?` returned two values, `false, early_return`,
|
78
|
+
# then use the early return value instead of continuing
|
79
|
+
if early_return
|
80
|
+
if authorized_result == false
|
81
|
+
early_return
|
82
|
+
else
|
83
|
+
raise "Unexpected result from #authorized? (expected `true`, `false` or `[false, {...}]`): [#{authorized_result.inspect}, #{early_return.inspect}]"
|
84
|
+
end
|
85
|
+
elsif authorized_result
|
86
|
+
# Finally, all the hooks have passed, so resolve it
|
87
|
+
if loaded_args.any?
|
88
|
+
public_send(self.class.resolve_method, **loaded_args)
|
89
|
+
else
|
90
|
+
public_send(self.class.resolve_method)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
nil
|
94
|
+
end
|
70
95
|
end
|
71
96
|
end
|
72
97
|
end
|
@@ -88,7 +113,20 @@ module GraphQL
|
|
88
113
|
# @param args [Hash] The input arguments, if there are any
|
89
114
|
# @raise [GraphQL::ExecutionError] To add an error to the response
|
90
115
|
# @raise [GraphQL::UnauthorizedError] To signal an authorization failure
|
91
|
-
|
116
|
+
# @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.)
|
117
|
+
def ready?(**args)
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
# Called after arguments are loaded, but before resolving.
|
122
|
+
#
|
123
|
+
# Override it to check everything before calling the mutation.
|
124
|
+
# @param args [Hash] The input arguments
|
125
|
+
# @raise [GraphQL::ExecutionError] To add an error to the response
|
126
|
+
# @raise [GraphQL::UnauthorizedError] To signal an authorization failure
|
127
|
+
# @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.)
|
128
|
+
def authorized?(**args)
|
129
|
+
true
|
92
130
|
end
|
93
131
|
|
94
132
|
private
|
@@ -124,31 +162,6 @@ module GraphQL
|
|
124
162
|
public_send("load_#{name}", value)
|
125
163
|
end
|
126
164
|
|
127
|
-
# TODO dedup with load_arguments
|
128
|
-
def validate_arguments(args)
|
129
|
-
validate_lazies = []
|
130
|
-
args.each do |key, value|
|
131
|
-
arg_defn = @arguments_by_keyword[key]
|
132
|
-
if arg_defn
|
133
|
-
validate_return = validate_argument(key, value)
|
134
|
-
if context.schema.lazy?(validate_return)
|
135
|
-
validate_lazies << context.schema.after_lazy(validate_return).then { "no-op" }
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# Avoid returning a lazy if none are needed
|
141
|
-
if validate_lazies.any?
|
142
|
-
GraphQL::Execution::Lazy.all(validate_lazies).then { args }
|
143
|
-
else
|
144
|
-
args
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def validate_argument(name, value)
|
149
|
-
public_send("validate_#{name}", value)
|
150
|
-
end
|
151
|
-
|
152
165
|
class LoadApplicationObjectFailedError < GraphQL::ExecutionError
|
153
166
|
# @return [GraphQL::Schema::Argument] the argument definition for the argument that was looked up
|
154
167
|
attr_reader :argument
|
@@ -164,43 +177,58 @@ module GraphQL
|
|
164
177
|
end
|
165
178
|
end
|
166
179
|
|
180
|
+
# Look up the corresponding object for a provided ID.
|
181
|
+
# By default, it uses Relay-style {Schema.object_from_id},
|
182
|
+
# override this to find objects another way.
|
183
|
+
#
|
184
|
+
# @param type [Class, Module] A GraphQL type definition
|
185
|
+
# @param id [String] A client-provided to look up
|
186
|
+
# @param context [GraphQL::Query::Context] the current context
|
187
|
+
def object_from_id(type, id, context)
|
188
|
+
context.schema.object_from_id(id, context)
|
189
|
+
end
|
190
|
+
|
167
191
|
def load_application_object(arg_kwarg, id)
|
168
192
|
argument = @arguments_by_keyword[arg_kwarg]
|
169
|
-
# See if any object can be found for this ID
|
170
|
-
application_object = context.schema.object_from_id(id, context)
|
171
|
-
if application_object.nil?
|
172
|
-
raise LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
|
173
|
-
end
|
174
|
-
# Double-check that the located object is actually of this type
|
175
|
-
# (Don't want to allow arbitrary access to objects this way)
|
176
193
|
lookup_as_type = @arguments_loads_as_type[arg_kwarg]
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
context.schema.
|
187
|
-
|
188
|
-
|
194
|
+
# See if any object can be found for this ID
|
195
|
+
loaded_application_object = object_from_id(lookup_as_type, id, context)
|
196
|
+
context.schema.after_lazy(loaded_application_object) do |application_object|
|
197
|
+
begin
|
198
|
+
if application_object.nil?
|
199
|
+
raise LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
|
200
|
+
end
|
201
|
+
# Double-check that the located object is actually of this type
|
202
|
+
# (Don't want to allow arbitrary access to objects this way)
|
203
|
+
application_object_type = context.schema.resolve_type(lookup_as_type, application_object, context)
|
204
|
+
possible_object_types = context.schema.possible_types(lookup_as_type)
|
205
|
+
if !possible_object_types.include?(application_object_type)
|
206
|
+
raise LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
|
207
|
+
else
|
208
|
+
# This object was loaded successfully
|
209
|
+
# and resolved to the right type,
|
210
|
+
# now apply the `.authorized?` class method if there is one
|
211
|
+
if (class_based_type = application_object_type.metadata[:type_class])
|
212
|
+
context.schema.after_lazy(class_based_type.authorized?(application_object, context)) do |authed|
|
213
|
+
if authed
|
214
|
+
application_object
|
215
|
+
else
|
216
|
+
raise GraphQL::UnauthorizedError.new(
|
217
|
+
object: application_object,
|
218
|
+
type: class_based_type,
|
219
|
+
context: context,
|
220
|
+
)
|
221
|
+
end
|
222
|
+
end
|
189
223
|
else
|
190
|
-
|
191
|
-
object: application_object,
|
192
|
-
type: class_based_type,
|
193
|
-
context: context,
|
194
|
-
)
|
224
|
+
application_object
|
195
225
|
end
|
196
226
|
end
|
197
|
-
|
198
|
-
|
227
|
+
rescue LoadApplicationObjectFailedError => err
|
228
|
+
# pass it to a handler
|
229
|
+
load_application_object_failed(err)
|
199
230
|
end
|
200
231
|
end
|
201
|
-
rescue LoadApplicationObjectFailedError => err
|
202
|
-
# pass it to a handler
|
203
|
-
load_application_object_failed(err)
|
204
232
|
end
|
205
233
|
|
206
234
|
def load_application_object_failed(err)
|
@@ -315,11 +343,6 @@ module GraphQL
|
|
315
343
|
RUBY
|
316
344
|
end
|
317
345
|
|
318
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
319
|
-
def validate_#{arg_defn.keyword}(value)
|
320
|
-
# No-op
|
321
|
-
end
|
322
|
-
RUBY
|
323
346
|
arg_defn
|
324
347
|
end
|
325
348
|
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'graphql/schema/base_64_bp'
|
3
|
+
|
2
4
|
module GraphQL
|
3
5
|
class Schema
|
4
6
|
module UniqueWithinType
|
@@ -25,7 +27,8 @@ module GraphQL
|
|
25
27
|
# @param node_id [String] A unique ID generated by {.encode}
|
26
28
|
# @return [Array<(String, String)>] The type name & value passed to {.encode}
|
27
29
|
def decode(node_id, separator: self.default_id_separator)
|
28
|
-
|
30
|
+
# urlsafe_decode64 is for forward compatibility
|
31
|
+
Base64Bp.urlsafe_decode64(node_id).split(separator, 2)
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Wrapper
|
6
|
+
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
7
|
+
include GraphQL::Schema::Member::TypeSystemHelpers
|
8
|
+
|
9
|
+
# @return [Class, Module] The inner type of this wrapping type, the type of which one or more objects may be present.
|
10
|
+
attr_reader :of_type
|
11
|
+
|
12
|
+
def initialize(of_type)
|
13
|
+
@of_type = of_type
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_graphql
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
def unwrap
|
21
|
+
@of_type.unwrap
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
self.class == other.class && of_type == other.of_type
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -14,9 +14,7 @@ module GraphQL
|
|
14
14
|
class ValidationContext
|
15
15
|
extend Forwardable
|
16
16
|
|
17
|
-
attr_reader :query, :
|
18
|
-
:document, :errors, :visitor,
|
19
|
-
:warden, :dependencies, :each_irep_node_handlers
|
17
|
+
attr_reader :query, :errors, :visitor, :dependencies, :each_irep_node_handlers
|
20
18
|
|
21
19
|
def_delegators :@query, :schema, :document, :fragments, :operations, :warden
|
22
20
|
|
data/lib/graphql/type_kinds.rb
CHANGED
@@ -29,6 +29,38 @@ module GraphQL
|
|
29
29
|
def to_s; @name; end
|
30
30
|
# Is this TypeKind composed of many values?
|
31
31
|
def composite?; @composite; end
|
32
|
+
|
33
|
+
def scalar?
|
34
|
+
self == TypeKinds::SCALAR
|
35
|
+
end
|
36
|
+
|
37
|
+
def object?
|
38
|
+
self == TypeKinds::OBJECT
|
39
|
+
end
|
40
|
+
|
41
|
+
def interface?
|
42
|
+
self == TypeKinds::INTERFACE
|
43
|
+
end
|
44
|
+
|
45
|
+
def union?
|
46
|
+
self == TypeKinds::UNION
|
47
|
+
end
|
48
|
+
|
49
|
+
def enum?
|
50
|
+
self == TypeKinds::ENUM
|
51
|
+
end
|
52
|
+
|
53
|
+
def input_object?
|
54
|
+
self == TypeKinds::INPUT_OBJECT
|
55
|
+
end
|
56
|
+
|
57
|
+
def list?
|
58
|
+
self == TypeKinds::LIST
|
59
|
+
end
|
60
|
+
|
61
|
+
def non_null?
|
62
|
+
self == TypeKinds::NON_NULL
|
63
|
+
end
|
32
64
|
end
|
33
65
|
|
34
66
|
TYPE_KINDS = [
|
@@ -41,13 +73,5 @@ module GraphQL
|
|
41
73
|
LIST = TypeKind.new("LIST", wraps: true, description: 'Indicates this type is a list. `ofType` is a valid field.'),
|
42
74
|
NON_NULL = TypeKind.new("NON_NULL", wraps: true, description: 'Indicates this type is a non-null. `ofType` is a valid field.'),
|
43
75
|
]
|
44
|
-
|
45
|
-
class TypeKind
|
46
|
-
TYPE_KINDS.map(&:name).each do |kind_name|
|
47
|
-
define_method("#{kind_name.downcase}?") do
|
48
|
-
self.name == kind_name
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
76
|
end
|
53
77
|
end
|
@@ -35,9 +35,6 @@ module GraphQL
|
|
35
35
|
# @return [Class]
|
36
36
|
attr_reader :node_type
|
37
37
|
|
38
|
-
# @return [Class]
|
39
|
-
attr_reader :edge_type
|
40
|
-
|
41
38
|
# Configure this connection to return `edges` and `nodes` based on `edge_type_class`.
|
42
39
|
#
|
43
40
|
# This method will use the inputs to create:
|
@@ -66,11 +63,28 @@ module GraphQL
|
|
66
63
|
description("The connection type for #{node_type_name}.")
|
67
64
|
end
|
68
65
|
|
66
|
+
# Filter this list according to the way its node type would scope them
|
67
|
+
def scope_items(items, context)
|
68
|
+
node_type.scope_items(items, context)
|
69
|
+
end
|
70
|
+
|
69
71
|
# Add the shortcut `nodes` field to this connection and its subclasses
|
70
72
|
def nodes_field
|
71
73
|
define_nodes_field
|
72
74
|
end
|
73
75
|
|
76
|
+
def authorized?(obj, ctx)
|
77
|
+
true # Let nodes be filtered out
|
78
|
+
end
|
79
|
+
|
80
|
+
def accessible?(ctx)
|
81
|
+
node_type.accessible?(ctx)
|
82
|
+
end
|
83
|
+
|
84
|
+
def visible?(ctx)
|
85
|
+
node_type.visible?(ctx)
|
86
|
+
end
|
87
|
+
|
74
88
|
private
|
75
89
|
|
76
90
|
def define_nodes_field
|
data/lib/graphql/version.rb
CHANGED