refactor(plugin-workflow): split transaction for collection trigger (#1080)

* refactor(plugin-workflow): split transaction for collection trigger

* fix(plugin-workflow): fix delay test case

* refactor(plugin-workflow): use micro queue to dispatch executions

* fix(plugin-workflow): fix usages of private api

* fix(plugin-workflow): fix wrong variable
This commit is contained in:
Junyi 2022-11-14 20:05:01 +08:00 committed by GitHub
parent a9d614700c
commit c470209ecd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 320 additions and 71 deletions

View File

@ -21,14 +21,11 @@ export interface UserPluginConfig {
export default class UsersPlugin extends Plugin<UserPluginConfig> { export default class UsersPlugin extends Plugin<UserPluginConfig> {
public jwtService: JwtService; public jwtService: JwtService;
public tokenMiddleware: Middleware;
public authenticators: Registry<HandlerType> = new Registry(); public authenticators: Registry<HandlerType> = new Registry();
constructor(app, options) { constructor(app, options) {
super(app, options); super(app, options);
this.jwtService = new JwtService(options?.jwt || {}); this.jwtService = new JwtService(options?.jwt || {});
this.tokenMiddleware = new Middleware(parseToken);
} }
async beforeLoad() { async beforeLoad() {

View File

@ -22,6 +22,8 @@ export default class WorkflowPlugin extends Plugin {
triggers: Registry<Trigger> = new Registry(); triggers: Registry<Trigger> = new Registry();
calculators = calculators; calculators = calculators;
extensions = extensions; extensions = extensions;
executing: Promise<any> = null;
pending: [ExecutionModel, JobModel][] = [];
onBeforeSave = async (instance: WorkflowModel, options) => { onBeforeSave = async (instance: WorkflowModel, options) => {
const Model = <typeof WorkflowModel>instance.constructor; const Model = <typeof WorkflowModel>instance.constructor;
@ -127,39 +129,38 @@ export default class WorkflowPlugin extends Plugin {
} }
} }
async trigger(workflow: WorkflowModel, context: Object, options: Transactionable & { context?: any } = {}): Promise<ExecutionModel | null> { public async trigger(workflow: WorkflowModel, context: Object, options: Transactionable & { context?: any } = {}): Promise<void> {
// `null` means not to trigger // `null` means not to trigger
if (context === null) { if (context == null) {
return null; return null;
} }
let transaction = null; // @ts-ignore
const transaction = options.transaction && !options.transaction.finished
if (workflow.useTransaction) { ? options.transaction
// @ts-ignore : await (<typeof WorkflowModel>workflow.constructor).database.sequelize.transaction();
transaction = options.transaction && !options.transaction.finished
? options.transaction
: await (<typeof WorkflowModel>workflow.constructor).database.sequelize.transaction();
if (options.context?.transaction) {
const existed = await workflow.countExecutions({ const existed = await workflow.countExecutions({
where: { where: {
transaction: transaction.id transaction: options.context.transaction
}, },
transaction transaction
}); });
if (existed) { if (existed) {
console.warn(`workflow ${workflow.id} has already been triggered in same execution (${transaction.id}), and newly triggering will be skipped.`); console.warn(`workflow ${workflow.id} has already been triggered in same execution (${options.context.transaction}), and newly triggering will be skipped.`);
return null; return;
} }
} }
const execution = await workflow.createExecution({ const execution = await workflow.createExecution({
context, context,
key: workflow.key, key: workflow.key,
status: EXECUTION_STATUS.STARTED, status: EXECUTION_STATUS.CREATED,
useTransaction: workflow.useTransaction, useTransaction: workflow.useTransaction,
transaction: transaction?.id // @ts-ignore
transaction: options.context?.transaction ?? transaction?.id
}, { transaction }); }, { transaction });
console.log('workflow triggered:', new Date(), workflow.id, execution.id); console.log('workflow triggered:', new Date(), workflow.id, execution.id);
@ -187,27 +188,61 @@ export default class WorkflowPlugin extends Plugin {
execution.workflow = workflow; execution.workflow = workflow;
const processor = this.createProcessor(execution, { transaction, _context: options.context });
await processor.start();
// @ts-ignore // @ts-ignore
if (transaction && (!options.transaction || options.transaction.finished)) { if (transaction && (!options.transaction || options.transaction.finished)) {
await transaction.commit(); await transaction.commit();
} }
return execution; setTimeout(() => this.dispatch(execution), 0);
} }
async resume(job) { public async resume(job) {
if (!job.execution) { if (!job.execution) {
job.execution = await job.getExecution(); job.execution = await job.getExecution();
} }
const processor = this.createProcessor(job.execution);
return processor.resume(job); setTimeout(() => this.dispatch(job.execution, job), 0);
} }
createProcessor(execution: ExecutionModel, options = {}): Processor { private async dispatch(execution?: ExecutionModel, job?: JobModel) {
if (this.executing) {
if (job) {
this.pending.push([execution, job]);
}
return;
}
if (!execution) {
execution = await this.db.getRepository('executions').findOne({
filter: {
status: EXECUTION_STATUS.CREATED
},
sort: 'createdAt'
}) as ExecutionModel;
if (!execution) {
return;
}
}
if (execution.status === EXECUTION_STATUS.CREATED) {
await execution.update({ status: EXECUTION_STATUS.STARTED });
}
const processor = this.createProcessor(execution, { _context: { transaction: execution.transaction} });
this.executing = job ? processor.resume(job) : processor.start();
await this.executing;
this.executing = null;
setTimeout(() => {
const args = this.pending.length ? this.pending.shift() : [];
this.dispatch(...args);
});
}
private createProcessor(execution: ExecutionModel, options = {}): Processor {
return new Processor(execution, { ...options, plugin: this }); return new Processor(execution, { ...options, plugin: this });
} }
} }

View File

@ -75,17 +75,9 @@ export default class Processor {
const { sequelize } = (<typeof ExecutionModel>this.execution.constructor).database; const { sequelize } = (<typeof ExecutionModel>this.execution.constructor).database;
// @ts-ignore // @ts-ignore
const transaction = options.transaction && !options.transaction.finished return options.transaction && !options.transaction.finished
? options.transaction ? options.transaction
: await sequelize.transaction(); : await sequelize.transaction();
// @ts-ignore
if (this.execution.transaction !== transaction.id) {
// @ts-ignore
await this.execution.update({ transaction: transaction.id }, { transaction });
}
return transaction;
} }
async prepare(commit?: boolean) { async prepare(commit?: boolean) {

View File

@ -1,6 +1,6 @@
import { MockServer } from '@nocobase/test'; import { MockServer } from '@nocobase/test';
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { getApp } from '.'; import { getApp, sleep } from '.';
@ -61,6 +61,9 @@ describe('workflow > Plugin', () => {
expect(workflow.current).toBe(true); expect(workflow.current).toBe(true);
const p1 = await PostRepo.create({ values: { title: 't1' } }); const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const count = await workflow.countExecutions(); const count = await workflow.countExecutions();
expect(count).toBe(0); expect(count).toBe(0);
@ -68,6 +71,9 @@ describe('workflow > Plugin', () => {
expect(workflow.current).toBe(true); expect(workflow.current).toBe(true);
const p2 = await PostRepo.create({ values: { title: 't2' } }); const p2 = await PostRepo.create({ values: { title: 't2' } });
await sleep(500);
const executions = await workflow.getExecutions(); const executions = await workflow.getExecutions();
expect(executions.length).toBe(1); expect(executions.length).toBe(1);
}); });
@ -82,7 +88,11 @@ describe('workflow > Plugin', () => {
} }
}); });
expect(workflow.current).toBe(true); expect(workflow.current).toBe(true);
const p1 = await PostRepo.create({ values: { title: 't1' } }); const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const c1 = await workflow.countExecutions(); const c1 = await workflow.countExecutions();
expect(c1).toBe(1); expect(c1).toBe(1);
@ -90,6 +100,9 @@ describe('workflow > Plugin', () => {
expect(workflow.current).toBe(true); expect(workflow.current).toBe(true);
const p2 = await PostRepo.create({ values: { title: 't2' } }); const p2 = await PostRepo.create({ values: { title: 't2' } });
await sleep(500);
const c2 = await workflow.countExecutions(); const c2 = await workflow.countExecutions();
expect(c2).toBe(1); expect(c2).toBe(1);
}); });
@ -105,6 +118,9 @@ describe('workflow > Plugin', () => {
}); });
const p1 = await PostRepo.create({ values: { title: 't1' } }); const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const c1 = await workflow.countExecutions(); const c1 = await workflow.countExecutions();
expect(c1).toBe(1); expect(c1).toBe(1);
@ -114,6 +130,9 @@ describe('workflow > Plugin', () => {
expect(workflow.current).toBe(true); expect(workflow.current).toBe(true);
const p2 = await PostRepo.create({ values: { title: 't2' } }); const p2 = await PostRepo.create({ values: { title: 't2' } });
await sleep(500);
const c2 = await workflow.countExecutions(); const c2 = await workflow.countExecutions();
expect(c2).toBe(1); expect(c2).toBe(1);
@ -123,6 +142,9 @@ describe('workflow > Plugin', () => {
expect(workflow.current).toBe(true); expect(workflow.current).toBe(true);
const p3 = await PostRepo.create({ values: { title: 't3' } }); const p3 = await PostRepo.create({ values: { title: 't3' } });
await sleep(500);
const c3 = await workflow.countExecutions(); const c3 = await workflow.countExecutions();
expect(c3).toBe(2); expect(c3).toBe(2);
}); });
@ -138,6 +160,9 @@ describe('workflow > Plugin', () => {
}); });
const p1 = await PostRepo.create({ values: { title: 't1' } }); const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const c1 = await workflow.countExecutions(); const c1 = await workflow.countExecutions();
expect(c1).toBe(1); expect(c1).toBe(1);
@ -149,6 +174,9 @@ describe('workflow > Plugin', () => {
}); });
const p2 = await PostRepo.create({ values: { title: 't2' } }); const p2 = await PostRepo.create({ values: { title: 't2' } });
await sleep(500);
const c2 = await workflow.countExecutions(); const c2 = await workflow.countExecutions();
expect(c2).toBe(1); expect(c2).toBe(1);
}); });

