Guillaume
Guillaume

Reputation: 1537

Ruby GraphQL actioncable subscription : There was an exception - RuntimeError( Failed to implement ListWasCreated.list, tried

I'm trying to listen in real-time to the new lists created from my Vue.js application.

I have followed the graphql-ruby documentation for subscriptions.

When I'm creating a new list from the rails console, I'm receiving an error in the log:

Log

GraphqlChannel is transmitting the subscription confirmation
GraphqlChannel#execute({"query"=>"subscription listWasCreated {\n  listWasCreated {\n    list {\n      title\n      __typename\n    }\n    __typename\n  }\n}\n", "variables"=>{}, "operationName"=>"listWasCreated"})
GraphqlChannel transmitting {:result=>{"data"=>{}}, :more=>true}
GraphqlChannel is streaming from graphql-subscription:c56670f6-d59c-438a-896c-ba8c2d91d375
GraphqlChannel is streaming from graphql-event::listWasCreated:
  List Load (0.2ms)  SELECT "lists".* FROM "lists" WHERE "lists"."id" = $1 LIMIT $2  [["id", "0946b254-c86c-4a53-b6fc-b1b29e54fd70"], ["LIMIT", 1]]
There was an exception - RuntimeError(          Failed to implement ListWasCreated.list, tried:

          - `#<Class:0x00007fa0af083ed0>#list`, which did not exist
          - `List#list`, which did not exist
          - Looking up hash key `:list` or `"list"` on `#<List:0x00007fa0dfbdc748>`, but it wasn't a Hash

          To implement this field, define one of the methods above (and check for typos)
)

Ruby code

# app/graphql/types/subscription_type.rb
class Types::SubscriptionType < Types::BaseObject
  field :list_was_created, subscription: Subscriptions::ListWasCreated
end
# app/graphql/subscriptions/list_was_created.rb
module Subscriptions
  class ListWasCreated < Subscriptions::BaseSubscription
    field :list, Types::ListType, null: false

    def list
      List.last
    end
  end
end
# app/graphql/subscriptions/base_subscription.rb
module Subscriptions
  class BaseSubscription < GraphQL::Schema::Subscription
    # Hook up base classes
    object_class Types::BaseObject
    field_class Types::BaseField
    argument_class Types::BaseArgument
  end
end
# app/graphql/types/list_type.rb
module Types
  class ListType < GraphQL::Schema::Object
    field :id, ID, null: false
    field :title, String, null: false
    field :status, String, null: false
  end
end
# app/models/list.rb
class List < ApplicationRecord
  # Callbacks
  after_save :notify_subscriber

  def notify_subscriber
    ListouSchema.subscriptions.trigger(:list_was_created, {}, self)
  end
end

GQL query subscription

subscription listWasCreated {
  listWasCreated {
    list {
      title
    }
  }
}

My non-ideal solution (too messy when multiple subscriptions)

# app/graphql/types/subscription_type.rb
class Types::SubscriptionType < Types::BaseObject
  field :list_was_created, Types::ListType, null: false
  def list_was_created
    List.last
  end
end
subscription listWasCreated {
  listWasCreated {
    status
    title
  }
}
GraphqlChannel is transmitting the subscription confirmation
GraphqlChannel#execute({"query"=>"subscription listWasCreated {\n  listWasCreated {\n    status\n    title\n    __typename\n  }\n}\n", "variables"=>{}, "operationName"=>"listWasCreated"})
  List Load (0.3ms)  SELECT "lists".* FROM "lists" ORDER BY "lists"."created_at" DESC LIMIT $1  [["LIMIT", 1]]
  ↳ app/graphql/types/subscription_type.rb:12:in `list_was_created'
GraphqlChannel transmitting {:result=>{"data"=>{"listWasCreated"=>{"status"=>"online", "title"=>"2021-01-31T18:29:44+01:00", "__typename"=>"List"}}}, :more=>true}
GraphqlChannel is streaming from graphql-subscription:edf5ce43-8838-4212-a9d8-8e660bafc4be
GraphqlChannel is streaming from graphql-event::listWasCreated:
  List Load (0.2ms)  SELECT "lists".* FROM "lists" WHERE "lists"."id" = $1 LIMIT $2  [["id", "e6561161-c15b-4cf3-8979-79085a1d1a94"], ["LIMIT", 1]]
  List Load (0.2ms)  SELECT "lists".* FROM "lists" ORDER BY "lists"."created_at" DESC LIMIT $1  [["LIMIT", 1]]
  ↳ app/graphql/types/subscription_type.rb:12:in `list_was_created'
[ActionCable] Broadcasting to graphql-subscription:edf5ce43-8838-4212-a9d8-8e660bafc4be: {:result=>{"data"=>{"listWasCreated"=>{"status"=>"online", "title"=>"2021-01-31T18:30:21+01:00", "__typename"=>"List"}}}, :more=>true}
GraphqlChannel transmitting {"result"=>{"data"=>{"listWasCreated"=>{"status"=>"online", "title"=>"2021-01-31T18:30:21+01:00", "__typename"=>"List"}}}, "more"=>true} (via streamed from graphql-subscription:edf5ce43-8838-4212-a9d8-8e660bafc4be)

What I would like

Using field :list_was_created, subscription: Subscriptions::ListWasCreated in app/graphql/types/subscription_type.rb because it is less messy and I can add many fields

Upvotes: 0

Views: 810

Answers (1)

Guillaume
Guillaume

Reputation: 1537

I found the solution...

I had to add the methods subscribe and update

# app/graphql/subscriptions/list_was_created.rb
module Subscriptions
  class ListWasCreated < Subscriptions::BaseSubscription
    field :list, Types::ListType, null: false

    def list
      List.last
    end

    def subscribe
      {
        list: list
      }
    end

    def update
      {
        list: list
      }
    end
  end
end

Upvotes: 1

Related Questions