Class: GraphQL::Schema::Resolver
- Inherits:
-
Object
- Object
- GraphQL::Schema::Resolver
- Includes:
- Member::GraphQLTypeNames, Member::HasPath
- 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 - Resolution, via
#resolve(**args)
method, which will be called to resolve the field. #object
and#context
accessors 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 FindInheritedValue::EmptyObjects
FindInheritedValue::EmptyObjects::EMPTY_ARRAY, FindInheritedValue::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
-
#field ⇒ GraphQL::Schema::Field
readonly
-
#object ⇒ Object
readonly
The application object this field is being resolved on.
Attributes included from Member::BaseDSLMethods
#default_graphql_name, #graphql_name
Class Method Summary collapse
-
.all_field_argument_definitions ⇒ Object
-
.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.
-
.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_given) ⇒ Integer?
Get or set the
default_page_size:
which will be configured for fields using this resolver (nil
means “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) ⇒ Object
-
.get_field_argument(name, context = GraphQL::Query::NullContext) ⇒ Object
-
.has_default_page_size? ⇒ Boolean
true
if this resolver or a superclass has an assigneddefault_page_size
. -
.has_max_page_size? ⇒ Boolean
true
if this resolver or a superclass has an assignedmax_page_size
. -
.max_page_size(new_max_page_size = :not_given) ⇒ Integer?
Get or set the
max_page_size:
which will be configured for fields using this resolver (nil
means “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
:resolve
set 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
null
applied.
Instance Method Summary collapse
-
#arguments ⇒ Object
-
#authorized?(**inputs) ⇒ Boolean, early_return_data
Called after arguments are loaded, but before resolving.
-
#dataloader ⇒ GraphQL::Dataloader
-
#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
#resolve
method. -
#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
default_relay, description, introspection, introspection?, mutation, name, visible?
Methods included from Member::HasArguments
add_argument, all_argument_definitions, 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
Constructor Details
#initialize(object:, context:, field:) ⇒ Resolver
Returns a new instance of Resolver.
32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/graphql/schema/resolver.rb', line 32 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 = {} self.class.arguments(context).each do |name, arg| @arguments_by_keyword[arg.keyword] = arg end @prepared_arguments = nil end |
Instance Attribute Details
#context ⇒ GraphQL::Query::Context (readonly)
48 49 50 |
# File 'lib/graphql/schema/resolver.rb', line 48 def context @context end |
#field ⇒ GraphQL::Schema::Field (readonly)
56 57 58 |
# File 'lib/graphql/schema/resolver.rb', line 56 def field @field end |
#object ⇒ Object (readonly)
Returns The application object this field is being resolved on.
45 46 47 |
# File 'lib/graphql/schema/resolver.rb', line 45 def object @object end |
Class Method Details
.all_field_argument_definitions ⇒ Object
219 220 221 |
# File 'lib/graphql/schema/resolver.rb', line 219 def all_field_argument_definitions all_argument_definitions 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
360 361 362 363 364 |
# File 'lib/graphql/schema/resolver.rb', line 360 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 |
.broadcastable(new_broadcastable) ⇒ Object
297 298 299 |
# File 'lib/graphql/schema/resolver.rb', line 297 def broadcastable(new_broadcastable) @broadcastable = new_broadcastable end |
.broadcastable? ⇒ Boolean?
302 303 304 305 306 307 308 |
# File 'lib/graphql/schema/resolver.rb', line 302 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
290 291 292 293 294 295 |
# File 'lib/graphql/schema/resolver.rb', line 290 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_given) ⇒ Integer?
Get or set the default_page_size:
which will be configured for fields using this resolver
(nil
means “unlimited default page size”.)
335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/graphql/schema/resolver.rb', line 335 def default_page_size(new_default_page_size = :not_given) if new_default_page_size != :not_given @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
369 370 371 372 |
# File 'lib/graphql/schema/resolver.rb', line 369 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.
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/graphql/schema/resolver.rb', line 375 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.any? 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
234 235 236 237 238 239 240 |
# File 'lib/graphql/schema/resolver.rb', line 234 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) ⇒ Object
211 212 213 |
# File 'lib/graphql/schema/resolver.rb', line 211 def field_arguments(context = GraphQL::Query::NullContext) arguments(context) end |
.get_field_argument(name, context = GraphQL::Query::NullContext) ⇒ Object
215 216 217 |
# File 'lib/graphql/schema/resolver.rb', line 215 def get_field_argument(name, context = GraphQL::Query::NullContext) get_argument(name, context) end |
.has_default_page_size? ⇒ Boolean
Returns true
if this resolver or a superclass has an assigned default_page_size
.
348 349 350 |
# File 'lib/graphql/schema/resolver.rb', line 348 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
.
327 328 329 |
# File 'lib/graphql/schema/resolver.rb', line 327 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_given) ⇒ Integer?
Get or set the max_page_size:
which will be configured for fields using this resolver
(nil
means “unlimited max page size”.)
314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/graphql/schema/resolver.rb', line 314 def max_page_size(new_max_page_size = :not_given) if new_max_page_size != :not_given @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.
247 248 249 250 251 252 253 |
# File 'lib/graphql/schema/resolver.rb', line 247 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.
225 226 227 228 229 230 |
# File 'lib/graphql/schema/resolver.rb', line 225 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
255 256 257 258 259 260 261 |
# File 'lib/graphql/schema/resolver.rb', line 255 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
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/graphql/schema/resolver.rb', line 270 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
353 354 355 |
# File 'lib/graphql/schema/resolver.rb', line 353 def type_expr @type_expr || (superclass.respond_to?(:type_expr) ? superclass.type_expr : nil) end |
Instance Method Details
#arguments ⇒ Object
58 59 60 |
# File 'lib/graphql/schema/resolver.rb', line 58 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.
145 146 147 148 149 |
# File 'lib/graphql/schema/resolver.rb', line 145 def (**inputs) arg_owner = @field # || self.class args = arg_owner.arguments(context) (args, inputs) end |
#dataloader ⇒ GraphQL::Dataloader
51 52 53 |
# File 'lib/graphql/schema/resolver.rb', line 51 def dataloader context.dataloader 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).
134 135 136 |
# File 'lib/graphql/schema/resolver.rb', line 134 def ready?(**args) true end |
#resolve(**args) ⇒ Object
Do the work. Everything happens here.
120 121 122 |
# File 'lib/graphql/schema/resolver.rb', line 120 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.
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 |
# File 'lib/graphql/schema/resolver.rb', line 66 def resolve_with_support(**args) # First call the ready? hook which may raise ready_val = if args.any? ready?(**args) else ready? end context.schema.after_lazy(ready_val) do |is_ready, ready_early_return| if ready_early_return 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 is_ready # 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.schema.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.any? (**loaded_args) else end context.schema.after_lazy() do |(, early_return)| # If the `authorized?` returned two values, `false, early_return`, # then use the early return value instead of continuing if 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 if loaded_args.any? public_send(self.class.resolve_method, **loaded_args) else public_send(self.class.resolve_method) end 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.
157 158 159 |
# File 'lib/graphql/schema/resolver.rb', line 157 def (err) raise err end |