Can't get a simple broadcast with graphql-ruby subscriptions working

1.3k views Asked by At

I have a barebones setup for update broadcasts with graphql-ruby over Action Cable.

  • The client receives the initial payload on subscription fine.
  • The client isn't receiving any updates and the update method of the subscription type isn't being called. No errors on the backend. The trigger call seems to be silently ignored. This is what I'm trying to troubleshoot / properly understand how it's supposed to work.

The docs I'm looking at:

The subscription type:

module Types
  class SubscriptionType < Types::BaseObject
    field :server_info, subscription: Subscriptions::ServerInfoSubscription, null: false
  end
end
module Subscriptions
  class ServerInfoSubscription < Subscriptions::BaseSubscription
    field :date_time, GraphQL::Types::ISO8601DateTime, null: false

    def subscribe
      { date_time: DateTime.now }
    end

    def update
      puts 'UPDATE CALLED' # Nope, it's not being called
      super
    end
  end
end

The way I'm attempting to trigger an update:

MySchema.subscriptions.trigger(:server_info, {}, { date_time: DateTime.now })

The schema is defined as:

class MySchema < GraphQL::Schema
  use GraphQL::Execution::Interpreter
  use GraphQL::Subscriptions::ActionCableSubscriptions

  subscription(Types::SubscriptionType)
end

I am using the redis adapter for the development environment in config/cable.yml:

development:
  adapter: redis
  url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
  channel_prefix: test-app_development

What am I missing? Any troubleshooting tips?

Update:

Some diagnostics:

Rails logs for the initial (and successful) subscription call:

GraphqlChannel#execute({"query"=>"subscription ServerInfoSubscription {\n  serverInfo {\n    dateTime\n    __typename\n  }\n}\n", "variables"=>{}, "operationName"=>"ServerInfoSubscription"})
GraphqlChannel transmitting {"data"=>{"serverInfo"=>{"dateTime"=>"2020-10-08T10:47:08-04:00", "__typename"=>"ServerInfoSubscriptionPayload"}}}
GraphqlChannel is streaming from graphql-subscription:9087735b-9b99-4d66-8b77-ff3622c8efe7
GraphqlChannel is streaming from graphql-event::dateTime:
Started POST "/graphql" for 192.168.64.210 at 2020-10-08 10:47:08 -0400

Checking active Redis PubSub channels:

$ redis-cli -h 192.168.64.210 -p 6379 pubsub channels
1) "test-app_development:graphql-subscription:9087735b-9b99-4d66-8b77-ff3622c8efe7"
2) "test-app_development:graphql-event::dateTime:"
3) "_action_cable_internal"

Outcome of running the trigger call in the Rails console:

irb(main):001:0> MySchema.subscriptions.trigger(:server_info, {}, { date_time: DateTime.now })
[ActionCable] Broadcasting to graphql-event::serverInfo:: "{\"date_time\":\"2020-10-08T10:54:18-04:00\",\"__sym_keys__\":[\"date_time\"]}"

The thing that stands out as obvious is that the Redis PubSub channel is called "graphql-event::dateTime:" while the trigger call broadcasts over the nonexistent "graphql-event::serverInfo:" channel. I do not understand why the actual Redis channel is called "graphql-event::dateTime" while the subscription query is on the top-level "ServerInfo" type.

2

There are 2 answers

0
Ates Goral On BEST ANSWER

I finally got it working, but couldn't pinpoint why this wasn't working:

MySchema.subscriptions.trigger(:server_info, {}, { date_time: DateTime.now })

Note that I had been passing an empty hash ({}) as the second argument.

The way I got it working is to add an argument to my query and use a matching argument (matching to the original subscription query). Something like this:

module Subscriptions
  class EventReceivedSubscription < Subscriptions::BaseSubscription
    argument :event_name, String, required: true

    field :event_name, String, null: false
    field :payload, String, null: true
  end
end
MySchema.subscriptions.trigger(
  :event_received,
  { event_name: 'greeting' },
  { payload: 'Hello' }
)

The Redis channel I can now see is:

$ redis-cli -h 192.168.64.210 -p 6379 pubsub channels
1) "test-app_development:graphql-event::eventReceived:eventName:greeting"
0
Icaka_bg On

It's because the method which you are trying to overwrite is expecting parameters, if you look at the GraphQL::Schema::Subscription

def subscribe( attrs = {})
...
end

def update( attrs = {} )
...
end

You can safely remove the :argument and define your update method with parameter which will solve the issue.

def update( _attrs = {} )
  # your logic here
end