Class: GraphQL::Subscriptions
- Inherits:
- 
      Object
      
        - Object
- GraphQL::Subscriptions
 
- Defined in:
- lib/graphql/subscriptions.rb,
 lib/graphql/subscriptions/event.rb,
 lib/graphql/subscriptions/serialize.rb,
 lib/graphql/subscriptions/broadcast_analyzer.rb,
 lib/graphql/subscriptions/action_cable_subscriptions.rb,
 lib/graphql/subscriptions/default_subscription_resolve_extension.rb
Direct Known Subclasses
Defined Under Namespace
Modules: Serialize Classes: ActionCableSubscriptions, BroadcastAnalyzer, DefaultSubscriptionResolveExtension, Event, InvalidTriggerError, SubscriptionScopeMissingError
Instance Attribute Summary collapse
- 
  
    
      #default_broadcastable  ⇒ Boolean 
    
    
  
  
  
  
    
      readonly
    
    
  
  
  
  
  
  
    Used when fields don’t have broadcastable:explicitly set.
Class Method Summary collapse
Instance Method Summary collapse
- 
  
    
      #broadcastable?(query_str, **query_options)  ⇒ Boolean 
    
    
  
  
  
  
  
  
  
  
  
    If true, then a query like this one would be broadcasted. 
- 
  
    
      #build_id  ⇒ String 
    
    
  
  
  
  
  
  
  
  
  
    A new unique identifier for a subscription. 
- 
  
    
      #delete_subscription(subscription_id)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    A subscription was terminated server-side. 
- 
  
    
      #deliver(subscription_id, result)  ⇒ void 
    
    
  
  
  
  
  
  
  
  
  
    A subscription query was re-evaluated, returning result.
- 
  
    
      #execute(subscription_id, event, object)  ⇒ void 
    
    
  
  
  
  
  
  
  
  
  
    Run the update query for this subscription and deliver it. 
- 
  
    
      #execute_all(event, object)  ⇒ void 
    
    
  
  
  
  
  
  
  
  
  
    Event eventoccurred onobject, Update all subscribers.
- 
  
    
      #execute_update(subscription_id, event, object)  ⇒ GraphQL::Query::Result 
    
    
  
  
  
  
  
  
  
  
  
    eventwas triggered onobject, andsubscription_idwas subscribed, so it should be updated.
- 
  
    
      #initialize(schema:, validate_update: true, broadcast: false, default_broadcastable: false, **rest)  ⇒ Subscriptions 
    
    
  
  
  
    constructor
  
  
  
  
  
  
  
    A new instance of Subscriptions. 
- 
  
    
      #normalize_name(event_or_arg_name)  ⇒ String 
    
    
  
  
  
  
  
  
  
  
  
    Convert a user-provided event name or argument to the equivalent in GraphQL. 
- 
  
    
      #read_subscription(subscription_id)  ⇒ Hash 
    
    
  
  
  
  
  
  
  
  
  
    The system wants to send an update to this subscription. 
- 
  
    
      #trigger(event_name, args, object, scope: nil, context: {})  ⇒ void 
    
    
  
  
  
  
  
  
  
  
  
    Fetch subscriptions matching this field + arguments pair And pass them off to the queue. 
- 
  
    
      #validate_update?(query:, context:, root_value:, subscription_topic:, operation_name:, variables:)  ⇒ Boolean 
    
    
  
  
  
  
  
  
  
  
  
    Define this method to customize whether to validate this subscription when executing an update. 
- 
  
    
      #write_subscription(query, events)  ⇒ void 
    
    
  
  
  
  
  
  
  
  
  
    querywas executed and found subscriptions toevents.
