@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.
Files changed (44) hide show
  1. package/dist/client/112c72b90000c81b.js +10 -0
  2. package/dist/client/739d458621edf81f.js +10 -0
  3. package/dist/client/98864c2e0ff69ea1.js +10 -0
  4. package/dist/client/FlowContext.d.ts +2 -0
  5. package/dist/client/components/TriggerCollectionRecordSelect.d.ts +10 -0
  6. package/dist/client/components/index.d.ts +1 -0
  7. package/dist/client/constants.d.ts +1 -0
  8. package/dist/client/e7b9d67c6a964bec.js +10 -0
  9. package/dist/client/index.js +1 -566
  10. package/dist/client/triggers/collection.d.ts +14 -0
  11. package/dist/client/triggers/index.d.ts +3 -3
  12. package/dist/client/triggers/schedule/ScheduleModes.d.ts +170 -0
  13. package/dist/client/triggers/schedule/TriggerScheduleConfig.d.ts +10 -0
  14. package/dist/client/triggers/schedule/index.d.ts +13 -0
  15. package/dist/client/variable.d.ts +16 -1
  16. package/dist/externalVersion.js +11 -11
  17. package/dist/locale/zh-CN.json +19 -5
  18. package/dist/node_modules/cron-parser/package.json +1 -1
  19. package/dist/node_modules/lru-cache/package.json +1 -1
  20. package/dist/server/Plugin.d.ts +13 -6
  21. package/dist/server/Plugin.js +165 -83
  22. package/dist/server/Processor.js +3 -3
  23. package/dist/server/actions/workflows.d.ts +5 -0
  24. package/dist/server/actions/workflows.js +59 -61
  25. package/dist/server/collections/executions.js +8 -0
  26. package/dist/server/collections/workflows.js +2 -2
  27. package/dist/server/index.d.ts +1 -1
  28. package/dist/server/index.js +2 -0
  29. package/dist/server/instructions/CreateInstruction.js +1 -1
  30. package/dist/server/instructions/DestroyInstruction.js +1 -1
  31. package/dist/server/instructions/UpdateInstruction.js +1 -1
  32. package/dist/server/repositories/WorkflowRepository.d.ts +12 -0
  33. package/dist/server/repositories/WorkflowRepository.js +112 -0
  34. package/dist/server/triggers/CollectionTrigger.d.ts +7 -2
  35. package/dist/server/triggers/CollectionTrigger.js +104 -73
  36. package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.d.ts +3 -2
  37. package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.js +51 -17
  38. package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.d.ts +1 -0
  39. package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.js +3 -0
  40. package/dist/server/triggers/ScheduleTrigger/index.d.ts +1 -0
  41. package/dist/server/triggers/ScheduleTrigger/index.js +7 -0
  42. package/dist/server/triggers/index.d.ts +4 -2
  43. package/dist/server/triggers/index.js +4 -0
  44. 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.context.stack ?? []).concat(processor.execution.id)))
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.context.stack ?? []).concat(processor.execution.id)))
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.context.stack ?? []).concat(processor.execution.id)))
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 { Transactionable } from '@nocobase/database';
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
- validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean>;
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
- async function handler(workflow, data, options) {
67
- const { condition, changed, mode, appends } = workflow.config;
68
- const [dataSourceName, collectionName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
69
- const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
70
- const collection = collectionManager.getCollection(collectionName);
71
- const { transaction, context } = options;
72
- const { repository, filterTargetKey } = collection;
73
- if (changed && changed.length && changed.filter((name) => {
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
- let result = data;
92
- if ((appends == null ? void 0 : appends.length) && !(mode & MODE_BITMAP.DESTROY)) {
93
- const includeFields = appends.reduce((set, field) => {
94
- set.add(field.split(".")[0]);
95
- set.add(field);
96
- return set;
97
- }, /* @__PURE__ */ new Set());
98
- result = await repository.findOne({
99
- filterByTk,
100
- appends: Array.from(includeFields),
101
- transaction
102
- });
103
- }
104
- const json = (0, import_utils.toJSON)(result);
105
- if (workflow.sync) {
106
- await this.workflow.trigger(
107
- workflow,
108
- { data: json, stack: context == null ? void 0 : context.stack },
109
- {
110
- transaction: this.workflow.useDataSourceTransaction(dataSourceName, transaction)
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
- } else {
114
- if (transaction) {
115
- transaction.afterCommit(() => {
116
- this.workflow.trigger(workflow, { data: json, stack: context == null ? void 0 : context.stack });
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
- if (context.stack) {
178
- const existed = await workflow.countExecutions({
179
- where: {
180
- id: context.stack
181
- },
182
- transaction: options.transaction
183
- });
184
- if (existed) {
185
- this.workflow.getLogger(workflow.id).warn(
186
- `workflow ${workflow.id} has already been triggered in stack executions (${context.stack}), and newly triggering will be skipped.`
187
- );
188
- return false;
189
- }
190
- }
191
- return true;
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 ScheduleTrigger {
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: () => ScheduleTrigger
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).getTime());
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 ScheduleTrigger {
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 (endTimestamp <= timestamp) {
202
+ if ((0, import_utils.parseDateWithoutMs)(endsOn) <= timestamp) {
203
203
  return [];
204
204
  }
205
205
  } else {
206
- conditions.push({
207
- [endsOn.field]: {
208
- [import_database.Op.gte]: new Date(endTimestamp)
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).collectionManager.db.on(event, listener);
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 { db } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName).collectionManager;
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
  }
@@ -15,4 +15,5 @@ export default class ScheduleTrigger extends Trigger {
15
15
  private getTrigger;
16
16
  on(workflow: any): void;
17
17
  off(workflow: any): void;
18
+ execute(workflow: any, values: any, options: any): Promise<any>;
18
19
  }
@@ -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
- abstract on(workflow: WorkflowModel): void;
16
- abstract off(workflow: WorkflowModel): void;
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;
@@ -34,6 +34,10 @@ class Trigger {
34
34
  constructor(workflow) {
35
35
  this.workflow = workflow;
36
36
  }
37
+ on(workflow) {
38
+ }
39
+ off(workflow) {
40
+ }
37
41
  validateEvent(workflow, context, options) {
38
42
  return true;
39
43
  }
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.3",
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.3"
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": "81afcf4affdb652faf4636e6d9351828ce8906be",
47
+ "gitHead": "40c48b2ecf3e2315a76dcb729dbda825b6a1331c",
48
48
  "keywords": [
49
49
  "Workflow"
50
50
  ]