From fd5fc496867f9f14a58a9e32a2567a88f2d1e3fd Mon Sep 17 00:00:00 2001 From: Steven Luscher Date: Wed, 2 Oct 2024 21:57:09 +0000 Subject: [PATCH] A channel augmenter that encodes and decodes messages as JSON --- packages/rpc-subscriptions/README.md | 8 ++++- .../__tests__/rpc-subscriptions-json-test.ts | 30 +++++++++++++++++++ .../src/rpc-subscriptions-json.ts | 26 ++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 packages/rpc-subscriptions/src/__tests__/rpc-subscriptions-json-test.ts create mode 100644 packages/rpc-subscriptions/src/rpc-subscriptions-json.ts diff --git a/packages/rpc-subscriptions/README.md b/packages/rpc-subscriptions/README.md index 0a882fdff66..97e49ae19e4 100644 --- a/packages/rpc-subscriptions/README.md +++ b/packages/rpc-subscriptions/README.md @@ -14,4 +14,10 @@ # @solana/rpc-subscriptions -TODO +This package contains types that implement RPC subscriptions as required by the Solana RPC. Additionally, it incorporates some useful defaults that make working with subscriptions easier, more performant, and more reliable. It can be used standalone, but it is also exported as part of the Solana JavaScript SDK [`@solana/web3.js@rc`](https://github.com/solana-labs/solana-web3.js/tree/master/packages/library). + +## Functions + +### `getRpcSubscriptionsChannelWithJSONSerialization(channel)` + +Given an `RpcSubscriptionsChannel`, will return a new channel that parses data published to the `'message'` channel as JSON, and JSON-stringifies messages sent via the `send(message)` method. diff --git a/packages/rpc-subscriptions/src/__tests__/rpc-subscriptions-json-test.ts b/packages/rpc-subscriptions/src/__tests__/rpc-subscriptions-json-test.ts new file mode 100644 index 00000000000..891cbb85dd5 --- /dev/null +++ b/packages/rpc-subscriptions/src/__tests__/rpc-subscriptions-json-test.ts @@ -0,0 +1,30 @@ +import { RpcSubscriptionsChannel } from '@solana/rpc-subscriptions-spec'; + +import { getRpcSubscriptionsChannelWithJSONSerialization } from '../rpc-subscriptions-json'; + +describe('getRpcSubscriptionsChannelWithJSONSerialization', () => { + let mockOn: jest.Mock; + let channel: RpcSubscriptionsChannel; + function receiveMessage(message: unknown) { + mockOn.mock.calls.filter(([type]) => type === 'message').forEach(([_, listener]) => listener(message)); + } + beforeEach(() => { + mockOn = jest.fn(); + channel = { + on: mockOn, + send: jest.fn(), + }; + }); + it('forwards JSON-serialized messages to the underlying channel', () => { + const channelWithJSONSerialization = getRpcSubscriptionsChannelWithJSONSerialization(channel); + channelWithJSONSerialization.send('hello'); + expect(channel.send).toHaveBeenCalledWith(JSON.stringify('hello')); + }); + it('deserializes messages received from the underlying channel as JSON', () => { + const channelWithJSONSerialization = getRpcSubscriptionsChannelWithJSONSerialization(channel); + const messageListener = jest.fn(); + channelWithJSONSerialization.on('message', messageListener); + receiveMessage(JSON.stringify('hello')); + expect(messageListener).toHaveBeenCalledWith('hello'); + }); +}); diff --git a/packages/rpc-subscriptions/src/rpc-subscriptions-json.ts b/packages/rpc-subscriptions/src/rpc-subscriptions-json.ts new file mode 100644 index 00000000000..ed7b29e4a5e --- /dev/null +++ b/packages/rpc-subscriptions/src/rpc-subscriptions-json.ts @@ -0,0 +1,26 @@ +import { RpcSubscriptionsChannel } from '@solana/rpc-subscriptions-spec'; + +export function getRpcSubscriptionsChannelWithJSONSerialization( + channel: RpcSubscriptionsChannel, +): RpcSubscriptionsChannel { + return { + ...channel, + on(type, listener, options) { + if (type !== 'message') { + return channel.on(type, listener, options); + } + return channel.on( + 'message', + function deserializingListener(message: string) { + const deserializedMessage = JSON.parse(message); + listener(deserializedMessage); + }, + options, + ); + }, + send(message) { + const serializedMessage = JSON.stringify(message); + return channel.send(serializedMessage); + }, + }; +}