Constructor Details
#initialize(schema:, validate_update: true, broadcast: false, default_broadcastable: false, **rest) ⇒ Subscriptions
Returns a new instance of Subscriptions.
| 40 41 42 43 44 45 46 47 | # File 'lib/graphql/subscriptions.rb', line 40 def initialize(schema:, validate_update: true, broadcast: false, default_broadcastable: false, **rest) if broadcast schema.query_analyzer(Subscriptions::BroadcastAnalyzer) end @default_broadcastable = default_broadcastable @schema = schema @validate_update = validate_update end | 
Instance Attribute Details
#default_broadcastable ⇒ Boolean (readonly)
Returns Used when fields don’t have broadcastable: explicitly set.
| 50 51 52 | # File 'lib/graphql/subscriptions.rb', line 50 def default_broadcastable @default_broadcastable end | 
Class Method Details
.use(defn, options = {}) ⇒ Object
| 25 26 27 28 29 30 31 32 33 34 35 36 | # File 'lib/graphql/subscriptions.rb', line 25 def self.use(defn, = {}) schema = defn.is_a?(Class) ? defn : defn.target if schema.subscriptions(inherited: false) raise ArgumentError, "Can't reinstall subscriptions. #{schema} is using #{schema.subscriptions}, can't also add #{self}" end [:schema] = schema schema.subscriptions = self.new(**) schema.add_subscription_extension_if_necessary nil end | 
Instance Method Details
#broadcastable?(query_str, **query_options) ⇒ Boolean
Returns if true, then a query like this one would be broadcasted.
| 233 234 235 236 237 238 239 240 | # File 'lib/graphql/subscriptions.rb', line 233 def broadcastable?(query_str, **) query = @schema.query_class.new(@schema, query_str, **) if !query.valid? raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}" end GraphQL::Analysis.analyze_query(query, @schema.query_analyzers) query.context.namespace(:subscriptions)[:subscription_broadcastable] end | 
#build_id ⇒ String
Returns A new unique identifier for a subscription.
| 216 217 218 | # File 'lib/graphql/subscriptions.rb', line 216 def build_id SecureRandom.uuid end | 
#delete_subscription(subscription_id) ⇒ Object
A subscription was terminated server-side. Clean up the database.
| 211 212 213 | # File 'lib/graphql/subscriptions.rb', line 211 def delete_subscription(subscription_id) raise GraphQL::RequiredImplementationMissingError end | 
#deliver(subscription_id, result) ⇒ void
This method returns an undefined value.
A subscription query was re-evaluated, returning result.
The result should be send to subscription_id.
| 194 195 196 | # File 'lib/graphql/subscriptions.rb', line 194 def deliver(subscription_id, result) raise GraphQL::RequiredImplementationMissingError end | 
#execute(subscription_id, event, object) ⇒ void
This method returns an undefined value.
Run the update query for this subscription and deliver it
| 158 159 160 161 162 163 164 165 166 167 168 169 170 | # File 'lib/graphql/subscriptions.rb', line 158 def execute(subscription_id, event, object) res = execute_update(subscription_id, event, object) if !res.nil? deliver(subscription_id, res) if res.context.namespace(:subscriptions)[:unsubscribed] # `unsubscribe` was called, clean up on our side # The transport should also send `{more: false}` to client delete_subscription(subscription_id) end end end | 
#execute_all(event, object) ⇒ void
This method returns an undefined value.
Event event occurred on object,
Update all subscribers.
| 177 178 179 | # File 'lib/graphql/subscriptions.rb', line 177 def execute_all(event, object) raise GraphQL::RequiredImplementationMissingError end | 
#execute_update(subscription_id, event, object) ⇒ GraphQL::Query::Result
event was triggered on object, and subscription_id was subscribed,
so it should be updated.
Load subscription_id’s GraphQL data, re-evaluate the query and return the result.
| 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 134 135 136 137 138 139 140 141 142 143 144 | # File 'lib/graphql/subscriptions.rb', line 104 def execute_update(subscription_id, event, object) # Lookup the saved data for this subscription query_data = read_subscription(subscription_id) if query_data.nil? delete_subscription(subscription_id) return nil end # Fetch the required keys from the saved data query_string = query_data.fetch(:query_string) variables = query_data.fetch(:variables) context = query_data.fetch(:context) operation_name = query_data.fetch(:operation_name) = { query: query_string, context: context, subscription_topic: event.topic, operation_name: operation_name, variables: variables, root_value: object, } # merge event's and query's context together context.merge!(event.context) unless event.context.nil? || context.nil? [:validate] = validate_update?(**) result = @schema.execute(**) subscriptions_context = result.context.namespace(:subscriptions) if subscriptions_context[:no_update] result = nil end if subscriptions_context[:unsubscribed] && !subscriptions_context[:final_update] # `unsubscribe` was called, clean up on our side # The transport should also send `{more: false}` to client delete_subscription(subscription_id) result = nil end result end | 
#normalize_name(event_or_arg_name) ⇒ String
Convert a user-provided event name or argument to the equivalent in GraphQL.
By default, it converts the identifier to camelcase. Override this in a subclass to change the transformation.
| 228 229 230 | # File 'lib/graphql/subscriptions.rb', line 228 def normalize_name(event_or_arg_name) Schema::Member::BuildType.camelize(event_or_arg_name.to_s) end | 
#read_subscription(subscription_id) ⇒ Hash
The system wants to send an update to this subscription. Read its data and return it.
| 185 186 187 | # File 'lib/graphql/subscriptions.rb', line 185 def read_subscription(subscription_id) raise GraphQL::RequiredImplementationMissingError end | 
#trigger(event_name, args, object, scope: nil, context: {}) ⇒ void
This method returns an undefined value.
Fetch subscriptions matching this field + arguments pair And pass them off to the queue.
| 60 61 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 | # File 'lib/graphql/subscriptions.rb', line 60 def trigger(event_name, args, object, scope: nil, context: {}) # Make something as context-like as possible, even though there isn't a current query: dummy_query = @schema.query_class.new(@schema, "{ __typename }", validate: false, context: context) context = dummy_query.context event_name = event_name.to_s # Try with the verbatim input first: field = dummy_query.types.field(@schema.subscription, event_name) # rubocop:disable Development/ContextIsPassedCop if field.nil? # And if it wasn't found, normalize it: normalized_event_name = normalize_name(event_name) field = dummy_query.types.field(@schema.subscription, normalized_event_name) # rubocop:disable Development/ContextIsPassedCop if field.nil? raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})" end else # Since we found a field, the original input was already normalized normalized_event_name = event_name end # Normalize symbol-keyed args to strings, try camelizing them # Should this accept a real context somehow? normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext.instance) event = Subscriptions::Event.new( name: normalized_event_name, arguments: normalized_args, field: field, scope: scope, context: context, ) execute_all(event, object) end | 
#validate_update?(query:, context:, root_value:, subscription_topic:, operation_name:, variables:) ⇒ Boolean
Define this method to customize whether to validate this subscription when executing an update.
| 150 151 152 | # File 'lib/graphql/subscriptions.rb', line 150 def validate_update?(query:, context:, root_value:, subscription_topic:, operation_name:, variables:) @validate_update end | 
#write_subscription(query, events) ⇒ void
This method returns an undefined value.
query was executed and found subscriptions to events.
Update the database to reflect this new state.
| 203 204 205 | # File 'lib/graphql/subscriptions.rb', line 203 def write_subscription(query, events) raise GraphQL::RequiredImplementationMissingError end |