From 42db519fa1ece772fa5afaa64a8ecc0ba7e6ea8f Mon Sep 17 00:00:00 2001 From: Junyi Date: Wed, 26 Oct 2022 15:33:56 +0800 Subject: [PATCH] fix(plugin-workflow): fix workflow update action (#964) --- .../src/server/__tests__/Plugin.test.ts | 171 ----------- .../__tests__/actions/workflows.test.ts | 276 ++++++++++++++++++ .../workflow/src/server/actions/workflows.ts | 16 +- 3 files changed, 290 insertions(+), 173 deletions(-) create mode 100644 packages/plugins/workflow/src/server/__tests__/actions/workflows.test.ts diff --git a/packages/plugins/workflow/src/server/__tests__/Plugin.test.ts b/packages/plugins/workflow/src/server/__tests__/Plugin.test.ts index 590490ecc..2831930cc 100644 --- a/packages/plugins/workflow/src/server/__tests__/Plugin.test.ts +++ b/packages/plugins/workflow/src/server/__tests__/Plugin.test.ts @@ -6,7 +6,6 @@ import { getApp } from '.'; describe('workflow > Plugin', () => { let app: MockServer; - let agent; let db: Database; let PostModel; let PostRepo; @@ -14,7 +13,6 @@ describe('workflow > Plugin', () => { beforeEach(async () => { app = await getApp(); - agent = app.agent(); db = app.db; WorkflowModel = db.getCollection('workflows').model; PostModel = db.getCollection('posts').model; @@ -155,173 +153,4 @@ describe('workflow > Plugin', () => { expect(c2).toBe(1); }); }); - - describe('revision', () => { - it('create revision', async () => { - const w1 = await WorkflowModel.create({ - enabled: true, - type: 'collection', - config: { - mode: 1, - collection: 'posts' - } - }); - - const { body, status } = await agent.resource(`workflows`).revision({ - filterByTk: w1.id - }); - - expect(status).toBe(200); - const { data: w2 } = body; - expect(w2.config).toMatchObject(w1.config); - expect(w2.key).toBe(w1.key); - expect(w2.current).toBeFalsy(); - expect(w2.enabled).toBe(false); - - const p1 = await PostRepo.create({ values: { title: 't1' } }); - - await WorkflowModel.update({ - enabled: true - }, { - where: { - id: w2.id - }, - individualHooks: true - }); - - const [w1next, w2next] = await WorkflowModel.findAll({ - order: [['id', 'ASC']] - }); - - expect(w1next.enabled).toBe(false); - expect(w1next.current).toBe(null); - expect(w2next.enabled).toBe(true); - expect(w2next.current).toBe(true); - - const p2 = await PostRepo.create({ values: { title: 't2' } }); - - const [e1] = await w1next.getExecutions(); - const [e2] = await w2next.getExecutions(); - expect(e1.key).toBe(e2.key); - expect(e1.workflowId).toBe(w1.id); - expect(e2.workflowId).toBe(w2.id); - }); - - it('revision with nodes', async () => { - const w1 = await WorkflowModel.create({ - enabled: true, - type: 'collection', - config: { - mode: 1, - collection: 'posts' - } - }); - - const n1 = await w1.createNode({ - type: 'echo' - }); - const n2 = await w1.createNode({ - type: 'calculation', - config: { - calculation: { - calculator: 'add', - operands: [ - { - type: '$jobsMapByNodeId', - options: { - nodeId: n1.id, - path: 'data.read' - } - }, - { - value: `{{$jobsMapByNodeId.${n1.id}.data.read}}` - } - ] - } - }, - upstreamId: n1.id - }); - await n1.setDownstream(n2); - - const { body } = await agent.resource(`workflows`).revision({ - filterByTk: w1.id - }); - const w2 = await WorkflowModel.findByPk(body.data.id, { - include: [ - 'nodes' - ] - }); - - const n1_2 = w2.nodes.find(n => !n.upstreamId); - const n2_2 = w2.nodes.find(n => !n.downstreamId); - - expect(n1_2.type).toBe('echo'); - expect(n2_2.type).toBe('calculation'); - expect(n2_2.config).toMatchObject({ - calculation: { - calculator: 'add', - operands: [ - { - type: '$jobsMapByNodeId', - options: { - nodeId: n1_2.id, - path: 'data.read' - } - }, - { - value: `{{$jobsMapByNodeId.${n1_2.id}.data.read}}` - } - ] - } - }); - - await w2.update({ enabled: true }); - - await PostRepo.create({ - values: { title: 't1', read: 1 } - }); - - const [execution] = await w2.getExecutions(); - const [echo, calculation] = await execution.getJobs({ order: [['id', 'ASC']] }); - expect(calculation.result).toBe(2); - }); - - it('revision with using of deleted nodes', async () => { - const w1 = await WorkflowModel.create({ - enabled: true, - type: 'collection', - config: { - mode: 1, - collection: 'posts' - } - }); - - const n2 = await w1.createNode({ - type: 'calculation', - config: { - calculation: { - calculator: 'add', - operands: [ - { - type: '$jobsMapByNodeId', - options: { - nodeId: 0, - path: 'data.read' - } - }, - { - value: `{{$jobsMapByNodeId.0.data.read}}` - } - ] - } - }, - }); - - const { status } = await agent.resource(`workflows`).revision({ - filterByTk: w1.id - }); - - expect(status).toBe(400); - }); - }); }); diff --git a/packages/plugins/workflow/src/server/__tests__/actions/workflows.test.ts b/packages/plugins/workflow/src/server/__tests__/actions/workflows.test.ts new file mode 100644 index 000000000..aa1936bb1 --- /dev/null +++ b/packages/plugins/workflow/src/server/__tests__/actions/workflows.test.ts @@ -0,0 +1,276 @@ +import { MockServer } from '@nocobase/test'; +import Database from '@nocobase/database'; +import { getApp } from '..'; + + + +describe('workflow > actions > workflows', () => { + let app: MockServer; + let agent; + let db: Database; + let PostModel; + let PostRepo; + let WorkflowModel; + + beforeEach(async () => { + app = await getApp(); + agent = app.agent(); + db = app.db; + WorkflowModel = db.getCollection('workflows').model; + PostModel = db.getCollection('posts').model; + PostRepo = db.getCollection('posts').repository; + }); + + afterEach(() => app.destroy()); + + // describe('create', () => { + // }); + + describe('update', () => { + it('update unexecuted workflow should be ok', async () => { + const workflow = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts' + } + }); + + const { status } = await agent.resource('workflows').update({ + filterByTk: workflow.id, + values: { + config: { + mode: 1, + collection: 'tags' + } + } + }); + + expect(status).toBe(200); + }); + + it('update executed workflow should not be ok', async () => { + const workflow = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts' + } + }); + + const p1 = await PostRepo.create({ values: { title: 't1' } }); + const c1 = await workflow.countExecutions(); + expect(c1).toBe(1); + + const { status } = await agent.resource('workflows').update({ + filterByTk: workflow.id, + values: { + config: { + mode: 1, + collection: 'tags' + } + } + }); + expect(status).toBe(400); + }); + + it('only update executed workflow with "enabled" should be ok', async () => { + const workflow = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts' + } + }); + + const p1 = await PostRepo.create({ values: { title: 't1' } }); + const c1 = await workflow.countExecutions(); + expect(c1).toBe(1); + + const { status } = await agent.resource('workflows').update({ + filterByTk: workflow.id, + values: { + enabled: false, + key: workflow.key + } + }); + expect(status).toBe(200); + + const p2 = await PostRepo.create({ values: { title: 't2' } }); + const c2 = await workflow.countExecutions(); + expect(c2).toBe(1); + }); + }); + + describe('revision', () => { + it('create revision', async () => { + const w1 = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts' + } + }); + + const { body, status } = await agent.resource(`workflows`).revision({ + filterByTk: w1.id + }); + + expect(status).toBe(200); + const { data: w2 } = body; + expect(w2.config).toMatchObject(w1.config); + expect(w2.key).toBe(w1.key); + expect(w2.current).toBeFalsy(); + expect(w2.enabled).toBe(false); + + const p1 = await PostRepo.create({ values: { title: 't1' } }); + + await WorkflowModel.update({ + enabled: true + }, { + where: { + id: w2.id + }, + individualHooks: true + }); + + const [w1next, w2next] = await WorkflowModel.findAll({ + order: [['id', 'ASC']] + }); + + expect(w1next.enabled).toBe(false); + expect(w1next.current).toBe(null); + expect(w2next.enabled).toBe(true); + expect(w2next.current).toBe(true); + + const p2 = await PostRepo.create({ values: { title: 't2' } }); + + const [e1] = await w1next.getExecutions(); + const [e2] = await w2next.getExecutions(); + expect(e1.key).toBe(e2.key); + expect(e1.workflowId).toBe(w1.id); + expect(e2.workflowId).toBe(w2.id); + }); + + it('revision with nodes', async () => { + const w1 = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts' + } + }); + + const n1 = await w1.createNode({ + type: 'echo' + }); + const n2 = await w1.createNode({ + type: 'calculation', + config: { + calculation: { + calculator: 'add', + operands: [ + { + type: '$jobsMapByNodeId', + options: { + nodeId: n1.id, + path: 'data.read' + } + }, + { + value: `{{$jobsMapByNodeId.${n1.id}.data.read}}` + } + ] + } + }, + upstreamId: n1.id + }); + await n1.setDownstream(n2); + + const { body } = await agent.resource(`workflows`).revision({ + filterByTk: w1.id + }); + const w2 = await WorkflowModel.findByPk(body.data.id, { + include: [ + 'nodes' + ] + }); + + const n1_2 = w2.nodes.find(n => !n.upstreamId); + const n2_2 = w2.nodes.find(n => !n.downstreamId); + + expect(n1_2.type).toBe('echo'); + expect(n2_2.type).toBe('calculation'); + expect(n2_2.config).toMatchObject({ + calculation: { + calculator: 'add', + operands: [ + { + type: '$jobsMapByNodeId', + options: { + nodeId: n1_2.id, + path: 'data.read' + } + }, + { + value: `{{$jobsMapByNodeId.${n1_2.id}.data.read}}` + } + ] + } + }); + + await w2.update({ enabled: true }); + + await PostRepo.create({ + values: { title: 't1', read: 1 } + }); + + const [execution] = await w2.getExecutions(); + const [echo, calculation] = await execution.getJobs({ order: [['id', 'ASC']] }); + expect(calculation.result).toBe(2); + }); + + it('revision with using of deleted nodes', async () => { + const w1 = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts' + } + }); + + const n2 = await w1.createNode({ + type: 'calculation', + config: { + calculation: { + calculator: 'add', + operands: [ + { + type: '$jobsMapByNodeId', + options: { + nodeId: 0, + path: 'data.read' + } + }, + { + value: `{{$jobsMapByNodeId.0.data.read}}` + } + ] + } + }, + }); + + const { status } = await agent.resource(`workflows`).revision({ + filterByTk: w1.id + }); + + expect(status).toBe(400); + }); + }); +}); diff --git a/packages/plugins/workflow/src/server/actions/workflows.ts b/packages/plugins/workflow/src/server/actions/workflows.ts index 447f73df4..b608afccb 100644 --- a/packages/plugins/workflow/src/server/actions/workflows.ts +++ b/packages/plugins/workflow/src/server/actions/workflows.ts @@ -1,6 +1,18 @@ -import { Context, utils } from '@nocobase/actions'; - +import actions, { Context, utils } from '@nocobase/actions'; +import { Repository } from '@nocobase/database'; +export async function update(context: Context, next) { + const repository = utils.getRepositoryFromParams(context) as Repository; + const { filterByTk, values } = context.action.params; + // only enable/disable + if (Object.keys(values).sort().join() !== 'enabled,key'){ + const workflow = await repository.findById(filterByTk); + if (workflow.get('executed')) { + return context.throw(400, 'executed workflow can not be updated'); + } + } + return actions.update(context, next); +} function typeOf(value) { if (Array.isArray(value)) {