mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-30 22:42:04 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70653b16bd | ||
|
|
e6f1d6bcf0 | ||
|
|
44f92063c3 | ||
|
|
17530c0f72 | ||
|
|
0ef69fbf75 | ||
|
|
f39c9a5389 | ||
|
|
92d7577f22 | ||
|
|
874aea6920 | ||
|
|
19caa7bbb4 | ||
|
|
dff0387ae2 | ||
|
|
469cc1720d | ||
|
|
99cdae7655 | ||
|
|
abc226f111 | ||
|
|
16e6a1fc44 | ||
|
|
a7a6d64931 |
27
README.md
27
README.md
@@ -2,11 +2,11 @@
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/czlonkowski/n8n-mcp)
|
||||
[](https://github.com/czlonkowski/n8n-mcp)
|
||||
[](https://github.com/czlonkowski/n8n-mcp)
|
||||
[](https://www.npmjs.com/package/n8n-mcp)
|
||||
[](https://codecov.io/gh/czlonkowski/n8n-mcp)
|
||||
[](https://github.com/czlonkowski/n8n-mcp/actions)
|
||||
[](https://github.com/n8n-io/n8n)
|
||||
[](https://github.com/n8n-io/n8n)
|
||||
[](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp)
|
||||
[](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp)
|
||||
|
||||
@@ -389,6 +389,10 @@ You are an expert in n8n automation software using n8n-MCP tools. Your role is t
|
||||
|
||||
6. **Building Phase** - Create or customize the workflow:
|
||||
- If using template: `get_template(templateId, {mode: "full"})`
|
||||
- **MANDATORY ATTRIBUTION**: When using a template, ALWAYS inform the user:
|
||||
- "This workflow is based on a template by **[author.name]** (@[author.username])"
|
||||
- "View the original template at: [url]"
|
||||
- Example: "This workflow is based on a template by **David Ashby** (@cfomodz). View the original at: https://n8n.io/workflows/2414"
|
||||
- Customize template or build from validated configurations
|
||||
- Connect nodes with proper structure
|
||||
- Add error handling where appropriate
|
||||
@@ -410,6 +414,7 @@ You are an expert in n8n automation software using n8n-MCP tools. Your role is t
|
||||
## Key Insights
|
||||
|
||||
- **TEMPLATES FIRST** - Always check for existing templates before building from scratch (2,500+ available!)
|
||||
- **ATTRIBUTION REQUIRED** - Always credit template authors with name, username, and link to n8n.io
|
||||
- **SMART FILTERING** - Use metadata filters to find templates matching user skill level and time constraints
|
||||
- **USE CODE NODE ONLY WHEN IT IS NECESSARY** - always prefer to use standard nodes over code node. Use code node only when you are sure you need it.
|
||||
- **VALIDATE EARLY AND OFTEN** - Catch errors before they reach deployment
|
||||
@@ -504,6 +509,7 @@ n8n_update_partial_workflow({
|
||||
|
||||
- ALWAYS check for existing templates before building from scratch
|
||||
- LEVERAGE metadata filters to find skill-appropriate templates
|
||||
- **ALWAYS ATTRIBUTE TEMPLATES**: When using any template, you MUST share the author's name, username, and link to the original template on n8n.io
|
||||
- VALIDATE templates before deployment (they may need updates)
|
||||
- USE diff operations for updates (80-90% token savings)
|
||||
- STATE validation results clearly
|
||||
@@ -861,6 +867,23 @@ See [Automated Release Guide](./docs/AUTOMATED_RELEASES.md) for complete details
|
||||
- [Anthropic](https://anthropic.com) for the Model Context Protocol
|
||||
- All contributors and users of this project
|
||||
|
||||
### Template Attribution
|
||||
|
||||
All workflow templates in this project are fetched from n8n's public template gallery at [n8n.io/workflows](https://n8n.io/workflows). Each template includes:
|
||||
- Full attribution to the original creator (name and username)
|
||||
- Direct link to the source template on n8n.io
|
||||
- Original workflow ID for reference
|
||||
|
||||
The AI agent instructions in this project contain mandatory attribution requirements. When using any template, the AI will automatically:
|
||||
- Share the template author's name and username
|
||||
- Provide a direct link to the original template on n8n.io
|
||||
- Display attribution in the format: "This workflow is based on a template by **[author]** (@[username]). View the original at: [url]"
|
||||
|
||||
Template creators retain all rights to their workflows. This project indexes templates to improve discoverability through AI assistants. If you're a template creator and have concerns about your template being indexed, please open an issue.
|
||||
|
||||
Special thanks to the prolific template contributors whose work helps thousands of users automate their workflows, including:
|
||||
**David Ashby** (@cfomodz), **Yaron Been** (@yaron-nofluff), **Jimleuk** (@jimleuk), **Davide** (@n3witalia), **David Olusola** (@dae221), **Ranjan Dailata** (@ranjancse), **Airtop** (@cesar-at-airtop), **Joseph LePage** (@joe), **Don Jayamaha Jr** (@don-the-gem-dealer), **Angel Menendez** (@djangelic), and the entire n8n community of creators!
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
BIN
data/nodes.db
BIN
data/nodes.db
Binary file not shown.
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2.11.3] - 2025-09-17
|
||||
|
||||
### Fixed
|
||||
- **n8n_update_partial_workflow Tool**: Fixed critical bug where updateNode and updateConnection operations were using incorrect property name
|
||||
- Changed from `changes` property to `updates` property to match documentation and expected behavior
|
||||
- Resolves issue where AI agents would break workflow connections when updating nodes
|
||||
- Fixes GitHub issues #159 (update_partial_workflow is invalid) and #168 (partial workflow update returns error)
|
||||
- All related tests updated to use correct property name
|
||||
|
||||
## [2.11.2] - 2025-09-16
|
||||
|
||||
### Updated
|
||||
- **n8n Dependencies**: Updated to latest versions for compatibility and new features
|
||||
- n8n: 1.110.1 → 1.111.0
|
||||
- n8n-core: 1.109.0 → 1.110.0
|
||||
- n8n-workflow: 1.107.0 → 1.108.0
|
||||
- @n8n/n8n-nodes-langchain: 1.109.1 → 1.110.0
|
||||
- **Node Database**: Rebuilt with 535 nodes from updated n8n packages
|
||||
- **Templates**: Preserved all 2,598 workflow templates with metadata intact
|
||||
- All critical nodes validated successfully (httpRequest, code, slack, agent)
|
||||
- Test suite: 1,911 tests passing, 5 flaky performance tests failing (99.7% pass rate)
|
||||
|
||||
## [2.11.1] - 2025-09-15
|
||||
|
||||
### Added
|
||||
- **Optional Fields Parameter for search_templates**: Enhanced search_templates tool with field filtering capability
|
||||
- New optional `fields` parameter accepts an array of field names to include in response
|
||||
- Supported fields: 'id', 'name', 'description', 'author', 'nodes', 'views', 'created', 'url', 'metadata'
|
||||
- Reduces response size by 70-98% when requesting only specific fields (e.g., just id and name)
|
||||
- Maintains full backward compatibility - existing calls without fields parameter work unchanged
|
||||
- Example: `search_templates({query: "slack", fields: ["id", "name"]})` returns minimal data
|
||||
- Significantly improves AI agent performance by reducing token usage
|
||||
|
||||
### Added
|
||||
- **Fuzzy Node Type Matching for Templates**: Improved template discovery with flexible node type resolution
|
||||
- Templates can now be found using simple node names: `["slack"]` instead of `["n8n-nodes-base.slack"]`
|
||||
@@ -1286,6 +1319,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Basic n8n and MCP integration
|
||||
- Core workflow automation features
|
||||
|
||||
[2.11.2]: https://github.com/czlonkowski/n8n-mcp/compare/v2.11.1...v2.11.2
|
||||
[2.11.1]: https://github.com/czlonkowski/n8n-mcp/compare/v2.11.0...v2.11.1
|
||||
[2.11.0]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.9...v2.11.0
|
||||
[2.10.9]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.8...v2.10.9
|
||||
[2.10.8]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.5...v2.10.8
|
||||
[2.10.5]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.4...v2.10.5
|
||||
[2.10.4]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.3...v2.10.4
|
||||
[2.10.3]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.2...v2.10.3
|
||||
[2.10.2]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.1...v2.10.2
|
||||
|
||||
14437
package-lock.json
generated
14437
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "n8n-mcp",
|
||||
"version": "2.11.0",
|
||||
"version": "2.11.3",
|
||||
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
@@ -128,12 +128,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.13.2",
|
||||
"@n8n/n8n-nodes-langchain": "^1.109.1",
|
||||
"@n8n/n8n-nodes-langchain": "^1.110.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"express": "^5.1.0",
|
||||
"n8n": "^1.110.1",
|
||||
"n8n-core": "^1.109.0",
|
||||
"n8n-workflow": "^1.107.0",
|
||||
"n8n": "^1.111.0",
|
||||
"n8n-core": "^1.110.0",
|
||||
"n8n-workflow": "^1.108.0",
|
||||
"openai": "^4.77.0",
|
||||
"sql.js": "^1.13.0",
|
||||
"uuid": "^10.0.0",
|
||||
@@ -143,5 +143,8 @@
|
||||
"@rollup/rollup-darwin-arm64": "^4.50.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.50.0",
|
||||
"better-sqlite3": "^11.10.0"
|
||||
},
|
||||
"overrides": {
|
||||
"pyodide": "0.26.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "n8n-mcp-runtime",
|
||||
"version": "2.10.9",
|
||||
"version": "2.11.2",
|
||||
"description": "n8n MCP Server Runtime Dependencies Only",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -21,7 +21,7 @@ const workflowDiffSchema = z.object({
|
||||
node: z.any().optional(),
|
||||
nodeId: z.string().optional(),
|
||||
nodeName: z.string().optional(),
|
||||
changes: z.any().optional(),
|
||||
updates: z.any().optional(),
|
||||
position: z.tuple([z.number(), z.number()]).optional(),
|
||||
// Connection operations
|
||||
source: z.string().optional(),
|
||||
|
||||
@@ -746,7 +746,8 @@ export class N8NDocumentationMCPServer {
|
||||
this.validateToolParams(name, args, ['query']);
|
||||
const searchLimit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
|
||||
const searchOffset = Math.max(Number(args.offset) || 0, 0);
|
||||
return this.searchTemplates(args.query, searchLimit, searchOffset);
|
||||
const searchFields = args.fields as string[] | undefined;
|
||||
return this.searchTemplates(args.query, searchLimit, searchOffset, searchFields);
|
||||
case 'get_templates_for_task':
|
||||
this.validateToolParams(name, args, ['task']);
|
||||
const taskLimit = Math.min(Math.max(Number(args.limit) || 10, 1), 100);
|
||||
@@ -2399,11 +2400,11 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
||||
};
|
||||
}
|
||||
|
||||
private async searchTemplates(query: string, limit: number = 20, offset: number = 0): Promise<any> {
|
||||
private async searchTemplates(query: string, limit: number = 20, offset: number = 0, fields?: string[]): Promise<any> {
|
||||
await this.ensureInitialized();
|
||||
if (!this.templateService) throw new Error('Template service not initialized');
|
||||
|
||||
const result = await this.templateService.searchTemplates(query, limit, offset);
|
||||
const result = await this.templateService.searchTemplates(query, limit, offset, fields);
|
||||
|
||||
if (result.items.length === 0 && offset === 0) {
|
||||
return {
|
||||
|
||||
@@ -5,13 +5,14 @@ export const searchTemplatesDoc: ToolDocumentation = {
|
||||
category: 'templates',
|
||||
essentials: {
|
||||
description: 'Search templates by name/description keywords. NOT for node types! For nodes use list_node_templates. Example: "chatbot".',
|
||||
keyParameters: ['query', 'limit'],
|
||||
example: 'search_templates({query: "chatbot"})',
|
||||
keyParameters: ['query', 'limit', 'fields'],
|
||||
example: 'search_templates({query: "chatbot", fields: ["id", "name"]})',
|
||||
performance: 'Fast (<100ms) - FTS5 full-text search',
|
||||
tips: [
|
||||
'Searches template names and descriptions, NOT node types',
|
||||
'Use keywords like "automation", "sync", "notification"',
|
||||
'For node-specific search, use list_node_templates instead'
|
||||
'For node-specific search, use list_node_templates instead',
|
||||
'Use fields parameter to get only specific data (reduces response by 70-90%)'
|
||||
]
|
||||
},
|
||||
full: {
|
||||
@@ -22,6 +23,11 @@ export const searchTemplatesDoc: ToolDocumentation = {
|
||||
required: true,
|
||||
description: 'Search query for template names/descriptions. NOT for node types! Examples: "chatbot", "automation", "social media", "webhook". For node-based search use list_node_templates instead.'
|
||||
},
|
||||
fields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
description: 'Fields to include in response. Options: "id", "name", "description", "author", "nodes", "views", "created", "url", "metadata". Default: all fields. Example: ["id", "name"] for minimal response.'
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
@@ -47,7 +53,9 @@ export const searchTemplatesDoc: ToolDocumentation = {
|
||||
'search_templates({query: "email notification"}) - Find email alert workflows',
|
||||
'search_templates({query: "data sync"}) - Find data synchronization workflows',
|
||||
'search_templates({query: "webhook automation", limit: 30}) - Find webhook-based automations',
|
||||
'search_templates({query: "social media scheduler"}) - Find social posting workflows'
|
||||
'search_templates({query: "social media scheduler"}) - Find social posting workflows',
|
||||
'search_templates({query: "slack", fields: ["id", "name"]}) - Get only IDs and names of Slack templates',
|
||||
'search_templates({query: "automation", fields: ["id", "name", "description"]}) - Get minimal info for automation templates'
|
||||
],
|
||||
useCases: [
|
||||
'Find workflows by business purpose',
|
||||
|
||||
@@ -48,7 +48,7 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
|
||||
},
|
||||
returns: 'Updated workflow object or validation results if validateOnly=true',
|
||||
examples: [
|
||||
'// Update node parameter\nn8n_update_partial_workflow({id: "abc", operations: [{type: "updateNode", nodeName: "HTTP Request", changes: {"parameters.url": "https://api.example.com"}}]})',
|
||||
'// Update node parameter\nn8n_update_partial_workflow({id: "abc", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {"parameters.url": "https://api.example.com"}}]})',
|
||||
'// Add connection between nodes\nn8n_update_partial_workflow({id: "xyz", operations: [{type: "addConnection", source: "Webhook", target: "Slack", sourceOutput: "main", targetInput: "main"}]})',
|
||||
'// Multiple operations in one call\nn8n_update_partial_workflow({id: "123", operations: [\n {type: "addNode", node: {name: "Transform", type: "n8n-nodes-base.code", position: [400, 300]}},\n {type: "addConnection", source: "Webhook", target: "Transform"},\n {type: "updateSettings", settings: {timezone: "America/New_York"}}\n]})',
|
||||
'// Validate before applying\nn8n_update_partial_workflow({id: "456", operations: [{type: "removeNode", nodeName: "Old Process"}], validateOnly: true})'
|
||||
@@ -73,7 +73,7 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
|
||||
'Operations validated together - all must be valid',
|
||||
'Order matters for dependent operations (e.g., must add node before connecting to it)',
|
||||
'Node references accept ID or name, but name must be unique',
|
||||
'Dot notation for nested updates: use "parameters.url" not nested objects'
|
||||
'Use "updates" property for updateNode operations: {type: "updateNode", updates: {...}}'
|
||||
],
|
||||
relatedTools: ['n8n_update_full_workflow', 'n8n_get_workflow', 'validate_workflow', 'tools_documentation']
|
||||
}
|
||||
|
||||
@@ -414,6 +414,14 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [
|
||||
type: 'string',
|
||||
description: 'Search keyword as string. Example: "chatbot"',
|
||||
},
|
||||
fields: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: ['id', 'name', 'description', 'author', 'nodes', 'views', 'created', 'url', 'metadata'],
|
||||
},
|
||||
description: 'Fields to include in response. Default: all fields. Example: ["id", "name"] for minimal response.',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
description: 'Maximum number of results. Default 20.',
|
||||
|
||||
@@ -453,8 +453,8 @@ export class WorkflowDiffEngine {
|
||||
const node = this.findNode(workflow, operation.nodeId, operation.nodeName);
|
||||
if (!node) return;
|
||||
|
||||
// Apply changes using dot notation
|
||||
Object.entries(operation.changes).forEach(([path, value]) => {
|
||||
// Apply updates using dot notation
|
||||
Object.entries(operation.updates).forEach(([path, value]) => {
|
||||
this.setNestedProperty(node, path, value);
|
||||
});
|
||||
}
|
||||
@@ -545,18 +545,18 @@ export class WorkflowDiffEngine {
|
||||
type: 'removeConnection',
|
||||
source: operation.source,
|
||||
target: operation.target,
|
||||
sourceOutput: operation.changes.sourceOutput,
|
||||
targetInput: operation.changes.targetInput
|
||||
sourceOutput: operation.updates.sourceOutput,
|
||||
targetInput: operation.updates.targetInput
|
||||
});
|
||||
|
||||
this.applyAddConnection(workflow, {
|
||||
type: 'addConnection',
|
||||
source: operation.source,
|
||||
target: operation.target,
|
||||
sourceOutput: operation.changes.sourceOutput,
|
||||
targetInput: operation.changes.targetInput,
|
||||
sourceIndex: operation.changes.sourceIndex,
|
||||
targetIndex: operation.changes.targetIndex
|
||||
sourceOutput: operation.updates.sourceOutput,
|
||||
targetInput: operation.updates.targetInput,
|
||||
sourceIndex: operation.updates.sourceIndex,
|
||||
targetIndex: operation.updates.targetIndex
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ export interface TemplateMinimal {
|
||||
};
|
||||
}
|
||||
|
||||
export type TemplateField = 'id' | 'name' | 'description' | 'author' | 'nodes' | 'views' | 'created' | 'url' | 'metadata';
|
||||
export type PartialTemplateInfo = Partial<TemplateInfo>;
|
||||
|
||||
export class TemplateService {
|
||||
private repository: TemplateRepository;
|
||||
|
||||
@@ -124,12 +127,17 @@ export class TemplateService {
|
||||
/**
|
||||
* Search templates by query
|
||||
*/
|
||||
async searchTemplates(query: string, limit: number = 20, offset: number = 0): Promise<PaginatedResponse<TemplateInfo>> {
|
||||
async searchTemplates(query: string, limit: number = 20, offset: number = 0, fields?: string[]): Promise<PaginatedResponse<PartialTemplateInfo>> {
|
||||
const templates = this.repository.searchTemplates(query, limit, offset);
|
||||
const total = this.repository.getSearchCount(query);
|
||||
|
||||
// If fields are specified, filter the template info
|
||||
const items = fields
|
||||
? templates.map(t => this.formatTemplateWithFields(t, fields))
|
||||
: templates.map(t => this.formatTemplateInfo(t));
|
||||
|
||||
return {
|
||||
items: templates.map(this.formatTemplateInfo),
|
||||
items,
|
||||
total,
|
||||
limit,
|
||||
offset,
|
||||
@@ -403,4 +411,21 @@ export class TemplateService {
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format template with only specified fields
|
||||
*/
|
||||
private formatTemplateWithFields(template: StoredTemplate, fields: string[]): PartialTemplateInfo {
|
||||
const fullInfo = this.formatTemplateInfo(template);
|
||||
const result: PartialTemplateInfo = {};
|
||||
|
||||
// Only include requested fields
|
||||
for (const field of fields) {
|
||||
if (field in fullInfo) {
|
||||
(result as any)[field] = (fullInfo as any)[field];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export interface UpdateNodeOperation extends DiffOperation {
|
||||
type: 'updateNode';
|
||||
nodeId?: string; // Can use either ID or name
|
||||
nodeName?: string;
|
||||
changes: {
|
||||
updates: {
|
||||
[path: string]: any; // Dot notation paths like 'parameters.url'
|
||||
};
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export interface UpdateConnectionOperation extends DiffOperation {
|
||||
type: 'updateConnection';
|
||||
source: string;
|
||||
target: string;
|
||||
changes: {
|
||||
updates: {
|
||||
sourceOutput?: string;
|
||||
targetInput?: string;
|
||||
sourceIndex?: number;
|
||||
|
||||
@@ -159,7 +159,7 @@ describe('handlers-workflow-diff', () => {
|
||||
{
|
||||
type: 'updateNode',
|
||||
nodeId: 'node2',
|
||||
changes: { name: 'Updated HTTP Request' },
|
||||
updates: { name: 'Updated HTTP Request' },
|
||||
},
|
||||
],
|
||||
validateOnly: true,
|
||||
@@ -196,7 +196,7 @@ describe('handlers-workflow-diff', () => {
|
||||
{
|
||||
type: 'updateNode',
|
||||
nodeId: 'node1',
|
||||
changes: { name: 'Updated Start' },
|
||||
updates: { name: 'Updated Start' },
|
||||
},
|
||||
{
|
||||
type: 'addNode',
|
||||
@@ -243,7 +243,7 @@ describe('handlers-workflow-diff', () => {
|
||||
{
|
||||
type: 'updateNode',
|
||||
nodeId: 'non-existent-node',
|
||||
changes: { name: 'Updated' },
|
||||
updates: { name: 'Updated' },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -320,7 +320,7 @@ describe('handlers-workflow-diff', () => {
|
||||
|
||||
const result = await handleUpdatePartialWorkflow({
|
||||
id: 'test-id',
|
||||
operations: [{ type: 'updateNode', nodeId: 'node1', changes: {} }],
|
||||
operations: [{ type: 'updateNode', nodeId: 'node1', updates: {} }],
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -341,7 +341,7 @@ describe('handlers-workflow-diff', () => {
|
||||
{
|
||||
// Missing required 'type' field
|
||||
nodeId: 'node1',
|
||||
changes: {},
|
||||
updates: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -417,7 +417,7 @@ describe('handlers-workflow-diff', () => {
|
||||
|
||||
await handleUpdatePartialWorkflow({
|
||||
id: 'test-id',
|
||||
operations: [{ type: 'updateNode', nodeId: 'node1', changes: {} }],
|
||||
operations: [{ type: 'updateNode', nodeId: 'node1', updates: {} }],
|
||||
});
|
||||
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
@@ -502,7 +502,7 @@ describe('handlers-workflow-diff', () => {
|
||||
type: 'updateNode',
|
||||
nodeId: 'node1',
|
||||
nodeName: 'Start', // Both nodeId and nodeName provided
|
||||
changes: { name: 'New Start' },
|
||||
updates: { name: 'New Start' },
|
||||
description: 'Update start node name',
|
||||
},
|
||||
{
|
||||
@@ -561,8 +561,8 @@ describe('handlers-workflow-diff', () => {
|
||||
const diffRequest = {
|
||||
id: 'test-workflow-id',
|
||||
operations: [
|
||||
{ type: 'updateNode', nodeId: 'node1', changes: { name: 'Updated' } },
|
||||
{ type: 'updateNode', nodeId: 'invalid-node', changes: { name: 'Fail' } },
|
||||
{ type: 'updateNode', nodeId: 'node1', updates: { name: 'Updated' } },
|
||||
{ type: 'updateNode', nodeId: 'invalid-node', updates: { name: 'Fail' } },
|
||||
{ type: 'addTag', tag: 'test' },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -683,7 +683,7 @@ describe('TemplateService', () => {
|
||||
|
||||
const result = await service.searchTemplates('test');
|
||||
|
||||
expect(result.items[0].author.verified).toBe(false);
|
||||
expect(result.items[0]?.author?.verified).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -281,7 +281,7 @@ describe('WorkflowDiffEngine', () => {
|
||||
const operation: UpdateNodeOperation = {
|
||||
type: 'updateNode',
|
||||
nodeId: 'http-1',
|
||||
changes: {
|
||||
updates: {
|
||||
'parameters.method': 'POST',
|
||||
'parameters.url': 'https://new-api.example.com'
|
||||
}
|
||||
@@ -304,7 +304,7 @@ describe('WorkflowDiffEngine', () => {
|
||||
const operation: UpdateNodeOperation = {
|
||||
type: 'updateNode',
|
||||
nodeName: 'Slack',
|
||||
changes: {
|
||||
updates: {
|
||||
'parameters.resource': 'channel',
|
||||
'parameters.operation': 'create',
|
||||
'credentials.slackApi.name': 'New Slack Account'
|
||||
@@ -329,7 +329,7 @@ describe('WorkflowDiffEngine', () => {
|
||||
const operation: UpdateNodeOperation = {
|
||||
type: 'updateNode',
|
||||
nodeId: 'non-existent',
|
||||
changes: {
|
||||
updates: {
|
||||
'parameters.test': 'value'
|
||||
}
|
||||
};
|
||||
@@ -617,7 +617,7 @@ describe('WorkflowDiffEngine', () => {
|
||||
type: 'updateConnection',
|
||||
source: 'IF',
|
||||
target: 'slack-1',
|
||||
changes: {
|
||||
updates: {
|
||||
sourceOutput: 'false',
|
||||
sourceIndex: 0,
|
||||
targetIndex: 0
|
||||
@@ -1039,7 +1039,7 @@ describe('WorkflowDiffEngine', () => {
|
||||
const operation: UpdateNodeOperation = {
|
||||
type: 'updateNode',
|
||||
nodeId: 'Webhook', // Using name as ID
|
||||
changes: {
|
||||
updates: {
|
||||
'parameters.path': 'new-webhook-path'
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user