mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 05:29:26 +08:00
Merge branch 'main' into next
This commit is contained in:
commit
1e3f988c48
4
.github/workflows/manual-merge.yml
vendored
4
.github/workflows/manual-merge.yml
vendored
@ -65,6 +65,10 @@ jobs:
|
|||||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
|
- name: Set user
|
||||||
|
run: |
|
||||||
|
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
|
||||||
|
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>'
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
@ -12,12 +12,28 @@ import { BelongsTo, HasOne } from 'sequelize';
|
|||||||
import { Collection, Model, modelAssociationByKey } from '@nocobase/database';
|
import { Collection, Model, modelAssociationByKey } from '@nocobase/database';
|
||||||
import Application, { DefaultContext } from '@nocobase/server';
|
import Application, { DefaultContext } from '@nocobase/server';
|
||||||
import { Context as ActionContext, Next } from '@nocobase/actions';
|
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';
|
import { joinCollectionName, parseCollectionName } from '@nocobase/data-source-manager';
|
||||||
|
|
||||||
interface Context extends ActionContext, DefaultContext {}
|
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 {
|
export default class extends Trigger {
|
||||||
static TYPE = 'action';
|
static TYPE = 'action';
|
||||||
|
|
||||||
@ -39,6 +55,16 @@ export default class extends Trigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
workflow.app.dataSourceManager.use(triggerWorkflowActionMiddleware);
|
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;
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTargetCollection(collection: Collection, association: string) {
|
getTargetCollection(collection: Collection, association: string) {
|
||||||
@ -168,7 +194,35 @@ export default class extends Trigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const event of syncGroup) {
|
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) {
|
for (const event of asyncGroup) {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import Database from '@nocobase/database';
|
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 { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
||||||
import { MockServer } from '@nocobase/test';
|
import { MockServer } from '@nocobase/test';
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ describe('workflow > action-trigger', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
app = await getApp({
|
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],
|
||||||
acl: true,
|
acl: true,
|
||||||
});
|
});
|
||||||
await app.pm.get('auth').install();
|
await app.pm.get('auth').install();
|
||||||
@ -529,6 +529,72 @@ describe('workflow > action-trigger', () => {
|
|||||||
expect(e3s.length).toBe(1);
|
expect(e3s.length).toBe(1);
|
||||||
expect(e3s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
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', () => {
|
describe('global workflow', () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user