fix(plugin-workflow): break cycling trigger through transaction id (#341)
This commit is contained in:
parent
1fb2dd884c
commit
5652d11b82
@ -509,4 +509,28 @@ describe('execution', () => {
|
|||||||
expect(jobs.length).toEqual(5);
|
expect(jobs.length).toEqual(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('cycling trigger', () => {
|
||||||
|
it('trigger should not be triggered more than once in same execution', async () => {
|
||||||
|
const n1 = await workflow.createNode({
|
||||||
|
type: 'create',
|
||||||
|
config: {
|
||||||
|
collection: 'posts',
|
||||||
|
params: {
|
||||||
|
values: {
|
||||||
|
title: 't2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const post = await PostModel.create({ title: 't1' });
|
||||||
|
|
||||||
|
const posts = await PostModel.findAll();
|
||||||
|
expect(posts.length).toBe(2);
|
||||||
|
|
||||||
|
const [execution] = await workflow.getExecutions();
|
||||||
|
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -17,6 +17,11 @@ export default {
|
|||||||
title: '使用事务',
|
title: '使用事务',
|
||||||
defaultValue: false
|
defaultValue: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'uuid',
|
||||||
|
name: 'transaction',
|
||||||
|
defaultValue: null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
interface: 'linkTo',
|
interface: 'linkTo',
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
|
@ -73,9 +73,9 @@ export default class ExecutionModel extends Model {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTransaction() {
|
async getTransaction() {
|
||||||
const { sequelize } = (<typeof WorkflowModel>this.constructor).database;
|
const { sequelize } = (<typeof ExecutionModel>this.constructor).database;
|
||||||
// @ts-ignore
|
|
||||||
if (!this.useTransaction) {
|
if (!this.useTransaction) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -83,15 +83,21 @@ export default class ExecutionModel extends Model {
|
|||||||
const { options } = this;
|
const { options } = this;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return options.transaction && !options.transaction.finished
|
const transaction = options.transaction && !options.transaction.finished
|
||||||
? options.transaction
|
? options.transaction
|
||||||
: sequelize.transaction();
|
: sequelize.transaction();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
if (this.transaction !== transaction.id) {
|
||||||
|
// @ts-ignore
|
||||||
|
await this.update({ transaction: transaction.id }, { transaction });
|
||||||
|
}
|
||||||
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
async prepare(options, commit = false) {
|
async prepare(options, commit = false) {
|
||||||
this.options = options || {};
|
this.options = options || {};
|
||||||
// @ts-ignore
|
const transaction = await this.getTransaction();
|
||||||
const transaction = await this.getTransaction()
|
|
||||||
this.transaction = transaction;
|
this.transaction = transaction;
|
||||||
|
|
||||||
if (!this.workflow) {
|
if (!this.workflow) {
|
||||||
@ -230,7 +236,7 @@ export default class ExecutionModel extends Model {
|
|||||||
|
|
||||||
// TODO(optimize)
|
// TODO(optimize)
|
||||||
async saveJob(payload) {
|
async saveJob(payload) {
|
||||||
const { database } = <typeof WorkflowModel>this.constructor;
|
const { database } = <typeof ExecutionModel>this.constructor;
|
||||||
const { model } = database.getCollection('jobs');
|
const { model } = database.getCollection('jobs');
|
||||||
const [job] = (await model.upsert(
|
const [job] = (await model.upsert(
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Database, Model } from '@nocobase/database';
|
import { Database, Model } from '@nocobase/database';
|
||||||
import { HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';
|
import { HasManyCountAssociationsMixin, HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';
|
||||||
|
|
||||||
import triggers from '../triggers';
|
import triggers from '../triggers';
|
||||||
import { EXECUTION_STATUS } from '../constants';
|
import { EXECUTION_STATUS } from '../constants';
|
||||||
@ -25,6 +25,7 @@ export default class WorkflowModel extends Model {
|
|||||||
declare createNode: HasManyCreateAssociationMixin<FlowNodeModel>;
|
declare createNode: HasManyCreateAssociationMixin<FlowNodeModel>;
|
||||||
|
|
||||||
declare executions: ExecutionModel[];
|
declare executions: ExecutionModel[];
|
||||||
|
declare countExecutions: HasManyCountAssociationsMixin;
|
||||||
declare getExecutions: HasManyGetAssociationsMixin<ExecutionModel>;
|
declare getExecutions: HasManyGetAssociationsMixin<ExecutionModel>;
|
||||||
declare createExecution: HasManyCreateAssociationMixin<ExecutionModel>;
|
declare createExecution: HasManyCreateAssociationMixin<ExecutionModel>;
|
||||||
|
|
||||||
@ -75,10 +76,25 @@ export default class WorkflowModel extends Model {
|
|||||||
|
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
|
|
||||||
|
if (this.useTransaction) {
|
||||||
|
const existed = await this.countExecutions({
|
||||||
|
where: {
|
||||||
|
transaction: transaction.id
|
||||||
|
},
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existed) {
|
||||||
|
console.warn(`workflow ${this.id} has already been triggered in same execution (${transaction.id}), and newly triggering will be skipped.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const execution = await this.createExecution({
|
const execution = await this.createExecution({
|
||||||
context,
|
context,
|
||||||
status: EXECUTION_STATUS.STARTED,
|
status: EXECUTION_STATUS.STARTED,
|
||||||
useTransaction: this.useTransaction
|
useTransaction: this.useTransaction,
|
||||||
|
transaction: transaction.id
|
||||||
}, { transaction });
|
}, { transaction });
|
||||||
|
|
||||||
execution.workflow = this;
|
execution.workflow = this;
|
||||||
|
Loading…
Reference in New Issue
Block a user