Class: GraphQL::Schema::Subscription
- Extended by:
- Member::HasFields, Resolver::HasPayloadType
- Defined in:
- lib/graphql/schema/subscription.rb
Overview
This class can be extended to create fields on your subscription root.
It provides hooks for the different parts of the subscription lifecycle:
#authorized?
: called before initial subscription and subsequent updates#subscribe
: called for the initial subscription#update
: called for subsequent update
Also, #unsubscribe
terminates the subscription.
Constant Summary collapse
- NO_UPDATE =
:no_update
Constants included from Resolver::HasPayloadType
Resolver::HasPayloadType::NO_INTERFACES
Constants included from Member::HasFields
Member::HasFields::CONFLICT_FIELD_NAMES, Member::HasFields::GRAPHQL_RUBY_KEYWORDS, Member::HasFields::RUBY_KEYWORDS
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
Attributes inherited from Resolver
Attributes included from Member::BaseDSLMethods
#default_graphql_name, #graphql_name
Class Method Summary collapse
-
.subscription_scope(new_scope = NOT_CONFIGURED, optional: false) ⇒ Symbol
Call this method to provide a new subscription_scope; OR call it without an argument to get the subscription_scope.
-
.subscription_scope_optional? ⇒ Boolean
-
.topic_for(arguments:, field:, scope:) ⇒ String
This is called during initial subscription to get a “name” for this subscription.
Instance Method Summary collapse
-
#event ⇒ Subscriptions::Event
This object is used as a representation of this subscription for the backend.
-
#initialize(object:, context:, field:) ⇒ Subscription
constructor
private
A new instance of Subscription.
-
#load_application_object_failed(err) ⇒ Object
If an argument is flagged with
loads:
and no object is found for it, remove this subscription (assuming that the object was deleted in the meantime, or that it became inaccessible). -
#resolve(**args) ⇒ Object
Implement the Resolve API.
-
#resolve_subscribe(**args) ⇒ Object
private
Wrap the user-defined
#subscribe
hook. -
#resolve_update(**args) ⇒ Object
private
Wrap the user-provided
#update
hook. -
#resolve_with_support(**args) ⇒ Object
private
-
#subscribe(args = {}) ⇒ Object
The default implementation returns nothing on subscribe.
-
#subscription_written? ⇒ Boolean
true
if #write_subscription was called already. -
#unsubscribe(update_value = nil) ⇒ void
Call this to halt execution and remove this subscription from the system.
-
#update(args = {}) ⇒ Object
The default implementation returns the root object.
-
#write_subscription ⇒ void
Calls through to
schema.subscriptions
to register this subscription with the backend.
Methods included from Resolver::HasPayloadType
field, field_class, object_class, payload_type, type
Methods included from Member::HasFields
add_field, all_field_definitions, field, field_class, global_id_field, has_no_fields, has_no_fields?, own_fields
Methods inherited from Resolver
all_field_argument_definitions, any_field_arguments?, argument, #arguments, #authorized?, broadcastable, broadcastable?, #call_resolve, complexity, #dataloader, default_page_size, extension, extensions, extras, field_arguments, get_field_argument, has_default_page_size?, has_max_page_size?, max_page_size, null, #ready?, resolve_method, resolver_method, type, type_expr, #unauthorized_object
Methods included from Member::BaseDSLMethods
#authorized?, #comment, #default_relay, #description, #introspection, #introspection?, #mutation, #name, #visible?
Methods included from Member::HasArguments
#add_argument, #all_argument_definitions, #any_arguments?, #argument, #argument_class, #arguments, #arguments_statically_coercible?, #coerce_arguments, #get_argument, #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
Constructor Details
#initialize(object:, context:, field:) ⇒ Subscription
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.
Returns a new instance of Subscription.
23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/graphql/schema/subscription.rb', line 23 def initialize(object:, context:, field:) super # Figure out whether this is an update or an initial subscription @mode = context.query.subscription_update? ? :update : :subscribe @subscription_written = false @original_arguments = nil if (subs_ns = context.namespace(:subscriptions)) && (sub_insts = subs_ns[:subscriptions]) sub_insts[context.current_path] = self end end |
Class Method Details
.subscription_scope(new_scope = NOT_CONFIGURED, optional: false) ⇒ Symbol
Call this method to provide a new subscription_scope; OR call it without an argument to get the subscription_scope
127 128 129 130 131 132 133 134 135 136 |
# File 'lib/graphql/schema/subscription.rb', line 127 def self.subscription_scope(new_scope = NOT_CONFIGURED, optional: false) if new_scope != NOT_CONFIGURED @subscription_scope = new_scope @subscription_scope_optional = optional elsif defined?(@subscription_scope) @subscription_scope else find_inherited_value(:subscription_scope) end end |
.subscription_scope_optional? ⇒ Boolean
138 139 140 141 142 143 144 |
# File 'lib/graphql/schema/subscription.rb', line 138 def self.subscription_scope_optional? if defined?(@subscription_scope_optional) @subscription_scope_optional else find_inherited_value(:subscription_scope_optional, false) end end |
.topic_for(arguments:, field:, scope:) ⇒ String
This is called during initial subscription to get a “name” for this subscription.
Later, when .trigger
is called, this will be called again to build another “name”.
Any subscribers with matching topic will begin the update flow.
The default implementation creates a string using the field name, subscription scope, and argument keys and values.
In that implementation, only .trigger
calls with exact matches result in updates to subscribers.
To implement a filtered stream-type subscription flow, override this method to return a string with field name and subscription scope.
Then, implement #update to compare its arguments to the current object
and return NO_UPDATE when an
update should be filtered out.
162 163 164 |
# File 'lib/graphql/schema/subscription.rb', line 162 def self.topic_for(arguments:, field:, scope:) Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments]) end |
Instance Method Details
#event ⇒ Subscriptions::Event
Returns This object is used as a representation of this subscription for the backend.
191 192 193 194 195 196 197 198 |
# File 'lib/graphql/schema/subscription.rb', line 191 def event @event ||= Subscriptions::Event.new( name: field.name, arguments: @original_arguments, context: context, field: field, ) end |
#load_application_object_failed(err) ⇒ Object
If an argument is flagged with loads:
and no object is found for it,
remove this subscription (assuming that the object was deleted in the meantime,
or that it became inaccessible).
107 108 109 110 111 112 |
# File 'lib/graphql/schema/subscription.rb', line 107 def load_application_object_failed(err) if @mode == :update unsubscribe end super end |
#resolve(**args) ⇒ Object
Implement the Resolve API. You can implement this if you want code to run for both the initial subscription and for later updates. Or, implement #subscribe and #update
61 62 63 64 65 |
# File 'lib/graphql/schema/subscription.rb', line 61 def resolve(**args) # Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever # have an unexpected `@mode` public_send("resolve_#{@mode}", **args) end |
#resolve_subscribe(**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.
Wrap the user-defined #subscribe
hook
69 70 71 72 73 74 75 76 |
# File 'lib/graphql/schema/subscription.rb', line 69 def resolve_subscribe(**args) ret_val = !args.empty? ? subscribe(**args) : subscribe if ret_val == :no_response context.skip else ret_val end end |
#resolve_update(**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.
Wrap the user-provided #update
hook
87 88 89 90 91 92 93 94 95 |
# File 'lib/graphql/schema/subscription.rb', line 87 def resolve_update(**args) ret_val = !args.empty? ? update(**args) : update if ret_val == NO_UPDATE context.namespace(:subscriptions)[:no_update] = true context.skip else ret_val end 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.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/graphql/schema/subscription.rb', line 36 def resolve_with_support(**args) @original_arguments = args # before `loads:` have been run result = nil unsubscribed = true unsubscribed_result = catch :graphql_subscription_unsubscribed do result = super unsubscribed = false end if unsubscribed if unsubscribed_result context.namespace(:subscriptions)[:final_update] = true unsubscribed_result else context.skip end else result end end |
#subscribe(args = {}) ⇒ Object
The default implementation returns nothing on subscribe.
Override it to return an object or
:no_response
to (explicitly) return nothing.
81 82 83 |
# File 'lib/graphql/schema/subscription.rb', line 81 def subscribe(args = {}) :no_response end |
#subscription_written? ⇒ Boolean
Returns true
if #write_subscription was called already.
186 187 188 |
# File 'lib/graphql/schema/subscription.rb', line 186 def subscription_written? @subscription_written end |
#unsubscribe(update_value = nil) ⇒ void
This method returns an undefined value.
Call this to halt execution and remove this subscription from the system
117 118 119 120 |
# File 'lib/graphql/schema/subscription.rb', line 117 def unsubscribe(update_value = nil) context.namespace(:subscriptions)[:unsubscribed] = true throw :graphql_subscription_unsubscribed, update_value end |
#update(args = {}) ⇒ Object
The default implementation returns the root object. Override it to return NO_UPDATE if you want to skip updates sometimes. Or override it to return a different object.
100 101 102 |
# File 'lib/graphql/schema/subscription.rb', line 100 def update(args = {}) object end |
#write_subscription ⇒ void
This method returns an undefined value.
Calls through to schema.subscriptions
to register this subscription with the backend.
This is automatically called by GraphQL-Ruby after a query finishes successfully,
but if you need to commit the subscription during #subscribe
, you can call it there.
(This method also sets a flag showing that this subscription was already written.)
If you call this method yourself, you may also need to #unsubscribe
or call subscriptions.delete_subscription
to clean up the database if the query crashes with an error
later in execution.
175 176 177 178 179 180 181 182 183 |
# File 'lib/graphql/schema/subscription.rb', line 175 def write_subscription if subscription_written? raise GraphQL::Error, "`write_subscription` was called but `#{self.class}#subscription_written?` is already true. Remove a call to `write subscription`." else @subscription_written = true context.schema.subscriptions.write_subscription(context.query, [event]) end nil end |