refactor(core): Switch plain errors in cli to ApplicationError (#7857)

Ensure all errors in `cli` are `ApplicationError` or children of it and
contain no variables in the message, to continue normalizing all the
errors we report to Sentry

Follow-up to: https://github.com/n8n-io/n8n/pull/7839
This commit is contained in:
Iván Ovejero
2023-11-29 12:25:10 +01:00
committed by GitHub
parent 87def60979
commit c08c5cc37b
58 changed files with 277 additions and 195 deletions

View File

@@ -2,7 +2,7 @@ import 'reflect-metadata';
import { Command } from '@oclif/command';
import { ExitError } from '@oclif/errors';
import { Container } from 'typedi';
import { ErrorReporterProxy as ErrorReporter, sleep } from 'n8n-workflow';
import { ApplicationError, ErrorReporterProxy as ErrorReporter, sleep } from 'n8n-workflow';
import { BinaryDataService, InstanceSettings, ObjectStoreService } from 'n8n-core';
import type { AbstractServer } from '@/AbstractServer';
import { Logger } from '@/Logger';
@@ -127,7 +127,7 @@ export abstract class BaseCommand extends Command {
if (!isSelected && !isAvailable) return;
if (isSelected && !isAvailable) {
throw new Error(
throw new ApplicationError(
'External storage selected but unavailable. Please make external storage available by adding "s3" to `N8N_AVAILABLE_BINARY_DATA_MODES`.',
);
}
@@ -171,7 +171,7 @@ export abstract class BaseCommand extends Command {
const host = config.getEnv('externalStorage.s3.host');
if (host === '') {
throw new Error(
throw new ApplicationError(
'External storage host not configured. Please set `N8N_EXTERNAL_STORAGE_S3_HOST`.',
);
}
@@ -182,13 +182,13 @@ export abstract class BaseCommand extends Command {
};
if (bucket.name === '') {
throw new Error(
throw new ApplicationError(
'External storage bucket name not configured. Please set `N8N_EXTERNAL_STORAGE_S3_BUCKET_NAME`.',
);
}
if (bucket.region === '') {
throw new Error(
throw new ApplicationError(
'External storage bucket region not configured. Please set `N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION`.',
);
}
@@ -199,13 +199,13 @@ export abstract class BaseCommand extends Command {
};
if (credentials.accessKey === '') {
throw new Error(
throw new ApplicationError(
'External storage access key not configured. Please set `N8N_EXTERNAL_STORAGE_S3_ACCESS_KEY`.',
);
}
if (credentials.accessSecret === '') {
throw new Error(
throw new ApplicationError(
'External storage access secret not configured. Please set `N8N_EXTERNAL_STORAGE_S3_ACCESS_SECRET`.',
);
}

View File

@@ -6,6 +6,7 @@ import type { Risk } from '@/security-audit/types';
import { BaseCommand } from './BaseCommand';
import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks';
import { ApplicationError } from 'n8n-workflow';
export class SecurityAudit extends BaseCommand {
static description = 'Generate a security audit report for this n8n instance';
@@ -46,7 +47,7 @@ export class SecurityAudit extends BaseCommand {
const hint = `Valid categories are: ${RISK_CATEGORIES.join(', ')}`;
throw new Error([message, hint].join('. '));
throw new ApplicationError([message, hint].join('. '));
}
const result = await Container.get(SecurityAuditService).run(

View File

@@ -2,7 +2,7 @@ import { promises as fs } from 'fs';
import { flags } from '@oclif/command';
import { PLACEHOLDER_EMPTY_WORKFLOW_ID } from 'n8n-core';
import type { IWorkflowBase } from 'n8n-workflow';
import { ExecutionBaseError } from 'n8n-workflow';
import { ApplicationError, ExecutionBaseError } from 'n8n-workflow';
import { ActiveExecutions } from '@/ActiveExecutions';
import { WorkflowRunner } from '@/WorkflowRunner';
@@ -89,7 +89,7 @@ export class Execute extends BaseCommand {
}
if (!workflowData) {
throw new Error('Failed to retrieve workflow data for requested workflow');
throw new ApplicationError('Failed to retrieve workflow data for requested workflow');
}
if (!isWorkflowIdValid(workflowId)) {
@@ -113,7 +113,7 @@ export class Execute extends BaseCommand {
const data = await activeExecutions.getPostExecutePromise(executionId);
if (data === undefined) {
throw new Error('Workflow did not return any data!');
throw new ApplicationError('Workflow did not return any data');
}
if (data.data.resultData.error) {

View File

@@ -3,7 +3,7 @@ import fs from 'fs';
import os from 'os';
import { flags } from '@oclif/command';
import type { IRun, ITaskData } from 'n8n-workflow';
import { jsonParse, sleep } from 'n8n-workflow';
import { ApplicationError, jsonParse, sleep } from 'n8n-workflow';
import { sep } from 'path';
import { diff } from 'json-diff';
import pick from 'lodash/pick';
@@ -486,7 +486,7 @@ export class ExecuteBatch extends BaseCommand {
this.updateStatus();
}
} else {
throw new Error('Wrong execution status - cannot proceed');
throw new ApplicationError('Wrong execution status - cannot proceed');
}
});
}

View File

@@ -7,6 +7,7 @@ import type { ICredentialsDb, ICredentialsDecryptedDb } from '@/Interfaces';
import { BaseCommand } from '../BaseCommand';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import Container from 'typedi';
import { ApplicationError } from 'n8n-workflow';
export class ExportCredentialsCommand extends BaseCommand {
static description = 'Export credentials';
@@ -125,7 +126,7 @@ export class ExportCredentialsCommand extends BaseCommand {
}
if (credentials.length === 0) {
throw new Error('No credentials found with specified filters.');
throw new ApplicationError('No credentials found with specified filters');
}
if (flags.separate) {

View File

@@ -6,6 +6,7 @@ import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { BaseCommand } from '../BaseCommand';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import Container from 'typedi';
import { ApplicationError } from 'n8n-workflow';
export class ExportWorkflowsCommand extends BaseCommand {
static description = 'Export workflows';
@@ -111,7 +112,7 @@ export class ExportWorkflowsCommand extends BaseCommand {
});
if (workflows.length === 0) {
throw new Error('No workflows found with specified filters.');
throw new ApplicationError('No workflows found with specified filters');
}
if (flags.separate) {

View File

@@ -12,7 +12,7 @@ import { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { disableAutoGeneratedIds } from '@db/utils/commandHelpers';
import { BaseCommand } from '../BaseCommand';
import type { ICredentialsEncrypted } from 'n8n-workflow';
import { jsonParse } from 'n8n-workflow';
import { ApplicationError, jsonParse } from 'n8n-workflow';
import { RoleService } from '@/services/role.service';
import { UM_FIX_INSTRUCTION } from '@/constants';
import { UserRepository } from '@db/repositories/user.repository';
@@ -113,7 +113,7 @@ export class ImportCredentialsCommand extends BaseCommand {
totalImported = credentials.length;
if (!Array.isArray(credentials)) {
throw new Error(
throw new ApplicationError(
'File does not seem to contain credentials. Make sure the credentials are contained in an array.',
);
}
@@ -149,7 +149,7 @@ export class ImportCredentialsCommand extends BaseCommand {
const ownerCredentialRole = await Container.get(RoleService).findCredentialOwnerRole();
if (!ownerCredentialRole) {
throw new Error(`Failed to find owner credential role. ${UM_FIX_INSTRUCTION}`);
throw new ApplicationError(`Failed to find owner credential role. ${UM_FIX_INSTRUCTION}`);
}
this.ownerCredentialRole = ownerCredentialRole;
@@ -179,7 +179,7 @@ export class ImportCredentialsCommand extends BaseCommand {
(await Container.get(UserRepository).findOneBy({ globalRoleId: ownerGlobalRole.id }));
if (!owner) {
throw new Error(`Failed to find owner. ${UM_FIX_INSTRUCTION}`);
throw new ApplicationError(`Failed to find owner. ${UM_FIX_INSTRUCTION}`);
}
return owner;
@@ -189,7 +189,7 @@ export class ImportCredentialsCommand extends BaseCommand {
const user = await Container.get(UserRepository).findOneBy({ id: userId });
if (!user) {
throw new Error(`Failed to find user with ID ${userId}`);
throw new ApplicationError('Failed to find user', { extra: { userId } });
}
return user;

View File

@@ -1,6 +1,6 @@
import { flags } from '@oclif/command';
import type { INode, INodeCredentialsDetails } from 'n8n-workflow';
import { jsonParse } from 'n8n-workflow';
import { ApplicationError, jsonParse } from 'n8n-workflow';
import fs from 'fs';
import glob from 'fast-glob';
import { Container } from 'typedi';
@@ -24,7 +24,7 @@ import { CredentialsRepository } from '@db/repositories/credentials.repository';
function assertHasWorkflowsToImport(workflows: unknown): asserts workflows is IWorkflowToImport[] {
if (!Array.isArray(workflows)) {
throw new Error(
throw new ApplicationError(
'File does not seem to contain workflows. Make sure the workflows are contained in an array.',
);
}
@@ -35,7 +35,7 @@ function assertHasWorkflowsToImport(workflows: unknown): asserts workflows is IW
!Object.prototype.hasOwnProperty.call(workflow, 'nodes') ||
!Object.prototype.hasOwnProperty.call(workflow, 'connections')
) {
throw new Error('File does not seem to contain valid workflows.');
throw new ApplicationError('File does not seem to contain valid workflows.');
}
}
}
@@ -217,7 +217,7 @@ export class ImportWorkflowsCommand extends BaseCommand {
const ownerWorkflowRole = await Container.get(RoleService).findWorkflowOwnerRole();
if (!ownerWorkflowRole) {
throw new Error(`Failed to find owner workflow role. ${UM_FIX_INSTRUCTION}`);
throw new ApplicationError(`Failed to find owner workflow role. ${UM_FIX_INSTRUCTION}`);
}
this.ownerWorkflowRole = ownerWorkflowRole;
@@ -244,7 +244,7 @@ export class ImportWorkflowsCommand extends BaseCommand {
(await Container.get(UserRepository).findOneBy({ globalRoleId: ownerGlobalRole?.id }));
if (!owner) {
throw new Error(`Failed to find owner. ${UM_FIX_INSTRUCTION}`);
throw new ApplicationError(`Failed to find owner. ${UM_FIX_INSTRUCTION}`);
}
return owner;
@@ -254,7 +254,7 @@ export class ImportWorkflowsCommand extends BaseCommand {
const user = await Container.get(UserRepository).findOneBy({ id: userId });
if (!user) {
throw new Error(`Failed to find user with ID ${userId}`);
throw new ApplicationError('Failed to find user', { extra: { userId } });
}
return user;

View File

@@ -13,7 +13,7 @@ import type {
INodeTypes,
IRun,
} from 'n8n-workflow';
import { Workflow, NodeOperationError, sleep } from 'n8n-workflow';
import { Workflow, NodeOperationError, sleep, ApplicationError } from 'n8n-workflow';
import * as Db from '@/Db';
import * as ResponseHelper from '@/ResponseHelper';
@@ -125,8 +125,9 @@ export class Worker extends BaseCommand {
`Worker failed to find data of execution "${executionId}" in database. Cannot continue.`,
{ executionId },
);
throw new Error(
`Unable to find data of execution "${executionId}" in database. Aborting execution.`,
throw new ApplicationError(
'Unable to find data of execution in database. Aborting execution.',
{ extra: { executionId } },
);
}
const workflowId = fullExecutionData.workflowData.id!;
@@ -150,7 +151,7 @@ export class Worker extends BaseCommand {
'Worker execution failed because workflow could not be found in database.',
{ workflowId, executionId },
);
throw new Error(`The workflow with the ID "${workflowId}" could not be found`);
throw new ApplicationError('Workflow could not be found', { extra: { workflowId } });
}
staticData = workflowData.staticData;
}
@@ -408,7 +409,7 @@ export class Worker extends BaseCommand {
try {
if (!connection.isInitialized) {
// Connection is not active
throw new Error('No active database connection!');
throw new ApplicationError('No active database connection');
}
// DB ping
await connection.query('SELECT 1');