fix(plugin-workflow): remove previous listeners when collection changed in config (#409)

This commit is contained in:
Junyi 2022-05-21 21:52:30 +08:00 committed by GitHub
parent b44753d528
commit dc05399beb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 15 deletions

View File

@ -0,0 +1,48 @@
import { Application } from '@nocobase/server';
import Database from '@nocobase/database';
import { getApp, sleep } from '..';
import { EXECUTION_STATUS } from '../../constants';
describe('workflow > triggers > collection', () => {
let app: Application;
let db: Database;
let PostRepo;
let WorkflowModel;
beforeEach(async () => {
app = await getApp();
db = app.db;
WorkflowModel = db.getCollection('workflows').model;
PostRepo = db.getCollection('posts').repository;
});
afterEach(() => db.close());
describe('toggle', () => {
it('when collection change', async () => {
const workflow = await WorkflowModel.create({
enabled: true,
type: 'collection',
config: {
mode: 1,
collection: 'posts'
}
});
await workflow.update({
config: {
...workflow.config,
collection: 'comments'
}
});
const post = await PostRepo.create({ values: { title: 't1' } });
const executions = await workflow.getExecutions();
expect(executions.length).toBe(0);
});
});
});

View File

@ -1,5 +1,5 @@
import { Database, Model } from '@nocobase/database';
import { HasManyCountAssociationsMixin, HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';
import { HasManyCountAssociationsMixin, HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, Transactionable } from 'sequelize';
import { EXECUTION_STATUS } from '../constants';
import ExecutionModel from './Execution';
@ -31,7 +31,7 @@ export default class WorkflowModel extends Model {
getTransaction(options) {
if (!this.useTransaction) {
return undefined;
return null;
}
return options.transaction && !options.transaction.finished
@ -39,7 +39,7 @@ export default class WorkflowModel extends Model {
: (<typeof WorkflowModel>this.constructor).database.sequelize.transaction();
}
trigger = async (context: Object, options) => {
trigger = async (context: Object, options = {}) => {
// `null` means not to trigger
if (context === null) {
return;
@ -73,9 +73,11 @@ export default class WorkflowModel extends Model {
await execution.start({ transaction });
if (!this.executed) {
await this.update({ executed: true }, { transaction });
// NOTE: not to trigger afterUpdate hook here
await this.update({ executed: true }, { transaction, hooks: false });
}
// @ts-ignore
if (transaction && (!options.transaction || options.transaction.finished)) {
await transaction.commit();
}

View File

@ -23,7 +23,7 @@ MODE_BITMAP_EVENTS.set(MODE_BITMAP.DESTROY, 'afterDestroy');
function getHookId(workflow, type) {
return `${type}#${workflow.get('id')}`;
return `${type}#${workflow.id}`;
}
// async function, should return promise
@ -43,12 +43,23 @@ function handler(this: WorkflowModel, data: Model, options) {
}
export default class CollectionTrigger implements Trigger {
db;
events = new Map();
constructor({ app }) {
this.db = app.db;
}
on(workflow: WorkflowModel) {
const { database } = <typeof WorkflowModel>workflow.constructor;
// NOTE: remove previous listener if config updated
const prev = workflow.previous();
if (prev.config) {
this.off({ ...workflow.get(), ...prev });
}
const { collection, mode } = workflow.config;
const Collection = database.getCollection(collection);
const Collection = this.db.getCollection(collection);
if (!Collection) {
return;
}
@ -60,12 +71,12 @@ export default class CollectionTrigger implements Trigger {
if (!this.events.has(name)) {
const listener = handler.bind(workflow);
this.events.set(name, listener);
database.on(event, listener);
this.db.on(event, listener);
}
} else {
const listener = this.events.get(name);
if (listener) {
database.off(event, listener);
this.db.off(event, listener);
this.events.delete(name);
}
}
@ -73,9 +84,8 @@ export default class CollectionTrigger implements Trigger {
}
off(workflow: WorkflowModel) {
const { database } = <typeof WorkflowModel>workflow.constructor;
const { collection, mode } = workflow.config;
const Collection = database.getCollection(collection);
const Collection = this.db.getCollection(collection);
if (!Collection) {
return;
}
@ -85,7 +95,7 @@ export default class CollectionTrigger implements Trigger {
if (mode & key) {
const listener = this.events.get(name);
if (listener) {
database.off(event, listener);
this.db.off(event, listener);
this.events.delete(name);
}
}

View File

@ -7,7 +7,8 @@ export interface Trigger {
off(workflow: WorkflowModel): void;
}
export default function({ triggers }) {
triggers.register('collection', new Collection());
// triggers.register('schedule', new Schedule());
export default function(plugin) {
const { triggers } = plugin;
triggers.register('collection', new Collection(plugin));
// triggers.register('schedule', new Schedule(plugin));
}