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
more...

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

[View source]

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.
[View source]

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

[View source]

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:

  • (NotImplementedError)
[View source]

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

def delete_subscription(subscription_id)
  raise NotImplementedError
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:

  • (NotImplementedError)
[View source]

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

def deliver(subscription_id, result)
  raise NotImplementedError
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:

  • (NotImplementedError)
[View source]

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

def each_subscription_id(event)
  raise NotImplementedError
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

[View source]

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:

[View source]

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)
[View source]

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:

  • (NotImplementedError)
[View source]

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

def read_subscription(subscription_id)
  raise NotImplementedError
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)
[View source]

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.

Parameters:

Raises:

  • (NotImplementedError)
[View source]

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

def write_subscription(query, events)
  raise NotImplementedError
end