graphql 1.8.4 → 1.8.5
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/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
|