From 9fba141e927b919684e9dcea3edeb73457d3fa7d Mon Sep 17 00:00:00 2001 From: Brad Jones Date: Fri, 7 Jul 2023 00:11:50 -0600 Subject: [PATCH] Support HTTP GET --- src/Client.ts | 5 +++-- src/RequestManager.ts | 6 +++--- src/transports/HTTPTransport.ts | 29 +++++++++++++++++++++++------ src/transports/Transport.ts | 7 ++++++- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/Client.ts b/src/Client.ts index 76ce0f4..a5b8fc2 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -2,6 +2,7 @@ import RequestManager from "./RequestManager"; import { JSONRPCError } from "./Error"; import { IClient, RequestArguments, NotificationArguments } from "./ClientInterface"; import { IJSONRPCNotification } from "./Request"; +import { TransportRequestOptions } from "./transports/Transport"; /** * OpenRPC Client JS is a browser-compatible JSON-RPC client with multiple transports and @@ -67,11 +68,11 @@ class Client implements IClient { * @example * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); */ - public async request(requestObject: RequestArguments, timeout?: number) { + public async request(requestObject: RequestArguments, timeout?: number, transportOptions?: TransportRequestOptions) { if (this.requestManager.connectPromise) { await this.requestManager.connectPromise; } - return this.requestManager.request(requestObject, false, timeout); + return this.requestManager.request(requestObject, false, timeout, transportOptions); } public async notify(requestObject: NotificationArguments) { diff --git a/src/RequestManager.ts b/src/RequestManager.ts index 3fcd44b..49ed33d 100644 --- a/src/RequestManager.ts +++ b/src/RequestManager.ts @@ -1,4 +1,4 @@ -import { Transport } from "./transports/Transport"; +import { Transport, TransportRequestOptions } from "./transports/Transport"; import { IJSONRPCRequest, IJSONRPCNotification, IBatchRequest } from "./Request"; import { JSONRPCError } from "./Error"; import StrictEventEmitter from "strict-event-emitter-types"; @@ -53,7 +53,7 @@ class RequestManager { return this.transports[0]; } - public async request(requestObject: JSONRPCMessage, notification: boolean = false, timeout?: number | null): Promise { + public async request(requestObject: JSONRPCMessage, notification: boolean = false, timeout?: number | null, transportOptions?: TransportRequestOptions): Promise { const internalID = this.nextID().toString(); const id = notification ? null : internalID; // naively grab first transport and use it @@ -64,7 +64,7 @@ class RequestManager { }); return result; } - return this.getPrimaryTransport().sendData(payload, timeout); + return this.getPrimaryTransport().sendData(payload, timeout, transportOptions); } public close(): void { diff --git a/src/transports/HTTPTransport.ts b/src/transports/HTTPTransport.ts index 06b8f9c..06a0712 100644 --- a/src/transports/HTTPTransport.ts +++ b/src/transports/HTTPTransport.ts @@ -1,5 +1,5 @@ import fetch from "isomorphic-fetch"; -import { Transport } from "./Transport"; +import { Transport, HTTPTransportRequestOptions } from "./Transport"; import { JSONRPCRequestData, getNotifications, @@ -13,6 +13,7 @@ interface HTTPTransportOptions { credentials?: CredentialsOption; headers?: Record; fetcher?: typeof fetch; + getQueryParamName?: string; } class HTTPTransport extends Transport { @@ -20,12 +21,14 @@ class HTTPTransport extends Transport { private readonly credentials?: CredentialsOption; private readonly headers: Headers; private readonly injectedFetcher?: typeof fetch; + private readonly getQueryParamName: string; constructor(uri: string, options?: HTTPTransportOptions) { super(); this.uri = uri; this.credentials = options && options.credentials; this.headers = HTTPTransport.setupHeaders(options && options.headers); this.injectedFetcher = options?.fetcher; + this.getQueryParamName = (options && options?.getQueryParamName) || "query"; } public connect(): Promise { return Promise.resolve(); @@ -33,19 +36,33 @@ class HTTPTransport extends Transport { public async sendData( data: JSONRPCRequestData, - timeout: number | null = null + timeout: number | null = null, + requestOptions?: HTTPTransportRequestOptions, ): Promise { + if (requestOptions === undefined) { + requestOptions = {method: "POST"}; + } const prom = this.transportRequestManager.addRequest(data, timeout); const notifications = getNotifications(data); const batch = getBatchRequests(data); const fetcher = this.injectedFetcher || fetch; try { - const result = await fetcher(this.uri, { - method: "POST", + const parsedData = JSON.stringify(this.parseData(data)); + let uri = this.uri; + const params = { + method: requestOptions.method, headers: this.headers, - body: JSON.stringify(this.parseData(data)), credentials: this.credentials, - }); + } as RequestInit; + if (requestOptions.method === 'GET') { + const q = new URLSearchParams(); + q.set(this.getQueryParamName, parsedData); + uri += "?" + q.toString(); + } + else { + params.body = parsedData; + } + const result = await fetcher(uri, params); // requirements are that notifications are successfully sent this.transportRequestManager.settlePendingRequest(notifications); if (this.onlyNotifications(data)) { diff --git a/src/transports/Transport.ts b/src/transports/Transport.ts index d18d3d2..f8da6c5 100644 --- a/src/transports/Transport.ts +++ b/src/transports/Transport.ts @@ -16,6 +16,11 @@ interface ITransportEvents { error: (data: JSONRPCError) => void; } +export type HTTPTransportRequestOptions = { + method: "GET" | "POST"; +} +export type TransportRequestOptions = HTTPTransportRequestOptions; + type TransportEventName = keyof ITransportEvents; export type TransportEventChannel = StrictEventEmitter; @@ -30,7 +35,7 @@ export abstract class Transport { public abstract connect(): Promise; public abstract close(): void; - public abstract async sendData(data: JSONRPCRequestData, timeout?: number | null): Promise; + public abstract async sendData(data: JSONRPCRequestData, timeout?: number | null, transportOptions?: TransportRequestOptions): Promise; public subscribe(event: TransportEventName, handler: ITransportEvents[TransportEventName]) { this.transportRequestManager.transportEventChannel.addListener(event, handler);