@nocobase/plugin-workflow-loop 1.0.0-alpha.1 → 1.0.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/LoopInstruction.d.ts +8 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.js +9 -0
- package/dist/externalVersion.js +12 -3
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/locale/index.d.ts +8 -0
- package/dist/locale/index.js +9 -0
- package/dist/server/LoopInstruction.d.ts +8 -0
- package/dist/server/LoopInstruction.js +9 -0
- package/dist/server/Plugin.d.ts +8 -0
- package/dist/server/Plugin.js +9 -0
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.js +9 -0
- package/package.json +2 -2
- package/src/client/LoopInstruction.tsx +0 -161
- package/src/client/index.ts +0 -18
- package/src/index.ts +0 -2
- package/src/locale/en-US.json +0 -9
- package/src/locale/index.ts +0 -12
- package/src/locale/ko_KR.json +0 -10
- package/src/locale/zh-CN.json +0 -8
- package/src/server/LoopInstruction.ts +0 -98
- package/src/server/Plugin.ts +0 -11
- package/src/server/__tests__/instruction.test.ts +0 -476
- package/src/server/index.ts +0 -1
|
@@ -1,3 +1,11 @@
|
|
|
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
|
+
*/
|
|
1
9
|
import React from 'react';
|
|
2
10
|
import { VariableOption, WorkflowVariableInput, Instruction } from '@nocobase/plugin-workflow/client';
|
|
3
11
|
export default class extends Instruction {
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
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
|
+
*/
|
|
1
9
|
import { Plugin } from '@nocobase/client';
|
|
2
10
|
export default class extends Plugin {
|
|
3
11
|
afterAdd(): Promise<void>;
|
package/dist/client/index.js
CHANGED
|
@@ -1,3 +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
|
+
|
|
1
10
|
(function(n,e){typeof exports=="object"&&typeof module!="undefined"?e(exports,require("@nocobase/client"),require("react/jsx-runtime"),require("@ant-design/icons"),require("@nocobase/plugin-workflow/client"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@nocobase/client","react/jsx-runtime","@ant-design/icons","@nocobase/plugin-workflow/client","react-i18next"],e):(n=typeof globalThis!="undefined"?globalThis:n||self,e(n["@nocobase/plugin-workflow-loop"]={},n["@nocobase/client"],n.jsxRuntime,n["@ant-design/icons"],n["@nocobase/plugin-workflow"],n["react-i18next"]))})(this,function(n,e,o,v,t,k){"use strict";var V=Object.defineProperty,W=Object.defineProperties;var P=Object.getOwnPropertyDescriptors;var O=Object.getOwnPropertySymbols;var F=Object.prototype.hasOwnProperty,M=Object.prototype.propertyIsEnumerable;var I=(n,e,o)=>e in n?V(n,e,{enumerable:!0,configurable:!0,writable:!0,value:o}):n[e]=o,N=(n,e)=>{for(var o in e||(e={}))F.call(e,o)&&I(n,o,e[o]);if(O)for(var o of O(e))M.call(e,o)&&I(n,o,e[o]);return n},T=(n,e)=>W(n,P(e));var h=(n,e,o)=>(I(n,typeof e!="symbol"?e+"":e,o),o);var y=(n,e,o)=>new Promise((v,t)=>{var k=i=>{try{d(o.next(i))}catch(w){t(w)}},u=i=>{try{d(o.throw(i))}catch(w){t(w)}},d=i=>i.done?v(i.value):Promise.resolve(i.value).then(k,u);d((o=o.apply(n,e)).next())});const u="workflow-loop";function d(p,g={}){const{t:r}=i(g);return r(p)}function i(p){return k.useTranslation(u,p)}function w(p,g){let r=p,f=null;for(let a=0;a<g.length;a++){const c=g[a],s=r.find(m=>m.value===c);if(!s)return null;f=s,!s.isLeaf&&s.loadChildren&&s.loadChildren(s),s.children&&(r=s.children)}return f}class j extends t.Instruction{constructor(){super(...arguments);h(this,"title",`{{t("Loop", { ns: "${u}" })}}`);h(this,"type","loop");h(this,"group","control");h(this,"description",`{{t("By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.", { ns: "${u}" })}}`);h(this,"fieldset",{target:{type:"string",title:`{{t("Loop target", { ns: "${u}" })}}`,description:`{{t("A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.", { ns: "${u}" })}}`,"x-decorator":"FormItem","x-component":"WorkflowVariableInput","x-component-props":{changeOnSelect:!0,useTypedConstant:["string","number","null"],className:e.css`
|
|
2
11
|
width: 100%;
|
|
3
12
|
|
package/dist/externalVersion.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
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
|
+
|
|
1
10
|
module.exports = {
|
|
2
11
|
"react": "18.2.0",
|
|
3
12
|
"@ant-design/icons": "5.2.6",
|
|
4
|
-
"@nocobase/client": "1.0.0-alpha.
|
|
5
|
-
"@nocobase/plugin-workflow": "1.0.0-alpha.
|
|
13
|
+
"@nocobase/client": "1.0.0-alpha.11",
|
|
14
|
+
"@nocobase/plugin-workflow": "1.0.0-alpha.11",
|
|
6
15
|
"react-i18next": "11.18.6",
|
|
7
|
-
"@nocobase/server": "1.0.0-alpha.
|
|
16
|
+
"@nocobase/server": "1.0.0-alpha.11"
|
|
8
17
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
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
|
+
*/
|
|
1
9
|
export * from './server';
|
|
2
10
|
export { default } from './server';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __create = Object.create;
|
|
2
11
|
var __defProp = Object.defineProperty;
|
|
3
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
package/dist/locale/index.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
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
|
+
*/
|
|
1
9
|
export declare const NAMESPACE = "workflow-loop";
|
|
2
10
|
export declare function useLang(key: string, options?: {}): string;
|
|
3
11
|
export declare function usePluginTranslation(options: any): import("react-i18next").UseTranslationResponse<"workflow-loop", undefined>;
|
package/dist/locale/index.js
CHANGED
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __defProp = Object.defineProperty;
|
|
2
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -1,3 +1,11 @@
|
|
|
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
|
+
*/
|
|
1
9
|
import { Processor, Instruction, FlowNodeModel, JobModel } from '@nocobase/plugin-workflow';
|
|
2
10
|
export default class extends Instruction {
|
|
3
11
|
run(node: FlowNodeModel, prevJob: JobModel, processor: Processor): Promise<{
|
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __defProp = Object.defineProperty;
|
|
2
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
package/dist/server/Plugin.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
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
|
+
*/
|
|
1
9
|
import { Plugin } from '@nocobase/server';
|
|
2
10
|
export default class extends Plugin {
|
|
3
11
|
load(): Promise<void>;
|
package/dist/server/Plugin.js
CHANGED
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __create = Object.create;
|
|
2
11
|
var __defProp = Object.defineProperty;
|
|
3
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1 +1,9 @@
|
|
|
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
|
+
*/
|
|
1
9
|
export { default } from './Plugin';
|
package/dist/server/index.js
CHANGED
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __create = Object.create;
|
|
2
11
|
var __defProp = Object.defineProperty;
|
|
3
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"displayName.zh-CN": "工作流:循环节点",
|
|
5
5
|
"description": "Used to repeat the sub-process processing of each value in an array, and can also be used for fixed times of sub-process processing.",
|
|
6
6
|
"description.zh-CN": "用于对一个数组中的每个值进行重复的子流程处理,也可用于固定次数的重复子流程处理。",
|
|
7
|
-
"version": "1.0.0-alpha.
|
|
7
|
+
"version": "1.0.0-alpha.11",
|
|
8
8
|
"license": "AGPL-3.0",
|
|
9
9
|
"main": "./dist/server/index.js",
|
|
10
10
|
"homepage": "https://docs.nocobase.com/handbook/workflow-loop",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@nocobase/server": "1.x",
|
|
22
22
|
"@nocobase/test": "1.x"
|
|
23
23
|
},
|
|
24
|
-
"gitHead": "
|
|
24
|
+
"gitHead": "00ffb260456e5a22701fd16c87112a92708d9dc1",
|
|
25
25
|
"keywords": [
|
|
26
26
|
"Workflow"
|
|
27
27
|
]
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ArrowUpOutlined } from '@ant-design/icons';
|
|
3
|
-
|
|
4
|
-
import { css, cx, useCompile } from '@nocobase/client';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
NodeDefaultView,
|
|
8
|
-
Branch,
|
|
9
|
-
useFlowContext,
|
|
10
|
-
useStyles,
|
|
11
|
-
VariableOption,
|
|
12
|
-
WorkflowVariableInput,
|
|
13
|
-
defaultFieldNames,
|
|
14
|
-
nodesOptions,
|
|
15
|
-
scopeOptions,
|
|
16
|
-
triggerOptions,
|
|
17
|
-
Instruction,
|
|
18
|
-
} from '@nocobase/plugin-workflow/client';
|
|
19
|
-
import { NAMESPACE, useLang } from '../locale';
|
|
20
|
-
|
|
21
|
-
function findOption(options: VariableOption[], paths: string[]) {
|
|
22
|
-
let opts = options;
|
|
23
|
-
let option = null;
|
|
24
|
-
for (let i = 0; i < paths.length; i++) {
|
|
25
|
-
const path = paths[i];
|
|
26
|
-
const current = opts.find((item) => item.value === path);
|
|
27
|
-
if (!current) {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
option = current;
|
|
31
|
-
if (!current.isLeaf && current.loadChildren) {
|
|
32
|
-
current.loadChildren(current);
|
|
33
|
-
}
|
|
34
|
-
if (current.children) {
|
|
35
|
-
opts = current.children;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return option;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export default class extends Instruction {
|
|
42
|
-
title = `{{t("Loop", { ns: "${NAMESPACE}" })}}`;
|
|
43
|
-
type = 'loop';
|
|
44
|
-
group = 'control';
|
|
45
|
-
description = `{{t("By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.", { ns: "${NAMESPACE}" })}}`;
|
|
46
|
-
fieldset = {
|
|
47
|
-
target: {
|
|
48
|
-
type: 'string',
|
|
49
|
-
title: `{{t("Loop target", { ns: "${NAMESPACE}" })}}`,
|
|
50
|
-
description: `{{t("A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.", { ns: "${NAMESPACE}" })}}`,
|
|
51
|
-
'x-decorator': 'FormItem',
|
|
52
|
-
'x-component': 'WorkflowVariableInput',
|
|
53
|
-
'x-component-props': {
|
|
54
|
-
changeOnSelect: true,
|
|
55
|
-
useTypedConstant: ['string', 'number', 'null'],
|
|
56
|
-
className: css`
|
|
57
|
-
width: 100%;
|
|
58
|
-
|
|
59
|
-
.variable {
|
|
60
|
-
flex: 1;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.ant-input.null-value {
|
|
64
|
-
width: 100%;
|
|
65
|
-
}
|
|
66
|
-
`,
|
|
67
|
-
},
|
|
68
|
-
required: true,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
components = {
|
|
72
|
-
WorkflowVariableInput,
|
|
73
|
-
};
|
|
74
|
-
Component({ data }) {
|
|
75
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
76
|
-
const { nodes } = useFlowContext();
|
|
77
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
78
|
-
const { styles } = useStyles();
|
|
79
|
-
const entry = nodes.find((node) => node.upstreamId === data.id && node.branchIndex != null);
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<NodeDefaultView data={data}>
|
|
83
|
-
<div className={styles.nodeSubtreeClass}>
|
|
84
|
-
<div
|
|
85
|
-
className={cx(
|
|
86
|
-
styles.branchBlockClass,
|
|
87
|
-
css`
|
|
88
|
-
padding-left: 20em;
|
|
89
|
-
`,
|
|
90
|
-
)}
|
|
91
|
-
>
|
|
92
|
-
<Branch from={data} entry={entry} branchIndex={entry?.branchIndex ?? 0} />
|
|
93
|
-
|
|
94
|
-
<div className={styles.branchClass}>
|
|
95
|
-
<div className="workflow-branch-lines" />
|
|
96
|
-
<div className={cx(styles.addButtonClass, styles.loopLineClass)}>
|
|
97
|
-
<ArrowUpOutlined />
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
</NodeDefaultView>
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
useScopeVariables(node, options) {
|
|
106
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
107
|
-
const compile = useCompile();
|
|
108
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
109
|
-
const langLoopTarget = useLang('Loop target');
|
|
110
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
111
|
-
const langLoopIndex = useLang('Loop index');
|
|
112
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
113
|
-
const langLoopLength = useLang('Loop length');
|
|
114
|
-
const { target } = node.config;
|
|
115
|
-
if (!target) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const { fieldNames = defaultFieldNames } = options;
|
|
120
|
-
|
|
121
|
-
// const { workflow } = useFlowContext();
|
|
122
|
-
// const current = useNodeContext();
|
|
123
|
-
// const upstreams = useAvailableUpstreams(current);
|
|
124
|
-
// find target data model by path described in `config.target`
|
|
125
|
-
// 1. get options from $context/$jobsMapByNodeKey
|
|
126
|
-
// 2. route to sub-options and use as loop target options
|
|
127
|
-
let targetOption: VariableOption = {
|
|
128
|
-
key: 'item',
|
|
129
|
-
[fieldNames.value]: 'item',
|
|
130
|
-
[fieldNames.label]: langLoopTarget,
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
if (typeof target === 'string' && target.startsWith('{{') && target.endsWith('}}')) {
|
|
134
|
-
const paths = target
|
|
135
|
-
.slice(2, -2)
|
|
136
|
-
.split('.')
|
|
137
|
-
.map((path) => path.trim());
|
|
138
|
-
|
|
139
|
-
const targetOptions = [scopeOptions, nodesOptions, triggerOptions].map((item: any) => {
|
|
140
|
-
const opts = item.useOptions({ ...options, current: node }).filter(Boolean);
|
|
141
|
-
return {
|
|
142
|
-
[fieldNames.label]: compile(item.label),
|
|
143
|
-
[fieldNames.value]: item.value,
|
|
144
|
-
key: item.value,
|
|
145
|
-
[fieldNames.children]: opts,
|
|
146
|
-
disabled: opts && !opts.length,
|
|
147
|
-
};
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
const found = findOption(targetOptions, paths);
|
|
151
|
-
|
|
152
|
-
targetOption = Object.assign({}, found, targetOption);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return [
|
|
156
|
-
targetOption,
|
|
157
|
-
{ key: 'index', [fieldNames.value]: 'index', [fieldNames.label]: langLoopIndex },
|
|
158
|
-
{ key: 'length', [fieldNames.value]: 'length', [fieldNames.label]: langLoopLength },
|
|
159
|
-
];
|
|
160
|
-
}
|
|
161
|
-
}
|
package/src/client/index.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Plugin } from '@nocobase/client';
|
|
2
|
-
import WorkflowPlugin from '@nocobase/plugin-workflow/client';
|
|
3
|
-
|
|
4
|
-
import LoopInstruction from './LoopInstruction';
|
|
5
|
-
|
|
6
|
-
export default class extends Plugin {
|
|
7
|
-
async afterAdd() {
|
|
8
|
-
// await this.app.pm.add()
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async beforeLoad() {}
|
|
12
|
-
|
|
13
|
-
// You can get and modify the app instance here
|
|
14
|
-
async load() {
|
|
15
|
-
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
|
16
|
-
workflow.registerInstruction('loop', LoopInstruction);
|
|
17
|
-
}
|
|
18
|
-
}
|
package/src/index.ts
DELETED
package/src/locale/en-US.json
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"Loop": "Loop",
|
|
3
|
-
"Loop target": "Loop target",
|
|
4
|
-
"Loop index": "Loop index",
|
|
5
|
-
"Loop length": "Loop length",
|
|
6
|
-
"By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.": "By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.",
|
|
7
|
-
"Scope variables": "Scope variables",
|
|
8
|
-
"A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.": "A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it."
|
|
9
|
-
}
|
package/src/locale/index.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { useTranslation } from 'react-i18next';
|
|
2
|
-
|
|
3
|
-
export const NAMESPACE = 'workflow-loop';
|
|
4
|
-
|
|
5
|
-
export function useLang(key: string, options = {}) {
|
|
6
|
-
const { t } = usePluginTranslation(options);
|
|
7
|
-
return t(key);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function usePluginTranslation(options) {
|
|
11
|
-
return useTranslation(NAMESPACE, options);
|
|
12
|
-
}
|
package/src/locale/ko_KR.json
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"Loop": "루프",
|
|
3
|
-
"Loop target": "루프 대상",
|
|
4
|
-
"Loop index": "현재 인덱스",
|
|
5
|
-
"Loop length": "루프 길이",
|
|
6
|
-
"By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.":
|
|
7
|
-
"루프 노드를 사용하면 여러 데이터 세트에 동일한 작업을 수행할 수 있습니다. 이러한 세트의 소스는 쿼리 노드에서 가져온 여러 레코드이거나 단일 레코드의 여러 관련 레코드일 수 있습니다. 루프 노드는 특정 횟수만큼 반복하거나 문자열의 각 문자를 반복하는 데에도 사용할 수 있습니다. 그러나 과도한 루핑은 성능 문제를 일으킬 수 있으므로 주의해서 사용하십시오.",
|
|
8
|
-
"A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.":
|
|
9
|
-
"단일 숫자는 루프 카운트로 처리되고, 단일 문자열은 문자 배열로 처리되며, 다른 배열이 아닌 값은 배열로 변환됩니다. 루프 노드는 루프 카운트에 도달하거나 배열 루프가 완료되었을 때 종료됩니다. 루프를 종료하려면 루프에 조건 노드를 추가할 수도 있습니다."
|
|
10
|
-
}
|
package/src/locale/zh-CN.json
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"Loop": "循环",
|
|
3
|
-
"Loop target": "循环对象",
|
|
4
|
-
"Loop index": "当前索引",
|
|
5
|
-
"Loop length": "循环长度",
|
|
6
|
-
"By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.": "使用循环节点可以对多条数据进行同样的操作,多条数据的来源可以是查询节点的多条结果,或者一条数据的多条关系数据。也可以用于一定次数的循环,或者对字符串中每一个字符的循环处理。循环次数过高可能引起性能问题,请谨慎使用。",
|
|
7
|
-
"A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.": "单一数字值将被视为循环次数,单一字符串值将被视为字符数组,其他非数组值将被转换为数组。达到循环次数,或者将数组循环完成后,循环节点结束。你也可以在循环中添加条件节点,以终止循环。"
|
|
8
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { Processor, Instruction, JOB_STATUS, FlowNodeModel, JobModel } from '@nocobase/plugin-workflow';
|
|
2
|
-
|
|
3
|
-
function getTargetLength(target) {
|
|
4
|
-
let length = 0;
|
|
5
|
-
if (typeof target === 'number') {
|
|
6
|
-
if (target < 0) {
|
|
7
|
-
throw new Error('Loop target in number type must be greater than 0');
|
|
8
|
-
}
|
|
9
|
-
length = Math.floor(target);
|
|
10
|
-
} else {
|
|
11
|
-
const targets = (Array.isArray(target) ? target : [target]).filter((t) => t != null);
|
|
12
|
-
length = targets.length;
|
|
13
|
-
}
|
|
14
|
-
return length;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default class extends Instruction {
|
|
18
|
-
async run(node: FlowNodeModel, prevJob: JobModel, processor: Processor) {
|
|
19
|
-
const [branch] = processor.getBranches(node);
|
|
20
|
-
const target = processor.getParsedValue(node.config.target, node.id);
|
|
21
|
-
const length = getTargetLength(target);
|
|
22
|
-
|
|
23
|
-
if (!branch || !length) {
|
|
24
|
-
return {
|
|
25
|
-
status: JOB_STATUS.RESOLVED,
|
|
26
|
-
result: 0,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const job = await processor.saveJob({
|
|
31
|
-
status: JOB_STATUS.PENDING,
|
|
32
|
-
// save loop index
|
|
33
|
-
result: 0,
|
|
34
|
-
nodeId: node.id,
|
|
35
|
-
nodeKey: node.key,
|
|
36
|
-
upstreamId: prevJob?.id ?? null,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// TODO: add loop scope to stack
|
|
40
|
-
// processor.stack.push({
|
|
41
|
-
// label: node.title,
|
|
42
|
-
// value: node.id
|
|
43
|
-
// });
|
|
44
|
-
|
|
45
|
-
await processor.run(branch, job);
|
|
46
|
-
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async resume(node: FlowNodeModel, branchJob, processor: Processor) {
|
|
51
|
-
const job = processor.findBranchParentJob(branchJob, node) as JobModel;
|
|
52
|
-
const loop = processor.nodesMap.get(job.nodeId);
|
|
53
|
-
const [branch] = processor.getBranches(node);
|
|
54
|
-
|
|
55
|
-
const { result, status } = job;
|
|
56
|
-
// if loop has been done (resolved / rejected), do not care newly executed branch jobs.
|
|
57
|
-
if (status !== JOB_STATUS.PENDING) {
|
|
58
|
-
return processor.exit();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const nextIndex = result + 1;
|
|
62
|
-
|
|
63
|
-
const target = processor.getParsedValue(loop.config.target, node.id);
|
|
64
|
-
// branchJob.status === JOB_STATUS.RESOLVED means branchJob is done, try next loop or exit as resolved
|
|
65
|
-
if (branchJob.status > JOB_STATUS.PENDING) {
|
|
66
|
-
job.set({ result: nextIndex });
|
|
67
|
-
|
|
68
|
-
const length = getTargetLength(target);
|
|
69
|
-
if (nextIndex < length) {
|
|
70
|
-
await processor.saveJob(job);
|
|
71
|
-
await processor.run(branch, job);
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// branchJob.status < JOB_STATUS.PENDING means branchJob is rejected, any rejection should cause loop rejected
|
|
77
|
-
job.set({
|
|
78
|
-
status: branchJob.status,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return job;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
getScope(node, index, processor) {
|
|
85
|
-
const target = processor.getParsedValue(node.config.target, node.id);
|
|
86
|
-
const targets = (Array.isArray(target) ? target : [target]).filter((t) => t != null);
|
|
87
|
-
const length = getTargetLength(target);
|
|
88
|
-
const item = typeof target === 'number' ? index : targets[index];
|
|
89
|
-
|
|
90
|
-
const result = {
|
|
91
|
-
item,
|
|
92
|
-
index,
|
|
93
|
-
length,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
return result;
|
|
97
|
-
}
|
|
98
|
-
}
|
package/src/server/Plugin.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Plugin } from '@nocobase/server';
|
|
2
|
-
import { default as WorkflowPlugin } from '@nocobase/plugin-workflow';
|
|
3
|
-
|
|
4
|
-
import LoopInstruction from './LoopInstruction';
|
|
5
|
-
|
|
6
|
-
export default class extends Plugin {
|
|
7
|
-
async load() {
|
|
8
|
-
const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
|
|
9
|
-
workflowPlugin.registerInstruction('loop', LoopInstruction);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
@@ -1,476 +0,0 @@
|
|
|
1
|
-
import Database from '@nocobase/database';
|
|
2
|
-
import { Application } from '@nocobase/server';
|
|
3
|
-
import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
|
|
4
|
-
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
|
5
|
-
|
|
6
|
-
import Plugin from '..';
|
|
7
|
-
|
|
8
|
-
describe('workflow > instructions > loop', () => {
|
|
9
|
-
let app: Application;
|
|
10
|
-
let db: Database;
|
|
11
|
-
let PostRepo;
|
|
12
|
-
let WorkflowModel;
|
|
13
|
-
let workflow;
|
|
14
|
-
let plugin;
|
|
15
|
-
|
|
16
|
-
beforeEach(async () => {
|
|
17
|
-
app = await getApp({
|
|
18
|
-
plugins: [Plugin],
|
|
19
|
-
});
|
|
20
|
-
plugin = app.pm.get('workflow');
|
|
21
|
-
|
|
22
|
-
db = app.db;
|
|
23
|
-
WorkflowModel = db.getCollection('workflows').model;
|
|
24
|
-
PostRepo = db.getCollection('posts').repository;
|
|
25
|
-
|
|
26
|
-
workflow = await WorkflowModel.create({
|
|
27
|
-
enabled: true,
|
|
28
|
-
type: 'collection',
|
|
29
|
-
config: {
|
|
30
|
-
mode: 1,
|
|
31
|
-
collection: 'posts',
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
afterEach(() => app.destroy());
|
|
37
|
-
|
|
38
|
-
describe('branch', () => {
|
|
39
|
-
it('no branch just pass', async () => {
|
|
40
|
-
const n1 = await workflow.createNode({
|
|
41
|
-
type: 'loop',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const n2 = await workflow.createNode({
|
|
45
|
-
type: 'echo',
|
|
46
|
-
upstreamId: n1.id,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
await n1.setDownstream(n2);
|
|
50
|
-
|
|
51
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
52
|
-
|
|
53
|
-
await sleep(500);
|
|
54
|
-
|
|
55
|
-
const [execution] = await workflow.getExecutions();
|
|
56
|
-
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
57
|
-
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
58
|
-
expect(jobs.length).toBe(2);
|
|
59
|
-
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
|
60
|
-
expect(jobs[0].result).toBe(0);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should exit when branch meets error', async () => {
|
|
64
|
-
const n1 = await workflow.createNode({
|
|
65
|
-
type: 'loop',
|
|
66
|
-
config: {
|
|
67
|
-
target: 2,
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const n2 = await workflow.createNode({
|
|
72
|
-
type: 'error',
|
|
73
|
-
upstreamId: n1.id,
|
|
74
|
-
branchIndex: 0,
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const n3 = await workflow.createNode({
|
|
78
|
-
type: 'echo',
|
|
79
|
-
upstreamId: n1.id,
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
await n1.setDownstream(n3);
|
|
83
|
-
|
|
84
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
85
|
-
|
|
86
|
-
await sleep(500);
|
|
87
|
-
|
|
88
|
-
const [execution] = await workflow.getExecutions();
|
|
89
|
-
expect(execution.status).toBe(EXECUTION_STATUS.ERROR);
|
|
90
|
-
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
91
|
-
expect(jobs.length).toBe(2);
|
|
92
|
-
expect(jobs[0].status).toBe(JOB_STATUS.ERROR);
|
|
93
|
-
expect(jobs[0].result).toBe(0);
|
|
94
|
-
expect(jobs[1].status).toBe(JOB_STATUS.ERROR);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
describe('config', () => {
|
|
99
|
-
it('no target just pass', async () => {
|
|
100
|
-
const n1 = await workflow.createNode({
|
|
101
|
-
type: 'loop',
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const n2 = await workflow.createNode({
|
|
105
|
-
type: 'echo',
|
|
106
|
-
upstreamId: n1.id,
|
|
107
|
-
branchIndex: 0,
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const n3 = await workflow.createNode({
|
|
111
|
-
type: 'echo',
|
|
112
|
-
upstreamId: n1.id,
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
await n1.setDownstream(n3);
|
|
116
|
-
|
|
117
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
118
|
-
|
|
119
|
-
await sleep(500);
|
|
120
|
-
|
|
121
|
-
const [execution] = await workflow.getExecutions();
|
|
122
|
-
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
123
|
-
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
124
|
-
expect(jobs.length).toBe(2);
|
|
125
|
-
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
|
126
|
-
expect(jobs[0].result).toBe(0);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('null target just pass', async () => {
|
|
130
|
-
const n1 = await workflow.createNode({
|
|
131
|
-
type: 'loop',
|
|
132
|
-
config: {
|
|
133
|
-
target: null,
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
const n2 = await workflow.createNode({
|
|
138
|
-
type: 'echo',
|
|
139
|
-
upstreamId: n1.id,
|
|
140
|
-
branchIndex: 0,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const n3 = await workflow.createNode({
|
|
144
|
-
type: 'echo',
|
|
145
|
-
upstreamId: n1.id,
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
await n1.setDownstream(n3);
|
|
149
|
-
|
|
150
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
151
|
-
|
|
152
|
-
await sleep(500);
|
|
153
|
-
|
|
154
|
-
const [execution] = await workflow.getExecutions();
|
|
155
|
-
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
156
|
-
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
157
|
-
expect(jobs.length).toBe(2);
|
|
158
|
-
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
|
159
|
-
expect(jobs[0].result).toBe(0);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('empty array just pass', async () => {
|
|
163
|
-
const n1 = await workflow.createNode({
|
|
164
|
-
type: 'loop',
|
|
165
|
-
config: {
|
|
166
|
-
target: [],
|
|
167
|
-
},
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
const n2 = await workflow.createNode({
|
|
171
|
-
type: 'echo',
|
|
172
|
-
upstreamId: n1.id,
|
|
173
|
-
branchIndex: 0,
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
const n3 = await workflow.createNode({
|
|
177
|
-
type: 'echo',
|
|
178
|
-
upstreamId: n1.id,
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
await n1.setDownstream(n3);
|
|
182
|
-
|
|
183
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
184
|
-
|
|
185
|
-
await sleep(500);
|
|
186
|
-
|
|
187
|
-
const [execution] = await workflow.getExecutions();
|
|
188
|
-
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
189
|
-
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
190
|
-
expect(jobs.length).toBe(2);
|
|
191
|
-
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
|
192
|
-
expect(jobs[0].result).toBe(0);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('target is number, cycle number times', async () => {
|
|
196
|
-
const n1 = await workflow.createNode({
|
|
197
|
-
type: 'loop',
|
|
198
|
-
config: {
|
|
199
|
-
target: 2.5,
|
|
200
|
-
},
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
const n2 = await workflow.createNode({
|
|
204
|
-
type: 'echo',
|
|
205
|
-
upstreamId: n1.id,
|
|
206
|
-
branchIndex: 0,
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
const n3 = await workflow.createNode({
|
|
210
|
-
type: 'echo',
|
|
211
|
-
upstreamId: n1.id,
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
await n1.setDownstream(n3);
|
|
215
|
-
|
|
216
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
217
|
-
|
|
218
|
-
await sleep(500);
|
|
219
|
-
|
|
220
|
-
const [execution] = await workflow.getExecutions();
|
|
221
|
-
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
222
|
-
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
223
|
-
|
|
224
|
-
expect(jobs.length).toBe(4);
|
|
225
|
-
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
|
226
|
-
expect(jobs[0].result).toBe(2);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('target is no array, set as an array', async () => {
|
|
230
|
-
const n1 = await workflow.createNode({
|
|
231
|
-
type: 'loop',
|
|
232
|
-
config: {
|
|
233
|
-
target: {},
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
const n2 = await workflow.createNode({
|
|
238
|
-
type: 'echo',
|
|
239
|
-
upstreamId: n1.id,
|
|
240
|
-
branchIndex: 0,
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
const n3 = await workflow.createNode({
|
|
244
|
-
type: 'echo',
|
|
245
|
-
upstreamId: n1.id,
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
await n1.setDownstream(n3);
|
|
249
|
-
|
|
250
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
251
|
-
|
|
252
|
-
await sleep(500);
|
|
253
|
-
|
|
254
|
-
const [execution] = await workflow.getExecutions();
|
|
255
|
-
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
256
|
-
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
257
|
-
|
|
258
|
-
expect(jobs.length).toBe(3);
|
|
259
|
-
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
|
260
|
-
expect(jobs[0].result).toBe(1);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('multiple targets', async () => {
|
|
264
|
-
const n1 = await workflow.createNode({
|
|
265
|
-
type: 'loop',
|
|
266
|
-
config: {
|
|
267
|
-
target: [1, 2],
|
|
268
|
-
},
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const n2 = await workflow.createNode({
|
|
272
|
-
type: 'echo',
|
|
273
|
-
upstreamId: n1.id,
|
|
274
|
-
branchIndex: 0,
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
const n3 = await workflow.createNode({
|
|
278
|
-
type: 'echo',
|
|
279
|
-
upstreamId: n1.id,
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
await n1.setDownstream(n3);
|
|
283
|
-
|
|
284
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
285
|
-
|
|
286
|
-
await sleep(500);
|
|
287
|
-
|
|
288
|
-
const [execution] = await workflow.getExecutions();
|
|
289
|
-
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
290
|
-
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
291
|
-
|
|
292
|
-
expect(jobs.length).toBe(4);
|
|
293
|
-
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
|
294
|
-
expect(jobs[0].result).toBe(2);
|
|
295
|
-
expect(jobs.filter((j) => j.nodeId === n2.id).length).toBe(2);
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
describe('scope variable', () => {
|
|
300
|
-
it('item.key', async () => {
|
|
301
|
-
const n1 = await workflow.createNode({
|
|
302
|
-
type: 'loop',
|
|
303
|
-
config: {
|
|
304
|
-
target: '{{$context.data.comments}}',
|
|
305
|
-
},
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
const n2 = await workflow.createNode({
|
|
309
|
-
type: 'calculation',
|
|
310
|
-
config: {
|
|
311
|
-
engine: 'formula.js',
|
|
312
|
-
expression: `{{$scopes.${n1.id}.item.content}}`,
|
|
313
|
-
},
|
|
314
|
-
upstreamId: n1.id,
|
|
315
|
-
branchIndex: 0,
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
const post = await PostRepo.create({
|
|
319
|
-
values: {
|
|
320
|
-
title: 't1',
|
|
321
|
-
comments: [{ content: 'c1' }, { content: 'c2' }],
|
|
322
|
-
},
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
await sleep(500);
|
|
326
|
-
|
|
327
|
-
const [execution] = await workflow.getExecutions();
|
|
328
|
-
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
329
|
-
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
330
|
-
expect(jobs.length).toBe(3);
|
|
331
|
-
expect(jobs[1].result).toBe('c1');
|
|
332
|
-
expect(jobs[2].result).toBe('c2');
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
describe('mixed', () => {
|
|
337
|
-
it.skip('loop branch contains parallel branches', async () => {
|
|
338
|
-
const n1 = await workflow.createNode({
|
|
339
|
-
type: 'loop',
|
|
340
|
-
config: {
|
|
341
|
-
target: 2,
|
|
342
|
-
},
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
const n2 = await workflow.createNode({
|
|
346
|
-
type: 'parallel',
|
|
347
|
-
branchIndex: 0,
|
|
348
|
-
upstreamId: n1.id,
|
|
349
|
-
config: {
|
|
350
|
-
mode: 'any',
|
|
351
|
-
},
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
const n3 = await workflow.createNode({
|
|
355
|
-
type: 'condition',
|
|
356
|
-
config: {
|
|
357
|
-
rejectOnFalse: true,
|
|
358
|
-
calculation: {
|
|
359
|
-
calculator: '<',
|
|
360
|
-
operands: [`{{$scopes.${n1.id}.item}}`, 1],
|
|
361
|
-
},
|
|
362
|
-
},
|
|
363
|
-
branchIndex: 0,
|
|
364
|
-
upstreamId: n2.id,
|
|
365
|
-
});
|
|
366
|
-
const n4 = await workflow.createNode({
|
|
367
|
-
type: 'echo',
|
|
368
|
-
upstreamId: n3.id,
|
|
369
|
-
});
|
|
370
|
-
await n3.setDownstream(n4);
|
|
371
|
-
|
|
372
|
-
const n5 = await workflow.createNode({
|
|
373
|
-
type: 'condition',
|
|
374
|
-
config: {
|
|
375
|
-
rejectOnFalse: true,
|
|
376
|
-
calculation: {
|
|
377
|
-
calculator: '<',
|
|
378
|
-
operands: [`{{$scopes.${n1.id}.item}}`, 1],
|
|
379
|
-
},
|
|
380
|
-
},
|
|
381
|
-
branchIndex: 1,
|
|
382
|
-
upstreamId: n2.id,
|
|
383
|
-
});
|
|
384
|
-
const n6 = await workflow.createNode({
|
|
385
|
-
type: 'echo',
|
|
386
|
-
upstreamId: n5.id,
|
|
387
|
-
});
|
|
388
|
-
await n5.setDownstream(n6);
|
|
389
|
-
|
|
390
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
391
|
-
|
|
392
|
-
await sleep(1000);
|
|
393
|
-
|
|
394
|
-
const [e1] = await workflow.getExecutions();
|
|
395
|
-
expect(e1.status).toEqual(EXECUTION_STATUS.FAILED);
|
|
396
|
-
const e1jobs = await e1.getJobs();
|
|
397
|
-
expect(e1jobs.length).toBe(7);
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
it('condition contains loop (target as 0)', async () => {
|
|
401
|
-
const n1 = await workflow.createNode({
|
|
402
|
-
type: 'condition',
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
const n2 = await workflow.createNode({
|
|
406
|
-
type: 'loop',
|
|
407
|
-
branchIndex: 1,
|
|
408
|
-
upstreamId: n1.id,
|
|
409
|
-
config: {
|
|
410
|
-
target: 0,
|
|
411
|
-
},
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
const n3 = await workflow.createNode({
|
|
415
|
-
type: 'echo',
|
|
416
|
-
branchIndex: 0,
|
|
417
|
-
upstreamId: n2.id,
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
const n4 = await workflow.createNode({
|
|
421
|
-
type: 'echo',
|
|
422
|
-
upstreamId: n1.id,
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
await n1.setDownstream(n4);
|
|
426
|
-
|
|
427
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
428
|
-
|
|
429
|
-
await sleep(500);
|
|
430
|
-
|
|
431
|
-
const [e1] = await workflow.getExecutions();
|
|
432
|
-
expect(e1.status).toEqual(EXECUTION_STATUS.RESOLVED);
|
|
433
|
-
const jobs = await e1.getJobs({ order: [['id', 'ASC']] });
|
|
434
|
-
expect(jobs.length).toBe(3);
|
|
435
|
-
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
it('condition contains loop (target as 2)', async () => {
|
|
439
|
-
const n1 = await workflow.createNode({
|
|
440
|
-
type: 'condition',
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
const n2 = await workflow.createNode({
|
|
444
|
-
type: 'loop',
|
|
445
|
-
branchIndex: 1,
|
|
446
|
-
upstreamId: n1.id,
|
|
447
|
-
config: {
|
|
448
|
-
target: 2,
|
|
449
|
-
},
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
const n3 = await workflow.createNode({
|
|
453
|
-
type: 'echo',
|
|
454
|
-
branchIndex: 0,
|
|
455
|
-
upstreamId: n2.id,
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
const n4 = await workflow.createNode({
|
|
459
|
-
type: 'echo',
|
|
460
|
-
upstreamId: n1.id,
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
await n1.setDownstream(n4);
|
|
464
|
-
|
|
465
|
-
const post = await PostRepo.create({ values: { title: 't1' } });
|
|
466
|
-
|
|
467
|
-
await sleep(500);
|
|
468
|
-
|
|
469
|
-
const [e1] = await workflow.getExecutions();
|
|
470
|
-
expect(e1.status).toEqual(EXECUTION_STATUS.RESOLVED);
|
|
471
|
-
const jobs = await e1.getJobs({ order: [['id', 'ASC']] });
|
|
472
|
-
expect(jobs.length).toBe(5);
|
|
473
|
-
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
|
474
|
-
});
|
|
475
|
-
});
|
|
476
|
-
});
|
package/src/server/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './Plugin';
|