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'; } from 'n8n-workflow';
import { import {
ApplicationError, ApplicationError,
ErrorReporterProxy as ErrorReporter,
LoggerProxy as Logger, LoggerProxy as Logger,
toCronExpression, toCronExpression,
TriggerCloseError,
WorkflowActivationError, WorkflowActivationError,
WorkflowDeactivationError, WorkflowDeactivationError,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -238,6 +240,14 @@ export class ActiveWorkflows {
try { try {
await response.closeFunction(); await response.closeFunction();
} catch (e) { } 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}`); const error = e instanceof Error ? e : new Error(`${e}`);
throw new WorkflowDeactivationError( throw new WorkflowDeactivationError(

View file

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

View file

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

View file

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