tachybase_todo/packages/plugins/workflow/src/actions/nodes.ts

200 lines
5.7 KiB
TypeScript
Raw Normal View History

import { Op } from 'sequelize';
import { Context, utils } from '@nocobase/actions';
import { MultipleRelationRepository } from '@nocobase/database';
import WorkflowModel from '../models/Workflow';
export async function create(context: Context, next) {
const { db } = context;
const repository = utils.getRepositoryFromParams(context) as MultipleRelationRepository;
const { whitelist, blacklist, updateAssociationValues, values, associatedIndex: workflowId } = context.action.params;
context.body = await db.sequelize.transaction(async transaction => {
const workflow = await repository.getSourceModel(transaction) as WorkflowModel;
if (workflow.executed) {
context.throw(400, 'Node could not be created in executed workflow');
}
const instance = await repository.create({
values,
whitelist,
blacklist,
updateAssociationValues,
context,
transaction
});
if (!instance.upstreamId) {
const previousHead = await repository.findOne({
filter: {
id: {
$ne: instance.id
},
upstreamId: null
},
transaction
});
if (previousHead) {
await previousHead.setUpstream(instance, { transaction });
await instance.setDownstream(previousHead, { transaction });
instance.set('downstream', previousHead);
}
return instance;
}
const upstream = await instance.getUpstream({ transaction });
if (instance.branchIndex == null) {
const downstream = await upstream.getDownstream({ transaction });
if (downstream) {
await downstream.setUpstream(instance, { transaction });
await instance.setDownstream(downstream, { transaction });
instance.set('downstream', downstream);
}
await upstream.update({
downstreamId: instance.id
}, { transaction });
upstream.set('downstream', instance);
} else {
const [downstream] = await upstream.getBranches({
where: {
id: {
[Op.ne]: instance.id
},
branchIndex: instance.branchIndex
},
transaction
});
if (downstream) {
await downstream.update({
upstreamId: instance.id,
branchIndex: null
}, { transaction });
await instance.setDownstream(downstream, { transaction });
instance.set('downstream', downstream);
}
}
instance.set('upstream', upstream);
});
await next();
}
function searchBranchNodes(nodes, from): any[] {
const branchHeads = nodes
.filter((item: any) => item.upstreamId === from.id && item.branchIndex != null);
return branchHeads.reduce((flatten: any[], head) => flatten.concat(searchBranchDownstreams(nodes, head)), []) as any[];
}
function searchBranchDownstreams(nodes, from) {
let result = [];
for (let search = from; search; search = search.downstream) {
result = [...result, search, ...searchBranchNodes(nodes, search)];
}
return result;
}
export async function destroy(context: Context, next) {
const { db } = context;
const repository = utils.getRepositoryFromParams(context) as MultipleRelationRepository;
const { filterByTk } = context.action.params;
context.body = await db.sequelize.transaction(async transaction => {
const workflow = await repository.getSourceModel(transaction) as WorkflowModel;
if (workflow.executed) {
context.throw(400, 'Nodes in executed workflow could not be deleted');
}
const fields = ['id', 'upstreamId', 'downstreamId', 'branchIndex'];
const instance = await repository.findOne({
filterByTk,
fields: [...fields, 'workflowId'],
appends: ['upstream', 'downstream'],
transaction
});
const { upstream, downstream } = instance.get();
if (upstream && upstream.downstreamId === instance.id) {
await upstream.update({
downstreamId: instance.downstreamId
}, { transaction });
}
if (downstream) {
await downstream.update({
upstreamId: instance.upstreamId,
branchIndex: instance.branchIndex
}, { transaction });
}
const nodes = await repository.find({
filter: {
workflowId: instance.workflowId
},
fields,
transaction
});
const nodesMap = new Map();
// make map
nodes.forEach(item => {
nodesMap.set(item.id, item);
});
// overwrite
nodesMap.set(instance.id, instance);
// make linked list
nodes.forEach(item => {
if (item.upstreamId) {
item.upstream = nodesMap.get(item.upstreamId);
}
if (item.downstreamId) {
item.downstream = nodesMap.get(item.downstreamId);
}
});
const branchNodes = searchBranchNodes(nodes, instance);
await repository.destroy({
filterByTk: [instance.id, ...branchNodes.map(item => item.id)],
transaction
});
return instance;
});
await next();
}
export async function update(context: Context, next) {
const { db } = context;
const repository = utils.getRepositoryFromParams(context);
const { filterByTk, values, whitelist, blacklist, filter, updateAssociationValues } = context.action.params;
context.body = await db.sequelize.transaction(async transaction => {
// TODO(optimize): duplicated instance query
const { workflow } = await repository.findOne({
filterByTk,
appends: ['workflow.executed'],
transaction
});
if (workflow.executed) {
context.throw(400, 'Nodes in executed workflow could not be reconfigured');
}
return repository.update({
filterByTk,
values,
whitelist,
blacklist,
filter,
updateAssociationValues,
context,
transaction
});
});
await next();
}