From c0ccf63ebdbdf922be2400c5ba5963093e43a20e Mon Sep 17 00:00:00 2001 From: Nikolay Matrosov Date: Sat, 24 Feb 2024 16:42:38 +0100 Subject: [PATCH 1/2] feat: add debug ids to error --- src/errors.ts | 30 ++++++++++++++++++++++++++++++ src/index.ts | 1 + src/middleware/error-metadata.ts | 26 ++++++++++++++++++++++++++ src/utils/client-factory.ts | 2 ++ 4 files changed, 59 insertions(+) create mode 100644 src/errors.ts create mode 100644 src/middleware/error-metadata.ts diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 00000000..aea62bd7 --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,30 @@ +import { Metadata } from 'nice-grpc'; + +export class ApiError extends Error { + metadata: Metadata; + + constructor(error: Error, metadata: Metadata) { + super(error.message); + this.name = 'ApiError'; + this.stack = error.stack; + this.metadata = metadata; + } + + /** + * Getter for the request ID from the metadata. + * Will provide additional information in case of opening a support ticket. + * @returns {string | undefined} The request ID if it exists, undefined otherwise. + */ + get requestId(): string | undefined { + return this.metadata.get('x-request-id'); + } + + /** + * Getter for the server trace ID from the metadata. + * Will provide additional information in case of opening a support ticket. + * @returns {string | undefined} The server trace ID if it exists, undefined otherwise. + */ + get serverTraceId(): string | undefined { + return this.metadata.get('x-server-trace-id'); + } +} diff --git a/src/index.ts b/src/index.ts index 0bd23339..250727e9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,4 +3,5 @@ export * as cloudApi from './generated/yandex/cloud'; export * from './session'; export * from './utils/operation'; export * from './utils/decode-message'; +export * as errors from './errors'; export { WrappedServiceClientType } from './types'; diff --git a/src/middleware/error-metadata.ts b/src/middleware/error-metadata.ts new file mode 100644 index 00000000..c1386717 --- /dev/null +++ b/src/middleware/error-metadata.ts @@ -0,0 +1,26 @@ +import { CallOptions, ClientMiddleware, Metadata } from 'nice-grpc'; +import { rethrowAbortError } from 'abort-controller-x'; +import { ApiError } from '../errors'; + +export const errorMetadataMiddleware: ClientMiddleware = async function* errorMetadataMiddleware( + call, +) { + let md: Metadata | undefined; + + const options: CallOptions = { + onHeader: (header: Metadata) => { + md = header; + }, + }; + + try { + return yield* call.next(call.request, options); + } catch (error: unknown) { + rethrowAbortError(error); + if (error instanceof Error && md !== undefined) { + throw new ApiError(error, md); + } + + throw error; + } +}; diff --git a/src/utils/client-factory.ts b/src/utils/client-factory.ts index e74b0824..7cb3c940 100644 --- a/src/utils/client-factory.ts +++ b/src/utils/client-factory.ts @@ -1,7 +1,9 @@ import { createClientFactory } from 'nice-grpc'; import { deadlineMiddleware } from 'nice-grpc-client-middleware-deadline'; import { retryMiddleware } from '../middleware/retry'; +import { errorMetadataMiddleware } from '../middleware/error-metadata'; export const clientFactory = createClientFactory() + .use(errorMetadataMiddleware) .use(retryMiddleware) .use(deadlineMiddleware); From 3761ea8d5b64e41cf2ae3ce1d10877aeab7d124d Mon Sep 17 00:00:00 2001 From: Nikolay Matrosov Date: Mon, 26 Feb 2024 11:49:52 +0100 Subject: [PATCH 2/2] feat: merge options and pass them through --- src/middleware/error-metadata.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/middleware/error-metadata.ts b/src/middleware/error-metadata.ts index c1386717..75c4c40c 100644 --- a/src/middleware/error-metadata.ts +++ b/src/middleware/error-metadata.ts @@ -4,17 +4,22 @@ import { ApiError } from '../errors'; export const errorMetadataMiddleware: ClientMiddleware = async function* errorMetadataMiddleware( call, + options, ) { let md: Metadata | undefined; - - const options: CallOptions = { + const { onHeader } = options; + const callOptions: CallOptions = { + ...options, onHeader: (header: Metadata) => { md = header; + if (onHeader !== undefined) { + onHeader(header); + } }, }; try { - return yield* call.next(call.request, options); + return yield* call.next(call.request, callOptions); } catch (error: unknown) { rethrowAbortError(error); if (error instanceof Error && md !== undefined) {