graphql 1.8.4 → 1.8.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/templates/graphql_controller.erb +10 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +398 -0
- data/lib/graphql/execution/execute.rb +24 -18
- data/lib/graphql/execution/lazy.rb +14 -1
- data/lib/graphql/introspection/type_type.rb +1 -1
- data/lib/graphql/language/lexer.rb +68 -42
- data/lib/graphql/language/lexer.rl +2 -0
- data/lib/graphql/language/nodes.rb +98 -0
- data/lib/graphql/language/parser.rb +1050 -770
- data/lib/graphql/language/parser.y +50 -2
- data/lib/graphql/object_type.rb +4 -0
- data/lib/graphql/query.rb +3 -1
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/schema.rb +10 -2
- data/lib/graphql/schema/input_object.rb +1 -1
- data/lib/graphql/schema/interface.rb +6 -0
- data/lib/graphql/schema/member/has_arguments.rb +1 -0
- data/lib/graphql/schema/member/instrumentation.rb +21 -16
- data/lib/graphql/schema/mutation.rb +1 -1
- data/lib/graphql/schema/object.rb +7 -2
- data/lib/graphql/schema/possible_types.rb +1 -1
- data/lib/graphql/schema/resolver.rb +210 -1
- data/lib/graphql/schema/validation.rb +1 -1
- data/lib/graphql/static_validation/message.rb +5 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +9 -0
- data/lib/graphql/type_kinds.rb +9 -6
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/types/iso_8601_date_time.rb +1 -5
- data/lib/graphql/unauthorized_error.rb +7 -2
- data/lib/graphql/version.rb +1 -1
- data/spec/generators/graphql/install_generator_spec.rb +12 -2
- data/spec/graphql/authorization_spec.rb +80 -1
- data/spec/graphql/directive_spec.rb +42 -0
- data/spec/graphql/execution_error_spec.rb +1 -1
- data/spec/graphql/query_spec.rb +14 -0
- data/spec/graphql/schema/interface_spec.rb +14 -0
- data/spec/graphql/schema/object_spec.rb +41 -1
- data/spec/graphql/schema/relay_classic_mutation_spec.rb +47 -0
- data/spec/graphql/schema/resolver_spec.rb +182 -8
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +6 -5
- data/spec/graphql/static_validation/rules/no_definitions_are_present_spec.rb +34 -0
- data/spec/graphql/static_validation/validator_spec.rb +15 -0
- data/spec/support/jazz.rb +36 -1
- metadata +2 -2
@@ -9,9 +9,13 @@ rule
|
|
9
9
|
| definitions_list definition { val[0] << val[1] }
|
10
10
|
|
11
11
|
definition:
|
12
|
+
executable_definition
|
13
|
+
| type_system_definition
|
14
|
+
| type_system_extension
|
15
|
+
|
16
|
+
executable_definition:
|
12
17
|
operation_definition
|
13
18
|
| fragment_definition
|
14
|
-
| type_system_definition
|
15
19
|
|
16
20
|
operation_definition:
|
17
21
|
operation_type operation_name_opt variable_definitions_opt directives_list_opt selection_set {
|
@@ -293,6 +297,47 @@ rule
|
|
293
297
|
| enum_type_definition
|
294
298
|
| input_object_type_definition
|
295
299
|
|
300
|
+
type_system_extension:
|
301
|
+
schema_extension
|
302
|
+
| type_extension
|
303
|
+
|
304
|
+
schema_extension:
|
305
|
+
EXTEND SCHEMA directives_list_opt LCURLY operation_type_definition_list RCURLY { return make_node(:SchemaExtension, position_source: val[0], directives: val[2], **val[4]) }
|
306
|
+
| EXTEND SCHEMA directives_list { return make_node(:SchemaExtension, position_source: val[0], directives: val[2]) }
|
307
|
+
|
308
|
+
type_extension:
|
309
|
+
scalar_type_extension
|
310
|
+
| object_type_extension
|
311
|
+
| interface_type_extension
|
312
|
+
| union_type_extension
|
313
|
+
| enum_type_extension
|
314
|
+
| input_object_type_extension
|
315
|
+
|
316
|
+
scalar_type_extension: EXTEND SCALAR name directives_list { return make_node(:ScalarTypeExtension, name: val[2], directives: val[3], position_source: val[0]) }
|
317
|
+
|
318
|
+
object_type_extension:
|
319
|
+
/* TODO - This first one shouldn't be necessary but parser is getting confused */
|
320
|
+
EXTEND TYPE name implements LCURLY field_definition_list RCURLY { return make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: [], fields: val[5], position_source: val[0]) }
|
321
|
+
| EXTEND TYPE name implements_opt directives_list_opt LCURLY field_definition_list RCURLY { return make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: val[6], position_source: val[0]) }
|
322
|
+
| EXTEND TYPE name implements_opt directives_list { return make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: [], position_source: val[0]) }
|
323
|
+
| EXTEND TYPE name implements { return make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: [], fields: [], position_source: val[0]) }
|
324
|
+
|
325
|
+
interface_type_extension:
|
326
|
+
EXTEND INTERFACE name directives_list_opt LCURLY field_definition_list RCURLY { return make_node(:InterfaceTypeExtension, name: val[2], directives: val[3], fields: val[5], position_source: val[0]) }
|
327
|
+
| EXTEND INTERFACE name directives_list { return make_node(:InterfaceTypeExtension, name: val[2], directives: val[3], fields: [], position_source: val[0]) }
|
328
|
+
|
329
|
+
union_type_extension:
|
330
|
+
EXTEND UNION name directives_list_opt EQUALS union_members { return make_node(:UnionTypeExtension, name: val[2], directives: val[3], types: val[5], position_source: val[0]) }
|
331
|
+
| EXTEND UNION name directives_list { return make_node(:UnionTypeExtension, name: val[2], directives: val[3], types: [], position_source: val[0]) }
|
332
|
+
|
333
|
+
enum_type_extension:
|
334
|
+
EXTEND ENUM name directives_list_opt LCURLY enum_value_definitions RCURLY { return make_node(:EnumTypeExtension, name: val[2], directives: val[3], values: val[5], position_source: val[0]) }
|
335
|
+
| EXTEND ENUM name directives_list { return make_node(:EnumTypeExtension, name: val[2], directives: val[3], values: [], position_source: val[0]) }
|
336
|
+
|
337
|
+
input_object_type_extension:
|
338
|
+
EXTEND INPUT name directives_list_opt LCURLY input_value_definition_list RCURLY { return make_node(:InputObjectTypeExtension, name: val[2], directives: val[3], fields: val[5], position_source: val[0]) }
|
339
|
+
| EXTEND INPUT name directives_list { return make_node(:InputObjectTypeExtension, name: val[2], directives: val[3], fields: [], position_source: val[0]) }
|
340
|
+
|
296
341
|
scalar_type_definition: SCALAR name directives_list_opt { return make_node(:ScalarTypeDefinition, name: val[1], directives: val[2], description: get_description(val[0]), position_source: val[0]) }
|
297
342
|
|
298
343
|
object_type_definition:
|
@@ -302,7 +347,10 @@ rule
|
|
302
347
|
|
303
348
|
implements_opt:
|
304
349
|
/* none */ { return [] }
|
305
|
-
|
|
350
|
+
| implements
|
351
|
+
|
352
|
+
implements:
|
353
|
+
IMPLEMENTS AMP interfaces_list { return val[2] }
|
306
354
|
| IMPLEMENTS interfaces_list { return val[1] }
|
307
355
|
| IMPLEMENTS legacy_interfaces_list { return val[1] }
|
308
356
|
|
data/lib/graphql/object_type.rb
CHANGED
data/lib/graphql/query.rb
CHANGED
@@ -76,7 +76,9 @@ module GraphQL
|
|
76
76
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
77
77
|
# @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
|
78
78
|
# @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
|
79
|
-
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables:
|
79
|
+
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: nil, max_complexity: nil, except: nil, only: nil)
|
80
|
+
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
81
|
+
variables ||= {}
|
80
82
|
@schema = schema
|
81
83
|
@filter = schema.default_filter.merge(except: except, only: only)
|
82
84
|
@context = schema.context_class.new(query: self, object: root_value, values: context)
|
@@ -43,7 +43,7 @@ module GraphQL
|
|
43
43
|
elsif value_was_provided
|
44
44
|
# Add the variable if a value was provided
|
45
45
|
memo[variable_name] = variable_type.coerce_input(provided_value, ctx)
|
46
|
-
elsif default_value
|
46
|
+
elsif default_value != nil
|
47
47
|
# Add the variable if it wasn't provided but it has a default value (including `null`)
|
48
48
|
memo[variable_name] = GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
|
49
49
|
end
|
data/lib/graphql/schema.rb
CHANGED
@@ -462,6 +462,10 @@ module GraphQL
|
|
462
462
|
object = object.object
|
463
463
|
end
|
464
464
|
|
465
|
+
if type.respond_to?(:graphql_definition)
|
466
|
+
type = type.graphql_definition
|
467
|
+
end
|
468
|
+
|
465
469
|
# Prefer a type-local function; fall back to the schema-level function
|
466
470
|
type_proc = type && type.resolve_type_proc
|
467
471
|
type_result = if type_proc
|
@@ -867,6 +871,10 @@ module GraphQL
|
|
867
871
|
#
|
868
872
|
# By default, this hook just replaces the unauthorized object with `nil`.
|
869
873
|
#
|
874
|
+
# Whatever value is returned from this method will be used instead of the
|
875
|
+
# unauthorized object (accessible ass `unauthorized_error.object`). If an
|
876
|
+
# error is raised, then `nil` will be used.
|
877
|
+
#
|
870
878
|
# If you want to add an error to the `"errors"` key, raise a {GraphQL::ExecutionError}
|
871
879
|
# in this hook.
|
872
880
|
#
|
@@ -997,11 +1005,11 @@ module GraphQL
|
|
997
1005
|
result = value.public_send(lazy_method)
|
998
1006
|
# The returned result might also be lazy, so check it, too
|
999
1007
|
after_lazy(result) do |final_result|
|
1000
|
-
yield(final_result)
|
1008
|
+
yield(final_result) if block_given?
|
1001
1009
|
end
|
1002
1010
|
end
|
1003
1011
|
else
|
1004
|
-
yield(value)
|
1012
|
+
yield(value) if block_given?
|
1005
1013
|
end
|
1006
1014
|
end
|
1007
1015
|
|
@@ -27,7 +27,7 @@ module GraphQL
|
|
27
27
|
attr_reader :arguments
|
28
28
|
|
29
29
|
# Ruby-like hash behaviors, read-only
|
30
|
-
def_delegators :@ruby_style_hash, :keys, :values, :each, :any?
|
30
|
+
def_delegators :@ruby_style_hash, :keys, :values, :each, :any?, :empty?
|
31
31
|
|
32
32
|
def to_h
|
33
33
|
@ruby_style_hash.inject({}) do |h, (key, value)|
|
@@ -61,6 +61,12 @@ module GraphQL
|
|
61
61
|
child_class.extend(interface_defn::DefinitionMethods)
|
62
62
|
end
|
63
63
|
elsif child_class < GraphQL::Schema::Object
|
64
|
+
# Add all definition methods of this interface and the interfaces it
|
65
|
+
# includes onto the child class.
|
66
|
+
(interfaces + [self] - [GraphQL::Schema::Interface]).each do |interface_defn|
|
67
|
+
child_class.extend(interface_defn::DefinitionMethods)
|
68
|
+
end
|
69
|
+
|
64
70
|
# This is being included into an object type, make sure it's using `implements(...)`
|
65
71
|
backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
|
66
72
|
if !backtrace_line
|
@@ -17,6 +17,7 @@ module GraphQL
|
|
17
17
|
kwargs[:owner] = self
|
18
18
|
arg_defn = self.argument_class.new(*args, **kwargs, &block)
|
19
19
|
own_arguments[arg_defn.name] = arg_defn
|
20
|
+
arg_defn
|
20
21
|
end
|
21
22
|
|
22
23
|
# @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
|
@@ -78,8 +78,6 @@ module GraphQL
|
|
78
78
|
ctx.wrapped_object = true
|
79
79
|
proxy_to_depth(result, @list_depth, ctx)
|
80
80
|
end
|
81
|
-
rescue GraphQL::UnauthorizedError => err
|
82
|
-
ctx.schema.unauthorized_object(err)
|
83
81
|
end
|
84
82
|
|
85
83
|
private
|
@@ -88,21 +86,28 @@ module GraphQL
|
|
88
86
|
if depth > 0
|
89
87
|
inner_obj.map { |i| proxy_to_depth(i, depth - 1, ctx) }
|
90
88
|
else
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
89
|
+
ctx.schema.after_lazy(inner_obj) do |inner_obj|
|
90
|
+
if inner_obj.nil?
|
91
|
+
# For lists with nil, we need another nil check here
|
92
|
+
nil
|
93
|
+
else
|
94
|
+
concrete_type = case @inner_return_type
|
95
|
+
when GraphQL::UnionType, GraphQL::InterfaceType
|
96
|
+
ctx.query.resolve_type(@inner_return_type, inner_obj)
|
97
|
+
when GraphQL::ObjectType
|
98
|
+
@inner_return_type
|
99
|
+
else
|
100
|
+
raise "unexpected proxying type #{@inner_return_type} for #{inner_obj} at #{ctx.owner_type}.#{ctx.field.name}"
|
101
|
+
end
|
99
102
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
103
|
+
if concrete_type && (object_class = concrete_type.metadata[:type_class])
|
104
|
+
# use the query-level context here, since it won't be field-specific anyways
|
105
|
+
query_ctx = ctx.query.context
|
106
|
+
object_class.authorized_new(inner_obj, query_ctx)
|
107
|
+
else
|
108
|
+
inner_obj
|
109
|
+
end
|
110
|
+
end
|
106
111
|
end
|
107
112
|
end
|
108
113
|
end
|
@@ -17,7 +17,7 @@ module GraphQL
|
|
17
17
|
# argument :post_id, ID, required: true
|
18
18
|
#
|
19
19
|
# field :comment, Types::Comment, null: true
|
20
|
-
# field :
|
20
|
+
# field :errors, [String], null: false
|
21
21
|
#
|
22
22
|
# def resolve(body:, post_id:)
|
23
23
|
# post = Post.find(post_id)
|
@@ -39,7 +39,13 @@ module GraphQL
|
|
39
39
|
if is_authorized
|
40
40
|
self.new(object, context)
|
41
41
|
else
|
42
|
-
|
42
|
+
# It failed the authorization check, so go to the schema's authorized object hook
|
43
|
+
err = GraphQL::UnauthorizedError.new(object: object, type: self, context: context)
|
44
|
+
# If a new value was returned, wrap that instead of the original value
|
45
|
+
new_obj = context.schema.unauthorized_object(err)
|
46
|
+
if new_obj
|
47
|
+
self.new(new_obj, context)
|
48
|
+
end
|
43
49
|
end
|
44
50
|
end
|
45
51
|
end
|
@@ -94,7 +100,6 @@ module GraphQL
|
|
94
100
|
obj_type.interfaces = interfaces
|
95
101
|
obj_type.introspection = introspection
|
96
102
|
obj_type.mutation = mutation
|
97
|
-
|
98
103
|
fields.each do |field_name, field_inst|
|
99
104
|
field_defn = field_inst.to_graphql
|
100
105
|
obj_type.fields[field_defn.name] = field_defn
|
@@ -29,6 +29,12 @@ module GraphQL
|
|
29
29
|
def initialize(object:, context:)
|
30
30
|
@object = object
|
31
31
|
@context = context
|
32
|
+
# Since this hash is constantly rebuilt, cache it for this call
|
33
|
+
@arguments_by_keyword = {}
|
34
|
+
self.class.arguments.each do |name, arg|
|
35
|
+
@arguments_by_keyword[arg.keyword] = arg
|
36
|
+
end
|
37
|
+
@arguments_loads_as_type = self.class.arguments_loads_as_type
|
32
38
|
end
|
33
39
|
|
34
40
|
# @return [Object] The application object this field is being resolved on
|
@@ -37,12 +43,170 @@ module GraphQL
|
|
37
43
|
# @return [GraphQL::Query::Context]
|
38
44
|
attr_reader :context
|
39
45
|
|
46
|
+
# This method is _actually_ called by the runtime,
|
47
|
+
# it does some preparation and then eventually calls
|
48
|
+
# the user-defined `#resolve` method.
|
49
|
+
# @api private
|
50
|
+
def resolve_with_support(**args)
|
51
|
+
# First call the before_prepare hook which may raise
|
52
|
+
before_prepare_val = if args.any?
|
53
|
+
before_prepare(**args)
|
54
|
+
else
|
55
|
+
before_prepare
|
56
|
+
end
|
57
|
+
context.schema.after_lazy(before_prepare_val) do
|
58
|
+
# Then call each prepare hook, which may return a different value
|
59
|
+
# for that argument, or may return a lazy object
|
60
|
+
load_arguments_val = load_arguments(args)
|
61
|
+
context.schema.after_lazy(load_arguments_val) do |loaded_args|
|
62
|
+
# Then validate each argument, which may raise or may return a lazy object
|
63
|
+
validate_arguments_val = validate_arguments(loaded_args)
|
64
|
+
context.schema.after_lazy(validate_arguments_val) do |validated_args|
|
65
|
+
# Finally, all the hooks have passed, so resolve it
|
66
|
+
if validated_args.any?
|
67
|
+
public_send(self.class.resolve_method, **validated_args)
|
68
|
+
else
|
69
|
+
public_send(self.class.resolve_method)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
40
76
|
# Do the work. Everything happens here.
|
41
77
|
# @return [Object] An object corresponding to the return type
|
42
78
|
def resolve(**args)
|
43
79
|
raise NotImplementedError, "#{self.class.name}#resolve should execute the field's logic"
|
44
80
|
end
|
45
81
|
|
82
|
+
# Called before arguments are prepared.
|
83
|
+
# Implement this hook to make checks before doing any work.
|
84
|
+
#
|
85
|
+
# If it returns a lazy object (like a promise), it will be synced by GraphQL
|
86
|
+
# (but the resulting value won't be used).
|
87
|
+
#
|
88
|
+
# @param args [Hash] The input arguments, if there are any
|
89
|
+
# @raise [GraphQL::ExecutionError] To add an error to the response
|
90
|
+
# @raise [GraphQL::UnauthorizedError] To signal an authorization failure
|
91
|
+
def before_prepare(**args)
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def load_arguments(args)
|
97
|
+
prepared_args = {}
|
98
|
+
prepare_lazies = []
|
99
|
+
|
100
|
+
args.each do |key, value|
|
101
|
+
arg_defn = @arguments_by_keyword[key]
|
102
|
+
if arg_defn
|
103
|
+
prepped_value = prepared_args[key] = load_argument(key, value)
|
104
|
+
if context.schema.lazy?(prepped_value)
|
105
|
+
prepare_lazies << context.schema.after_lazy(prepped_value) do |finished_prepped_value|
|
106
|
+
prepared_args[key] = finished_prepped_value
|
107
|
+
end
|
108
|
+
end
|
109
|
+
else
|
110
|
+
# These are `extras: [...]`
|
111
|
+
prepared_args[key] = value
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Avoid returning a lazy if none are needed
|
116
|
+
if prepare_lazies.any?
|
117
|
+
GraphQL::Execution::Lazy.all(prepare_lazies).then { prepared_args }
|
118
|
+
else
|
119
|
+
prepared_args
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def load_argument(name, value)
|
124
|
+
public_send("load_#{name}", value)
|
125
|
+
end
|
126
|
+
|
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
|
+
class LoadApplicationObjectFailedError < GraphQL::ExecutionError
|
153
|
+
# @return [GraphQL::Schema::Argument] the argument definition for the argument that was looked up
|
154
|
+
attr_reader :argument
|
155
|
+
# @return [String] The ID provided by the client
|
156
|
+
attr_reader :id
|
157
|
+
# @return [Object] The value found with this ID
|
158
|
+
attr_reader :object
|
159
|
+
def initialize(argument:, id:, object:)
|
160
|
+
@id = id
|
161
|
+
@argument = argument
|
162
|
+
@object = object
|
163
|
+
super("No object found for `#{argument.graphql_name}: #{id.inspect}`")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def load_application_object(arg_kwarg, id)
|
168
|
+
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
|
+
lookup_as_type = @arguments_loads_as_type[arg_kwarg]
|
177
|
+
application_object_type = context.schema.resolve_type(lookup_as_type, application_object, context)
|
178
|
+
possible_object_types = context.schema.possible_types(lookup_as_type)
|
179
|
+
if !possible_object_types.include?(application_object_type)
|
180
|
+
raise LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
|
181
|
+
else
|
182
|
+
# This object was loaded successfully
|
183
|
+
# and resolved to the right type,
|
184
|
+
# now apply the `.authorized?` class method if there is one
|
185
|
+
if (class_based_type = application_object_type.metadata[:type_class])
|
186
|
+
context.schema.after_lazy(class_based_type.authorized?(application_object, context)) do |authed|
|
187
|
+
if authed
|
188
|
+
application_object
|
189
|
+
else
|
190
|
+
raise GraphQL::UnauthorizedError.new(
|
191
|
+
object: application_object,
|
192
|
+
type: class_based_type,
|
193
|
+
context: context,
|
194
|
+
)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
else
|
198
|
+
application_object
|
199
|
+
end
|
200
|
+
end
|
201
|
+
rescue LoadApplicationObjectFailedError => err
|
202
|
+
# pass it to a handler
|
203
|
+
load_application_object_failed(err)
|
204
|
+
end
|
205
|
+
|
206
|
+
def load_application_object_failed(err)
|
207
|
+
raise err
|
208
|
+
end
|
209
|
+
|
46
210
|
class << self
|
47
211
|
# Default `:resolve` set below.
|
48
212
|
# @return [Symbol] The method to call on instances of this object to resolve the field
|
@@ -113,7 +277,7 @@ module GraphQL
|
|
113
277
|
type: type_expr,
|
114
278
|
description: description,
|
115
279
|
extras: extras,
|
116
|
-
method:
|
280
|
+
method: :resolve_with_support,
|
117
281
|
resolver_class: self,
|
118
282
|
arguments: arguments,
|
119
283
|
null: null,
|
@@ -125,6 +289,51 @@ module GraphQL
|
|
125
289
|
def type_expr
|
126
290
|
@type_expr || (superclass.respond_to?(:type_expr) ? superclass.type_expr : nil)
|
127
291
|
end
|
292
|
+
|
293
|
+
# Add an argument to this field's signature, but
|
294
|
+
# also add some preparation hook methods which will be used for this argument
|
295
|
+
# @see {GraphQL::Schema::Argument#initialize} for the signature
|
296
|
+
def argument(name, type, *rest, loads: nil, **kwargs, &block)
|
297
|
+
if loads
|
298
|
+
arg_keyword = name.to_s.sub(/_id$/, "").to_sym
|
299
|
+
kwargs[:as] = arg_keyword
|
300
|
+
own_arguments_loads_as_type[arg_keyword] = loads
|
301
|
+
end
|
302
|
+
arg_defn = super(name, type, *rest, **kwargs, &block)
|
303
|
+
|
304
|
+
if loads
|
305
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
306
|
+
def load_#{arg_defn.keyword}(value)
|
307
|
+
load_application_object(:#{arg_defn.keyword}, value)
|
308
|
+
end
|
309
|
+
RUBY
|
310
|
+
else
|
311
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
312
|
+
def load_#{arg_defn.keyword}(value)
|
313
|
+
value
|
314
|
+
end
|
315
|
+
RUBY
|
316
|
+
end
|
317
|
+
|
318
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
319
|
+
def validate_#{arg_defn.keyword}(value)
|
320
|
+
# No-op
|
321
|
+
end
|
322
|
+
RUBY
|
323
|
+
arg_defn
|
324
|
+
end
|
325
|
+
|
326
|
+
# @api private
|
327
|
+
def arguments_loads_as_type
|
328
|
+
inherited_lookups = superclass.respond_to?(:arguments_loads_as_type) ? superclass.arguments_loads_as_type : {}
|
329
|
+
inherited_lookups.merge(own_arguments_loads_as_type)
|
330
|
+
end
|
331
|
+
|
332
|
+
private
|
333
|
+
|
334
|
+
def own_arguments_loads_as_type
|
335
|
+
@own_arguments_loads_as_type ||= {}
|
336
|
+
end
|
128
337
|
end
|
129
338
|
end
|
130
339
|
end
|