Skip to content

Commit ea67aad

Browse files
authored
Merge pull request #444 from streamich/typed-rpc-client
RPC client types
2 parents dd9daf0 + 2f258ab commit ea67aad

31 files changed

+630
-339
lines changed

src/reactive-rpc/browser/createBinaryWsRpcClient.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import {RpcPersistentClient, WebSocketChannel} from '../common';
44
import {RpcCodec} from '../common/codec/RpcCodec';
55
import {BinaryRpcMessageCodec} from '../common/codec/binary';
66

7-
export const createBinaryWsRpcClient = (url: string) => {
7+
/**
8+
* Constructs a JSON Reactive RPC client.
9+
* @param url RPC endpoint.
10+
* @param token Authentication token.
11+
* @returns An RPC client.
12+
*/
13+
export const createBinaryWsRpcClient = (url: string, token: string) => {
814
const writer = new Writer(1024 * 4);
915
const msg = new BinaryRpcMessageCodec();
1016
const req = new CborJsonValueCodec(writer);
@@ -14,7 +20,7 @@ export const createBinaryWsRpcClient = (url: string) => {
1420
channel: {
1521
newChannel: () =>
1622
new WebSocketChannel({
17-
newSocket: () => new WebSocket(url, [codec.specifier()]),
23+
newSocket: () => new WebSocket(url, [codec.specifier(), token]),
1824
}),
1925
},
2026
});

src/reactive-rpc/browser/createJsonWsRpcClient.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import {RpcPersistentClient, WebSocketChannel} from '../common';
44
import {RpcCodec} from '../common/codec/RpcCodec';
55
import {CompactRpcMessageCodec} from '../common/codec/compact';
66

7-
export const createJsonWsRpcClient = (url: string) => {
7+
/**
8+
* Constructs a JSON Reactive RPC client.
9+
* @param url RPC endpoint.
10+
* @param token Authentication token.
11+
* @returns An RPC client.
12+
*/
13+
export const createJsonWsRpcClient = (url: string, token: string) => {
814
const writer = new Writer(1024 * 4);
915
const msg = new CompactRpcMessageCodec();
1016
const req = new JsonJsonValueCodec(writer);
@@ -14,7 +20,7 @@ export const createJsonWsRpcClient = (url: string) => {
1420
channel: {
1521
newChannel: () =>
1622
new WebSocketChannel({
17-
newSocket: () => new WebSocket(url, [codec.specifier()]),
23+
newSocket: () => new WebSocket(url, [codec.specifier(), token]),
1824
}),
1925
},
2026
});

src/reactive-rpc/common/codec/compact/__tests__/smoke-tests.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ for (const jsonCodec of codecList) {
8484
});
8585

8686
test('Response Error typed', () => {
87-
const value = RpcError.internalErrorValue();
87+
const value = RpcError.internalErrorValue(null);
8888
const message = new ResponseErrorMessage(123, value);
8989
const encoded = codec.encode(jsonCodec, [message]);
9090
const decoded1 = jsonCodec.decoder.read(encoded);

src/reactive-rpc/common/rpc/RpcMessageBatchProcessor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export class RpcMessageBatchProcessor<Ctx = unknown> {
5959
}
6060
return result;
6161
} catch (error) {
62-
const value = RpcError.internalErrorValue();
62+
const value = RpcError.internalErrorValue(error);
6363
return [new msg.ResponseErrorMessage(-1, value)];
6464
}
6565
}

src/reactive-rpc/common/rpc/__tests__/RpcMessageStreamProcessor.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const setup = (
5151
isStreaming: false,
5252
call: async () => {
5353
// tslint:disable-next-line:no-string-throw
54-
throw RpcError.internal('this promise can throw');
54+
throw RpcError.internal(null, 'this promise can throw');
5555
},
5656
},
5757
promiseDelay: {
@@ -64,7 +64,7 @@ const setup = (
6464
error: {
6565
isStreaming: false,
6666
call: async () => {
67-
throw RpcError.internal('this promise can throw');
67+
throw RpcError.internal(null, 'this promise can throw');
6868
},
6969
},
7070
emitOnceSync: {
@@ -635,7 +635,7 @@ describe('pre-call checks', () => {
635635

636636
test('fails call when pre-call checks fail', async () => {
637637
const onPreCall = jest.fn(async (request) => {
638-
throw RpcError.internal('fail...');
638+
throw RpcError.internal(null, 'fail...');
639639
});
640640
const {server, send} = setup(
641641
{},

src/reactive-rpc/common/rpc/__tests__/sample-api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const double: IStaticRpcMethod<object, {num: number}, {num: number}> = {
7676
const error: IStaticRpcMethod<object, void, void> = {
7777
isStreaming: false,
7878
call: async () => {
79-
throw new RpcError('this promise can throw', '', 0, '', undefined);
79+
throw new RpcError('this promise can throw', '', 0, '', undefined, undefined);
8080
},
8181
};
8282

@@ -96,7 +96,7 @@ const streamError: IStreamingRpcMethod<object, void, void> = {
9696
call$: () =>
9797
from(
9898
(async () => {
99-
throw RpcError.internal('Stream always errors');
99+
throw RpcError.internal(null, 'Stream always errors');
100100
})(),
101101
),
102102
};

src/reactive-rpc/common/rpc/caller/RpcCaller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ export class RpcCaller<Ctx = unknown> {
8686
* @returns Response data.
8787
*/
8888
public async call(name: string, request: unknown, ctx: Ctx): Promise<Value<unknown>> {
89+
const method = this.getMethodStrict(name);
90+
this.validate(method, request);
8991
try {
90-
const method = this.getMethodStrict(name);
91-
this.validate(method, request);
9292
const preCall = method.onPreCall;
9393
if (preCall) await preCall(ctx, request);
9494
const data = await method.call(request, ctx);

src/reactive-rpc/common/rpc/caller/TypeRouterCaller.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,11 @@ export class TypeRouterCaller<Router extends TypeRouter<any>, Ctx = unknown> ext
3030
public readonly req: {[K in keyof Routes<Router>]: MethodReq<Routes<Router>[K]>} = null as any;
3131
public readonly res: {[K in keyof Routes<Router>]: MethodRes<Routes<Router>[K]>} = null as any;
3232

33-
public get<K extends keyof Routes<Router>>(id: K): MethodDefinition<Ctx, Routes<Router>[K]> {
33+
public get<K extends keyof Routes<Router>>(id: K): MethodDefinition<Ctx, Routes<Router>[K]> | undefined {
3434
let method = this.methods.get(id as string) as any;
3535
if (method) return method;
3636
const fn = this.router.routes[id as string];
37-
// TODO: do this check without relying on constructor and importing the `FunctionType` class.
38-
if (!fn || !(fn instanceof FunctionType || fn instanceof FunctionStreamingType))
39-
throw RpcError.valueFromCode(RpcErrorCodes.METHOD_NOT_FOUND, `Type [alias = ${id as string}] is not a function.`);
37+
if (!fn || !(fn instanceof FunctionType || fn instanceof FunctionStreamingType)) return undefined;
4038
const validator = fn.req.validator('object');
4139
const requestSchema = (fn.req as AbstractType<Schema>).getSchema();
4240
const isRequestVoid = requestSchema.__t === 'const' && requestSchema.value === undefined;

src/reactive-rpc/common/rpc/caller/__tests__/RpcApiCaller.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('static calls', () => {
4646
},
4747
});
4848
const [, error] = await of(caller.call('test', {}, {}));
49-
expect(error).toEqual(RpcError.internalErrorValue());
49+
expect(error).toEqual(RpcError.internalErrorValue(null));
5050
});
5151
});
5252

@@ -95,10 +95,10 @@ describe('streaming calls', () => {
9595
});
9696

9797
const [, error1] = await of(caller.call('test', {}, {}));
98-
expect(error1).toEqual(RpcError.internalErrorValue());
98+
expect(error1).toEqual(RpcError.internalErrorValue(null));
9999

100100
const [, error2] = await of(Rx.firstValueFrom(caller.call$('test', Rx.of(undefined), {})));
101-
expect(error2).toEqual(RpcError.internalErrorValue());
101+
expect(error2).toEqual(RpcError.internalErrorValue(null));
102102
});
103103
});
104104

src/reactive-rpc/common/rpc/caller/error/RpcError.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,21 @@ export type RpcErrorValue = Value<RpcError>;
3636
export class RpcError extends Error implements IRpcError {
3737
public static from(error: unknown) {
3838
if (error instanceof RpcError) return error;
39-
return RpcError.internal();
39+
return RpcError.internal(error);
4040
}
4141

42-
public static fromCode(errno: RpcErrorCodes, message: string = '', meta: unknown = undefined): RpcError {
42+
public static fromCode(
43+
errno: RpcErrorCodes,
44+
message: string = '',
45+
meta: unknown = undefined,
46+
originalError: unknown = undefined,
47+
): RpcError {
4348
const code = RpcErrorCodes[errno];
44-
return new RpcError(message || code, code, errno, undefined, meta || undefined);
49+
return new RpcError(message || code, code, errno, undefined, meta || undefined, originalError);
4550
}
4651

47-
public static internal(message: string = 'Internal Server Error'): RpcError {
48-
return RpcError.fromCode(RpcErrorCodes.INTERNAL_ERROR, message);
52+
public static internal(originalError: unknown, message: string = 'Internal Server Error'): RpcError {
53+
return RpcError.fromCode(RpcErrorCodes.INTERNAL_ERROR, message, undefined, originalError);
4954
}
5055

5156
public static badRequest(): RpcError {
@@ -60,7 +65,7 @@ export class RpcError extends Error implements IRpcError {
6065
return new Value(error, RpcErrorType);
6166
}
6267

63-
public static valueFrom(error: unknown, def = RpcError.internalErrorValue()): RpcErrorValue {
68+
public static valueFrom(error: unknown, def = RpcError.internalErrorValue(error)): RpcErrorValue {
6469
if (error instanceof Value && error.data instanceof RpcError && error.type === RpcErrorType) return error;
6570
if (error instanceof RpcError) return RpcError.value(error);
6671
return def;
@@ -70,8 +75,8 @@ export class RpcError extends Error implements IRpcError {
7075
return RpcError.value(RpcError.fromCode(errno, message));
7176
}
7277

73-
public static internalErrorValue(): RpcErrorValue {
74-
return RpcError.value(RpcError.internal());
78+
public static internalErrorValue(originalError: unknown): RpcErrorValue {
79+
return RpcError.value(RpcError.internal(originalError));
7580
}
7681

7782
public static isRpcError(error: unknown): error is RpcError {
@@ -84,6 +89,7 @@ export class RpcError extends Error implements IRpcError {
8489
public readonly errno: number,
8590
public readonly errorId: string | undefined,
8691
public readonly meta: unknown | undefined,
92+
public readonly originalError: unknown | undefined,
8793
) {
8894
super(message);
8995
if (message === code) this.code = undefined;

0 commit comments

Comments
 (0)