Skip to content

Commit

Permalink
fix: close IMAP connection gracefully after use
Browse files Browse the repository at this point in the history
  • Loading branch information
umanamente committed May 27, 2024
1 parent f7c36dc commit b2d4bc3
Showing 1 changed file with 72 additions and 57 deletions.
129 changes: 72 additions & 57 deletions nodes/Imap/Imap.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,72 +125,87 @@ export class Imap implements INodeType {
});
}

// get node parameters
const FIRST_ITEM_INDEX = 0; // resource and operation are the same for all items
const resource = this.getNodeParameter('resource', FIRST_ITEM_INDEX) as string;
const operation = this.getNodeParameter('operation', FIRST_ITEM_INDEX) as string;

var resultBranches: INodeExecutionData[][] = [];
var resultItems: INodeExecutionData[] = [];
resultBranches.push(resultItems);

// run corresponding operation
const handler = allResourceDefinitions.find((resourceDef) => resourceDef.resource.value === resource)?.operationDefs.find((operationDef) => operationDef.operation.value === operation);
if (handler) {
// running operation in a loop for each input item
const items = this.getInputData();

for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
try {
// some errors are not thrown but logged by ImapFlow internally, so we try to catch them
ImapFlowErrorCatcher.getInstance().startErrorCatching();

const result = await handler.executeImapAction(this, itemIndex, client);
if (result) {
resultItems.push(...result);
} else {
this.logger.warn(`Operation "${operation}" for resource "${resource}" returned no data`);
}
} catch (error) {
const internalImapErrors = ImapFlowErrorCatcher.getInstance().stopAndGetErrors();
const internalImapErrorsMessage = internalImapErrors.join(", \n");
// try/catch to close connection in any case
try {

if (internalImapErrors.length > 0) {
this.logger.error(`IMAP server reported errors: ${internalImapErrorsMessage}`);
}

if (error instanceof NodeApiError) {
// don't include internal IMAP errors, because the error message is already composed by the handler
throw error;
}
// get node parameters
const FIRST_ITEM_INDEX = 0; // resource and operation are the same for all items
const resource = this.getNodeParameter('resource', FIRST_ITEM_INDEX) as string;
const operation = this.getNodeParameter('operation', FIRST_ITEM_INDEX) as string;

// seems to be unknown error, check IMAP internal errors and include them in the error message
var resultBranches: INodeExecutionData[][] = [];
var resultItems: INodeExecutionData[] = [];
resultBranches.push(resultItems);

var errorMessage = error.responseText || error.message || undefined;
if (!errorMessage) {
if (internalImapErrorsMessage) {
errorMessage = internalImapErrorsMessage;
// run corresponding operation
const handler = allResourceDefinitions.find((resourceDef) => resourceDef.resource.value === resource)?.operationDefs.find((operationDef) => operationDef.operation.value === operation);
if (handler) {
// running operation in a loop for each input item
const items = this.getInputData();

for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
try {
// some errors are not thrown but logged by ImapFlow internally, so we try to catch them
ImapFlowErrorCatcher.getInstance().startErrorCatching();

const result = await handler.executeImapAction(this, itemIndex, client);
if (result) {
resultItems.push(...result);
} else {
errorMessage = 'Unknown error';
this.logger.warn(`Operation "${operation}" for resource "${resource}" returned no data`);
}
} catch (error) {
const internalImapErrors = ImapFlowErrorCatcher.getInstance().stopAndGetErrors();
const internalImapErrorsMessage = internalImapErrors.join(", \n");

if (internalImapErrors.length > 0) {
this.logger.error(`IMAP server reported errors: ${internalImapErrorsMessage}`);
}

if (error instanceof NodeApiError) {
// don't include internal IMAP errors, because the error message is already composed by the handler
throw error;
}

// seems to be unknown error, check IMAP internal errors and include them in the error message

var errorMessage = error.responseText || error.message || undefined;
if (!errorMessage) {
if (internalImapErrorsMessage) {
errorMessage = internalImapErrorsMessage;
} else {
errorMessage = 'Unknown error';
}
}
this.logger.error(`Operation "${operation}" for resource "${resource}" failed: ${errorMessage}`);
this.logger.error(JSON.stringify(error));
var errorDetails : any = {
message: errorMessage,
};
if (internalImapErrorsMessage) {
errorDetails.description = "The following errors were reported by the IMAP server: \n" + internalImapErrorsMessage;
}
throw new NodeApiError(this.getNode(), {}, errorDetails);
}
this.logger.error(`Operation "${operation}" for resource "${resource}" failed: ${errorMessage}`);
this.logger.error(JSON.stringify(error));
var errorDetails : any = {
message: errorMessage,
};
if (internalImapErrorsMessage) {
errorDetails.description = "The following errors were reported by the IMAP server: \n" + internalImapErrorsMessage;
}
throw new NodeApiError(this.getNode(), {}, errorDetails);
}

} else {
this.logger.error(`Unknown operation "${operation}" for resource "${resource}"`);
throw new NodeApiError(this.getNode(), {}, {
message: `Unknown operation "${operation}" for resource "${resource}"`,
});
}

} else {
this.logger.error(`Unknown operation "${operation}" for resource "${resource}"`);
throw new NodeApiError(this.getNode(), {}, {
message: `Unknown operation "${operation}" for resource "${resource}"`,
});
// close connection
client.logout();
this.logger?.info('IMAP connection closed');

} catch (error) {
// close connection and rethrow error
client.logout();
this.logger?.error(`IMAP connection closed. Error: ${error.message}`);
throw error;
}

return resultBranches;
Expand All @@ -208,7 +223,7 @@ export class Imap implements INodeType {
try {
const client = createImapClient(credentials);
await client.connect();
client.close();
client.logout();
} catch (error) {
return {
status: 'Error',
Expand Down

0 comments on commit b2d4bc3

Please sign in to comment.