fix(EmailReadImap Node): Fix issue that crashed process if node was configured wrong (#3079)

* 🐛 Fix issue that IMAP node can crash n8n

* 👕 Fix lint issue
This commit is contained in:
Jan Oberhauser 2022-04-02 17:33:31 +02:00 committed by GitHub
parent 2c72584b55
commit 85f15d4989
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 8 deletions

View file

@ -682,6 +682,15 @@ export class ActiveWorkflowRunner {
(error) => console.error(error), (error) => console.error(error),
); );
}; };
returnFunctions.emitError = async (error: Error): Promise<void> => {
await this.activeWorkflows?.remove(workflowData.id.toString());
this.activationErrors[workflowData.id.toString()] = {
time: new Date().getTime(),
error: {
message: error.message,
},
};
};
return returnFunctions; return returnFunctions;
}; };
} }

View file

@ -1750,6 +1750,9 @@ export function getExecuteTriggerFunctions(
emit: (data: INodeExecutionData[][]): void => { emit: (data: INodeExecutionData[][]): void => {
throw new Error('Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!'); throw new Error('Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!');
}, },
emitError: (error: Error): void => {
throw new Error('Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!');
},
async getCredentials(type: string): Promise<ICredentialDataDecryptedObject | undefined> { async getCredentials(type: string): Promise<ICredentialDataDecryptedObject | undefined> {
return getCredentials(workflow, node, type, additionalData, mode); return getCredentials(workflow, node, type, additionalData, mode);
}, },

View file

@ -1,13 +1,15 @@
import { ITriggerFunctions } from 'n8n-core'; import { ITriggerFunctions } from 'n8n-core';
import { import {
createDeferredPromise,
IBinaryData, IBinaryData,
IBinaryKeyData, IBinaryKeyData,
IDataObject, IDataObject,
IDeferredPromise,
INodeExecutionData, INodeExecutionData,
INodeType, INodeType,
INodeTypeDescription, INodeTypeDescription,
ITriggerResponse, ITriggerResponse,
LoggerProxy, LoggerProxy as Logger,
NodeOperationError, NodeOperationError,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -25,10 +27,6 @@ import {
import * as lodash from 'lodash'; import * as lodash from 'lodash';
import {
LoggerProxy as Logger
} from 'n8n-workflow';
export class EmailReadImap implements INodeType { export class EmailReadImap implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
displayName: 'EmailReadImap', displayName: 'EmailReadImap',
@ -377,6 +375,8 @@ export class EmailReadImap implements INodeType {
return newEmails; return newEmails;
}; };
const returnedPromise: IDeferredPromise<void> | undefined = await createDeferredPromise<void>();
const establishConnection = (): Promise<ImapSimple> => { const establishConnection = (): Promise<ImapSimple> => {
let searchCriteria = [ let searchCriteria = [
@ -425,7 +425,11 @@ export class EmailReadImap implements INodeType {
} }
} catch (error) { } catch (error) {
Logger.error('Email Read Imap node encountered an error fetching new emails', { error }); Logger.error('Email Read Imap node encountered an error fetching new emails', { error });
throw error; // Wait with resolving till the returnedPromise got resolved, else n8n will be unhappy
// if it receives an error before the workflow got activated
returnedPromise.promise().then(() => {
this.emitError(error as Error);
});
} }
} }
}, },
@ -475,10 +479,12 @@ export class EmailReadImap implements INodeType {
await connection.end(); await connection.end();
} }
// Resolve returned-promise so that waiting errors can be emitted
returnedPromise.resolve();
return { return {
closeFunction, closeFunction,
}; };
} }
} }

View file

@ -683,6 +683,7 @@ export interface ITriggerFunctions {
data: INodeExecutionData[][], data: INodeExecutionData[][],
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>, responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
): void; ): void;
emitError(error: Error, responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>): void;
getCredentials(type: string): Promise<ICredentialDataDecryptedObject | undefined>; getCredentials(type: string): Promise<ICredentialDataDecryptedObject | undefined>;
getMode(): WorkflowExecuteMode; getMode(): WorkflowExecuteMode;
getActivationMode(): WorkflowActivateMode; getActivationMode(): WorkflowActivateMode;

View file

@ -949,7 +949,7 @@ export class Workflow {
const triggerResponse = await nodeType.trigger.call(triggerFunctions); const triggerResponse = await nodeType.trigger.call(triggerFunctions);
// Add the manual trigger response which resolves when the first time data got emitted // Add the manual trigger response which resolves when the first time data got emitted
triggerResponse!.manualTriggerResponse = new Promise((resolve) => { triggerResponse!.manualTriggerResponse = new Promise((resolve, reject) => {
triggerFunctions.emit = ( triggerFunctions.emit = (
(resolveEmit) => (resolveEmit) =>
( (
@ -967,6 +967,20 @@ export class Workflow {
resolveEmit(data); resolveEmit(data);
} }
)(resolve); )(resolve);
triggerFunctions.emitError = (
(rejectEmit) =>
(error: Error, responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>) => {
additionalData.hooks!.hookFunctions.sendResponse = [
async (): Promise<void> => {
if (responsePromise) {
responsePromise.reject(error);
}
},
];
rejectEmit(error);
}
)(reject);
}); });
return triggerResponse; return triggerResponse;