mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-01 18:52:20 +08:00
refactor(plugin-workflow): change api of manually execute (#5850)
* feat: add subflow node * feat: add execution event * feat: add force option * feat: avoid recurring call * feat: add stack to execution * feat: add cyclic call validator * feat: collection trigger add execution stack * fix: stack * fix: manual execute * refactor(plugin-workflow): adjust api and implementent * chore: revert locale back * feat: trigger field set add scope variables * feat: add changeOnSelect * feat: variables support ID reference * feat: change locale * refactor(plugin-workflow): support execute by passing filterByTk for a record * refactor(plugin-workflow): adjust props name * fix(plugin-workflow): fix execute variable context * fix(plugin-workflow): fix variables and locales * chore(plugin-workflow): remove demo code * fix(plugin-workflow): fix import * fix(plugin-workflow): fix build error --------- Co-authored-by: mytharcher <mytharcher@gmail.com>
This commit is contained in:
parent
84cfa82304
commit
be44388e90
@ -434,7 +434,7 @@ export function Input(props: VariableInputProps) {
|
||||
style={{ overflow: 'hidden' }}
|
||||
className={cx('ant-input', { 'ant-input-disabled': disabled }, hashId)}
|
||||
>
|
||||
<Tag contentEditable={false} color="blue">
|
||||
<Tag color="blue">
|
||||
{variableText.map((item, index) => {
|
||||
return (
|
||||
<React.Fragment key={item}>
|
||||
|
@ -9,7 +9,13 @@
|
||||
|
||||
import { useForm } from '@formily/react';
|
||||
|
||||
import { SchemaInitializerItemType, parseCollectionName, useCollectionDataSource, useCompile } from '@nocobase/client';
|
||||
import {
|
||||
SchemaInitializerItemType,
|
||||
parseCollectionName,
|
||||
useCollectionDataSource,
|
||||
useCompile,
|
||||
RemoteSelect,
|
||||
} from '@nocobase/client';
|
||||
import {
|
||||
Trigger,
|
||||
CollectionBlockInitializer,
|
||||
@ -19,8 +25,10 @@ import {
|
||||
RadioWithTooltip,
|
||||
useGetCollectionFields,
|
||||
TriggerCollectionRecordSelect,
|
||||
WorkflowVariableWrapper,
|
||||
} from '@nocobase/plugin-workflow/client';
|
||||
import { NAMESPACE, useLang } from '../locale';
|
||||
import React from 'react';
|
||||
|
||||
const COLLECTION_TRIGGER_ACTION = {
|
||||
CREATE: 'create',
|
||||
@ -36,8 +44,8 @@ function useVariables(config, options) {
|
||||
const getMainCollectionFields = useGetCollectionFields();
|
||||
|
||||
const langTriggerData = useLang('Trigger data');
|
||||
const langUserSubmittedForm = useLang('User submitted action');
|
||||
const langRoleSubmittedForm = useLang('Role of user submitted action');
|
||||
const langUserSubmittedForm = useLang('User acted');
|
||||
const langRoleSubmittedForm = useLang('Role of user acted');
|
||||
const result = [
|
||||
...getCollectionFieldOptions({
|
||||
// depth,
|
||||
@ -198,8 +206,8 @@ export default class extends Trigger {
|
||||
triggerFieldset = {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: `{{t("Trigger data", { ns: "${NAMESPACE}" })}}`,
|
||||
description: `{{t("Choose a record of the collection to trigger.", { ns: "workflow" })}}`,
|
||||
title: `{{t("Trigger data", { ns: "workflow" })}}`,
|
||||
description: `{{t("Choose a record or primary key of a record in the collection to trigger.", { ns: "workflow" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'TriggerCollectionRecordSelect',
|
||||
default: null,
|
||||
@ -207,37 +215,90 @@ export default class extends Trigger {
|
||||
},
|
||||
userId: {
|
||||
type: 'number',
|
||||
title: `{{t("User submitted action", { ns: "${NAMESPACE}" })}}`,
|
||||
title: `{{t("User acted", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'RemoteSelect',
|
||||
'x-component': 'WorkflowVariableWrapper',
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label: 'nickname',
|
||||
value: 'id',
|
||||
nullable: false,
|
||||
changeOnSelect: true,
|
||||
variableOptions: {
|
||||
types: [
|
||||
(field) => {
|
||||
if (field.isForeignKey || field.type === 'context') {
|
||||
return field.target === 'users';
|
||||
}
|
||||
return field.collectionName === 'users' && field.name === 'id';
|
||||
},
|
||||
],
|
||||
},
|
||||
service: {
|
||||
resource: 'users',
|
||||
render(props) {
|
||||
return (
|
||||
<RemoteSelect
|
||||
fieldNames={{ label: 'nickname', value: 'id' }}
|
||||
service={{ resource: 'users' }}
|
||||
manual={false}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
manual: false,
|
||||
},
|
||||
// properties: {
|
||||
// remoteSelect: {
|
||||
// 'x-component': 'RemoteSelect',
|
||||
// 'x-component-props': {
|
||||
// fieldNames: {
|
||||
// label: 'nickname',
|
||||
// value: 'id',
|
||||
// },
|
||||
// service: {
|
||||
// resource: 'users',
|
||||
// },
|
||||
// manual: false,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
default: null,
|
||||
required: true,
|
||||
},
|
||||
roleName: {
|
||||
type: 'string',
|
||||
title: `{{t("Role of user submitted action", { ns: "${NAMESPACE}" })}}`,
|
||||
title: `{{t("Role of user acted", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'RemoteSelect',
|
||||
'x-component': 'WorkflowVariableWrapper',
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label: 'title',
|
||||
value: 'name',
|
||||
nullable: false,
|
||||
changeOnSelect: true,
|
||||
variableOptions: {
|
||||
types: [
|
||||
(field) => {
|
||||
if (field.isForeignKey) {
|
||||
return field.target === 'roles';
|
||||
}
|
||||
return field.collectionName === 'roles' && field.name === 'name';
|
||||
},
|
||||
],
|
||||
},
|
||||
service: {
|
||||
resource: 'roles',
|
||||
render(props) {
|
||||
return (
|
||||
<RemoteSelect
|
||||
fieldNames={{ label: 'title', value: 'name' }}
|
||||
service={{ resource: 'roles' }}
|
||||
manual={false}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
manual: false,
|
||||
},
|
||||
// 'x-component-props': {
|
||||
// fieldNames: {
|
||||
// label: 'title',
|
||||
// value: 'name',
|
||||
// },
|
||||
// service: {
|
||||
// resource: 'roles',
|
||||
// },
|
||||
// manual: false,
|
||||
// },
|
||||
default: null,
|
||||
},
|
||||
};
|
||||
@ -252,6 +313,7 @@ export default class extends Trigger {
|
||||
RadioWithTooltip,
|
||||
CheckboxGroupWithTooltip,
|
||||
TriggerCollectionRecordSelect,
|
||||
WorkflowVariableWrapper,
|
||||
};
|
||||
isActionTriggerable = (config, context) => {
|
||||
return !config.global && ['submit', 'customize:save', 'customize:update'].includes(context.buttonAction);
|
||||
|
@ -12,6 +12,6 @@
|
||||
"Update record action": "更新记录操作",
|
||||
"Associations to use": "待使用的关系数据",
|
||||
"Trigger data": "触发器数据",
|
||||
"User submitted action": "提交操作的用户",
|
||||
"Role of user submitted action": "提交操作用户的角色"
|
||||
"User acted": "操作者",
|
||||
"Role of user acted": "操作者角色"
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ export default class extends Trigger {
|
||||
}
|
||||
|
||||
for (const event of syncGroup) {
|
||||
await this.workflow.trigger(event[0], event[1], { httpContext: context });
|
||||
await this.workflow.trigger(event[0], event[1]);
|
||||
}
|
||||
|
||||
for (const event of asyncGroup) {
|
||||
@ -185,18 +185,27 @@ export default class extends Trigger {
|
||||
}
|
||||
}
|
||||
|
||||
async execute(workflow: WorkflowModel, context: Context, options: EventOptions) {
|
||||
const { values } = context.action.params;
|
||||
async execute(workflow: WorkflowModel, values, options: EventOptions) {
|
||||
// const { values } = context.action.params;
|
||||
const [dataSourceName, collectionName] = parseCollectionName(workflow.config.collection);
|
||||
const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
|
||||
const { filterTargetKey, repository } = collectionManager.getCollection(collectionName);
|
||||
const filterByTk = Array.isArray(filterTargetKey)
|
||||
? pick(
|
||||
values.data,
|
||||
filterTargetKey.sort((a, b) => a.localeCompare(b)),
|
||||
)
|
||||
: values.data[filterTargetKey];
|
||||
const UserRepo = context.app.db.getRepository('users');
|
||||
|
||||
let { data } = values;
|
||||
let filterByTk;
|
||||
let loadNeeded = false;
|
||||
if (data && typeof data === 'object') {
|
||||
filterByTk = Array.isArray(filterTargetKey)
|
||||
? pick(
|
||||
data,
|
||||
filterTargetKey.sort((a, b) => a.localeCompare(b)),
|
||||
)
|
||||
: data[filterTargetKey];
|
||||
} else {
|
||||
filterByTk = data;
|
||||
loadNeeded = true;
|
||||
}
|
||||
const UserRepo = this.workflow.app.db.getRepository('users');
|
||||
const actor = await UserRepo.findOne({
|
||||
filterByTk: values.userId,
|
||||
appends: ['roles'],
|
||||
@ -207,8 +216,7 @@ export default class extends Trigger {
|
||||
const { roles, ...user } = actor.desensitize().get();
|
||||
const roleName = values.roleName || roles?.[0]?.name;
|
||||
|
||||
let { data } = values;
|
||||
if (workflow.config.appends?.length) {
|
||||
if (loadNeeded || workflow.config.appends?.length) {
|
||||
data = await repository.findOne({
|
||||
filterByTk,
|
||||
appends: workflow.config.appends,
|
||||
@ -221,10 +229,7 @@ export default class extends Trigger {
|
||||
user,
|
||||
roleName,
|
||||
},
|
||||
{
|
||||
...options,
|
||||
httpContext: context,
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import { RemoteSelect, Variable } from '@nocobase/client';
|
||||
import { useWorkflowVariableOptions } from '@nocobase/plugin-workflow/client';
|
||||
|
||||
function isUserKeyField(field) {
|
||||
if (field.isForeignKey) {
|
||||
if (field.isForeignKey || field.type === 'context') {
|
||||
return field.target === 'users';
|
||||
}
|
||||
return field.collectionName === 'users' && field.name === 'id';
|
||||
|
@ -14,3 +14,9 @@ export const FlowContext = React.createContext<any>({});
|
||||
export function useFlowContext() {
|
||||
return useContext(FlowContext);
|
||||
}
|
||||
|
||||
export const CurrentWorkflowContext = React.createContext<any>({});
|
||||
|
||||
export function useCurrentWorkflowContext() {
|
||||
return useContext(CurrentWorkflowContext);
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Alert, App, Breadcrumb, Button, Dropdown, Result, Spin, Switch, Tag, Tooltip } from 'antd';
|
||||
import { DownOutlined, EllipsisOutlined, RightOutlined } from '@ant-design/icons';
|
||||
import { NoticeType } from 'antd/es/message/interface';
|
||||
import { useField, useForm } from '@formily/react';
|
||||
import {
|
||||
ActionContextProvider,
|
||||
ResourceActionProvider,
|
||||
@ -30,10 +32,11 @@ import {
|
||||
} from '@nocobase/client';
|
||||
import { dayjs } from '@nocobase/utils/client';
|
||||
|
||||
import PluginWorkflowClient from '.';
|
||||
import { CanvasContent } from './CanvasContent';
|
||||
import { ExecutionStatusColumn } from './components/ExecutionStatus';
|
||||
import { ExecutionLink } from './ExecutionLink';
|
||||
import { FlowContext, useFlowContext } from './FlowContext';
|
||||
import { CurrentWorkflowContext, FlowContext, useFlowContext } from './FlowContext';
|
||||
import { lang, NAMESPACE } from './locale';
|
||||
import { executionSchema } from './schemas/executions';
|
||||
import useStyles from './style';
|
||||
@ -41,10 +44,8 @@ import { linkNodes, getWorkflowDetailPath } from './utils';
|
||||
import { Fieldset } from './components/Fieldset';
|
||||
import { useRefreshActionProps } from './hooks/useRefreshActionProps';
|
||||
import { useTrigger } from './triggers';
|
||||
import { useField, useForm } from '@formily/react';
|
||||
import { ExecutionStatusOptionsMap } from './constants';
|
||||
import PluginWorkflowClient from '.';
|
||||
import { NoticeType } from 'antd/es/message/interface';
|
||||
import { HideVariableContext } from './variable';
|
||||
|
||||
function ExecutionResourceProvider({ request, filter = {}, ...others }) {
|
||||
const { workflow } = useFlowContext();
|
||||
@ -144,99 +145,103 @@ function ExecuteActionButton() {
|
||||
const trigger = useTrigger();
|
||||
|
||||
return (
|
||||
<SchemaComponent
|
||||
components={{
|
||||
Alert,
|
||||
Fieldset,
|
||||
ActionDisabledProvider,
|
||||
...trigger.components,
|
||||
}}
|
||||
scope={{
|
||||
useCancelAction,
|
||||
useExecuteConfirmAction,
|
||||
}}
|
||||
schema={{
|
||||
name: `trigger-modal-${workflow.type}-${workflow.id}`,
|
||||
type: 'void',
|
||||
'x-decorator': 'ActionDisabledProvider',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
openSize: 'small',
|
||||
},
|
||||
title: `{{t('Execute manually', { ns: "${NAMESPACE}" })}}`,
|
||||
properties: {
|
||||
drawer: {
|
||||
<CurrentWorkflowContext.Provider value={workflow}>
|
||||
<HideVariableContext.Provider value={true}>
|
||||
<SchemaComponent
|
||||
components={{
|
||||
Alert,
|
||||
Fieldset,
|
||||
ActionDisabledProvider,
|
||||
...trigger.components,
|
||||
}}
|
||||
scope={{
|
||||
useCancelAction,
|
||||
useExecuteConfirmAction,
|
||||
}}
|
||||
schema={{
|
||||
name: `trigger-modal-${workflow.type}-${workflow.id}`,
|
||||
type: 'void',
|
||||
'x-decorator': 'FormV2',
|
||||
'x-component': 'Action.Modal',
|
||||
'x-decorator': 'ActionDisabledProvider',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
openSize: 'small',
|
||||
},
|
||||
title: `{{t('Execute manually', { ns: "${NAMESPACE}" })}}`,
|
||||
properties: {
|
||||
...(Object.keys(trigger.triggerFieldset ?? {}).length
|
||||
? {
|
||||
alert: {
|
||||
type: 'void',
|
||||
'x-component': 'Alert',
|
||||
'x-component-props': {
|
||||
message: `{{t('Trigger variables need to be filled for executing.', { ns: "${NAMESPACE}" })}}`,
|
||||
className: css`
|
||||
margin-bottom: 1em;
|
||||
`,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {
|
||||
description: {
|
||||
type: 'void',
|
||||
'x-component': 'p',
|
||||
'x-content': `{{t('This will perform all the actions configured in the workflow. Are you sure you want to continue?', { ns: "${NAMESPACE}" })}}`,
|
||||
},
|
||||
}),
|
||||
fieldset: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Fieldset',
|
||||
title: `{{t('Trigger variables', { ns: "${NAMESPACE}" })}}`,
|
||||
properties: trigger.triggerFieldset,
|
||||
},
|
||||
...(workflow.executed
|
||||
? {}
|
||||
: {
|
||||
autoRevision: {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': `{{t('Automatically create a new version after execution', { ns: "${NAMESPACE}" })}}`,
|
||||
default: true,
|
||||
},
|
||||
}),
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Modal.Footer',
|
||||
'x-decorator': 'FormV2',
|
||||
'x-component': 'Action.Modal',
|
||||
title: `{{t('Execute manually', { ns: "${NAMESPACE}" })}}`,
|
||||
properties: {
|
||||
cancel: {
|
||||
...(Object.keys(trigger.triggerFieldset ?? {}).length
|
||||
? {
|
||||
alert: {
|
||||
type: 'void',
|
||||
'x-component': 'Alert',
|
||||
'x-component-props': {
|
||||
message: `{{t('Trigger variables need to be filled for executing.', { ns: "${NAMESPACE}" })}}`,
|
||||
className: css`
|
||||
margin-bottom: 1em;
|
||||
`,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {
|
||||
description: {
|
||||
type: 'void',
|
||||
'x-component': 'p',
|
||||
'x-content': `{{t('This will perform all the actions configured in the workflow. Are you sure you want to continue?', { ns: "${NAMESPACE}" })}}`,
|
||||
},
|
||||
}),
|
||||
fieldset: {
|
||||
type: 'void',
|
||||
title: `{{t('Cancel')}}`,
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{useCancelAction}}',
|
||||
},
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Fieldset',
|
||||
title: `{{t('Trigger variables', { ns: "${NAMESPACE}" })}}`,
|
||||
properties: trigger.triggerFieldset,
|
||||
},
|
||||
submit: {
|
||||
...(workflow.executed
|
||||
? {}
|
||||
: {
|
||||
autoRevision: {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': `{{t('Automatically create a new version after execution', { ns: "${NAMESPACE}" })}}`,
|
||||
default: true,
|
||||
},
|
||||
}),
|
||||
footer: {
|
||||
type: 'void',
|
||||
title: `{{t('Confirm')}}`,
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{useExecuteConfirmAction}}',
|
||||
'x-component': 'Action.Modal.Footer',
|
||||
properties: {
|
||||
cancel: {
|
||||
type: 'void',
|
||||
title: `{{t('Cancel')}}`,
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{useCancelAction}}',
|
||||
},
|
||||
},
|
||||
submit: {
|
||||
type: 'void',
|
||||
title: `{{t('Confirm')}}`,
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{useExecuteConfirmAction}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
</HideVariableContext.Provider>
|
||||
</CurrentWorkflowContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -11,17 +11,17 @@ import React from 'react';
|
||||
|
||||
import { parseCollectionName, RemoteSelect, useApp } from '@nocobase/client';
|
||||
|
||||
import { useFlowContext } from '../FlowContext';
|
||||
import { useCurrentWorkflowContext } from '../FlowContext';
|
||||
import { WorkflowVariableWrapper } from '../variable';
|
||||
|
||||
export function TriggerCollectionRecordSelect(props) {
|
||||
const { workflow } = useFlowContext();
|
||||
const workflow = useCurrentWorkflowContext();
|
||||
const app = useApp();
|
||||
|
||||
const [dataSourceName, collectionName] = parseCollectionName(workflow.config.collection);
|
||||
const { collectionManager } = app.dataSourceManager.getDataSource(dataSourceName);
|
||||
const collection = collectionManager.getCollection(collectionName);
|
||||
|
||||
return (
|
||||
const render = (props) => (
|
||||
<RemoteSelect
|
||||
objectValue
|
||||
dataSource={dataSourceName}
|
||||
@ -36,4 +36,13 @@ export function TriggerCollectionRecordSelect(props) {
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<WorkflowVariableWrapper
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
nullable={false}
|
||||
changeOnSelect
|
||||
render={render}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,65 +0,0 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { Instruction } from '.';
|
||||
import { NAMESPACE } from '../locale';
|
||||
import { JOB_STATUS } from '../constants';
|
||||
import { TriggerCollectionRecordSelect } from '../components/TriggerCollectionRecordSelect';
|
||||
import { Fieldset } from '../components/Fieldset';
|
||||
|
||||
export default class extends Instruction {
|
||||
title = `{{t("Call another workflow", { ns: "${NAMESPACE}" })}}`;
|
||||
type = 'subflow';
|
||||
group = 'control';
|
||||
description = `{{t("Run another workflow and use its output as variables.", { ns: "${NAMESPACE}" })}}`;
|
||||
fieldset = {
|
||||
workflow: {
|
||||
type: 'number',
|
||||
title: `{{t("Workflow", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
enum: [
|
||||
{ label: `{{t("Post added", { ns: "${NAMESPACE}" })}}`, value: 1 },
|
||||
{ label: `{{t("Some other action", { ns: "${NAMESPACE}" })}}`, value: 2 },
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
context: {
|
||||
type: 'object',
|
||||
title: `{{t("Context", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Fieldset',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: `{{t("Trigger data", { ns: "${NAMESPACE}" })}}`,
|
||||
description: `{{t("Choose a record of the collection to trigger.", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'TriggerCollectionRecordSelect',
|
||||
default: null,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
'x-reactions': [
|
||||
{
|
||||
dependencies: ['workflow'],
|
||||
fulfill: {
|
||||
state: {
|
||||
visible: '{{workflow === 1}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
components = {
|
||||
TriggerCollectionRecordSelect,
|
||||
Fieldset,
|
||||
};
|
||||
}
|
@ -197,7 +197,7 @@ export default class extends Trigger {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: `{{t("Trigger data", { ns: "${NAMESPACE}" })}}`,
|
||||
description: `{{t("Choose a record of the collection to trigger.", { ns: "${NAMESPACE}" })}}`,
|
||||
description: `{{t("Choose a record or primary key of a record in the collection to trigger.", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'TriggerCollectionRecordSelect',
|
||||
default: null,
|
||||
|
@ -7,7 +7,9 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { NAMESPACE } from '../../locale';
|
||||
import React from 'react';
|
||||
import { DatePicker } from 'antd';
|
||||
import { NAMESPACE, lang } from '../../locale';
|
||||
import { appends, collection } from '../../schemas/collection';
|
||||
import { SCHEDULE_MODE } from './constants';
|
||||
|
||||
@ -73,13 +75,21 @@ export const ScheduleModes = {
|
||||
type: 'string',
|
||||
title: `{{t('Execute on', { ns: "${NAMESPACE}" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'DatePicker',
|
||||
'x-component': 'WorkflowVariableWrapper',
|
||||
'x-component-props': {
|
||||
showTime: true,
|
||||
placeholder: `{{t('Current time', { ns: "${NAMESPACE}" })}}`,
|
||||
// showTime: true,
|
||||
// placeholder: `{{t('Current time', { ns: "${NAMESPACE}" })}}`,
|
||||
nullable: false,
|
||||
changeOnSelect: true,
|
||||
render(props) {
|
||||
return <DatePicker showTime placeholder={lang('Current time')} {...props} />;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
validate(config) {
|
||||
return Boolean(config.startsOn);
|
||||
},
|
||||
},
|
||||
[SCHEDULE_MODE.DATE_FIELD]: {
|
||||
fieldset: {
|
||||
@ -180,7 +190,7 @@ export const ScheduleModes = {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: `{{t("Trigger data", { ns: "${NAMESPACE}" })}}`,
|
||||
description: `{{t("Choose a record of the collection to trigger.", { ns: "${NAMESPACE}" })}}`,
|
||||
description: `{{t("Choose a record or an ID reference of the collection to trigger.", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'TriggerCollectionRecordSelect',
|
||||
default: null,
|
||||
|
@ -9,14 +9,15 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { SchemaComponent } from '@nocobase/client';
|
||||
import { useFlowContext } from '../../FlowContext';
|
||||
import { useTrigger } from '..';
|
||||
import { SchemaComponent, usePlugin } from '@nocobase/client';
|
||||
import WorkflowPlugin from '../..';
|
||||
import { useCurrentWorkflowContext } from '../../FlowContext';
|
||||
import { ScheduleModes } from './ScheduleModes';
|
||||
|
||||
export function TriggerScheduleConfig() {
|
||||
const { workflow } = useFlowContext();
|
||||
const trigger = useTrigger();
|
||||
const workflow = useCurrentWorkflowContext();
|
||||
const workflowPlugin = usePlugin(WorkflowPlugin);
|
||||
const trigger = workflowPlugin.triggers.get(workflow.type);
|
||||
|
||||
return (
|
||||
<SchemaComponent
|
||||
|
@ -17,6 +17,7 @@ import { ScheduleConfig } from './ScheduleConfig';
|
||||
import { SCHEDULE_MODE } from './constants';
|
||||
import { TriggerScheduleConfig } from './TriggerScheduleConfig';
|
||||
import { ScheduleModes } from './ScheduleModes';
|
||||
import { WorkflowVariableWrapper } from '../../variable';
|
||||
import { TriggerCollectionRecordSelect } from '../../components/TriggerCollectionRecordSelect';
|
||||
|
||||
function useVariables(config, opts) {
|
||||
@ -89,6 +90,7 @@ export default class extends Trigger {
|
||||
ScheduleConfig,
|
||||
TriggerScheduleConfig,
|
||||
TriggerCollectionRecordSelect,
|
||||
WorkflowVariableWrapper,
|
||||
};
|
||||
useVariables = useVariables;
|
||||
useInitializers(config): SchemaInitializerItemType | null {
|
||||
|
@ -7,11 +7,11 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { createContext, useCallback, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { uniqBy } from 'lodash';
|
||||
|
||||
import { Variable, parseCollectionName, useApp, useCompile, usePlugin } from '@nocobase/client';
|
||||
import { Variable, parseCollectionName, useApp, useCompile, usePlugin, useVariableScope } from '@nocobase/client';
|
||||
|
||||
import { useFlowContext } from './FlowContext';
|
||||
import { NAMESPACE, lang } from './locale';
|
||||
@ -120,6 +120,9 @@ export const systemOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const BaseTypeSets = {
|
||||
boolean: new Set(['checkbox']),
|
||||
number: new Set(['integer', 'number', 'percent']),
|
||||
@ -402,3 +405,34 @@ export function WorkflowVariableJSON({ variableOptions, ...props }): JSX.Element
|
||||
const scope = useWorkflowVariableOptions(variableOptions);
|
||||
return <Variable.JSON scope={scope} {...props} />;
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export function WorkflowVariableWrapper(props): JSX.Element {
|
||||
const { render, variableOptions, changeOnSelect, nullable, ...others } = props;
|
||||
const hideVariable = useHideVariable();
|
||||
const scope = useWorkflowVariableOptions(variableOptions);
|
||||
|
||||
if (!hideVariable && scope?.length > 0) {
|
||||
return (
|
||||
<Variable.Input scope={scope} changeOnSelect={changeOnSelect} nullable={nullable} {...others}>
|
||||
{render?.()}
|
||||
</Variable.Input>
|
||||
);
|
||||
}
|
||||
|
||||
return render?.(others);
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export const HideVariableContext = createContext(false);
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export function useHideVariable() {
|
||||
return useContext(HideVariableContext);
|
||||
}
|
||||
|
@ -73,7 +73,7 @@
|
||||
"Preload associations": "预加载关联数据",
|
||||
"Please select the associated fields that need to be accessed in subsequent nodes. With more than two levels of to-many associations may cause performance issue, please use with caution.":
|
||||
"请选中需要在后续节点中被访问的关系字段。超过两层的对多关联可能会导致性能问题,请谨慎使用。",
|
||||
"Choose a record of the collection to trigger.": "选择数据表中的一行记录来触发。",
|
||||
"Choose a record or primary key of a record in the collection to trigger.": "选择数据表中的一行记录或者记录的主键来触发。",
|
||||
|
||||
"Schedule event": "定时任务",
|
||||
"Triggered according to preset time conditions. Suitable for one-time or periodic tasks, such as sending notifications and cleaning data on a schedule.": "按预设的时间条件定时触发。适用于一次性或周期性的任务,如定时发送通知、清理数据等。",
|
||||
|
@ -47,6 +47,9 @@ export type EventOptions = {
|
||||
context?: any;
|
||||
deferred?: boolean;
|
||||
manually?: boolean;
|
||||
force?: boolean;
|
||||
stack?: Array<ID>;
|
||||
onTriggerFail?: Function;
|
||||
[key: string]: any;
|
||||
} & Transactionable;
|
||||
|
||||
@ -371,7 +374,7 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
logger.debug(`ignored event data:`, context);
|
||||
return;
|
||||
}
|
||||
if (!options.manually && !workflow.enabled) {
|
||||
if (!options.force && !options.manually && !workflow.enabled) {
|
||||
logger.warn(`workflow ${workflow.id} is not enabled, event will be ignored`);
|
||||
return;
|
||||
}
|
||||
@ -448,6 +451,33 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
return new Processor(execution, { ...options, plugin: this });
|
||||
}
|
||||
|
||||
private async validateEvent(workflow: WorkflowModel, context: any, options: EventOptions) {
|
||||
const trigger = this.triggers.get(workflow.type);
|
||||
const triggerValid = await trigger.validateEvent(workflow, context, options);
|
||||
if (!triggerValid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { stack } = options;
|
||||
let valid = true;
|
||||
if (stack?.length > 0) {
|
||||
const existed = await workflow.countExecutions({
|
||||
where: {
|
||||
id: stack,
|
||||
},
|
||||
transaction: options.transaction,
|
||||
});
|
||||
|
||||
if (existed) {
|
||||
this.getLogger(workflow.id).warn(
|
||||
`workflow ${workflow.id} has already been triggered in stacks executions (${stack}), and newly triggering will be skipped.`,
|
||||
);
|
||||
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
private async createExecution(
|
||||
workflow: WorkflowModel,
|
||||
context,
|
||||
@ -456,13 +486,13 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
const { deferred } = options;
|
||||
const transaction = await this.useDataSourceTransaction('main', options.transaction, true);
|
||||
const sameTransaction = options.transaction === transaction;
|
||||
const trigger = this.triggers.get(workflow.type);
|
||||
const valid = await trigger.validateEvent(workflow, context, { ...options, transaction });
|
||||
const valid = await this.validateEvent(workflow, context, { ...options, transaction });
|
||||
if (!valid) {
|
||||
if (!sameTransaction) {
|
||||
await transaction.commit();
|
||||
}
|
||||
return null;
|
||||
options.onTriggerFail?.(workflow, context, options);
|
||||
return Promise.reject(new Error('event is not valid'));
|
||||
}
|
||||
|
||||
let execution;
|
||||
@ -472,6 +502,7 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
context,
|
||||
key: workflow.key,
|
||||
eventKey: options.eventKey ?? randomUUID(),
|
||||
stack: options.stack,
|
||||
status: deferred ? EXECUTION_STATUS.STARTED : EXECUTION_STATUS.QUEUEING,
|
||||
},
|
||||
{ transaction },
|
||||
@ -533,8 +564,8 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
if (execution?.status === EXECUTION_STATUS.QUEUEING && !this.executing && !this.pending.length) {
|
||||
this.pending.push([execution]);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`failed to create execution: ${err.message}`, err);
|
||||
} catch (error) {
|
||||
logger.error(`failed to create execution:`, { error });
|
||||
// this.events.push(event); // NOTE: retry will cause infinite loop
|
||||
}
|
||||
|
||||
@ -587,8 +618,11 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
if (next) {
|
||||
await this.process(...next);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
this.executing = null;
|
||||
this.getLogger('dispatcher').info(`execution dispatched finished`);
|
||||
|
||||
if (next) {
|
||||
this.dispatch();
|
||||
@ -624,7 +658,7 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
return processor;
|
||||
}
|
||||
|
||||
async execute(workflow: WorkflowModel, context: Context, options: EventOptions = {}) {
|
||||
async execute(workflow: WorkflowModel, values, options: EventOptions = {}) {
|
||||
const trigger = this.triggers.get(workflow.type);
|
||||
if (!trigger) {
|
||||
throw new Error(`trigger type "${workflow.type}" of workflow ${workflow.id} is not registered`);
|
||||
@ -632,7 +666,7 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
if (!trigger.execute) {
|
||||
throw new Error(`"execute" method of trigger ${workflow.type} is not implemented`);
|
||||
}
|
||||
return trigger.execute(workflow, context, options);
|
||||
return trigger.execute(workflow, values, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,7 +108,10 @@ export async function trigger(context: Context, next) {
|
||||
|
||||
export async function execute(context: Context, next) {
|
||||
const plugin = context.app.pm.get(Plugin) as Plugin;
|
||||
const { filterByTk, autoRevision } = context.action.params;
|
||||
const { filterByTk, values, autoRevision } = context.action.params;
|
||||
if (!values) {
|
||||
return context.throw(400, 'values is required');
|
||||
}
|
||||
if (!filterByTk) {
|
||||
return context.throw(400, 'filterByTk is required');
|
||||
}
|
||||
@ -124,7 +127,7 @@ export async function execute(context: Context, next) {
|
||||
const { executed } = workflow;
|
||||
let processor;
|
||||
try {
|
||||
processor = (await plugin.execute(workflow, context, { manually: true })) as Processor;
|
||||
processor = (await plugin.execute(workflow, values, { manually: true })) as Processor;
|
||||
if (!processor) {
|
||||
return context.throw(400, 'workflow not triggered');
|
||||
}
|
||||
|
@ -42,5 +42,13 @@ export default {
|
||||
type: 'integer',
|
||||
name: 'status',
|
||||
},
|
||||
{
|
||||
type: 'json',
|
||||
name: 'stack',
|
||||
},
|
||||
{
|
||||
type: 'json',
|
||||
name: 'output',
|
||||
},
|
||||
],
|
||||
} as CollectionOptions;
|
||||
|
@ -29,7 +29,7 @@ export class CreateInstruction extends Instruction {
|
||||
const created = await repository.create({
|
||||
...options,
|
||||
context: {
|
||||
stack: Array.from(new Set((processor.execution.context.stack ?? []).concat(processor.execution.id))),
|
||||
stack: Array.from(new Set((processor.execution.stack ?? []).concat(processor.execution.id))),
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
@ -27,7 +27,7 @@ export class DestroyInstruction extends Instruction {
|
||||
const result = await repository.destroy({
|
||||
...options,
|
||||
context: {
|
||||
stack: Array.from(new Set((processor.execution.context.stack ?? []).concat(processor.execution.id))),
|
||||
stack: Array.from(new Set((processor.execution.stack ?? []).concat(processor.execution.id))),
|
||||
},
|
||||
transaction: this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction),
|
||||
});
|
||||
|
@ -27,7 +27,7 @@ export class UpdateInstruction extends Instruction {
|
||||
const result = await repository.update({
|
||||
...options,
|
||||
context: {
|
||||
stack: Array.from(new Set((processor.execution.context.stack ?? []).concat(processor.execution.id))),
|
||||
stack: Array.from(new Set((processor.execution.stack ?? []).concat(processor.execution.id))),
|
||||
},
|
||||
transaction: this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction),
|
||||
});
|
||||
|
@ -59,23 +59,24 @@ export default class CollectionTrigger extends Trigger {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { stack } = options.context ?? {};
|
||||
if (workflow.sync) {
|
||||
await this.workflow.trigger(workflow, ctx, {
|
||||
transaction,
|
||||
stack,
|
||||
});
|
||||
} else {
|
||||
if (transaction) {
|
||||
transaction.afterCommit(() => {
|
||||
this.workflow.trigger(workflow, ctx);
|
||||
this.workflow.trigger(workflow, ctx, { stack });
|
||||
});
|
||||
} else {
|
||||
this.workflow.trigger(workflow, ctx);
|
||||
this.workflow.trigger(workflow, ctx, { stack });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async prepare(workflow: WorkflowModel, data: Model | Record<string, any>, options) {
|
||||
async prepare(workflow: WorkflowModel, data: Model | Record<string, any> | string | number, options) {
|
||||
const { condition, changed, mode, appends } = workflow.config;
|
||||
const [dataSourceName, collectionName] = parseCollectionName(workflow.config.collection);
|
||||
const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
|
||||
@ -83,9 +84,23 @@ export default class CollectionTrigger extends Trigger {
|
||||
const { transaction, context } = options;
|
||||
const { repository, filterTargetKey } = collection;
|
||||
|
||||
let target = data;
|
||||
let filterByTk;
|
||||
let loadNeeded = false;
|
||||
if (target && typeof target === 'object') {
|
||||
filterByTk = Array.isArray(filterTargetKey)
|
||||
? pick(
|
||||
target,
|
||||
filterTargetKey.sort((a, b) => a.localeCompare(b)),
|
||||
)
|
||||
: target[filterTargetKey];
|
||||
} else {
|
||||
filterByTk = target;
|
||||
loadNeeded = true;
|
||||
}
|
||||
// NOTE: if no configured fields changed, do not trigger
|
||||
if (
|
||||
data instanceof Model &&
|
||||
target instanceof Model &&
|
||||
changed &&
|
||||
changed.length &&
|
||||
changed
|
||||
@ -93,14 +108,11 @@ export default class CollectionTrigger extends Trigger {
|
||||
const field = collection.getField(name);
|
||||
return field && !['linkTo', 'hasOne', 'hasMany', 'belongsToMany'].includes(field.options.type);
|
||||
})
|
||||
.every((name) => !data.changedWithAssociations(getFieldRawName(collection, name)))
|
||||
.every((name) => !(<Model>target).changedWithAssociations(getFieldRawName(collection, name)))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filterByTk = Array.isArray(filterTargetKey)
|
||||
? pick(data, filterTargetKey)
|
||||
: { [filterTargetKey]: data[filterTargetKey] };
|
||||
// NOTE: if no configured condition, or not match, do not trigger
|
||||
if (isValidFilter(condition) && !(mode & MODE_BITMAP.DESTROY)) {
|
||||
// TODO: change to map filter format to calculation format
|
||||
@ -117,17 +129,14 @@ export default class CollectionTrigger extends Trigger {
|
||||
}
|
||||
}
|
||||
|
||||
let result = data;
|
||||
|
||||
if (appends?.length && !(mode & MODE_BITMAP.DESTROY)) {
|
||||
if (loadNeeded || (appends?.length && !(mode & MODE_BITMAP.DESTROY))) {
|
||||
const includeFields = appends.reduce((set, field) => {
|
||||
set.add(field.split('.')[0]);
|
||||
set.add(field);
|
||||
return set;
|
||||
}, new Set());
|
||||
|
||||
// @ts-ignore
|
||||
result = await repository.findOne({
|
||||
target = await repository.findOne({
|
||||
filterByTk,
|
||||
appends: Array.from(includeFields),
|
||||
transaction,
|
||||
@ -135,8 +144,7 @@ export default class CollectionTrigger extends Trigger {
|
||||
}
|
||||
|
||||
return {
|
||||
data: toJSON(result),
|
||||
stack: context?.stack,
|
||||
data: toJSON(target),
|
||||
};
|
||||
}
|
||||
|
||||
@ -195,31 +203,31 @@ export default class CollectionTrigger extends Trigger {
|
||||
}
|
||||
}
|
||||
|
||||
async validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean> {
|
||||
if (context.stack) {
|
||||
const existed = await workflow.countExecutions({
|
||||
where: {
|
||||
id: context.stack,
|
||||
},
|
||||
transaction: options.transaction,
|
||||
});
|
||||
// async validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean> {
|
||||
// if (context.stack) {
|
||||
// const existed = await workflow.countExecutions({
|
||||
// where: {
|
||||
// id: context.stack,
|
||||
// },
|
||||
// transaction: options.transaction,
|
||||
// });
|
||||
|
||||
if (existed) {
|
||||
this.workflow
|
||||
.getLogger(workflow.id)
|
||||
.warn(
|
||||
`workflow ${workflow.id} has already been triggered in stack executions (${context.stack}), and newly triggering will be skipped.`,
|
||||
);
|
||||
// if (existed) {
|
||||
// this.workflow
|
||||
// .getLogger(workflow.id)
|
||||
// .warn(
|
||||
// `workflow ${workflow.id} has already been triggered in stack executions (${context.stack}), and newly triggering will be skipped.`,
|
||||
// );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
// return true;
|
||||
// }
|
||||
|
||||
async execute(workflow: WorkflowModel, context: Context, options: EventOptions) {
|
||||
const ctx = await this.prepare(workflow, context.action.params.values?.data, options);
|
||||
async execute(workflow: WorkflowModel, values, options: EventOptions) {
|
||||
const ctx = await this.prepare(workflow, values?.data, options);
|
||||
const [dataSourceName] = parseCollectionName(workflow.config.collection);
|
||||
const { transaction } = options;
|
||||
return this.workflow.trigger(workflow, ctx, {
|
||||
|
@ -13,6 +13,7 @@ import type Plugin from '../../Plugin';
|
||||
import type { WorkflowModel } from '../../types';
|
||||
import { parseDateWithoutMs, SCHEDULE_MODE } from './utils';
|
||||
import { parseCollectionName, SequelizeCollectionManager, SequelizeDataSource } from '@nocobase/data-source-manager';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
export type ScheduleOnField = {
|
||||
field: string;
|
||||
@ -403,4 +404,33 @@ export default class DateFieldScheduleTrigger {
|
||||
this.events.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
async execute(workflow, values, options) {
|
||||
const [dataSourceName, collectionName] = parseCollectionName(workflow.config.collection);
|
||||
const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
|
||||
const { filterTargetKey, repository } = collectionManager.getCollection(collectionName);
|
||||
|
||||
let { data } = values;
|
||||
let filterByTk;
|
||||
let loadNeeded = false;
|
||||
if (data && typeof data === 'object') {
|
||||
filterByTk = Array.isArray(filterTargetKey)
|
||||
? pick(
|
||||
data,
|
||||
filterTargetKey.sort((a, b) => a.localeCompare(b)),
|
||||
)
|
||||
: data[filterTargetKey];
|
||||
} else {
|
||||
filterByTk = data;
|
||||
loadNeeded = true;
|
||||
}
|
||||
if (loadNeeded || workflow.config.appends?.length) {
|
||||
data = await repository.findOne({
|
||||
filterByTk,
|
||||
appends: workflow.config.appends,
|
||||
});
|
||||
}
|
||||
|
||||
return this.workflow.trigger(workflow, { ...values, data, date: values?.date ?? new Date() }, options);
|
||||
}
|
||||
}
|
||||
|
@ -136,4 +136,8 @@ export default class StaticScheduleTrigger {
|
||||
off(workflow) {
|
||||
this.schedule(workflow, null, false);
|
||||
}
|
||||
|
||||
execute(workflow, values, options) {
|
||||
return this.workflow.trigger(workflow, { ...values, date: values?.date ?? new Date() }, options);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import { Context } from '@nocobase/actions';
|
||||
import { parseCollectionName } from '@nocobase/data-source-manager';
|
||||
import Trigger from '..';
|
||||
import type Plugin from '../../Plugin';
|
||||
import DateFieldScheduleTrigger from './DateFieldScheduleTrigger';
|
||||
@ -46,9 +47,12 @@ export default class ScheduleTrigger extends Trigger {
|
||||
}
|
||||
}
|
||||
|
||||
async execute(workflow, context: Context, options) {
|
||||
const { values } = context.action.params;
|
||||
return this.workflow.trigger(workflow, { ...values, date: values?.date ?? new Date() }, options);
|
||||
async execute(workflow, values: any, options) {
|
||||
const mode = workflow.config.mode;
|
||||
const trigger = this.getTrigger(mode);
|
||||
if (trigger) {
|
||||
return trigger.execute(workflow, values, options);
|
||||
}
|
||||
}
|
||||
|
||||
// async validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean> {
|
||||
|
@ -23,7 +23,7 @@ export abstract class Trigger {
|
||||
sync?: boolean;
|
||||
execute?(
|
||||
workflow: WorkflowModel,
|
||||
context: any,
|
||||
values: any,
|
||||
options: Transactionable,
|
||||
): void | Processor | Promise<void | Processor>;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user