fix(plugin-workflow): fix trigger getter (#1060)

This commit is contained in:
Junyi 2022-11-09 09:44:07 +08:00 committed by GitHub
parent 2aa4468763
commit 1bd3e93588
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 102 additions and 33 deletions

View File

@ -540,6 +540,9 @@ export default {
'Load failed': '加载失败', 'Load failed': '加载失败',
'Trigger': '触发器', 'Trigger': '触发器',
'Trigger context': '触发数据',
'Trigger data': '触发数据',
'Trigger time': '触发时间',
'Triggered at': '触发时间', 'Triggered at': '触发时间',
'Collection event': '数据表事件', 'Collection event': '数据表事件',
'Trigger on': '触发时机', 'Trigger on': '触发时机',
@ -585,7 +588,6 @@ export default {
'End': '结束', 'End': '结束',
'Trigger context': '触发数据',
'Node result': '节点数据', 'Node result': '节点数据',
'Constant': '常量', 'Constant': '常量',

View File

@ -1,5 +1,5 @@
import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { css } from '@emotion/css';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Calculation } from '../calculators'; import { Calculation } from '../calculators';

View File

@ -42,6 +42,20 @@ const FieldsSelect = observer((props) => {
); );
}); });
const COLLECTION_TRIGGER_MODE = {
CREATED: 1,
UPDATED: 2,
SAVED: 3,
DELETED: 4,
};
const collectionModeOptions = [
{ label: '{{t("After record added")}}', value: COLLECTION_TRIGGER_MODE.CREATED },
{ label: '{{t("After record updated")}}', value: COLLECTION_TRIGGER_MODE.UPDATED },
{ label: '{{t("After record added or updated")}}', value: COLLECTION_TRIGGER_MODE.SAVED },
{ label: '{{t("After record deleted")}}', value: COLLECTION_TRIGGER_MODE.DELETED },
];
export default { export default {
@ -85,12 +99,7 @@ export default {
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'Select', 'x-component': 'Select',
'x-component-props': { 'x-component-props': {
options: [ options: collectionModeOptions,
{ value: 1, label: '{{t("After record added")}}' },
{ value: 2, label: '{{t("After record updated")}}' },
{ value: 3, label: '{{t("After record added or updated")}}' },
{ value: 4, label: '{{t("After record deleted")}}' }
],
placeholder: '{{t("Trigger on")}}' placeholder: '{{t("Trigger on")}}'
}, },
required: true, required: true,
@ -99,7 +108,7 @@ export default {
target: 'config.changed', target: 'config.changed',
fulfill: { fulfill: {
state: { state: {
disabled: '{{!($self.value & 0b010)}}', disabled: `{{!($self.value & ${COLLECTION_TRIGGER_MODE.UPDATED})}}`,
}, },
} }
}, },

View File

@ -9,9 +9,10 @@ import { collection } from '../../schemas/collection';
import { OnField } from './OnField'; import { OnField } from './OnField';
import { EndsByField } from './EndsByField'; import { EndsByField } from './EndsByField';
import { RepeatField } from './RepeatField'; import { RepeatField } from './RepeatField';
import { SCHEDULE_MODE } from './constants';
const ModeFieldsets = { const ModeFieldsets = {
0: { [SCHEDULE_MODE.STATIC]: {
startsOn: { startsOn: {
type: 'datetime', type: 'datetime',
name: 'startsOn', name: 'startsOn',
@ -70,7 +71,7 @@ const ModeFieldsets = {
} }
} }
}, },
1: { [SCHEDULE_MODE.COLLECTION_FIELD]: {
collection: { collection: {
...collection, ...collection,
'x-reactions': [ 'x-reactions': [
@ -148,6 +149,11 @@ const ModeFieldsets = {
} }
}; };
const scheduleModeOptions = [
{ value: SCHEDULE_MODE.STATIC, label: '{{t("Based on certain date")}}' },
{ value: SCHEDULE_MODE.COLLECTION_FIELD, label: '{{t("Based on date field of collection")}}' },
]
export const ScheduleConfig = () => { export const ScheduleConfig = () => {
const { values = {}, clearFormGraph } = useForm(); const { values = {}, clearFormGraph } = useForm();
const { config = {} } = values; const { config = {} } = values;
@ -173,10 +179,7 @@ export const ScheduleConfig = () => {
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'Radio.Group', 'x-component': 'Radio.Group',
'x-component-props': { 'x-component-props': {
options: [ options: scheduleModeOptions
{ value: 0, label: '{{t("Based on certain date")}}' },
{ value: 1, label: '{{t("Based on date field of collection")}}' },
]
}, },
required: true required: true
}} }}

View File

@ -0,0 +1,4 @@
export const SCHEDULE_MODE = {
STATIC: 0,
COLLECTION_FIELD: 1
};

View File

@ -1,12 +1,14 @@
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Select } from 'antd'; import { css } from '@emotion/css';
import { Cascader } from 'antd';
import { useCompile, useCollectionDataSource, useCollectionManager } from '@nocobase/client'; import { useCompile, useCollectionDataSource, useCollectionManager } from '@nocobase/client';
import { ScheduleConfig } from './ScheduleConfig'; import { ScheduleConfig } from './ScheduleConfig';
import { useFlowContext } from '../../FlowContext'; import { useFlowContext } from '../../FlowContext';
import { BaseTypeSet } from '../../calculators'; import { BaseTypeSet } from '../../calculators';
import { SCHEDULE_MODE } from './constants';
export default { export default {
title: '{{t("Schedule event")}}', title: '{{t("Schedule event")}}',
@ -29,24 +31,35 @@ export default {
getter({ type, options, onChange }) { getter({ type, options, onChange }) {
const { t } = useTranslation(); const { t } = useTranslation();
const compile = useCompile(); const compile = useCompile();
const { collections = [] } = useCollectionManager();
const { workflow } = useFlowContext(); const { workflow } = useFlowContext();
const { collections = [] } = useCollectionManager();
const path = options?.path ? options.path.split('.') : [];
const varOptions: any[] = [
{ value: 'date', label: t('Trigger time') },
];
if (workflow.config.mode === SCHEDULE_MODE.COLLECTION_FIELD) {
const collection = collections.find(item => item.name === workflow.config.collection) ?? { fields: [] }; const collection = collections.find(item => item.name === workflow.config.collection) ?? { fields: [] };
varOptions.push({
return ( value: 'data',
<Select label: t('Trigger data'),
placeholder={t('Fields')} children: collection.fields
value={options?.path?.replace(/^data\./, '')}
onChange={(path) => {
onChange({ type, options: { ...options, path: `data.${path}` } });
}}
>
{collection.fields
.filter(field => BaseTypeSet.has(field?.uiSchema?.type)) .filter(field => BaseTypeSet.has(field?.uiSchema?.type))
.map(field => ( .map(field => ({
<Select.Option key={field.name} value={field.name}>{compile(field.uiSchema.title)}</Select.Option> value: field.name,
))} label: compile(field.uiSchema?.title),
</Select> }))
});
}
return (
<Cascader
placeholder={t('Trigger context')}
value={path}
options={varOptions}
onChange={(next) => {
onChange({ type, options: { ...options, path: next.join('.') } });
}}
allowClear={false}
/>
); );
} }
}; };

View File

@ -156,7 +156,7 @@ export default class WorkflowPlugin extends Plugin {
key: workflow.key, key: workflow.key,
status: EXECUTION_STATUS.STARTED, status: EXECUTION_STATUS.STARTED,
useTransaction: workflow.useTransaction, useTransaction: workflow.useTransaction,
transaction: transaction.id transaction: transaction?.id
}, { transaction }); }, { transaction });
console.log('workflow triggered:', new Date(), workflow.id, execution.id); console.log('workflow triggered:', new Date(), workflow.id, execution.id);

View File

@ -79,6 +79,44 @@ describe('workflow > instructions > delay', () => {
const [j2] = await e2.getJobs(); const [j2] = await e2.getJobs();
expect(j2.status).toBe(JOB_STATUS.REJECTED); expect(j2.status).toBe(JOB_STATUS.REJECTED);
}); });
it('delay to resolve and rollback in downstream node', async () => {
const n1 = await workflow.createNode({
type: 'delay',
config: {
duration: 1000,
endStatus: JOB_STATUS.RESOLVED
}
});
const n2 = await workflow.createNode({
type: 'create',
config: {
collection: 'comment',
params: {
values: {
status: 'should be number but use string to raise an error'
}
}
},
upstreamId: n1.id
});
await n1.setDownstream(n2);
const post = await PostRepo.create({ values: { title: 't1' } });
const [e1] = await workflow.getExecutions();
expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
const [j1] = await e1.getJobs();
expect(j1.status).toBe(JOB_STATUS.PENDING);
await sleep(2000);
const [e2] = await workflow.getExecutions();
expect(e2.status).toEqual(EXECUTION_STATUS.REJECTED);
const [j2, j3] = await e2.getJobs({ order: [['id', 'ASC']] });
expect(j2.status).toBe(JOB_STATUS.RESOLVED);
expect(j3.status).toBe(JOB_STATUS.REJECTED);
});
}); });
describe('app lifecycle', () => { describe('app lifecycle', () => {