From eb30006f8eb04d759fa075cc344647dbbc5aec29 Mon Sep 17 00:00:00 2001 From: Arijit Roy Date: Wed, 8 Oct 2025 23:42:55 +0530 Subject: [PATCH] =?UTF-8?q?feat:=20add=20create=20workflow=20execution=20f?= =?UTF-8?q?unctionality=20with=20parameters=20and=E2=80=A6=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add create workflow execution functionality with parameters and API integration * feat: update Feather node options and enhance create workflow execution to conditionally include forwardingPhoneNumber * fix: update TypeScript version specification and format JSON files for consistency * fix: change default value of forwardingPhoneNumber parameter to null for better handling of optional input --- nodes/Feather/Feather.node.ts | 39 ++++-- .../createWorkflowExecution.description.ts | 121 ++++++++++++++++++ .../operations/createWorkflowExecution.ts | 121 ++++++++++++++++++ package-lock.json | 2 +- package.json | 32 +++-- 5 files changed, 292 insertions(+), 23 deletions(-) create mode 100644 nodes/Feather/operations/createWorkflowExecution.description.ts create mode 100644 nodes/Feather/operations/createWorkflowExecution.ts diff --git a/nodes/Feather/Feather.node.ts b/nodes/Feather/Feather.node.ts index 550540e..df01710 100644 --- a/nodes/Feather/Feather.node.ts +++ b/nodes/Feather/Feather.node.ts @@ -10,6 +10,8 @@ import { executeCancelWorkflowExecution } from './operations/cancelWorkflowExecu import { cancelWorkflowExecutionDescription } from './operations/cancelWorkflowExecution.description'; import { executeCreateAgentWorkflow } from './operations/createAgentWorkflow'; import { createAgentWorkflowDescription } from './operations/createAgentWorkflow.description'; +import { executeCreateWorkflowExecution } from './operations/createWorkflowExecution'; +import { createWorkflowExecutionDescription } from './operations/createWorkflowExecution.description'; import { executeDispatchPhoneCall } from './operations/dispatchPhoneCall'; import { dispatchPhoneCallDescription } from './operations/dispatchPhoneCall.description'; import { executeGetWorkflows } from './operations/getWorkflows'; @@ -43,16 +45,10 @@ export class Feather implements INodeType { noDataExpression: true, options: [ { - name: 'Get Workflows', - value: 'getWorkflows', - description: 'Get a list of workflows', - action: 'Get workflows', - }, - { - name: 'Dispatch Phone Call', - value: 'dispatchPhoneCall', - description: 'Dispatch a phone call with custom parameters', - action: 'Dispatch a phone call', + name: 'Cancel Workflow Execution', + value: 'cancelWorkflowExecution', + description: 'Cancel a workflow execution', + action: 'Cancel a workflow execution', }, { name: 'Create Agent Workflow', @@ -61,10 +57,22 @@ export class Feather implements INodeType { action: 'Create an agent workflow', }, { - name: 'Cancel Workflow Execution', - value: 'cancelWorkflowExecution', - description: 'Cancel a workflow execution', - action: 'Cancel a workflow execution', + name: 'Create Workflow Execution', + value: 'createWorkflowExecution', + description: 'Create a new workflow execution', + action: 'Create a workflow execution', + }, + { + name: 'Dispatch Phone Call', + value: 'dispatchPhoneCall', + description: 'Dispatch a phone call with custom parameters', + action: 'Dispatch a phone call', + }, + { + name: 'Get Workflows', + value: 'getWorkflows', + description: 'Get a list of workflows', + action: 'Get workflows', }, ], default: 'getWorkflows', @@ -72,6 +80,7 @@ export class Feather implements INodeType { ...getWorkflowsDescription, ...dispatchPhoneCallDescription, ...createAgentWorkflowDescription, + ...createWorkflowExecutionDescription, ...cancelWorkflowExecutionDescription, ], }; @@ -96,6 +105,8 @@ export class Feather implements INodeType { returnData.push(await executeDispatchPhoneCall.call(this, i, baseURL, credentials)); } else if (operation === 'createAgentWorkflow') { returnData.push(await executeCreateAgentWorkflow.call(this, i, baseURL)); + } else if (operation === 'createWorkflowExecution') { + returnData.push(await executeCreateWorkflowExecution.call(this, i, baseURL)); } else if (operation === 'cancelWorkflowExecution') { returnData.push(await executeCancelWorkflowExecution.call(this, i, baseURL)); } diff --git a/nodes/Feather/operations/createWorkflowExecution.description.ts b/nodes/Feather/operations/createWorkflowExecution.description.ts new file mode 100644 index 0000000..2ec3132 --- /dev/null +++ b/nodes/Feather/operations/createWorkflowExecution.description.ts @@ -0,0 +1,121 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const createWorkflowExecutionDescription: INodeProperties[] = [ + { + displayName: 'Workflow ID', + name: 'workflowId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + operation: ['createWorkflowExecution'], + }, + }, + description: 'The ID of the workflow to execute', + }, + { + displayName: 'Customer Lead ID', + name: 'customerLeadId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + operation: ['createWorkflowExecution'], + }, + }, + description: 'Customer lead identifier', + }, + { + displayName: 'Primary Phone', + name: 'primaryPhone', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + operation: ['createWorkflowExecution'], + }, + }, + description: 'Primary phone number for the execution', + }, + { + displayName: 'Zipcode', + name: 'zipcode', + type: 'string', + default: '', + displayOptions: { + show: { + operation: ['createWorkflowExecution'], + }, + }, + description: 'Zipcode for the execution', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + displayOptions: { + show: { + operation: ['createWorkflowExecution'], + }, + }, + description: 'State for the execution', + }, + { + displayName: 'Forwarding Phone Number', + name: 'forwardingPhoneNumber', + type: 'string', + default: '', + displayOptions: { + show: { + operation: ['createWorkflowExecution'], + }, + }, + description: 'Phone number to forward calls to', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: ['createWorkflowExecution'], + }, + }, + options: [ + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + description: 'First name for metadata', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + default: '', + description: 'Last name for metadata', + }, + { + displayName: 'Variables (JSON)', + name: 'variables', + type: 'json', + default: '{}', + description: 'Variables for the execution as JSON object', + }, + { + displayName: 'Additional Metadata (JSON)', + name: 'additionalMetadata', + type: 'json', + default: '{}', + description: 'Additional metadata fields as JSON object', + }, + ], + }, +]; diff --git a/nodes/Feather/operations/createWorkflowExecution.ts b/nodes/Feather/operations/createWorkflowExecution.ts new file mode 100644 index 0000000..dd56a3c --- /dev/null +++ b/nodes/Feather/operations/createWorkflowExecution.ts @@ -0,0 +1,121 @@ +import { IExecuteFunctions, INodeExecutionData, NodeOperationError } from 'n8n-workflow'; + +export async function executeCreateWorkflowExecution( + this: IExecuteFunctions, + i: number, + baseURL: string, +): Promise { + try { + console.log('Starting workflow execution creation...'); + + // Get required parameters + const workflowId = this.getNodeParameter('workflowId', i) as string; + const customerLeadId = this.getNodeParameter('customerLeadId', i) as string; + const primaryPhone = this.getNodeParameter('primaryPhone', i) as string; + + // Get optional parameters + const zipcode = this.getNodeParameter('zipcode', i, null) as string | null; + const state = this.getNodeParameter('state', i, null) as string | null; + const forwardingPhoneNumber = this.getNodeParameter('forwardingPhoneNumber', i, null) as + | string + | null; + + // Get additional fields + const additionalFields = this.getNodeParameter('additionalFields', i, {}) as Record< + string, + unknown + >; + + console.log('Basic parameters:', { workflowId, customerLeadId, primaryPhone, zipcode, state }); + + // Build request body + const body: Record = { + customerLeadId, + primaryPhone, + zipcode, + state, + }; + + // Only include forwardingPhoneNumber if it's provided + if (forwardingPhoneNumber) { + body.forwardingPhoneNumber = forwardingPhoneNumber; + } + + // Handle variables (JSON) + if (additionalFields.variables) { + try { + body.variables = + typeof additionalFields.variables === 'string' + ? JSON.parse(additionalFields.variables as string) + : additionalFields.variables; + } catch { + throw new NodeOperationError(this.getNode(), 'Invalid JSON in Variables field', { + itemIndex: i, + }); + } + } else { + body.variables = {}; + } + + // Handle metadata + const metadata: Record = {}; + if (additionalFields.firstName) { + metadata.firstName = additionalFields.firstName; + } + if (additionalFields.lastName) { + metadata.lastName = additionalFields.lastName; + } + + // Add any additional metadata fields + if (additionalFields.additionalMetadata) { + try { + const additionalMetadata = + typeof additionalFields.additionalMetadata === 'string' + ? JSON.parse(additionalFields.additionalMetadata as string) + : additionalFields.additionalMetadata; + Object.assign(metadata, additionalMetadata); + } catch { + throw new NodeOperationError(this.getNode(), 'Invalid JSON in Additional Metadata field', { + itemIndex: i, + }); + } + } + + body.metadata = metadata; + + console.log('Preparing API request with execution data:', JSON.stringify(body, null, 2)); + + try { + const response = await this.helpers.httpRequestWithAuthentication.call(this, 'featherApi', { + method: 'POST', + url: `${baseURL}/api/v1/workflow/${workflowId}/execution`, + headers: { + 'Content-Type': 'application/json', + accept: 'application/json, text/plain, */*', + }, + body, + json: true, + }); + + console.log('Workflow execution created successfully:', JSON.stringify(response, null, 2)); + + return { + json: response, + pairedItem: { + item: i, + }, + }; + } catch (apiError) { + console.error('API request failed:', apiError); + console.error('Request details:', { + url: `${baseURL}/api/v1/workflow/${workflowId}/execution`, + workflowId, + body, + }); + throw apiError; + } + } catch (error) { + console.error('Error in workflow execution creation:', error); + throw error; + } +} diff --git a/package-lock.json b/package-lock.json index ad4997b..1104761 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "eslint": "9.32.0", "prettier": "3.6.2", "release-it": "^19.0.4", - "typescript": "5.9.2" + "typescript": "^5.9.2" }, "peerDependencies": { "n8n-workflow": "*" diff --git a/package.json b/package.json index f97cad1..1703fe7 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,17 @@ "description": "n8n community node to work with the Example API", "license": "MIT", "homepage": "https://example.com", - "keywords": ["n8n-community-node-package"], - "author": { "name": "radioactive11", "email": "roy.arijit@icloud.com" }, - "repository": { "type": "git", "url": "" }, + "keywords": [ + "n8n-community-node-package" + ], + "author": { + "name": "radioactive11", + "email": "roy.arijit@icloud.com" + }, + "repository": { + "type": "git", + "url": "" + }, "scripts": { "build": "n8n-node build", "build:watch": "tsc --watch", @@ -16,18 +24,26 @@ "release": "n8n-node release", "prepublishOnly": "n8n-node prerelease" }, - "files": ["dist"], + "files": [ + "dist" + ], "n8n": { "n8nNodesApiVersion": 1, - "credentials": ["dist/credentials/FeatherApi.credentials.js"], - "nodes": ["dist/nodes/Feather/Feather.node.js"] + "credentials": [ + "dist/credentials/FeatherApi.credentials.js" + ], + "nodes": [ + "dist/nodes/Feather/Feather.node.js" + ] }, "devDependencies": { "@n8n/node-cli": "0.11.0", "eslint": "9.32.0", "prettier": "3.6.2", "release-it": "^19.0.4", - "typescript": "5.9.2" + "typescript": "^5.9.2" }, - "peerDependencies": { "n8n-workflow": "*" } + "peerDependencies": { + "n8n-workflow": "*" + } }