mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 05:29:26 +08:00
refactor(plugin-workflow-action-trigger): support to use end node to determine status (#6399)
This commit is contained in:
parent
e3123a4abb
commit
58b5396c30
@ -12,12 +12,28 @@ import { BelongsTo, HasOne } from 'sequelize';
|
||||
import { Model, modelAssociationByKey } from '@nocobase/database';
|
||||
import Application, { DefaultContext } from '@nocobase/server';
|
||||
import { Context as ActionContext, Next } from '@nocobase/actions';
|
||||
import PluginErrorHandler from '@nocobase/plugin-error-handler';
|
||||
|
||||
import WorkflowPlugin, { EventOptions, Trigger, WorkflowModel, toJSON } from '@nocobase/plugin-workflow';
|
||||
import WorkflowPlugin, {
|
||||
EXECUTION_STATUS,
|
||||
EventOptions,
|
||||
Trigger,
|
||||
WorkflowModel,
|
||||
toJSON,
|
||||
} from '@nocobase/plugin-workflow';
|
||||
import { joinCollectionName, parseCollectionName } from '@nocobase/data-source-manager';
|
||||
|
||||
interface Context extends ActionContext, DefaultContext {}
|
||||
|
||||
class RequestOnActionTriggerError extends Error {
|
||||
status = 400;
|
||||
messages: any[] = [];
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'RequestOnActionTriggerError';
|
||||
}
|
||||
}
|
||||
|
||||
export default class extends Trigger {
|
||||
static TYPE = 'action';
|
||||
|
||||
@ -43,6 +59,16 @@ export default class extends Trigger {
|
||||
}
|
||||
|
||||
workflow.app.dataSourceManager.use(triggerWorkflowActionMiddleware);
|
||||
|
||||
workflow.app.pm.get(PluginErrorHandler).errorHandler.register(
|
||||
(err) => err instanceof RequestOnActionTriggerError || err.name === 'RequestOnActionTriggerError',
|
||||
async (err, ctx) => {
|
||||
ctx.body = {
|
||||
errors: err.messages,
|
||||
};
|
||||
ctx.status = err.status;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,7 +203,35 @@ export default class extends Trigger {
|
||||
}
|
||||
|
||||
for (const event of syncGroup) {
|
||||
await this.workflow.trigger(event[0], event[1]);
|
||||
const processor = await this.workflow.trigger(event[0], event[1], { httpContext: context });
|
||||
|
||||
// NOTE: workflow trigger failed
|
||||
if (!processor) {
|
||||
return context.throw(500);
|
||||
}
|
||||
|
||||
const { lastSavedJob, nodesMap } = processor;
|
||||
const lastNode = nodesMap.get(lastSavedJob?.nodeId);
|
||||
// NOTE: passthrough
|
||||
if (processor.execution.status === EXECUTION_STATUS.RESOLVED) {
|
||||
if (lastNode?.type === 'end') {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// NOTE: intercept
|
||||
if (processor.execution.status < EXECUTION_STATUS.STARTED) {
|
||||
if (lastNode?.type !== 'end') {
|
||||
return context.throw(500, 'Workflow on your action failed, please contact the administrator');
|
||||
}
|
||||
|
||||
const err = new RequestOnActionTriggerError('Request failed');
|
||||
err.status = 400;
|
||||
err.messages = context.state.messages;
|
||||
return context.throw(err.status, err);
|
||||
}
|
||||
// NOTE: should not be pending
|
||||
return context.throw(500, 'Workflow on your action hangs, please contact the administrator');
|
||||
}
|
||||
|
||||
for (const event of asyncGroup) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import { omit } from 'lodash';
|
||||
import Database from '@nocobase/database';
|
||||
import { EXECUTION_STATUS } from '@nocobase/plugin-workflow';
|
||||
import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
|
||||
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
||||
import { MockServer } from '@nocobase/test';
|
||||
|
||||
@ -30,7 +30,7 @@ describe('workflow > action-trigger', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await getApp({
|
||||
plugins: ['users', 'auth', 'acl', 'data-source-manager', 'system-settings', Plugin],
|
||||
plugins: ['users', 'auth', 'acl', 'data-source-manager', 'system-settings', 'error-handler', Plugin],
|
||||
});
|
||||
await app.pm.get('auth').install();
|
||||
agent = app.agent();
|
||||
@ -661,6 +661,72 @@ describe('workflow > action-trigger', () => {
|
||||
expect(e3s.length).toBe(1);
|
||||
expect(e3s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
});
|
||||
|
||||
it('execution failed on node error', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
sync: true,
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const n1 = await workflow.createNode({
|
||||
type: 'error',
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(500);
|
||||
});
|
||||
|
||||
it('execution failed on end node success', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
sync: true,
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const n1 = await workflow.createNode({
|
||||
type: 'end',
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
});
|
||||
|
||||
it('execution failed on end node success', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
sync: true,
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const n1 = await workflow.createNode({
|
||||
type: 'end',
|
||||
config: {
|
||||
endStatus: JOB_STATUS.FAILED,
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('global workflow', () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user