Skip to content

Commit

Permalink
Merge pull request #186 from open-rpc/feat/provider-interface
Browse files Browse the repository at this point in the history
Feat/provider interface
  • Loading branch information
shanejonas committed Jul 27, 2020
2 parents 37af797 + 5a3c194 commit a6a4103
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 131 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ A browser-compatible JSON-RPC client with multiple transports:
import { RequestManager, HTTPTransport, Client } from "@open-rpc/client-js";
const transport = new HTTPTransport("http://localhost:8545");
const client = new Client(new RequestManager([transport]));
const result = await client.request("addition", [2, 2]);
const result = await client.request({method: "addition", params: [2, 2]});
// => { jsonrpc: '2.0', id: 1, result: 4 }
```

Expand Down Expand Up @@ -55,7 +55,7 @@ emitter.on(chan1, (jsonrpcRequest) => {
});

const main = async () => {
const result = await client.request("addition", [2, 2]);
const result = await client.request({method: "addition", params: [2, 2]});
console.log(result);
};

Expand All @@ -78,7 +78,7 @@ const requestManager = new RequestManager([transport]);
const client = new Client(requestManager);

const main = async () => {
const result = await client.request("addition", [2, 2]);
const result = await client.request({method: "addition", params: [2, 2]});
console.log(result);
};

Expand All @@ -101,7 +101,7 @@ const requestManager = new RequestManager([transport]);
const client = new Client(requestManager);

const main = async () => {
const result = await client.request("addition", [2, 2]);
const result = await client.request({method: "addition", params: [2, 2]});
console.log(result);
};

Expand Down
74 changes: 52 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": " A browser-compatible JSON-RPC client with multiple transports.",
"main": "build/index.js",
"scripts": {
"build": "tsc && typedoc --out docs",
"build": "tsc && typedoc src/index.ts",
"lint": "tslint --fix -p .",
"test": "npm run lint && jest --coverage"
},
Expand All @@ -29,7 +29,7 @@
"serve-handler": "^6.1.3",
"ts-jest": "^25.0.0",
"tslint": "^6.0.0",
"typedoc": "^0.17.0",
"typedoc": "^0.17.0-3",
"typescript": "^3.4.5"
},
"dependencies": {
Expand Down
92 changes: 92 additions & 0 deletions src/Client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import RequestManager from "./RequestManager";
import { JSONRPCError } from "./Error";
import { IClient, RequestArguments, NotificationArguments } from "./ClientInterface";

/**
* OpenRPC Client JS is a browser-compatible JSON-RPC client with multiple transports and
* multiple request managers to enable features like round-robin or fallback-by-position.
*
* @example
* ```typescript
* import { RequestManager, HTTPTransport, Client } from '@open-rpc/client-js';
* const transport = new HTTPTransport('http://localhost:3333');
* const client = new Client(new RequestManager([transport]));
* const result = await client.request({method: 'addition', params: [2, 2]});
* // => { jsonrpc: '2.0', id: 1, result: 4 }
* ```
*
*/
class Client implements IClient {
public requestManager: RequestManager;
constructor(requestManager: RequestManager) {
this.requestManager = requestManager;
}

/**
* Initiates [[RequestManager.startBatch]] in order to build a batch call.
*
* Subsequent calls to [[Client.request]] will be added to the batch. Once [[Client.stopBatch]] is called, the
* promises for the [[Client.request]] will then be resolved. If the [[RequestManager]] already has a batch in
* progress, this method is a noop.
*
* @example
* myClient.startBatch();
* myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar'));
* myClient.request({method: "foo", params: ["baz"]}).then(() => console.log('foobaz'));
* myClient.stopBatch();
*/
public startBatch(): void {
return this.requestManager.startBatch();
}

/**
* Initiates [[RequestManager.stopBatch]] in order to finalize and send the batch to the underlying transport.
*
* [[Client.stopBatch]] will send the [[Client.request]] calls made since the last [[Client.startBatch]] call. For
* that reason, [[Client.startBatch]] MUST be called before [[Client.stopBatch]].
*
* @example
* myClient.startBatch();
* myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar'));
* myClient.request({method: "foo", params: ["baz"]}).then(() => console.log('foobaz'));
* myClient.stopBatch();
*/
public stopBatch(): void {
return this.requestManager.stopBatch();
}

/**
* A JSON-RPC call is represented by sending a Request object to a Server.
*
* @param requestObject.method A String containing the name of the method to be invoked. Method names that begin with the word rpc
* followed by a period character (U+002E or ASCII 46) are reserved for rpc-internal methods and extensions and
* MUST NOT be used for anything else.
* @param requestObject.params A Structured value that holds the parameter values to be used during the invocation of the method.
*
* @example
* myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar'));
*/
public async request(requestObject: RequestArguments, timeout?: number) {
if (this.requestManager.connectPromise) {
await this.requestManager.connectPromise;
}
return this.requestManager.request(requestObject, false, timeout);
}

public async notify(requestObject: NotificationArguments) {
if (this.requestManager.connectPromise) {
await this.requestManager.connectPromise;
}
return this.requestManager.request(requestObject, true);
}

public onNotification(callback: (data: any) => void) {
this.requestManager.requestChannel.addListener("notification", callback);
}

public onError(callback: (data: JSONRPCError) => void) {
this.requestManager.requestChannel.addListener("error", callback);
}
}

export default Client;
15 changes: 15 additions & 0 deletions src/ClientInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
interface Arguments {
readonly method: string;
readonly params?: readonly unknown[] | object;
}

export type RequestArguments = Arguments;

export type NotificationArguments = Arguments;

export type JSONRPCMessage = RequestArguments | NotificationArguments;

export interface IClient {
request(args: RequestArguments): Promise<unknown>;
notify(args: NotificationArguments): Promise<unknown>;
}
2 changes: 1 addition & 1 deletion src/Error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const ERR_UNKNOWN = 7979;
export class JSONRPCError extends Error {
public message: string;
public code: number;
public data: any;
public data?: unknown;
constructor(message: string, code: number, data?: any) {
super(message);
this.message = message;
Expand Down
24 changes: 12 additions & 12 deletions src/RequestManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("client-js", () => {
addMockServerTransport(emitter, "from1", "to1://local/rpc-response");
const transport = new EventEmitterTransport(emitter, "from1", "to1://local/rpc-response");
const c = new RequestManager([transport]);
const result = await c.request("foo", ["bar"]);
const result = await c.request({ method: "foo", params: ["bar"] });
expect(result.method).toEqual("foo");
expect(result.params).toEqual(["bar"]);
});
Expand All @@ -35,8 +35,8 @@ describe("client-js", () => {
addMockServerTransport(emitter, "from1", "to1://local/rpc-error");
const transport = new EventEmitterTransport(emitter, "from1", "to1://local/rpc-error");
const c = new RequestManager([transport]);
await expect(c.request("foo", ["bar"])).rejects.toThrowError("Error message");
});
await expect(c.request({ method: "foo", params: ["bar"] })).rejects.toThrowError("Error message");
});

it("can error on malformed response and recieve error", async () => {
const emitter = new EventEmitter();
Expand All @@ -48,7 +48,7 @@ describe("client-js", () => {
resolve(d);
});
});
await expect(c.request("foo", ["bar"], false, 1000))
await expect(c.request({ method: "foo", params: ["bar"] }, false, 1000))
.rejects.toThrowError("Request timeout request took longer than 1000 ms to resolve");
const formatError = await unknownError as JSONRPCError;
expect(formatError.message).toContain("Bad response format");
Expand All @@ -69,8 +69,8 @@ describe("client-js", () => {
const c = new RequestManager([transport]);
c.startBatch();
const requests = [
c.request("foo", ["bar"]),
c.request("foo", ["bar"]),
c.request({ method: "foo", params: ["bar"] }),
c.request({ method: "foo", params: ["bar"] }),
];
c.stopBatch();
await expect(Promise.all(requests)).rejects.toThrowError("Error message");
Expand All @@ -85,8 +85,8 @@ describe("client-js", () => {
const c = new RequestManager([transport]);
c.startBatch();
const requests = [
c.request("foo", []),
c.request("foo", ["bar"]),
c.request({ method: "foo", params: [] }),
c.request({ method: "foo", params: ["bar"] })
];
c.stopBatch();
const [a, b] = await Promise.all(requests);
Expand All @@ -105,8 +105,8 @@ describe("client-js", () => {
const c = new RequestManager([transport]);
c.startBatch();
const requests = [
c.request("foo", [], true),
c.request("foo", ["bar"], true),
c.request({ method: "foo", params: [] }, true),
c.request({ method: "foo", params: ["bar"] }, true),
];
c.stopBatch();
const [a, b] = await Promise.all(requests);
Expand All @@ -131,10 +131,10 @@ describe("client-js", () => {
const c = new RequestManager([transport]);
await c.connect();
c.startBatch();
c.request("foo", []);
c.request({ method: "foo", params: [] });
expect(c.batch.length).toBe(1);
c.startBatch();
c.request("foo", []);
c.request({ method: "foo", params: [] });
expect(c.batch.length).toBe(2);
});
});
Expand Down
Loading

0 comments on commit a6a4103

Please sign in to comment.