diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/ExecutionCanvas.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/ExecutionCanvas.tsx
index c2f059a93..a9f39b39e 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/ExecutionCanvas.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/ExecutionCanvas.tsx
@@ -32,6 +32,7 @@ function attachJobs(nodes, jobs: any[] = []): void {
node.jobs.push(item);
item.node = {
id: node.id,
+ key: node.key,
title: node.title,
type: node.type,
};
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/components/ValueBlock.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/components/ValueBlock.tsx
index d25239eb0..13639cebd 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/components/ValueBlock.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/components/ValueBlock.tsx
@@ -32,7 +32,7 @@ function Initializer({ node, resultTitle, insert, ...props }) {
'x-component': 'ValueBlock.Result',
'x-component-props': {
// NOTE: as same format as other reference for migration of revision
- dataSource: `{{$jobsMapByNodeId.${node.id}}}`,
+ dataSource: `{{$jobsMapByNodeKey.${node.key}}}`,
},
},
},
@@ -49,7 +49,10 @@ function Result({ dataSource }) {
return field.title;
}
const result = parse(dataSource)({
- $jobsMapByNodeId: (execution.jobs ?? []).reduce((map, job) => Object.assign(map, { [job.nodeId]: job.result }), {}),
+ $jobsMapByNodeKey: (execution.jobs ?? []).reduce(
+ (map, job) => Object.assign(map, { [job.nodeId]: job.result }),
+ {},
+ ),
});
return (
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/aggregate.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/aggregate.tsx
index 4868250b8..36afbfa8e 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/aggregate.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/aggregate.tsx
@@ -346,7 +346,7 @@ export default {
ValueBlock,
AssociatedConfig,
},
- useVariables({ id, title }, { types, fieldNames = defaultFieldNames }) {
+ useVariables({ key, title }, { types, fieldNames = defaultFieldNames }) {
if (
types &&
!types.some((type) => type in BaseTypeSets || Object.values(BaseTypeSets).some((set) => set.has(type)))
@@ -354,7 +354,7 @@ export default {
return null;
}
return {
- [fieldNames.value]: `${id}`,
+ [fieldNames.value]: key,
[fieldNames.label]: title,
};
},
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/calculation.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/calculation.tsx
index 344037497..3b0e64029 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/calculation.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/calculation.tsx
@@ -1,11 +1,9 @@
import { FormItem, FormLayout } from '@formily/antd-v5';
import { SchemaInitializerItemOptions, Variable, css, defaultFieldNames, useCollectionManager } from '@nocobase/client';
import { Evaluator, evaluators, getOptions } from '@nocobase/evaluators/client';
-import { parse } from '@nocobase/utils/client';
import { Radio } from 'antd';
import React from 'react';
import { useTranslation } from 'react-i18next';
-import { useFlowContext } from '../FlowContext';
import { RadioWithTooltip } from '../components/RadioWithTooltip';
import { ValueBlock } from '../components/ValueBlock';
import { renderEngineReference } from '../components/renderEngineReference';
@@ -167,32 +165,11 @@ export default {
});
return ;
},
- CalculationResult({ dataSource }) {
- const { execution } = useFlowContext();
- if (!execution) {
- return lang('Calculation result');
- }
- const result = parse(dataSource)({
- $jobsMapByNodeId: (execution.jobs ?? []).reduce(
- (map, job) => Object.assign(map, { [job.nodeId]: job.result }),
- {},
- ),
- });
-
- return (
-
- {JSON.stringify(result, null, 2)}
-
- );
- },
RadioWithTooltip,
DynamicConfig,
+ ValueBlock,
},
- useVariables({ id, title }, { types, fieldNames = defaultFieldNames }) {
+ useVariables({ key, title }, { types, fieldNames = defaultFieldNames }) {
if (
types &&
!types.some((type) => type in BaseTypeSets || Object.values(BaseTypeSets).some((set) => set.has(type)))
@@ -200,7 +177,7 @@ export default {
return null;
}
return {
- [fieldNames.value]: `${id}`,
+ [fieldNames.value]: key,
[fieldNames.label]: title,
};
},
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/create.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/create.tsx
index a9467097e..52b076e31 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/create.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/create.tsx
@@ -44,13 +44,12 @@ export default {
components: {
CollectionFieldset,
},
- useVariables({ id, title, config }, options) {
+ useVariables({ key: name, title, config }, options) {
const compile = useCompile();
const { getCollectionFields } = useCollectionManager();
// const depth = config?.params?.appends?.length
// ? config?.params?.appends.reduce((max, item) => Math.max(max, item.split('.').length), 1)
// : 0;
- const name = `${id}`;
const [result] = getCollectionFieldOptions({
// collection: config.collection,
// depth: options?.depth ?? depth,
@@ -83,7 +82,7 @@ export default {
title: node.title ?? `#${node.id}`,
component: CollectionBlockInitializer,
collection: node.config.collection,
- dataSource: `{{$jobsMapByNodeId.${node.id}}}`,
+ dataSource: `{{$jobsMapByNodeKey.${node.key}}}`,
};
},
initializers: {},
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx
index 3115bbd66..6d26bbf43 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx
@@ -13,7 +13,7 @@ import {
useResourceActionContext,
} from '@nocobase/client';
import { Registry, parse, str2moment } from '@nocobase/utils/client';
-import { App, Button, Dropdown, Input, Tag, message } from 'antd';
+import { App, Button, Dropdown, Input, Tag, Tooltip, message } from 'antd';
import React, { useContext, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { AddButton } from '../AddButton';
@@ -199,7 +199,7 @@ export function RemoveButton() {
const template = parse(node.config);
const refs = template.parameters.filter(
- ({ key }) => key.startsWith(`$jobsMapByNodeId.${current.id}.`) || key === `$jobsMapByNodeId.${current.id}`,
+ ({ key }) => key.startsWith(`$jobsMapByNodeKey.${current.key}.`) || key === `$jobsMapByNodeKey.${current.key}`,
);
return refs.length;
});
@@ -371,7 +371,33 @@ export function NodeDefaultView(props) {
},
[`${instruction.type}_${data.id}`]: {
type: 'void',
- title: data.title,
+ title: (
+
+ {data.title}
+
+
+ {data.key}
+
+
+
+ ),
'x-component': 'Action.Drawer',
'x-decorator': 'Form',
'x-decorator-props': {
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/loop.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/loop.tsx
index fa048d54f..98043043a 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/loop.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/loop.tsx
@@ -118,7 +118,7 @@ export default {
// const current = useNodeContext();
// const upstreams = useAvailableUpstreams(current);
// find target data model by path described in `config.target`
- // 1. get options from $context/$jobsMapByNodeId
+ // 1. get options from $context/$jobsMapByNodeKey
// 2. route to sub-options and use as loop target options
let targetOption: VariableOption = {
key: 'item',
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/DetailsBlockProvider.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/DetailsBlockProvider.tsx
index fa69370de..ef3918278 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/DetailsBlockProvider.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/DetailsBlockProvider.tsx
@@ -13,12 +13,17 @@ import { useFlowContext } from '../../FlowContext';
import { parse } from '@nocobase/utils/client';
function useFlowContextData(dataSource) {
- const { execution } = useFlowContext();
+ const { execution, nodes } = useFlowContext();
+
+ const nodesKeyMap = useMemo(() => {
+ return nodes.reduce((map, node) => Object.assign(map, { [node.id]: node.key }), {});
+ }, [nodes]);
+
const data = useMemo(
() => ({
$context: execution?.context,
- $jobsMapByNodeId: (execution?.jobs ?? []).reduce(
- (map, job) => Object.assign(map, { [job.nodeId]: job.result }),
+ $jobsMapByNodeKey: (execution?.jobs ?? []).reduce(
+ (map, job) => Object.assign(map, { [nodesKeyMap[job.nodeId]]: job.result }),
{},
),
}),
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/index.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/index.tsx
index b9b31acd2..445da407e 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/index.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/index.tsx
@@ -85,7 +85,7 @@ export default {
ModeConfig,
AssigneesSelect,
},
- useVariables({ id, title, config }, { types, fieldNames = defaultFieldNames }) {
+ useVariables({ key, title, config }, { types, fieldNames = defaultFieldNames }) {
const compile = useCompile();
const { getCollectionFields } = useCollectionManager();
const formKeys = Object.keys(config.forms ?? {});
@@ -119,7 +119,7 @@ export default {
return options.length
? {
- [fieldNames.value]: `${id}`,
+ [fieldNames.value]: key,
[fieldNames.label]: title,
[fieldNames.children]: options,
}
@@ -143,7 +143,7 @@ export default {
title: form.title ?? formKey,
component: CollectionBlockInitializer,
collection: form.collection,
- dataSource: `{{$jobsMapByNodeId.${node.id}.${formKey}}}`,
+ dataSource: `{{$jobsMapByNodeKey.${node.key}.${formKey}}}`,
} as SchemaInitializerItemOptions)
: null;
})
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/query.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/query.tsx
index f488eed3d..70b2021ce 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/query.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/query.tsx
@@ -89,13 +89,12 @@ export default {
FilterDynamicComponent,
SchemaComponentContext,
},
- useVariables({ id, title, config }, options) {
+ useVariables({ key: name, title, config }, options) {
const compile = useCompile();
const { getCollectionFields } = useCollectionManager();
// const depth = config?.params?.appends?.length
// ? config?.params?.appends.reduce((max, item) => Math.max(max, item.split('.').length), 1)
// : 0;
- const name = `${id}`;
const [result] = getCollectionFieldOptions({
// collection: config.collection,
// depth: options?.depth ?? depth,
@@ -128,7 +127,7 @@ export default {
title: node.title ?? `#${node.id}`,
component: CollectionBlockInitializer,
collection: node.config.collection,
- dataSource: `{{$jobsMapByNodeId.${node.id}}}`,
+ dataSource: `{{$jobsMapByNodeKey.${node.key}}}`,
};
},
initializers: {},
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx
index b0d5c276c..834b65bd9 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx
+++ b/packages/plugins/@nocobase/plugin-workflow/src/client/variable.tsx
@@ -37,7 +37,7 @@ export const defaultFieldNames = { label: 'label', value: 'value', children: 'ch
export const nodesOptions = {
label: `{{t("Node result", { ns: "${NAMESPACE}" })}}`,
- value: '$jobsMapByNodeId',
+ value: '$jobsMapByNodeKey',
useOptions(options: OptionsOfUseVariableOptions) {
const current = useNodeContext();
const upstreams = useAvailableUpstreams(current);
@@ -76,8 +76,8 @@ export const scopeOptions = {
const subOptions = instruction.useScopeVariables?.(node, options);
if (subOptions) {
result.push({
- key: node.id.toString(),
- [fieldNames.value]: node.id.toString(),
+ key: node.key,
+ [fieldNames.value]: node.key,
[fieldNames.label]: node.title ?? `#${node.id}`,
[fieldNames.children]: subOptions,
});
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.ts b/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.ts
index 3eea9b744..45c6584a1 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/locale/zh-CN.ts
@@ -89,6 +89,7 @@ export default {
Advanced: '高级模式',
End: '结束',
'Node result': '节点数据',
+ 'Variable key of node': '节点变量标识',
Calculator: '运算',
'Calculate an expression based on a calculation engine and obtain a value as the result. Variables in the upstream nodes can be used in the expression. The expression can be static or dynamic one from an expression collections.':
'基于计算引擎对一个表达式进行计算,并获得一个值作为结果。表达式中可以使用上游节点里的变量。表达式可以是静态的,也可以是表达式表中的动态表达式。',
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts
index ab953e27e..73b918c19 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts
@@ -30,7 +30,7 @@ export default class Processor {
nodes: FlowNodeModel[] = [];
nodesMap = new Map();
jobsMap = new Map();
- jobsMapByNodeId: { [key: number]: any } = {};
+ jobsMapByNodeKey: { [key: string]: any } = {};
constructor(
public execution: ExecutionModel,
@@ -61,8 +61,9 @@ export default class Processor {
private makeJobs(jobs: Array) {
jobs.forEach((job) => {
this.jobsMap.set(job.id, job);
- // TODO: should consider cycle, and from previous job
- this.jobsMapByNodeId[job.nodeId] = job.result;
+
+ const node = this.nodesMap.get(job.nodeId);
+ this.jobsMapByNodeKey[node.key] = job.result;
});
}
@@ -252,7 +253,9 @@ export default class Processor {
);
}
this.jobsMap.set(job.id, job);
- this.jobsMapByNodeId[job.nodeId] = job.result;
+
+ const node = this.nodesMap.get(job.nodeId);
+ this.jobsMapByNodeKey[node.key] = job.result;
return job;
}
@@ -339,13 +342,13 @@ export default class Processor {
for (let n = this.findBranchParentNode(node); n; n = this.findBranchParentNode(n)) {
const instruction = this.options.plugin.instructions.get(n.type);
if (typeof instruction.getScope === 'function') {
- $scopes[n.id] = instruction.getScope(n, this.jobsMapByNodeId[n.id], this);
+ $scopes[n.id] = $scopes[n.key] = instruction.getScope(n, this.jobsMapByNodeKey[n.key], this);
}
}
return {
$context: this.execution.context,
- $jobsMapByNodeId: this.jobsMapByNodeId,
+ $jobsMapByNodeKey: this.jobsMapByNodeKey,
$system: systemFns,
$scopes,
};
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/actions/workflows.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/actions/workflows.test.ts
index d24f221fe..53b28c0c5 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/actions/workflows.test.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/actions/workflows.test.ts
@@ -260,7 +260,7 @@ describe('workflow > actions > workflows', () => {
type: 'calculation',
config: {
engine: 'math.js',
- expression: `{{$jobsMapByNodeId.${n1.id}.data.read}} + {{$jobsMapByNodeId.${n1.id}.data.read}}`,
+ expression: `{{$jobsMapByNodeKey.${n1.key}.data.read}} + {{$jobsMapByNodeKey.${n1.key}.data.read}}`,
},
upstreamId: n1.id,
});
@@ -279,11 +279,12 @@ describe('workflow > actions > workflows', () => {
const n1_2 = w2.nodes.find((n) => !n.upstreamId);
const n2_2 = w2.nodes.find((n) => !n.downstreamId);
+ expect(n1_2.key).toBe(n1.key);
expect(n1_2.type).toBe('echo');
expect(n2_2.type).toBe('calculation');
expect(n2_2.config).toMatchObject({
engine: 'math.js',
- expression: `{{$jobsMapByNodeId.${n1_2.id}.data.read}} + {{$jobsMapByNodeId.${n1_2.id}.data.read}}`,
+ expression: `{{$jobsMapByNodeKey.${n1_2.key}.data.read}} + {{$jobsMapByNodeKey.${n1_2.key}.data.read}}`,
});
await w2.update({ enabled: true });
@@ -299,47 +300,6 @@ describe('workflow > actions > workflows', () => {
expect(calculation.result).toBe(2);
});
- it('revision with using of deleted nodes', async () => {
- const w1 = await WorkflowModel.create({
- enabled: true,
- type: 'collection',
- config: {
- mode: 1,
- collection: 'posts',
- },
- });
-
- const n2 = await w1.createNode({
- type: 'calculation',
- config: {
- calculation: {
- calculator: 'add',
- operands: [
- {
- type: '$jobsMapByNodeId',
- options: {
- nodeId: 0,
- path: 'data.read',
- },
- },
- {
- value: `{{$jobsMapByNodeId.0.data.read}}`,
- },
- ],
- },
- },
- });
-
- const { status } = await agent.resource(`workflows`).revision({
- filterByTk: w1.id,
- filter: {
- key: w1.key,
- },
- });
-
- expect(status).toBe(400);
- });
-
it('duplicate workflow', async () => {
const w1 = await WorkflowModel.create({
enabled: true,
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/aggregate.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/aggregate.test.ts
index a83a9b153..5b67c9523 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/aggregate.test.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/aggregate.test.ts
@@ -248,7 +248,7 @@ describe('workflow > instructions > aggregate', () => {
associated: true,
association: {
name: 'posts',
- associatedKey: `{{$jobsMapByNodeId.${n1.id}.id}}`,
+ associatedKey: `{{$jobsMapByNodeKey.${n1.key}.id}}`,
associatedCollection: 'tags',
},
params: {
@@ -266,7 +266,7 @@ describe('workflow > instructions > aggregate', () => {
associated: true,
association: {
name: 'posts',
- associatedKey: `{{$jobsMapByNodeId.${n1.id}.id}}`,
+ associatedKey: `{{$jobsMapByNodeKey.${n1.key}.id}}`,
associatedCollection: 'tags',
},
params: {
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/calculation.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/calculation.test.ts
index 5561c0c5a..d9c7cf433 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/calculation.test.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/calculation.test.ts
@@ -88,7 +88,7 @@ describe('workflow > instructions > calculation', () => {
expect(job.result).toBe(2);
});
- it('$jobsMapByNodeId', async () => {
+ it('$jobsMapByNodeKey', async () => {
const n1 = await workflow.createNode({
type: 'echo',
});
@@ -97,7 +97,7 @@ describe('workflow > instructions > calculation', () => {
type: 'calculation',
config: {
engine: 'math.js',
- expression: `{{$jobsMapByNodeId.${n1.id}.data.read}} + 1`,
+ expression: `{{$jobsMapByNodeKey.${n1.key}.data.read}} + 1`,
},
upstreamId: n1.id,
});
@@ -231,7 +231,7 @@ describe('workflow > instructions > calculation', () => {
const n2 = await workflow.createNode({
type: 'calculation',
config: {
- dynamic: `{{$jobsMapByNodeId.${n1.id}}}`,
+ dynamic: `{{$jobsMapByNodeKey.${n1.key}}}`,
scope: '{{$context.data}}',
},
upstreamId: n1.id,
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/manual.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/manual.test.ts
index 50105c1a2..f2699c301 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/manual.test.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/manual.test.ts
@@ -867,7 +867,7 @@ describe('workflow > instructions > manual', () => {
type: 'calculation',
config: {
engine: 'math.js',
- expression: `{{$jobsMapByNodeId.${n1.id}.f1.number}} + 1`,
+ expression: `{{$jobsMapByNodeKey.${n1.key}.f1.number}} + 1`,
},
upstreamId: n1.id,
});
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/query.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/query.test.ts
index 72b3ef4f3..c1d1eb413 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/query.test.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/query.test.ts
@@ -130,7 +130,7 @@ describe('workflow > instructions > query', () => {
collection: 'posts',
params: {
filter: {
- title: `{{$jobsMapByNodeId.${n1.id}.data.title}}`,
+ title: `{{$jobsMapByNodeKey.${n1.key}.data.title}}`,
},
},
},
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/update.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/update.test.ts
index 0ef52a0cb..c20485afc 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/update.test.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/__tests__/instructions/update.test.ts
@@ -78,7 +78,7 @@ describe('workflow > instructions > update', () => {
collection: 'posts',
params: {
filter: {
- id: `{{$jobsMapByNodeId.${n1.id}.id}}`,
+ id: `{{$jobsMapByNodeKey.${n1.key}.id}}`,
},
values: {
title: 'changed',
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/actions/workflows.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/actions/workflows.ts
index a67a237d1..64acbd11e 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/actions/workflows.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/actions/workflows.ts
@@ -1,6 +1,8 @@
import actions, { Context, utils } from '@nocobase/actions';
import { Op, Repository } from '@nocobase/database';
+import Plugin from '../Plugin';
+
export async function update(context: Context, next) {
const repository = utils.getRepositoryFromParams(context) as Repository;
const { filterByTk, values } = context.action.params;
@@ -51,51 +53,8 @@ export async function destroy(context: Context, next) {
next();
}
-function typeOf(value) {
- if (Array.isArray(value)) {
- return 'array';
- } else if (value instanceof Date) {
- return 'date';
- } else if (value === null) {
- return 'null';
- }
-
- return typeof value;
-}
-
-function migrateConfig(config, oldToNew) {
- function migrate(value) {
- switch (typeOf(value)) {
- case 'object':
- if (value.type === '$jobsMapByNodeId') {
- return {
- ...value,
- options: {
- ...value.options,
- nodeId: oldToNew.get(value.options?.nodeId)?.id,
- },
- };
- }
- return Object.keys(value).reduce((result, key) => ({ ...result, [key]: migrate(value[key]) }), {});
- case 'array':
- return value.map((item) => migrate(item));
- case 'string':
- return value.replace(/({{\$jobsMapByNodeId|{{\$scopes)\.([\w-]+)/g, (_, jobVar, oldNodeId) => {
- const newNode = oldToNew.get(Number.parseInt(oldNodeId, 10));
- if (!newNode) {
- throw new Error('node configurated for result is not existed');
- }
- return `${jobVar}.${newNode.id}`;
- });
- default:
- return value;
- }
- }
-
- return migrate(config);
-}
-
export async function revision(context: Context, next) {
+ const plugin = context.app.getPlugin('workflow') as Plugin;
const { db } = context;
const repository = utils.getRepositoryFromParams(context);
const { filterByTk, filter = {}, values = {} } = context.action.params;
@@ -109,6 +68,8 @@ export async function revision(context: Context, next) {
transaction,
});
+ const trigger = plugin.triggers.get(origin.type);
+
const revisionData = filter.key
? {
key: filter.key,
@@ -121,9 +82,12 @@ export async function revision(context: Context, next) {
values: {
title: `${origin.title} copy`,
description: origin.description,
- type: origin.type,
- config: origin.config,
...revisionData,
+ type: origin.type,
+ config:
+ typeof trigger.duplicateConfig === 'function'
+ ? await trigger.duplicateConfig(origin, { transaction })
+ : origin.config,
},
transaction,
});
@@ -136,10 +100,15 @@ export async function revision(context: Context, next) {
const oldToNew = new Map();
const newToOld = new Map();
for await (const node of origin.nodes) {
+ const instruction = plugin.instructions.get(node.type);
const newNode = await instance.createNode(
{
type: node.type,
- config: node.config,
+ key: node.key,
+ config:
+ typeof instruction.duplicateConfig === 'function'
+ ? await instruction.duplicateConfig(node, { transaction })
+ : node.config,
title: node.title,
branchIndex: node.branchIndex,
},
@@ -154,18 +123,11 @@ export async function revision(context: Context, next) {
const oldNode = originalNodesMap.get(oldId);
const newUpstream = oldNode.upstreamId ? oldToNew.get(oldNode.upstreamId) : null;
const newDownstream = oldNode.downstreamId ? oldToNew.get(oldNode.downstreamId) : null;
- let migratedConfig;
- try {
- migratedConfig = migrateConfig(oldNode.config, oldToNew);
- } catch (err) {
- return context.throw(400, err.message);
- }
await newNode.update(
{
upstreamId: newUpstream?.id ?? null,
downstreamId: newDownstream?.id ?? null,
- config: migratedConfig,
},
{ transaction },
);
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/collections/flow_nodes.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/collections/flow_nodes.ts
index 2fae8712a..62dadd794 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/collections/flow_nodes.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/collections/flow_nodes.ts
@@ -5,6 +5,10 @@ export default {
duplicator: 'required',
name: 'flow_nodes',
fields: [
+ {
+ type: 'uid',
+ name: 'key',
+ },
{
type: 'string',
name: 'title',
diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/index.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/index.ts
index b8cfcefdd..4ddec04f1 100644
--- a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/index.ts
+++ b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/index.ts
@@ -1,6 +1,7 @@
import path from 'path';
import { requireModule } from '@nocobase/utils';
+import { Transactionable } from '@nocobase/database';
import Plugin from '..';
import Processor from '../Processor';
@@ -25,7 +26,9 @@ export interface Instruction {
// for start node in main flow (or branch) to resume when manual sub branch triggered
resume?: Runner;
- getScope?: (node: FlowNodeModel, job: any, processor: Processor) => any;
+ getScope?: (node: FlowNodeModel, data: any, processor: Processor) => any;
+
+ duplicateConfig?: (node: FlowNodeModel, options: Transactionable) => object | Promise