Class: GraphQL::Subscriptions

Inherits:
Object
  • Object
show all
Defined in:
lib/graphql/subscriptions.rb,
lib/graphql/subscriptions/event.rb,
lib/graphql/subscriptions/serialize.rb,
lib/graphql/subscriptions/instrumentation.rb,
lib/graphql/subscriptions/subscription_root.rb,
lib/graphql/subscriptions/action_cable_subscriptions.rb

Direct Known Subclasses

ActionCableSubscriptions

Defined Under Namespace

Modules: Serialize, SubscriptionRoot Classes: ActionCableSubscriptions, Event, Instrumentation, InvalidTriggerError

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schema:, **rest) ⇒ Subscriptions

Returns a new instance of Subscriptions

Parameters:

  • schema (Class)

    the GraphQL schema this manager belongs to



31
32
33
# File 'lib/graphql/subscriptions.rb', line 31

def initialize(schema:, **rest)
  @schema = schema
end

Class Method Details

.use(defn, options = {}) ⇒ Object

See Also:

  • for options, concrete implementations may add options.


20
21
22
23
24
25
26
27
28
# File 'lib/graphql/subscriptions.rb', line 20

def self.use(defn, options = {})
  schema = defn.target
  options[:schema] = schema
  schema.subscriptions = self.new(options)
  instrumentation = Subscriptions::Instrumentation.new(schema: schema)
  defn.instrument(:field, instrumentation)
  defn.instrument(:query, instrumentation)
  nil
end

Instance Method Details

#build_idString

Returns A new unique identifier for a subscription

Returns:

  • (String)

    A new unique identifier for a subscription



165
166
167
# File 'lib/graphql/subscriptions.rb', line 165

def build_id
  SecureRandom.uuid
end

#delete_subscription(subscription_id) ⇒ Object

A subscription was terminated server-side. Clean up the database.

Parameters:

  • subscription_id (String)

Returns:

  • void.

Raises:



160
161
162
# File 'lib/graphql/subscriptions.rb', line 160

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.

Parameters:

  • subscription_id (String)
  • result (Hash)

Raises:



143
144
145
# File 'lib/graphql/subscriptions.rb', line 143

def deliver(subscription_id, result)
  raise GraphQL::RequiredImplementationMissingError
end

#each_subscription_id(event) {|subscription_id| ... } ⇒ void

This method returns an undefined value.

Get each subscription_id subscribed to event.topic and yield them

Parameters:

Yield Parameters:

  • subscription_id (String)

Raises:



126
127
128
# File 'lib/graphql/subscriptions.rb', line 126

def each_subscription_id(event)
  raise GraphQL::RequiredImplementationMissingError
end

#execute(subscription_id, event, object) ⇒ void

This method returns an undefined value.

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 deliver the result.

This is where a queue may be inserted to push updates in the background.

Parameters:

  • subscription_id (String)
  • event (GraphQL::Subscriptions::Event)

    The event which was triggered

  • object (Object)

    The value for the subscription field



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
# File 'lib/graphql/subscriptions.rb', line 83

def execute(subscription_id, event, object)
  # Lookup the saved data for this subscription
  query_data = read_subscription(subscription_id)
  # 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)
  # Re-evaluate the saved query
  result = @schema.execute(
    {
      query: query_string,
      context: context,
      subscription_topic: event.topic,
      operation_name: operation_name,
      variables: variables,
      root_value: object,
    }
  )
  deliver(subscription_id, result)
rescue GraphQL::Schema::Subscription::NoUpdateError
  # This update was skipped in user code; do nothing.
rescue GraphQL::Schema::Subscription::UnsubscribedError
  # `unsubscribe` was called, clean up on our side
  # TODO also send `{more: false}` to client?
  delete_subscription(subscription_id)
end

#execute_all(event, object) ⇒ void

This method returns an undefined value.

Event event occurred on object, Update all subscribers.

Parameters:



116
117
118
119
120
# File 'lib/graphql/subscriptions.rb', line 116

def execute_all(event, object)
  each_subscription_id(event) do |subscription_id|
    execute(subscription_id, event, object)
  end
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.

Parameters:

  • event_or_arg_name (String, Symbol)

Returns:

  • (String)


177
178
179
# File 'lib/graphql/subscriptions.rb', line 177

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.

Parameters:

  • subscription_id (String)

Returns:

  • (Hash)

    Containing required keys

Raises:



134
135
136
# File 'lib/graphql/subscriptions.rb', line 134

def read_subscription(subscription_id)
  raise GraphQL::RequiredImplementationMissingError
end

#trigger(event_name, args, object, scope: nil) ⇒ void

This method returns an undefined value.

Fetch subscriptions matching this field + arguments pair And pass them off to the queue.

Parameters:

  • event_name (String)
  • args (Hash<String, Symbol => Object])

    rgs [Hash<String, Symbol => Object]

  • object (Object)
  • scope (Symbol, String)


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/graphql/subscriptions.rb', line 42

def trigger(event_name, args, object, scope: nil)
  event_name = event_name.to_s

  # Try with the verbatim input first:
  field = @schema.get_field(@schema.subscription, event_name)

  if field.nil?
    # And if it wasn't found, normalize it:
    normalized_event_name = normalize_name(event_name)
    field = @schema.get_field(@schema.subscription, normalized_event_name)
    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
  normalized_args = normalize_arguments(normalized_event_name, field, args)

  event = Subscriptions::Event.new(
    name: normalized_event_name,
    arguments: normalized_args,
    field: field,
    scope: scope,
  )
  execute_all(event, object)
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.



152
153
154
# File 'lib/graphql/subscriptions.rb', line 152

def write_subscription(query, events)
  raise GraphQL::RequiredImplementationMissingError
end