fix(plugin-workflow): fix trigger getter (#1060)
This commit is contained in:
parent
2aa4468763
commit
1bd3e93588
@ -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': '常量',
|
||||||
|
|
||||||
|
@ -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';
|
||||||
|
@ -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})}}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
export const SCHEDULE_MODE = {
|
||||||
|
STATIC: 0,
|
||||||
|
COLLECTION_FIELD: 1
|
||||||
|
};
|
@ -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 collection = collections.find(item => item.name === workflow.config.collection) ?? { fields: [] };
|
const { collections = [] } = useCollectionManager();
|
||||||
|
const path = options?.path ? options.path.split('.') : [];
|
||||||
return (
|
const varOptions: any[] = [
|
||||||
<Select
|
{ value: 'date', label: t('Trigger time') },
|
||||||
placeholder={t('Fields')}
|
];
|
||||||
value={options?.path?.replace(/^data\./, '')}
|
if (workflow.config.mode === SCHEDULE_MODE.COLLECTION_FIELD) {
|
||||||
onChange={(path) => {
|
const collection = collections.find(item => item.name === workflow.config.collection) ?? { fields: [] };
|
||||||
onChange({ type, options: { ...options, path: `data.${path}` } });
|
varOptions.push({
|
||||||
}}
|
value: 'data',
|
||||||
>
|
label: t('Trigger data'),
|
||||||
{collection.fields
|
children: 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}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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', () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user