Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/provider interface #186

Merged
merged 8 commits into from
Jul 27, 2020
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
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