2022-03-27 15:51:48 +08:00
|
|
|
import { Op } from 'sequelize';
|
2022-05-12 12:19:25 +08:00
|
|
|
import { Context, utils } from '@nocobase/actions';
|
|
|
|
import { MultipleRelationRepository } from '@nocobase/database';
|
|
|
|
import WorkflowModel from '../models/Workflow';
|
2022-03-27 15:51:48 +08:00
|
|
|
|
|
|
|
export async function create(context: Context, next) {
|
2022-05-12 12:19:25 +08:00
|
|
|
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
|
|
|
|
});
|
2022-03-27 15:51:48 +08:00
|
|
|
|
|
|
|
if (!instance.upstreamId) {
|
|
|
|
const previousHead = await repository.findOne({
|
|
|
|
filter: {
|
|
|
|
id: {
|
|
|
|
$ne: instance.id
|
|
|
|
},
|
|
|
|
upstreamId: null
|
2022-05-12 12:19:25 +08:00
|
|
|
},
|
|
|
|
transaction
|
2022-03-27 15:51:48 +08:00
|
|
|
});
|
|
|
|
if (previousHead) {
|
2022-05-12 12:19:25 +08:00
|
|
|
await previousHead.setUpstream(instance, { transaction });
|
|
|
|
await instance.setDownstream(previousHead, { transaction });
|
2022-03-27 15:51:48 +08:00
|
|
|
instance.set('downstream', previousHead);
|
|
|
|
}
|
2022-05-12 12:19:25 +08:00
|
|
|
return instance;
|
2022-03-27 15:51:48 +08:00
|
|
|
}
|
|
|
|
|
2022-05-12 12:19:25 +08:00
|
|
|
const upstream = await instance.getUpstream({ transaction });
|
2022-03-27 15:51:48 +08:00
|
|
|
|
|
|
|
if (instance.branchIndex == null) {
|
2022-05-12 12:19:25 +08:00
|
|
|
const downstream = await upstream.getDownstream({ transaction });
|
2022-03-27 15:51:48 +08:00
|
|
|
|
|
|
|
if (downstream) {
|
2022-05-12 12:19:25 +08:00
|
|
|
await downstream.setUpstream(instance, { transaction });
|
|
|
|
await instance.setDownstream(downstream, { transaction });
|
2022-03-27 15:51:48 +08:00
|
|
|
instance.set('downstream', downstream);
|
|
|
|
}
|
|
|
|
|
|
|
|
await upstream.update({
|
|
|
|
downstreamId: instance.id
|
2022-05-12 12:19:25 +08:00
|
|
|
}, { transaction });
|
2022-03-27 15:51:48 +08:00
|
|
|
|
|
|
|
upstream.set('downstream', instance);
|
|
|
|
} else {
|
|
|
|
const [downstream] = await upstream.getBranches({
|
|
|
|
where: {
|
|
|
|
id: {
|
|
|
|
[Op.ne]: instance.id
|
|
|
|
},
|
|
|
|
branchIndex: instance.branchIndex
|
2022-05-12 12:19:25 +08:00
|
|
|
},
|
|
|
|
transaction
|
2022-03-27 15:51:48 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
if (downstream) {
|
|
|
|
await downstream.update({
|
|
|
|
upstreamId: instance.id,
|
|
|
|
branchIndex: null
|
2022-05-12 12:19:25 +08:00
|
|
|
}, { transaction });
|
|
|
|
await instance.setDownstream(downstream, { transaction });
|
2022-03-27 15:51:48 +08:00
|
|
|
instance.set('downstream', downstream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
instance.set('upstream', upstream);
|
|
|
|
});
|
2022-05-12 12:19:25 +08:00
|
|
|
|
|
|
|
await next();
|
2022-03-27 15:51:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2022-05-12 12:19:25 +08:00
|
|
|
const repository = utils.getRepositoryFromParams(context) as MultipleRelationRepository;
|
2022-03-27 15:51:48 +08:00
|
|
|
const { filterByTk } = context.action.params;
|
|
|
|
|
|
|
|
context.body = await db.sequelize.transaction(async transaction => {
|
2022-05-12 12:19:25 +08:00
|
|
|
const workflow = await repository.getSourceModel(transaction) as WorkflowModel;
|
|
|
|
if (workflow.executed) {
|
|
|
|
context.throw(400, 'Nodes in executed workflow could not be deleted');
|
|
|
|
}
|
|
|
|
|
2022-03-27 15:51:48 +08:00
|
|
|
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();
|
|
|
|
}
|
2022-05-12 12:19:25 +08:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|