Merge branch 'next' into develop

This commit is contained in:
nocobase[bot] 2025-01-18 06:16:40 +00:00
commit 7093f478c1
5 changed files with 142 additions and 3 deletions

View File

@ -168,6 +168,18 @@ const workflowFieldset = {
multiple: true,
},
},
stackLimit: {
type: 'number',
title: `{{ t("Maximum number of loop calls", { ns: "${NAMESPACE}" }) }}`,
description: `{{ t("If the number of loop calls is too large, there will be performance issues.", { ns: "${NAMESPACE}" }) }}`,
'x-decorator': 'FormItem',
default: 1,
'x-component': 'InputNumber',
'x-component-props': {
min: 1,
precision: 0,
},
},
},
},
};

View File

@ -94,5 +94,7 @@
"Trigger in executed workflow cannot be modified": "Trigger in executed workflow cannot be modified",
"Node in executed workflow cannot be modified": "Node in executed workflow cannot be modified",
"Can not delete": "Can not delete",
"The result of this node has been referenced by other nodes ({{nodes}}), please remove the usage before deleting.": "The result of this node has been referenced by other nodes ({{nodes}}), please remove the usage before deleting."
"The result of this node has been referenced by other nodes ({{nodes}}), please remove the usage before deleting.": "The result of this node has been referenced by other nodes ({{nodes}}), please remove the usage before deleting.",
"Maximum number of loop calls": "Maximum number of loop calls",
"If the number of loop calls is too large, there will be performance issues.": "If the number of loop calls is too large, there will be performance issues."
}

View File

@ -31,6 +31,8 @@
"Data operation nodes in workflow will run in a same transaction until any interruption. Any failure will cause data rollback, and will also rollback the history of the execution.":
"工作流中的节点将在同一个事务中运行。任何失败都会导致数据回滚,同时也会回滚相应的执行历史。",
"Auto delete history when execution is on end status": "执行结束后自动删除对应状态的历史记录",
"Maximum number of loop calls": "最大循环调用次数",
"If the number of loop calls is too large, there will be performance issues.": "如果循环调用次数过大,会有性能问题",
"Trigger": "触发器",
"Unknown trigger": "未知触发器",
"Workflow with unknown type will cause error. Please delete it or check plugin which provide this type.": "未知类型的工作流会导致错误,请删除或检查提供该类型的插件。",

View File

@ -488,9 +488,10 @@ export default class PluginWorkflowServer extends Plugin {
transaction: options.transaction,
});
if (existed) {
const limitCount = workflow.options.stackLimit || 1;
if (existed >= limitCount) {
this.getLogger(workflow.id).warn(
`workflow ${workflow.id} has already been triggered in stacks executions (${stack}), and newly triggering will be skipped.`,
`workflow ${workflow.id} has already been triggered in stacks executions (${stack}), and max call coont is ${limitCount}, newly triggering will be skipped.`,
);
valid = false;

View File

@ -841,6 +841,128 @@ describe('workflow > triggers > collection', () => {
expect(e2s.length).toBe(1);
expect(e2s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
});
it('stack limit for same execution', async () => {
const workflow = await WorkflowModel.create({
enabled: true,
type: 'collection',
config: {
mode: 1,
collection: 'posts',
},
options: {
stackLimit: 3,
},
});
const n1 = await workflow.createNode({
type: 'create',
config: {
collection: 'posts',
params: {
values: {
title: 't2',
},
},
},
});
const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const posts = await PostRepo.find();
expect(posts.length).toBe(4);
const e1s = await workflow.getExecutions();
expect(e1s.length).toBe(3);
expect(e1s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
expect(e1s[1].status).toBe(EXECUTION_STATUS.RESOLVED);
expect(e1s[2].status).toBe(EXECUTION_STATUS.RESOLVED);
// NOTE: second trigger to ensure no skipped event
const p3 = await PostRepo.create({ values: { title: 't3' } });
await sleep(500);
const posts2 = await PostRepo.find();
expect(posts2.length).toBe(8);
const e2s = await workflow.getExecutions({ order: [['createdAt', 'DESC']] });
expect(e2s.length).toBe(6);
expect(e2s[3].status).toBe(EXECUTION_STATUS.RESOLVED);
expect(e2s[4].status).toBe(EXECUTION_STATUS.RESOLVED);
expect(e2s[5].status).toBe(EXECUTION_STATUS.RESOLVED);
});
it('stack limit for multiple cycling trigger', async () => {
const w1 = await WorkflowModel.create({
enabled: true,
type: 'collection',
config: {
mode: 1,
collection: 'posts',
},
options: {
stackLimit: 3,
},
});
const n1 = await w1.createNode({
type: 'create',
config: {
collection: 'categories',
params: {
values: {
title: 'c1',
},
},
},
});
const w2 = await WorkflowModel.create({
enabled: true,
type: 'collection',
config: {
mode: 1,
collection: 'categories',
},
options: {
stackLimit: 3,
},
});
const n2 = await w2.createNode({
type: 'create',
config: {
collection: 'posts',
params: {
values: {
title: 't2',
},
},
},
});
const p1 = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const posts = await PostRepo.find();
expect(posts.length).toBe(4);
const e1s = await w1.getExecutions();
expect(e1s.length).toBe(3);
expect(e1s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
expect(e1s[1].status).toBe(EXECUTION_STATUS.RESOLVED);
expect(e1s[2].status).toBe(EXECUTION_STATUS.RESOLVED);
const e2s = await w2.getExecutions();
expect(e2s.length).toBe(3);
expect(e2s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
expect(e2s[1].status).toBe(EXECUTION_STATUS.RESOLVED);
expect(e2s[2].status).toBe(EXECUTION_STATUS.RESOLVED);
});
});
describe('sync', () => {