View File

@ -1,6 +1,6 @@
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { Application } from '@nocobase/server'; import { Application } from '@nocobase/server';
import { getApp } from '.'; import { getApp, sleep } from '.';
import { BRANCH_INDEX, EXECUTION_STATUS, JOB_STATUS } from '../constants'; import { BRANCH_INDEX, EXECUTION_STATUS, JOB_STATUS } from '../constants';
@ -37,6 +37,8 @@ describe('workflow > Processor', () => {
it('empty workflow without any nodes', async () => { it('empty workflow without any nodes', async () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.context.data.title).toEqual(post.title); expect(execution.context.data.title).toEqual(post.title);
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
@ -49,6 +51,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
@ -65,6 +69,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.context.data.title).toEqual(post.title); expect(execution.context.data.title).toEqual(post.title);
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
@ -92,6 +98,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.context.data.title).toEqual(post.title); expect(execution.context.data.title).toEqual(post.title);
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
@ -110,6 +118,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.REJECTED); expect(execution.status).toEqual(EXECUTION_STATUS.REJECTED);
@ -136,6 +146,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.STARTED); expect(execution.status).toEqual(EXECUTION_STATUS.STARTED);
const [pending] = await execution.getJobs(); const [pending] = await execution.getJobs();
@ -143,8 +155,11 @@ describe('workflow > Processor', () => {
expect(pending.result).toEqual(null); expect(pending.result).toEqual(null);
pending.set('result', 123); pending.set('result', 123);
const processor = plugin.createProcessor(execution); pending.execution = execution;
await processor.resume(pending); await plugin.resume(pending);
await sleep(500);
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -167,6 +182,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.STARTED); expect(execution.status).toEqual(EXECUTION_STATUS.STARTED);
const [pending] = await execution.getJobs(); const [pending] = await execution.getJobs();
@ -174,8 +191,11 @@ describe('workflow > Processor', () => {
expect(pending.result).toEqual(null); expect(pending.result).toEqual(null);
pending.set('result', 123); pending.set('result', 123);
const processor = plugin.createProcessor(execution); pending.execution = execution;
await processor.resume(pending); await plugin.resume(pending);
await sleep(500);
expect(execution.status).toEqual(EXECUTION_STATUS.REJECTED); expect(execution.status).toEqual(EXECUTION_STATUS.REJECTED);
const jobs = await execution.getJobs(); const jobs = await execution.getJobs();
@ -206,6 +226,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
@ -237,13 +259,17 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.STARTED); expect(execution.status).toEqual(EXECUTION_STATUS.STARTED);
const [pending] = await execution.getJobs({ where: { nodeId: n2.id } }); const [pending] = await execution.getJobs({ where: { nodeId: n2.id } });
pending.set('result', 123); pending.set('result', 123);
const processor = plugin.createProcessor(execution); pending.execution = execution;
await processor.resume(pending); await plugin.resume(pending);
await sleep(500);
const jobs = await execution.getJobs(); const jobs = await execution.getJobs();
expect(jobs.length).toEqual(3); expect(jobs.length).toEqual(3);
@ -270,13 +296,18 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.STARTED); expect(execution.status).toEqual(EXECUTION_STATUS.STARTED);
const [pending] = await execution.getJobs({ where: { nodeId: n2.id } }); const [pending] = await execution.getJobs({ where: { nodeId: n2.id } });
pending.set('result', 123); pending.set('result', 123);
const processor = plugin.createProcessor(execution); pending.execution = execution;
await processor.resume(pending); await plugin.resume(pending);
await sleep(500);
expect(execution.status).toEqual(EXECUTION_STATUS.REJECTED); expect(execution.status).toEqual(EXECUTION_STATUS.REJECTED);
const jobs = await execution.getJobs(); const jobs = await execution.getJobs();
@ -319,6 +350,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.STARTED); expect(execution.status).toEqual(EXECUTION_STATUS.STARTED);
@ -327,8 +360,10 @@ describe('workflow > Processor', () => {
const pending = pendingJobs.find(item => item.nodeId === n3.id ); const pending = pendingJobs.find(item => item.nodeId === n3.id );
pending.set('result', 123); pending.set('result', 123);
const processor = plugin.createProcessor(execution); pending.execution = execution;
await processor.resume(pending); await plugin.resume(pending);
await sleep(500);
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -369,6 +404,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [e1] = await workflow.getExecutions(); const [e1] = await workflow.getExecutions();
expect(e1.status).toEqual(EXECUTION_STATUS.STARTED); expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
@ -377,8 +414,10 @@ describe('workflow > Processor', () => {
const pending = pendingJobs.find(item => item.nodeId === n2.id ); const pending = pendingJobs.find(item => item.nodeId === n2.id );
pending.set('result', 123); pending.set('result', 123);
const processor = plugin.createProcessor(e1); pending.execution = e1;
await processor.resume(pending); await plugin.resume(pending);
await sleep(500);
const [e2] = await workflow.getExecutions(); const [e2] = await workflow.getExecutions();
expect(e2.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(e2.status).toEqual(EXECUTION_STATUS.RESOLVED);
@ -403,6 +442,8 @@ describe('workflow > Processor', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const posts = await PostRepo.find(); const posts = await PostRepo.find();
expect(posts.length).toBe(2); expect(posts.length).toBe(2);

View File

@ -1,6 +1,6 @@
import { MockServer } from '@nocobase/test'; import { MockServer } from '@nocobase/test';
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { getApp } from '..'; import { getApp, sleep } from '..';
@ -61,6 +61,9 @@ describe('workflow > actions > workflows', () => {
}); });
const p1 = await PostRepo.create({ values: { title: 't1' } }); const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const c1 = await workflow.countExecutions(); const c1 = await workflow.countExecutions();
expect(c1).toBe(1); expect(c1).toBe(1);
@ -87,6 +90,9 @@ describe('workflow > actions > workflows', () => {
}); });
const p1 = await PostRepo.create({ values: { title: 't1' } }); const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const c1 = await workflow.countExecutions(); const c1 = await workflow.countExecutions();
expect(c1).toBe(1); expect(c1).toBe(1);
@ -100,6 +106,9 @@ describe('workflow > actions > workflows', () => {
expect(status).toBe(200); expect(status).toBe(200);
const p2 = await PostRepo.create({ values: { title: 't2' } }); const p2 = await PostRepo.create({ values: { title: 't2' } });
await sleep(500);
const c2 = await workflow.countExecutions(); const c2 = await workflow.countExecutions();
expect(c2).toBe(1); expect(c2).toBe(1);
}); });
@ -129,6 +138,8 @@ describe('workflow > actions > workflows', () => {
const p1 = await PostRepo.create({ values: { title: 't1' } }); const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
await WorkflowModel.update({ await WorkflowModel.update({
enabled: true enabled: true
}, { }, {
@ -149,6 +160,8 @@ describe('workflow > actions > workflows', () => {
const p2 = await PostRepo.create({ values: { title: 't2' } }); const p2 = await PostRepo.create({ values: { title: 't2' } });
await sleep(500);
const [e1] = await w1next.getExecutions(); const [e1] = await w1next.getExecutions();
const [e2] = await w2next.getExecutions(); const [e2] = await w2next.getExecutions();
expect(e1.key).toBe(e2.key); expect(e1.key).toBe(e2.key);
@ -230,6 +243,8 @@ describe('workflow > actions > workflows', () => {
values: { title: 't1', read: 1 } values: { title: 't1', read: 1 }
}); });
await sleep(500);
const [execution] = await w2.getExecutions(); const [execution] = await w2.getExecutions();
const [echo, calculation] = await execution.getJobs({ order: [['id', 'ASC']] }); const [echo, calculation] = await execution.getJobs({ order: [['id', 'ASC']] });
expect(calculation.result).toBe(2); expect(calculation.result).toBe(2);

View File

@ -1,6 +1,6 @@
import { Application } from '@nocobase/server'; import { Application } from '@nocobase/server';
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { getApp } from '..'; import { getApp, sleep } from '..';
@ -47,6 +47,9 @@ describe('workflow > instructions > calculation', () => {
}); });
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(2); expect(job.result).toBe(2);
@ -67,6 +70,9 @@ describe('workflow > instructions > calculation', () => {
}); });
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(1); expect(job.result).toBe(1);
@ -87,6 +93,9 @@ describe('workflow > instructions > calculation', () => {
}); });
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(1); expect(job.result).toBe(1);
@ -114,6 +123,9 @@ describe('workflow > instructions > calculation', () => {
await n1.setDownstream(n2); await n1.setDownstream(n2);
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [n1Job, n2Job] = await execution.getJobs({ order: [['id', 'ASC']]}); const [n1Job, n2Job] = await execution.getJobs({ order: [['id', 'ASC']]});
expect(n2Job.result).toBe(1); expect(n2Job.result).toBe(1);
@ -141,6 +153,9 @@ describe('workflow > instructions > calculation', () => {
await n1.setDownstream(n2); await n1.setDownstream(n2);
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [n1Job, n2Job] = await execution.getJobs({ order: [['id', 'ASC']]}); const [n1Job, n2Job] = await execution.getJobs({ order: [['id', 'ASC']]});
expect(n2Job.result).toBe(1); expect(n2Job.result).toBe(1);
@ -161,6 +176,9 @@ describe('workflow > instructions > calculation', () => {
}); });
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(2); expect(job.result).toBe(2);
@ -192,6 +210,9 @@ describe('workflow > instructions > calculation', () => {
}); });
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(-1); expect(job.result).toBe(-1);

