Skip to content

Commit

Permalink
feat: change public request interface to match provider
Browse files Browse the repository at this point in the history
BREAKING
  • Loading branch information
shanejonas committed Jul 22, 2020
1 parent 40242fe commit 2df9e47
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 37 deletions.
2 changes: 1 addition & 1 deletion 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
2 changes: 1 addition & 1 deletion 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 --out docs --includeDeclarations --excludeExternals --excludeNotExported --excludePrivate --hideGenerator --mode file --readme none",
"lint": "tslint --fix -p .",
"test": "npm run lint && jest --coverage"
},
Expand Down
4 changes: 3 additions & 1 deletion src/Error.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { ProviderRpcError } from "./ProviderInterface";

export const ERR_TIMEOUT = 7777;
export const ERR_MISSIING_ID = 7878;
export const ERR_UNKNOWN = 7979;

export class JSONRPCError extends Error {
export class JSONRPCError extends Error implements ProviderRpcError{
public message: string;
public code: number;
public data: any;
Expand Down
14 changes: 14 additions & 0 deletions src/ProviderInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface ProviderRequestArguments {
readonly method: string;
readonly params?: readonly unknown[] | object;
}

export interface Provider {
request(args: ProviderRequestArguments): Promise<any>;
}

export interface ProviderRpcError extends Error {
message: string;
code: number;
data?: unknown;
}
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
7 changes: 4 additions & 3 deletions src/RequestManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IJSONRPCRequest, IJSONRPCNotification, IBatchRequest } from "./Request"
import { JSONRPCError } from "./Error";
import StrictEventEmitter from "strict-event-emitter-types";
import { EventEmitter } from "events";
import { ProviderRequestArguments, Provider } from "./ProviderInterface";

export type RequestChannel = StrictEventEmitter<EventEmitter, IRequestEvents>;

Expand All @@ -16,7 +17,7 @@ export interface IRequestEvents {
* If a transport fails, or times out, move on to the next.
*/

class RequestManager {
class RequestManager implements Provider {
public transports: Transport[];
public connectPromise: Promise<any>;
public batch: IBatchRequest[] = [];
Expand All @@ -43,11 +44,11 @@ class RequestManager {
return this.transports[0];
}

public async request(method: string, params: any[], notification: boolean = false, timeout?: number): Promise<any> {
public async request(args: ProviderRequestArguments, notification: boolean = false, timeout?: number): Promise<any> {
const internalID = (++this.lastId).toString();
const id = notification ? null : internalID;
// naively grab first transport and use it
const payload = {request: this.makeRequest(method, params, id) , internalID};
const payload = {request: this.makeRequest(args.method, args.params || [], id) , internalID};
if (this.batchStarted) {
const result = new Promise((resolve, reject) => {
this.batch.push({ resolve, reject, request: payload });
Expand Down
2 changes: 1 addition & 1 deletion src/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { Client, RequestManager, HTTPTransport } from ".";
const t = new HTTPTransport("http://localhost:3333");
const c = new Client(new RequestManager([t]));

c.request("addition", [2, 2]).then((result: any) => {
c.request({method: "addition", params: [2, 2]}).then((result: any) => {
console.log('addition result: ', result); // tslint:disable-line
});
4 changes: 2 additions & 2 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ describe("client-js", () => {
const emitter = new EventEmitter();
const c = new Client(new RequestManager([new EventEmitterTransport(emitter, "from1", "to1")]));
expect(typeof c.request).toEqual("function");
expect(typeof c.request("my_method", null).then).toEqual("function");
expect(typeof c.request({ method: "my_method"}).then).toEqual("function");
});

it("has a notify method that returns a promise", () => {
const emitter = new EventEmitter();
const c = new Client(new RequestManager([new EventEmitterTransport(emitter, "from1", "to1")]));
expect(typeof c.request).toEqual("function");
expect(typeof c.notify("my_method", null).then).toEqual("function");
expect(typeof c.notify({method: "my_method"}).then).toEqual("function");
});

it("can recieve notifications", (done) => {
Expand Down
32 changes: 16 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import WebSocketTransport from "./transports/WebSocketTransport";
import PostMessageWindowTransport from "./transports/PostMessageWindowTransport";
import PostMessageIframeTransport from "./transports/PostMessageIframeTransport";
import { JSONRPCError } from "./Error";

interface IClient {
request(method: string, params: any): Promise<any>;
}
import { Provider, ProviderRequestArguments } from "./ProviderInterface";

/**
* OpenRPC Client JS is a browser-compatible JSON-RPC client with multiple transports and
Expand All @@ -19,12 +16,12 @@ interface IClient {
* 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(addition’, [2, 2]);
* const result = await client.request({method: 'addition', params: [2, 2]});
* // => { jsonrpc: '2.0', id: 1, result: 4 }
* ```
*
*/
class Client implements IClient {
class Client implements Provider {
public requestManager: RequestManager;
constructor(requestManager: RequestManager) {
this.requestManager = requestManager;
Expand All @@ -39,8 +36,8 @@ class Client implements IClient {
*
* @example
* myClient.startBatch();
* myClient.request("foo", ["bar"]).then(() => console.log('foobar'));
* myClient.request("foo", ["baz"]).then(() => console.log('foobaz'));
* 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 {
Expand All @@ -55,8 +52,8 @@ class Client implements IClient {
*
* @example
* myClient.startBatch();
* myClient.request("foo", ["bar"]).then(() => console.log('foobar'));
* myClient.request("foo", ["baz"]).then(() => console.log('foobaz'));
* 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 {
Expand All @@ -66,19 +63,22 @@ class Client implements IClient {
/**
* A JSON-RPC call is represented by sending a Request object to a Server.
*
* @param method A String containing the name of the method to be invoked. Method names that begin with the word rpc
* @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 params A Structured value that holds the parameter values to be used during the invocation of the method.
* @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(method: string, params: any, timeout?: number) {
public async request(requestObject: ProviderRequestArguments, timeout?: number) {
await this.requestManager.connectPromise;
return this.requestManager.request(method, params, false, timeout);
return this.requestManager.request(requestObject, false, timeout);
}

public async notify(method: string, params: any) {
public async notify(requestObject: ProviderRequestArguments) {
await this.requestManager.connectPromise;
return this.requestManager.request(method, params, true);
return this.requestManager.request(requestObject, true);
}

public onNotification(callback: (data: any) => void) {
Expand Down

0 comments on commit 2df9e47

Please sign in to comment.