From 4894c9557b8deee2bb48cdb002b2ec2342dc577a Mon Sep 17 00:00:00 2001 From: Christian Capeans Date: Fri, 21 Jul 2023 14:31:11 +0200 Subject: [PATCH] Add generic error handlers --- .../core/data-transfer/src/engine/index.ts | 43 ++++++++++++------- .../data-transfer/src/errors/providers.ts | 2 + .../providers/local-destination/index.ts | 6 ++- .../src/strapi/providers/utils.ts | 15 ++++++- .../src/strapi/remote/handlers/utils.ts | 14 +++--- .../data-transfer/types/transfer-engine.d.ts | 8 +++- .../lib/commands/actions/transfer/action.js | 3 +- 7 files changed, 65 insertions(+), 26 deletions(-) diff --git a/packages/core/data-transfer/src/engine/index.ts b/packages/core/data-transfer/src/engine/index.ts index 2b1630185a..c1655d186b 100644 --- a/packages/core/data-transfer/src/engine/index.ts +++ b/packages/core/data-transfer/src/engine/index.ts @@ -26,8 +26,10 @@ import type { StreamItem, SchemaDiffHandler, SchemaDiffHandlerContext, - AssetsBackupErrorHandler, - AssetsBackupErrorHandlerContext, + ErrorHandler, + ErrorHandlerContext, + ErrorHandlers, + ErrorCode, } from '../../types'; import type { Diff } from '../utils/json'; @@ -41,6 +43,7 @@ import { } from './diagnostic'; import { DataTransferError } from '../errors'; import * as utils from '../utils'; +import { ProviderTransferError } from '../errors/providers'; export const TRANSFER_STAGES: ReadonlyArray = Object.freeze([ 'entities', @@ -110,18 +113,31 @@ class TransferEngine< #handlers: { schemaDiff: SchemaDiffHandler[]; - assetsBackupError: AssetsBackupErrorHandler[]; + errors: Partial; } = { schemaDiff: [], - assetsBackupError: [], + errors: {}, }; onSchemaDiff(handler: SchemaDiffHandler) { this.#handlers?.schemaDiff?.push(handler); } - onAssetsBackupError(handler: AssetsBackupErrorHandler) { - this.#handlers?.assetsBackupError?.push(handler); + addErrorHandler(handlerName: ErrorCode, handler: ErrorHandler) { + if (!this.#handlers.errors[handlerName]) { + this.#handlers.errors[handlerName] = []; + } + this.#handlers.errors[handlerName]?.push(handler); + } + + async emitResolvableError(error: Error, context?: ErrorHandlerContext) { + if (error instanceof ProviderTransferError && error.details?.details.code) { + const errorCode = error.details?.details.code as ErrorCode; + if (!this.#handlers.errors[errorCode]) { + this.#handlers.errors[errorCode] = []; + } + await utils.middleware.runMiddleware(context ?? {}, this.#handlers.errors[errorCode] ?? []); + } } // Save the currently open stream so that we can access it at any time @@ -750,20 +766,17 @@ class TransferEngine< async beforeTransfer(): Promise { const runWithDiagnostic = async (provider: IProvider) => { - const context: AssetsBackupErrorHandlerContext = {}; + const context: ErrorHandlerContext = {}; try { await provider.beforeTransfer?.(); } catch (error) { - await utils.middleware.runMiddleware( - context, - this.#handlers.assetsBackupError - ); - if (context.ignore) { - return; - } - // Error happening during the before transfer step should be considered fatal errors if (error instanceof Error) { + await this.emitResolvableError(error, context); + + if (context.ignore) { + return; + } this.panic(error); } else { this.panic( diff --git a/packages/core/data-transfer/src/errors/providers.ts b/packages/core/data-transfer/src/errors/providers.ts index d84b85083e..7f75f943d9 100644 --- a/packages/core/data-transfer/src/errors/providers.ts +++ b/packages/core/data-transfer/src/errors/providers.ts @@ -1,3 +1,4 @@ +import { ErrorCode } from '../../types'; import { DataTransferError } from './base'; import { Severity, SeverityKind } from './constants'; @@ -5,6 +6,7 @@ type ProviderStep = 'initialization' | 'validation' | 'transfer'; type ProviderErrorDetails

= { step: P; + code?: ErrorCode; } & ([U] extends [never] ? unknown : { details?: U }); export class ProviderError< diff --git a/packages/core/data-transfer/src/strapi/providers/local-destination/index.ts b/packages/core/data-transfer/src/strapi/providers/local-destination/index.ts index 7e86166228..1acb5716e4 100644 --- a/packages/core/data-transfer/src/strapi/providers/local-destination/index.ts +++ b/packages/core/data-transfer/src/strapi/providers/local-destination/index.ts @@ -195,7 +195,10 @@ class LocalStrapiDestinationProvider implements IDestinationProvider { await fse.outputFile(path.join(assetsDirectory, '.gitkeep'), ''); } catch (err) { throw new ProviderTransferError( - 'The backup folder for the assets could not be created inside the public folder. Please ensure Strapi has write permissions on the public directory' + 'The backup folder for the assets could not be created inside the public folder. Please ensure Strapi has write permissions on the public directory', + { + code: 'ASSETS_DIRECTORY_ERR', + } ); } return backupDirectory; @@ -306,7 +309,6 @@ class LocalStrapiDestinationProvider implements IDestinationProvider { }); callback(); } catch (error) { - console.log('Error uploading: ', error); callback(new Error(`Error while uploading asset ${chunk.filename} ${error}`)); } }); diff --git a/packages/core/data-transfer/src/strapi/providers/utils.ts b/packages/core/data-transfer/src/strapi/providers/utils.ts index 4d80be861c..f1e5ec24fb 100644 --- a/packages/core/data-transfer/src/strapi/providers/utils.ts +++ b/packages/core/data-transfer/src/strapi/providers/utils.ts @@ -2,10 +2,12 @@ import { randomUUID } from 'crypto'; import { RawData, WebSocket } from 'ws'; import type { Client, Server } from '../../../types/remote/protocol'; +import { ProviderErrorDetails } from '../../../dist/errors/providers'; import { ProviderError, ProviderTransferError, ProviderInitializationError, + ProviderValidationError, } from '../../errors/providers'; interface IDispatcherState { @@ -72,7 +74,18 @@ export const createDispatcher = ( if (response.uuid === uuid) { clearInterval(interval); if (response.error) { - return reject(new ProviderError('error', response.error.message)); + const message = response.error.message; + const details = response.error.details?.details as ProviderErrorDetails; + const step = response.error.details?.step; + let error = new ProviderError('error', message, details); + if (step === 'transfer') { + error = new ProviderTransferError(message, details); + } else if (step === 'validation') { + error = new ProviderValidationError(message, details); + } else if (step === 'initialization') { + error = new ProviderInitializationError(message); + } + return reject(error); } resolve(response.data ?? null); } else { diff --git a/packages/core/data-transfer/src/strapi/remote/handlers/utils.ts b/packages/core/data-transfer/src/strapi/remote/handlers/utils.ts index 10bd6227d0..26f06d6eaf 100644 --- a/packages/core/data-transfer/src/strapi/remote/handlers/utils.ts +++ b/packages/core/data-transfer/src/strapi/remote/handlers/utils.ts @@ -6,7 +6,7 @@ import { WebSocket, WebSocketServer } from 'ws'; import type { Handler, TransferState } from './abstract'; import type { Protocol } from '../../../../types'; -import { ProviderTransferError } from '../../../errors/providers'; +import { ProviderError, ProviderTransferError } from '../../../errors/providers'; import { VALID_TRANSFER_COMMANDS, ValidTransferCommand } from './constants'; import { TransferMethod } from '../constants'; @@ -138,16 +138,23 @@ export const handlerControllerFactory = }, respond(uuid, e, data) { + let details = {}; return new Promise((resolve, reject) => { if (!uuid && !e) { reject(new Error('Missing uuid for this message')); return; } + this.response = { uuid, data, e, }; + + if (e instanceof ProviderError) { + details = e.details; + } + const payload = JSON.stringify({ uuid, data: data ?? null, @@ -155,6 +162,7 @@ export const handlerControllerFactory = ? { code: e?.name ?? 'ERR', message: e?.message, + details, } : null, }); @@ -183,10 +191,6 @@ export const handlerControllerFactory = const response = JSON.parse(raw.toString()); if (response.uuid === uuid) { - if (response.error) { - return reject(new Error(response.error.message)); - } - resolve(response.data ?? null); } else { ws.once('message', onResponse); diff --git a/packages/core/data-transfer/types/transfer-engine.d.ts b/packages/core/data-transfer/types/transfer-engine.d.ts index d4d7170e0d..e6be97e4ec 100644 --- a/packages/core/data-transfer/types/transfer-engine.d.ts +++ b/packages/core/data-transfer/types/transfer-engine.d.ts @@ -18,11 +18,15 @@ export type SchemaDiffHandlerContext = { }; export type SchemaDiffHandler = Middleware; -export type AssetsBackupErrorHandlerContext = { +export type ErrorHandlerContext = { ignore?: boolean; }; -export type AssetsBackupErrorHandler = Middleware; +export type ErrorHandler = Middleware; + +export type ErrorCode = 'ASSETS_DIRECTORY_ERR'; + +export type ErrorHandlers = { [k in ErrorCode]: ErrorHandler[] }; /** * Defines the capabilities and properties of the transfer engine diff --git a/packages/core/strapi/lib/commands/actions/transfer/action.js b/packages/core/strapi/lib/commands/actions/transfer/action.js index ade3738d30..8c2f6eb548 100644 --- a/packages/core/strapi/lib/commands/actions/transfer/action.js +++ b/packages/core/strapi/lib/commands/actions/transfer/action.js @@ -151,7 +151,8 @@ module.exports = async (opts) => { engine.onSchemaDiff(getDiffHandler(engine, { force: opts.force, action: 'transfer' })); - engine.onAssetsBackupError( + engine.addErrorHandler( + 'ASSETS_DIRECTORY_ERR', getAssetsBackupHandler(engine, { force: opts.force, action: 'transfer' }) );