@ldsg/workflow 0.3.8 → 0.3.10
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/__tests__/unit/resource.js +62 -61
- package/dist/constants.d.ts +3 -3
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +11 -11
- package/dist/handler.d.ts +5 -7
- package/dist/handler.d.ts.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/instantiate-resource.d.ts +4 -4
- package/dist/instantiate-resource.js +1 -1
- package/dist/resource.d.ts +2 -2
- package/dist/resource.d.ts.map +1 -1
- package/dist/resource.js +64 -51
- package/dist/types/index.d.ts +6 -6
- package/dist/types/json_schema_with_expression_extension.d.ts +8 -6
- package/dist/types/json_schema_with_expression_extension.d.ts.map +1 -1
- package/dist/types/node.d.ts +11 -11
- package/dist/types/node.d.ts.map +1 -1
- package/dist/types/specific-resource-settings.d.ts +1 -1
- package/dist/types/specific-resource-settings.d.ts.map +1 -1
- package/dist/types/workflow.d.ts +7 -3
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/utils/evaluate-variables/constants.d.ts.map +1 -1
- package/dist/utils/evaluate-variables/constants.js +17 -17
- package/dist/utils/evaluate-variables/index.d.ts +2 -2
- package/dist/utils/evaluate-variables/index.d.ts.map +1 -1
- package/dist/utils/evaluate-variables/index.debug.d.ts +2 -0
- package/dist/utils/evaluate-variables/index.debug.d.ts.map +1 -0
- package/dist/utils/evaluate-variables/index.debug.js +194 -0
- package/dist/utils/evaluate-variables/index.js +5 -5
- package/dist/utils/evaluate-variables/index.test.d.ts +2 -0
- package/dist/utils/evaluate-variables/index.test.d.ts.map +1 -0
- package/dist/utils/evaluate-variables/index.test.js +219 -0
- package/dist/utils/evaluate-variables/utils/get-data-with-json-schema-and-input/index.d.ts +21 -0
- package/dist/utils/evaluate-variables/utils/get-data-with-json-schema-and-input/index.d.ts.map +1 -0
- package/dist/utils/evaluate-variables/utils/get-data-with-json-schema-and-input/index.js +17 -0
- package/dist/utils/evaluate-variables/utils/{resolve-json-schema.d.ts → get-data-with-json-schema-and-input/resolve-json-schema.d.ts} +10 -7
- package/dist/utils/evaluate-variables/utils/get-data-with-json-schema-and-input/resolve-json-schema.d.ts.map +1 -0
- package/dist/utils/evaluate-variables/utils/get-data-with-json-schema-and-input/resolve-json-schema.js +73 -0
- package/dist/utils/evaluate-variables/utils/get-field-path-by-field-type-key.d.ts.map +1 -1
- package/dist/utils/evaluate-variables/utils/get-field-path-by-field-type-key.js +3 -3
- package/dist/utils/get-execution-levels.d.ts +17 -0
- package/dist/utils/get-execution-levels.d.ts.map +1 -0
- package/dist/utils/get-execution-levels.js +136 -0
- package/dist/utils/get-execution-levels.test.d.ts +2 -0
- package/dist/utils/get-execution-levels.test.d.ts.map +1 -0
- package/dist/utils/get-execution-levels.test.js +228 -0
- package/package.json +22 -13
- package/dist/utils/evaluate-variables/utils/resolve-json-schema.d.ts.map +0 -1
- package/dist/utils/evaluate-variables/utils/resolve-json-schema.js +0 -112
- package/dist/utils/evaluate-variables.test.d.ts +0 -2
- package/dist/utils/evaluate-variables.test.d.ts.map +0 -1
- package/dist/utils/evaluate-variables.test.js +0 -219
- package/dist/utils/get-ordered-node-list.d.ts +0 -19
- package/dist/utils/get-ordered-node-list.d.ts.map +0 -1
- package/dist/utils/get-ordered-node-list.js +0 -41
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -17
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveJsonSchema = void 0;
|
|
7
|
+
const jsonata_1 = __importDefault(require("jsonata"));
|
|
8
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
9
|
+
const resolveJsonSchema = async (params) => {
|
|
10
|
+
var _a;
|
|
11
|
+
const { input, jsonSchema, source } = params;
|
|
12
|
+
const { type: jsonSchemaType, 'x-expression': jsonSchemaExpression, const: jsonSchemaConst } = jsonSchema;
|
|
13
|
+
let data;
|
|
14
|
+
/**
|
|
15
|
+
* 若常量值存在,则直接返回
|
|
16
|
+
*/
|
|
17
|
+
if (jsonSchemaConst) {
|
|
18
|
+
data = jsonSchemaConst;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
data = source;
|
|
22
|
+
/**
|
|
23
|
+
* 若表达式存在,则尝试获取表达式值
|
|
24
|
+
*/
|
|
25
|
+
if (jsonSchemaExpression) {
|
|
26
|
+
const expression = (0, jsonata_1.default)(jsonSchemaExpression);
|
|
27
|
+
data = await expression.evaluate(input);
|
|
28
|
+
}
|
|
29
|
+
switch (jsonSchemaType) {
|
|
30
|
+
case 'object': {
|
|
31
|
+
data = data !== null && data !== void 0 ? data : {};
|
|
32
|
+
const jsonSchemaProperties = (_a = jsonSchema.properties) !== null && _a !== void 0 ? _a : {};
|
|
33
|
+
const keys = lodash_1.default.keys(jsonSchemaProperties);
|
|
34
|
+
const keysMapResPromises = keys.map(async (key) => {
|
|
35
|
+
const source = lodash_1.default.get(data, key);
|
|
36
|
+
const resolveJsonSchemaRes = await (0, exports.resolveJsonSchema)({
|
|
37
|
+
jsonSchema: jsonSchemaProperties[key],
|
|
38
|
+
input,
|
|
39
|
+
source
|
|
40
|
+
});
|
|
41
|
+
const res = {
|
|
42
|
+
key,
|
|
43
|
+
resolveJsonSchemaRes
|
|
44
|
+
};
|
|
45
|
+
return res;
|
|
46
|
+
});
|
|
47
|
+
const keysMapRes = await Promise.all(keysMapResPromises);
|
|
48
|
+
lodash_1.default.each(keysMapRes, ({ key, resolveJsonSchemaRes }) => {
|
|
49
|
+
lodash_1.default.set(data, key, resolveJsonSchemaRes.data);
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case 'array': {
|
|
54
|
+
throw new Error('unsupported array type');
|
|
55
|
+
}
|
|
56
|
+
case 'boolean':
|
|
57
|
+
case 'integer':
|
|
58
|
+
case 'number':
|
|
59
|
+
case 'string': {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'null': {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
default: {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const res = { data };
|
|
71
|
+
return res;
|
|
72
|
+
};
|
|
73
|
+
exports.resolveJsonSchema = resolveJsonSchema;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-field-path-by-field-type-key.d.ts","sourceRoot":"","sources":["../../../../src/utils/evaluate-variables/utils/get-field-path-by-field-type-key.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,GAAI,cAAc,MAAM,KAAG,
|
|
1
|
+
{"version":3,"file":"get-field-path-by-field-type-key.d.ts","sourceRoot":"","sources":["../../../../src/utils/evaluate-variables/utils/get-field-path-by-field-type-key.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,GAAI,cAAc,MAAM,KAAG,MAejE,CAAC"}
|
|
@@ -3,14 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getFieldPathByFieldTypeKey = void 0;
|
|
4
4
|
const getFieldPathByFieldTypeKey = (fieldTypeKey) => {
|
|
5
5
|
// 将字符串按点分割成数组
|
|
6
|
-
const splitRes = fieldTypeKey.split(
|
|
6
|
+
const splitRes = fieldTypeKey.split('.');
|
|
7
7
|
// 过滤数组:除了索引为复数且值为 properties ,其他所有元素均保留
|
|
8
8
|
const filterRes = splitRes.filter((value, index) => {
|
|
9
|
-
const res = !(value ===
|
|
9
|
+
const res = !(value === 'properties' && index % 2 === 0);
|
|
10
10
|
return res;
|
|
11
11
|
});
|
|
12
12
|
// 将过滤后的数组重新用点连接成字符串
|
|
13
|
-
const res = filterRes.join(
|
|
13
|
+
const res = filterRes.join('.');
|
|
14
14
|
return res;
|
|
15
15
|
};
|
|
16
16
|
exports.getFieldPathByFieldTypeKey = getFieldPathByFieldTypeKey;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WorkflowData, WorkflowNodeInfo } from '../types';
|
|
2
|
+
interface GetExecutionLevelsParams {
|
|
3
|
+
workflowData: WorkflowData;
|
|
4
|
+
startNode: WorkflowNodeInfo;
|
|
5
|
+
endNode: WorkflowNodeInfo;
|
|
6
|
+
}
|
|
7
|
+
interface GetExecutionLevelsRes {
|
|
8
|
+
executionLevels: WorkflowNodeInfo[][];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 获取执行层级列表,用于并行执行
|
|
12
|
+
* 根据节点依赖关系构建执行层级,同一层级的节点可以并行执行
|
|
13
|
+
* 例如:连线如 1-2、1-3、2-4、3-4、4-5 时,需要 2、3 均执行完成后才执行 4
|
|
14
|
+
*/
|
|
15
|
+
export declare const getExecutionLevels: (params: GetExecutionLevelsParams) => GetExecutionLevelsRes;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=get-execution-levels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-execution-levels.d.ts","sourceRoot":"","sources":["../../src/utils/get-execution-levels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE1D,UAAU,wBAAwB;IAChC,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED,UAAU,qBAAqB;IAC7B,eAAe,EAAE,gBAAgB,EAAE,EAAE,CAAC;CACvC;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,wBAAwB,KAAG,qBAuJrE,CAAC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getExecutionLevels = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 获取执行层级列表,用于并行执行
|
|
6
|
+
* 根据节点依赖关系构建执行层级,同一层级的节点可以并行执行
|
|
7
|
+
* 例如:连线如 1-2、1-3、2-4、3-4、4-5 时,需要 2、3 均执行完成后才执行 4
|
|
8
|
+
*/
|
|
9
|
+
const getExecutionLevels = (params) => {
|
|
10
|
+
const { workflowData, startNode, endNode } = params;
|
|
11
|
+
const { edges, nodes } = workflowData;
|
|
12
|
+
// 构建节点映射
|
|
13
|
+
const nodeMap = new Map();
|
|
14
|
+
nodes.forEach((node) => {
|
|
15
|
+
nodeMap.set(node.id, node);
|
|
16
|
+
});
|
|
17
|
+
// 构建依赖关系图
|
|
18
|
+
const dependencies = new Map(); // 节点 -> 依赖的节点集合
|
|
19
|
+
const dependents = new Map(); // 节点 -> 被依赖的节点集合
|
|
20
|
+
// 初始化所有节点的依赖和被依赖集合
|
|
21
|
+
nodes.forEach((node) => {
|
|
22
|
+
dependencies.set(node.id, new Set());
|
|
23
|
+
dependents.set(node.id, new Set());
|
|
24
|
+
});
|
|
25
|
+
// 根据边构建依赖关系
|
|
26
|
+
edges.forEach((edge) => {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
const { sourceWorkflowNodeResourceId, targetWorkflowNodeResourceId } = edge;
|
|
29
|
+
// target 依赖于 source
|
|
30
|
+
(_a = dependencies.get(targetWorkflowNodeResourceId)) === null || _a === void 0 ? void 0 : _a.add(sourceWorkflowNodeResourceId);
|
|
31
|
+
// source 被 target 依赖
|
|
32
|
+
(_b = dependents.get(sourceWorkflowNodeResourceId)) === null || _b === void 0 ? void 0 : _b.add(targetWorkflowNodeResourceId);
|
|
33
|
+
});
|
|
34
|
+
// 使用拓扑排序构建执行层级
|
|
35
|
+
const executionLevels = [];
|
|
36
|
+
const completedNodes = new Set();
|
|
37
|
+
const remainingDependencies = new Map();
|
|
38
|
+
// 首先进行循环依赖检测
|
|
39
|
+
const visited = new Set();
|
|
40
|
+
const recursionStack = new Set();
|
|
41
|
+
function hasCycle(nodeId) {
|
|
42
|
+
if (recursionStack.has(nodeId)) {
|
|
43
|
+
return true; // 发现循环依赖
|
|
44
|
+
}
|
|
45
|
+
if (visited.has(nodeId)) {
|
|
46
|
+
return false; // 已经检查过,没有循环
|
|
47
|
+
}
|
|
48
|
+
visited.add(nodeId);
|
|
49
|
+
recursionStack.add(nodeId);
|
|
50
|
+
const deps = dependencies.get(nodeId);
|
|
51
|
+
if (deps) {
|
|
52
|
+
for (const dep of deps) {
|
|
53
|
+
if (hasCycle(dep)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
recursionStack.delete(nodeId);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// 检查所有节点是否存在循环依赖
|
|
62
|
+
for (const nodeId of dependencies.keys()) {
|
|
63
|
+
if (hasCycle(nodeId)) {
|
|
64
|
+
throw new Error('Circular dependency detected in workflow');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// 复制依赖关系到剩余依赖映射
|
|
68
|
+
dependencies.forEach((deps, nodeId) => {
|
|
69
|
+
remainingDependencies.set(nodeId, new Set(deps));
|
|
70
|
+
});
|
|
71
|
+
// 开始节点必须在第一层
|
|
72
|
+
const firstLevel = [startNode];
|
|
73
|
+
executionLevels.push(firstLevel);
|
|
74
|
+
completedNodes.add(startNode.id);
|
|
75
|
+
// 更新剩余依赖
|
|
76
|
+
remainingDependencies.delete(startNode.id);
|
|
77
|
+
remainingDependencies.forEach((deps) => {
|
|
78
|
+
deps.delete(startNode.id);
|
|
79
|
+
});
|
|
80
|
+
// 构建后续层级
|
|
81
|
+
while (remainingDependencies.size > 0) {
|
|
82
|
+
const currentLevel = [];
|
|
83
|
+
const nodesToProcess = [];
|
|
84
|
+
// 找到所有依赖已满足的节点
|
|
85
|
+
remainingDependencies.forEach((deps, nodeId) => {
|
|
86
|
+
if (deps.size === 0) {
|
|
87
|
+
nodesToProcess.push(nodeId);
|
|
88
|
+
const node = nodeMap.get(nodeId);
|
|
89
|
+
if (node) {
|
|
90
|
+
currentLevel.push(node);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// 如果没有找到可以执行的节点,说明存在循环依赖
|
|
95
|
+
if (currentLevel.length === 0) {
|
|
96
|
+
throw new Error('Circular dependency detected in workflow');
|
|
97
|
+
}
|
|
98
|
+
executionLevels.push(currentLevel);
|
|
99
|
+
// 标记当前层级的节点为已完成
|
|
100
|
+
nodesToProcess.forEach((nodeId) => {
|
|
101
|
+
completedNodes.add(nodeId);
|
|
102
|
+
remainingDependencies.delete(nodeId);
|
|
103
|
+
});
|
|
104
|
+
// 更新剩余依赖
|
|
105
|
+
const remainingDepsArray = Array.from(remainingDependencies.entries());
|
|
106
|
+
remainingDepsArray.forEach(([nodeId, deps]) => {
|
|
107
|
+
nodesToProcess.forEach((processedNodeId) => {
|
|
108
|
+
deps.delete(processedNodeId);
|
|
109
|
+
});
|
|
110
|
+
remainingDependencies.set(nodeId, deps);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// 确保结束节点在最后一层
|
|
114
|
+
if (executionLevels.length > 0) {
|
|
115
|
+
const lastLevel = executionLevels[executionLevels.length - 1];
|
|
116
|
+
const endNodeInLastLevel = lastLevel.some((node) => node.id === endNode.id);
|
|
117
|
+
if (!endNodeInLastLevel) {
|
|
118
|
+
// 如果结束节点不在最后一层,需要检查是否已经在某一层中
|
|
119
|
+
let endNodeFound = false;
|
|
120
|
+
for (const level of executionLevels) {
|
|
121
|
+
if (level.some((node) => node.id === endNode.id)) {
|
|
122
|
+
endNodeFound = true;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (!endNodeFound) {
|
|
127
|
+
// 如果结束节点不在任何层级中,添加到最后一层
|
|
128
|
+
executionLevels.push([endNode]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
executionLevels
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
exports.getExecutionLevels = getExecutionLevels;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-execution-levels.test.d.ts","sourceRoot":"","sources":["../../src/utils/get-execution-levels.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const get_execution_levels_1 = require("./get-execution-levels");
|
|
4
|
+
// 创建测试节点
|
|
5
|
+
const createTestNode = (id) => ({
|
|
6
|
+
id,
|
|
7
|
+
propertiesSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
input: { type: 'object' },
|
|
11
|
+
output: { type: 'object' }
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
execute: async () => ({ variables: {} })
|
|
15
|
+
});
|
|
16
|
+
describe('getExecutionLevels', () => {
|
|
17
|
+
test('should create correct execution levels for parallel workflow', () => {
|
|
18
|
+
// 测试场景:1-2, 1-3, 2-4, 3-4, 4-5
|
|
19
|
+
// 期望的层级:
|
|
20
|
+
// Level 0: [1]
|
|
21
|
+
// Level 1: [2, 3] (并行)
|
|
22
|
+
// Level 2: [4] (需要2,3都完成)
|
|
23
|
+
// Level 3: [5]
|
|
24
|
+
const nodes = [
|
|
25
|
+
createTestNode('node-1'),
|
|
26
|
+
createTestNode('node-2'),
|
|
27
|
+
createTestNode('node-3'),
|
|
28
|
+
createTestNode('node-4'),
|
|
29
|
+
createTestNode('node-5')
|
|
30
|
+
];
|
|
31
|
+
const workflowData = {
|
|
32
|
+
edges: [
|
|
33
|
+
{
|
|
34
|
+
sourceWorkflowNodeResourceId: 'node-1',
|
|
35
|
+
targetWorkflowNodeResourceId: 'node-2'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
sourceWorkflowNodeResourceId: 'node-1',
|
|
39
|
+
targetWorkflowNodeResourceId: 'node-3'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
sourceWorkflowNodeResourceId: 'node-2',
|
|
43
|
+
targetWorkflowNodeResourceId: 'node-4'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
sourceWorkflowNodeResourceId: 'node-3',
|
|
47
|
+
targetWorkflowNodeResourceId: 'node-4'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
sourceWorkflowNodeResourceId: 'node-4',
|
|
51
|
+
targetWorkflowNodeResourceId: 'node-5'
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
nodes
|
|
55
|
+
};
|
|
56
|
+
const startNode = nodes[0]; // node-1
|
|
57
|
+
const endNode = nodes[4]; // node-5
|
|
58
|
+
const { executionLevels } = (0, get_execution_levels_1.getExecutionLevels)({
|
|
59
|
+
workflowData,
|
|
60
|
+
startNode,
|
|
61
|
+
endNode
|
|
62
|
+
});
|
|
63
|
+
// 验证层级数量
|
|
64
|
+
expect(executionLevels.length).toBe(4);
|
|
65
|
+
// 验证第一层(只有node-1)
|
|
66
|
+
expect(executionLevels[0]).toHaveLength(1);
|
|
67
|
+
expect(executionLevels[0][0].id).toBe('node-1');
|
|
68
|
+
// 验证第二层(node-2和node-3,可以并行)
|
|
69
|
+
expect(executionLevels[1]).toHaveLength(2);
|
|
70
|
+
const level2NodeIds = executionLevels[1].map((node) => node.id).sort();
|
|
71
|
+
expect(level2NodeIds).toEqual(['node-2', 'node-3']);
|
|
72
|
+
// 验证第三层(只有node-4,需要2,3都完成)
|
|
73
|
+
expect(executionLevels[2]).toHaveLength(1);
|
|
74
|
+
expect(executionLevels[2][0].id).toBe('node-4');
|
|
75
|
+
// 验证第四层(只有node-5)
|
|
76
|
+
expect(executionLevels[3]).toHaveLength(1);
|
|
77
|
+
expect(executionLevels[3][0].id).toBe('node-5');
|
|
78
|
+
});
|
|
79
|
+
test('should handle simple sequential workflow', () => {
|
|
80
|
+
// 测试简单的顺序工作流:1-2-3
|
|
81
|
+
const nodes = [
|
|
82
|
+
createTestNode('node-1'),
|
|
83
|
+
createTestNode('node-2'),
|
|
84
|
+
createTestNode('node-3')
|
|
85
|
+
];
|
|
86
|
+
const workflowData = {
|
|
87
|
+
edges: [
|
|
88
|
+
{
|
|
89
|
+
sourceWorkflowNodeResourceId: 'node-1',
|
|
90
|
+
targetWorkflowNodeResourceId: 'node-2'
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
sourceWorkflowNodeResourceId: 'node-2',
|
|
94
|
+
targetWorkflowNodeResourceId: 'node-3'
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
nodes
|
|
98
|
+
};
|
|
99
|
+
const startNode = nodes[0]; // node-1
|
|
100
|
+
const endNode = nodes[2]; // node-3
|
|
101
|
+
const { executionLevels } = (0, get_execution_levels_1.getExecutionLevels)({
|
|
102
|
+
workflowData,
|
|
103
|
+
startNode,
|
|
104
|
+
endNode
|
|
105
|
+
});
|
|
106
|
+
// 验证层级数量
|
|
107
|
+
expect(executionLevels.length).toBe(3);
|
|
108
|
+
// 每层只有一个节点
|
|
109
|
+
executionLevels.forEach((level, index) => {
|
|
110
|
+
expect(level).toHaveLength(1);
|
|
111
|
+
expect(level[0].id).toBe(`node-${index + 1}`);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
test('should handle complex parallel workflow', () => {
|
|
115
|
+
// 测试更复杂的并行工作流:1-2, 1-3, 1-4, 2-5, 3-5, 4-6, 5-6, 6-7
|
|
116
|
+
// 期望的层级:
|
|
117
|
+
// Level 0: [1]
|
|
118
|
+
// Level 1: [2, 3, 4] (并行)
|
|
119
|
+
// Level 2: [5] (需要2,3完成)
|
|
120
|
+
// Level 3: [6] (需要4,5完成)
|
|
121
|
+
// Level 4: [7]
|
|
122
|
+
const nodes = [
|
|
123
|
+
createTestNode('node-1'),
|
|
124
|
+
createTestNode('node-2'),
|
|
125
|
+
createTestNode('node-3'),
|
|
126
|
+
createTestNode('node-4'),
|
|
127
|
+
createTestNode('node-5'),
|
|
128
|
+
createTestNode('node-6'),
|
|
129
|
+
createTestNode('node-7')
|
|
130
|
+
];
|
|
131
|
+
const workflowData = {
|
|
132
|
+
edges: [
|
|
133
|
+
{
|
|
134
|
+
sourceWorkflowNodeResourceId: 'node-1',
|
|
135
|
+
targetWorkflowNodeResourceId: 'node-2'
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
sourceWorkflowNodeResourceId: 'node-1',
|
|
139
|
+
targetWorkflowNodeResourceId: 'node-3'
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
sourceWorkflowNodeResourceId: 'node-1',
|
|
143
|
+
targetWorkflowNodeResourceId: 'node-4'
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
sourceWorkflowNodeResourceId: 'node-2',
|
|
147
|
+
targetWorkflowNodeResourceId: 'node-5'
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
sourceWorkflowNodeResourceId: 'node-3',
|
|
151
|
+
targetWorkflowNodeResourceId: 'node-5'
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
sourceWorkflowNodeResourceId: 'node-4',
|
|
155
|
+
targetWorkflowNodeResourceId: 'node-6'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
sourceWorkflowNodeResourceId: 'node-5',
|
|
159
|
+
targetWorkflowNodeResourceId: 'node-6'
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
sourceWorkflowNodeResourceId: 'node-6',
|
|
163
|
+
targetWorkflowNodeResourceId: 'node-7'
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
nodes
|
|
167
|
+
};
|
|
168
|
+
const startNode = nodes[0]; // node-1
|
|
169
|
+
const endNode = nodes[6]; // node-7
|
|
170
|
+
const { executionLevels } = (0, get_execution_levels_1.getExecutionLevels)({
|
|
171
|
+
workflowData,
|
|
172
|
+
startNode,
|
|
173
|
+
endNode
|
|
174
|
+
});
|
|
175
|
+
// 验证层级数量
|
|
176
|
+
expect(executionLevels.length).toBe(5);
|
|
177
|
+
// 验证第一层(只有node-1)
|
|
178
|
+
expect(executionLevels[0]).toHaveLength(1);
|
|
179
|
+
expect(executionLevels[0][0].id).toBe('node-1');
|
|
180
|
+
// 验证第二层(node-2, node-3, node-4,可以并行)
|
|
181
|
+
expect(executionLevels[1]).toHaveLength(3);
|
|
182
|
+
const level2NodeIds = executionLevels[1].map((node) => node.id).sort();
|
|
183
|
+
expect(level2NodeIds).toEqual(['node-2', 'node-3', 'node-4']);
|
|
184
|
+
// 验证第三层(只有node-5,需要2,3完成)
|
|
185
|
+
expect(executionLevels[2]).toHaveLength(1);
|
|
186
|
+
expect(executionLevels[2][0].id).toBe('node-5');
|
|
187
|
+
// 验证第四层(只有node-6,需要4,5完成)
|
|
188
|
+
expect(executionLevels[3]).toHaveLength(1);
|
|
189
|
+
expect(executionLevels[3][0].id).toBe('node-6');
|
|
190
|
+
// 验证第五层(只有node-7)
|
|
191
|
+
expect(executionLevels[4]).toHaveLength(1);
|
|
192
|
+
expect(executionLevels[4][0].id).toBe('node-7');
|
|
193
|
+
});
|
|
194
|
+
test('should throw error for circular dependency', () => {
|
|
195
|
+
// 测试循环依赖:1-2, 2-3, 3-1
|
|
196
|
+
const nodes = [
|
|
197
|
+
createTestNode('node-1'),
|
|
198
|
+
createTestNode('node-2'),
|
|
199
|
+
createTestNode('node-3')
|
|
200
|
+
];
|
|
201
|
+
const workflowData = {
|
|
202
|
+
edges: [
|
|
203
|
+
{
|
|
204
|
+
sourceWorkflowNodeResourceId: 'node-1',
|
|
205
|
+
targetWorkflowNodeResourceId: 'node-2'
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
sourceWorkflowNodeResourceId: 'node-2',
|
|
209
|
+
targetWorkflowNodeResourceId: 'node-3'
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
sourceWorkflowNodeResourceId: 'node-3',
|
|
213
|
+
targetWorkflowNodeResourceId: 'node-1'
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
nodes
|
|
217
|
+
};
|
|
218
|
+
const startNode = nodes[0]; // node-1
|
|
219
|
+
const endNode = nodes[2]; // node-3
|
|
220
|
+
expect(() => {
|
|
221
|
+
(0, get_execution_levels_1.getExecutionLevels)({
|
|
222
|
+
workflowData,
|
|
223
|
+
startNode,
|
|
224
|
+
endNode
|
|
225
|
+
});
|
|
226
|
+
}).toThrow('Circular dependency detected in workflow');
|
|
227
|
+
});
|
|
228
|
+
});
|
package/package.json
CHANGED
|
@@ -1,31 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ldsg/workflow",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "wanxger",
|
|
5
8
|
"main": "dist/index.js",
|
|
9
|
+
"types": "dist",
|
|
6
10
|
"files": [
|
|
7
11
|
"dist/"
|
|
8
12
|
],
|
|
9
|
-
"types": "dist",
|
|
10
|
-
"keywords": [],
|
|
11
|
-
"author": "wanxger",
|
|
12
|
-
"license": "MIT",
|
|
13
|
-
"publishConfig": {
|
|
14
|
-
"access": "public"
|
|
15
|
-
},
|
|
16
13
|
"dependencies": {
|
|
17
14
|
"json-schema": "0.4.0",
|
|
18
15
|
"json-schema-default": "^1.0.2",
|
|
16
|
+
"jsonata": "^2.0.6",
|
|
19
17
|
"lodash": "^4.17.21",
|
|
20
|
-
"@ldsg/
|
|
21
|
-
"@ldsg/resource": "0.3.
|
|
22
|
-
"@ldsg/
|
|
18
|
+
"@ldsg/resource": "0.3.10",
|
|
19
|
+
"@ldsg/resource-definition": "0.3.10",
|
|
20
|
+
"@ldsg/constants": "0.3.10",
|
|
21
|
+
"@ldsg/handler": "0.3.10"
|
|
23
22
|
},
|
|
24
23
|
"devDependencies": {
|
|
25
|
-
"@types/
|
|
24
|
+
"@types/fs-extra": "^11.0.4",
|
|
25
|
+
"@types/jest": "^29.5.2",
|
|
26
26
|
"@types/json-schema": "^7.0.15",
|
|
27
27
|
"@types/lodash": "^4.14.202",
|
|
28
|
-
"
|
|
28
|
+
"fs-extra": "^11.3.0",
|
|
29
|
+
"jest": "^29.5.0",
|
|
30
|
+
"supertest": "^6.3.4",
|
|
31
|
+
"ts-jest": "^29.1.5",
|
|
32
|
+
"ts-node": "^10.9.2",
|
|
33
|
+
"typescript": "^5.8.3",
|
|
34
|
+
"@ldsg/types": "0.3.10"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
29
38
|
},
|
|
30
39
|
"scripts": {
|
|
31
40
|
"build": "tsc --project tsconfig.build.json",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-json-schema.d.ts","sourceRoot":"","sources":["../../../../src/utils/evaluate-variables/utils/resolve-json-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAC;AAO/D,UAAU,uBAAuB;IAC/B,UAAU,EAAE,WAAW,CAAC;IAExB;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC;IAEX;;OAEG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,GAAG,CAAC;IAEZ,IAAI,CAAC,EAAE,GAAG,CAAC;IAEX,OAAO,CAAC,EAAE,GAAG,CAAC;IAEd,UAAU,CAAC,EAAE,GAAG,CAAC;CAClB;AAED,KAAK,iBAAiB,GAAG,CACvB,MAAM,EAAE,uBAAuB,KAC5B,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAEnC,eAAO,MAAM,iBAAiB,EAAE,iBAkK/B,CAAC"}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolveJsonSchema = void 0;
|
|
7
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
8
|
-
const constants_1 = require("../constants");
|
|
9
|
-
const resolveJsonSchema = async (params) => {
|
|
10
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
11
|
-
const { input, jsonSchema, returnExpressionAsData } = params;
|
|
12
|
-
const { type } = jsonSchema;
|
|
13
|
-
const res = {};
|
|
14
|
-
switch (type) {
|
|
15
|
-
case "object": {
|
|
16
|
-
const properties = lodash_1.default.get(jsonSchema, "properties");
|
|
17
|
-
const promises = lodash_1.default.keys(properties).map(async (propertyKey) => {
|
|
18
|
-
const propertyValue = lodash_1.default.get(properties, propertyKey);
|
|
19
|
-
if (!lodash_1.default.isObject(propertyValue)) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const resolveJsonSchemaRes = await (0, exports.resolveJsonSchema)({
|
|
23
|
-
jsonSchema: propertyValue,
|
|
24
|
-
input,
|
|
25
|
-
returnExpressionAsData,
|
|
26
|
-
});
|
|
27
|
-
lodash_1.default.set(res, `data.${propertyKey}`, resolveJsonSchemaRes.data);
|
|
28
|
-
lodash_1.default.set(res, `const.${propertyKey}`, resolveJsonSchemaRes.const);
|
|
29
|
-
lodash_1.default.set(res, `default.${propertyKey}`, resolveJsonSchemaRes.default);
|
|
30
|
-
lodash_1.default.set(res, `expression.${propertyKey}`, resolveJsonSchemaRes.expression);
|
|
31
|
-
});
|
|
32
|
-
await Promise.all(promises);
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
case "array": {
|
|
36
|
-
const itemsType = (_a = jsonSchema.items) === null || _a === void 0 ? void 0 : _a.type;
|
|
37
|
-
let objectExpression;
|
|
38
|
-
if (itemsType === "object") {
|
|
39
|
-
const resolveJsonSchemaRes = await (0, exports.resolveJsonSchema)({
|
|
40
|
-
jsonSchema: jsonSchema.items,
|
|
41
|
-
input,
|
|
42
|
-
returnExpressionAsData: true,
|
|
43
|
-
});
|
|
44
|
-
const expressionSubstrings = [];
|
|
45
|
-
lodash_1.default.each(resolveJsonSchemaRes.expression, (value, key) => {
|
|
46
|
-
if (value) {
|
|
47
|
-
expressionSubstrings.push(`"${key}": ${value}`);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
objectExpression = `{${expressionSubstrings.join(",")}}`;
|
|
51
|
-
}
|
|
52
|
-
const promises = constants_1.FIELD_VALUE_RETRIEVAL_METHODS.map(async (fieldValueRetrievalMethod) => {
|
|
53
|
-
const { name, handle } = fieldValueRetrievalMethod;
|
|
54
|
-
/**
|
|
55
|
-
* 字段的真实 path 是 key 去除类型
|
|
56
|
-
*/
|
|
57
|
-
const value = lodash_1.default.get(jsonSchema, name);
|
|
58
|
-
let res;
|
|
59
|
-
if (!lodash_1.default.isNil(value)) {
|
|
60
|
-
res = await handle({
|
|
61
|
-
input,
|
|
62
|
-
retrievalMethodValue: value,
|
|
63
|
-
objectExpression,
|
|
64
|
-
returnExpressionAsData,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
return res;
|
|
68
|
-
});
|
|
69
|
-
const fieldValues = await Promise.all(promises);
|
|
70
|
-
res.const = (_b = fieldValues.find((value) => !lodash_1.default.isNil(value === null || value === void 0 ? void 0 : value.const))) === null || _b === void 0 ? void 0 : _b.const;
|
|
71
|
-
res.data = (_c = fieldValues.find((value) => !lodash_1.default.isNil(value === null || value === void 0 ? void 0 : value.data))) === null || _c === void 0 ? void 0 : _c.data;
|
|
72
|
-
res.default = (_d = fieldValues.find((value) => !lodash_1.default.isNil(value === null || value === void 0 ? void 0 : value.default))) === null || _d === void 0 ? void 0 : _d.default;
|
|
73
|
-
res.expression = (_e = fieldValues.find((value) => !lodash_1.default.isNil(value === null || value === void 0 ? void 0 : value.expression))) === null || _e === void 0 ? void 0 : _e.expression;
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
case "boolean":
|
|
77
|
-
case "integer":
|
|
78
|
-
case "number":
|
|
79
|
-
case "string": {
|
|
80
|
-
const promises = constants_1.FIELD_VALUE_RETRIEVAL_METHODS.map(async (fieldValueRetrievalMethod) => {
|
|
81
|
-
const { name, handle } = fieldValueRetrievalMethod;
|
|
82
|
-
/**
|
|
83
|
-
* 字段的真实 path 是 key 去除类型
|
|
84
|
-
*/
|
|
85
|
-
const value = lodash_1.default.get(jsonSchema, name);
|
|
86
|
-
let res;
|
|
87
|
-
if (!lodash_1.default.isNil(value)) {
|
|
88
|
-
res = await handle({
|
|
89
|
-
input,
|
|
90
|
-
retrievalMethodValue: value,
|
|
91
|
-
returnExpressionAsData,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
return res;
|
|
95
|
-
});
|
|
96
|
-
const fieldValues = await Promise.all(promises);
|
|
97
|
-
res.const = (_f = fieldValues.find((value) => !lodash_1.default.isNil(value === null || value === void 0 ? void 0 : value.const))) === null || _f === void 0 ? void 0 : _f.const;
|
|
98
|
-
res.data = (_g = fieldValues.find((value) => !lodash_1.default.isNil(value === null || value === void 0 ? void 0 : value.data))) === null || _g === void 0 ? void 0 : _g.data;
|
|
99
|
-
res.default = (_h = fieldValues.find((value) => !lodash_1.default.isNil(value === null || value === void 0 ? void 0 : value.default))) === null || _h === void 0 ? void 0 : _h.default;
|
|
100
|
-
res.expression = (_j = fieldValues.find((value) => !lodash_1.default.isNil(value === null || value === void 0 ? void 0 : value.expression))) === null || _j === void 0 ? void 0 : _j.expression;
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
case "null": {
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
default: {
|
|
107
|
-
throw new Error("invalid field type");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return res;
|
|
111
|
-
};
|
|
112
|
-
exports.resolveJsonSchema = resolveJsonSchema;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"evaluate-variables.test.d.ts","sourceRoot":"","sources":["../../src/utils/evaluate-variables.test.ts"],"names":[],"mappings":""}
|