From 474954648aeacbb0ad5edd62b3ad3aecdebbbbae Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Fri, 1 Nov 2024 22:55:54 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20make=20use=20of=20the=20?= =?UTF-8?q?rpc-error=20package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 7 +- .../services/blocks/BlocksServices.ts | 6 +- .../codec/compact/CompactRpcMessageCodec.ts | 2 +- src/common/rpc/RpcMessageBatchProcessor.ts | 2 +- src/common/rpc/RpcMessageStreamProcessor.ts | 20 ++-- src/common/rpc/caller/RpcCaller.ts | 5 +- src/common/rpc/caller/TypedApiCaller.ts | 4 +- src/common/rpc/caller/error/RpcError.ts | 94 +------------------ src/common/rpc/caller/error/typed.ts | 5 +- src/common/rpc/caller/error/types.ts | 4 + src/server/uws/RpcApp.ts | 2 +- src/util/rx/BufferSubject.ts | 2 +- yarn.lock | 5 + 13 files changed, 40 insertions(+), 118 deletions(-) create mode 100644 src/common/rpc/caller/error/types.ts diff --git a/package.json b/package.json index 75399fc6..c586e114 100644 --- a/package.json +++ b/package.json @@ -79,15 +79,15 @@ "@jsonjoy.com/json-pack": "^1.1.0", "@jsonjoy.com/util": "^1.4.0", "abstract-level": "^2.0.0", + "classic-level": "^1.4.1", "fs-zoo": "^1.1.0", "memory-level": "^1.0.0", + "rpc-error": "^1.1.0", "rx-use": "^1.8.1", "sonic-forest": "^1.0.3", - "thingies": "^2.4.0", - "classic-level": "^1.4.1" + "thingies": "^2.4.0" }, "devDependencies": { - "json-joy": "^17.0.0", "@biomejs/biome": "^1.9.3", "@types/benchmark": "^2.1.5", "@types/jest": "^29.5.12", @@ -96,6 +96,7 @@ "@types/ws": "^8.5.10", "benchmark": "^2.1.4", "jest": "^29.7.0", + "json-joy": "^17.0.0", "rimraf": "^5.0.5", "rxjs": "^7.8.1", "ts-jest": "^29.1.2", diff --git a/src/__demos__/json-crdt-server/services/blocks/BlocksServices.ts b/src/__demos__/json-crdt-server/services/blocks/BlocksServices.ts index a111f3bc..c01a4cc3 100644 --- a/src/__demos__/json-crdt-server/services/blocks/BlocksServices.ts +++ b/src/__demos__/json-crdt-server/services/blocks/BlocksServices.ts @@ -111,14 +111,14 @@ export class BlocksServices { public async get(id: string) { const {store} = this; const result = await store.get(id); - if (!result) throw RpcError.fromCode(RpcErrorCodes.NOT_FOUND); + if (!result) throw RpcError.notFound(); return result; } public async view(id: string) { const {store} = this; const result = await store.get(id); - if (!result) throw RpcError.fromCode(RpcErrorCodes.NOT_FOUND); + if (!result) throw RpcError.notFound(); const model = Model.load(result.block.snapshot.blob); return model.view(); } @@ -141,7 +141,7 @@ export class BlocksServices { ) { const {store} = this; if (typeof offset !== 'number') offset = await store.seq(id); - if (typeof offset !== 'number') throw RpcError.fromCode(RpcErrorCodes.NOT_FOUND); + if (typeof offset !== 'number') throw RpcError.notFound(); let min = 0, max = 0; if (!limit || Math.round(limit) !== limit) throw RpcError.badRequest('INVALID_LIMIT'); diff --git a/src/common/codec/compact/CompactRpcMessageCodec.ts b/src/common/codec/compact/CompactRpcMessageCodec.ts index e3601fe9..21116deb 100644 --- a/src/common/codec/compact/CompactRpcMessageCodec.ts +++ b/src/common/codec/compact/CompactRpcMessageCodec.ts @@ -11,7 +11,7 @@ import type * as types from './types'; import type {TlvBinaryJsonEncoder} from '@jsonjoy.com/json-pack'; const fromJson = (arr: unknown | unknown[] | types.CompactMessage): msg.ReactiveRpcMessage => { - if (!(arr instanceof Array)) throw RpcError.fromCode(RpcErrorCodes.BAD_REQUEST); + if (!(arr instanceof Array)) throw RpcError.badRequest(); const type = arr[0]; switch (type) { case CompactMessageType.RequestComplete: { diff --git a/src/common/rpc/RpcMessageBatchProcessor.ts b/src/common/rpc/RpcMessageBatchProcessor.ts index b1018a16..59275781 100644 --- a/src/common/rpc/RpcMessageBatchProcessor.ts +++ b/src/common/rpc/RpcMessageBatchProcessor.ts @@ -1,8 +1,8 @@ import * as msg from '../messages'; -import type {RpcErrorValue} from './caller'; import {validateId, validateMethod} from './validation'; import {TypedRpcError} from './caller/error/typed'; import type {RpcCaller} from './caller/RpcCaller'; +import type {RpcErrorValue} from './caller/error/types'; export type IncomingBatchMessage = | msg.RequestDataMessage diff --git a/src/common/rpc/RpcMessageStreamProcessor.ts b/src/common/rpc/RpcMessageStreamProcessor.ts index b9bed99a..720c49d6 100644 --- a/src/common/rpc/RpcMessageStreamProcessor.ts +++ b/src/common/rpc/RpcMessageStreamProcessor.ts @@ -1,11 +1,11 @@ import * as msg from '../messages'; import {TimedQueue} from '../util/TimedQueue'; import {RpcErrorCodes, RpcError} from './caller/error/RpcError'; -import type {RpcValue} from '../messages/Value'; import {subscribeCompleteObserver} from '../util/subscribeCompleteObserver'; +import {TypedRpcError} from './caller/error/typed'; +import type {RpcValue} from '../messages/Value'; import type {RpcCaller} from './caller/RpcCaller'; import type {Call, RpcApiMap} from './caller/types'; -import {TypedRpcError} from './caller/error/typed'; type Send = (messages: (msg.ReactiveRpcServerMessage | msg.NotificationMessage)[]) => void; @@ -182,12 +182,12 @@ export class RpcMessageStreamProcessor { let call = this.activeStreamCalls.get(id); if (!call) { if (!method) { - this.sendError(id, RpcErrorCodes.NO_METHOD_SPECIFIED); + this.sendError(id, RpcErrorCodes.METHOD_INV); return; } const info = this.caller.info(method); if (!info) { - this.sendError(id, RpcErrorCodes.METHOD_NOT_FOUND); + this.sendError(id, RpcErrorCodes.METHOD_UNK); return; } if (info.isStreaming) { @@ -216,12 +216,12 @@ export class RpcMessageStreamProcessor { return; } if (!method) { - this.sendError(id, RpcErrorCodes.NO_METHOD_SPECIFIED); + this.sendError(id, RpcErrorCodes.METHOD_INV); return; } const caller = this.caller; if (!caller.exists(method)) { - this.sendError(id, RpcErrorCodes.METHOD_NOT_FOUND); + this.sendError(id, RpcErrorCodes.METHOD_UNK); return; } const {isStreaming} = caller.info(method); @@ -245,16 +245,16 @@ export class RpcMessageStreamProcessor { return; } if (!method) { - this.sendError(id, RpcErrorCodes.NO_METHOD_SPECIFIED); + this.sendError(id, RpcErrorCodes.METHOD_INV); return; } if (!this.caller.exists(method)) { - this.sendError(id, RpcErrorCodes.METHOD_NOT_FOUND); + this.sendError(id, RpcErrorCodes.METHOD_UNK); return; } const {isStreaming} = this.caller.info(method); if (!isStreaming) { - void this.sendError(id, RpcErrorCodes.INVALID_METHOD); + void this.sendError(id, RpcErrorCodes.METHOD_UNK); return; } const streamCall = this.createStreamCall(id, method, ctx); @@ -272,7 +272,7 @@ export class RpcMessageStreamProcessor { public onNotificationMessage(message: msg.NotificationMessage, ctx: Ctx): void { const {method, value} = message; - if (!method || method.length > 128) throw RpcError.fromCode(RpcErrorCodes.INVALID_METHOD); + if (!method || method.length > 128) throw RpcError.fromErrno(RpcErrorCodes.METHOD_INV); const request = value && typeof value === 'object' ? value?.data : undefined; this.caller.notification(method, request, ctx).catch(() => {}); } diff --git a/src/common/rpc/caller/RpcCaller.ts b/src/common/rpc/caller/RpcCaller.ts index 4b78acbb..ad297c19 100644 --- a/src/common/rpc/caller/RpcCaller.ts +++ b/src/common/rpc/caller/RpcCaller.ts @@ -1,6 +1,6 @@ import {firstValueFrom, from, type Observable, Subject} from 'rxjs'; import {catchError, finalize, first, map, mergeWith, share, switchMap, take, takeUntil, tap} from 'rxjs/operators'; -import {RpcError, RpcErrorCodes, type RpcErrorValue} from './error/RpcError'; +import {RpcError, RpcErrorCodes} from 'rpc-error'; import {TypedRpcError} from './error/typed'; import {RpcValue} from '../../messages/Value'; import {StaticRpcMethod} from '../methods/StaticRpcMethod'; @@ -8,6 +8,7 @@ import {BufferSubject} from '../../../util/rx/BufferSubject'; import type {Call} from './types'; import type {RpcMethod} from '../types'; import type {StreamingRpcMethod} from '../methods/StreamingRpcMethod'; +import type {RpcErrorValue} from './error/types'; export interface RpcApiCallerOptions { getMethod: (name: string) => undefined | StaticRpcMethod | StreamingRpcMethod; @@ -51,7 +52,7 @@ export class RpcCaller { public getMethodStrict(name: string): StaticRpcMethod | StreamingRpcMethod { const method = this.getMethod(name); - if (!method) throw TypedRpcError.valueFromCode(RpcErrorCodes.METHOD_NOT_FOUND); + if (!method) throw TypedRpcError.valueFromCode(RpcErrorCodes.METHOD_UNK); return method; } diff --git a/src/common/rpc/caller/TypedApiCaller.ts b/src/common/rpc/caller/TypedApiCaller.ts index 87be98c5..f29da56a 100644 --- a/src/common/rpc/caller/TypedApiCaller.ts +++ b/src/common/rpc/caller/TypedApiCaller.ts @@ -1,4 +1,4 @@ -import {RpcErrorCodes} from './error/RpcError'; +import {RpcErrorCodes} from 'rpc-error'; import {TypedRpcError} from './error/typed'; import {RpcCaller, type RpcApiCallerOptions} from './RpcCaller'; import {FunctionStreamingType, FunctionType} from '@jsonjoy.com/json-type/lib/type/classes'; @@ -85,7 +85,7 @@ export class TypedApiCaller extends RpcCal public get(id: K): MethodDefinition { const method = this.methods.get(id as string) as any; - if (!method) throw TypedRpcError.valueFromCode(RpcErrorCodes.METHOD_NOT_FOUND); + if (!method) throw TypedRpcError.valueFromCode(RpcErrorCodes.METHOD_UNK); return method; } } diff --git a/src/common/rpc/caller/error/RpcError.ts b/src/common/rpc/caller/error/RpcError.ts index 8efd7ea6..a13df72c 100644 --- a/src/common/rpc/caller/error/RpcError.ts +++ b/src/common/rpc/caller/error/RpcError.ts @@ -1,93 +1,3 @@ -import type {RpcValue} from '../../../messages/Value'; -import type {IRpcError} from './RpcErrorType'; +import {RpcError, RpcErrorCodes, IRpcError} from 'rpc-error'; -export enum RpcErrorCodes { - /** Any unknown sever error is wrapped into INTERNAL_ERROR, error 500. */ - INTERNAL_ERROR = 0, - - /** When request is not valid, e.g. when request validation fails, error 400. */ - BAD_REQUEST = 1, - - /** - * Error thrown when there was no activity on a - * stream for a long time, and timeout was reached. - */ - TIMEOUT = 2, - - /** Resource not found, error 404. */ - NOT_FOUND = 3, - - /** When operation cannot be performed due to a conflict, error 409. */ - CONFLICT = 4, - - ID_TAKEN = 5, - INVALID_METHOD = 6, - INVALID_METHOD_NAME = 7, - NO_METHOD_SPECIFIED = 8, - METHOD_NOT_FOUND = 9, - - STOP = 10, - DISCONNECT = 11, - BUFFER_OVERFLOW = 12, -} - -export type RpcErrorValue = RpcValue; - -export class RpcError extends Error implements IRpcError { - public static from(error: unknown): RpcError { - if (error instanceof RpcError) return error; - return RpcError.internal(error); - } - - public static fromCode( - errno: RpcErrorCodes, - message = '', - meta: unknown = undefined, - originalError: unknown = undefined, - ): RpcError { - const code = RpcErrorCodes[errno]; - return new RpcError(message || code, code, errno, undefined, meta || undefined, originalError); - } - - public static internal(originalError: unknown, message = 'Internal Server Error'): RpcError { - return RpcError.fromCode(RpcErrorCodes.INTERNAL_ERROR, message, undefined, originalError); - } - - public static badRequest(message = 'Bad Request'): RpcError { - return RpcError.fromCode(RpcErrorCodes.BAD_REQUEST, message); - } - - public static notFound(message = 'Not Found'): RpcError { - return RpcError.fromCode(RpcErrorCodes.NOT_FOUND, message); - } - - public static validation(message: string, meta?: unknown): RpcError { - return RpcError.fromCode(RpcErrorCodes.BAD_REQUEST, message, meta); - } - - public static isRpcError(error: unknown): error is RpcError { - return error instanceof RpcError; - } - - constructor( - public readonly message: string, - public readonly code: string | undefined, - public readonly errno: number, - public readonly errorId: string | undefined, - public readonly meta: unknown | undefined, - public readonly originalError: unknown | undefined, - ) { - super(message); - if (message === code) this.code = undefined; - Object.setPrototypeOf(this, RpcError.prototype); - } - - public toJson(): IRpcError { - const err: IRpcError = {message: this.message}; - if (this.code) err.code = this.code; - if (this.errno) err.errno = this.errno; - if (this.errorId) err.errorId = this.errorId; - if (this.meta) err.meta = this.meta; - return err; - } -} +export {RpcError, RpcErrorCodes, IRpcError}; diff --git a/src/common/rpc/caller/error/typed.ts b/src/common/rpc/caller/error/typed.ts index cb7d8e00..5a2c04e8 100644 --- a/src/common/rpc/caller/error/typed.ts +++ b/src/common/rpc/caller/error/typed.ts @@ -1,6 +1,7 @@ -import {RpcError, type RpcErrorCodes, type RpcErrorValue} from './RpcError'; +import {RpcError, type RpcErrorCodes} from 'rpc-error'; import {RpcValue} from '../../../messages/Value'; import {RpcErrorType} from './RpcErrorType'; +import type {RpcErrorValue} from './types'; /** * @protected @@ -21,7 +22,7 @@ export class TypedRpcError { } public static valueFromCode(errno: RpcErrorCodes, message?: string): RpcErrorValue { - return TypedRpcError.value(RpcError.fromCode(errno, message)); + return TypedRpcError.value(RpcError.fromErrno(errno, message)); } public static internalErrorValue(originalError: unknown): RpcErrorValue { diff --git a/src/common/rpc/caller/error/types.ts b/src/common/rpc/caller/error/types.ts new file mode 100644 index 00000000..d211821b --- /dev/null +++ b/src/common/rpc/caller/error/types.ts @@ -0,0 +1,4 @@ +import type {RpcError} from 'rpc-error'; +import type {RpcValue} from '../../../messages/Value'; + +export type RpcErrorValue = RpcValue; diff --git a/src/server/uws/RpcApp.ts b/src/server/uws/RpcApp.ts index 2fc74cf1..a6fff02b 100644 --- a/src/server/uws/RpcApp.ts +++ b/src/server/uws/RpcApp.ts @@ -20,7 +20,7 @@ import type {RpcCaller} from '../../common/rpc/caller/RpcCaller'; const HDR_BAD_REQUEST = Buffer.from('400 Bad Request', 'utf8'); const HDR_NOT_FOUND = Buffer.from('404 Not Found', 'utf8'); -const ERR_NOT_FOUND = RpcError.fromCode(RpcErrorCodes.NOT_FOUND, 'Not Found'); +const ERR_NOT_FOUND = RpcError.fromCode('NOT_FOUND', 'Not Found'); const noop = () => {}; diff --git a/src/util/rx/BufferSubject.ts b/src/util/rx/BufferSubject.ts index fd5bb8fd..f2ae62d3 100644 --- a/src/util/rx/BufferSubject.ts +++ b/src/util/rx/BufferSubject.ts @@ -26,7 +26,7 @@ export class BufferSubject extends Subject { public next(value: T): void { if (this.isBuffering) { if (this.buffer.length >= this.bufferSize) { - this.error(RpcError.fromCode(RpcErrorCodes.BUFFER_OVERFLOW)); + this.error(RpcError.fromErrno(RpcErrorCodes.OVERFLOW)); return; } this.buffer.push(value); diff --git a/yarn.lock b/yarn.lock index 3b678197..7a7ab134 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2829,6 +2829,11 @@ rimraf@^5.0.5: dependencies: glob "^10.3.7" +rpc-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/rpc-error/-/rpc-error-1.1.0.tgz#ce38ef99f416b562da7008cb04d88d34dbacaeff" + integrity sha512-RNrbrCi0EOw9kNRqrCVBu7oEvekKt35tzW333mEzZvr4i+WZ/lp4c34l2FtIwmfrqdP+lkubZTdI9aVqN5tTLQ== + rx-use@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/rx-use/-/rx-use-1.8.1.tgz#d6dc272eed2197fe785b4b678a7220d856cff1d3"