From 1ac504ba5b158971bd8ca062d0ea5e2be6a0b800 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); + }, + }; +}