Class: GraphQL::Schema::Resolver
- Inherits:
-
Object
- Object
- GraphQL::Schema::Resolver
- Extended by:
- Member::BaseDSLMethods, Member::HasArguments, Member::HasAuthorization, Member::HasDeprecationReason, Member::HasDirectives, Member::HasPath, Member::HasValidators
- Defined in:
- lib/graphql/schema/resolver.rb,
lib/graphql/schema/resolver/has_payload_type.rb
Overview
A class-based container for field configuration and resolution logic. It supports:
- Arguments, via
.argument(...)helper, which will be applied to the field. - Return type, via
.type(..., null: ...), which will be applied to the field. - Description, via
.description(...), which will be applied to the field - Comment, via
.comment(...), which will be applied to the field - Resolution, via
#resolve(**args)method, which will be called to resolve the field. #objectand#contextaccessors for use during#resolve.
Resolvers can be attached with the resolver: option in a field(...) call.
A resolver's configuration may be overridden with other keywords in the field(...) call.
Direct Known Subclasses
Defined Under Namespace
Modules: HasPayloadType
Constant Summary
Constants included from Member::HasArguments
Member::HasArguments::NO_ARGUMENTS
Constants included from EmptyObjects
EmptyObjects::EMPTY_ARRAY, EmptyObjects::EMPTY_HASH
Constants included from Member::GraphQLTypeNames
Member::GraphQLTypeNames::Boolean, Member::GraphQLTypeNames::ID, Member::GraphQLTypeNames::Int
Instance Attribute Summary collapse
- #context ⇒ GraphQL::Query::Context readonly
-
#exec_index ⇒ Object
Returns the value of attribute exec_index.
-
#exec_result ⇒ Object
Returns the value of attribute exec_result.
- #field ⇒ GraphQL::Schema::Field readonly
-
#field_resolve_step ⇒ Object
Returns the value of attribute field_resolve_step.
-
#object ⇒ Object
The application object this field is being resolved on.
-
#prepared_arguments ⇒ Object
writeonly
Sets the attribute prepared_arguments.
-
#raw_arguments ⇒ Object
Returns the value of attribute raw_arguments.
Attributes included from Member::BaseDSLMethods
#default_graphql_name, #graphql_name
Attributes included from Member::HasDeprecationReason
Class Method Summary collapse
- .all_field_argument_definitions ⇒ Object
- .any_field_arguments? ⇒ Boolean
-
.argument(*args, **kwargs, &block) ⇒ Object
Add an argument to this field's signature, but also add some preparation hook methods which will be used for this argument.
- .authorizes?(context) ⇒ Boolean
- .broadcastable(new_broadcastable) ⇒ Object
- .broadcastable? ⇒ Boolean?
-
.complexity(new_complexity = nil) ⇒ Integer, Proc
Specifies the complexity of the field.
-
.default_page_size(new_default_page_size = NOT_CONFIGURED) ⇒ Integer?
Get or set the
default_page_size:which will be configured for fields using this resolver (nilmeans "unlimited default page size".). -
.extension(extension, **options) ⇒ Object
Registers new extension.
- .extensions ⇒ Object private
-
.extras(new_extras = nil) ⇒ Object
Additional info injected into #resolve.
- .field_arguments(context = GraphQL::Query::NullContext.instance) ⇒ Object
- .get_field_argument(name, context = GraphQL::Query::NullContext.instance) ⇒ Object
-
.has_default_page_size? ⇒ Boolean
trueif this resolver or a superclass has an assigneddefault_page_size. -
.has_max_page_size? ⇒ Boolean
trueif this resolver or a superclass has an assignedmax_page_size. -
.max_page_size(new_max_page_size = NOT_CONFIGURED) ⇒ Integer?
Get or set the
max_page_size:which will be configured for fields using this resolver (nilmeans "unlimited max page size".). -
.null(allow_null = nil) ⇒ Object
If
true(default), then the return type for this resolver will be nullable. -
.resolve_method(new_method = nil) ⇒ Symbol
Default
:resolveset below. - .resolver_method(new_method_name = nil) ⇒ Object
-
.type(new_type = nil, null: nil) ⇒ Class
Call this method to get the return type of the field, or use it as a configuration method to assign a return type instead of generating one.
-
.type_expr ⇒ Object
A non-normalized type configuration, without
nullapplied.
Instance Method Summary collapse
- #arguments ⇒ Object
-
#authorized?(**inputs) ⇒ Boolean, early_return_data
Called after arguments are loaded, but before resolving.
- #call ⇒ Object
- #call_resolve(args_hash) ⇒ Object
-
#initialize(object:, context:, field:) ⇒ Resolver
constructor
A new instance of Resolver.
-
#ready?(**args) ⇒ Boolean, early_return_data
Called before arguments are prepared.
-
#resolve(**args) ⇒ Object
Do the work.
-
#resolve_with_support(**args) ⇒ Object
private
This method is actually called by the runtime, it does some preparation and then eventually calls the user-defined
#resolvemethod. -
#unauthorized_object(err) ⇒ Object
Called when an object loaded by
loads:fails the.authorized?check for its resolved GraphQL object type.
Methods included from Member::BaseDSLMethods
comment, default_relay?, description, introspection, introspection?, mutation, visible?
Methods included from Member::HasArguments
add_argument, all_argument_definitions, any_arguments?, argument, argument_class, arguments_statically_coercible?, coerce_arguments, own_arguments, remove_argument, validate_directive_argument
Methods included from Member::HasValidators
Methods included from Member::HasPath
Methods included from Member::HasDirectives
add_directive, directive, directives, get_directives, inherited, remove_directive, remove_directive
Methods included from Member::HasDataloader
#dataload, #dataload_all, #dataload_all_associations, #dataload_all_records, #dataload_association, #dataload_record, #dataloader
Constructor Details
#initialize(object:, context:, field:) ⇒ Resolver
Returns a new instance of Resolver.
37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/graphql/schema/resolver.rb', line 37 def initialize(object:, context:, field:) @object = object @context = context @field = field # Since this hash is constantly rebuilt, cache it for this call @arguments_by_keyword = {} context.types.arguments(self.class).each do |arg| @arguments_by_keyword[arg.keyword] = arg end @prepared_arguments = nil end |
Instance Attribute Details
#context ⇒ GraphQL::Query::Context (readonly)
55 56 57 |
# File 'lib/graphql/schema/resolver.rb', line 55 def context @context end |
#exec_index ⇒ Object
Returns the value of attribute exec_index.
49 50 51 |
# File 'lib/graphql/schema/resolver.rb', line 49 def exec_index @exec_index end |
#exec_result ⇒ Object
Returns the value of attribute exec_result.
49 50 51 |
# File 'lib/graphql/schema/resolver.rb', line 49 def exec_result @exec_result end |
#field ⇒ GraphQL::Schema::Field (readonly)
58 59 60 |
# File 'lib/graphql/schema/resolver.rb', line 58 def field @field end |
#field_resolve_step ⇒ Object
Returns the value of attribute field_resolve_step.
49 50 51 |
# File 'lib/graphql/schema/resolver.rb', line 49 def field_resolve_step @field_resolve_step end |
#object ⇒ Object
Returns The application object this field is being resolved on.
52 53 54 |
# File 'lib/graphql/schema/resolver.rb', line 52 def object @object end |
#prepared_arguments=(value) ⇒ Object (writeonly)
Sets the attribute prepared_arguments
60 61 62 |
# File 'lib/graphql/schema/resolver.rb', line 60 def prepared_arguments=(value) @prepared_arguments = value end |
#raw_arguments ⇒ Object
Returns the value of attribute raw_arguments.
49 50 51 |
# File 'lib/graphql/schema/resolver.rb', line 49 def raw_arguments @raw_arguments end |
Class Method Details
.all_field_argument_definitions ⇒ Object
314 315 316 |
# File 'lib/graphql/schema/resolver.rb', line 314 def all_field_argument_definitions all_argument_definitions end |
.any_field_arguments? ⇒ Boolean
306 307 308 |
# File 'lib/graphql/schema/resolver.rb', line 306 def any_field_arguments? any_arguments? end |
.argument(*args, **kwargs, &block) ⇒ Object
Add an argument to this field's signature, but also add some preparation hook methods which will be used for this argument
455 456 457 458 459 |
# File 'lib/graphql/schema/resolver.rb', line 455 def argument(*args, **kwargs, &block) # Use `from_resolver: true` to short-circuit the InputObject's own `loads:` implementation # so that we can support `#load_{x}` methods below. super(*args, from_resolver: true, **kwargs) end |
.authorizes?(context) ⇒ Boolean
235 236 237 |
# File 'lib/graphql/schema/resolver.rb', line 235 def self.(context) self.instance_method(:authorized?).owner != GraphQL::Schema::Resolver end |
.broadcastable(new_broadcastable) ⇒ Object
392 393 394 |
# File 'lib/graphql/schema/resolver.rb', line 392 def broadcastable(new_broadcastable) @broadcastable = new_broadcastable end |
.broadcastable? ⇒ Boolean?
397 398 399 400 401 402 403 |
# File 'lib/graphql/schema/resolver.rb', line 397 def broadcastable? if defined?(@broadcastable) @broadcastable else (superclass.respond_to?(:broadcastable?) ? superclass.broadcastable? : nil) end end |
.complexity(new_complexity = nil) ⇒ Integer, Proc
Specifies the complexity of the field. Defaults to 1
385 386 387 388 389 390 |
# File 'lib/graphql/schema/resolver.rb', line 385 def complexity(new_complexity = nil) if new_complexity @complexity = new_complexity end @complexity || (superclass.respond_to?(:complexity) ? superclass.complexity : 1) end |
.default_page_size(new_default_page_size = NOT_CONFIGURED) ⇒ Integer?
Get or set the default_page_size: which will be configured for fields using this resolver
(nil means "unlimited default page size".)
430 431 432 433 434 435 436 437 438 439 440 |
# File 'lib/graphql/schema/resolver.rb', line 430 def default_page_size(new_default_page_size = NOT_CONFIGURED) if new_default_page_size != NOT_CONFIGURED @default_page_size = new_default_page_size elsif defined?(@default_page_size) @default_page_size elsif superclass.respond_to?(:default_page_size) superclass.default_page_size else nil end end |
.extension(extension, **options) ⇒ Object
Registers new extension
464 465 466 467 |
# File 'lib/graphql/schema/resolver.rb', line 464 def extension(extension, **) @own_extensions ||= [] @own_extensions << {extension => } end |
.extensions ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/graphql/schema/resolver.rb', line 470 def extensions own_exts = @own_extensions # Jump through some hoops to avoid creating arrays when we don't actually need them if superclass.respond_to?(:extensions) s_exts = superclass.extensions if own_exts if !s_exts.empty? own_exts + s_exts else own_exts end else s_exts end else own_exts || EMPTY_ARRAY end end |
.extras(new_extras = nil) ⇒ Object
Additional info injected into #resolve
329 330 331 332 333 334 335 |
# File 'lib/graphql/schema/resolver.rb', line 329 def extras(new_extras = nil) if new_extras @own_extras = new_extras end own_extras = @own_extras || [] own_extras + (superclass.respond_to?(:extras) ? superclass.extras : []) end |
.field_arguments(context = GraphQL::Query::NullContext.instance) ⇒ Object
302 303 304 |
# File 'lib/graphql/schema/resolver.rb', line 302 def field_arguments(context = GraphQL::Query::NullContext.instance) arguments(context) end |
.get_field_argument(name, context = GraphQL::Query::NullContext.instance) ⇒ Object
310 311 312 |
# File 'lib/graphql/schema/resolver.rb', line 310 def get_field_argument(name, context = GraphQL::Query::NullContext.instance) get_argument(name, context) end |
.has_default_page_size? ⇒ Boolean
Returns true if this resolver or a superclass has an assigned default_page_size.
443 444 445 |
# File 'lib/graphql/schema/resolver.rb', line 443 def has_default_page_size? (!!defined?(@default_page_size)) || (superclass.respond_to?(:has_default_page_size?) && superclass.has_default_page_size?) end |
.has_max_page_size? ⇒ Boolean
Returns true if this resolver or a superclass has an assigned max_page_size.
422 423 424 |
# File 'lib/graphql/schema/resolver.rb', line 422 def has_max_page_size? (!!defined?(@max_page_size)) || (superclass.respond_to?(:has_max_page_size?) && superclass.has_max_page_size?) end |
.max_page_size(new_max_page_size = NOT_CONFIGURED) ⇒ Integer?
Get or set the max_page_size: which will be configured for fields using this resolver
(nil means "unlimited max page size".)
409 410 411 412 413 414 415 416 417 418 419 |
# File 'lib/graphql/schema/resolver.rb', line 409 def max_page_size(new_max_page_size = NOT_CONFIGURED) if new_max_page_size != NOT_CONFIGURED @max_page_size = new_max_page_size elsif defined?(@max_page_size) @max_page_size elsif superclass.respond_to?(:max_page_size) superclass.max_page_size else nil end end |
.null(allow_null = nil) ⇒ Object
If true (default), then the return type for this resolver will be nullable.
If false, then the return type is non-null.
342 343 344 345 346 347 348 |
# File 'lib/graphql/schema/resolver.rb', line 342 def null(allow_null = nil) if !allow_null.nil? @null = allow_null end @null.nil? ? (superclass.respond_to?(:null) ? superclass.null : true) : @null end |
.resolve_method(new_method = nil) ⇒ Symbol
Default :resolve set below.
320 321 322 323 324 325 |
# File 'lib/graphql/schema/resolver.rb', line 320 def resolve_method(new_method = nil) if new_method @resolve_method = new_method end @resolve_method || (superclass.respond_to?(:resolve_method) ? superclass.resolve_method : :resolve) end |
.resolver_method(new_method_name = nil) ⇒ Object
350 351 352 353 354 355 356 |
# File 'lib/graphql/schema/resolver.rb', line 350 def resolver_method(new_method_name = nil) if new_method_name @resolver_method = new_method_name else @resolver_method || :resolve_with_support end end |
.type(new_type = nil, null: nil) ⇒ Class
Call this method to get the return type of the field, or use it as a configuration method to assign a return type instead of generating one. TODO unify with #null
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/graphql/schema/resolver.rb', line 365 def type(new_type = nil, null: nil) if new_type if null.nil? raise ArgumentError, "required argument `null:` is missing" end @type_expr = new_type @null = null else if type_expr GraphQL::Schema::Member::BuildType.parse_type(type_expr, null: self.null) elsif superclass.respond_to?(:type) superclass.type else nil end end end |
.type_expr ⇒ Object
A non-normalized type configuration, without null applied
448 449 450 |
# File 'lib/graphql/schema/resolver.rb', line 448 def type_expr @type_expr || (superclass.respond_to?(:type_expr) ? superclass.type_expr : nil) end |
Instance Method Details
#arguments ⇒ Object
135 136 137 |
# File 'lib/graphql/schema/resolver.rb', line 135 def arguments @prepared_arguments || raise("Arguments have not been prepared yet, still waiting for #load_arguments to resolve. (Call `.arguments` later in the code.)") end |
#authorized?(**inputs) ⇒ Boolean, early_return_data
Called after arguments are loaded, but before resolving.
Override it to check everything before calling the mutation.
229 230 231 232 233 |
# File 'lib/graphql/schema/resolver.rb', line 229 def (**inputs) arg_owner = @field # || self.class args = context.types.arguments(arg_owner) (args, inputs) end |
#call ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/graphql/schema/resolver.rb', line 62 def call if self.class < Schema::HasSingleInputArgument @prepared_arguments = @prepared_arguments[:input] end q = context.query trace_objs = [object] q.current_trace.begin_execute_field(field, @prepared_arguments, trace_objs, q) is_ready = ready?(**@prepared_arguments) runner = @field_resolve_step.runner if runner.resolves_lazies && runner.schema.lazy?(is_ready) is_ready, new_return_value = runner.schema.sync_lazy(is_ready) end if is_ready.is_a?(Array) is_ready, new_return_value = is_ready if is_ready != false raise "Unexpected result from #ready? (expected `true`, `false` or `[false, {...}]`): [#{is_ready.inspect}, #{new_return_value.inspect}]" else new_return_value end end if is_ready begin is_authed, new_return_value = (**@prepared_arguments) rescue GraphQL::UnauthorizedError => err new_return_value = q.schema.(err) is_authed = true # the error was handled end end if runner.resolves_lazies && runner.schema.lazy?(is_authed) is_authed, new_return_value = runner.schema.sync_lazy(is_authed) end result = if is_authed Schema::Validator.validate!(self.class.validators, object, context, @prepared_arguments, as: @field) if q.subscription? && @field.owner == context.schema.subscription # This needs to use arguments without `loads:`. TODO extract this into subscription-related code somehow? @original_arguments = @field_resolve_step.runner.input_values[q].argument_values(@field, @field_resolve_step.ast_node.arguments, nil) end call_resolve(@prepared_arguments) elsif new_return_value.nil? err = UnauthorizedFieldError.new(object: object, type: @field_resolve_step.parent_type, context: context, field: @field) context.schema.(err) else new_return_value end q = context.query q.current_trace.end_execute_field(field, @prepared_arguments, trace_objs, q, [result]) exec_result[exec_index] = result rescue GraphQL::UnauthorizedError => auth_err exec_result[exec_index] = begin context.schema.(auth_err) rescue GraphQL::ExecutionError => exec_err exec_err end rescue GraphQL::RuntimeError => err exec_result[exec_index] = err rescue StandardError => stderr exec_result[exec_index] = begin context.query.handle_or_reraise(stderr) rescue GraphQL::ExecutionError => ex_err ex_err end ensure field_pending_steps = field_resolve_step.pending_steps field_pending_steps.delete(self) if field_pending_steps.size == 0 && field_resolve_step.field_results field_resolve_step.runner.add_step(field_resolve_step) end end |
#call_resolve(args_hash) ⇒ Object
194 195 196 197 198 199 200 |
# File 'lib/graphql/schema/resolver.rb', line 194 def call_resolve(args_hash) if !args_hash.empty? public_send(self.class.resolve_method, **args_hash) else public_send(self.class.resolve_method) end end |
#ready?(**args) ⇒ Boolean, early_return_data
Called before arguments are prepared. Implement this hook to make checks before doing any work.
If it returns a lazy object (like a promise), it will be synced by GraphQL (but the resulting value won't be used).
218 219 220 |
# File 'lib/graphql/schema/resolver.rb', line 218 def ready?(**args) true end |
#resolve(**args) ⇒ Object
Do the work. Everything happens here.
204 205 206 |
# File 'lib/graphql/schema/resolver.rb', line 204 def resolve(**args) raise GraphQL::RequiredImplementationMissingError, "#{self.class.name}#resolve should execute the field's logic" end |
#resolve_with_support(**args) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method is actually called by the runtime,
it does some preparation and then eventually calls
the user-defined #resolve method.
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/graphql/schema/resolver.rb', line 143 def resolve_with_support(**args) # First call the ready? hook which may raise raw_ready_val = if !args.empty? ready?(**args) else ready? end context.query.after_lazy(raw_ready_val) do |ready_val| if ready_val.is_a?(Array) is_ready, ready_early_return = ready_val if is_ready != false raise "Unexpected result from #ready? (expected `true`, `false` or `[false, {...}]`): [#{is_ready.inspect}, #{ready_early_return.inspect}]" else ready_early_return end elsif ready_val # Then call each prepare hook, which may return a different value # for that argument, or may return a lazy object load_arguments_val = load_arguments(args) context.query.after_lazy(load_arguments_val) do |loaded_args| @prepared_arguments = loaded_args Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field) # Then call `authorized?`, which may raise or may return a lazy object = if !loaded_args.empty? (**loaded_args) else end context.query.after_lazy() do || # If the `authorized?` returned two values, `false, early_return`, # then use the early return value instead of continuing if .is_a?(Array) , early_return = if == false early_return else raise "Unexpected result from #authorized? (expected `true`, `false` or `[false, {...}]`): [#{.inspect}, #{early_return.inspect}]" end elsif # Finally, all the hooks have passed, so resolve it call_resolve(loaded_args) else raise GraphQL::UnauthorizedFieldError.new(context: context, object: object, type: field.owner, field: field) end end end end end end |
#unauthorized_object(err) ⇒ Object
Called when an object loaded by loads: fails the .authorized? check for its resolved GraphQL object type.
By default, the error is re-raised and passed along to GraphQL::Schema::Resolver.{Schema{Schema.unauthorized_object}.
Any value returned here will be used instead of of the loaded object.
245 246 247 |
# File 'lib/graphql/schema/resolver.rb', line 245 def (err) raise err end |