refactor(plugin-workflow): refactor apis (#3267)
* refactor(plugin-workflow): refactor apis * fix(plugin-workflow-parallel): fix import in test cases * fix(plugin-workflow): fix some module import source * fix(plugin-workflow): move manual table acl to manual plugin * fix(plugin-workflow-manual): fix folder typo
This commit is contained in:
parent
57c7dd3e95
commit
8ee8ab7d6d
@ -13,7 +13,6 @@ export default class extends Plugin {
|
|||||||
// You can get and modify the app instance here
|
// You can get and modify the app instance here
|
||||||
async load() {
|
async load() {
|
||||||
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
||||||
const aggregateInstruction = new AggregateInstruction();
|
workflow.registerInstruction('aggregate', AggregateInstruction);
|
||||||
workflow.instructions.register(aggregateInstruction.type, aggregateInstruction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ export default class extends Instruction {
|
|||||||
const result = await repo.aggregate({
|
const result = await repo.aggregate({
|
||||||
...options,
|
...options,
|
||||||
method: aggregators[aggregator],
|
method: aggregators[aggregator],
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -4,11 +4,8 @@ import WorkflowPlugin from '@nocobase/plugin-workflow';
|
|||||||
import AggregateInstruction from './AggregateInstruction';
|
import AggregateInstruction from './AggregateInstruction';
|
||||||
|
|
||||||
export default class extends Plugin {
|
export default class extends Plugin {
|
||||||
workflow: WorkflowPlugin;
|
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const workflowPlugin = this.app.getPlugin('workflow') as WorkflowPlugin;
|
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
||||||
this.workflow = workflowPlugin;
|
workflowPlugin.registerInstruction('aggregate', AggregateInstruction);
|
||||||
workflowPlugin.instructions.register('aggregate', new AggregateInstruction(workflowPlugin));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ export default class extends Plugin {
|
|||||||
// You can get and modify the app instance here
|
// You can get and modify the app instance here
|
||||||
async load() {
|
async load() {
|
||||||
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
||||||
const delayInstruction = new DelayInstruction();
|
workflow.registerInstruction('delay', DelayInstruction);
|
||||||
workflow.instructions.register(delayInstruction.type, delayInstruction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,15 @@ interface DelayConfig {
|
|||||||
export default class extends Instruction {
|
export default class extends Instruction {
|
||||||
timers: Map<number, NodeJS.Timeout> = new Map();
|
timers: Map<number, NodeJS.Timeout> = new Map();
|
||||||
|
|
||||||
constructor(public plugin: WorkflowPlugin) {
|
constructor(public workflow: WorkflowPlugin) {
|
||||||
super(plugin);
|
super(workflow);
|
||||||
|
|
||||||
plugin.app.on('afterStart', this.load);
|
workflow.app.on('afterStart', this.load);
|
||||||
plugin.app.on('beforeStop', this.unload);
|
workflow.app.on('beforeStop', this.unload);
|
||||||
}
|
}
|
||||||
|
|
||||||
load = async () => {
|
load = async () => {
|
||||||
const { model } = this.plugin.db.getCollection('jobs');
|
const { model } = this.workflow.app.db.getCollection('jobs');
|
||||||
const jobs = (await model.findAll({
|
const jobs = (await model.findAll({
|
||||||
where: {
|
where: {
|
||||||
status: JOB_STATUS.PENDING,
|
status: JOB_STATUS.PENDING,
|
||||||
@ -79,7 +79,7 @@ export default class extends Instruction {
|
|||||||
job.execution = await job.getExecution();
|
job.execution = await job.getExecution();
|
||||||
}
|
}
|
||||||
if (job.execution.status === EXECUTION_STATUS.STARTED) {
|
if (job.execution.status === EXECUTION_STATUS.STARTED) {
|
||||||
this.plugin.resume(job);
|
this.workflow.resume(job);
|
||||||
}
|
}
|
||||||
if (this.timers.get(job.id)) {
|
if (this.timers.get(job.id)) {
|
||||||
this.timers.delete(job.id);
|
this.timers.delete(job.id);
|
||||||
|
@ -4,11 +4,8 @@ import WorkflowPlugin from '@nocobase/plugin-workflow';
|
|||||||
import DelayInstruction from './DelayInstruction';
|
import DelayInstruction from './DelayInstruction';
|
||||||
|
|
||||||
export default class extends Plugin {
|
export default class extends Plugin {
|
||||||
workflow: WorkflowPlugin;
|
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const workflowPlugin = this.app.getPlugin('workflow') as WorkflowPlugin;
|
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
||||||
this.workflow = workflowPlugin;
|
workflowPlugin.registerInstruction('delay', DelayInstruction);
|
||||||
workflowPlugin.instructions.register('delay', new DelayInstruction(workflowPlugin));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,12 @@ import { ExpressionField } from './expression-field';
|
|||||||
import { DynamicCalculation } from './DynamicCalculation';
|
import { DynamicCalculation } from './DynamicCalculation';
|
||||||
|
|
||||||
export default class extends Plugin {
|
export default class extends Plugin {
|
||||||
workflow: WorkflowPlugin;
|
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
this.db.registerFieldTypes({
|
this.db.registerFieldTypes({
|
||||||
expression: ExpressionField,
|
expression: ExpressionField,
|
||||||
});
|
});
|
||||||
|
|
||||||
const workflowPlugin = this.app.getPlugin('workflow') as WorkflowPlugin;
|
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
||||||
this.workflow = workflowPlugin;
|
workflowPlugin.registerInstruction('dynamic-calculation', DynamicCalculation);
|
||||||
workflowPlugin.instructions.register('dynamic-calculation', new DynamicCalculation(workflowPlugin));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { NAMESPACE, useLang } from '../locale';
|
|||||||
|
|
||||||
export default class extends Trigger {
|
export default class extends Trigger {
|
||||||
title = `{{t("Form event", { ns: "${NAMESPACE}" })}}`;
|
title = `{{t("Form event", { ns: "${NAMESPACE}" })}}`;
|
||||||
type = 'form';
|
|
||||||
description = `{{t("Event triggers when submitted a workflow bound form action.", { ns: "${NAMESPACE}" })}}`;
|
description = `{{t("Event triggers when submitted a workflow bound form action.", { ns: "${NAMESPACE}" })}}`;
|
||||||
fieldset = {
|
fieldset = {
|
||||||
collection: {
|
collection: {
|
||||||
|
@ -13,7 +13,6 @@ export default class extends Plugin {
|
|||||||
// You can get and modify the app instance here
|
// You can get and modify the app instance here
|
||||||
async load() {
|
async load() {
|
||||||
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
||||||
const formTrigger = new FormTrigger();
|
workflow.registerTrigger('form', FormTrigger);
|
||||||
workflow.triggers.register(formTrigger.type, formTrigger);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ import { Model, modelAssociationByKey } from '@nocobase/database';
|
|||||||
import WorkflowPlugin, { Trigger, WorkflowModel, toJSON } from '@nocobase/plugin-workflow';
|
import WorkflowPlugin, { Trigger, WorkflowModel, toJSON } from '@nocobase/plugin-workflow';
|
||||||
|
|
||||||
export default class extends Trigger {
|
export default class extends Trigger {
|
||||||
constructor(plugin: WorkflowPlugin) {
|
constructor(workflow: WorkflowPlugin) {
|
||||||
super(plugin);
|
super(workflow);
|
||||||
|
|
||||||
plugin.app.resourcer.use(this.middleware);
|
workflow.app.resourcer.use(this.middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
async triggerAction(context, next) {
|
async triggerAction(context, next) {
|
||||||
@ -58,7 +58,7 @@ export default class extends Trigger {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const triggers = triggerWorkflows.split(',').map((trigger) => trigger.split('!'));
|
const triggers = triggerWorkflows.split(',').map((trigger) => trigger.split('!'));
|
||||||
const workflowRepo = this.plugin.db.getRepository('workflows');
|
const workflowRepo = this.workflow.db.getRepository('workflows');
|
||||||
const workflows = await workflowRepo.find({
|
const workflows = await workflowRepo.find({
|
||||||
filter: {
|
filter: {
|
||||||
key: triggers.map((trigger) => trigger[0]),
|
key: triggers.map((trigger) => trigger[0]),
|
||||||
@ -95,11 +95,11 @@ export default class extends Trigger {
|
|||||||
appends,
|
appends,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.plugin.trigger(workflow, { data: toJSON(payload), ...userInfo });
|
this.workflow.trigger(workflow, { data: toJSON(payload), ...userInfo });
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const data = trigger[1] ? get(values, trigger[1]) : values;
|
const data = trigger[1] ? get(values, trigger[1]) : values;
|
||||||
this.plugin.trigger(workflow, {
|
this.workflow.trigger(workflow, {
|
||||||
data,
|
data,
|
||||||
...userInfo,
|
...userInfo,
|
||||||
});
|
});
|
||||||
|
@ -4,11 +4,8 @@ import WorkflowPlugin from '@nocobase/plugin-workflow';
|
|||||||
import FormTrigger from './FormTrigger';
|
import FormTrigger from './FormTrigger';
|
||||||
|
|
||||||
export default class extends Plugin {
|
export default class extends Plugin {
|
||||||
workflow: WorkflowPlugin;
|
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const workflowPlugin = this.app.getPlugin('workflow') as WorkflowPlugin;
|
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
||||||
this.workflow = workflowPlugin;
|
|
||||||
workflowPlugin.triggers.register('form', new FormTrigger(workflowPlugin));
|
workflowPlugin.triggers.register('form', new FormTrigger(workflowPlugin));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ export default class extends Plugin {
|
|||||||
// You can get and modify the app instance here
|
// You can get and modify the app instance here
|
||||||
async load() {
|
async load() {
|
||||||
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
||||||
const loopInstruction = new LoopInstruction();
|
workflow.registerInstruction('loop', LoopInstruction);
|
||||||
workflow.instructions.register(loopInstruction.type, loopInstruction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,8 @@ import { default as WorkflowPlugin } from '@nocobase/plugin-workflow';
|
|||||||
import LoopInstruction from './LoopInstruction';
|
import LoopInstruction from './LoopInstruction';
|
||||||
|
|
||||||
export default class extends Plugin {
|
export default class extends Plugin {
|
||||||
workflow: WorkflowPlugin;
|
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const workflowPlugin = this.app.getPlugin('workflow') as WorkflowPlugin;
|
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
||||||
this.workflow = workflowPlugin;
|
workflowPlugin.registerInstruction('loop', LoopInstruction);
|
||||||
workflowPlugin.instructions.register('loop', new LoopInstruction(workflowPlugin));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 { BRANCH_INDEX, EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
|
import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
|
||||||
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
||||||
|
|
||||||
import Plugin from '..';
|
import Plugin from '..';
|
||||||
@ -404,7 +404,7 @@ describe('workflow > instructions > loop', () => {
|
|||||||
|
|
||||||
const n2 = await workflow.createNode({
|
const n2 = await workflow.createNode({
|
||||||
type: 'loop',
|
type: 'loop',
|
||||||
branchIndex: BRANCH_INDEX.ON_TRUE,
|
branchIndex: 1,
|
||||||
upstreamId: n1.id,
|
upstreamId: n1.id,
|
||||||
config: {
|
config: {
|
||||||
target: 0,
|
target: 0,
|
||||||
@ -442,7 +442,7 @@ describe('workflow > instructions > loop', () => {
|
|||||||
|
|
||||||
const n2 = await workflow.createNode({
|
const n2 = await workflow.createNode({
|
||||||
type: 'loop',
|
type: 'loop',
|
||||||
branchIndex: BRANCH_INDEX.ON_TRUE,
|
branchIndex: 1,
|
||||||
upstreamId: n1.id,
|
upstreamId: n1.id,
|
||||||
config: {
|
config: {
|
||||||
target: 2,
|
target: 2,
|
||||||
|
@ -85,8 +85,8 @@ function getMode(mode) {
|
|||||||
export default class extends Instruction {
|
export default class extends Instruction {
|
||||||
formTypes = new Registry<FormHandler>();
|
formTypes = new Registry<FormHandler>();
|
||||||
|
|
||||||
constructor(public plugin: WorkflowPlugin) {
|
constructor(public workflow: WorkflowPlugin) {
|
||||||
super(plugin);
|
super(workflow);
|
||||||
|
|
||||||
initFormTypes(this);
|
initFormTypes(this);
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ export default class extends Instruction {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// NOTE: batch create users jobs
|
// NOTE: batch create users jobs
|
||||||
const UserJobModel = processor.options.plugin.db.getModel('users_jobs');
|
const UserJobModel = this.workflow.app.db.getModel('users_jobs');
|
||||||
await UserJobModel.bulkCreate(
|
await UserJobModel.bulkCreate(
|
||||||
assignees.map((userId) => ({
|
assignees.map((userId) => ({
|
||||||
userId,
|
userId,
|
||||||
@ -114,7 +114,7 @@ export default class extends Instruction {
|
|||||||
status: JOB_STATUS.PENDING,
|
status: JOB_STATUS.PENDING,
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -125,13 +125,13 @@ export default class extends Instruction {
|
|||||||
// NOTE: check all users jobs related if all done then continue as parallel
|
// NOTE: check all users jobs related if all done then continue as parallel
|
||||||
const { assignees = [], mode } = node.config as ManualConfig;
|
const { assignees = [], mode } = node.config as ManualConfig;
|
||||||
|
|
||||||
const UserJobModel = processor.options.plugin.db.getModel('users_jobs');
|
const UserJobModel = this.workflow.app.db.getModel('users_jobs');
|
||||||
const distribution = await UserJobModel.count({
|
const distribution = await UserJobModel.count({
|
||||||
where: {
|
where: {
|
||||||
jobId: job.id,
|
jobId: job.id,
|
||||||
},
|
},
|
||||||
group: ['status'],
|
group: ['status'],
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
const submitted = distribution.reduce(
|
const submitted = distribution.reduce(
|
||||||
|
@ -3,16 +3,14 @@ import actions from '@nocobase/actions';
|
|||||||
import { HandlerType } from '@nocobase/resourcer';
|
import { HandlerType } from '@nocobase/resourcer';
|
||||||
import WorkflowPlugin, { JOB_STATUS } from '@nocobase/plugin-workflow';
|
import WorkflowPlugin, { JOB_STATUS } from '@nocobase/plugin-workflow';
|
||||||
|
|
||||||
import jobsCollection from './collecions/jobs';
|
import jobsCollection from './collections/jobs';
|
||||||
import usersCollection from './collecions/users';
|
import usersCollection from './collections/users';
|
||||||
import usersJobsCollection from './collecions/users_jobs';
|
import usersJobsCollection from './collections/users_jobs';
|
||||||
import { submit } from './actions';
|
import { submit } from './actions';
|
||||||
|
|
||||||
import ManualInstruction from './ManualInstruction';
|
import ManualInstruction from './ManualInstruction';
|
||||||
|
|
||||||
export default class extends Plugin {
|
export default class extends Plugin {
|
||||||
workflow: WorkflowPlugin;
|
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
this.app.db.collection(usersJobsCollection);
|
this.app.db.collection(usersJobsCollection);
|
||||||
this.app.db.extendCollection(usersCollection);
|
this.app.db.extendCollection(usersCollection);
|
||||||
@ -41,8 +39,9 @@ export default class extends Plugin {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const workflowPlugin = this.app.getPlugin('workflow') as WorkflowPlugin;
|
this.app.acl.allow('users_jobs', ['list', 'get', 'submit'], 'loggedIn');
|
||||||
this.workflow = workflowPlugin;
|
|
||||||
workflowPlugin.instructions.register('manual', new ManualInstruction(workflowPlugin));
|
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
||||||
|
workflowPlugin.registerInstruction('manual', ManualInstruction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ export async function submit(context: Context, next) {
|
|||||||
await handler.call(instruction, userJob, forms[formKey], processor);
|
await handler.call(instruction, userJob, forms[formKey], processor);
|
||||||
}
|
}
|
||||||
|
|
||||||
await userJob.save({ transaction: processor.transaction });
|
await userJob.save();
|
||||||
|
|
||||||
await processor.exit();
|
await processor.exit();
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { Processor } from '@nocobase/plugin-workflow';
|
|||||||
import ManualInstruction from '../ManualInstruction';
|
import ManualInstruction from '../ManualInstruction';
|
||||||
|
|
||||||
export default async function (this: ManualInstruction, instance, { collection }, processor: Processor) {
|
export default async function (this: ManualInstruction, instance, { collection }, processor: Processor) {
|
||||||
const repo = this.plugin.db.getRepository(collection);
|
const repo = this.workflow.db.getRepository(collection);
|
||||||
if (!repo) {
|
if (!repo) {
|
||||||
throw new Error(`collection ${collection} for create data on manual node not found`);
|
throw new Error(`collection ${collection} for create data on manual node not found`);
|
||||||
}
|
}
|
||||||
@ -18,6 +18,6 @@ export default async function (this: ManualInstruction, instance, { collection }
|
|||||||
context: {
|
context: {
|
||||||
executionId: processor.execution.id,
|
executionId: processor.execution.id,
|
||||||
},
|
},
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { Processor } from '@nocobase/plugin-workflow';
|
|||||||
import ManualInstruction from '../ManualInstruction';
|
import ManualInstruction from '../ManualInstruction';
|
||||||
|
|
||||||
export default async function (this: ManualInstruction, instance, { collection, filter = {} }, processor: Processor) {
|
export default async function (this: ManualInstruction, instance, { collection, filter = {} }, processor: Processor) {
|
||||||
const repo = this.plugin.db.getRepository(collection);
|
const repo = this.workflow.db.getRepository(collection);
|
||||||
if (!repo) {
|
if (!repo) {
|
||||||
throw new Error(`collection ${collection} for update data on manual node not found`);
|
throw new Error(`collection ${collection} for update data on manual node not found`);
|
||||||
}
|
}
|
||||||
@ -18,6 +18,6 @@ export default async function (this: ManualInstruction, instance, { collection,
|
|||||||
context: {
|
context: {
|
||||||
executionId: processor.execution.id,
|
executionId: processor.execution.id,
|
||||||
},
|
},
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ export default class extends Plugin {
|
|||||||
// You can get and modify the app instance here
|
// You can get and modify the app instance here
|
||||||
async load() {
|
async load() {
|
||||||
const workflow = this.app.pm.get(WorkflowPlugin);
|
const workflow = this.app.pm.get(WorkflowPlugin);
|
||||||
const parallelInstruction = new ParallelInstruction();
|
workflow.registerInstruction('parallel', ParallelInstruction);
|
||||||
workflow.instructions.register(parallelInstruction.type, parallelInstruction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ export default class extends Instruction {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (job.status === JOB_STATUS.PENDING) {
|
if (job.status === JOB_STATUS.PENDING) {
|
||||||
await job.save({ transaction: processor.transaction });
|
await job.save();
|
||||||
return processor.exit();
|
return processor.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,8 @@ import WorkflowPlugin from '@nocobase/plugin-workflow';
|
|||||||
import ParallelInstruction from './ParallelInstruction';
|
import ParallelInstruction from './ParallelInstruction';
|
||||||
|
|
||||||
export default class extends Plugin {
|
export default class extends Plugin {
|
||||||
workflow: WorkflowPlugin;
|
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const workflowPlugin = this.app.getPlugin('workflow') as WorkflowPlugin;
|
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
||||||
this.workflow = workflowPlugin;
|
workflowPlugin.registerInstruction('parallel', ParallelInstruction);
|
||||||
workflowPlugin.instructions.register('parallel', new ParallelInstruction(workflowPlugin));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 { BRANCH_INDEX, EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
|
import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
|
||||||
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
||||||
|
|
||||||
import Plugin from '..';
|
import Plugin from '..';
|
||||||
@ -453,7 +453,7 @@ describe('workflow > instructions > parallel', () => {
|
|||||||
|
|
||||||
const n2 = await workflow.createNode({
|
const n2 = await workflow.createNode({
|
||||||
type: 'parallel',
|
type: 'parallel',
|
||||||
branchIndex: BRANCH_INDEX.ON_TRUE,
|
branchIndex: 1,
|
||||||
upstreamId: n1.id,
|
upstreamId: n1.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -521,7 +521,7 @@ describe('workflow > instructions > parallel', () => {
|
|||||||
const n4 = await workflow.createNode({
|
const n4 = await workflow.createNode({
|
||||||
type: 'echo',
|
type: 'echo',
|
||||||
upstreamId: n3.id,
|
upstreamId: n3.id,
|
||||||
branchIndex: BRANCH_INDEX.ON_TRUE,
|
branchIndex: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const n5 = await workflow.createNode({
|
const n5 = await workflow.createNode({
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { ArrayItems } from '@formily/antd-v5';
|
import { ArrayItems } from '@formily/antd-v5';
|
||||||
|
|
||||||
import { defaultFieldNames } from '@nocobase/client';
|
import {
|
||||||
|
Instruction,
|
||||||
import { Instruction, WorkflowVariableInput, WorkflowVariableJSON } from '@nocobase/plugin-workflow/client';
|
WorkflowVariableInput,
|
||||||
|
WorkflowVariableJSON,
|
||||||
|
defaultFieldNames,
|
||||||
|
} from '@nocobase/plugin-workflow/client';
|
||||||
|
|
||||||
import { NAMESPACE } from '../locale';
|
import { NAMESPACE } from '../locale';
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ export default class extends Plugin {
|
|||||||
// You can get and modify the app instance here
|
// You can get and modify the app instance here
|
||||||
async load() {
|
async load() {
|
||||||
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
||||||
const requestInstruction = new RequestInstruction();
|
workflow.registerInstruction('request', RequestInstruction);
|
||||||
workflow.instructions.register(requestInstruction.type, requestInstruction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,8 @@ import WorkflowPlugin from '@nocobase/plugin-workflow';
|
|||||||
import RequestInstruction from './RequestInstruction';
|
import RequestInstruction from './RequestInstruction';
|
||||||
|
|
||||||
export default class extends Plugin {
|
export default class extends Plugin {
|
||||||
workflow: WorkflowPlugin;
|
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const workflowPlugin = this.app.getPlugin('workflow') as WorkflowPlugin;
|
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
||||||
this.workflow = workflowPlugin;
|
workflowPlugin.registerInstruction('request', RequestInstruction);
|
||||||
workflowPlugin.instructions.register('request', new RequestInstruction(workflowPlugin));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ export default class extends Instruction {
|
|||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
processor.logger.info(`request (#${node.id}) response received, status: ${job.get('status')}`);
|
processor.logger.info(`request (#${node.id}) response received, status: ${job.get('status')}`);
|
||||||
this.plugin.resume(job);
|
this.workflow.resume(job);
|
||||||
});
|
});
|
||||||
|
|
||||||
processor.logger.info(`request (#${node.id}) sent to "${config.url}", waiting for response...`);
|
processor.logger.info(`request (#${node.id}) sent to "${config.url}", waiting for response...`);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { css, defaultFieldNames } from '@nocobase/client';
|
import { css } from '@nocobase/client';
|
||||||
|
|
||||||
import { Instruction, WorkflowVariableRawTextArea } from '@nocobase/plugin-workflow/client';
|
import { Instruction, WorkflowVariableRawTextArea, defaultFieldNames } from '@nocobase/plugin-workflow/client';
|
||||||
|
|
||||||
import { NAMESPACE } from '../locale';
|
import { NAMESPACE } from '../locale';
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ export default class extends Plugin {
|
|||||||
// You can get and modify the app instance here
|
// You can get and modify the app instance here
|
||||||
async load() {
|
async load() {
|
||||||
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
||||||
const sqlInstruction = new SQLInstruction();
|
workflow.registerInstruction('sql', SQLInstruction);
|
||||||
workflow.instructions.register(sqlInstruction.type, sqlInstruction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,8 @@ import WorkflowPlugin from '@nocobase/plugin-workflow';
|
|||||||
import SQLInstruction from './SQLInstruction';
|
import SQLInstruction from './SQLInstruction';
|
||||||
|
|
||||||
export default class extends Plugin {
|
export default class extends Plugin {
|
||||||
workflow: WorkflowPlugin;
|
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const workflowPlugin = this.app.getPlugin('workflow') as WorkflowPlugin;
|
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
||||||
this.workflow = workflowPlugin;
|
workflowPlugin.registerInstruction('sql', SQLInstruction);
|
||||||
workflowPlugin.instructions.register('sql', new SQLInstruction(workflowPlugin));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ export default class extends Instruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await sequelize.query(sql, {
|
const result = await sequelize.query(sql, {
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
// plain: true,
|
// plain: true,
|
||||||
// model: db.getCollection(node.config.collection).model
|
// model: db.getCollection(node.config.collection).model
|
||||||
});
|
});
|
||||||
|
@ -45,6 +45,26 @@ export default class extends Plugin {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
registerTrigger(type: string, trigger: Trigger | { new (): Trigger }) {
|
||||||
|
if (typeof trigger === 'function') {
|
||||||
|
this.triggers.register(type, new trigger());
|
||||||
|
} else if (trigger) {
|
||||||
|
this.triggers.register(type, trigger);
|
||||||
|
} else {
|
||||||
|
throw new TypeError('invalid trigger type to register');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerInstruction(type: string, instruction: Instruction | { new (): Instruction }) {
|
||||||
|
if (typeof instruction === 'function') {
|
||||||
|
this.instructions.register(type, new instruction());
|
||||||
|
} else if (instruction instanceof Instruction) {
|
||||||
|
this.instructions.register(type, instruction);
|
||||||
|
} else {
|
||||||
|
throw new TypeError('invalid instruction type to register');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
this.addRoutes();
|
this.addRoutes();
|
||||||
this.addScopes();
|
this.addScopes();
|
||||||
@ -57,15 +77,15 @@ export default class extends Plugin {
|
|||||||
aclSnippet: 'pm.workflow.workflows',
|
aclSnippet: 'pm.workflow.workflows',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.triggers.register('collection', new CollectionTrigger());
|
this.registerTrigger('collection', CollectionTrigger);
|
||||||
this.triggers.register('schedule', new ScheduleTrigger());
|
this.registerTrigger('schedule', ScheduleTrigger);
|
||||||
|
|
||||||
this.instructions.register('calculation', new CalculationInstruction());
|
this.registerInstruction('calculation', CalculationInstruction);
|
||||||
this.instructions.register('condition', new ConditionInstruction());
|
this.registerInstruction('condition', ConditionInstruction);
|
||||||
this.instructions.register('query', new QueryInstruction());
|
this.registerInstruction('query', QueryInstruction);
|
||||||
this.instructions.register('create', new CreateInstruction());
|
this.registerInstruction('create', CreateInstruction);
|
||||||
this.instructions.register('update', new UpdateInstruction());
|
this.registerInstruction('update', UpdateInstruction);
|
||||||
this.instructions.register('destroy', new DestroyInstruction());
|
this.registerInstruction('destroy', DestroyInstruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
addScopes() {
|
addScopes() {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { SchemaInitializerItemType, defaultFieldNames } from '@nocobase/client';
|
import { SchemaInitializerItemType } from '@nocobase/client';
|
||||||
import { Evaluator, evaluators, getOptions } from '@nocobase/evaluators/client';
|
import { Evaluator, evaluators, getOptions } from '@nocobase/evaluators/client';
|
||||||
|
|
||||||
import { RadioWithTooltip } from '../components/RadioWithTooltip';
|
import { RadioWithTooltip } from '../components/RadioWithTooltip';
|
||||||
import { ValueBlock } from '../components/ValueBlock';
|
import { ValueBlock } from '../components/ValueBlock';
|
||||||
import { renderEngineReference } from '../components/renderEngineReference';
|
import { renderEngineReference } from '../components/renderEngineReference';
|
||||||
import { NAMESPACE, lang } from '../locale';
|
import { NAMESPACE, lang } from '../locale';
|
||||||
import { BaseTypeSets, WorkflowVariableTextArea } from '../variable';
|
import { BaseTypeSets, WorkflowVariableTextArea, defaultFieldNames } from '../variable';
|
||||||
import { Instruction } from '.';
|
import { Instruction } from '.';
|
||||||
|
|
||||||
export default class extends Instruction {
|
export default class extends Instruction {
|
||||||
|
@ -30,7 +30,13 @@ import { JobStatusOptionsMap } from '../constants';
|
|||||||
import { useGetAriaLabelOfAddButton } from '../hooks/useGetAriaLabelOfAddButton';
|
import { useGetAriaLabelOfAddButton } from '../hooks/useGetAriaLabelOfAddButton';
|
||||||
import { lang } from '../locale';
|
import { lang } from '../locale';
|
||||||
import useStyles from '../style';
|
import useStyles from '../style';
|
||||||
import { VariableOption, VariableOptions } from '../variable';
|
import { UseVariableOptions, VariableOption } from '../variable';
|
||||||
|
|
||||||
|
export type NodeAvailableContext = {
|
||||||
|
workflow: object;
|
||||||
|
upstream: object;
|
||||||
|
branchIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
export abstract class Instruction {
|
export abstract class Instruction {
|
||||||
title: string;
|
title: string;
|
||||||
@ -43,10 +49,10 @@ export abstract class Instruction {
|
|||||||
scope?: { [key: string]: any };
|
scope?: { [key: string]: any };
|
||||||
components?: { [key: string]: any };
|
components?: { [key: string]: any };
|
||||||
Component?(props): JSX.Element;
|
Component?(props): JSX.Element;
|
||||||
useVariables?(node, options?): VariableOption;
|
useVariables?(node, options?: UseVariableOptions): VariableOption;
|
||||||
useScopeVariables?(node, options?): VariableOptions;
|
useScopeVariables?(node, options?): VariableOption[];
|
||||||
useInitializers?(node): SchemaInitializerItemType | null;
|
useInitializers?(node): SchemaInitializerItemType | null;
|
||||||
isAvailable?(ctx: object): boolean;
|
isAvailable?(ctx: NodeAvailableContext): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function useUpdateAction() {
|
function useUpdateAction() {
|
||||||
@ -362,7 +368,7 @@ export function NodeDefaultView(props) {
|
|||||||
className: 'workflow-node-config-button',
|
className: 'workflow-node-config-button',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[`${instruction.type}_${data.id}`]: {
|
[data.id]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: (
|
title: (
|
||||||
<div
|
<div
|
||||||
|
@ -101,14 +101,6 @@ const workflowFieldset = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
'x-component': 'fieldset',
|
'x-component': 'fieldset',
|
||||||
properties: {
|
properties: {
|
||||||
// NOTE: not to expose this option for now, because hard to track errors
|
|
||||||
// useTransaction: {
|
|
||||||
// type: 'boolean',
|
|
||||||
// title: `{{ t("Use transaction", { ns: "${NAMESPACE}" }) }}`,
|
|
||||||
// description: `{{ t("Data operation nodes in workflow will run in a same transaction until any interruption. Any failure will cause data rollback, and will also rollback the history of the execution.", { ns: "${NAMESPACE}" }) }}`,
|
|
||||||
// 'x-decorator': 'FormItem',
|
|
||||||
// 'x-component': 'Checkbox',
|
|
||||||
// },
|
|
||||||
deleteExecutionOnStatus: {
|
deleteExecutionOnStatus: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
title: `{{ t("Auto delete history when execution is on end status", { ns: "${NAMESPACE}" }) }}`,
|
title: `{{ t("Auto delete history when execution is on end status", { ns: "${NAMESPACE}" }) }}`,
|
||||||
|
@ -22,7 +22,6 @@ const collectionModeOptions = [
|
|||||||
|
|
||||||
export default class extends Trigger {
|
export default class extends Trigger {
|
||||||
title = `{{t("Collection event", { ns: "${NAMESPACE}" })}}`;
|
title = `{{t("Collection event", { ns: "${NAMESPACE}" })}}`;
|
||||||
type = 'collection';
|
|
||||||
description = `{{t("Event will be triggered on collection data row created, updated or deleted.", { ns: "${NAMESPACE}" })}}`;
|
description = `{{t("Event will be triggered on collection data row created, updated or deleted.", { ns: "${NAMESPACE}" })}}`;
|
||||||
fieldset = {
|
fieldset = {
|
||||||
collection: {
|
collection: {
|
||||||
|
@ -7,6 +7,7 @@ import { ISchema, useForm } from '@formily/react';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ActionContextProvider,
|
ActionContextProvider,
|
||||||
|
FieldNames,
|
||||||
FormProvider,
|
FormProvider,
|
||||||
SchemaComponent,
|
SchemaComponent,
|
||||||
SchemaInitializerItemType,
|
SchemaInitializerItemType,
|
||||||
@ -24,7 +25,7 @@ import { useFlowContext } from '../FlowContext';
|
|||||||
import { DrawerDescription } from '../components/DrawerDescription';
|
import { DrawerDescription } from '../components/DrawerDescription';
|
||||||
import { NAMESPACE, lang } from '../locale';
|
import { NAMESPACE, lang } from '../locale';
|
||||||
import useStyles from '../style';
|
import useStyles from '../style';
|
||||||
import { VariableOptions } from '../variable';
|
import { UseVariableOptions, VariableOption } from '../variable';
|
||||||
|
|
||||||
function useUpdateConfigAction() {
|
function useUpdateConfigAction() {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
@ -54,10 +55,9 @@ function useUpdateConfigAction() {
|
|||||||
|
|
||||||
export abstract class Trigger {
|
export abstract class Trigger {
|
||||||
title: string;
|
title: string;
|
||||||
type: string;
|
|
||||||
description?: string;
|
description?: string;
|
||||||
// group: string;
|
// group: string;
|
||||||
useVariables?(config: any, options?): VariableOptions;
|
useVariables?(config: Record<string, any>, options?: UseVariableOptions): VariableOption[];
|
||||||
fieldset: { [key: string]: ISchema };
|
fieldset: { [key: string]: ISchema };
|
||||||
view?: ISchema;
|
view?: ISchema;
|
||||||
scope?: { [key: string]: any };
|
scope?: { [key: string]: any };
|
||||||
|
@ -9,7 +9,6 @@ import { SCHEDULE_MODE } from './constants';
|
|||||||
|
|
||||||
export default class extends Trigger {
|
export default class extends Trigger {
|
||||||
title = `{{t("Schedule event", { ns: "${NAMESPACE}" })}}`;
|
title = `{{t("Schedule event", { ns: "${NAMESPACE}" })}}`;
|
||||||
type = 'schedule';
|
|
||||||
description = `{{t("Event will be scheduled and triggered base on time conditions.", { ns: "${NAMESPACE}" })}}`;
|
description = `{{t("Event will be scheduled and triggered base on time conditions.", { ns: "${NAMESPACE}" })}}`;
|
||||||
fieldset = {
|
fieldset = {
|
||||||
config: {
|
config: {
|
||||||
|
@ -11,21 +11,26 @@ export type VariableOption = {
|
|||||||
key?: string;
|
key?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
children?: VariableOptions;
|
children?: VariableOption[] | null;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VariableOptions = VariableOption[] | null;
|
|
||||||
|
|
||||||
export type VariableDataType =
|
export type VariableDataType =
|
||||||
| string
|
| 'boolean'
|
||||||
|
| 'number'
|
||||||
|
| 'string'
|
||||||
|
| 'date'
|
||||||
| {
|
| {
|
||||||
type: string;
|
type: 'reference';
|
||||||
options?: { entity?: boolean; collection?: string };
|
options: {
|
||||||
|
collection: string;
|
||||||
|
multiple?: boolean;
|
||||||
|
entity?: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
| ((field: any, appends?: string[]) => boolean);
|
| ((field: any) => boolean);
|
||||||
|
|
||||||
export type OptionsOfUseVariableOptions = {
|
export type UseVariableOptions = {
|
||||||
types?: VariableDataType[];
|
types?: VariableDataType[];
|
||||||
fieldNames?: {
|
fieldNames?: {
|
||||||
label?: string;
|
label?: string;
|
||||||
@ -34,7 +39,6 @@ export type OptionsOfUseVariableOptions = {
|
|||||||
};
|
};
|
||||||
appends?: string[] | null;
|
appends?: string[] | null;
|
||||||
depth?: number;
|
depth?: number;
|
||||||
current?: any;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultFieldNames = { label: 'label', value: 'value', children: 'children' } as const;
|
export const defaultFieldNames = { label: 'label', value: 'value', children: 'children' } as const;
|
||||||
@ -42,7 +46,7 @@ export const defaultFieldNames = { label: 'label', value: 'value', children: 'ch
|
|||||||
export const nodesOptions = {
|
export const nodesOptions = {
|
||||||
label: `{{t("Node result", { ns: "${NAMESPACE}" })}}`,
|
label: `{{t("Node result", { ns: "${NAMESPACE}" })}}`,
|
||||||
value: '$jobsMapByNodeKey',
|
value: '$jobsMapByNodeKey',
|
||||||
useOptions(options: OptionsOfUseVariableOptions) {
|
useOptions(options: UseVariableOptions) {
|
||||||
const { instructions } = usePlugin(WorkflowPlugin);
|
const { instructions } = usePlugin(WorkflowPlugin);
|
||||||
const current = useNodeContext();
|
const current = useNodeContext();
|
||||||
const upstreams = useAvailableUpstreams(current);
|
const upstreams = useAvailableUpstreams(current);
|
||||||
@ -61,7 +65,7 @@ export const nodesOptions = {
|
|||||||
export const triggerOptions = {
|
export const triggerOptions = {
|
||||||
label: `{{t("Trigger variables", { ns: "${NAMESPACE}" })}}`,
|
label: `{{t("Trigger variables", { ns: "${NAMESPACE}" })}}`,
|
||||||
value: '$context',
|
value: '$context',
|
||||||
useOptions(options: OptionsOfUseVariableOptions) {
|
useOptions(options: UseVariableOptions) {
|
||||||
const { triggers } = usePlugin(WorkflowPlugin);
|
const { triggers } = usePlugin(WorkflowPlugin);
|
||||||
const { workflow } = useFlowContext();
|
const { workflow } = useFlowContext();
|
||||||
const trigger = triggers.get(workflow.type);
|
const trigger = triggers.get(workflow.type);
|
||||||
@ -72,7 +76,7 @@ export const triggerOptions = {
|
|||||||
export const scopeOptions = {
|
export const scopeOptions = {
|
||||||
label: `{{t("Scope variables", { ns: "${NAMESPACE}" })}}`,
|
label: `{{t("Scope variables", { ns: "${NAMESPACE}" })}}`,
|
||||||
value: '$scopes',
|
value: '$scopes',
|
||||||
useOptions(options: OptionsOfUseVariableOptions) {
|
useOptions(options: UseVariableOptions & { current: any }) {
|
||||||
const { fieldNames = defaultFieldNames, current } = options;
|
const { fieldNames = defaultFieldNames, current } = options;
|
||||||
const { instructions } = usePlugin(WorkflowPlugin);
|
const { instructions } = usePlugin(WorkflowPlugin);
|
||||||
const source = useNodeContext();
|
const source = useNodeContext();
|
||||||
@ -98,7 +102,7 @@ export const scopeOptions = {
|
|||||||
export const systemOptions = {
|
export const systemOptions = {
|
||||||
label: `{{t("System variables", { ns: "${NAMESPACE}" })}}`,
|
label: `{{t("System variables", { ns: "${NAMESPACE}" })}}`,
|
||||||
value: '$system',
|
value: '$system',
|
||||||
useOptions({ types, fieldNames = defaultFieldNames }: OptionsOfUseVariableOptions) {
|
useOptions({ types, fieldNames = defaultFieldNames }: UseVariableOptions) {
|
||||||
return [
|
return [
|
||||||
...(!types || types.includes('date')
|
...(!types || types.includes('date')
|
||||||
? [
|
? [
|
||||||
@ -136,13 +140,12 @@ export const BaseTypeSets = {
|
|||||||
// { type: 'reference', options: { collection: 'attachments', multiple: false } }
|
// { type: 'reference', options: { collection: 'attachments', multiple: false } }
|
||||||
// { type: 'reference', options: { collection: 'myExpressions', entity: false } }
|
// { type: 'reference', options: { collection: 'myExpressions', entity: false } }
|
||||||
|
|
||||||
function matchFieldType(field, type): boolean {
|
function matchFieldType(field, type: VariableDataType): boolean {
|
||||||
const inputType = typeof type;
|
if (typeof type === 'string') {
|
||||||
if (inputType === 'string') {
|
|
||||||
return BaseTypeSets[type]?.has(field.interface);
|
return BaseTypeSets[type]?.has(field.interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputType === 'object' && type.type === 'reference') {
|
if (typeof type === 'object' && type.type === 'reference') {
|
||||||
if (isAssociationField(field)) {
|
if (isAssociationField(field)) {
|
||||||
return (
|
return (
|
||||||
type.options?.entity && (field.collectionName === type.options?.collection || type.options?.collection === '*')
|
type.options?.entity && (field.collectionName === type.options?.collection || type.options?.collection === '*')
|
||||||
@ -157,7 +160,7 @@ function matchFieldType(field, type): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputType === 'function') {
|
if (typeof type === 'function') {
|
||||||
return type(field);
|
return type(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +235,7 @@ function useOptions(scope, opts) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWorkflowVariableOptions(options: OptionsOfUseVariableOptions = {}) {
|
export function useWorkflowVariableOptions(options: UseVariableOptions = {}) {
|
||||||
const fieldNames = Object.assign({}, defaultFieldNames, options.fieldNames ?? {});
|
const fieldNames = Object.assign({}, defaultFieldNames, options.fieldNames ?? {});
|
||||||
const opts = Object.assign(options, { fieldNames });
|
const opts = Object.assign(options, { fieldNames });
|
||||||
const result = [
|
const result = [
|
||||||
|
@ -12,10 +12,10 @@ import Processor from './Processor';
|
|||||||
import initActions from './actions';
|
import initActions from './actions';
|
||||||
import { EXECUTION_STATUS } from './constants';
|
import { EXECUTION_STATUS } from './constants';
|
||||||
import initFunctions, { CustomFunction } from './functions';
|
import initFunctions, { CustomFunction } from './functions';
|
||||||
import type Trigger from './triggers';
|
import Trigger from './triggers';
|
||||||
import CollectionTrigger from './triggers/CollectionTrigger';
|
import CollectionTrigger from './triggers/CollectionTrigger';
|
||||||
import ScheduleTrigger from './triggers/ScheduleTrigger';
|
import ScheduleTrigger from './triggers/ScheduleTrigger';
|
||||||
import type Instruction from './instructions';
|
import Instruction from './instructions';
|
||||||
import CalculationInstruction from './instructions/CalculationInstruction';
|
import CalculationInstruction from './instructions/CalculationInstruction';
|
||||||
import ConditionInstruction from './instructions/ConditionInstruction';
|
import ConditionInstruction from './instructions/ConditionInstruction';
|
||||||
import CreateInstruction from './instructions/CreateInstruction';
|
import CreateInstruction from './instructions/CreateInstruction';
|
||||||
@ -112,29 +112,45 @@ export default class WorkflowPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
initTriggers<T extends Trigger>(more: { [key: string]: T | { new (p: Plugin): T } } = {}) {
|
registerTrigger<T extends Trigger>(type: string, trigger: T | { new (p: Plugin): T }) {
|
||||||
const { triggers } = this;
|
if (typeof trigger === 'function') {
|
||||||
|
this.triggers.register(type, new trigger(this));
|
||||||
triggers.register('collection', new CollectionTrigger(this));
|
} else if (trigger) {
|
||||||
triggers.register('schedule', new ScheduleTrigger(this));
|
this.triggers.register(type, trigger);
|
||||||
|
} else {
|
||||||
for (const [name, trigger] of Object.entries(more)) {
|
throw new Error('invalid trigger type to register');
|
||||||
triggers.register(name, typeof trigger === 'function' ? new trigger(this) : trigger);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initInstructions<T extends Instruction>(more: { [key: string]: T | { new (p: Plugin): T } } = {}) {
|
registerInstruction<T extends Instruction>(type: string, instruction: T | { new (p: Plugin): T }) {
|
||||||
const { instructions } = this;
|
if (typeof instruction === 'function') {
|
||||||
|
this.instructions.register(type, new instruction(this));
|
||||||
|
} else if (instruction) {
|
||||||
|
this.instructions.register(type, instruction);
|
||||||
|
} else {
|
||||||
|
throw new Error('invalid instruction type to register');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
instructions.register('calculation', new CalculationInstruction(this));
|
private initTriggers<T extends Trigger>(more: { [key: string]: T | { new (p: Plugin): T } } = {}) {
|
||||||
instructions.register('condition', new ConditionInstruction(this));
|
this.registerTrigger('collection', CollectionTrigger);
|
||||||
instructions.register('create', new CreateInstruction(this));
|
this.registerTrigger('schedule', ScheduleTrigger);
|
||||||
instructions.register('destroy', new DestroyInstruction(this));
|
|
||||||
instructions.register('query', new QueryInstruction(this));
|
for (const [name, trigger] of Object.entries(more)) {
|
||||||
instructions.register('update', new UpdateInstruction(this));
|
this.registerTrigger(name, trigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private initInstructions<T extends Instruction>(more: { [key: string]: T | { new (p: Plugin): T } } = {}) {
|
||||||
|
this.registerInstruction('calculation', CalculationInstruction);
|
||||||
|
this.registerInstruction('condition', ConditionInstruction);
|
||||||
|
this.registerInstruction('create', CreateInstruction);
|
||||||
|
this.registerInstruction('destroy', DestroyInstruction);
|
||||||
|
this.registerInstruction('query', QueryInstruction);
|
||||||
|
this.registerInstruction('update', UpdateInstruction);
|
||||||
|
|
||||||
for (const [name, instruction] of Object.entries({ ...more })) {
|
for (const [name, instruction] of Object.entries({ ...more })) {
|
||||||
instructions.register(name, typeof instruction === 'function' ? new instruction(this) : instruction);
|
this.registerInstruction(name, instruction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +187,6 @@ export default class WorkflowPlugin extends Plugin {
|
|||||||
actions: ['workflows:list'],
|
actions: ['workflows:list'],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.app.acl.allow('users_jobs', ['list', 'get', 'submit'], 'loggedIn');
|
|
||||||
this.app.acl.allow('workflows', ['trigger'], 'loggedIn');
|
this.app.acl.allow('workflows', ['trigger'], 'loggedIn');
|
||||||
|
|
||||||
await db.import({
|
await db.import({
|
||||||
|
@ -25,8 +25,6 @@ export default class Processor {
|
|||||||
|
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
|
|
||||||
transaction?: Transaction;
|
|
||||||
|
|
||||||
nodes: FlowNodeModel[] = [];
|
nodes: FlowNodeModel[] = [];
|
||||||
nodesMap = new Map<number, FlowNodeModel>();
|
nodesMap = new Map<number, FlowNodeModel>();
|
||||||
jobsMap = new Map<number, JobModel>();
|
jobsMap = new Map<number, JobModel>();
|
||||||
@ -67,35 +65,18 @@ export default class Processor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getTransaction() {
|
|
||||||
if (!this.execution.workflow.options?.useTransaction) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { options } = this;
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
return options.transaction && !options.transaction.finished
|
|
||||||
? options.transaction
|
|
||||||
: await options.plugin.db.sequelize.transaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async prepare() {
|
public async prepare() {
|
||||||
const { execution } = this;
|
const { execution } = this;
|
||||||
if (!execution.workflow) {
|
if (!execution.workflow) {
|
||||||
execution.workflow = await execution.getWorkflow();
|
execution.workflow = await execution.getWorkflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
const transaction = await this.getTransaction();
|
|
||||||
this.transaction = transaction;
|
|
||||||
|
|
||||||
const nodes = await execution.workflow.getNodes();
|
const nodes = await execution.workflow.getNodes();
|
||||||
|
|
||||||
this.makeNodes(nodes);
|
this.makeNodes(nodes);
|
||||||
|
|
||||||
const jobs = await execution.getJobs({
|
const jobs = await execution.getJobs({
|
||||||
order: [['id', 'ASC']],
|
order: [['id', 'ASC']],
|
||||||
transaction,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.makeJobs(jobs);
|
this.makeJobs(jobs);
|
||||||
@ -125,13 +106,6 @@ export default class Processor {
|
|||||||
await this.recall(node, job);
|
await this.recall(node, job);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async commit() {
|
|
||||||
// @ts-ignore
|
|
||||||
if (this.transaction && (!this.options.transaction || this.options.transaction.finished)) {
|
|
||||||
await this.transaction.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async exec(instruction: Runner, node: FlowNodeModel, prevJob) {
|
private async exec(instruction: Runner, node: FlowNodeModel, prevJob) {
|
||||||
let job;
|
let job;
|
||||||
try {
|
try {
|
||||||
@ -224,10 +198,9 @@ export default class Processor {
|
|||||||
async exit(s?: number) {
|
async exit(s?: number) {
|
||||||
if (typeof s === 'number') {
|
if (typeof s === 'number') {
|
||||||
const status = (<typeof Processor>this.constructor).StatusMap[s] ?? Math.sign(s);
|
const status = (<typeof Processor>this.constructor).StatusMap[s] ?? Math.sign(s);
|
||||||
await this.execution.update({ status }, { transaction: this.transaction });
|
await this.execution.update({ status });
|
||||||
}
|
}
|
||||||
this.logger.info(`execution (${this.execution.id}) exiting with status ${this.execution.status}`);
|
this.logger.info(`execution (${this.execution.id}) exiting with status ${this.execution.status}`);
|
||||||
await this.commit();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,22 +210,15 @@ export default class Processor {
|
|||||||
const { model } = database.getCollection('jobs');
|
const { model } = database.getCollection('jobs');
|
||||||
let job;
|
let job;
|
||||||
if (payload instanceof model) {
|
if (payload instanceof model) {
|
||||||
job = await payload.save({ transaction: this.transaction });
|
job = await payload.save();
|
||||||
} else if (payload.id) {
|
} else if (payload.id) {
|
||||||
job = await model.findByPk(payload.id);
|
job = await model.findByPk(payload.id);
|
||||||
await job.update(payload, {
|
await job.update(payload);
|
||||||
transaction: this.transaction,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
job = await model.create(
|
job = await model.create({
|
||||||
{
|
...payload,
|
||||||
...payload,
|
executionId: this.execution.id,
|
||||||
executionId: this.execution.id,
|
});
|
||||||
},
|
|
||||||
{
|
|
||||||
transaction: this.transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
this.jobsMap.set(job.id, job);
|
this.jobsMap.set(job.id, job);
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import Database from '@nocobase/database';
|
import { MockDatabase } from '@nocobase/database';
|
||||||
import { MockServer } from '@nocobase/test';
|
import { MockServer } from '@nocobase/test';
|
||||||
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
||||||
import { BRANCH_INDEX, EXECUTION_STATUS, JOB_STATUS } from '../constants';
|
import { EXECUTION_STATUS, JOB_STATUS } from '../constants';
|
||||||
|
|
||||||
describe('workflow > Processor', () => {
|
describe('workflow > Processor', () => {
|
||||||
let app: MockServer;
|
let app: MockServer;
|
||||||
let db: Database;
|
let db: MockDatabase;
|
||||||
let PostRepo;
|
let PostRepo;
|
||||||
let WorkflowModel;
|
let WorkflowModel;
|
||||||
let workflow;
|
let workflow;
|
||||||
@ -251,13 +251,13 @@ describe('workflow > Processor', () => {
|
|||||||
|
|
||||||
const n2 = await workflow.createNode({
|
const n2 = await workflow.createNode({
|
||||||
type: 'echo',
|
type: 'echo',
|
||||||
branchIndex: BRANCH_INDEX.ON_TRUE,
|
branchIndex: 1,
|
||||||
upstreamId: n1.id,
|
upstreamId: n1.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
await workflow.createNode({
|
await workflow.createNode({
|
||||||
type: 'echo',
|
type: 'echo',
|
||||||
branchIndex: BRANCH_INDEX.ON_FALSE,
|
branchIndex: 0,
|
||||||
upstreamId: n1.id,
|
upstreamId: n1.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -283,7 +283,7 @@ describe('workflow > Processor', () => {
|
|||||||
|
|
||||||
const n2 = await workflow.createNode({
|
const n2 = await workflow.createNode({
|
||||||
type: 'prompt',
|
type: 'prompt',
|
||||||
branchIndex: BRANCH_INDEX.ON_TRUE,
|
branchIndex: 1,
|
||||||
upstreamId: n1.id,
|
upstreamId: n1.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -323,7 +323,7 @@ describe('workflow > Processor', () => {
|
|||||||
|
|
||||||
const n2 = await workflow.createNode({
|
const n2 = await workflow.createNode({
|
||||||
type: 'prompt->error',
|
type: 'prompt->error',
|
||||||
branchIndex: BRANCH_INDEX.ON_TRUE,
|
branchIndex: 1,
|
||||||
upstreamId: n1.id,
|
upstreamId: n1.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import Database from '@nocobase/database';
|
import Database from '@nocobase/database';
|
||||||
import { Application } from '@nocobase/server';
|
import { Application } from '@nocobase/server';
|
||||||
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
||||||
import { BRANCH_INDEX, EXECUTION_STATUS, JOB_STATUS } from '../../constants';
|
import { EXECUTION_STATUS, JOB_STATUS } from '../../constants';
|
||||||
|
import { BRANCH_INDEX } from '../../instructions/ConditionInstruction';
|
||||||
|
|
||||||
describe('workflow > instructions > condition', () => {
|
describe('workflow > instructions > condition', () => {
|
||||||
let app: Application;
|
let app: Application;
|
||||||
|
@ -54,12 +54,11 @@ export async function destroy(context: Context, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function revision(context: Context, next) {
|
export async function revision(context: Context, next) {
|
||||||
const plugin = context.app.getPlugin('workflow') as Plugin;
|
const plugin = context.app.getPlugin(Plugin);
|
||||||
const { db } = context;
|
|
||||||
const repository = utils.getRepositoryFromParams(context);
|
const repository = utils.getRepositoryFromParams(context);
|
||||||
const { filterByTk, filter = {}, values = {} } = context.action.params;
|
const { filterByTk, filter = {}, values = {} } = context.action.params;
|
||||||
|
|
||||||
context.body = await db.sequelize.transaction(async (transaction) => {
|
context.body = await context.db.sequelize.transaction(async (transaction) => {
|
||||||
const origin = await repository.findOne({
|
const origin = await repository.findOne({
|
||||||
filterByTk,
|
filterByTk,
|
||||||
filter,
|
filter,
|
||||||
@ -140,7 +139,7 @@ export async function revision(context: Context, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function sync(context: Context, next) {
|
export async function sync(context: Context, next) {
|
||||||
const plugin = context.app.getPlugin('workflow');
|
const plugin = context.app.getPlugin(Plugin);
|
||||||
const repository = utils.getRepositoryFromParams(context);
|
const repository = utils.getRepositoryFromParams(context);
|
||||||
const { filterByTk, filter = {} } = context.action.params;
|
const { filterByTk, filter = {} } = context.action.params;
|
||||||
|
|
||||||
|
@ -35,11 +35,6 @@ export default function () {
|
|||||||
required: true,
|
required: true,
|
||||||
defaultValue: {},
|
defaultValue: {},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'boolean',
|
|
||||||
name: 'useTransaction',
|
|
||||||
// defaultValue: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
name: 'nodes',
|
name: 'nodes',
|
||||||
|
@ -8,7 +8,7 @@ export const EXECUTION_STATUS = {
|
|||||||
CANCELED: -4,
|
CANCELED: -4,
|
||||||
REJECTED: -5,
|
REJECTED: -5,
|
||||||
RETRY_NEEDED: -6,
|
RETRY_NEEDED: -6,
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
export const JOB_STATUS = {
|
export const JOB_STATUS = {
|
||||||
PENDING: 0,
|
PENDING: 0,
|
||||||
@ -19,10 +19,4 @@ export const JOB_STATUS = {
|
|||||||
CANCELED: -4,
|
CANCELED: -4,
|
||||||
REJECTED: -5,
|
REJECTED: -5,
|
||||||
RETRY_NEEDED: -6,
|
RETRY_NEEDED: -6,
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
export const BRANCH_INDEX = {
|
|
||||||
DEFAULT: null,
|
|
||||||
ON_TRUE: 1,
|
|
||||||
ON_FALSE: 0,
|
|
||||||
};
|
|
||||||
|
@ -7,6 +7,12 @@ import type { FlowNodeModel, JobModel } from '../types';
|
|||||||
|
|
||||||
type Comparer = (a: any, b: any) => boolean;
|
type Comparer = (a: any, b: any) => boolean;
|
||||||
|
|
||||||
|
export const BRANCH_INDEX = {
|
||||||
|
DEFAULT: null,
|
||||||
|
ON_TRUE: 1,
|
||||||
|
ON_FALSE: 0,
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const calculators = new Registry<Comparer>();
|
export const calculators = new Registry<Comparer>();
|
||||||
|
|
||||||
// built-in functions
|
// built-in functions
|
||||||
|
@ -15,7 +15,7 @@ export class CreateInstruction extends Instruction {
|
|||||||
context: {
|
context: {
|
||||||
executionId: processor.execution.id,
|
executionId: processor.execution.id,
|
||||||
},
|
},
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = created;
|
let result = created;
|
||||||
@ -28,7 +28,7 @@ export class CreateInstruction extends Instruction {
|
|||||||
result = await repository.findOne({
|
result = await repository.findOne({
|
||||||
filterByTk: created[model.primaryKeyAttribute],
|
filterByTk: created[model.primaryKeyAttribute],
|
||||||
appends: Array.from(includeFields),
|
appends: Array.from(includeFields),
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export class DestroyInstruction extends Instruction {
|
|||||||
context: {
|
context: {
|
||||||
executionId: processor.execution.id,
|
executionId: processor.execution.id,
|
||||||
},
|
},
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -33,7 +33,7 @@ export class QueryInstruction extends Instruction {
|
|||||||
.filter((item) => item.field)
|
.filter((item) => item.field)
|
||||||
.map((item) => `${item.direction?.toLowerCase() === 'desc' ? '-' : ''}${item.field}`),
|
.map((item) => `${item.direction?.toLowerCase() === 'desc' ? '-' : ''}${item.field}`),
|
||||||
appends,
|
appends,
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (failOnEmpty && (multiple ? !result.length : !result)) {
|
if (failOnEmpty && (multiple ? !result.length : !result)) {
|
||||||
|
@ -14,7 +14,7 @@ export class UpdateInstruction extends Instruction {
|
|||||||
context: {
|
context: {
|
||||||
executionId: processor.execution.id,
|
executionId: processor.execution.id,
|
||||||
},
|
},
|
||||||
transaction: processor.transaction,
|
// transaction: processor.transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -18,7 +18,7 @@ export type Runner = (node: FlowNodeModel, input: any, processor: Processor) =>
|
|||||||
// what should a instruction do?
|
// what should a instruction do?
|
||||||
// - base on input and context, do any calculations or system call (io), and produce a result or pending.
|
// - base on input and context, do any calculations or system call (io), and produce a result or pending.
|
||||||
export abstract class Instruction {
|
export abstract class Instruction {
|
||||||
constructor(public plugin: Plugin) {}
|
constructor(public workflow: Plugin) {}
|
||||||
|
|
||||||
abstract run(node: FlowNodeModel, input: any, processor: Processor): InstructionResult;
|
abstract run(node: FlowNodeModel, input: any, processor: Processor): InstructionResult;
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ async function handler(this: CollectionTrigger, workflow: WorkflowModel, data: M
|
|||||||
// TODO: `result.toJSON()` throws error
|
// TODO: `result.toJSON()` throws error
|
||||||
const json = toJSON(result);
|
const json = toJSON(result);
|
||||||
|
|
||||||
this.plugin.trigger(
|
this.workflow.trigger(
|
||||||
workflow,
|
workflow,
|
||||||
{ data: json },
|
{ data: json },
|
||||||
{
|
{
|
||||||
@ -98,7 +98,7 @@ export default class CollectionTrigger extends Trigger {
|
|||||||
events = new Map();
|
events = new Map();
|
||||||
|
|
||||||
on(workflow: WorkflowModel) {
|
on(workflow: WorkflowModel) {
|
||||||
const { db } = this.plugin.app;
|
const { db } = this.workflow.app;
|
||||||
const { collection, mode } = workflow.config;
|
const { collection, mode } = workflow.config;
|
||||||
const Collection = db.getCollection(collection);
|
const Collection = db.getCollection(collection);
|
||||||
if (!Collection) {
|
if (!Collection) {
|
||||||
@ -125,7 +125,7 @@ export default class CollectionTrigger extends Trigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
off(workflow: WorkflowModel) {
|
off(workflow: WorkflowModel) {
|
||||||
const { db } = this.plugin.app;
|
const { db } = this.workflow.app;
|
||||||
const { collection, mode } = workflow.config;
|
const { collection, mode } = workflow.config;
|
||||||
const Collection = db.getCollection(collection);
|
const Collection = db.getCollection(collection);
|
||||||
if (!Collection) {
|
if (!Collection) {
|
||||||
|
@ -103,7 +103,7 @@ ScheduleModes.set(SCHEDULE_MODE.CONSTANT, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.plugin.trigger(workflow, { date: now });
|
this.workflow.trigger(workflow, { date: now });
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
@ -201,7 +201,7 @@ ScheduleModes.set(SCHEDULE_MODE.COLLECTION_FIELD, {
|
|||||||
this.setCache(workflow);
|
this.setCache(workflow);
|
||||||
};
|
};
|
||||||
this.events.set(name, listener);
|
this.events.set(name, listener);
|
||||||
this.plugin.app.db.on(event, listener);
|
this.workflow.app.db.on(event, listener);
|
||||||
},
|
},
|
||||||
|
|
||||||
off(workflow) {
|
off(workflow) {
|
||||||
@ -211,12 +211,12 @@ ScheduleModes.set(SCHEDULE_MODE.COLLECTION_FIELD, {
|
|||||||
if (this.events.has(name)) {
|
if (this.events.has(name)) {
|
||||||
const listener = this.events.get(name);
|
const listener = this.events.get(name);
|
||||||
this.events.delete(name);
|
this.events.delete(name);
|
||||||
this.plugin.app.db.off(event, listener);
|
this.workflow.app.db.off(event, listener);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async shouldCache(workflow, now) {
|
async shouldCache(workflow, now) {
|
||||||
const { db } = this.plugin.app;
|
const { db } = this.workflow.app;
|
||||||
const { startsOn, endsOn, repeat, collection } = workflow.config;
|
const { startsOn, endsOn, repeat, collection } = workflow.config;
|
||||||
const timestamp = now.getTime();
|
const timestamp = now.getTime();
|
||||||
|
|
||||||
@ -305,7 +305,7 @@ ScheduleModes.set(SCHEDULE_MODE.COLLECTION_FIELD, {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const tsFn = DialectTimestampFnMap[this.plugin.app.db.options.dialect];
|
const tsFn = DialectTimestampFnMap[this.workflow.app.db.options.dialect];
|
||||||
if (typeof repeat === 'number' && tsFn) {
|
if (typeof repeat === 'number' && tsFn) {
|
||||||
const modExp = fn(
|
const modExp = fn(
|
||||||
'MOD',
|
'MOD',
|
||||||
@ -343,7 +343,7 @@ ScheduleModes.set(SCHEDULE_MODE.COLLECTION_FIELD, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const repo = this.plugin.app.db.getRepository(collection);
|
const repo = this.workflow.app.db.getRepository(collection);
|
||||||
const instances = await repo.find({
|
const instances = await repo.find({
|
||||||
where: {
|
where: {
|
||||||
[Op.and]: conditions,
|
[Op.and]: conditions,
|
||||||
@ -357,7 +357,7 @@ ScheduleModes.set(SCHEDULE_MODE.COLLECTION_FIELD, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
instances.forEach((item) => {
|
instances.forEach((item) => {
|
||||||
this.plugin.trigger(workflow, {
|
this.workflow.trigger(workflow, {
|
||||||
date: now,
|
date: now,
|
||||||
data: item.toJSON(),
|
data: item.toJSON(),
|
||||||
});
|
});
|
||||||
@ -423,10 +423,10 @@ export default class ScheduleTrigger extends Trigger {
|
|||||||
// caching workflows in range, default to 1min
|
// caching workflows in range, default to 1min
|
||||||
cacheCycle = 60_000;
|
cacheCycle = 60_000;
|
||||||
|
|
||||||
constructor(plugin: Plugin) {
|
constructor(workflow: Plugin) {
|
||||||
super(plugin);
|
super(workflow);
|
||||||
|
|
||||||
plugin.app.on('beforeStop', () => {
|
workflow.app.on('beforeStop', () => {
|
||||||
if (this.timer) {
|
if (this.timer) {
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
}
|
}
|
||||||
@ -434,7 +434,7 @@ export default class ScheduleTrigger extends Trigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
if (this.plugin.app.getPlugin('multi-app-share-collection')?.enabled && this.plugin.app.name !== 'main') {
|
if (this.workflow.app.getPlugin('multi-app-share-collection')?.enabled && this.workflow.app.name !== 'main') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,7 +474,7 @@ export default class ScheduleTrigger extends Trigger {
|
|||||||
|
|
||||||
async onTick(now) {
|
async onTick(now) {
|
||||||
// NOTE: trigger workflows in sequence when sqlite due to only one transaction
|
// NOTE: trigger workflows in sequence when sqlite due to only one transaction
|
||||||
const isSqlite = this.plugin.app.db.options.dialect === 'sqlite';
|
const isSqlite = this.workflow.app.db.options.dialect === 'sqlite';
|
||||||
return Array.from(this.cache.values()).reduce(
|
return Array.from(this.cache.values()).reduce(
|
||||||
(prev, workflow) => {
|
(prev, workflow) => {
|
||||||
if (!this.shouldTrigger(workflow, now)) {
|
if (!this.shouldTrigger(workflow, now)) {
|
||||||
@ -491,7 +491,7 @@ export default class ScheduleTrigger extends Trigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async reload() {
|
async reload() {
|
||||||
const WorkflowRepo = this.plugin.app.db.getRepository('workflows');
|
const WorkflowRepo = this.workflow.app.db.getRepository('workflows');
|
||||||
const workflows = await WorkflowRepo.find({
|
const workflows = await WorkflowRepo.find({
|
||||||
filter: { enabled: true, type: 'schedule' },
|
filter: { enabled: true, type: 'schedule' },
|
||||||
});
|
});
|
||||||
@ -510,7 +510,7 @@ export default class ScheduleTrigger extends Trigger {
|
|||||||
const should = await this.shouldCache(workflow, now);
|
const should = await this.shouldCache(workflow, now);
|
||||||
|
|
||||||
if (should) {
|
if (should) {
|
||||||
this.plugin.getLogger(workflow.id).info('caching scheduled workflow will run in next minute');
|
this.workflow.getLogger(workflow.id).info('caching scheduled workflow will run in next minute');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setCache(workflow, !should);
|
this.setCache(workflow, !should);
|
||||||
|
@ -3,7 +3,7 @@ import type Plugin from '../Plugin';
|
|||||||
import type { WorkflowModel } from '../types';
|
import type { WorkflowModel } from '../types';
|
||||||
|
|
||||||
export abstract class Trigger {
|
export abstract class Trigger {
|
||||||
constructor(public readonly plugin: Plugin) {}
|
constructor(public readonly workflow: Plugin) {}
|
||||||
abstract on(workflow: WorkflowModel): void;
|
abstract on(workflow: WorkflowModel): void;
|
||||||
abstract off(workflow: WorkflowModel): void;
|
abstract off(workflow: WorkflowModel): void;
|
||||||
duplicateConfig?(workflow: WorkflowModel, options: Transactionable): object | Promise<object>;
|
duplicateConfig?(workflow: WorkflowModel, options: Transactionable): object | Promise<object>;
|
||||||
|
@ -9,9 +9,6 @@ export default class ExecutionModel extends Model {
|
|||||||
declare title: string;
|
declare title: string;
|
||||||
declare context: any;
|
declare context: any;
|
||||||
declare status: number;
|
declare status: number;
|
||||||
// NOTE: this duplicated column is for transaction in preparing cycle from workflow
|
|
||||||
declare useTransaction: boolean;
|
|
||||||
declare transaction: string;
|
|
||||||
|
|
||||||
declare createdAt: Date;
|
declare createdAt: Date;
|
||||||
declare updatedAt: Date;
|
declare updatedAt: Date;
|
||||||
|
@ -19,7 +19,6 @@ export default class WorkflowModel extends Model {
|
|||||||
declare description?: string;
|
declare description?: string;
|
||||||
declare type: string;
|
declare type: string;
|
||||||
declare config: any;
|
declare config: any;
|
||||||
declare useTransaction: boolean;
|
|
||||||
declare executed: number;
|
declare executed: number;
|
||||||
|
|
||||||
declare createdAt: Date;
|
declare createdAt: Date;
|
||||||
|
Loading…
Reference in New Issue
Block a user