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

Commit c867faa

Browse files
committed
fix(batching): add tests
1 parent 5cc5887 commit c867faa

3 files changed

Lines changed: 76 additions & 20 deletions

File tree

‎src/RequestManager.test.ts‎

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,42 @@ describe("client-js", () => {
3939
c.request("foo", []);
4040
});
4141

42-
it("can send a request and error", () => {
42+
it("can batch a request", async () => {
43+
const transport = new EventEmitterTransport("foo://unique-uri");
44+
transport.sendData = (data) => {
45+
const result = JSON.stringify([
46+
{
47+
jsonrpc: "2.0",
48+
id: 3,
49+
result: "foo",
50+
},
51+
{
52+
jsonrpc: "2.0",
53+
id: 4,
54+
result: "bar",
55+
},
56+
]);
57+
transport.connection.emit("message", result);
58+
};
59+
60+
const c = new RequestManager([transport]);
61+
return c.connect().then(() => {
62+
c.startBatch();
63+
const requests = [
64+
c.request("foo", []),
65+
c.request("foo", []),
66+
];
67+
c.endBatch();
68+
return Promise.all(requests).then((results) => {
69+
expect(results[0]).toEqual("foo");
70+
expect(results[1]).toEqual("bar");
71+
c.close();
72+
});
73+
74+
});
75+
});
76+
77+
it("can send a request and error", async () => {
4378
const transport = new EventEmitterTransport("foo://unique-uri");
4479
const c = new RequestManager([transport]);
4580
transport.onData = (fn) => {

‎src/RequestManager.ts‎

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import ITransport from "./transports/Transport";
2+
23
let id = 1;
34

45
interface IJSONRPCRequest {
@@ -7,6 +8,24 @@ interface IJSONRPCRequest {
78
method: string;
89
params: any[] | object;
910
}
11+
interface IJSONRPCError {
12+
code: number;
13+
message: string;
14+
data: any;
15+
}
16+
17+
interface IJSONRPCResponse {
18+
jsonrpc: "2.0";
19+
id: number;
20+
result?: any;
21+
error?: IJSONRPCError;
22+
}
23+
24+
interface IJSONRPCNotification {
25+
jsonrpc: "2.0";
26+
method: string;
27+
params: any[] | object;
28+
}
1029

1130
/*
1231
** Naive Request Manager, only use 1st transport.
@@ -15,8 +34,8 @@ interface IJSONRPCRequest {
1534
*/
1635
class RequestManager {
1736
public transports: ITransport[];
37+
public connectPromise: Promise<any>;
1838
private requests: any;
19-
private connectPromise: Promise<any>;
2039
private batchStarted: boolean = false;
2140
private batch: IJSONRPCRequest[] = [];
2241

@@ -40,7 +59,6 @@ class RequestManager {
4059
}
4160

4261
public async request(method: string, params: any): Promise<any> {
43-
await this.connectPromise;
4462
return new Promise((resolve, reject) => {
4563
const i = id++;
4664
// naively grab first transport and use it
@@ -56,7 +74,7 @@ class RequestManager {
5674
params,
5775
};
5876
if (this.batchStarted) {
59-
this.batch.push(payload); // could dedupe
77+
this.batch.push(payload);
6078
} else {
6179
transport.sendData(JSON.stringify(payload));
6280
}
@@ -95,7 +113,19 @@ class RequestManager {
95113
}
96114

97115
private onData(data: string): void {
98-
const parsedData = JSON.parse(data);
116+
const parsedData: IJSONRPCResponse[] | IJSONRPCResponse = JSON.parse(data);
117+
if (Array.isArray(parsedData)) {
118+
parsedData.forEach((response) => {
119+
if (this.requests[response.id]) {
120+
if (response.error) {
121+
this.requests[response.id].reject(new Error(response.error.message));
122+
} else {
123+
this.requests[response.id].resolve(response.result);
124+
}
125+
}
126+
});
127+
return;
128+
}
99129
if (typeof parsedData.result === "undefined" && typeof parsedData.error === "undefined") {
100130
return;
101131
}

‎src/index.ts‎

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,16 @@ class Client implements IClient {
2929
}
3030

3131
/**
32-
* A JSON-RPC call is represented by sending a Request object to a Server.
33-
*
34-
* @param method A String containing the name of the method to be invoked.
35-
* Method names that begin with the word rpc followed by a
36-
* period character (U+002E or ASCII 46) are reserved for rpc-internal
37-
* methods and extensions and MUST NOT be used for anything else.
38-
* @param params A Structured value that holds the parameter values to be used during the invocation of the method.
32+
* Starts a JSON-RPC Batch call. The client will batch following requests.
33+
* This method REQUIRES endBatch be called when finished calling requests.
3934
*/
4035
public startBatch(): void {
4136
return this.requestManager.startBatch();
4237
}
4338

4439
/**
45-
* A JSON-RPC call is represented by sending a Request object to a Server. See [[RequestManager#endBatch]]
46-
*
47-
* @param method A String containing the name of the method to be invoked.
48-
* Method names that begin with the word rpc followed by a
49-
* period character (U+002E or ASCII 46) are reserved for rpc-internal
50-
* methods and extensions and MUST NOT be used for anything else.
51-
* @param params A Structured value that holds the parameter values to be used during the invocation of the method.
40+
* Ends a JSON-RPC Batch call. This client will batch requests from when startBatch
41+
* was called until endBatch. This REQUIRES startBatch to be called first.
5242
*/
5343
public endBatch(): void {
5444
return this.requestManager.endBatch();
@@ -62,7 +52,8 @@ class Client implements IClient {
6252
* methods and extensions and MUST NOT be used for anything else.
6353
* @param params A Structured value that holds the parameter values to be used during the invocation of the method.
6454
*/
65-
public request(method: string, params: any) {
55+
public async request(method: string, params: any) {
56+
await this.requestManager.connectPromise;
6657
return this.requestManager.request(method, params);
6758
}
6859
}

0 commit comments

Comments
 (0)