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

Add fromJson and toJson options to HTTP transport #3192

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -82,6 +82,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 request payload 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 @@ -98,6 +102,10 @@ const transport = createHttpTransport({
});
```

##### `toJson`

An optional function that takes the request payload and converts it to a JSON string. 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,29 @@
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);
await makeHttpRequest({ payload: { foo: 123 } });
expect(fromJson).toHaveBeenCalledWith('{"ok":true}', { foo: 123 });
});
it('returns the value parsed by `fromJson`', async () => {
expect.assertions(1);
fromJson.mockReturnValueOnce({ result: 456 });
await expect(makeHttpRequest({ payload: { foo: 123 } })).resolves.toEqual({ result: 456 });
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { RpcTransport } from '@solana/rpc-spec';

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', () => {
makeHttpRequest({ payload: { foo: 123 } });
expect(toJson).toHaveBeenCalledWith({ foo: 123 });
});
it('uses passes the JSON string to the fetch API', () => {
toJson.mockReturnValueOnce('{"someAugmented":"jsonString"}');
makeHttpRequest({ payload: { foo: 123 } });
expect(fetchSpy).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
body: '{"someAugmented":"jsonString"}',
}),
);
});
});
9 changes: 7 additions & 2 deletions packages/rpc-transport-http/src/http-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {

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

Expand All @@ -32,7 +34,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 @@ -45,7 +47,7 @@ export function createHttpTransport(config: Config): RpcTransport {
payload,
signal,
}: Parameters<RpcTransport>[0]): Promise<RpcResponse<TResponse>> {
const body = JSON.stringify(payload);
const body = toJson ? toJson(payload) : JSON.stringify(payload);
const requestInfo = {
...dispatcherConfig,
body,
Expand All @@ -66,6 +68,9 @@ export function createHttpTransport(config: Config): RpcTransport {
statusCode: response.status,
});
}
if (fromJson) {
return fromJson(await response.text(), payload) as TResponse;
}
return await response.json();
};
}