Sitelet https://github.com/open-rpc/client-js/commit/c3686c9
Skip to content

Commit c3686c9

Browse files
committed
feat: refactor clientjs to support err handling
and notifications Clientjs needed a refactor, because error states from failed transports and notifications would silently fail or would require exceptional handling. Additionally the refactor adds support for timeout, and eventual implementation of request strategies for choosing which transport to use in a request. This refactor exposes notifications handler, and error handler as well as a timeout handler for timing out request. The refactor also moves request resolution to be handle by the transport, and transport selection and batching start and stopping to be handled by the request manager freeing multiple transports to potentially be used at the same time.
1 parent 42260ee commit c3686c9

23 files changed

Lines changed: 1041 additions & 341 deletions

‎jest.config.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ module.exports = {
55
"restoreMocks": true,
66
"rootDir": "./src",
77
"testEnvironment": "jsdom",
8-
"preset": "ts-jest"
8+
"preset": "ts-jest",
9+
"coveragePathIgnorePatterns": ["Error.ts"],
910
}

‎package-lock.json‎

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"dependencies": {
3333
"isomorphic-fetch": "^2.2.1",
3434
"isomorphic-ws": "^4.0.1",
35+
"strict-event-emitter-types": "^2.0.0",
3536
"ws": "^7.0.0"
3637
}
3738
}

‎src/Error.test.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { convertJSONToRPCError, JSONRPCError } from "./Error";
2+
import { generateMockErrorResponse } from "./__mocks__/requestData";
3+
4+
describe("Error test", () => {
5+
6+
it("should convert payload to JSONRPC error ", () => {
7+
8+
let err = convertJSONToRPCError("message");
9+
expect(err instanceof Error).toBe(true);
10+
err = convertJSONToRPCError(generateMockErrorResponse(1, "somedata"));
11+
expect(err instanceof Error).toBe(true);
12+
});
13+
14+
it("should construct JSONRPCError", () => {
15+
const err = new JSONRPCError("test", 9999);
16+
const err2 = new JSONRPCError("test", 9999, "testdata");
17+
});
18+
19+
});

‎src/Error.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export const ERR_TIMEOUT = 7777;
2+
export const ERR_MISSIING_ID = 7878;
3+
export const ERR_UNKNOWN = 7979;
4+
5+
export class JSONRPCError extends Error {
6+
public message: string;
7+
public code: number;
8+
public data: any;
9+
constructor(message: string, code: number, data?: any) {
10+
super(message);
11+
this.message = message;
12+
this.code = code;
13+
this.data = data;
14+
}
15+
}
16+
17+
export const convertJSONToRPCError = (payload: any): JSONRPCError => {
18+
if (payload.error) {
19+
const { message, code, data } = payload.error;
20+
return new JSONRPCError(message, code, data);
21+
}
22+
return new JSONRPCError("Unknown error", ERR_UNKNOWN, payload);
23+
};

‎src/Request.ts‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
2+
export type JSONRPCRequestData = IJSONRPCData | IBatchRequest[] ;
3+
4+
export interface IJSONRPCData {
5+
internalID: string | number;
6+
request: IJSONRPCRequest | IJSONRPCNotification;
7+
}
8+
9+
export interface IBatchRequest {
10+
resolve: (data: any) => void;
11+
reject: (data: any) => void;
12+
request: IJSONRPCData; // IJSONRPCNotification | IJSONRPCRequest;
13+
}
14+
15+
export interface IJSONRPCRequest {
16+
jsonrpc: "2.0";
17+
id: string | number;
18+
method: string;
19+
params: any[] | object;
20+
}
21+
22+
export interface IJSONRPCError {
23+
code: number;
24+
message: string;
25+
data: any;
26+
}
27+
28+
export interface IJSONRPCResponse {
29+
jsonrpc: "2.0";
30+
id: string | number; // can also be null
31+
result?: any;
32+
error?: IJSONRPCError;
33+
}
34+
35+
export interface IJSONRPCNotificationResponse {
36+
jsonrpc: "2.0";
37+
id?: null | undefined;
38+
result?: any;
39+
error?: IJSONRPCError;
40+
}
41+
42+
export interface IJSONRPCNotification {
43+
jsonrpc: "2.0";
44+
id?: null | undefined;
45+
method: string;
46+
params: any[] | object;
47+
}
48+
49+
interface IRPCRequest {
50+
method: string;
51+
params: any[];
52+
type: "single";
53+
}
54+
55+
interface IBatchRPCRequest {
56+
type: "batch";
57+
batch: IJSONRPCRequest[];
58+
}
59+
60+
export type Request = IRPCRequest | IBatchRPCRequest;
61+
62+
export const isNotification = (data: IJSONRPCData): boolean => {
63+
return (data.request.id === undefined || data.request.id === null);
64+
};
65+
66+
export const getBatchRequests = (data: JSONRPCRequestData): IJSONRPCData[] => {
67+
if (data instanceof Array) {
68+
return data.filter((datum) => {
69+
const id = datum.request.request.id;
70+
return id !== null && id !== undefined;
71+
}).map((batchRequest: IBatchRequest) => {
72+
return batchRequest.request;
73+
});
74+
}
75+
return [];
76+
};
77+
export const getNotifications = (data: JSONRPCRequestData): IJSONRPCData[] => {
78+
if (data instanceof Array) {
79+
return data.filter((datum) => {
80+
return isNotification(datum.request);
81+
}).map((batchRequest: IBatchRequest) => {
82+
return batchRequest.request;
83+
});
84+
}
85+
if (isNotification(data)) {
86+
return [data];
87+
}
88+
return [];
89+
};

0 commit comments

Comments
 (0)