View File

@ -1,6 +1,6 @@
import { Application } from '@nocobase/server'; import { Application } from '@nocobase/server';
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { getApp } from '..'; import { getApp, sleep } from '..';
import { EXECUTION_STATUS, BRANCH_INDEX } from '../../constants'; import { EXECUTION_STATUS, BRANCH_INDEX } from '../../constants';
@ -67,6 +67,8 @@ describe('workflow > instructions > condition', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
@ -104,6 +106,8 @@ describe('workflow > instructions > condition', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED); expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
@ -142,6 +146,8 @@ describe('workflow > instructions > condition', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(true); expect(job.result).toBe(true);
@ -171,6 +177,8 @@ describe('workflow > instructions > condition', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(false); expect(job.result).toBe(false);
@ -200,6 +208,8 @@ describe('workflow > instructions > condition', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions({ include: ['jobs'] }); const [execution] = await workflow.getExecutions({ include: ['jobs'] });
const [job] = execution.jobs; const [job] = execution.jobs;
expect(job.result).toBe(true); expect(job.result).toBe(true);
@ -229,6 +239,8 @@ describe('workflow > instructions > condition', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(false); expect(job.result).toBe(false);
@ -263,6 +275,8 @@ describe('workflow > instructions > condition', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(false); expect(job.result).toBe(false);

View File

@ -1,6 +1,6 @@
import { Application } from '@nocobase/server'; import { Application } from '@nocobase/server';
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { getApp } from '..'; import { getApp, sleep } from '..';
@ -47,6 +47,8 @@ describe('workflow > instructions > create', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.postId).toBe(post.id); expect(job.result.postId).toBe(post.id);

View File

@ -36,13 +36,15 @@ describe('workflow > instructions > delay', () => {
const n1 = await workflow.createNode({ const n1 = await workflow.createNode({
type: 'delay', type: 'delay',
config: { config: {
duration: 1000, duration: 2000,
endStatus: JOB_STATUS.RESOLVED endStatus: JOB_STATUS.RESOLVED
} }
}); });
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [e1] = await workflow.getExecutions(); const [e1] = await workflow.getExecutions();
expect(e1.status).toEqual(EXECUTION_STATUS.STARTED); expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
const [j1] = await e1.getJobs(); const [j1] = await e1.getJobs();
@ -60,13 +62,15 @@ describe('workflow > instructions > delay', () => {
const n1 = await workflow.createNode({ const n1 = await workflow.createNode({
type: 'delay', type: 'delay',
config: { config: {
duration: 1000, duration: 2000,
endStatus: JOB_STATUS.REJECTED endStatus: JOB_STATUS.REJECTED
} }
}); });
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [e1] = await workflow.getExecutions(); const [e1] = await workflow.getExecutions();
expect(e1.status).toEqual(EXECUTION_STATUS.STARTED); expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
const [j1] = await e1.getJobs(); const [j1] = await e1.getJobs();
@ -84,7 +88,7 @@ describe('workflow > instructions > delay', () => {
const n1 = await workflow.createNode({ const n1 = await workflow.createNode({
type: 'delay', type: 'delay',
config: { config: {
duration: 1000, duration: 2000,
endStatus: JOB_STATUS.RESOLVED endStatus: JOB_STATUS.RESOLVED
} }
}); });
@ -104,6 +108,8 @@ describe('workflow > instructions > delay', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [e1] = await workflow.getExecutions(); const [e1] = await workflow.getExecutions();
expect(e1.status).toEqual(EXECUTION_STATUS.STARTED); expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
const [j1] = await e1.getJobs(); const [j1] = await e1.getJobs();
@ -133,6 +139,8 @@ describe('workflow > instructions > delay', () => {
it('restart app should trigger delayed job', async () => { it('restart app should trigger delayed job', async () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [e1] = await workflow.getExecutions(); const [e1] = await workflow.getExecutions();
expect(e1.status).toEqual(EXECUTION_STATUS.STARTED); expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
const [j1] = await e1.getJobs(); const [j1] = await e1.getJobs();
@ -153,6 +161,8 @@ describe('workflow > instructions > delay', () => {
it('restart app should trigger missed delayed job', async () => { it('restart app should trigger missed delayed job', async () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [e1] = await workflow.getExecutions(); const [e1] = await workflow.getExecutions();
expect(e1.status).toEqual(EXECUTION_STATUS.STARTED); expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
const [j1] = await e1.getJobs(); const [j1] = await e1.getJobs();

View File

@ -1,6 +1,6 @@
import { Application } from '@nocobase/server'; import { Application } from '@nocobase/server';
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { getApp } from '..'; import { getApp, sleep } from '..';
@ -47,6 +47,8 @@ describe('workflow > instructions > destroy', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(1); expect(job.result).toBe(1);

View File

@ -1,6 +1,6 @@
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { Application } from '@nocobase/server'; import { Application } from '@nocobase/server';
import { getApp } from '..'; import { getApp, sleep } from '..';
import { EXECUTION_STATUS } from '../../constants'; import { EXECUTION_STATUS } from '../../constants';
@ -51,6 +51,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -74,6 +76,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.REJECTED); expect(execution.status).toBe(EXECUTION_STATUS.REJECTED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -99,6 +103,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.REJECTED); expect(execution.status).toBe(EXECUTION_STATUS.REJECTED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -127,6 +133,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -153,6 +161,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -179,6 +189,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.REJECTED); expect(execution.status).toBe(EXECUTION_STATUS.REJECTED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -207,6 +219,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -233,6 +247,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.REJECTED); expect(execution.status).toBe(EXECUTION_STATUS.REJECTED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -263,6 +279,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -298,6 +316,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -325,6 +345,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -360,13 +382,17 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.STARTED); expect(execution.status).toBe(EXECUTION_STATUS.STARTED);
const [pending] = await execution.getJobs({ where: { nodeId: n2.id } }); const [pending] = await execution.getJobs({ where: { nodeId: n2.id } });
pending.set('result', 123); pending.set('result', 123);
const processor = plugin.createProcessor(execution); pending.execution = execution;
await processor.resume(pending); await plugin.resume(pending);
await sleep(500);
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@ -406,6 +432,8 @@ describe('workflow > instructions > parallel', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });

View File

@ -47,6 +47,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [pending] = await workflow.getExecutions(); const [pending] = await workflow.getExecutions();
expect(pending.status).toBe(EXECUTION_STATUS.STARTED); expect(pending.status).toBe(EXECUTION_STATUS.STARTED);
const [j1] = await pending.getJobs(); const [j1] = await pending.getJobs();
@ -130,6 +132,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [pending] = await workflow.getExecutions(); const [pending] = await workflow.getExecutions();
expect(pending.status).toBe(EXECUTION_STATUS.STARTED); expect(pending.status).toBe(EXECUTION_STATUS.STARTED);
const [j1] = await pending.getJobs(); const [j1] = await pending.getJobs();
@ -194,6 +198,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [pending] = await workflow.getExecutions(); const [pending] = await workflow.getExecutions();
expect(pending.status).toBe(EXECUTION_STATUS.STARTED); expect(pending.status).toBe(EXECUTION_STATUS.STARTED);
const [j1] = await pending.getJobs(); const [j1] = await pending.getJobs();
@ -234,6 +240,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const UserJobModel = db.getModel('users_jobs'); const UserJobModel = db.getModel('users_jobs');
const usersJobs = await UserJobModel.findAll(); const usersJobs = await UserJobModel.findAll();
expect(usersJobs.length).toBe(1); expect(usersJobs.length).toBe(1);
@ -271,6 +279,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const UserJobModel = db.getModel('users_jobs'); const UserJobModel = db.getModel('users_jobs');
const pendingJobs = await UserJobModel.findAll({ const pendingJobs = await UserJobModel.findAll({
order: [[ 'userId', 'ASC' ]] order: [[ 'userId', 'ASC' ]]
@ -327,6 +337,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const UserJobModel = db.getModel('users_jobs'); const UserJobModel = db.getModel('users_jobs');
const pendingJobs = await UserJobModel.findAll({ const pendingJobs = await UserJobModel.findAll({
order: [[ 'userId', 'ASC' ]] order: [[ 'userId', 'ASC' ]]
@ -374,6 +386,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const UserJobModel = db.getModel('users_jobs'); const UserJobModel = db.getModel('users_jobs');
const pendingJobs = await UserJobModel.findAll({ const pendingJobs = await UserJobModel.findAll({
order: [[ 'userId', 'ASC' ]] order: [[ 'userId', 'ASC' ]]
@ -431,6 +445,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const UserJobModel = db.getModel('users_jobs'); const UserJobModel = db.getModel('users_jobs');
const pendingJobs = await UserJobModel.findAll({ const pendingJobs = await UserJobModel.findAll({
order: [[ 'userId', 'ASC' ]] order: [[ 'userId', 'ASC' ]]
@ -473,6 +489,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const UserJobModel = db.getModel('users_jobs'); const UserJobModel = db.getModel('users_jobs');
const pendingJobs = await UserJobModel.findAll({ const pendingJobs = await UserJobModel.findAll({
order: [[ 'userId', 'ASC' ]] order: [[ 'userId', 'ASC' ]]
@ -523,6 +541,8 @@ describe.skip('workflow > instructions > prompt', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const UserJobModel = db.getModel('users_jobs'); const UserJobModel = db.getModel('users_jobs');
const pendingJobs = await UserJobModel.findAll({ const pendingJobs = await UserJobModel.findAll({
order: [[ 'userId', 'ASC' ]] order: [[ 'userId', 'ASC' ]]

View File

@ -1,6 +1,6 @@
import { Application } from '@nocobase/server'; import { Application } from '@nocobase/server';
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { getApp } from '..'; import { getApp, sleep } from '..';
@ -46,6 +46,8 @@ describe('workflow > instructions > query', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.title).toBe(post.title); expect(job.result.title).toBe(post.title);
@ -66,6 +68,8 @@ describe('workflow > instructions > query', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.title).toBe(post.title); expect(job.result.title).toBe(post.title);
@ -86,6 +90,8 @@ describe('workflow > instructions > query', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result).toBe(null); expect(job.result).toBe(null);
@ -106,6 +112,8 @@ describe('workflow > instructions > query', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.title).toBe(post.title); expect(job.result.title).toBe(post.title);
@ -131,6 +139,8 @@ describe('workflow > instructions > query', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const jobs = await execution.getJobs({ order: [['id', 'ASC']] }); const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
expect(jobs[1].result.title).toBe(post.title); expect(jobs[1].result.title).toBe(post.title);
@ -154,6 +164,8 @@ describe('workflow > instructions > query', () => {
values: { title: 't1', tags: [tag.id] } values: { title: 't1', tags: [tag.id] }
}); });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.id).toBe(tag.id); expect(job.result.id).toBe(tag.id);
@ -175,6 +187,8 @@ describe('workflow > instructions > query', () => {
values: { title: 't1', tags: [tag.id] } values: { title: 't1', tags: [tag.id] }
}); });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.posts.length).toBe(1); expect(job.result.posts.length).toBe(1);
@ -193,8 +207,13 @@ describe('workflow > instructions > query', () => {
}); });
const p1 = await PostRepo.create({ values: { title: 't1' } }); const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const p2 = await PostRepo.create({ values: { title: 't2' } }); const p2 = await PostRepo.create({ values: { title: 't2' } });
await sleep(500);
// get the 2nd execution // get the 2nd execution
const [execution] = await workflow.getExecutions({ order: [['id', 'DESC']] }); const [execution] = await workflow.getExecutions({ order: [['id', 'DESC']] });
expect(execution.context.data.title).toBe(p2.title); expect(execution.context.data.title).toBe(p2.title);
@ -215,6 +234,8 @@ describe('workflow > instructions > query', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.length).toBe(1); expect(job.result.length).toBe(1);
@ -237,6 +258,8 @@ describe('workflow > instructions > query', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.length).toBe(1); expect(job.result.length).toBe(1);
@ -259,6 +282,8 @@ describe('workflow > instructions > query', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.length).toBe(0); expect(job.result.length).toBe(0);

View File

@ -1,6 +1,6 @@
import { Application } from '@nocobase/server'; import { Application } from '@nocobase/server';
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { getApp } from '..'; import { getApp, sleep } from '..';
@ -51,6 +51,8 @@ describe('workflow > instructions > update', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
expect(post.published).toBe(false); expect(post.published).toBe(false);
await sleep(500);
const [execution] = await workflow.getExecutions(); const [execution] = await workflow.getExecutions();
const [job] = await execution.getJobs(); const [job] = await execution.getJobs();
expect(job.result.published).toBe(true); expect(job.result.published).toBe(true);
@ -93,6 +95,9 @@ describe('workflow > instructions > update', () => {
// NOTE: the result of post immediately created will not be changed by workflow // NOTE: the result of post immediately created will not be changed by workflow
const { id } = await PostRepo.create({ values: { title: 'test' } }); const { id } = await PostRepo.create({ values: { title: 'test' } });
await sleep(500);
// should get from db // should get from db
const post = await PostRepo.findById(id); const post = await PostRepo.findById(id);
expect(post.title).toBe('changed'); expect(post.title).toBe('changed');

View File

@ -41,6 +41,8 @@ describe('workflow > triggers > collection', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const executions = await workflow.getExecutions(); const executions = await workflow.getExecutions();
expect(executions.length).toBe(0); expect(executions.length).toBe(0);
}); });
@ -60,6 +62,8 @@ describe('workflow > triggers > collection', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await PostRepo.update({ filterByTk: post.id, values: { title: 't2' } }); await PostRepo.update({ filterByTk: post.id, values: { title: 't2' } });
await sleep(500);
const executions = await workflow.getExecutions(); const executions = await workflow.getExecutions();
expect(executions.length).toBe(1); expect(executions.length).toBe(1);
expect(executions[0].context.data.title).toBe('t2'); expect(executions[0].context.data.title).toBe('t2');
@ -79,6 +83,8 @@ describe('workflow > triggers > collection', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await PostRepo.update({ filterByTk: post.id, values: { title: 't2' } }); await PostRepo.update({ filterByTk: post.id, values: { title: 't2' } });
await sleep(500);
const executions = await workflow.getExecutions(); const executions = await workflow.getExecutions();
expect(executions.length).toBe(1); expect(executions.length).toBe(1);
expect(executions[0].status).toBe(EXECUTION_STATUS.RESOLVED); expect(executions[0].status).toBe(EXECUTION_STATUS.RESOLVED);
@ -99,6 +105,8 @@ describe('workflow > triggers > collection', () => {
const post = await PostRepo.create({ values: { title: 't1' } }); const post = await PostRepo.create({ values: { title: 't1' } });
await PostRepo.update({ filterByTk: post.id, values: { title: 't2' } }); await PostRepo.update({ filterByTk: post.id, values: { title: 't2' } });
await sleep(500);
const executions = await workflow.getExecutions(); const executions = await workflow.getExecutions();
expect(executions.length).toBe(0); expect(executions.length).toBe(0);
}); });

View File

@ -1,5 +1,5 @@
export const EXECUTION_STATUS = { export const EXECUTION_STATUS = {
CRETEAD: null, CREATED: null,
STARTED: 0, STARTED: 0,
RESOLVED: 1, RESOLVED: 1,
REJECTED: -1, REJECTED: -1,

View File

@ -47,7 +47,7 @@ export default class implements Instruction {
}) as JobModel[]; }) as JobModel[];
jobs.forEach(job => { jobs.forEach(job => {
this.schedule(job, job.node.config.duration); this.schedule(job, job.node!.config.duration);
}); });
} }
@ -70,8 +70,8 @@ export default class implements Instruction {
async trigger(job) { async trigger(job) {
const execution = await job.getExecution() as ExecutionModel; const execution = await job.getExecution() as ExecutionModel;
if (execution.status === EXECUTION_STATUS.STARTED) { if (execution.status === EXECUTION_STATUS.STARTED) {
const processor = this.plugin.createProcessor(execution); job.execution = execution;
await processor.resume(job); await this.plugin.resume(job);
} }
if (this.timers.get(job.id)) { if (this.timers.get(job.id)) {
this.timers.delete(job.id); this.timers.delete(job.id);

View File

@ -38,6 +38,7 @@ function getFieldRawName(collection: Collection, name: string) {
async function handler(this: CollectionTrigger, workflow: WorkflowModel, data: Model, options) { async function handler(this: CollectionTrigger, workflow: WorkflowModel, data: Model, options) {
const { collection: collectionName, condition, changed } = workflow.config; const { collection: collectionName, condition, changed } = workflow.config;
const collection = (<typeof Model>data.constructor).database.getCollection(collectionName); const collection = (<typeof Model>data.constructor).database.getCollection(collectionName);
const { transaction, context } = options;
// NOTE: if no configured fields changed, do not trigger // NOTE: if no configured fields changed, do not trigger
if (changed if (changed
@ -46,7 +47,6 @@ async function handler(this: CollectionTrigger, workflow: WorkflowModel, data: M
.filter(name => !['linkTo', 'hasOne', 'hasMany', 'belongsToMany'].includes(collection.getField(name).type)) .filter(name => !['linkTo', 'hasOne', 'hasMany', 'belongsToMany'].includes(collection.getField(name).type))
.every(name => !data.changedWithAssociations(getFieldRawName(collection, name))) .every(name => !data.changedWithAssociations(getFieldRawName(collection, name)))
) { ) {
// TODO: temp comment out
return; return;
} }
// NOTE: if no configured condition match, do not trigger // NOTE: if no configured condition match, do not trigger
@ -54,7 +54,6 @@ async function handler(this: CollectionTrigger, workflow: WorkflowModel, data: M
// TODO: change to map filter format to calculation format // TODO: change to map filter format to calculation format
// const calculation = toCalculation(condition); // const calculation = toCalculation(condition);
const { repository, model } = collection; const { repository, model } = collection;
const { transaction, context } = options;
const count = await repository.count({ const count = await repository.count({
filter: { filter: {
$and: [ $and: [
@ -71,9 +70,16 @@ async function handler(this: CollectionTrigger, workflow: WorkflowModel, data: M
} }
} }
return this.plugin.trigger(workflow, { data: data.get() }, { // TODO(bug): use setTimeout here test case will not exit (only work in SQLite)?
context: options.context, // setTimeout(() => {
transaction: options.transaction // this.plugin.trigger(workflow, { data: data.get() }, {
// context
// });
// }, 0);
await this.plugin.trigger(workflow, { data: data.get() }, {
context,
transaction
}); });
} }