Skip to content

Instantly share code, notes, and snippets.

@cschiewek
Last active August 29, 2019 20:44
Show Gist options
  • Save cschiewek/15e293486ffc21ae1ebf4ac13253e215 to your computer and use it in GitHub Desktop.
Save cschiewek/15e293486ffc21ae1ebf4ac13253e215 to your computer and use it in GitHub Desktop.
Rails 6 Graphql ActionCable

I can't seem to get graphql subscriptions to work with the graphql gem and ActionCable 6.

The schema is able to be introspected and the following query:

subscription buildSub($buildUuid:String!) {
  build(uuid:$buildUuid) {
    uuid
    status
    success
    lastModified
  }
}

returns correctly in GraphiQL, but it uses controller, not the channel.

When I run the same query in GraphQL playground, it returns:

{
  "error": "Could not connect to websocket endpoint ws://localhost:3030/graphql. Please check if the endpoint url is correct."
}

And I get the following in my rails logs

Started GET "/graphql" for ::1 at 2019-08-29 14:36:22 -0600
Started GET "/graphql/" [WebSocket] for ::1 at 2019-08-29 14:36:22 -0600
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
Finished "/graphql/" [WebSocket] for ::1 at 2019-08-29 14:36:22 -0600
class GraphqlChannel < ApplicationCable::Channel
def subscribed
@subscription_ids = []
end
def execute(data)
Rails.logger.warn "!!! Executing !!!"
query = data["query"]
variables = ensure_hash(data["variables"])
operation_name = data["operationName"]
context = {
# Re-implement whatever context methods you need
# in this channel or ApplicationCable::Channel
# current_user: current_user,
# Make sure the channel is in the context
channel: self,
}
result = Schema.execute({
query: query,
context: context,
variables: variables,
operation_name: operation_name
})
payload = {
result: result.subscription? ? { data: nil } : result.to_h,
more: result.subscription?,
}
# Track the subscription here so we can remove it
# on unsubscribe.
if result.context[:subscription_id]
@subscription_ids << result.context[:subscription_id]
end
transmit(payload)
end
def unsubscribed
@subscription_ids.each { |sid|
Schema.subscriptions.delete_subscription(sid)
}
end
private
def ensure_hash(ambiguous_param)
case ambiguous_param
when String
if ambiguous_param.present?
ensure_hash(JSON.parse(ambiguous_param))
else
{}
end
when Hash, ActionController::Parameters
ambiguous_param
when nil
{}
else
raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
end
end
end
class Schema < GraphQL::Schema
use GraphQL::Execution::Interpreter
use GraphQL::Subscriptions::ActionCableSubscriptions
subscription Types::SubscriptionType
end
class Subscriptions::Build < GraphQL::Schema::Subscription
argument :uuid, String, required: true
payload_type Types::BuildType
def subscribe(uuid:)
Build.find(uuid)
end
end
class Types::BuildType < GraphQL::Schema::Object
description 'A build'
field :uuid, String, null: false
field :status, String, null: false
field :success, Boolean, null: true
field :last_modified, Types::DateTimeType, null: true
end
class Types::SubscriptionType < GraphQL::Schema::Object
field :build, subscription: Subscriptions::Build
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment