@nocobase/plugin-workflow-action-trigger 0.21.0-alpha.1 → 0.21.0-alpha.11
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/ActionTrigger.d.ts +52 -2
- package/dist/client/index.js +1 -1
- package/dist/externalVersion.js +9 -9
- package/dist/locale/zh-CN.json +11 -3
- package/dist/server/ActionTrigger.d.ts +3 -2
- package/dist/server/ActionTrigger.js +60 -33
- package/package.json +6 -6
- package/src/client/ActionTrigger.tsx +81 -4
- package/src/client/__e2e__/workflowCRUD.test.ts +1 -1
- package/src/client/index.ts +2 -6
- package/src/locale/zh-CN.json +11 -3
- package/src/server/ActionTrigger.ts +65 -43
- package/src/server/__tests__/trigger.test.ts +149 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SchemaInitializerItemType, useCollectionDataSource } from '@nocobase/client';
|
|
2
|
-
import { Trigger, useWorkflowAnyExecuted } from '@nocobase/plugin-workflow/client';
|
|
2
|
+
import { Trigger, useWorkflowAnyExecuted, CheckboxGroupWithTooltip, RadioWithTooltip } from '@nocobase/plugin-workflow/client';
|
|
3
3
|
export default class extends Trigger {
|
|
4
4
|
title: string;
|
|
5
5
|
description: string;
|
|
@@ -8,10 +8,12 @@ export default class extends Trigger {
|
|
|
8
8
|
type: string;
|
|
9
9
|
required: boolean;
|
|
10
10
|
'x-decorator': string;
|
|
11
|
+
'x-decorator-props': {
|
|
12
|
+
tooltip: string;
|
|
13
|
+
};
|
|
11
14
|
'x-component': string;
|
|
12
15
|
'x-disabled': string;
|
|
13
16
|
title: string;
|
|
14
|
-
description: string;
|
|
15
17
|
'x-reactions': {
|
|
16
18
|
target: string;
|
|
17
19
|
effects: string[];
|
|
@@ -22,6 +24,50 @@ export default class extends Trigger {
|
|
|
22
24
|
};
|
|
23
25
|
}[];
|
|
24
26
|
};
|
|
27
|
+
global: {
|
|
28
|
+
type: string;
|
|
29
|
+
title: string;
|
|
30
|
+
'x-decorator': string;
|
|
31
|
+
'x-component': string;
|
|
32
|
+
'x-component-props': {
|
|
33
|
+
direction: string;
|
|
34
|
+
options: {
|
|
35
|
+
label: string;
|
|
36
|
+
value: boolean;
|
|
37
|
+
}[];
|
|
38
|
+
};
|
|
39
|
+
default: boolean;
|
|
40
|
+
'x-reactions': {
|
|
41
|
+
dependencies: string[];
|
|
42
|
+
fulfill: {
|
|
43
|
+
state: {
|
|
44
|
+
visible: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}[];
|
|
48
|
+
};
|
|
49
|
+
actions: {
|
|
50
|
+
type: string;
|
|
51
|
+
title: string;
|
|
52
|
+
'x-decorator': string;
|
|
53
|
+
'x-component': string;
|
|
54
|
+
'x-component-props': {
|
|
55
|
+
direction: string;
|
|
56
|
+
options: {
|
|
57
|
+
label: string;
|
|
58
|
+
value: string;
|
|
59
|
+
}[];
|
|
60
|
+
};
|
|
61
|
+
required: boolean;
|
|
62
|
+
'x-reactions': {
|
|
63
|
+
dependencies: string[];
|
|
64
|
+
fulfill: {
|
|
65
|
+
state: {
|
|
66
|
+
visible: string;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
}[];
|
|
70
|
+
};
|
|
25
71
|
appends: {
|
|
26
72
|
type: string;
|
|
27
73
|
title: string;
|
|
@@ -47,6 +93,10 @@ export default class extends Trigger {
|
|
|
47
93
|
useCollectionDataSource: typeof useCollectionDataSource;
|
|
48
94
|
useWorkflowAnyExecuted: typeof useWorkflowAnyExecuted;
|
|
49
95
|
};
|
|
96
|
+
components: {
|
|
97
|
+
RadioWithTooltip: typeof RadioWithTooltip;
|
|
98
|
+
CheckboxGroupWithTooltip: typeof CheckboxGroupWithTooltip;
|
|
99
|
+
};
|
|
50
100
|
isActionTriggerable: (config: any, context: any) => boolean;
|
|
51
101
|
useVariables(config: any, options: any): import("@nocobase/plugin-workflow/client").VariableOption[];
|
|
52
102
|
useInitializers(config: any): SchemaInitializerItemType | null;
|
package/dist/client/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(o,e){typeof exports=="object"&&typeof module!="undefined"?e(exports,require("@nocobase/client"),require("@nocobase/plugin-workflow/client"),require("@formily/react"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@nocobase/client","@nocobase/plugin-workflow/client","@formily/react","react-i18next"],e):(o=typeof globalThis!="undefined"?globalThis:o||self,e(o["@nocobase/plugin-workflow-action-trigger"]={},o["@nocobase/client"],o["@nocobase/plugin-workflow"],o["@formily/react"],o["react-i18next"]))})(this,function(o,e,t,w,m){"use strict";var
|
|
1
|
+
(function(o,e){typeof exports=="object"&&typeof module!="undefined"?e(exports,require("@nocobase/client"),require("@nocobase/plugin-workflow/client"),require("@formily/react"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@nocobase/client","@nocobase/plugin-workflow/client","@formily/react","react-i18next"],e):(o=typeof globalThis!="undefined"?globalThis:o||self,e(o["@nocobase/plugin-workflow-action-trigger"]={},o["@nocobase/client"],o["@nocobase/plugin-workflow"],o["@formily/react"],o["react-i18next"]))})(this,function(o,e,t,w,m){"use strict";var E=Object.defineProperty,$=Object.defineProperties;var M=Object.getOwnPropertyDescriptors;var S=Object.getOwnPropertySymbols;var v=Object.prototype.hasOwnProperty,D=Object.prototype.propertyIsEnumerable;var k=(o,e,t)=>e in o?E(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t,g=(o,e)=>{for(var t in e||(e={}))v.call(e,t)&&k(o,t,e[t]);if(S)for(var t of S(e))D.call(e,t)&&k(o,t,e[t]);return o},f=(o,e)=>$(o,M(e));var s=(o,e,t)=>(k(o,typeof e!="symbol"?e+"":e,t),t);var F=(o,e,t)=>new Promise((w,m)=>{var i=r=>{try{c(t.next(r))}catch(l){m(l)}},a=r=>{try{c(t.throw(r))}catch(l){m(l)}},c=r=>r.done?w(r.value):Promise.resolve(r.value).then(i,a);c((t=t.apply(o,e)).next())});const i="workflow-action-trigger";function a(u,A={}){const{t:n}=c(A);return n(u)}function c(u){return m.useTranslation(i,u)}const r={CREATE:"create",UPDATE:"update",UPSERT:"updateOrCreate",DESTROY:"destroy"};class l extends t.Trigger{constructor(){super(...arguments);s(this,"title",`{{t("Post-action event", { ns: "${i}" })}}`);s(this,"description",`{{t('Triggered after the completion of a request initiated through an action button or API, such as after adding, updating, deleting data, or "submit to workflow". Suitable for data processing, sending notifications, etc., after actions are completed.', { ns: "${i}" })}}`);s(this,"fieldset",{collection:{type:"string",required:!0,"x-decorator":"FormItem","x-decorator-props":{tooltip:`{{t("The collection to which the triggered data belongs.", { ns: "${i}" })}}`},"x-component":"DataSourceCollectionCascader","x-disabled":"{{ useWorkflowAnyExecuted() }}",title:`{{t("Collection", { ns: "${i}" })}}`,"x-reactions":[{target:"appends",effects:["onFieldValueChange"],fulfill:{state:{value:[]}}}]},global:{type:"boolean",title:`{{t("Trigger mode", { ns: "${i}" })}}`,"x-decorator":"FormItem","x-component":"RadioWithTooltip","x-component-props":{direction:"vertical",options:[{label:`{{t("Local mode, triggered after the completion of actions bound to this workflow", { ns: "${i}" })}}`,value:!1},{label:`{{t("Global mode, triggered after the completion of the following actions", { ns: "${i}" })}}`,value:!0}]},default:!1,"x-reactions":[{dependencies:["collection"],fulfill:{state:{visible:"{{!!$deps[0]}}"}}}]},actions:{type:"number",title:`{{t("Select actions", { ns: "${i}" })}}`,"x-decorator":"FormItem","x-component":"CheckboxGroupWithTooltip","x-component-props":{direction:"vertical",options:[{label:`{{t("Create record action", { ns: "${i}" })}}`,value:r.CREATE},{label:`{{t("Update record action", { ns: "${i}" })}}`,value:r.UPDATE}]},required:!0,"x-reactions":[{dependencies:["collection","global"],fulfill:{state:{visible:"{{!!$deps[0] && !!$deps[1]}}"}}}]},appends:{type:"array",title:`{{t("Associations to use", { ns: "${i}" })}}`,description:'{{t("Please select the associated fields that need to be accessed in subsequent nodes. With more than two levels of to-many associations may cause performance issue, please use with caution.", { ns: "workflow" })}}',"x-decorator":"FormItem","x-component":"AppendsTreeSelect","x-component-props":{title:"Preload associations",multiple:!0,useCollection(){const{values:n}=w.useForm();return n==null?void 0:n.collection}},"x-reactions":[{dependencies:["collection"],fulfill:{state:{visible:"{{!!$deps[0]}}"}}}]}});s(this,"scope",{useCollectionDataSource:e.useCollectionDataSource,useWorkflowAnyExecuted:t.useWorkflowAnyExecuted});s(this,"components",{RadioWithTooltip:t.RadioWithTooltip,CheckboxGroupWithTooltip:t.CheckboxGroupWithTooltip});s(this,"isActionTriggerable",(n,d)=>d.action==="customize:triggerWorkflows"||["create","update","customize:update"].includes(d.action)&&!n.global)}useVariables(n,d){var y;const T=e.useCompile(),{getCollectionFields:I}=e.useCollectionManager_deprecated(),z=a("Trigger data"),x=a("User submitted action"),C=a("Role of user submitted action"),W=[{collectionName:n.collection,name:"data",type:"hasOne",target:n.collection,uiSchema:{title:z}},{collectionName:"users",name:"user",type:"hasOne",target:"users",uiSchema:{title:x}},{name:"roleName",uiSchema:{title:C}}];return t.getCollectionFieldOptions(f(g({appends:["data","user",...((y=n.appends)==null?void 0:y.map(R=>`data.${R}`))||[]]},d),{fields:W,compile:T,getCollectionFields:I}))}useInitializers(n){return n.collection?{name:"triggerData",type:"item",key:"triggerData",title:`{{t("Trigger data", { ns: "${i}" })}}`,Component:t.CollectionBlockInitializer,collection:n.collection,dataPath:"$context.data"}:null}}const h={name:"submitToWorkflow",title:'{{t("Submit to workflow", { ns: "workflow" })}}',Component:"CustomizeActionInitializer",schema:{title:'{{t("Submit to workflow", { ns: "workflow" })}}',"x-component":"Action","x-use-component-props":"useTriggerWorkflowsActionProps","x-designer":"Action.Designer","x-action-settings":{skipValidator:!1,onSuccess:{manualClose:!0,redirecting:!1,successMessage:'{{t("Submitted successfully")}}'},triggerWorkflows:[]},"x-action":"customize:triggerWorkflows"}},p={name:"submitToWorkflow",title:'{{t("Submit to workflow", { ns: "workflow" })}}',Component:"CustomizeActionInitializer",schema:{title:'{{t("Submit to workflow", { ns: "workflow" })}}',"x-component":"Action","x-use-component-props":"useRecordTriggerWorkflowsActionProps","x-designer":"Action.Designer","x-action-settings":{onSuccess:{manualClose:!0,redirecting:!1,successMessage:'{{t("Submitted successfully")}}'},triggerWorkflows:[]},"x-action":"customize:triggerWorkflows"}},b=f(g({},p),{schema:f(g({},p.schema),{"x-component":"Action.Link"})});class P extends e.Plugin{load(){return F(this,null,function*(){this.app.pm.get("workflow").registerTrigger("action",l),this.app.addScopes({useTriggerWorkflowsActionProps:t.useTriggerWorkflowsActionProps,useRecordTriggerWorkflowsActionProps:t.useRecordTriggerWorkflowsActionProps}),this.app.schemaInitializerManager.get("FormActionInitializers").add("customize.submitToWorkflow",h),this.app.schemaInitializerManager.get("createForm:configureActions").add("customize.submitToWorkflow",h),this.app.schemaInitializerManager.get("editForm:configureActions").add("customize.submitToWorkflow",h),this.app.schemaInitializerManager.get("detailsWithPaging:configureActions").add("customize.submitToWorkflow",p),this.app.schemaInitializerManager.get("details:configureActions").add("customize.submitToWorkflow",p),this.app.schemaInitializerManager.get("table:configureItemActions").add("customize.submitToWorkflow",b),this.app.schemaInitializerManager.get("gridCard:configureItemActions").add("customize.submitToWorkflow",b),this.app.schemaInitializerManager.get("list:configureItemActions").add("customize.submitToWorkflow",b)})}}o.default=P,Object.defineProperties(o,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/dist/externalVersion.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
"@formily/react": "2.3.0",
|
|
3
|
-
"@nocobase/client": "0.21.0-alpha.
|
|
4
|
-
"@nocobase/plugin-workflow": "0.21.0-alpha.
|
|
3
|
+
"@nocobase/client": "0.21.0-alpha.11",
|
|
4
|
+
"@nocobase/plugin-workflow": "0.21.0-alpha.11",
|
|
5
5
|
"react-i18next": "11.18.6",
|
|
6
6
|
"lodash": "4.17.21",
|
|
7
7
|
"sequelize": "6.35.2",
|
|
8
|
-
"@nocobase/database": "0.21.0-alpha.
|
|
9
|
-
"@nocobase/server": "0.21.0-alpha.
|
|
10
|
-
"@nocobase/actions": "0.21.0-alpha.
|
|
11
|
-
"@nocobase/data-source-manager": "0.21.0-alpha.
|
|
12
|
-
"@nocobase/plugin-workflow-test": "0.21.0-alpha.
|
|
13
|
-
"@nocobase/test": "0.21.0-alpha.
|
|
14
|
-
"@nocobase/utils": "0.21.0-alpha.
|
|
8
|
+
"@nocobase/database": "0.21.0-alpha.11",
|
|
9
|
+
"@nocobase/server": "0.21.0-alpha.11",
|
|
10
|
+
"@nocobase/actions": "0.21.0-alpha.11",
|
|
11
|
+
"@nocobase/data-source-manager": "0.21.0-alpha.11",
|
|
12
|
+
"@nocobase/plugin-workflow-test": "0.21.0-alpha.11",
|
|
13
|
+
"@nocobase/test": "0.21.0-alpha.11",
|
|
14
|
+
"@nocobase/utils": "0.21.0-alpha.11"
|
|
15
15
|
};
|
package/dist/locale/zh-CN.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
2
|
+
"Post-action event": "操作后事件",
|
|
3
|
+
"Triggered after the completion of a request initiated through an action button or API, such as after adding, updating, deleting data, or \"submit to workflow\". Suitable for data processing, sending notifications, etc., after actions are completed.":
|
|
4
|
+
"通过操作按钮或 API 发起请求并在执行完成后触发,比如新增、更新、删除数据或者“提交至工作流”之后。适用于在操作完成后进行数据处理、发送通知等。",
|
|
4
5
|
"Collection": "数据表",
|
|
5
|
-
"
|
|
6
|
+
"The collection to which the triggered data belongs.": "触发数据所属的数据表。",
|
|
7
|
+
"Trigger mode": "触发模式",
|
|
8
|
+
"Local mode, triggered after the completion of actions bound to this workflow": "局部模式,绑定该工作流的操作执行完成后触发",
|
|
9
|
+
"Global mode, triggered after the completion of the following actions": "全局模式,以下操作执行完成后都触发",
|
|
10
|
+
"Action to submit to workflow directly is only supported on bound buttons, and will not be affected under global mode.": "直接提交至工作流的操作仅支持使用按钮绑定,不受全局模式的影响。",
|
|
11
|
+
"Select actions": "选择操作",
|
|
12
|
+
"Create record action": "创建记录操作",
|
|
13
|
+
"Update record action": "更新记录操作",
|
|
6
14
|
"Associations to use": "待使用的关系数据",
|
|
7
15
|
"Trigger data": "触发器数据",
|
|
8
16
|
"User submitted action": "提交操作的用户",
|
|
@@ -4,10 +4,11 @@ import WorkflowPlugin, { Trigger, WorkflowModel } from '@nocobase/plugin-workflo
|
|
|
4
4
|
interface Context extends ActionContext, DefaultContext {
|
|
5
5
|
}
|
|
6
6
|
export default class extends Trigger {
|
|
7
|
+
static TYPE: string;
|
|
7
8
|
constructor(workflow: WorkflowPlugin);
|
|
8
|
-
|
|
9
|
+
workflowTriggerAction(context: Context, next: Next): Promise<void>;
|
|
9
10
|
middleware: (context: Context, next: Next) => Promise<void>;
|
|
10
|
-
private
|
|
11
|
+
private collectionTriggerAction;
|
|
11
12
|
on(workflow: WorkflowModel): void;
|
|
12
13
|
off(workflow: WorkflowModel): void;
|
|
13
14
|
}
|
|
@@ -25,40 +25,43 @@ var import_database = require("@nocobase/database");
|
|
|
25
25
|
var import_plugin_workflow = require("@nocobase/plugin-workflow");
|
|
26
26
|
var import_data_source_manager = require("@nocobase/data-source-manager");
|
|
27
27
|
class ActionTrigger_default extends import_plugin_workflow.Trigger {
|
|
28
|
+
static TYPE = "action";
|
|
28
29
|
constructor(workflow) {
|
|
29
30
|
super(workflow);
|
|
30
31
|
workflow.app.use(this.middleware, { after: "dataSource" });
|
|
31
32
|
}
|
|
32
|
-
async
|
|
33
|
+
async workflowTriggerAction(context, next) {
|
|
33
34
|
const { triggerWorkflows } = context.action.params;
|
|
34
35
|
if (!triggerWorkflows) {
|
|
35
36
|
return context.throw(400);
|
|
36
37
|
}
|
|
37
38
|
context.status = 202;
|
|
38
39
|
await next();
|
|
39
|
-
this.
|
|
40
|
+
return this.collectionTriggerAction(context);
|
|
40
41
|
}
|
|
41
42
|
middleware = async (context, next) => {
|
|
42
|
-
const {
|
|
43
|
-
resourceName,
|
|
44
|
-
actionName,
|
|
45
|
-
params: { triggerWorkflows }
|
|
46
|
-
} = context.action;
|
|
43
|
+
const { resourceName, actionName } = context.action;
|
|
47
44
|
if (resourceName === "workflows" && actionName === "trigger") {
|
|
48
|
-
return this.
|
|
45
|
+
return this.workflowTriggerAction(context, next);
|
|
49
46
|
}
|
|
50
47
|
await next();
|
|
51
|
-
if (!triggerWorkflows) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
48
|
if (!["create", "update"].includes(actionName)) {
|
|
55
49
|
return;
|
|
56
50
|
}
|
|
57
|
-
return this.
|
|
51
|
+
return this.collectionTriggerAction(context);
|
|
58
52
|
};
|
|
59
|
-
async
|
|
60
|
-
const {
|
|
53
|
+
async collectionTriggerAction(context) {
|
|
54
|
+
const {
|
|
55
|
+
resourceName,
|
|
56
|
+
actionName,
|
|
57
|
+
params: { triggerWorkflows = "", values }
|
|
58
|
+
} = context.action;
|
|
61
59
|
const dataSourceHeader = context.get("x-data-source") || "main";
|
|
60
|
+
const collection = context.app.dataSourceManager.dataSources.get(dataSourceHeader).collectionManager.getCollection(resourceName);
|
|
61
|
+
if (!collection) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const fullCollectionName = (0, import_data_source_manager.joinCollectionName)(dataSourceHeader, collection.name);
|
|
62
65
|
const { currentUser, currentRole } = context.state;
|
|
63
66
|
const { model: UserModel } = this.workflow.db.getCollection("users");
|
|
64
67
|
const userInfo = {
|
|
@@ -66,21 +69,42 @@ class ActionTrigger_default extends import_plugin_workflow.Trigger {
|
|
|
66
69
|
roleName: currentRole
|
|
67
70
|
};
|
|
68
71
|
const triggers = triggerWorkflows.split(",").map((trigger) => trigger.split("!"));
|
|
69
|
-
const
|
|
70
|
-
const workflows = (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
const triggersKeysMap = new Map(triggers);
|
|
73
|
+
const workflows = Array.from(this.workflow.enabledCache.values()).filter(
|
|
74
|
+
(item) => item.type === "action" && item.config.collection
|
|
75
|
+
);
|
|
76
|
+
const globalWorkflows = /* @__PURE__ */ new Map();
|
|
77
|
+
const localWorkflows = /* @__PURE__ */ new Map();
|
|
78
|
+
workflows.forEach((item) => {
|
|
79
|
+
var _a;
|
|
80
|
+
if (resourceName === "workflows" && actionName === "trigger") {
|
|
81
|
+
localWorkflows.set(item.key, item);
|
|
82
|
+
} else if (item.config.collection === fullCollectionName) {
|
|
83
|
+
if (item.config.global) {
|
|
84
|
+
if ((_a = item.config.actions) == null ? void 0 : _a.includes(actionName)) {
|
|
85
|
+
globalWorkflows.set(item.key, item);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
localWorkflows.set(item.key, item);
|
|
89
|
+
}
|
|
76
90
|
}
|
|
77
|
-
})
|
|
91
|
+
});
|
|
92
|
+
const triggeringLocalWorkflows = [];
|
|
93
|
+
const uniqueTriggersMap = /* @__PURE__ */ new Map();
|
|
94
|
+
triggers.forEach((trigger) => {
|
|
95
|
+
const [key] = trigger;
|
|
96
|
+
const workflow = localWorkflows.get(key);
|
|
97
|
+
if (workflow && !uniqueTriggersMap.has(key)) {
|
|
98
|
+
triggeringLocalWorkflows.push(workflow);
|
|
99
|
+
uniqueTriggersMap.set(key, true);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
78
102
|
const syncGroup = [];
|
|
79
103
|
const asyncGroup = [];
|
|
80
|
-
for (const workflow of
|
|
81
|
-
const {
|
|
82
|
-
const [dataSourceName, collectionName] = (0, import_data_source_manager.parseCollectionName)(collection);
|
|
83
|
-
const
|
|
104
|
+
for (const workflow of triggeringLocalWorkflows.concat(...globalWorkflows.values())) {
|
|
105
|
+
const { appends = [] } = workflow.config;
|
|
106
|
+
const [dataSourceName, collectionName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
|
|
107
|
+
const dataPath = triggersKeysMap.get(workflow.key);
|
|
84
108
|
const event = [workflow];
|
|
85
109
|
if (context.action.resourceName !== "workflows") {
|
|
86
110
|
if (!context.body) {
|
|
@@ -92,9 +116,12 @@ class ActionTrigger_default extends import_plugin_workflow.Trigger {
|
|
|
92
116
|
const { body: data } = context;
|
|
93
117
|
for (const row of Array.isArray(data) ? data : [data]) {
|
|
94
118
|
let payload = row;
|
|
95
|
-
if (
|
|
96
|
-
const paths =
|
|
119
|
+
if (dataPath) {
|
|
120
|
+
const paths = dataPath.split(".");
|
|
97
121
|
for (const field of paths) {
|
|
122
|
+
if (!payload) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
98
125
|
if (payload.get(field)) {
|
|
99
126
|
payload = payload.get(field);
|
|
100
127
|
} else {
|
|
@@ -103,14 +130,14 @@ class ActionTrigger_default extends import_plugin_workflow.Trigger {
|
|
|
103
130
|
}
|
|
104
131
|
}
|
|
105
132
|
}
|
|
106
|
-
const model = payload.constructor;
|
|
107
133
|
if (payload instanceof import_database.Model) {
|
|
134
|
+
const model = payload.constructor;
|
|
108
135
|
if (collectionName !== model.collection.name) {
|
|
109
136
|
continue;
|
|
110
137
|
}
|
|
111
138
|
if (appends.length) {
|
|
112
139
|
payload = await model.collection.repository.findOne({
|
|
113
|
-
filterByTk: payload.get(model.
|
|
140
|
+
filterByTk: payload.get(model.collection.filterTargetKey),
|
|
114
141
|
appends
|
|
115
142
|
});
|
|
116
143
|
}
|
|
@@ -118,9 +145,9 @@ class ActionTrigger_default extends import_plugin_workflow.Trigger {
|
|
|
118
145
|
event.push({ data: (0, import_plugin_workflow.toJSON)(payload), ...userInfo });
|
|
119
146
|
}
|
|
120
147
|
} else {
|
|
121
|
-
const {
|
|
122
|
-
let data =
|
|
123
|
-
const pk = (0, import_lodash.get)(data,
|
|
148
|
+
const { filterTargetKey, repository } = context.app.dataSourceManager.dataSources.get(dataSourceName).collectionManager.getCollection(collectionName);
|
|
149
|
+
let data = dataPath ? (0, import_lodash.get)(values, dataPath) : values;
|
|
150
|
+
const pk = (0, import_lodash.get)(data, filterTargetKey);
|
|
124
151
|
if (appends.length && pk != null) {
|
|
125
152
|
data = await repository.findOne({
|
|
126
153
|
filterByTk: pk,
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/plugin-workflow-action-trigger",
|
|
3
|
-
"displayName": "Workflow:
|
|
4
|
-
"displayName.zh-CN": "
|
|
5
|
-
"description": "
|
|
6
|
-
"description.zh-CN": "
|
|
7
|
-
"version": "0.21.0-alpha.
|
|
3
|
+
"displayName": "Workflow: Post-action event",
|
|
4
|
+
"displayName.zh-CN": "工作流:操作后事件",
|
|
5
|
+
"description": "Triggered after the completion of a request initiated through an action button or API, such as after adding, updating, deleting data, or \"submit to workflow\". Suitable for data processing, sending notifications, etc., after actions are completed.",
|
|
6
|
+
"description.zh-CN": "通过操作按钮或 API 发起请求并在执行完成后触发,比如新增、更新、删除数据或者“提交至工作流”之后。适用于在操作完成后进行数据处理、发送通知等。",
|
|
7
|
+
"version": "0.21.0-alpha.11",
|
|
8
8
|
"license": "AGPL-3.0",
|
|
9
9
|
"main": "./dist/server/index.js",
|
|
10
10
|
"homepage": "https://docs.nocobase.com/plugins/workflow-action-trigger",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@nocobase/server": "0.x",
|
|
22
22
|
"@nocobase/test": "0.x"
|
|
23
23
|
},
|
|
24
|
-
"gitHead": "
|
|
24
|
+
"gitHead": "69fe8a6d75864a3ba98c5a6d3ebfe43a585d4cd3",
|
|
25
25
|
"keywords": [
|
|
26
26
|
"Workflow"
|
|
27
27
|
]
|
|
@@ -11,21 +11,32 @@ import {
|
|
|
11
11
|
CollectionBlockInitializer,
|
|
12
12
|
getCollectionFieldOptions,
|
|
13
13
|
useWorkflowAnyExecuted,
|
|
14
|
+
CheckboxGroupWithTooltip,
|
|
15
|
+
RadioWithTooltip,
|
|
14
16
|
} from '@nocobase/plugin-workflow/client';
|
|
15
17
|
import { NAMESPACE, useLang } from '../locale';
|
|
16
18
|
|
|
19
|
+
const COLLECTION_TRIGGER_ACTION = {
|
|
20
|
+
CREATE: 'create',
|
|
21
|
+
UPDATE: 'update',
|
|
22
|
+
UPSERT: 'updateOrCreate',
|
|
23
|
+
DESTROY: 'destroy',
|
|
24
|
+
};
|
|
25
|
+
|
|
17
26
|
export default class extends Trigger {
|
|
18
|
-
title = `{{t("
|
|
19
|
-
description = `{{t(
|
|
27
|
+
title = `{{t("Post-action event", { ns: "${NAMESPACE}" })}}`;
|
|
28
|
+
description = `{{t('Triggered after the completion of a request initiated through an action button or API, such as after adding, updating, deleting data, or "submit to workflow". Suitable for data processing, sending notifications, etc., after actions are completed.', { ns: "${NAMESPACE}" })}}`;
|
|
20
29
|
fieldset = {
|
|
21
30
|
collection: {
|
|
22
31
|
type: 'string',
|
|
23
32
|
required: true,
|
|
24
33
|
'x-decorator': 'FormItem',
|
|
34
|
+
'x-decorator-props': {
|
|
35
|
+
tooltip: `{{t("The collection to which the triggered data belongs.", { ns: "${NAMESPACE}" })}}`,
|
|
36
|
+
},
|
|
25
37
|
'x-component': 'DataSourceCollectionCascader',
|
|
26
38
|
'x-disabled': '{{ useWorkflowAnyExecuted() }}',
|
|
27
39
|
title: `{{t("Collection", { ns: "${NAMESPACE}" })}}`,
|
|
28
|
-
description: `{{t("Which collection record belongs to.", { ns: "${NAMESPACE}" })}}`,
|
|
29
40
|
'x-reactions': [
|
|
30
41
|
{
|
|
31
42
|
target: 'appends',
|
|
@@ -38,6 +49,65 @@ export default class extends Trigger {
|
|
|
38
49
|
},
|
|
39
50
|
],
|
|
40
51
|
},
|
|
52
|
+
global: {
|
|
53
|
+
type: 'boolean',
|
|
54
|
+
title: `{{t("Trigger mode", { ns: "${NAMESPACE}" })}}`,
|
|
55
|
+
'x-decorator': 'FormItem',
|
|
56
|
+
'x-component': 'RadioWithTooltip',
|
|
57
|
+
'x-component-props': {
|
|
58
|
+
direction: 'vertical',
|
|
59
|
+
options: [
|
|
60
|
+
{
|
|
61
|
+
label: `{{t("Local mode, triggered after the completion of actions bound to this workflow", { ns: "${NAMESPACE}" })}}`,
|
|
62
|
+
value: false,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: `{{t("Global mode, triggered after the completion of the following actions", { ns: "${NAMESPACE}" })}}`,
|
|
66
|
+
value: true,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
default: false,
|
|
71
|
+
'x-reactions': [
|
|
72
|
+
{
|
|
73
|
+
dependencies: ['collection'],
|
|
74
|
+
fulfill: {
|
|
75
|
+
state: {
|
|
76
|
+
visible: '{{!!$deps[0]}}',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
actions: {
|
|
83
|
+
type: 'number',
|
|
84
|
+
title: `{{t("Select actions", { ns: "${NAMESPACE}" })}}`,
|
|
85
|
+
'x-decorator': 'FormItem',
|
|
86
|
+
'x-component': 'CheckboxGroupWithTooltip',
|
|
87
|
+
'x-component-props': {
|
|
88
|
+
direction: 'vertical',
|
|
89
|
+
options: [
|
|
90
|
+
{ label: `{{t("Create record action", { ns: "${NAMESPACE}" })}}`, value: COLLECTION_TRIGGER_ACTION.CREATE },
|
|
91
|
+
{ label: `{{t("Update record action", { ns: "${NAMESPACE}" })}}`, value: COLLECTION_TRIGGER_ACTION.UPDATE },
|
|
92
|
+
// { label: `{{t("upsert", { ns: "${NAMESPACE}" })}}`, value: COLLECTION_TRIGGER_ACTION.UPSERT },
|
|
93
|
+
// {
|
|
94
|
+
// label: `{{t("Delete single or many records", { ns: "${NAMESPACE}" })}}`,
|
|
95
|
+
// value: COLLECTION_TRIGGER_ACTION.DESTROY,
|
|
96
|
+
// },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
required: true,
|
|
100
|
+
'x-reactions': [
|
|
101
|
+
{
|
|
102
|
+
dependencies: ['collection', 'global'],
|
|
103
|
+
fulfill: {
|
|
104
|
+
state: {
|
|
105
|
+
visible: '{{!!$deps[0] && !!$deps[1]}}',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
41
111
|
appends: {
|
|
42
112
|
type: 'array',
|
|
43
113
|
title: `{{t("Associations to use", { ns: "${NAMESPACE}" })}}`,
|
|
@@ -68,8 +138,15 @@ export default class extends Trigger {
|
|
|
68
138
|
useCollectionDataSource,
|
|
69
139
|
useWorkflowAnyExecuted,
|
|
70
140
|
};
|
|
141
|
+
components = {
|
|
142
|
+
RadioWithTooltip,
|
|
143
|
+
CheckboxGroupWithTooltip,
|
|
144
|
+
};
|
|
71
145
|
isActionTriggerable = (config, context) => {
|
|
72
|
-
return
|
|
146
|
+
return (
|
|
147
|
+
context.action === 'customize:triggerWorkflows' ||
|
|
148
|
+
(['create', 'update', 'customize:update'].includes(context.action) && !config.global)
|
|
149
|
+
);
|
|
73
150
|
};
|
|
74
151
|
useVariables(config, options) {
|
|
75
152
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
@@ -56,7 +56,7 @@ test.describe('Add new', () => {
|
|
|
56
56
|
const workFlowName = faker.string.alphanumeric(5);
|
|
57
57
|
await createWorkFlow.name.fill(workFlowName);
|
|
58
58
|
await createWorkFlow.triggerType.click();
|
|
59
|
-
await page.
|
|
59
|
+
await page.getByTitle('Post-action event').click();
|
|
60
60
|
await page.getByLabel('action-Action-Submit-workflows').click();
|
|
61
61
|
|
|
62
62
|
// 3、预期结果:列表中出现新建的工作流
|
package/src/client/index.ts
CHANGED
|
@@ -13,9 +13,7 @@ const submitToWorkflowActionInitializer: SchemaInitializerItemType = {
|
|
|
13
13
|
schema: {
|
|
14
14
|
title: '{{t("Submit to workflow", { ns: "workflow" })}}',
|
|
15
15
|
'x-component': 'Action',
|
|
16
|
-
'x-component-props':
|
|
17
|
-
useProps: '{{ useTriggerWorkflowsActionProps }}',
|
|
18
|
-
},
|
|
16
|
+
'x-use-component-props': 'useTriggerWorkflowsActionProps',
|
|
19
17
|
'x-designer': 'Action.Designer',
|
|
20
18
|
'x-action-settings': {
|
|
21
19
|
// assignedValues: {},
|
|
@@ -38,9 +36,7 @@ const recordTriggerWorkflowActionInitializer: SchemaInitializerItemType = {
|
|
|
38
36
|
schema: {
|
|
39
37
|
title: '{{t("Submit to workflow", { ns: "workflow" })}}',
|
|
40
38
|
'x-component': 'Action',
|
|
41
|
-
'x-component-props':
|
|
42
|
-
useProps: '{{ useRecordTriggerWorkflowsActionProps }}',
|
|
43
|
-
},
|
|
39
|
+
'x-use-component-props': 'useRecordTriggerWorkflowsActionProps',
|
|
44
40
|
'x-designer': 'Action.Designer',
|
|
45
41
|
'x-action-settings': {
|
|
46
42
|
// assignedValues: {},
|
package/src/locale/zh-CN.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
2
|
+
"Post-action event": "操作后事件",
|
|
3
|
+
"Triggered after the completion of a request initiated through an action button or API, such as after adding, updating, deleting data, or \"submit to workflow\". Suitable for data processing, sending notifications, etc., after actions are completed.":
|
|
4
|
+
"通过操作按钮或 API 发起请求并在执行完成后触发,比如新增、更新、删除数据或者“提交至工作流”之后。适用于在操作完成后进行数据处理、发送通知等。",
|
|
4
5
|
"Collection": "数据表",
|
|
5
|
-
"
|
|
6
|
+
"The collection to which the triggered data belongs.": "触发数据所属的数据表。",
|
|
7
|
+
"Trigger mode": "触发模式",
|
|
8
|
+
"Local mode, triggered after the completion of actions bound to this workflow": "局部模式,绑定该工作流的操作执行完成后触发",
|
|
9
|
+
"Global mode, triggered after the completion of the following actions": "全局模式,以下操作执行完成后都触发",
|
|
10
|
+
"Action to submit to workflow directly is only supported on bound buttons, and will not be affected under global mode.": "直接提交至工作流的操作仅支持使用按钮绑定,不受全局模式的影响。",
|
|
11
|
+
"Select actions": "选择操作",
|
|
12
|
+
"Create record action": "创建记录操作",
|
|
13
|
+
"Update record action": "更新记录操作",
|
|
6
14
|
"Associations to use": "待使用的关系数据",
|
|
7
15
|
"Trigger data": "触发器数据",
|
|
8
16
|
"User submitted action": "提交操作的用户",
|
|
@@ -5,18 +5,20 @@ import Application, { DefaultContext } from '@nocobase/server';
|
|
|
5
5
|
import { Context as ActionContext, Next } from '@nocobase/actions';
|
|
6
6
|
|
|
7
7
|
import WorkflowPlugin, { Trigger, WorkflowModel, toJSON } from '@nocobase/plugin-workflow';
|
|
8
|
-
import { parseCollectionName } from '@nocobase/data-source-manager';
|
|
8
|
+
import { joinCollectionName, parseCollectionName } from '@nocobase/data-source-manager';
|
|
9
9
|
|
|
10
10
|
interface Context extends ActionContext, DefaultContext {}
|
|
11
11
|
|
|
12
12
|
export default class extends Trigger {
|
|
13
|
+
static TYPE = 'action';
|
|
14
|
+
|
|
13
15
|
constructor(workflow: WorkflowPlugin) {
|
|
14
16
|
super(workflow);
|
|
15
17
|
|
|
16
18
|
workflow.app.use(this.middleware, { after: 'dataSource' });
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
async
|
|
21
|
+
async workflowTriggerAction(context: Context, next: Next) {
|
|
20
22
|
const { triggerWorkflows } = context.action.params;
|
|
21
23
|
|
|
22
24
|
if (!triggerWorkflows) {
|
|
@@ -26,37 +28,41 @@ export default class extends Trigger {
|
|
|
26
28
|
context.status = 202;
|
|
27
29
|
await next();
|
|
28
30
|
|
|
29
|
-
this.
|
|
31
|
+
return this.collectionTriggerAction(context);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
middleware = async (context: Context, next: Next) => {
|
|
33
|
-
const {
|
|
34
|
-
resourceName,
|
|
35
|
-
actionName,
|
|
36
|
-
params: { triggerWorkflows },
|
|
37
|
-
} = context.action;
|
|
35
|
+
const { resourceName, actionName } = context.action;
|
|
38
36
|
|
|
39
37
|
if (resourceName === 'workflows' && actionName === 'trigger') {
|
|
40
|
-
return this.
|
|
38
|
+
return this.workflowTriggerAction(context, next);
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
await next();
|
|
44
42
|
|
|
45
|
-
if (!triggerWorkflows) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
43
|
if (!['create', 'update'].includes(actionName)) {
|
|
50
44
|
return;
|
|
51
45
|
}
|
|
52
46
|
|
|
53
|
-
return this.
|
|
47
|
+
return this.collectionTriggerAction(context);
|
|
54
48
|
};
|
|
55
49
|
|
|
56
|
-
private async
|
|
57
|
-
const {
|
|
50
|
+
private async collectionTriggerAction(context: Context) {
|
|
51
|
+
const {
|
|
52
|
+
resourceName,
|
|
53
|
+
actionName,
|
|
54
|
+
params: { triggerWorkflows = '', values },
|
|
55
|
+
} = context.action;
|
|
58
56
|
const dataSourceHeader = context.get('x-data-source') || 'main';
|
|
57
|
+
const collection = context.app.dataSourceManager.dataSources
|
|
58
|
+
.get(dataSourceHeader)
|
|
59
|
+
.collectionManager.getCollection(resourceName);
|
|
60
|
+
|
|
61
|
+
if (!collection) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
59
64
|
|
|
65
|
+
const fullCollectionName = joinCollectionName(dataSourceHeader, collection.name);
|
|
60
66
|
const { currentUser, currentRole } = context.state;
|
|
61
67
|
const { model: UserModel } = this.workflow.db.getCollection('users');
|
|
62
68
|
const userInfo = {
|
|
@@ -65,23 +71,41 @@ export default class extends Trigger {
|
|
|
65
71
|
};
|
|
66
72
|
|
|
67
73
|
const triggers = triggerWorkflows.split(',').map((trigger) => trigger.split('!'));
|
|
68
|
-
const
|
|
69
|
-
const workflows = (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
})
|
|
78
|
-
|
|
74
|
+
const triggersKeysMap = new Map<string, string>(triggers);
|
|
75
|
+
const workflows = Array.from(this.workflow.enabledCache.values()).filter(
|
|
76
|
+
(item) => item.type === 'action' && item.config.collection,
|
|
77
|
+
);
|
|
78
|
+
const globalWorkflows = new Map();
|
|
79
|
+
const localWorkflows = new Map();
|
|
80
|
+
workflows.forEach((item) => {
|
|
81
|
+
if (resourceName === 'workflows' && actionName === 'trigger') {
|
|
82
|
+
localWorkflows.set(item.key, item);
|
|
83
|
+
} else if (item.config.collection === fullCollectionName) {
|
|
84
|
+
if (item.config.global) {
|
|
85
|
+
if (item.config.actions?.includes(actionName)) {
|
|
86
|
+
globalWorkflows.set(item.key, item);
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
localWorkflows.set(item.key, item);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
const triggeringLocalWorkflows = [];
|
|
94
|
+
const uniqueTriggersMap = new Map();
|
|
95
|
+
triggers.forEach((trigger) => {
|
|
96
|
+
const [key] = trigger;
|
|
97
|
+
const workflow = localWorkflows.get(key);
|
|
98
|
+
if (workflow && !uniqueTriggersMap.has(key)) {
|
|
99
|
+
triggeringLocalWorkflows.push(workflow);
|
|
100
|
+
uniqueTriggersMap.set(key, true);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
79
103
|
const syncGroup = [];
|
|
80
104
|
const asyncGroup = [];
|
|
81
|
-
for (const workflow of
|
|
82
|
-
const {
|
|
83
|
-
const [dataSourceName, collectionName] = parseCollectionName(collection);
|
|
84
|
-
const
|
|
105
|
+
for (const workflow of triggeringLocalWorkflows.concat(...globalWorkflows.values())) {
|
|
106
|
+
const { appends = [] } = workflow.config;
|
|
107
|
+
const [dataSourceName, collectionName] = parseCollectionName(workflow.config.collection);
|
|
108
|
+
const dataPath = triggersKeysMap.get(workflow.key);
|
|
85
109
|
const event = [workflow];
|
|
86
110
|
if (context.action.resourceName !== 'workflows') {
|
|
87
111
|
if (!context.body) {
|
|
@@ -93,9 +117,12 @@ export default class extends Trigger {
|
|
|
93
117
|
const { body: data } = context;
|
|
94
118
|
for (const row of Array.isArray(data) ? data : [data]) {
|
|
95
119
|
let payload = row;
|
|
96
|
-
if (
|
|
97
|
-
const paths =
|
|
120
|
+
if (dataPath) {
|
|
121
|
+
const paths = dataPath.split('.');
|
|
98
122
|
for (const field of paths) {
|
|
123
|
+
if (!payload) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
99
126
|
if (payload.get(field)) {
|
|
100
127
|
payload = payload.get(field);
|
|
101
128
|
} else {
|
|
@@ -104,37 +131,32 @@ export default class extends Trigger {
|
|
|
104
131
|
}
|
|
105
132
|
}
|
|
106
133
|
}
|
|
107
|
-
const model = payload.constructor;
|
|
108
134
|
if (payload instanceof Model) {
|
|
135
|
+
const model = payload.constructor as unknown as Model;
|
|
109
136
|
if (collectionName !== model.collection.name) {
|
|
110
137
|
continue;
|
|
111
138
|
}
|
|
112
139
|
if (appends.length) {
|
|
113
140
|
payload = await model.collection.repository.findOne({
|
|
114
|
-
filterByTk: payload.get(model.
|
|
141
|
+
filterByTk: payload.get(model.collection.filterTargetKey),
|
|
115
142
|
appends,
|
|
116
143
|
});
|
|
117
144
|
}
|
|
118
145
|
}
|
|
119
|
-
// this.workflow.trigger(workflow, { data: toJSON(payload), ...userInfo });
|
|
120
146
|
event.push({ data: toJSON(payload), ...userInfo });
|
|
121
147
|
}
|
|
122
148
|
} else {
|
|
123
|
-
const {
|
|
149
|
+
const { filterTargetKey, repository } = (<Application>context.app).dataSourceManager.dataSources
|
|
124
150
|
.get(dataSourceName)
|
|
125
151
|
.collectionManager.getCollection(collectionName);
|
|
126
|
-
let data =
|
|
127
|
-
const pk = get(data,
|
|
152
|
+
let data = dataPath ? get(values, dataPath) : values;
|
|
153
|
+
const pk = get(data, filterTargetKey);
|
|
128
154
|
if (appends.length && pk != null) {
|
|
129
155
|
data = await repository.findOne({
|
|
130
156
|
filterByTk: pk,
|
|
131
157
|
appends,
|
|
132
158
|
});
|
|
133
159
|
}
|
|
134
|
-
// this.workflow.trigger(workflow, {
|
|
135
|
-
// data,
|
|
136
|
-
// ...userInfo,
|
|
137
|
-
// });
|
|
138
160
|
event.push({ data, ...userInfo });
|
|
139
161
|
}
|
|
140
162
|
(workflow.sync ? syncGroup : asyncGroup).push(event);
|
|
@@ -10,7 +10,7 @@ describe('workflow > action-trigger', () => {
|
|
|
10
10
|
let db: Database;
|
|
11
11
|
let agent;
|
|
12
12
|
let PostRepo;
|
|
13
|
-
let
|
|
13
|
+
let CategoryRepo;
|
|
14
14
|
let WorkflowModel;
|
|
15
15
|
let UserRepo;
|
|
16
16
|
let users;
|
|
@@ -25,7 +25,7 @@ describe('workflow > action-trigger', () => {
|
|
|
25
25
|
db = app.db;
|
|
26
26
|
WorkflowModel = db.getCollection('workflows').model;
|
|
27
27
|
PostRepo = db.getCollection('posts').repository;
|
|
28
|
-
|
|
28
|
+
CategoryRepo = db.getCollection('categories').repository;
|
|
29
29
|
UserRepo = db.getCollection('users').repository;
|
|
30
30
|
|
|
31
31
|
users = await UserRepo.create({
|
|
@@ -461,6 +461,33 @@ describe('workflow > action-trigger', () => {
|
|
|
461
461
|
});
|
|
462
462
|
});
|
|
463
463
|
|
|
464
|
+
describe('associations actions', () => {
|
|
465
|
+
it('trigger on associated data', async () => {
|
|
466
|
+
const workflow = await WorkflowModel.create({
|
|
467
|
+
enabled: true,
|
|
468
|
+
type: 'action',
|
|
469
|
+
config: {
|
|
470
|
+
collection: 'posts',
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const c1 = await CategoryRepo.create({ values: { title: 'c1' } });
|
|
475
|
+
|
|
476
|
+
const res1 = await userAgents[0].resource('categories.posts', c1.id).create({
|
|
477
|
+
values: { title: 'p1' },
|
|
478
|
+
triggerWorkflows: `${workflow.key}`,
|
|
479
|
+
});
|
|
480
|
+
expect(res1.status).toBe(200);
|
|
481
|
+
|
|
482
|
+
await sleep(500);
|
|
483
|
+
|
|
484
|
+
const e1s = await workflow.getExecutions();
|
|
485
|
+
expect(e1s.length).toBe(1);
|
|
486
|
+
expect(e1s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
487
|
+
expect(e1s[0].context.data).toMatchObject({ title: 'p1', categoryId: c1.id });
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
464
491
|
describe('workflow key', () => {
|
|
465
492
|
it('revision', async () => {
|
|
466
493
|
const w1 = await WorkflowModel.create({
|
|
@@ -572,6 +599,126 @@ describe('workflow > action-trigger', () => {
|
|
|
572
599
|
});
|
|
573
600
|
});
|
|
574
601
|
|
|
602
|
+
describe('global workflow', () => {
|
|
603
|
+
it('no action configured should not be triggered', async () => {
|
|
604
|
+
const workflow = await WorkflowModel.create({
|
|
605
|
+
enabled: true,
|
|
606
|
+
type: 'action',
|
|
607
|
+
config: {
|
|
608
|
+
collection: 'posts',
|
|
609
|
+
global: true,
|
|
610
|
+
},
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
const res1 = await userAgents[0].resource('posts').create({
|
|
614
|
+
values: { title: 't1' },
|
|
615
|
+
});
|
|
616
|
+
expect(res1.status).toBe(200);
|
|
617
|
+
|
|
618
|
+
await sleep(500);
|
|
619
|
+
|
|
620
|
+
const e1 = await workflow.getExecutions();
|
|
621
|
+
expect(e1.length).toBe(0);
|
|
622
|
+
|
|
623
|
+
const res2 = await userAgents[0].resource('posts').create({
|
|
624
|
+
values: { title: 't1' },
|
|
625
|
+
triggerWorkflows: `${workflow.key}`,
|
|
626
|
+
});
|
|
627
|
+
expect(res2.status).toBe(200);
|
|
628
|
+
|
|
629
|
+
await sleep(500);
|
|
630
|
+
|
|
631
|
+
const e2 = await workflow.getExecutions();
|
|
632
|
+
expect(e2.length).toBe(0);
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('trigger on both create and update actions', async () => {
|
|
636
|
+
const workflow = await WorkflowModel.create({
|
|
637
|
+
enabled: true,
|
|
638
|
+
type: 'action',
|
|
639
|
+
config: {
|
|
640
|
+
collection: 'posts',
|
|
641
|
+
global: true,
|
|
642
|
+
actions: ['create', 'update'],
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
const res1 = await userAgents[0].resource('posts').create({
|
|
647
|
+
values: { title: 't1' },
|
|
648
|
+
});
|
|
649
|
+
expect(res1.status).toBe(200);
|
|
650
|
+
|
|
651
|
+
await sleep(500);
|
|
652
|
+
|
|
653
|
+
const e1 = await workflow.getExecutions();
|
|
654
|
+
expect(e1.length).toBe(1);
|
|
655
|
+
expect(e1[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
656
|
+
expect(e1[0].context.data).toMatchObject({ title: 't1' });
|
|
657
|
+
|
|
658
|
+
const res2 = await userAgents[0].resource('posts').update({
|
|
659
|
+
filterByTk: res1.body.data.id,
|
|
660
|
+
values: { title: 't2' },
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
await sleep(500);
|
|
664
|
+
|
|
665
|
+
const e2 = await workflow.getExecutions({ order: [['id', 'ASC']] });
|
|
666
|
+
expect(e2.length).toBe(2);
|
|
667
|
+
expect(e2[1].status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
668
|
+
expect(e2[1].context.data).toMatchObject({ title: 't2' });
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
it('trigger on action when bound to button', async () => {
|
|
672
|
+
const workflow = await WorkflowModel.create({
|
|
673
|
+
enabled: true,
|
|
674
|
+
type: 'action',
|
|
675
|
+
config: {
|
|
676
|
+
collection: 'posts',
|
|
677
|
+
global: true,
|
|
678
|
+
actions: ['create', 'update'],
|
|
679
|
+
},
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
const res1 = await userAgents[0].resource('posts').create({
|
|
683
|
+
values: { title: 't1' },
|
|
684
|
+
triggerWorkflows: `${workflow.key}`,
|
|
685
|
+
});
|
|
686
|
+
expect(res1.status).toBe(200);
|
|
687
|
+
|
|
688
|
+
await sleep(500);
|
|
689
|
+
|
|
690
|
+
const e1 = await workflow.getExecutions();
|
|
691
|
+
expect(e1.length).toBe(1);
|
|
692
|
+
expect(e1[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
693
|
+
expect(e1[0].context.data).toMatchObject({ title: 't1' });
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it('trigger on action directly submit to workflow', async () => {
|
|
697
|
+
const workflow = await WorkflowModel.create({
|
|
698
|
+
enabled: true,
|
|
699
|
+
type: 'action',
|
|
700
|
+
config: {
|
|
701
|
+
collection: 'posts',
|
|
702
|
+
global: true,
|
|
703
|
+
actions: ['create', 'update'],
|
|
704
|
+
},
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
const res1 = await userAgents[0].resource('workflows').trigger({
|
|
708
|
+
values: { title: 't1' },
|
|
709
|
+
triggerWorkflows: `${workflow.key}`,
|
|
710
|
+
});
|
|
711
|
+
expect(res1.status).toBe(202);
|
|
712
|
+
|
|
713
|
+
await sleep(500);
|
|
714
|
+
|
|
715
|
+
const e1 = await workflow.getExecutions();
|
|
716
|
+
expect(e1.length).toBe(1);
|
|
717
|
+
expect(e1[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
718
|
+
expect(e1[0].context.data).toMatchObject({ title: 't1' });
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
|
|
575
722
|
describe('multiple data source', () => {
|
|
576
723
|
it('trigger on different data source', async () => {
|
|
577
724
|
const workflow = await WorkflowModel.create({
|