From a68d125991443e9e6684a302d4d0c0b0769722d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Matyas?= Date: Tue, 18 Mar 2025 17:28:33 +0100 Subject: [PATCH] Add support for passing custom callbacks to ActionCableLink --- .../src/subscriptions/ActionCableLink.ts | 17 ++++++++++++-- .../__tests__/ActionCableLinkTest.ts | 22 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/javascript_client/src/subscriptions/ActionCableLink.ts b/javascript_client/src/subscriptions/ActionCableLink.ts index 5d8ec84492..79027e8aec 100644 --- a/javascript_client/src/subscriptions/ActionCableLink.ts +++ b/javascript_client/src/subscriptions/ActionCableLink.ts @@ -4,21 +4,28 @@ import { print } from "graphql" type RequestResult = FetchResult<{ [key: string]: any; }, Record, Record> type ConnectionParams = object | ((operation: Operation) => object) +type SubscriptionCallbacks = { + connected?: (args?: { reconnected: boolean }) => void; + disconnected?: () => void; + received?: (payload: any) => void; +}; class ActionCableLink extends ApolloLink { cable: Consumer channelName: string actionName: string connectionParams: ConnectionParams + callbacks: SubscriptionCallbacks constructor(options: { - cable: Consumer, channelName?: string, actionName?: string, connectionParams?: ConnectionParams + cable: Consumer, channelName?: string, actionName?: string, connectionParams?: ConnectionParams, callbacks?: SubscriptionCallbacks }) { super() this.cable = options.cable this.channelName = options.channelName || "GraphqlChannel" this.actionName = options.actionName || "execute" this.connectionParams = options.connectionParams || {} + this.callbacks = options.callbacks || {} } // Interestingly, this link does _not_ call through to `next` because @@ -29,11 +36,12 @@ class ActionCableLink extends ApolloLink { var actionName = this.actionName var connectionParams = (typeof this.connectionParams === "function") ? this.connectionParams(operation) : this.connectionParams + var callbacks = this.callbacks var channel = this.cable.subscriptions.create(Object.assign({},{ channel: this.channelName, channelId: channelId }, connectionParams), { - connected: function() { + connected: function(args?: any) { this.perform( actionName, { @@ -44,6 +52,7 @@ class ActionCableLink extends ApolloLink { operationName: operation.operationName } ) + callbacks.connected?.(args) }, received: function(payload) { if (payload?.result?.data || payload?.result?.errors) { @@ -53,6 +62,10 @@ class ActionCableLink extends ApolloLink { if (!payload.more) { observer.complete() } + callbacks.received?.(payload) + }, + disconnected: function() { + callbacks.disconnected?.() } }) // Make the ActionCable subscription behave like an Apollo subscription diff --git a/javascript_client/src/subscriptions/__tests__/ActionCableLinkTest.ts b/javascript_client/src/subscriptions/__tests__/ActionCableLinkTest.ts index 4ba63f6846..a4314fdc8d 100644 --- a/javascript_client/src/subscriptions/__tests__/ActionCableLinkTest.ts +++ b/javascript_client/src/subscriptions/__tests__/ActionCableLinkTest.ts @@ -171,4 +171,26 @@ describe("ActionCableLink", () => { expect(subscription.params["test"]).toEqual(1) }) + + it('allows passing custom callbacks', () => { + var connected = jest.fn() + var received = jest.fn() + var disconnected = jest.fn() + + var observable = new ActionCableLink( + Object.assign(options, { callbacks: { connected, received, disconnected } }) + ).request(operation, null as any) + + // unpack the underlying subscription + var subscription: any = (observable.subscribe(() => null) as any)._cleanup + + subscription.received({ result: { data: "data 1" }, more: true }) + subscription.received({ result: { data: "data 2" }, more: false }) + subscription.disconnected() + + expect(connected).toHaveBeenCalledTimes(1) + expect(received).toHaveBeenCalledWith({ result: { data: "data 1" }, more: true }) + expect(received).toHaveBeenCalledWith({ result: { data: "data 2" }, more: false }) + expect(disconnected).toHaveBeenCalledTimes(1) + }) })