diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/client/WorkflowTodo.tsx b/packages/plugins/@nocobase/plugin-workflow-manual/src/client/WorkflowTodo.tsx index beeae222c4..15e05a2849 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/client/WorkflowTodo.tsx +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/client/WorkflowTodo.tsx @@ -338,7 +338,8 @@ function FlowContextProvider(props) { appends: ['node', 'job', 'workflow', 'workflow.nodes', 'execution', 'execution.jobs'], }) .then(({ data }) => { - const { node, workflow: { nodes = [], ...workflow } = {}, execution, ...userJob } = data?.data ?? {}; + const { node, workflow: workflowWithNodes, execution, ...userJob } = data?.data ?? {}; + const { nodes = [], ...workflow } = workflowWithNodes || {}; linkNodes(nodes); setNode(node); setFlowContext({ @@ -682,6 +683,9 @@ function useTodoActionParams(status) { filter: { ...filter, userId: user?.data?.id, + workflowId: { + $ne: null, + }, }, appends: [ 'job.id', diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/Plugin.ts b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/Plugin.ts index 7338d0958e..acaca83335 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/Plugin.ts +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/Plugin.ts @@ -8,11 +8,9 @@ */ import { Plugin } from '@nocobase/server'; -import actions from '@nocobase/actions'; -import { HandlerType } from '@nocobase/resourcer'; -import WorkflowPlugin, { JOB_STATUS } from '@nocobase/plugin-workflow'; +import WorkflowPlugin from '@nocobase/plugin-workflow'; -import * as jobActions from './actions'; +import { submit } from './actions'; import ManualInstruction from './ManualInstruction'; import { MANUAL_TASK_TYPE } from '../common/constants'; @@ -27,28 +25,7 @@ interface WorkflowManualTaskModel { export default class extends Plugin { async load() { - this.app.resourceManager.define({ - name: 'workflowManualTasks', - actions: { - list: { - filter: { - $or: [ - { - 'workflow.enabled': true, - }, - { - 'workflow.enabled': false, - status: { - $ne: JOB_STATUS.PENDING, - }, - }, - ], - }, - handler: actions.list as HandlerType, - }, - ...jobActions, - }, - }); + this.app.resourceManager.registerActionHandler('workflowManualTasks:submit', submit); this.app.acl.allow('workflowManualTasks', ['list', 'get', 'submit'], 'loggedIn'); diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/__tests__/status.test.ts b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/__tests__/status.test.ts new file mode 100644 index 0000000000..26a62df0e3 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/__tests__/status.test.ts @@ -0,0 +1,147 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import Database from '@nocobase/database'; +import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow'; +import { getApp, sleep } from '@nocobase/plugin-workflow-test'; +import { MockServer } from '@nocobase/test'; + +// NOTE: skipped because time is not stable on github ci, but should work in local +describe('workflow > instructions > manual > assignees', () => { + let app: MockServer; + let agent; + let userAgents; + let db: Database; + let PostRepo; + let CommentRepo; + let WorkflowModel; + let workflow; + let UserModel; + let users; + let UserJobModel; + + beforeEach(async () => { + app = await getApp({ + plugins: ['users', 'auth', 'workflow-manual'], + }); + // await app.getPlugin('auth').install(); + agent = app.agent(); + db = app.db; + WorkflowModel = db.getCollection('workflows').model; + PostRepo = db.getCollection('posts').repository; + CommentRepo = db.getCollection('comments').repository; + UserModel = db.getCollection('users').model; + UserJobModel = db.getModel('workflowManualTasks'); + + users = await UserModel.bulkCreate([ + { id: 2, nickname: 'a' }, + { id: 3, nickname: 'b' }, + ]); + + userAgents = await Promise.all(users.map((user) => app.agent().login(user))); + + workflow = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts', + appends: ['category', 'category.posts'], + }, + }); + }); + + afterEach(() => app.destroy()); + + describe('workflow status', () => { + it('enabled', async () => { + const n1 = await workflow.createNode({ + type: 'manual', + config: { + assignees: [users[0].id], + forms: { + f1: { + actions: [{ status: JOB_STATUS.RESOLVED, key: 'resolve' }], + }, + }, + }, + }); + + const post = await PostRepo.create({ + values: { title: 't1', category: { title: 'c1' } }, + context: { state: { currentUser: users[0] } }, + }); + + await sleep(500); + + const [pending] = await workflow.getExecutions(); + expect(pending.status).toBe(EXECUTION_STATUS.STARTED); + + const usersJobs = await UserJobModel.findAll(); + expect(usersJobs.length).toBe(1); + + const res1 = await userAgents[0].resource('workflowManualTasks').submit({ + filterByTk: usersJobs[0].id, + values: { result: { f1: {}, _: 'resolve' } }, + }); + expect(res1.status).toBe(202); + + await sleep(500); + + const [resolved] = await workflow.getExecutions(); + expect(resolved.status).toBe(EXECUTION_STATUS.RESOLVED); + const [j1] = await resolved.getJobs(); + expect(j1.status).toBe(JOB_STATUS.RESOLVED); + }); + + it('disabled after triggered', async () => { + const n1 = await workflow.createNode({ + type: 'manual', + config: { + assignees: [users[0].id], + forms: { + f1: { + actions: [{ status: JOB_STATUS.RESOLVED, key: 'resolve' }], + }, + }, + }, + }); + + const post = await PostRepo.create({ + values: { title: 't1', category: { title: 'c1' } }, + context: { state: { currentUser: users[0] } }, + }); + + await sleep(500); + + const [pending] = await workflow.getExecutions(); + expect(pending.status).toBe(EXECUTION_STATUS.STARTED); + + const usersJobs = await UserJobModel.findAll(); + expect(usersJobs.length).toBe(1); + + await workflow.update({ + enabled: false, + }); + + const res1 = await userAgents[0].resource('workflowManualTasks').submit({ + filterByTk: usersJobs[0].id, + values: { result: { f1: {}, _: 'resolve' } }, + }); + expect(res1.status).toBe(202); + + await sleep(500); + + const [resolved] = await workflow.getExecutions(); + expect(resolved.status).toBe(EXECUTION_STATUS.RESOLVED); + const [j1] = await resolved.getJobs(); + expect(j1.status).toBe(JOB_STATUS.RESOLVED); + }); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/actions.ts b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/actions.ts index 33dccdbeb5..3335d55ca0 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/actions.ts +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/actions.ts @@ -47,7 +47,7 @@ export async function submit(context: Context, next) { task.status !== JOB_STATUS.PENDING || task.job.status !== JOB_STATUS.PENDING || task.execution.status !== EXECUTION_STATUS.STARTED || - !task.workflow.enabled || + // !task.workflow.enabled || !actionKey || actionItem?.status == null ) { diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/collections/workflowManualTasks.ts b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/collections/workflowManualTasks.ts index ba1c22ddb9..a2fe7d5427 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/server/collections/workflowManualTasks.ts +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/server/collections/workflowManualTasks.ts @@ -74,6 +74,7 @@ export default defineCollection({ }, }, }, + onDelete: 'SET NULL', }, { type: 'integer',