@nocobase/plugin-workflow 1.6.0-alpha.3 → 1.6.0-alpha.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/4d75ef32f02d7285.js +10 -0
- package/dist/client/56ce448358002e64.js +10 -0
- package/dist/client/58bb427e05b600de.js +10 -0
- package/dist/client/FlowContext.d.ts +2 -0
- package/dist/client/WorkflowTasks.d.ts +17 -0
- package/dist/client/index.d.ts +20 -7
- package/dist/client/index.js +1 -1
- package/dist/client/nodes/calculation.d.ts +3 -2
- package/dist/client/nodes/condition.d.ts +1 -0
- package/dist/client/nodes/create.d.ts +3 -2
- package/dist/client/nodes/destroy.d.ts +2 -0
- package/dist/client/nodes/end.d.ts +2 -0
- package/dist/client/nodes/index.d.ts +1 -0
- package/dist/client/nodes/output.d.ts +31 -0
- package/dist/client/nodes/query.d.ts +5 -4
- package/dist/client/nodes/update.d.ts +3 -2
- package/dist/client/settings/{customizeSubmitToWorkflowActionSettings.d.ts → BindWorkflowConfig.d.ts} +2 -2
- package/dist/client/triggers/schedule/ScheduleModes.d.ts +5 -2
- package/dist/client/triggers/schedule/index.d.ts +2 -0
- package/dist/client/variable.d.ts +17 -6
- package/dist/externalVersion.js +14 -14
- package/dist/locale/en-US.json +3 -1
- package/dist/locale/zh-CN.json +11 -7
- 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 +9 -6
- package/dist/server/Plugin.js +116 -45
- package/dist/server/Processor.d.ts +1 -0
- package/dist/server/Processor.js +6 -5
- package/dist/server/actions/workflows.d.ts +0 -1
- package/dist/server/actions/workflows.js +5 -7
- package/dist/server/collections/executions.js +9 -0
- package/dist/server/collections/flow_nodes.js +1 -0
- package/dist/server/collections/jobs.js +1 -0
- package/dist/server/collections/workflows.js +1 -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/triggers/CollectionTrigger.d.ts +6 -6
- package/dist/server/triggers/CollectionTrigger.js +54 -31
- package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.d.ts +5 -1
- package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.js +53 -14
- 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 +2 -2
- package/dist/server/triggers/ScheduleTrigger/index.js +21 -3
- package/dist/server/triggers/index.d.ts +4 -1
- package/package.json +3 -3
- package/dist/client/5ed8ff0f70ed5911.js +0 -10
- package/dist/client/92877729dbcede8f.js +0 -10
- package/dist/client/e7b9d67c6a964bec.js +0 -10
package/dist/server/Plugin.js
CHANGED
|
@@ -41,6 +41,7 @@ __export(Plugin_exports, {
|
|
|
41
41
|
module.exports = __toCommonJS(Plugin_exports);
|
|
42
42
|
var import_path = __toESM(require("path"));
|
|
43
43
|
var import_crypto = require("crypto");
|
|
44
|
+
var import_sequelize = require("sequelize");
|
|
44
45
|
var import_lru_cache = __toESM(require("lru-cache"));
|
|
45
46
|
var import_database = require("@nocobase/database");
|
|
46
47
|
var import_server = require("@nocobase/server");
|
|
@@ -126,7 +127,7 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
126
127
|
/**
|
|
127
128
|
* @experimental
|
|
128
129
|
*/
|
|
129
|
-
getLogger(workflowId) {
|
|
130
|
+
getLogger(workflowId = "dispatcher") {
|
|
130
131
|
const now = /* @__PURE__ */ new Date();
|
|
131
132
|
const date = `${now.getFullYear()}-${`0${now.getMonth() + 1}`.slice(-2)}-${`0${now.getDate()}`.slice(-2)}`;
|
|
132
133
|
const key = `${date}-${workflowId}}`;
|
|
@@ -226,7 +227,8 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
226
227
|
"executions:destroy",
|
|
227
228
|
"flow_nodes:update",
|
|
228
229
|
"flow_nodes:destroy",
|
|
229
|
-
"flow_nodes:test"
|
|
230
|
+
"flow_nodes:test",
|
|
231
|
+
"jobs:get"
|
|
230
232
|
]
|
|
231
233
|
});
|
|
232
234
|
this.app.acl.registerSnippet({
|
|
@@ -256,7 +258,6 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
256
258
|
(model, { transaction }) => this.toggle(model, false, { transaction })
|
|
257
259
|
);
|
|
258
260
|
this.app.on("afterStart", async () => {
|
|
259
|
-
this.app.setMaintainingMessage("check for not started executions");
|
|
260
261
|
this.ready = true;
|
|
261
262
|
const collection = db.getCollection("workflows");
|
|
262
263
|
const workflows = await collection.repository.find({
|
|
@@ -266,8 +267,14 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
266
267
|
this.toggle(workflow, true, { silent: true });
|
|
267
268
|
});
|
|
268
269
|
this.checker = setInterval(() => {
|
|
270
|
+
this.getLogger("dispatcher").info(`(cycling) check for queueing executions`);
|
|
269
271
|
this.dispatch();
|
|
270
272
|
}, 3e5);
|
|
273
|
+
this.app.on("workflow:dispatch", () => {
|
|
274
|
+
this.app.logger.info("workflow:dispatch");
|
|
275
|
+
this.dispatch();
|
|
276
|
+
});
|
|
277
|
+
this.getLogger("dispatcher").info("(starting) check for queueing executions");
|
|
271
278
|
this.dispatch();
|
|
272
279
|
});
|
|
273
280
|
this.app.on("beforeStop", async () => {
|
|
@@ -323,10 +330,19 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
323
330
|
logger.debug(`ignored event data:`, context);
|
|
324
331
|
return;
|
|
325
332
|
}
|
|
326
|
-
if (!options.manually && !workflow.enabled) {
|
|
333
|
+
if (!options.force && !options.manually && !workflow.enabled) {
|
|
327
334
|
logger.warn(`workflow ${workflow.id} is not enabled, event will be ignored`);
|
|
328
335
|
return;
|
|
329
336
|
}
|
|
337
|
+
const duplicated = this.events.find(([w, c, { eventKey }]) => {
|
|
338
|
+
if (eventKey && options.eventKey) {
|
|
339
|
+
return eventKey === options.eventKey;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
if (duplicated) {
|
|
343
|
+
logger.warn(`event of workflow ${workflow.id} is duplicated (${options.eventKey}), event will be ignored`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
330
346
|
if (context == null) {
|
|
331
347
|
logger.warn(`workflow ${workflow.id} event data context is null, event will be ignored`);
|
|
332
348
|
return;
|
|
@@ -340,9 +356,10 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
340
356
|
logger.info(`new event triggered, now events: ${this.events.length}`);
|
|
341
357
|
logger.debug(`event data:`, { context });
|
|
342
358
|
if (this.events.length > 1) {
|
|
359
|
+
logger.info(`new event is pending to be prepared after previous preparation is finished`);
|
|
343
360
|
return;
|
|
344
361
|
}
|
|
345
|
-
|
|
362
|
+
setImmediate(this.prepare);
|
|
346
363
|
}
|
|
347
364
|
async triggerSync(workflow, context, { deferred, ...options } = {}) {
|
|
348
365
|
let execution;
|
|
@@ -367,33 +384,63 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
367
384
|
`execution (${job.execution.id}) resuming from job (${job.id}) added to pending list`
|
|
368
385
|
);
|
|
369
386
|
this.pending.push([job.execution, job]);
|
|
387
|
+
if (this.executing) {
|
|
388
|
+
await this.executing;
|
|
389
|
+
}
|
|
370
390
|
this.dispatch();
|
|
371
391
|
}
|
|
372
392
|
/**
|
|
373
393
|
* Start a deferred execution
|
|
374
394
|
* @experimental
|
|
375
395
|
*/
|
|
376
|
-
start(execution) {
|
|
396
|
+
async start(execution) {
|
|
377
397
|
if (execution.status !== import_constants.EXECUTION_STATUS.STARTED) {
|
|
378
398
|
return;
|
|
379
399
|
}
|
|
400
|
+
this.getLogger(execution.workflowId).info(`starting deferred execution (${execution.id})`);
|
|
380
401
|
this.pending.push([execution]);
|
|
402
|
+
if (this.executing) {
|
|
403
|
+
await this.executing;
|
|
404
|
+
}
|
|
381
405
|
this.dispatch();
|
|
382
406
|
}
|
|
383
|
-
|
|
384
|
-
|
|
407
|
+
async validateEvent(workflow, context, options) {
|
|
408
|
+
const trigger = this.triggers.get(workflow.type);
|
|
409
|
+
const triggerValid = await trigger.validateEvent(workflow, context, options);
|
|
410
|
+
if (!triggerValid) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
const { stack } = options;
|
|
414
|
+
let valid = true;
|
|
415
|
+
if ((stack == null ? void 0 : stack.length) > 0) {
|
|
416
|
+
const existed = await workflow.countExecutions({
|
|
417
|
+
where: {
|
|
418
|
+
id: stack
|
|
419
|
+
},
|
|
420
|
+
transaction: options.transaction
|
|
421
|
+
});
|
|
422
|
+
const limitCount = workflow.options.stackLimit || 1;
|
|
423
|
+
if (existed >= limitCount) {
|
|
424
|
+
this.getLogger(workflow.id).warn(
|
|
425
|
+
`workflow ${workflow.id} has already been triggered in stacks executions (${stack}), and max call coont is ${limitCount}, newly triggering will be skipped.`
|
|
426
|
+
);
|
|
427
|
+
valid = false;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return valid;
|
|
385
431
|
}
|
|
386
432
|
async createExecution(workflow, context, options) {
|
|
433
|
+
var _a;
|
|
387
434
|
const { deferred } = options;
|
|
388
435
|
const transaction = await this.useDataSourceTransaction("main", options.transaction, true);
|
|
389
436
|
const sameTransaction = options.transaction === transaction;
|
|
390
|
-
const
|
|
391
|
-
const valid = await trigger.validateEvent(workflow, context, { ...options, transaction });
|
|
437
|
+
const valid = await this.validateEvent(workflow, context, { ...options, transaction });
|
|
392
438
|
if (!valid) {
|
|
393
439
|
if (!sameTransaction) {
|
|
394
440
|
await transaction.commit();
|
|
395
441
|
}
|
|
396
|
-
|
|
442
|
+
(_a = options.onTriggerFail) == null ? void 0 : _a.call(options, workflow, context, options);
|
|
443
|
+
return Promise.reject(new Error("event is not valid"));
|
|
397
444
|
}
|
|
398
445
|
let execution;
|
|
399
446
|
try {
|
|
@@ -402,6 +449,7 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
402
449
|
context,
|
|
403
450
|
key: workflow.key,
|
|
404
451
|
eventKey: options.eventKey ?? (0, import_crypto.randomUUID)(),
|
|
452
|
+
stack: options.stack,
|
|
405
453
|
status: deferred ? import_constants.EXECUTION_STATUS.STARTED : import_constants.EXECUTION_STATUS.QUEUEING
|
|
406
454
|
},
|
|
407
455
|
{ transaction }
|
|
@@ -441,7 +489,7 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
441
489
|
const event = this.events.shift();
|
|
442
490
|
this.eventsCount = this.events.length;
|
|
443
491
|
if (!event) {
|
|
444
|
-
this.getLogger("dispatcher").
|
|
492
|
+
this.getLogger("dispatcher").info(`events queue is empty, no need to prepare`);
|
|
445
493
|
return;
|
|
446
494
|
}
|
|
447
495
|
const logger = this.getLogger(event[0].id);
|
|
@@ -451,12 +499,16 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
451
499
|
if ((execution == null ? void 0 : execution.status) === import_constants.EXECUTION_STATUS.QUEUEING && !this.executing && !this.pending.length) {
|
|
452
500
|
this.pending.push([execution]);
|
|
453
501
|
}
|
|
454
|
-
} catch (
|
|
455
|
-
logger.error(`failed to create execution
|
|
502
|
+
} catch (error) {
|
|
503
|
+
logger.error(`failed to create execution:`, { error });
|
|
456
504
|
}
|
|
457
505
|
if (this.events.length) {
|
|
458
506
|
await this.prepare();
|
|
459
507
|
} else {
|
|
508
|
+
this.getLogger("dispatcher").info("no more events need to be prepared, dispatching...");
|
|
509
|
+
if (this.executing) {
|
|
510
|
+
await this.executing;
|
|
511
|
+
}
|
|
460
512
|
this.dispatch();
|
|
461
513
|
}
|
|
462
514
|
};
|
|
@@ -474,59 +526,78 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
474
526
|
}
|
|
475
527
|
this.executing = (async () => {
|
|
476
528
|
let next = null;
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
"workflow.enabled": true,
|
|
486
|
-
"workflow.id": {
|
|
487
|
-
[import_database.Op.not]: null
|
|
488
|
-
}
|
|
529
|
+
if (this.pending.length) {
|
|
530
|
+
next = this.pending.shift();
|
|
531
|
+
this.getLogger(next[0].workflowId).info(`pending execution (${next[0].id}) ready to process`);
|
|
532
|
+
} else {
|
|
533
|
+
try {
|
|
534
|
+
await this.db.sequelize.transaction(
|
|
535
|
+
{
|
|
536
|
+
isolationLevel: this.db.options.dialect === "sqlite" ? [][0] : import_sequelize.Transaction.ISOLATION_LEVELS.REPEATABLE_READ
|
|
489
537
|
},
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
538
|
+
async (transaction) => {
|
|
539
|
+
const execution = await this.db.getRepository("executions").findOne({
|
|
540
|
+
filter: {
|
|
541
|
+
status: import_constants.EXECUTION_STATUS.QUEUEING,
|
|
542
|
+
"workflow.enabled": true
|
|
543
|
+
},
|
|
544
|
+
sort: "id",
|
|
545
|
+
transaction
|
|
546
|
+
});
|
|
547
|
+
if (execution) {
|
|
548
|
+
this.getLogger(execution.workflowId).info(`execution (${execution.id}) fetched from db`);
|
|
549
|
+
await execution.update(
|
|
550
|
+
{
|
|
551
|
+
status: import_constants.EXECUTION_STATUS.STARTED
|
|
552
|
+
},
|
|
553
|
+
{ transaction }
|
|
554
|
+
);
|
|
555
|
+
execution.workflow = this.enabledCache.get(execution.workflowId);
|
|
556
|
+
next = [execution];
|
|
557
|
+
} else {
|
|
558
|
+
this.getLogger("dispatcher").info(`no execution in db queued to process`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
);
|
|
562
|
+
} catch (error) {
|
|
563
|
+
this.getLogger("dispatcher").error(`fetching execution from db failed: ${error.message}`, { error });
|
|
505
564
|
}
|
|
506
565
|
}
|
|
566
|
+
if (next) {
|
|
567
|
+
await this.process(...next);
|
|
568
|
+
}
|
|
569
|
+
this.executing = null;
|
|
570
|
+
if (next || this.pending.length) {
|
|
571
|
+
this.getLogger("dispatcher").info(`last process finished, will do another dispatch`);
|
|
572
|
+
this.dispatch();
|
|
573
|
+
}
|
|
507
574
|
})();
|
|
508
575
|
}
|
|
576
|
+
createProcessor(execution, options = {}) {
|
|
577
|
+
return new import_Processor.default(execution, { ...options, plugin: this });
|
|
578
|
+
}
|
|
509
579
|
async process(execution, job, options = {}) {
|
|
510
580
|
var _a, _b;
|
|
581
|
+
const logger = this.getLogger(execution.workflowId);
|
|
511
582
|
if (execution.status === import_constants.EXECUTION_STATUS.QUEUEING) {
|
|
512
583
|
const transaction = await this.useDataSourceTransaction("main", options.transaction);
|
|
513
584
|
await execution.update({ status: import_constants.EXECUTION_STATUS.STARTED }, { transaction });
|
|
585
|
+
logger.info(`queueing execution (${execution.id}) from pending list updated to started`);
|
|
514
586
|
}
|
|
515
|
-
const logger = this.getLogger(execution.workflowId);
|
|
516
587
|
const processor = this.createProcessor(execution, options);
|
|
517
588
|
logger.info(`execution (${execution.id}) ${job ? "resuming" : "starting"}...`);
|
|
518
589
|
try {
|
|
519
590
|
await (job ? processor.resume(job) : processor.start());
|
|
520
591
|
logger.info(`execution (${execution.id}) finished with status: ${execution.status}`, { execution });
|
|
521
592
|
if (execution.status && ((_b = (_a = execution.workflow.options) == null ? void 0 : _a.deleteExecutionOnStatus) == null ? void 0 : _b.includes(execution.status))) {
|
|
522
|
-
await execution.destroy();
|
|
593
|
+
await execution.destroy({ transaction: processor.mainTransaction });
|
|
523
594
|
}
|
|
524
595
|
} catch (err) {
|
|
525
596
|
logger.error(`execution (${execution.id}) error: ${err.message}`, err);
|
|
526
597
|
}
|
|
527
598
|
return processor;
|
|
528
599
|
}
|
|
529
|
-
async execute(workflow,
|
|
600
|
+
async execute(workflow, values, options = {}) {
|
|
530
601
|
const trigger = this.triggers.get(workflow.type);
|
|
531
602
|
if (!trigger) {
|
|
532
603
|
throw new Error(`trigger type "${workflow.type}" of workflow ${workflow.id} is not registered`);
|
|
@@ -534,7 +605,7 @@ class PluginWorkflowServer extends import_server.Plugin {
|
|
|
534
605
|
if (!trigger.execute) {
|
|
535
606
|
throw new Error(`"execute" method of trigger ${workflow.type} is not implemented`);
|
|
536
607
|
}
|
|
537
|
-
return trigger.execute(workflow,
|
|
608
|
+
return trigger.execute(workflow, values, options);
|
|
538
609
|
}
|
|
539
610
|
/**
|
|
540
611
|
* @experimental
|
package/dist/server/Processor.js
CHANGED
|
@@ -117,11 +117,11 @@ class Processor {
|
|
|
117
117
|
execution,
|
|
118
118
|
options: { plugin }
|
|
119
119
|
} = this;
|
|
120
|
-
if (!execution.workflow) {
|
|
121
|
-
execution.workflow = plugin.enabledCache.get(execution.workflowId);
|
|
122
|
-
}
|
|
123
120
|
this.mainTransaction = plugin.useDataSourceTransaction("main", this.transaction);
|
|
124
121
|
const transaction = this.mainTransaction;
|
|
122
|
+
if (!execution.workflow) {
|
|
123
|
+
execution.workflow = plugin.enabledCache.get(execution.workflowId) || await execution.getWorkflow({ transaction });
|
|
124
|
+
}
|
|
125
125
|
const nodes = await execution.workflow.getNodes({ transaction });
|
|
126
126
|
this.makeNodes(nodes);
|
|
127
127
|
const jobs = await execution.getJobs({
|
|
@@ -212,7 +212,7 @@ class Processor {
|
|
|
212
212
|
if (parentNode) {
|
|
213
213
|
this.logger.debug(`not on main, recall to parent entry node (${node.id})})`);
|
|
214
214
|
await this.recall(parentNode, job);
|
|
215
|
-
return
|
|
215
|
+
return null;
|
|
216
216
|
}
|
|
217
217
|
return this.exit(job.status);
|
|
218
218
|
}
|
|
@@ -369,7 +369,8 @@ class Processor {
|
|
|
369
369
|
$context: this.execution.context,
|
|
370
370
|
$jobsMapByNodeKey: this.jobsMapByNodeKey,
|
|
371
371
|
$system: systemFns,
|
|
372
|
-
$scopes
|
|
372
|
+
$scopes,
|
|
373
|
+
$env: this.options.plugin.app.environment.getVariables()
|
|
373
374
|
};
|
|
374
375
|
}
|
|
375
376
|
/**
|
|
@@ -15,5 +15,4 @@ export declare function sync(context: Context, next: any): Promise<void>;
|
|
|
15
15
|
* @deprecated
|
|
16
16
|
* Keep for action trigger compatibility
|
|
17
17
|
*/
|
|
18
|
-
export declare function trigger(context: Context, next: any): Promise<any>;
|
|
19
18
|
export declare function execute(context: Context, next: any): Promise<any>;
|
|
@@ -40,7 +40,6 @@ __export(workflows_exports, {
|
|
|
40
40
|
execute: () => execute,
|
|
41
41
|
revision: () => revision,
|
|
42
42
|
sync: () => sync,
|
|
43
|
-
trigger: () => trigger,
|
|
44
43
|
update: () => update
|
|
45
44
|
});
|
|
46
45
|
module.exports = __toCommonJS(workflows_exports);
|
|
@@ -116,12 +115,12 @@ async function sync(context, next) {
|
|
|
116
115
|
context.status = 204;
|
|
117
116
|
await next();
|
|
118
117
|
}
|
|
119
|
-
async function trigger(context, next) {
|
|
120
|
-
return next();
|
|
121
|
-
}
|
|
122
118
|
async function execute(context, next) {
|
|
123
119
|
const plugin = context.app.pm.get(import_Plugin.default);
|
|
124
|
-
const { filterByTk, autoRevision } = context.action.params;
|
|
120
|
+
const { filterByTk, values, autoRevision } = context.action.params;
|
|
121
|
+
if (!values) {
|
|
122
|
+
return context.throw(400, "values is required");
|
|
123
|
+
}
|
|
125
124
|
if (!filterByTk) {
|
|
126
125
|
return context.throw(400, "filterByTk is required");
|
|
127
126
|
}
|
|
@@ -137,7 +136,7 @@ async function execute(context, next) {
|
|
|
137
136
|
const { executed } = workflow;
|
|
138
137
|
let processor;
|
|
139
138
|
try {
|
|
140
|
-
processor = await plugin.execute(workflow,
|
|
139
|
+
processor = await plugin.execute(workflow, values, { manually: true });
|
|
141
140
|
if (!processor) {
|
|
142
141
|
return context.throw(400, "workflow not triggered");
|
|
143
142
|
}
|
|
@@ -174,6 +173,5 @@ async function execute(context, next) {
|
|
|
174
173
|
execute,
|
|
175
174
|
revision,
|
|
176
175
|
sync,
|
|
177
|
-
trigger,
|
|
178
176
|
update
|
|
179
177
|
});
|
|
@@ -33,6 +33,7 @@ var executions_default = {
|
|
|
33
33
|
dumpRules: {
|
|
34
34
|
group: "log"
|
|
35
35
|
},
|
|
36
|
+
migrationRules: ["schema-only"],
|
|
36
37
|
name: "executions",
|
|
37
38
|
shared: true,
|
|
38
39
|
fields: [
|
|
@@ -61,6 +62,14 @@ var executions_default = {
|
|
|
61
62
|
{
|
|
62
63
|
type: "integer",
|
|
63
64
|
name: "status"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: "json",
|
|
68
|
+
name: "stack"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: "json",
|
|
72
|
+
name: "output"
|
|
64
73
|
}
|
|
65
74
|
]
|
|
66
75
|
};
|
|
@@ -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
|
});
|
|
@@ -6,11 +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 { Model
|
|
9
|
+
import { Model } from '@nocobase/database';
|
|
10
10
|
import Trigger from '.';
|
|
11
11
|
import type { WorkflowModel } from '../types';
|
|
12
12
|
import type { EventOptions } from '../Plugin';
|
|
13
|
-
import { Context } from '@nocobase/actions';
|
|
14
13
|
export interface CollectionChangeTriggerConfig {
|
|
15
14
|
collection: string;
|
|
16
15
|
mode: number;
|
|
@@ -19,12 +18,13 @@ export interface CollectionChangeTriggerConfig {
|
|
|
19
18
|
export default class CollectionTrigger extends Trigger {
|
|
20
19
|
events: Map<any, any>;
|
|
21
20
|
private static handler;
|
|
22
|
-
prepare(workflow: WorkflowModel, data: Model | Record<string, any
|
|
21
|
+
prepare(workflow: WorkflowModel, data: Model | Record<string, any> | string | number, options: any): Promise<{
|
|
23
22
|
data: any;
|
|
24
|
-
stack: any;
|
|
25
23
|
}>;
|
|
26
24
|
on(workflow: WorkflowModel): void;
|
|
27
25
|
off(workflow: WorkflowModel): void;
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
execute(workflow: WorkflowModel, values: any, options: EventOptions): Promise<void | import("..").Processor>;
|
|
27
|
+
validateContext(values: any): {
|
|
28
|
+
data: string;
|
|
29
|
+
};
|
|
30
30
|
}
|
|
@@ -68,6 +68,10 @@ class CollectionTrigger extends import__.default {
|
|
|
68
68
|
events = /* @__PURE__ */ new Map();
|
|
69
69
|
// async function, should return promise
|
|
70
70
|
static async handler(workflow, data, options) {
|
|
71
|
+
const { skipWorkflow = false, stack } = options.context ?? {};
|
|
72
|
+
if (skipWorkflow) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
71
75
|
const [dataSourceName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
|
|
72
76
|
const transaction = this.workflow.useDataSourceTransaction(dataSourceName, options.transaction);
|
|
73
77
|
const ctx = await this.prepare(workflow, data, { ...options, transaction });
|
|
@@ -76,15 +80,16 @@ class CollectionTrigger extends import__.default {
|
|
|
76
80
|
}
|
|
77
81
|
if (workflow.sync) {
|
|
78
82
|
await this.workflow.trigger(workflow, ctx, {
|
|
79
|
-
transaction
|
|
83
|
+
transaction,
|
|
84
|
+
stack
|
|
80
85
|
});
|
|
81
86
|
} else {
|
|
82
87
|
if (transaction) {
|
|
83
88
|
transaction.afterCommit(() => {
|
|
84
|
-
this.workflow.trigger(workflow, ctx);
|
|
89
|
+
this.workflow.trigger(workflow, ctx, { stack });
|
|
85
90
|
});
|
|
86
91
|
} else {
|
|
87
|
-
this.workflow.trigger(workflow, ctx);
|
|
92
|
+
this.workflow.trigger(workflow, ctx, { stack });
|
|
88
93
|
}
|
|
89
94
|
}
|
|
90
95
|
}
|
|
@@ -95,13 +100,24 @@ class CollectionTrigger extends import__.default {
|
|
|
95
100
|
const collection = collectionManager.getCollection(collectionName);
|
|
96
101
|
const { transaction, context } = options;
|
|
97
102
|
const { repository, filterTargetKey } = collection;
|
|
98
|
-
|
|
103
|
+
let target = data;
|
|
104
|
+
let filterByTk;
|
|
105
|
+
let loadNeeded = false;
|
|
106
|
+
if (target && typeof target === "object") {
|
|
107
|
+
filterByTk = Array.isArray(filterTargetKey) ? (0, import_lodash.pick)(
|
|
108
|
+
target,
|
|
109
|
+
filterTargetKey.sort((a, b) => a.localeCompare(b))
|
|
110
|
+
) : target[filterTargetKey];
|
|
111
|
+
} else {
|
|
112
|
+
filterByTk = target;
|
|
113
|
+
loadNeeded = true;
|
|
114
|
+
}
|
|
115
|
+
if (target instanceof import_database.Model && changed && changed.length && changed.filter((name) => {
|
|
99
116
|
const field = collection.getField(name);
|
|
100
117
|
return field && !["linkTo", "hasOne", "hasMany", "belongsToMany"].includes(field.options.type);
|
|
101
|
-
}).every((name) => !
|
|
118
|
+
}).every((name) => !target.changedWithAssociations(getFieldRawName(collection, name)))) {
|
|
102
119
|
return null;
|
|
103
120
|
}
|
|
104
|
-
const filterByTk = Array.isArray(filterTargetKey) ? (0, import_lodash.pick)(data, filterTargetKey) : { [filterTargetKey]: data[filterTargetKey] };
|
|
105
121
|
if ((0, import_utils.isValidFilter)(condition) && !(mode & MODE_BITMAP.DESTROY)) {
|
|
106
122
|
const count = await repository.count({
|
|
107
123
|
filterByTk,
|
|
@@ -113,22 +129,20 @@ class CollectionTrigger extends import__.default {
|
|
|
113
129
|
return null;
|
|
114
130
|
}
|
|
115
131
|
}
|
|
116
|
-
|
|
117
|
-
if ((appends == null ? void 0 : appends.length) && !(mode & MODE_BITMAP.DESTROY)) {
|
|
132
|
+
if (loadNeeded || (appends == null ? void 0 : appends.length) && !(mode & MODE_BITMAP.DESTROY)) {
|
|
118
133
|
const includeFields = appends.reduce((set, field) => {
|
|
119
134
|
set.add(field.split(".")[0]);
|
|
120
135
|
set.add(field);
|
|
121
136
|
return set;
|
|
122
137
|
}, /* @__PURE__ */ new Set());
|
|
123
|
-
|
|
138
|
+
target = await repository.findOne({
|
|
124
139
|
filterByTk,
|
|
125
140
|
appends: Array.from(includeFields),
|
|
126
141
|
transaction
|
|
127
142
|
});
|
|
128
143
|
}
|
|
129
144
|
return {
|
|
130
|
-
data: (0, import_utils2.toJSON)(
|
|
131
|
-
stack: context == null ? void 0 : context.stack
|
|
145
|
+
data: (0, import_utils2.toJSON)(target)
|
|
132
146
|
};
|
|
133
147
|
}
|
|
134
148
|
on(workflow) {
|
|
@@ -182,26 +196,27 @@ class CollectionTrigger extends import__.default {
|
|
|
182
196
|
}
|
|
183
197
|
}
|
|
184
198
|
}
|
|
185
|
-
async validateEvent(workflow, context, options) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
// async validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean> {
|
|
200
|
+
// if (context.stack) {
|
|
201
|
+
// const existed = await workflow.countExecutions({
|
|
202
|
+
// where: {
|
|
203
|
+
// id: context.stack,
|
|
204
|
+
// },
|
|
205
|
+
// transaction: options.transaction,
|
|
206
|
+
// });
|
|
207
|
+
// if (existed) {
|
|
208
|
+
// this.workflow
|
|
209
|
+
// .getLogger(workflow.id)
|
|
210
|
+
// .warn(
|
|
211
|
+
// `workflow ${workflow.id} has already been triggered in stack executions (${context.stack}), and newly triggering will be skipped.`,
|
|
212
|
+
// );
|
|
213
|
+
// return false;
|
|
214
|
+
// }
|
|
215
|
+
// }
|
|
216
|
+
// return true;
|
|
217
|
+
// }
|
|
218
|
+
async execute(workflow, values, options) {
|
|
219
|
+
const ctx = await this.prepare(workflow, values == null ? void 0 : values.data, options);
|
|
205
220
|
const [dataSourceName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
|
|
206
221
|
const { transaction } = options;
|
|
207
222
|
return this.workflow.trigger(workflow, ctx, {
|
|
@@ -209,4 +224,12 @@ class CollectionTrigger extends import__.default {
|
|
|
209
224
|
transaction: this.workflow.useDataSourceTransaction(dataSourceName, transaction)
|
|
210
225
|
});
|
|
211
226
|
}
|
|
227
|
+
validateContext(values) {
|
|
228
|
+
if (!values.data) {
|
|
229
|
+
return {
|
|
230
|
+
data: "Data is required"
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
212
235
|
}
|
|
@@ -30,10 +30,14 @@ export default class DateFieldScheduleTrigger {
|
|
|
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>;
|
|
40
|
+
validateContext(values: any): {
|
|
41
|
+
data: string;
|
|
42
|
+
};
|
|
39
43
|
}
|