fix(Postgres Trigger Node): closeFunction errors should not prevent a workflow from being deactivated (#8738)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2024-02-26 14:33:00 +01:00 committed by GitHub
parent 15490ad1d4
commit 7012577fce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 63 additions and 27 deletions

View file

@ -15,8 +15,10 @@ import type {
} from 'n8n-workflow';
import {
ApplicationError,
ErrorReporterProxy as ErrorReporter,
LoggerProxy as Logger,
toCronExpression,
TriggerCloseError,
WorkflowActivationError,
WorkflowDeactivationError,
} from 'n8n-workflow';
@ -238,6 +240,14 @@ export class ActiveWorkflows {
try {
await response.closeFunction();
} catch (e) {
if (e instanceof TriggerCloseError) {
Logger.error(
`There was a problem calling "closeFunction" on "${e.node.name}" in workflow "${workflowId}"`,
);
ErrorReporter.error(e, { extra: { target, workflowId } });
return;
}
const error = e instanceof Error ? e : new Error(`${e}`);
throw new WorkflowDeactivationError(

View file

@ -1,5 +1,5 @@
import {
NodeOperationError,
TriggerCloseError,
type IDataObject,
type INodeType,
type INodeTypeDescription,
@ -257,33 +257,42 @@ export class PostgresTrigger implements INodeType {
const cleanUpDb = async () => {
try {
await connection.none('UNLISTEN $1:name', [pgNames.channelName]);
if (triggerMode === 'createTrigger') {
const functionName = pgNames.functionName.includes('(')
? pgNames.functionName.split('(')[0]
: pgNames.functionName;
await connection.any('DROP FUNCTION IF EXISTS $1:name CASCADE', [functionName]);
const schema = this.getNodeParameter('schema', undefined, {
extractValue: true,
}) as string;
const table = this.getNodeParameter('tableName', undefined, {
extractValue: true,
}) as string;
await connection.any('DROP TRIGGER IF EXISTS $1:name ON $2:name.$3:name CASCADE', [
pgNames.triggerName,
schema,
table,
]);
try {
// check if the connection is healthy
await connection.query('SELECT 1');
} catch {
// connection already closed. Can't perform cleanup
// eslint-disable-next-line n8n-nodes-base/node-execute-block-wrong-error-thrown
throw new TriggerCloseError(this.getNode(), { level: 'warning' });
}
try {
await connection.none('UNLISTEN $1:name', [pgNames.channelName]);
if (triggerMode === 'createTrigger') {
const functionName = pgNames.functionName.includes('(')
? pgNames.functionName.split('(')[0]
: pgNames.functionName;
await connection.any('DROP FUNCTION IF EXISTS $1:name CASCADE', [functionName]);
const schema = this.getNodeParameter('schema', undefined, {
extractValue: true,
}) as string;
const table = this.getNodeParameter('tableName', undefined, {
extractValue: true,
}) as string;
await connection.any('DROP TRIGGER IF EXISTS $1:name ON $2:name.$3:name CASCADE', [
pgNames.triggerName,
schema,
table,
]);
}
} catch (error) {
// eslint-disable-next-line n8n-nodes-base/node-execute-block-wrong-error-thrown
throw new TriggerCloseError(this.getNode(), { cause: error as Error, level: 'error' });
}
connection.client.removeListener('notification', onNotification);
} catch (error) {
throw new NodeOperationError(
this.getNode(),
`Postgres Trigger Error: ${(error as Error).message}`,
);
} finally {
connection.client.removeListener('notification', onNotification);
if (!db.$pool.ending) await db.$pool.end();
}
};

View file

@ -1,7 +1,7 @@
import callsites from 'callsites';
import type { Event } from '@sentry/node';
type Level = 'warning' | 'error' | 'fatal' | 'info';
export type Level = 'warning' | 'error' | 'fatal' | 'info';
export type ReportingOptions = {
level?: Level;

View file

@ -10,6 +10,7 @@ export { WorkflowDeactivationError } from './workflow-deactivation.error';
export { WorkflowOperationError } from './workflow-operation.error';
export { SubworkflowOperationError } from './subworkflow-operation.error';
export { CliWorkflowOperationError } from './cli-subworkflow-operation.error';
export { TriggerCloseError } from './trigger-close.error';
export { NodeError } from './abstract/node.error';
export { ExecutionBaseError } from './abstract/execution-base.error';

View file

@ -0,0 +1,16 @@
import type { INode } from '../Interfaces';
import { ApplicationError, type Level } from './application.error';
interface TriggerCloseErrorOptions extends ErrorOptions {
level: Level;
}
export class TriggerCloseError extends ApplicationError {
constructor(
readonly node: INode,
{ cause, level }: TriggerCloseErrorOptions,
) {
super('Trigger Close Failed', { cause, extra: { nodeName: node.name } });
this.level = level;
}
}