Skip to content

Commit

Permalink
fix(rpc): add interface for fetch
Browse files Browse the repository at this point in the history
this way the user can easily replace it with their own fetch implementation (e.g. Angular HttpClient)
  • Loading branch information
marcj committed May 28, 2024
1 parent 5c49778 commit 6035736
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 12 deletions.
1 change: 1 addition & 0 deletions packages/rpc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from './src/client/action.js';
export * from './src/client/client-direct.js';
export * from './src/client/client-websocket.js';
export * from './src/client/client.js';
export * from './src/client/http.js';
export * from './src/client/message-subject.js';
export * from './src/client/entity-state.js';
export * from './src/server/action.js';
Expand Down
2 changes: 1 addition & 1 deletion packages/rpc/src/client/client-websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function createRpcWebSocketClientProvider(baseUrl: string = typeof locati
[name: number]: number
} = { 4200: 8080 }) {
return {
provide: RpcWebSocketClient,
provide: RpcClient,
useFactory: () => new RpcWebSocketClient(webSocketFromBaseUrl(baseUrl, portMapping))
};
}
Expand Down
65 changes: 55 additions & 10 deletions packages/rpc/src/client/http.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,57 @@
import { ClientTransportAdapter } from './client.js';
import { ClientTransportAdapter, RpcClient } from './client.js';
import { TransportClientConnection } from '../transport.js';
import { RpcMessageDefinition } from '../protocol.js';
import { RpcTypes } from '../model.js';
import { HttpRpcMessage } from '../server/http.js';
import { serialize } from '@deepkit/type';

export interface RpcHttpResponseInterface {
status: number;
headers: { [name: string]: string };
body?: any;
}

export interface RpcHttpInterface {
fetch(url: string, options: {
headers: { [name: string]: string },
method: string,
body: any
}): Promise<RpcHttpResponseInterface>;
}

export class RpcHttpFetch implements RpcHttpInterface {
async fetch(url: string, options: {
headers: { [name: string]: string },
method: string,
body: any
}): Promise<RpcHttpResponseInterface> {
const res = await fetch(url, options);

return {
status: res.status,
headers: Object.fromEntries(res.headers.entries()),
body: await res.json(),
};
}
}

export function createRpcHttpClientProvider(
baseUrl: string = typeof location !== 'undefined' ? location.origin : 'http://localhost',
headers: { [name: string]: string } = {},
http?: RpcHttpInterface,
) {
return {
provide: RpcClient,
useFactory: () => new RpcClient(new RpcHttpClientAdapter(baseUrl, headers, http)),
};
}

export class RpcHttpClientAdapter implements ClientTransportAdapter {
constructor(public url: string, public headers: { [name: string]: string } = {}) {
constructor(
public url: string,
public headers: { [name: string]: string } = {},
public http: RpcHttpInterface = new RpcHttpFetch(),
) {
this.url = url.endsWith('/') ? url.slice(0, -1) : url;
}

Expand Down Expand Up @@ -44,7 +89,7 @@ export class RpcHttpClientAdapter implements ClientTransportAdapter {
const allPrimitive = messageBody.args.every(v => ['string', 'number', 'boolean', 'bigint'].includes(typeof v));
if (allPrimitive) {
for (const a of messageBody.args) {
qs.push('arg=' + encodeURIComponent(JSON.stringify(a)));
qs.push('arg=' + encodeURIComponent(String(a)));
}
method = 'GET';
} else {
Expand All @@ -55,22 +100,22 @@ export class RpcHttpClientAdapter implements ClientTransportAdapter {
throw new Error('Unsupported message type ' + message.type + ' for Http adapter');
}

const res = await fetch(this.url + '/' + path + '?' + qs.join('&'), {
const res = await this.http.fetch(this.url + '/' + path + '?' + qs.join('&'), {
headers: Object.assign({
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': String(connection.token)
'Authorization': String(connection.token),
}, this.headers),
method,
body,
});

const type = Number(res.headers.get('X-Message-Type'));
const composite = 'true' === res.headers.get('X-Message-Composite');
const routeType = Number(res.headers.get('X-Message-RouteType'));
let json = await res.json();
const type = Number(res.headers['x-message-type']);
const composite = 'true' === res.headers['x-message-composite'];
const routeType = Number(res.headers['x-message-routetype']);
let json = res.body;
if (type === RpcTypes.ResponseActionSimple) {
json = {v: json};
json = { v: json };
}
connection.read(new HttpRpcMessage(message.id, composite, type, routeType, {}, json));
},
Expand Down
3 changes: 2 additions & 1 deletion packages/rpc/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"lib": [
"es2021",
"es2022.error",
"dom"
"dom",
"dom.iterable"
],
"types": [
"dot-prop",
Expand Down

0 comments on commit 6035736

Please sign in to comment.