Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
Add fromJson and toJson options to HTTP transport
Browse files Browse the repository at this point in the history
  • Loading branch information
lorisleiva committed Sep 6, 2024
1 parent 3a681f1 commit 0690002
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-dragons-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@solana/rpc-transport-http': patch
---

Add `fromJson` and `toJson` options to the HTTP transport
8 changes: 8 additions & 0 deletions packages/rpc-transport-http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ const balances = await Promise.allSettled(
);
```

##### `fromJson`

An optional function that takes the response as a JSON string and converts it to a JSON value. The RPC request object is also provided as a second argument. When not provided, the JSON value will be accessed via the `response.json()` method of the fetch API.

##### `headers`

An object of headers to set on the request. Avoid [forbidden headers](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name). Additionally, the headers `Accept`, `Content-Length`, and `Content-Type` are disallowed.
Expand All @@ -94,6 +98,10 @@ const transport = createHttpTransport({
});
```

##### `toJson`

An optional function that takes the request payload and converts it to a JSON string. The RPC request object is also provided as a second argument. When not provided, `JSON.stringify` will be used.

##### `url`

A string representing the target endpoint. In Node, it must be an absolute URL using the `http` or `https` protocol.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { RpcTransport } from '@solana/rpc-spec';

describe('createHttpTransport and `fromJson` function', () => {
let fromJson: jest.Mock;
let fetchSpy: jest.SpyInstance;
let makeHttpRequest: RpcTransport;
beforeEach(async () => {
await jest.isolateModulesAsync(async () => {
fromJson = jest.fn();
fetchSpy = jest.spyOn(globalThis, 'fetch');
fetchSpy.mockResolvedValue({ ok: true, text: () => '{"ok":true}' });
const { createHttpTransport } =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
await import('../http-transport');
makeHttpRequest = createHttpTransport({ fromJson, url: 'http://localhost' });
});
});
it('uses the `fromJson` function to parse the response from a JSON string', async () => {
expect.assertions(1);
const request = { methodName: 'foo', params: 123 };
await makeHttpRequest(request);
expect(fromJson).toHaveBeenCalledWith('{"ok":true}', request);
});
it('returns the value parsed by `fromJson`', async () => {
expect.assertions(1);
const request = { methodName: 'foo', params: 123 };
fromJson.mockReturnValueOnce({ result: 456 });
await expect(makeHttpRequest(request)).resolves.toEqual({ result: 456 });
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { RpcTransport } from '@solana/rpc-spec';
import { createRpcMessage } from '@solana/rpc-spec-types';

describe('createHttpTransport and `toJson` function', () => {
let toJson: jest.Mock;
let fetchSpy: jest.SpyInstance;
let makeHttpRequest: RpcTransport;
beforeEach(async () => {
await jest.isolateModulesAsync(async () => {
toJson = jest.fn(value => JSON.stringify(value));
fetchSpy = jest.spyOn(globalThis, 'fetch');
fetchSpy.mockResolvedValue({ json: () => ({ ok: true }), ok: true });
const { createHttpTransport } =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
await import('../http-transport');
makeHttpRequest = createHttpTransport({ toJson, url: 'http://localhost' });
});
});
it('uses the `toJson` function to transform the payload to a JSON string', () => {
const request = { methodName: 'foo', params: 123 };
makeHttpRequest(request);
expect(toJson).toHaveBeenCalledWith(
expect.objectContaining({
...createRpcMessage(request.methodName, request.params),
id: expect.any(Number),
}),
request,
);
});
it('uses passes the JSON string to the fetch API', () => {
toJson.mockReturnValueOnce('{"someAugmented":"jsonString"}');
makeHttpRequest({ methodName: 'foo', params: 123 });
expect(fetchSpy).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
body: '{"someAugmented":"jsonString"}',
}),
);
});
});
11 changes: 8 additions & 3 deletions packages/rpc-transport-http/src/http-transport.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR, SolanaError } from '@solana/errors';
import { RpcResponse, RpcTransport } from '@solana/rpc-spec';
import { RpcRequest, RpcResponse, RpcTransport } from '@solana/rpc-spec';
import { createRpcMessage } from '@solana/rpc-spec-types';
import type Dispatcher from 'undici-types/dispatcher';

Expand All @@ -11,7 +11,9 @@ import {

type Config = Readonly<{
dispatcher_NODE_ONLY?: Dispatcher;
fromJson?: (rawResponse: string, request: RpcRequest) => RpcResponse;
headers?: AllowedHttpRequestHeaders;
toJson?: (payload: unknown, request: RpcRequest) => string;
url: string;
}>;

Expand All @@ -33,7 +35,7 @@ export function createHttpTransport(config: Config): RpcTransport {
if (__DEV__ && !__NODEJS__ && 'dispatcher_NODE_ONLY' in config) {
warnDispatcherWasSuppliedInNonNodeEnvironment();
}
const { headers, url } = config;
const { fromJson, headers, toJson, url } = config;
if (__DEV__ && headers) {
assertIsAllowedHttpRequestHeaders(headers);
}
Expand All @@ -48,7 +50,7 @@ export function createHttpTransport(config: Config): RpcTransport {
signal,
}: Parameters<RpcTransport>[0]): Promise<RpcResponse<TResponse>> {
const payload = createRpcMessage(methodName, params);
const body = JSON.stringify(payload);
const body = toJson ? toJson(payload, { methodName, params }) : JSON.stringify(payload);
const requestInfo = {
...dispatcherConfig,
body,
Expand All @@ -69,6 +71,9 @@ export function createHttpTransport(config: Config): RpcTransport {
statusCode: response.status,
});
}
if (fromJson) {
return fromJson(await response.text(), { methodName, params }) as TResponse;
}
return await response.json();
};
}

0 comments on commit 0690002

Please sign in to comment.