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.
Defined Under Namespace
Classes: EarlyUnsubscribe
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 EmptyObjects
EmptyObjects::EMPTY_ARRAY, EmptyObjects::EMPTY_HASH
Constants included from Member::HasArguments
Member::HasArguments::NO_ARGUMENTS
Constants included from Member::GraphQLTypeNames
Member::GraphQLTypeNames::Boolean, Member::GraphQLTypeNames::ID, Member::GraphQLTypeNames::Int
Instance Attribute Summary
Attributes inherited from Resolver
#context, #exec_index, #exec_result, #field, #field_resolve_step, #object, #prepared_arguments, #raw_arguments
Attributes included from Member::BaseDSLMethods
#default_graphql_name, #graphql_name
Attributes included from Member::HasDeprecationReason
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
- #call_resolve(args_hash) ⇒ Object private
-
#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
#subscribehook. -
#resolve_update(**args) ⇒ Object
private
Wrap the user-provided
#updatehook. - #resolve_with_support(**args) ⇒ Object private
-
#subscribe(args = {}) ⇒ Object
The default implementation returns nothing on subscribe.
-
#subscription_written? ⇒ Boolean
trueif #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.subscriptionsto 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?, authorizes?, broadcastable, broadcastable?, #call, complexity, 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, #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::HasAuthorization
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:) ⇒ 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.
21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/graphql/schema/subscription.rb', line 21 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
164 165 166 167 168 169 170 171 172 173 |
# File 'lib/graphql/schema/subscription.rb', line 164 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
175 176 177 178 179 180 181 |
# File 'lib/graphql/schema/subscription.rb', line 175 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.
199 200 201 |
# File 'lib/graphql/schema/subscription.rb', line 199 def self.topic_for(arguments:, field:, scope:) Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments]) end |
Instance Method Details
#call_resolve(args_hash) ⇒ 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.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/graphql/schema/subscription.rb', line 34 def call_resolve(args_hash) if @field_resolve_step.nil? super else context.namespace(:subscriptions)[:update_event] = event result = nil unsubscribed = true unsubscribed_result = nil begin result = super unsubscribed = false rescue EarlyUnsubscribe => err unsubscribed_result = err.unsubscribed_result end if unsubscribed if unsubscribed_result context.namespace(:subscriptions)[:final_update] = true unsubscribed_result else context.skip end else result end end end |
#event ⇒ Subscriptions::Event
Returns This object is used as a representation of this subscription for the backend.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/graphql/schema/subscription.rb', line 228 def event @event ||= begin if @original_arguments.nil? && @field_resolve_step @original_arguments, _errors = @field_resolve_step.arguments_without_loads end Subscriptions::Event.new( name: field.name, arguments: @original_arguments, context: context, field: field, ) end 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).
138 139 140 141 142 143 |
# File 'lib/graphql/schema/subscription.rb', line 138 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
92 93 94 95 96 |
# File 'lib/graphql/schema/subscription.rb', line 92 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
100 101 102 103 104 105 106 107 |
# File 'lib/graphql/schema/subscription.rb', line 100 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
118 119 120 121 122 123 124 125 126 |
# File 'lib/graphql/schema/subscription.rb', line 118 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.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/graphql/schema/subscription.rb', line 64 def resolve_with_support(**args) @original_arguments = args # before `loads:` have been run result = nil unsubscribed = true unsubscribed_result = nil begin result = super unsubscribed = false rescue EarlyUnsubscribe => err unsubscribed_result = err.unsubscribed_result 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.
112 113 114 |
# File 'lib/graphql/schema/subscription.rb', line 112 def subscribe(args = {}) :no_response end |
#subscription_written? ⇒ Boolean
Returns true if #write_subscription was called already.
223 224 225 |
# File 'lib/graphql/schema/subscription.rb', line 223 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
148 149 150 151 152 153 |
# File 'lib/graphql/schema/subscription.rb', line 148 def unsubscribe(update_value = nil) context.namespace(:subscriptions)[:unsubscribed] = true err = EarlyUnsubscribe.new err.unsubscribed_result = update_value raise err 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.
131 132 133 |
# File 'lib/graphql/schema/subscription.rb', line 131 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.
212 213 214 215 216 217 218 219 220 |
# File 'lib/graphql/schema/subscription.rb', line 212 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 |