fix(issue-248): use unconditional empty settings object for cloud API compatibility

Issue #248 required three iterations to solve due to n8n API version differences:

1. First attempt: Whitelist filtering
   - Failed: API rejects ANY settings properties via update endpoint

2. Second attempt: Complete settings removal
   - Failed: Cloud API requires settings property to exist

3. Final solution: Unconditional empty settings object
   - Success: Satisfies both API requirements

Changes:
- src/services/n8n-validation.ts:153
  - Changed from conditional `if (cleanedWorkflow.settings)` to unconditional
  - Always sets `cleanedWorkflow.settings = {}`
  - Works for both cloud (requires property) and self-hosted (rejects properties)

- tests/unit/services/n8n-validation.test.ts
  - Updated all 4 tests to expect `settings: {}` instead of removed settings
  - Tests verify empty object approach works for all scenarios

Tested:
-  localhost workflow (wwTodXf1jbUy3Ja5)
-  cloud workflow (n8n.estyl.team/workflow/WKFeCRUjTeYbYhTf)
-  All 72 unit tests passing

References:
- https://community.n8n.io/t/api-workflow-update-endpoint-doesnt-support-setting-callerpolicy/161916
- Tested with @agent-n8n-mcp-tester on production workflows
This commit is contained in:
czlonkowski
2025-10-02 16:33:11 +02:00
parent fe1e3640af
commit 99518f71cf
5 changed files with 79 additions and 25 deletions

View File

@@ -344,12 +344,12 @@ describe('n8n-validation', () => {
expect(cleaned).not.toHaveProperty('shared');
expect(cleaned).not.toHaveProperty('active');
// Should keep name but remove settings (n8n API limitation)
// Should keep name but replace settings with empty object (n8n API limitation)
expect(cleaned.name).toBe('Updated Workflow');
expect(cleaned).not.toHaveProperty('settings');
expect(cleaned.settings).toEqual({});
});
it('should not add default settings for update', () => {
it('should add empty settings object for cloud API compatibility', () => {
const workflow = {
name: 'Test Workflow',
nodes: [],
@@ -357,10 +357,10 @@ describe('n8n-validation', () => {
} as any;
const cleaned = cleanWorkflowForUpdate(workflow);
expect(cleaned).not.toHaveProperty('settings');
expect(cleaned.settings).toEqual({});
});
it('should remove settings entirely to prevent API errors (Issue #248 - corrected fix)', () => {
it('should replace settings with empty object to prevent API errors (Issue #248 - final fix)', () => {
const workflow = {
name: 'Test Workflow',
nodes: [],
@@ -375,11 +375,11 @@ describe('n8n-validation', () => {
const cleaned = cleanWorkflowForUpdate(workflow);
// Settings should be removed entirely (n8n API doesn't support updating settings)
expect(cleaned).not.toHaveProperty('settings');
// Settings replaced with empty object (satisfies both API versions)
expect(cleaned.settings).toEqual({});
});
it('should remove settings with callerPolicy (Issue #248 - API limitation)', () => {
it('should replace settings with callerPolicy (Issue #248 - API limitation)', () => {
const workflow = {
name: 'Test Workflow',
nodes: [],
@@ -393,11 +393,11 @@ describe('n8n-validation', () => {
const cleaned = cleanWorkflowForUpdate(workflow);
// Settings must be removed (n8n API rejects updates with settings properties)
expect(cleaned).not.toHaveProperty('settings');
// Settings replaced with empty object (n8n API rejects updates with settings properties)
expect(cleaned.settings).toEqual({});
});
it('should remove all settings regardless of content (Issue #248 - API design)', () => {
it('should replace all settings regardless of content (Issue #248 - API design)', () => {
const workflow = {
name: 'Test Workflow',
nodes: [],
@@ -417,9 +417,9 @@ describe('n8n-validation', () => {
const cleaned = cleanWorkflowForUpdate(workflow);
// Settings removed due to n8n API limitation (cannot update settings via API)
// Settings replaced with empty object due to n8n API limitation (cannot update settings via API)
// See: https://community.n8n.io/t/api-workflow-update-endpoint-doesnt-support-setting-callerpolicy/161916
expect(cleaned).not.toHaveProperty('settings');
expect(cleaned.settings).toEqual({});
});
it('should handle workflows without settings gracefully', () => {
@@ -430,7 +430,7 @@ describe('n8n-validation', () => {
} as any;
const cleaned = cleanWorkflowForUpdate(workflow);
expect(cleaned).not.toHaveProperty('settings');
expect(cleaned.settings).toEqual({});
});
});
});
@@ -1309,7 +1309,7 @@ describe('n8n-validation', () => {
expect(forUpdate).not.toHaveProperty('active');
expect(forUpdate).not.toHaveProperty('tags');
expect(forUpdate).not.toHaveProperty('meta');
expect(forUpdate).not.toHaveProperty('settings'); // Should not add defaults for update
expect(forUpdate.settings).toEqual({}); // Settings replaced with empty object for API compatibility
expect(validateWorkflowStructure(forUpdate)).toEqual([]);
});
});