@nocobase/plugin-workflow 1.5.0-beta.3 → 1.5.0-beta.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/112c72b90000c81b.js +10 -0
- package/dist/client/739d458621edf81f.js +10 -0
- package/dist/client/98864c2e0ff69ea1.js +10 -0
- package/dist/client/FlowContext.d.ts +2 -0
- package/dist/client/components/TriggerCollectionRecordSelect.d.ts +10 -0
- package/dist/client/components/index.d.ts +1 -0
- package/dist/client/constants.d.ts +1 -0
- package/dist/client/e7b9d67c6a964bec.js +10 -0
- package/dist/client/index.js +1 -566
- package/dist/client/triggers/collection.d.ts +14 -0
- package/dist/client/triggers/index.d.ts +3 -3
- package/dist/client/triggers/schedule/ScheduleModes.d.ts +170 -0
- package/dist/client/triggers/schedule/TriggerScheduleConfig.d.ts +10 -0
- package/dist/client/triggers/schedule/index.d.ts +13 -0
- package/dist/client/variable.d.ts +16 -1
- package/dist/externalVersion.js +11 -11
- package/dist/locale/zh-CN.json +19 -5
- package/dist/node_modules/cron-parser/package.json +1 -1
- package/dist/node_modules/lru-cache/package.json +1 -1
- package/dist/server/Plugin.d.ts +13 -6
- package/dist/server/Plugin.js +165 -83
- package/dist/server/Processor.js +3 -3
- package/dist/server/actions/workflows.d.ts +5 -0
- package/dist/server/actions/workflows.js +59 -61
- package/dist/server/collections/executions.js +8 -0
- package/dist/server/collections/workflows.js +2 -2
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/instructions/CreateInstruction.js +1 -1
- package/dist/server/instructions/DestroyInstruction.js +1 -1
- package/dist/server/instructions/UpdateInstruction.js +1 -1
- package/dist/server/repositories/WorkflowRepository.d.ts +12 -0
- package/dist/server/repositories/WorkflowRepository.js +112 -0
- package/dist/server/triggers/CollectionTrigger.d.ts +7 -2
- package/dist/server/triggers/CollectionTrigger.js +104 -73
- package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.d.ts +3 -2
- package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.js +51 -17
- package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.d.ts +1 -0
- package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.js +3 -0
- package/dist/server/triggers/ScheduleTrigger/index.d.ts +1 -0
- package/dist/server/triggers/ScheduleTrigger/index.js +7 -0
- package/dist/server/triggers/index.d.ts +4 -2
- package/dist/server/triggers/index.js +4 -0
- package/package.json +3 -3
|
@@ -44,7 +44,7 @@ class CreateInstruction extends import__.Instruction {
|
|
|
44
44
|
const created = await repository.create({
|
|
45
45
|
...options,
|
|
46
46
|
context: {
|
|
47
|
-
stack: Array.from(new Set((processor.execution.
|
|
47
|
+
stack: Array.from(new Set((processor.execution.stack ?? []).concat(processor.execution.id)))
|
|
48
48
|
},
|
|
49
49
|
transaction
|
|
50
50
|
});
|
|
@@ -42,7 +42,7 @@ class DestroyInstruction extends import__.Instruction {
|
|
|
42
42
|
const result = await repository.destroy({
|
|
43
43
|
...options,
|
|
44
44
|
context: {
|
|
45
|
-
stack: Array.from(new Set((processor.execution.
|
|
45
|
+
stack: Array.from(new Set((processor.execution.stack ?? []).concat(processor.execution.id)))
|
|
46
46
|
},
|
|
47
47
|
transaction: this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction)
|
|
48
48
|
});
|
|
@@ -42,7 +42,7 @@ class UpdateInstruction extends import__.Instruction {
|
|
|
42
42
|
const result = await repository.update({
|
|
43
43
|
...options,
|
|
44
44
|
context: {
|
|
45
|
-
stack: Array.from(new Set((processor.execution.
|
|
45
|
+
stack: Array.from(new Set((processor.execution.stack ?? []).concat(processor.execution.id)))
|
|
46
46
|
},
|
|
47
47
|
transaction: this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction)
|
|
48
48
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Repository } from '@nocobase/database';
|
|
10
|
+
export default class WorkflowRepository extends Repository {
|
|
11
|
+
revision(options: any): Promise<any>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __create = Object.create;
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
15
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(from))
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
27
|
+
};
|
|
28
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
29
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
30
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
31
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
32
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
33
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
34
|
+
mod
|
|
35
|
+
));
|
|
36
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
|
+
var WorkflowRepository_exports = {};
|
|
38
|
+
__export(WorkflowRepository_exports, {
|
|
39
|
+
default: () => WorkflowRepository
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(WorkflowRepository_exports);
|
|
42
|
+
var import_database = require("@nocobase/database");
|
|
43
|
+
var import_Plugin = __toESM(require("../Plugin"));
|
|
44
|
+
class WorkflowRepository extends import_database.Repository {
|
|
45
|
+
async revision(options) {
|
|
46
|
+
const { filterByTk, filter, values, context } = options;
|
|
47
|
+
const plugin = context.app.pm.get(import_Plugin.default);
|
|
48
|
+
return this.database.sequelize.transaction(async (transaction) => {
|
|
49
|
+
const origin = await this.findOne({
|
|
50
|
+
filterByTk,
|
|
51
|
+
filter,
|
|
52
|
+
appends: ["nodes"],
|
|
53
|
+
context,
|
|
54
|
+
transaction
|
|
55
|
+
});
|
|
56
|
+
const trigger = plugin.triggers.get(origin.type);
|
|
57
|
+
const revisionData = filter.key ? {
|
|
58
|
+
key: filter.key,
|
|
59
|
+
title: origin.title,
|
|
60
|
+
triggerTitle: origin.triggerTitle,
|
|
61
|
+
allExecuted: origin.allExecuted,
|
|
62
|
+
current: null,
|
|
63
|
+
...values
|
|
64
|
+
} : values;
|
|
65
|
+
const instance = await this.create({
|
|
66
|
+
values: {
|
|
67
|
+
title: `${origin.title} copy`,
|
|
68
|
+
description: origin.description,
|
|
69
|
+
...revisionData,
|
|
70
|
+
sync: origin.sync,
|
|
71
|
+
type: origin.type,
|
|
72
|
+
config: typeof trigger.duplicateConfig === "function" ? await trigger.duplicateConfig(origin, { transaction }) : origin.config
|
|
73
|
+
},
|
|
74
|
+
transaction
|
|
75
|
+
});
|
|
76
|
+
const originalNodesMap = /* @__PURE__ */ new Map();
|
|
77
|
+
origin.nodes.forEach((node) => {
|
|
78
|
+
originalNodesMap.set(node.id, node);
|
|
79
|
+
});
|
|
80
|
+
const oldToNew = /* @__PURE__ */ new Map();
|
|
81
|
+
const newToOld = /* @__PURE__ */ new Map();
|
|
82
|
+
for await (const node of origin.nodes) {
|
|
83
|
+
const instruction = plugin.instructions.get(node.type);
|
|
84
|
+
const newNode = await instance.createNode(
|
|
85
|
+
{
|
|
86
|
+
type: node.type,
|
|
87
|
+
key: node.key,
|
|
88
|
+
config: typeof instruction.duplicateConfig === "function" ? await instruction.duplicateConfig(node, { transaction }) : node.config,
|
|
89
|
+
title: node.title,
|
|
90
|
+
branchIndex: node.branchIndex
|
|
91
|
+
},
|
|
92
|
+
{ transaction }
|
|
93
|
+
);
|
|
94
|
+
oldToNew.set(node.id, newNode);
|
|
95
|
+
newToOld.set(newNode.id, node);
|
|
96
|
+
}
|
|
97
|
+
for await (const [oldId, newNode] of oldToNew.entries()) {
|
|
98
|
+
const oldNode = originalNodesMap.get(oldId);
|
|
99
|
+
const newUpstream = oldNode.upstreamId ? oldToNew.get(oldNode.upstreamId) : null;
|
|
100
|
+
const newDownstream = oldNode.downstreamId ? oldToNew.get(oldNode.downstreamId) : null;
|
|
101
|
+
await newNode.update(
|
|
102
|
+
{
|
|
103
|
+
upstreamId: (newUpstream == null ? void 0 : newUpstream.id) ?? null,
|
|
104
|
+
downstreamId: (newDownstream == null ? void 0 : newDownstream.id) ?? null
|
|
105
|
+
},
|
|
106
|
+
{ transaction }
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return instance;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { Model } from '@nocobase/database';
|
|
10
10
|
import Trigger from '.';
|
|
11
11
|
import type { WorkflowModel } from '../types';
|
|
12
|
+
import type { EventOptions } from '../Plugin';
|
|
12
13
|
export interface CollectionChangeTriggerConfig {
|
|
13
14
|
collection: string;
|
|
14
15
|
mode: number;
|
|
@@ -16,7 +17,11 @@ export interface CollectionChangeTriggerConfig {
|
|
|
16
17
|
}
|
|
17
18
|
export default class CollectionTrigger extends Trigger {
|
|
18
19
|
events: Map<any, any>;
|
|
20
|
+
private static handler;
|
|
21
|
+
prepare(workflow: WorkflowModel, data: Model | Record<string, any> | string | number, options: any): Promise<{
|
|
22
|
+
data: any;
|
|
23
|
+
}>;
|
|
19
24
|
on(workflow: WorkflowModel): void;
|
|
20
25
|
off(workflow: WorkflowModel): void;
|
|
21
|
-
|
|
26
|
+
execute(workflow: WorkflowModel, values: any, options: EventOptions): Promise<void | import("..").Processor>;
|
|
22
27
|
}
|
|
@@ -39,11 +39,12 @@ __export(CollectionTrigger_exports, {
|
|
|
39
39
|
default: () => CollectionTrigger
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(CollectionTrigger_exports);
|
|
42
|
-
var import__ = __toESM(require("."));
|
|
43
|
-
var import_utils = require("../utils");
|
|
44
|
-
var import_data_source_manager = require("@nocobase/data-source-manager");
|
|
45
|
-
var import_utils2 = require("@nocobase/utils");
|
|
46
42
|
var import_lodash = require("lodash");
|
|
43
|
+
var import_utils = require("@nocobase/utils");
|
|
44
|
+
var import_database = require("@nocobase/database");
|
|
45
|
+
var import_data_source_manager = require("@nocobase/data-source-manager");
|
|
46
|
+
var import__ = __toESM(require("."));
|
|
47
|
+
var import_utils2 = require("../utils");
|
|
47
48
|
const MODE_BITMAP = {
|
|
48
49
|
CREATE: 1,
|
|
49
50
|
UPDATE: 2,
|
|
@@ -63,65 +64,84 @@ function getFieldRawName(collection, name) {
|
|
|
63
64
|
}
|
|
64
65
|
return name;
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const field = collection.getField(name);
|
|
75
|
-
return field && !["linkTo", "hasOne", "hasMany", "belongsToMany"].includes(field.options.type);
|
|
76
|
-
}).every((name) => !data.changedWithAssociations(getFieldRawName(collection, name)))) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const filterByTk = Array.isArray(filterTargetKey) ? (0, import_lodash.pick)(data, filterTargetKey) : { [filterTargetKey]: data[filterTargetKey] };
|
|
80
|
-
if ((0, import_utils2.isValidFilter)(condition) && !(mode & MODE_BITMAP.DESTROY)) {
|
|
81
|
-
const count = await repository.count({
|
|
82
|
-
filterByTk,
|
|
83
|
-
filter: condition,
|
|
84
|
-
context,
|
|
85
|
-
transaction
|
|
86
|
-
});
|
|
87
|
-
if (!count) {
|
|
67
|
+
class CollectionTrigger extends import__.default {
|
|
68
|
+
events = /* @__PURE__ */ new Map();
|
|
69
|
+
// async function, should return promise
|
|
70
|
+
static async handler(workflow, data, options) {
|
|
71
|
+
const [dataSourceName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
|
|
72
|
+
const transaction = this.workflow.useDataSourceTransaction(dataSourceName, options.transaction);
|
|
73
|
+
const ctx = await this.prepare(workflow, data, { ...options, transaction });
|
|
74
|
+
if (!ctx) {
|
|
88
75
|
return;
|
|
89
76
|
}
|
|
77
|
+
const { stack } = options.context ?? {};
|
|
78
|
+
if (workflow.sync) {
|
|
79
|
+
await this.workflow.trigger(workflow, ctx, {
|
|
80
|
+
transaction,
|
|
81
|
+
stack
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
if (transaction) {
|
|
85
|
+
transaction.afterCommit(() => {
|
|
86
|
+
this.workflow.trigger(workflow, ctx, { stack });
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
this.workflow.trigger(workflow, ctx, { stack });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
90
92
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
93
|
+
async prepare(workflow, data, options) {
|
|
94
|
+
const { condition, changed, mode, appends } = workflow.config;
|
|
95
|
+
const [dataSourceName, collectionName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
|
|
96
|
+
const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
|
|
97
|
+
const collection = collectionManager.getCollection(collectionName);
|
|
98
|
+
const { transaction, context } = options;
|
|
99
|
+
const { repository, filterTargetKey } = collection;
|
|
100
|
+
let target = data;
|
|
101
|
+
let filterByTk;
|
|
102
|
+
let loadNeeded = false;
|
|
103
|
+
if (target && typeof target === "object") {
|
|
104
|
+
filterByTk = Array.isArray(filterTargetKey) ? (0, import_lodash.pick)(
|
|
105
|
+
target,
|
|
106
|
+
filterTargetKey.sort((a, b) => a.localeCompare(b))
|
|
107
|
+
) : target[filterTargetKey];
|
|
108
|
+
} else {
|
|
109
|
+
filterByTk = target;
|
|
110
|
+
loadNeeded = true;
|
|
111
|
+
}
|
|
112
|
+
if (target instanceof import_database.Model && changed && changed.length && changed.filter((name) => {
|
|
113
|
+
const field = collection.getField(name);
|
|
114
|
+
return field && !["linkTo", "hasOne", "hasMany", "belongsToMany"].includes(field.options.type);
|
|
115
|
+
}).every((name) => !target.changedWithAssociations(getFieldRawName(collection, name)))) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
if ((0, import_utils.isValidFilter)(condition) && !(mode & MODE_BITMAP.DESTROY)) {
|
|
119
|
+
const count = await repository.count({
|
|
120
|
+
filterByTk,
|
|
121
|
+
filter: condition,
|
|
122
|
+
context,
|
|
123
|
+
transaction
|
|
124
|
+
});
|
|
125
|
+
if (!count) {
|
|
126
|
+
return null;
|
|
111
127
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
128
|
+
}
|
|
129
|
+
if (loadNeeded || (appends == null ? void 0 : appends.length) && !(mode & MODE_BITMAP.DESTROY)) {
|
|
130
|
+
const includeFields = appends.reduce((set, field) => {
|
|
131
|
+
set.add(field.split(".")[0]);
|
|
132
|
+
set.add(field);
|
|
133
|
+
return set;
|
|
134
|
+
}, /* @__PURE__ */ new Set());
|
|
135
|
+
target = await repository.findOne({
|
|
136
|
+
filterByTk,
|
|
137
|
+
appends: Array.from(includeFields),
|
|
138
|
+
transaction
|
|
117
139
|
});
|
|
118
|
-
} else {
|
|
119
|
-
this.workflow.trigger(workflow, { data: json, stack: context == null ? void 0 : context.stack });
|
|
120
140
|
}
|
|
141
|
+
return {
|
|
142
|
+
data: (0, import_utils2.toJSON)(target)
|
|
143
|
+
};
|
|
121
144
|
}
|
|
122
|
-
}
|
|
123
|
-
class CollectionTrigger extends import__.default {
|
|
124
|
-
events = /* @__PURE__ */ new Map();
|
|
125
145
|
on(workflow) {
|
|
126
146
|
var _a, _b;
|
|
127
147
|
const { collection, mode } = workflow.config;
|
|
@@ -138,7 +158,7 @@ class CollectionTrigger extends import__.default {
|
|
|
138
158
|
const name = getHookId(workflow, `${collection}.${type}`);
|
|
139
159
|
if (mode & key) {
|
|
140
160
|
if (!this.events.has(name)) {
|
|
141
|
-
const listener = handler.bind(this, workflow);
|
|
161
|
+
const listener = this.constructor.handler.bind(this, workflow);
|
|
142
162
|
this.events.set(name, listener);
|
|
143
163
|
db.on(event, listener);
|
|
144
164
|
}
|
|
@@ -173,21 +193,32 @@ class CollectionTrigger extends import__.default {
|
|
|
173
193
|
}
|
|
174
194
|
}
|
|
175
195
|
}
|
|
176
|
-
async validateEvent(workflow, context, options) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
196
|
+
// async validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean> {
|
|
197
|
+
// if (context.stack) {
|
|
198
|
+
// const existed = await workflow.countExecutions({
|
|
199
|
+
// where: {
|
|
200
|
+
// id: context.stack,
|
|
201
|
+
// },
|
|
202
|
+
// transaction: options.transaction,
|
|
203
|
+
// });
|
|
204
|
+
// if (existed) {
|
|
205
|
+
// this.workflow
|
|
206
|
+
// .getLogger(workflow.id)
|
|
207
|
+
// .warn(
|
|
208
|
+
// `workflow ${workflow.id} has already been triggered in stack executions (${context.stack}), and newly triggering will be skipped.`,
|
|
209
|
+
// );
|
|
210
|
+
// return false;
|
|
211
|
+
// }
|
|
212
|
+
// }
|
|
213
|
+
// return true;
|
|
214
|
+
// }
|
|
215
|
+
async execute(workflow, values, options) {
|
|
216
|
+
const ctx = await this.prepare(workflow, values == null ? void 0 : values.data, options);
|
|
217
|
+
const [dataSourceName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
|
|
218
|
+
const { transaction } = options;
|
|
219
|
+
return this.workflow.trigger(workflow, ctx, {
|
|
220
|
+
...options,
|
|
221
|
+
transaction: this.workflow.useDataSourceTransaction(dataSourceName, transaction)
|
|
222
|
+
});
|
|
192
223
|
}
|
|
193
224
|
}
|
|
@@ -21,7 +21,7 @@ export interface ScheduleTriggerConfig {
|
|
|
21
21
|
startsOn?: ScheduleOnField;
|
|
22
22
|
endsOn?: string | ScheduleOnField;
|
|
23
23
|
}
|
|
24
|
-
export default class
|
|
24
|
+
export default class DateFieldScheduleTrigger {
|
|
25
25
|
workflow: Plugin;
|
|
26
26
|
events: Map<any, any>;
|
|
27
27
|
private timer;
|
|
@@ -30,10 +30,11 @@ export default class ScheduleTrigger {
|
|
|
30
30
|
constructor(workflow: Plugin);
|
|
31
31
|
reload(): Promise<void>;
|
|
32
32
|
inspect(workflows: WorkflowModel[]): void;
|
|
33
|
-
loadRecordsToSchedule({ config: { collection, limit, startsOn, repeat, endsOn }, allExecuted }: WorkflowModel, currentDate: Date): Promise<import("@nocobase/database").Model<any, any>[]>;
|
|
33
|
+
loadRecordsToSchedule({ id, config: { collection, limit, startsOn, repeat, endsOn }, allExecuted }: WorkflowModel, currentDate: Date): Promise<import("@nocobase/database").Model<any, any>[]>;
|
|
34
34
|
getRecordNextTime(workflow: WorkflowModel, record: any, nextSecond?: boolean): any;
|
|
35
35
|
schedule(workflow: WorkflowModel, record: any, nextTime: any, toggle?: boolean, options?: {}): Promise<void>;
|
|
36
36
|
trigger(workflow: WorkflowModel, record: any, nextTime: any, { transaction }?: Transactionable): Promise<void>;
|
|
37
37
|
on(workflow: WorkflowModel): void;
|
|
38
38
|
off(workflow: WorkflowModel): void;
|
|
39
|
+
execute(workflow: any, values: any, options: any): Promise<void | import("../..").Processor>;
|
|
39
40
|
}
|
|
@@ -36,13 +36,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
36
36
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
37
|
var DateFieldScheduleTrigger_exports = {};
|
|
38
38
|
__export(DateFieldScheduleTrigger_exports, {
|
|
39
|
-
default: () =>
|
|
39
|
+
default: () => DateFieldScheduleTrigger
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(DateFieldScheduleTrigger_exports);
|
|
42
42
|
var import_database = require("@nocobase/database");
|
|
43
43
|
var import_cron_parser = __toESM(require("cron-parser"));
|
|
44
44
|
var import_utils = require("./utils");
|
|
45
45
|
var import_data_source_manager = require("@nocobase/data-source-manager");
|
|
46
|
+
var import_lodash = require("lodash");
|
|
46
47
|
function getOnTimestampWithOffset({ field, offset = 0, unit = 1e3 }, now) {
|
|
47
48
|
if (!field) {
|
|
48
49
|
return null;
|
|
@@ -64,7 +65,7 @@ function getDataOptionTime(record, on, dir = 1) {
|
|
|
64
65
|
if (!field || !record.get(field)) {
|
|
65
66
|
return null;
|
|
66
67
|
}
|
|
67
|
-
const second = new Date(record.get(field)
|
|
68
|
+
const second = new Date(record.get(field));
|
|
68
69
|
second.setMilliseconds(0);
|
|
69
70
|
return second.getTime() + offset * unit * dir;
|
|
70
71
|
}
|
|
@@ -95,7 +96,7 @@ function matchCronNextTime(cron, currentDate, range) {
|
|
|
95
96
|
function getHookId(workflow, type) {
|
|
96
97
|
return `${type}#${workflow.id}`;
|
|
97
98
|
}
|
|
98
|
-
class
|
|
99
|
+
class DateFieldScheduleTrigger {
|
|
99
100
|
constructor(workflow) {
|
|
100
101
|
this.workflow = workflow;
|
|
101
102
|
workflow.app.on("afterStart", async () => {
|
|
@@ -131,6 +132,7 @@ class ScheduleTrigger {
|
|
|
131
132
|
const now = /* @__PURE__ */ new Date();
|
|
132
133
|
workflows.forEach(async (workflow) => {
|
|
133
134
|
const records = await this.loadRecordsToSchedule(workflow, now);
|
|
135
|
+
this.workflow.getLogger(workflow.id).info(`[Schedule on date field] ${records.length} records to schedule`);
|
|
134
136
|
records.forEach((record) => {
|
|
135
137
|
const nextTime = this.getRecordNextTime(workflow, record);
|
|
136
138
|
this.schedule(workflow, record, nextTime, Boolean(nextTime));
|
|
@@ -144,17 +146,20 @@ class ScheduleTrigger {
|
|
|
144
146
|
// b. repeat in range (number or cron):
|
|
145
147
|
// i. endsOn after now -> yes
|
|
146
148
|
// ii. endsOn before now -> no
|
|
147
|
-
async loadRecordsToSchedule({ config: { collection, limit, startsOn, repeat, endsOn }, allExecuted }, currentDate) {
|
|
149
|
+
async loadRecordsToSchedule({ id, config: { collection, limit, startsOn, repeat, endsOn }, allExecuted }, currentDate) {
|
|
148
150
|
const { dataSourceManager } = this.workflow.app;
|
|
149
151
|
if (limit && allExecuted >= limit) {
|
|
152
|
+
this.workflow.getLogger(id).warn(`[Schedule on date field] limit reached (all executed ${allExecuted})`);
|
|
150
153
|
return [];
|
|
151
154
|
}
|
|
152
155
|
if (!startsOn) {
|
|
156
|
+
this.workflow.getLogger(id).warn(`[Schedule on date field] "startsOn" is not configured`);
|
|
153
157
|
return [];
|
|
154
158
|
}
|
|
155
159
|
const timestamp = currentDate.getTime();
|
|
156
160
|
const startTimestamp = getOnTimestampWithOffset(startsOn, currentDate);
|
|
157
161
|
if (!startTimestamp) {
|
|
162
|
+
this.workflow.getLogger(id).warn(`[Schedule on date field] "startsOn.field" is not configured`);
|
|
158
163
|
return [];
|
|
159
164
|
}
|
|
160
165
|
const [dataSourceName, collectionName] = (0, import_data_source_manager.parseCollectionName)(collection);
|
|
@@ -193,21 +198,21 @@ class ScheduleTrigger {
|
|
|
193
198
|
}
|
|
194
199
|
}
|
|
195
200
|
if (endsOn) {
|
|
196
|
-
const now = /* @__PURE__ */ new Date();
|
|
197
|
-
const endTimestamp = getOnTimestampWithOffset(endsOn, now);
|
|
198
|
-
if (!endTimestamp) {
|
|
199
|
-
return [];
|
|
200
|
-
}
|
|
201
201
|
if (typeof endsOn === "string") {
|
|
202
|
-
if (
|
|
202
|
+
if ((0, import_utils.parseDateWithoutMs)(endsOn) <= timestamp) {
|
|
203
203
|
return [];
|
|
204
204
|
}
|
|
205
205
|
} else {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
const endTimestamp = getOnTimestampWithOffset(endsOn, currentDate);
|
|
207
|
+
if (endTimestamp) {
|
|
208
|
+
conditions.push({
|
|
209
|
+
[endsOn.field]: {
|
|
210
|
+
[import_database.Op.gte]: new Date(endTimestamp)
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
} else {
|
|
214
|
+
this.workflow.getLogger(id).warn(`[Schedule on date field] "endsOn.field" is not configured`);
|
|
215
|
+
}
|
|
211
216
|
}
|
|
212
217
|
}
|
|
213
218
|
} else {
|
|
@@ -217,6 +222,7 @@ class ScheduleTrigger {
|
|
|
217
222
|
}
|
|
218
223
|
});
|
|
219
224
|
}
|
|
225
|
+
this.workflow.getLogger(id).debug(`[Schedule on date field] conditions: `, { conditions });
|
|
220
226
|
return model.findAll({
|
|
221
227
|
where: {
|
|
222
228
|
[import_database.Op.and]: conditions
|
|
@@ -340,7 +346,9 @@ class ScheduleTrigger {
|
|
|
340
346
|
return this.schedule(workflow, data, nextTime, Boolean(nextTime), { transaction });
|
|
341
347
|
};
|
|
342
348
|
this.events.set(name, listener);
|
|
343
|
-
this.workflow.app.dataSourceManager.dataSources.get(dataSourceName)
|
|
349
|
+
const dataSource = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
|
|
350
|
+
const { db } = dataSource.collectionManager;
|
|
351
|
+
db.on(event, listener);
|
|
344
352
|
}
|
|
345
353
|
off(workflow) {
|
|
346
354
|
for (const [key, timer] of this.cache.entries()) {
|
|
@@ -355,9 +363,35 @@ class ScheduleTrigger {
|
|
|
355
363
|
const name = getHookId(workflow, event);
|
|
356
364
|
const listener = this.events.get(name);
|
|
357
365
|
if (listener) {
|
|
358
|
-
const
|
|
366
|
+
const dataSource = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
|
|
367
|
+
const { db } = dataSource.collectionManager;
|
|
359
368
|
db.off(event, listener);
|
|
360
369
|
this.events.delete(name);
|
|
361
370
|
}
|
|
362
371
|
}
|
|
372
|
+
async execute(workflow, values, options) {
|
|
373
|
+
var _a;
|
|
374
|
+
const [dataSourceName, collectionName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
|
|
375
|
+
const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
|
|
376
|
+
const { filterTargetKey, repository } = collectionManager.getCollection(collectionName);
|
|
377
|
+
let { data } = values;
|
|
378
|
+
let filterByTk;
|
|
379
|
+
let loadNeeded = false;
|
|
380
|
+
if (data && typeof data === "object") {
|
|
381
|
+
filterByTk = Array.isArray(filterTargetKey) ? (0, import_lodash.pick)(
|
|
382
|
+
data,
|
|
383
|
+
filterTargetKey.sort((a, b) => a.localeCompare(b))
|
|
384
|
+
) : data[filterTargetKey];
|
|
385
|
+
} else {
|
|
386
|
+
filterByTk = data;
|
|
387
|
+
loadNeeded = true;
|
|
388
|
+
}
|
|
389
|
+
if (loadNeeded || ((_a = workflow.config.appends) == null ? void 0 : _a.length)) {
|
|
390
|
+
data = await repository.findOne({
|
|
391
|
+
filterByTk,
|
|
392
|
+
appends: workflow.config.appends
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
return this.workflow.trigger(workflow, { ...values, data, date: (values == null ? void 0 : values.date) ?? /* @__PURE__ */ new Date() }, options);
|
|
396
|
+
}
|
|
363
397
|
}
|
|
@@ -18,4 +18,5 @@ export default class StaticScheduleTrigger {
|
|
|
18
18
|
trigger(workflow: WorkflowModel, time: number): Promise<void>;
|
|
19
19
|
on(workflow: any): void;
|
|
20
20
|
off(workflow: any): void;
|
|
21
|
+
execute(workflow: any, values: any, options: any): void | Promise<import("../..").Processor>;
|
|
21
22
|
}
|
|
@@ -149,4 +149,7 @@ class StaticScheduleTrigger {
|
|
|
149
149
|
off(workflow) {
|
|
150
150
|
this.schedule(workflow, null, false);
|
|
151
151
|
}
|
|
152
|
+
execute(workflow, values, options) {
|
|
153
|
+
return this.workflow.trigger(workflow, { ...values, date: (values == null ? void 0 : values.date) ?? /* @__PURE__ */ new Date() }, options);
|
|
154
|
+
}
|
|
152
155
|
}
|
|
@@ -68,6 +68,13 @@ class ScheduleTrigger extends import__.default {
|
|
|
68
68
|
trigger.off(workflow);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
async execute(workflow, values, options) {
|
|
72
|
+
const mode = workflow.config.mode;
|
|
73
|
+
const trigger = this.getTrigger(mode);
|
|
74
|
+
if (trigger) {
|
|
75
|
+
return trigger.execute(workflow, values, options);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
71
78
|
// async validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean> {
|
|
72
79
|
// if (!context.date) {
|
|
73
80
|
// return false;
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
import { Transactionable } from '@nocobase/database';
|
|
10
10
|
import type Plugin from '../Plugin';
|
|
11
11
|
import type { WorkflowModel } from '../types';
|
|
12
|
+
import Processor from '../Processor';
|
|
12
13
|
export declare abstract class Trigger {
|
|
13
14
|
readonly workflow: Plugin;
|
|
14
15
|
constructor(workflow: Plugin);
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
on(workflow: WorkflowModel): void;
|
|
17
|
+
off(workflow: WorkflowModel): void;
|
|
17
18
|
validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): boolean | Promise<boolean>;
|
|
18
19
|
duplicateConfig?(workflow: WorkflowModel, options: Transactionable): object | Promise<object>;
|
|
19
20
|
sync?: boolean;
|
|
21
|
+
execute?(workflow: WorkflowModel, values: any, options: Transactionable): void | Processor | Promise<void | Processor>;
|
|
20
22
|
}
|
|
21
23
|
export default Trigger;
|
package/package.json
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
"displayName.zh-CN": "工作流",
|
|
5
5
|
"description": "A powerful BPM tool that provides foundational support for business automation, with the capability to extend unlimited triggers and nodes.",
|
|
6
6
|
"description.zh-CN": "一个强大的 BPM 工具,为业务自动化提供基础支持,并且可任意扩展更多的触发器和节点。",
|
|
7
|
-
"version": "1.5.0-beta.
|
|
7
|
+
"version": "1.5.0-beta.30",
|
|
8
8
|
"license": "AGPL-3.0",
|
|
9
9
|
"main": "./dist/server/index.js",
|
|
10
10
|
"homepage": "https://docs.nocobase.com/handbook/workflow",
|
|
11
11
|
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/workflow",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@nocobase/plugin-workflow-test": "1.5.0-beta.
|
|
13
|
+
"@nocobase/plugin-workflow-test": "1.5.0-beta.30"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@ant-design/icons": "5.x",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@nocobase/test": "1.x",
|
|
45
45
|
"@nocobase/utils": "1.x"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "40c48b2ecf3e2315a76dcb729dbda825b6a1331c",
|
|
48
48
|
"keywords": [
|
|
49
49
|
"Workflow"
|
|
50
50
|
]
|