diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index 37341cbe914..4b7e34b8e83 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -405,6 +405,15 @@ export class WorkflowsController { let updateData = new WorkflowEntity(); const { tags, parentFolderId, ...rest } = req.body; + + // TODO: Add zod validation for entire `rest` object before assigning to `updateData` + if ( + rest.settings?.timeSavedMode !== undefined && + !['fixed', 'dynamic'].includes(rest.settings.timeSavedMode) + ) { + throw new BadRequestError('Invalid timeSavedMode'); + } + Object.assign(updateData, rest); const isSharingEnabled = this.license.isSharingEnabled(); diff --git a/packages/cli/test/integration/workflows/workflows.controller.test.ts b/packages/cli/test/integration/workflows/workflows.controller.test.ts index 8ea5b3929eb..d42db96d52a 100644 --- a/packages/cli/test/integration/workflows/workflows.controller.test.ts +++ b/packages/cli/test/integration/workflows/workflows.controller.test.ts @@ -2458,6 +2458,8 @@ describe('PATCH /workflows/:workflowId', () => { saveDataSuccessExecution: 'all', executionTimeout: 3600, timezone: 'America/New_York', + timeSavedMode: 'fixed', + timeSavedPerExecution: 10, }, }; @@ -2468,6 +2470,8 @@ describe('PATCH /workflows/:workflowId', () => { } = response.body; expect(response.statusCode).toBe(200); + expect(response.body.data.settings.timeSavedMode).toBe('fixed'); + expect(response.body.data.settings.timeSavedPerExecution).toBe(10); expect(id).toBe(workflow.id); expect(await workflowHistoryRepository.count({ where: { workflowId: id } })).toBe(2); @@ -2723,6 +2727,22 @@ describe('PATCH /workflows/:workflowId', () => { expect(updatedWorkflow.parentFolder).toBe(null); }); + test('should fail if an invalid timeSavedMode is provided', async () => { + const workflow = await createWorkflow({}, owner); + const payload = { + name: 'name updated', + versionId: workflow.versionId, + settings: { + timeSavedMode: 'invalid' as 'fixed' | 'dynamic', + }, + }; + + const response = await authOwnerAgent.patch(`/workflows/${workflow.id}`).send(payload); + + expect(response.statusCode).toBe(400); + expect(response.body.message).toBe('Invalid timeSavedMode'); + }); + test('should fail if trying update workflow parent folder with a folder that does not belong to project', async () => { const ownerPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id); const memberPersonalProject = await projectRepository.getPersonalProjectForUserOrFail( diff --git a/packages/workflow/src/interfaces.ts b/packages/workflow/src/interfaces.ts index a1dcbff5472..f0e7ddad055 100644 --- a/packages/workflow/src/interfaces.ts +++ b/packages/workflow/src/interfaces.ts @@ -2738,6 +2738,7 @@ export interface IWorkflowSettings { executionTimeout?: number; executionOrder?: 'v0' | 'v1'; timeSavedPerExecution?: number; + timeSavedMode?: 'fixed' | 'dynamic'; availableInMCP?: boolean; }