Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions nodes/Feather/Feather.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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',
Expand All @@ -61,17 +57,30 @@ 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',
},
...getWorkflowsDescription,
...dispatchPhoneCallDescription,
...createAgentWorkflowDescription,
...createWorkflowExecutionDescription,
...cancelWorkflowExecutionDescription,
],
};
Expand All @@ -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));
Comment on lines +108 to +109
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing credentials parameter in executeCreateWorkflowExecution call - other operations like getWorkflows and dispatchPhoneCall pass credentials as the third parameter

} else if (operation === 'cancelWorkflowExecution') {
returnData.push(await executeCancelWorkflowExecution.call(this, i, baseURL));
}
Expand Down
121 changes: 121 additions & 0 deletions nodes/Feather/operations/createWorkflowExecution.description.ts
Original file line number Diff line number Diff line change
@@ -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',
},
],
},
];
121 changes: 121 additions & 0 deletions nodes/Feather/operations/createWorkflowExecution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { IExecuteFunctions, INodeExecutionData, NodeOperationError } from 'n8n-workflow';

export async function executeCreateWorkflowExecution(
this: IExecuteFunctions,
i: number,
baseURL: string,
): Promise<INodeExecutionData> {
try {
console.log('Starting workflow execution creation...');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Remove console.log statements in production code


// 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<string, unknown> = {
customerLeadId,
primaryPhone,
zipcode,
state,
};
Comment on lines +32 to +37
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Including null values in API payload might cause issues - consider excluding null fields entirely

Suggested change
const body: Record<string, unknown> = {
customerLeadId,
primaryPhone,
zipcode,
state,
};
const body: Record<string, unknown> = {
customerLeadId,
primaryPhone,
};
// Only include optional fields if they have values
if (zipcode) {
body.zipcode = zipcode;
}
if (state) {
body.state = state;
}


// Only include forwardingPhoneNumber if it's provided
if (forwardingPhoneNumber) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Inconsistent handling - empty strings are falsy and won't be included, but null values in zipcode/state are included in payload

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<string, unknown> = {};
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));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Remove console.log statements in production code


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;
}
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 24 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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": "*"
}
}