Skip to content

Commit

Permalink
✨ Error handling if flow has failed
Browse files Browse the repository at this point in the history
  • Loading branch information
David Papp committed Mar 2, 2023
1 parent 933034c commit 8882e00
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 29 deletions.
107 changes: 84 additions & 23 deletions nodes/StateMachine/Statemachine.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ export class Statemachine implements INodeType {
description: 'Set the value of you want to store in state',
action: 'Set the value of you want to store in state',
},
{
name: 'Clean',
value: 'clean',
description: 'Clean the stored value from the state',
action: 'Clean the stored value from the state',
},
{
name: 'Error Handling',
value: 'error_handling',
description: 'If your workflow has an error, this mode can handle it',
action: 'If your workflow has an error this mode can handle it',
},
],
default: 'store',
},
Expand All @@ -61,20 +73,39 @@ export class Statemachine implements INodeType {
type: 'string',
displayOptions: {
show: {
operation: ['store'],
operation: ['store', 'clean'],
},
},
default: '',
required: true,
description: 'Name of the key to get from Redis',
},
{
displayName: 'Execution ID',
name: 'executionId',
type: 'hidden',
required: true,
default: '={{ $execution.id }}',
},
{
displayName: 'Previous Execution ID',
name: 'previousExecutionId',
type: 'string',
displayOptions: {
show: {
operation: ['error_handling'],
},
},
required: true,
default: '',
},
{
displayName: 'Global Statement',
name: 'global',
type: 'boolean',
displayOptions: {
show: {
operation: ['store'],
operation: ['store', 'clean'],
},
},
default: true,
Expand Down Expand Up @@ -195,7 +226,8 @@ export class Statemachine implements INodeType {
}

const client = redis.createClient(redisOptions);

const operation = this.getNodeParameter('operation', 0);
const executionId = this.getNodeParameter('executionId', 0);
client.on('error', (err: Error) => {
client.quit();
reject(err);
Expand All @@ -210,34 +242,63 @@ export class Statemachine implements INodeType {
let item: INodeExecutionData;
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
item = { json: {} };
const value = this.getNodeParameter('value', itemIndex) as string;
const expire = this.getNodeParameter('expire', itemIndex, false) as boolean;
const global = this.getNodeParameter('global', itemIndex, false) as boolean;

const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number;

const hash = crypto.createHash('sha256');
hash.update(JSON.stringify(value));
let key;
if (global) {
key = `n8n-state-global-${hash.digest('hex')}`;
} else {
key = `n8n-state-workflow-${meta.id}-${hash.digest('hex')}`;
if (operation === 'store' || operation === 'clean') {
const value = this.getNodeParameter('value', itemIndex) as string;
const global = this.getNodeParameter('global', itemIndex, false) as boolean;
const hash = crypto.createHash('sha256');
hash.update(JSON.stringify(value));
let key;
if (global) {
key = `n8n-state-global-${hash.digest('hex')}`;
} else {
key = `n8n-state-workflow-${meta.id}-${hash.digest('hex')}`;
}

if (operation === 'store') {
const data = (await getValue(client, key)) || null;

if (data === null) {
const expire = this.getNodeParameter('expire', itemIndex, false) as boolean;
const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number;

await setValue(client, key, value, expire, ttl);
item.json.state = value;
const clientPush = util.promisify(client.LPUSH).bind(client);
// @ts-ignore: typescript not understanding generic function signatures
await clientPush(`${meta.id}-${executionId}`, key);
returnItems.push(item);
} else {
break;
}
} else if (operation === 'clean') {
const clientDel = util.promisify(client.del).bind(client);
// @ts-ignore
await clientDel(key);
}
}
if (operation === 'error_handling') {
const previousExecutionId = this.getNodeParameter(
'previousExecutionId',
itemIndex,
) as string;

const clientLLen = util.promisify(client.LLEN).bind(client);
const keysRange = await clientLLen(`${meta.id}-${previousExecutionId}`);

const data = (await getValue(client, key)) || null;
for (let keyIndex = 0; keyIndex < keysRange; keyIndex++) {
const clientLindex = util.promisify(client.lindex).bind(client);
const keyName = await clientLindex(`${meta.id}-${previousExecutionId}`, keyIndex);

if (data === null) {
await setValue(client, key, value, expire, ttl);
item.json.state = value;
returnItems.push(item);
} else {
break;
const clientDel = util.promisify(client.del).bind(client);
// @ts-ignore
await clientDel(keyName);
}
}
}
client.quit();
resolve(this.prepareOutputData(returnItems));
} catch (error) {
console.log(error);
reject(error);
}
});
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "n8n-nodes-statemachine",
"version": "0.1.0",
"version": "0.1.1",
"description": "A simple state machine refers to a programming concept where an application or workflow is divided into a series of states or steps, and the program progresses from one state to the next based on certain conditions or triggers",
"keywords": [
"n8n-community-node-package",
"n8n",
"statemachine",
"state"
],
"license": "Apache-2.0",
"license": "MIT",
"homepage": "https://github.com/pigri/n8n-nodes-statemachine",
"author": {
"name": "David Papp",
Expand All @@ -22,10 +22,10 @@
"scripts": {
"build": "tsc && gulp build:icons",
"dev": "tsc --watch",
"format": "prettier nodes credentials --write",
"lint": "eslint nodes credentials package.json",
"lintfix": "eslint nodes credentials package.json --fix",
"prepublishOnly": "npm run build && npm run lint -c .eslintrc.prepublish.js nodes credentials package.json"
"format": "prettier nodes --write",
"lint": "eslint nodes package.json",
"lintfix": "eslint nodes package.json --fix",
"prepublishOnly": "npm run build && npm run lint -c .eslintrc.prepublish.js nodes package.json"
},
"files": [
"dist"
Expand Down

0 comments on commit 8882e00

Please sign in to comment.