Add generic error handlers

This commit is contained in:
Christian Capeans 2023-07-21 14:31:11 +02:00
parent af2694c639
commit 4894c9557b
7 changed files with 65 additions and 26 deletions

View File

@ -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<TransferStage> = Object.freeze([
'entities',
@ -110,18 +113,31 @@ class TransferEngine<
#handlers: {
schemaDiff: SchemaDiffHandler[];
assetsBackupError: AssetsBackupErrorHandler[];
errors: Partial<ErrorHandlers>;
} = {
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<void> {
const runWithDiagnostic = async (provider: IProvider) => {
const context: AssetsBackupErrorHandlerContext = {};
const context: ErrorHandlerContext = {};
try {
await provider.beforeTransfer?.();
} catch (error) {
await utils.middleware.runMiddleware<AssetsBackupErrorHandlerContext>(
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(

View File

@ -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<P extends ProviderStep = ProviderStep, U = never> = {
step: P;
code?: ErrorCode;
} & ([U] extends [never] ? unknown : { details?: U });
export class ProviderError<

View File

@ -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}`));
}
});

View File

@ -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 {

View File

@ -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<void>((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);

View File

@ -18,11 +18,15 @@ export type SchemaDiffHandlerContext = {
};
export type SchemaDiffHandler = Middleware<SchemaDiffHandlerContext>;
export type AssetsBackupErrorHandlerContext = {
export type ErrorHandlerContext = {
ignore?: boolean;
};
export type AssetsBackupErrorHandler = Middleware<AssetsBackupErrorHandlerContext>;
export type ErrorHandler<T extends ErrorHandlerContext = ErrorHandlerContext> = Middleware<T>;
export type ErrorCode = 'ASSETS_DIRECTORY_ERR';
export type ErrorHandlers = { [k in ErrorCode]: ErrorHandler[] };
/**
* Defines the capabilities and properties of the transfer engine

View File

@ -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' })
);