# frozen_string_literal: true module GraphQL module Streaming # Accept patches from GraphQL and send them to clients via `channel_name`. # # Patches are also issued with `query_id`. This way, clients on the same channel # can tell whether a patch is for their query or someone else's. # # When a query is finished (no more patches will be sent), the collector can # notify clients with {#close} # # @example Sending patches over ActionCable # # Use this middleware to close queries when they're finished: # MySchema.middleware << GraphQL::Streaming::ActionCableMiddleware.new # # class GraphqlChannel < ApplicationCable::Channel # # Implement `#fetch(data)`, which corresponds with GraphQLCable client # def fetch(data) # query_string = data["query"] # variables = ensure_hash(data["variables"] || {}) # # # build a collector including `query_id` # # which comes from GraphQLCable client # broadcaster = ActionCable.server.broadcaster_for(channel_name) # query_id = query_id = data["query_id"] # collector = GraphQL::Streaming::ActionCableCollector.new(query_id, broadcaster) # # context = { collector: collector } # Schema.execute(query_string, variables: variables, context: context) # end # end # # @example Tell the client to stop listening for patches # collector = GraphQL::Streaming::ActionCableCollector.new(query_id, broadcaster) # # ... # collector.close # class ActionCableCollector # @param [String] A unique identifier for this query (probably provided by the client) # @param [ActionCable::Server::Broadcasting::Broadcaster] The broadcast target for GraphQL's patches def initialize(channel, query_id) @query_id = query_id @channel = channel @closed = false end # Implements the "collector" API for DeferredExecution. # Sends `{patch: {...}, query_id: @query_id}` over `@broadcaster`. # @return [void] def patch(path:, value:) if !@closed @channel.send_graphql_payload({ query_id: @query_id, patch: { path: path, value: value, }, }) end end # Broadcast a message to terminate listeners on this query # @return [void] def close @channel.send_graphql_payload({ query_id: @query_id, close: true, }) @closed = true end end end end