diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/Plugin.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/Plugin.tsx index c558b1413..03b764584 100644 --- a/packages/plugins/@tachybase/plugin-workflow/src/client/Plugin.tsx +++ b/packages/plugins/@tachybase/plugin-workflow/src/client/Plugin.tsx @@ -107,6 +107,12 @@ export class PluginWorkflow extends Plugin { this.addComponents(); this.app.pluginSettingsManager.add(NAMESPACE, { + icon: 'PartitionOutlined', + title: `{{t("Workflow", { ns: "${NAMESPACE}" })}}`, + aclSnippet: 'pm.workflow.workflows', + }); + + this.app.pluginSettingsManager.add(NAMESPACE + '.list', { icon: 'PartitionOutlined', title: `{{t("Workflow", { ns: "${NAMESPACE}" })}}`, Component: WorkflowPane, diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/configuration/APIRegular.trigger.ts b/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/configuration/APIRegular.trigger.ts deleted file mode 100644 index 2c717fc18..000000000 --- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/configuration/APIRegular.trigger.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { - SchemaInitializerItemType, - useCollectionDataSource, - useCollectionManager_deprecated, - useCompile, -} from '@tachybase/client'; - -import { - CheckboxGroupWithTooltip, - CollectionBlockInitializer, - FieldsSelect, - getCollectionFieldOptions, - RadioWithTooltip, -} from '../../..'; -import { lang, tval } from '../../../locale'; -import { Trigger } from '../../../triggers'; - -const enum ACTION_TYPES { - CREATE = 'create', - UPDATE = 'update', - UPSERT = 'updateOrCreate', - DESTROY = 'destroy', -} -export class APIRegularTrigger extends Trigger { - title = lang('API Regular(deprecated)'); - description = lang('Trigger when an API call is made.'); - fieldset = { - collection: { - type: 'string', - title: tval('Collection'), - required: true, - 'x-decorator': 'FormItem', - 'x-component': 'DataSourceCollectionCascader', - 'x-reactions': [ - { - target: 'changed', - effects: ['onFieldValueChange'], - fulfill: { - state: { - value: [], - }, - }, - }, - ], - }, - global: { - type: 'boolean', - title: tval('Trigger mode'), - 'x-decorator': 'FormItem', - 'x-component': 'RadioWithTooltip', - 'x-component-props': { - direction: 'vertical', - options: [ - { - label: tval('Local mode, triggered before executing the actions bound to this workflow'), - value: false, - }, - { - label: tval('Global mode, triggered before executing the following actions'), - value: true, - }, - ], - }, - default: false, - }, - actions: { - type: 'number', - title: tval('Select actions'), - 'x-decorator': 'FormItem', - 'x-component': 'CheckboxGroupWithTooltip', - 'x-component-props': { - direction: 'vertical', - options: [ - { label: tval('Create record action'), value: ACTION_TYPES.CREATE }, - { label: tval('Update record action'), value: ACTION_TYPES.UPDATE }, - { label: tval('Delete record action'), value: ACTION_TYPES.DESTROY }, - ], - }, - required: true, - 'x-reactions': [ - { - dependencies: ['collection', 'global'], - fulfill: { - state: { - visible: '{{!!$deps[0] && !!$deps[1]}}', - }, - }, - }, - ], - }, - }; - - scope = { useCollectionDataSource }; - - components = { - FieldsSelect: FieldsSelect, - RadioWithTooltip: RadioWithTooltip, - CheckboxGroupWithTooltip: CheckboxGroupWithTooltip, - }; - - isActionTriggerable = (config, context) => { - const { global } = config; - return !global && !context.direct; - }; - - useVariables(config, options) { - // eslint-disable-next-line react-hooks/rules-of-hooks - const compile = useCompile(); - // eslint-disable-next-line react-hooks/rules-of-hooks - const { getCollectionFields } = useCollectionManager_deprecated(); - // eslint-disable-next-line react-hooks/rules-of-hooks - const langTriggerData = lang('Trigger data'); - // eslint-disable-next-line react-hooks/rules-of-hooks - const langUserSubmittedForm = lang('User submitted action'); - // eslint-disable-next-line react-hooks/rules-of-hooks - const langRoleSubmittedForm = lang('Role of user submitted action'); - const rootFields = [ - { - collectionName: config.collection, - name: 'data', - type: 'hasOne', - target: config.collection, - uiSchema: { - title: langTriggerData, - }, - }, - { - collectionName: 'users', - name: 'user', - type: 'hasOne', - target: 'users', - uiSchema: { - title: langUserSubmittedForm, - }, - }, - { - name: 'roleName', - uiSchema: { - title: langRoleSubmittedForm, - }, - }, - ]; - const result = getCollectionFieldOptions({ - // depth, - appends: ['data', 'user', ...(config.appends?.map((item) => `data.${item}`) || [])], - ...options, - fields: rootFields, - compile, - getCollectionFields, - }); - return result; - } - useInitializers(config): SchemaInitializerItemType | null { - if (!config.collection) { - return null; - } - - return { - name: 'triggerData', - type: 'item', - key: 'triggerData', - title: tval('Trigger data'), - Component: CollectionBlockInitializer, - collection: config.collection, - dataPath: '$context.data', - }; - } -} diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/configuration/kit.ts b/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/configuration/kit.ts index 1fed37340..fcd211472 100644 --- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/configuration/kit.ts +++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/configuration/kit.ts @@ -1,12 +1,3 @@ import { Plugin } from '@tachybase/client'; -import PluginWorkflow from '../../..'; -import { NAMESPACE_TRIGGER_API_REGULAR } from '../../../../common/constants'; -import { APIRegularTrigger } from './APIRegular.trigger'; - -export class KitAPIRegularConfiguration extends Plugin { - async load() { - const pluginWorkflow = this.app.pm.get(PluginWorkflow); - pluginWorkflow.registerTrigger(NAMESPACE_TRIGGER_API_REGULAR, APIRegularTrigger); - } -} +export class KitAPIRegularConfiguration extends Plugin {} diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/usage/APIRegular.setting.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/usage/APIRegular.setting.tsx index 209d9af1f..02af9f20a 100644 --- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/usage/APIRegular.setting.tsx +++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/api-regular/usage/APIRegular.setting.tsx @@ -3,7 +3,6 @@ import { SchemaSettings, SchemaSettingsItemType, useDesignable, - useRequest, useSchemaToolbar, WorkflowConfig, } from '@tachybase/client'; @@ -11,8 +10,6 @@ import { isValid, useFieldSchema } from '@tachybase/schema'; import { useTranslation } from 'react-i18next'; -import { NAMESPACE_TRIGGER_API_REGULAR } from '../../../../common/constants'; - const schemaSettingsItems: SchemaSettingsItemType[] = [ { name: 'editButton', @@ -56,47 +53,6 @@ const schemaSettingsItems: SchemaSettingsItemType[] = [ return isValid(fieldSchema?.['x-action-settings']?.triggerWorkflows); }, }, - { - name: 'Bind workflow', - type: 'select', - useComponentProps() { - const { dn } = useDesignable(); - const { t } = useTranslation(); - const fieldSchema = useFieldSchema(); - const { data } = useRequest({ - resource: 'workflows', - action: 'list', - params: { - filter: { - type: NAMESPACE_TRIGGER_API_REGULAR, - }, - }, - }); - // FIXME 不会实时生效 - const options = - data?.data?.map((workflow) => ({ - label: workflow.title, - value: workflow.id, - })) ?? []; - return { - title: t('Bind workflow'), - value: fieldSchema['x-action-settings'].bindWorkflow, - options: options.concat({ - label: t('disabled'), - value: false, - }), - onChange(value) { - fieldSchema['x-action-settings'].bindWorkflow = value; - dn.emit('patch', { - schema: { - 'x-uid': fieldSchema['x-uid'], - 'x-action-settings': fieldSchema['x-action-settings'], - }, - }); - }, - }; - }, - }, { name: 'remove', sort: 100, diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/webhook/WebhookManager.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/webhook/WebhookManager.tsx index f717c2060..01a716fa5 100644 --- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/webhook/WebhookManager.tsx +++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/webhook/WebhookManager.tsx @@ -1,22 +1,23 @@ import React from 'react'; import { - CollectionOptions, ExtendCollectionsProvider, ResourceActionProvider, SchemaComponent, + useAPIClient, useRecord, WorkflowSelect, } from '@tachybase/client'; import { CodeMirror } from '@tachybase/components'; -import { ISchema } from '@tachybase/schema'; +import { ISchema, useForm } from '@tachybase/schema'; import { Button, Space } from 'antd'; import { ExecutionStatusColumn } from '../../components/ExecutionStatus'; import OpenDrawer from '../../components/OpenDrawer'; import { ExecutionLink } from '../../ExecutionLink'; -import { lang, tval } from '../../locale'; +import { lang } from '../../locale'; import { executionSchema } from '../../schemas/executions'; +import { dispatchers } from './collections/dispatchers'; export const ExecutionResourceProvider = ({ request, filter = {}, ...others }) => { const webhook = useRecord(); @@ -37,84 +38,22 @@ export const ExecutionResourceProvider = ({ request, filter = {}, ...others }) = return ; }; -export const collection: CollectionOptions = { - name: 'webhooks', - title: 'webhooks', - fields: [ - { - type: 'string', - name: 'name', - interface: 'input', - uiSchema: { - title: tval('Name'), - type: 'string', - 'x-component': 'Input', - required: true, - } as ISchema, - }, - { - type: 'boolean', - name: 'enabled', - interface: 'radioGroup', - uiSchema: { - title: tval('Enabled'), - type: 'string', - required: true, - enum: [ - { label: tval('On'), value: true }, - { label: tval('Off'), value: false }, - ], - 'x-component': 'Radio.Group', - 'x-decorator': 'FormItem', - default: false, - } as ISchema, - }, - { - type: 'string', - name: 'workflowKey', - interface: 'select', - uiSchema: { - title: tval('Workflow'), - type: 'string', - 'x-component': 'WorkflowSelect', - 'x-component-props': { - buttonAction: 'customize:triggerWorkflows', - noCollection: true, - label: 'title', - value: 'key', +export const useTestActionProps = () => { + const form = useForm(); + const webhook = useRecord(); + const api = useAPIClient(); + return { + async onClick() { + const res = await api.resource('webhooks').test({ + values: { + body: JSON.parse(form.values.body), + params: JSON.parse(form.values.params), + name: webhook.name, }, - } as ISchema, + }); + alert(JSON.stringify(res.data)); }, - { - type: 'string', - name: 'type', - interface: 'radioGroup', - uiSchema: { - title: tval('Type'), - type: 'string', - required: true, - enum: [ - { label: tval('Code'), value: 'code' }, - { label: tval('Plugin'), value: 'plugin', disabled: true }, - ], - 'x-component': 'Radio.Group', - 'x-decorator': 'FormItem', - default: 'code', - } as ISchema, - }, - { - type: 'text', - name: 'code', - interface: 'textarea', - uiSchema: { - title: tval('Code'), - type: 'string', - 'x-component': 'CodeMirror', - default: - '// ctx.query can get user query\n// ctx.body to pass your data to workflow or to client who trigger this webhook.', - } as ISchema, - }, - ], + }; }; const schema: ISchema = { @@ -126,7 +65,7 @@ const schema: ISchema = { 'x-acl-action': 'webhooks:list', 'x-use-decorator-props': 'useTableBlockDecoratorProps', 'x-decorator-props': { - collection, + collection: dispatchers, dataSource: 'main', action: 'list', params: { @@ -207,7 +146,7 @@ const schema: ISchema = { 'x-use-decorator-props': 'useCreateFormBlockDecoratorProps', 'x-decorator-props': { dataSource: 'main', - collection, + collection: dispatchers, }, 'x-component': 'CardItem', properties: { @@ -382,6 +321,9 @@ const schema: ISchema = { type: 'void', 'x-decorator': 'TableV2.Column.Decorator', 'x-component': 'TableV2.Column', + 'x-component-props': { + width: 50, + }, properties: { name: { 'x-collection-field': 'webhooks.name', @@ -403,6 +345,9 @@ const schema: ISchema = { type: 'void', 'x-decorator': 'TableV2.Column.Decorator', 'x-component': 'TableV2.Column', + 'x-component-props': { + width: 20, + }, properties: { enabled: { 'x-collection-field': 'webhooks.enabled', @@ -424,6 +369,9 @@ const schema: ISchema = { type: 'void', 'x-decorator': 'TableV2.Column.Decorator', 'x-component': 'TableV2.Column', + 'x-component-props': { + width: 20, + }, properties: { workflowKey: { 'x-collection-field': 'webhooks.workflowKey', @@ -455,6 +403,9 @@ const schema: ISchema = { type: 'void', 'x-decorator': 'TableV2.Column.Decorator', 'x-component': 'TableV2.Column', + 'x-component-props': { + width: 20, + }, properties: { type: { 'x-collection-field': 'webhooks.type', @@ -542,7 +493,7 @@ const schema: ISchema = { 'x-decorator-props': { action: 'get', dataSource: 'main', - collection, + collection: dispatchers, }, 'x-component': 'CardItem', properties: { @@ -722,6 +673,153 @@ const schema: ISchema = { 'x-decorator': 'ACLActionProvider', type: 'void', }, + test: { + type: 'void', + title: '{{ t("Test") }}', + 'x-action': 'update', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'drawer', + icon: 'EditOutlined', + }, + 'x-decorator': 'ACLActionProvider', + properties: { + drawer: { + type: 'void', + title: '{{ t("Edit record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + properties: { + tab1: { + type: 'void', + title: '{{t("Edit")}}', + 'x-component': 'Tabs.TabPane', + 'x-component-props': {}, + properties: { + grid: { + type: 'void', + 'x-component': 'Grid', + properties: { + to6mk4v552h: { + type: 'void', + 'x-component': 'Grid.Row', + properties: { + sqvdrzdbr7r: { + type: 'void', + 'x-component': 'Grid.Col', + properties: { + '8uym9fty5oy': { + type: 'void', + 'x-acl-action-props': { + skipScopeCheck: false, + }, + 'x-acl-action': 'webhooks:update', + 'x-decorator': 'FormBlockProvider', + 'x-use-decorator-props': 'useEditFormBlockDecoratorProps', + 'x-decorator-props': { + action: 'get', + dataSource: 'main', + collection: dispatchers, + }, + 'x-component': 'CardItem', + properties: { + je28rbthifp: { + type: 'void', + 'x-component': 'FormV2', + 'x-use-component-props': 'useEditFormBlockProps', + properties: { + yviwt5e73dx: { + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--tb-spacing)', + }, + }, + properties: { + unkmoqgvtvr: { + title: '{{ t("Submit") }}', + 'x-action': 'submit', + 'x-component': 'Action', + 'x-use-component-props': 'useTestActionProps', + 'x-component-props': { + type: 'primary', + htmlType: 'submit', + }, + 'x-action-settings': { + triggerWorkflows: [], + onSuccess: { + manualClose: false, + redirecting: false, + successMessage: '{{t("Updated successfully")}}', + }, + isDeltaChanged: false, + }, + type: 'void', + }, + }, + }, + grid: { + type: 'void', + 'x-component': 'Grid', + properties: { + '38fm61ehxvn': { + type: 'void', + 'x-component': 'Grid.Row', + properties: { + arovreokda8: { + type: 'void', + 'x-component': 'Grid.Col', + properties: { + params: { + type: 'string', + 'x-component': 'CodeMirror', + 'x-component-props': {}, + 'x-decorator': 'FormItem', + 'x-decorator-props': { + label: 'query', + }, + }, + body: { + type: 'string', + 'x-component': 'CodeMirror', + 'x-component-props': {}, + 'x-decorator': 'FormItem', + 'x-decorator-props': { + label: 'body', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, }, }, }, @@ -735,10 +833,11 @@ const schema: ISchema = { export const WebhookManager = () => { return ( - + ( + + + +); + +const Dispatcher = (props) => ; + +Icon.register({ Dispatcher }); + export class PluginWebhook extends Plugin { async load() { - this.app.pluginSettingsManager.add('webhook', { - title: tval('Webhook manager'), - icon: 'ShareAltOutlined', + this.app.pluginSettingsManager.add('workflow.webhook', { + title: tval('Dispatcher'), + icon: 'Dispatcher', Component: WebhookManager, }); } diff --git a/packages/plugins/@tachybase/plugin-workflow/src/locale/zh-CN.json b/packages/plugins/@tachybase/plugin-workflow/src/locale/zh-CN.json index 4fe79995d..4364b4d9e 100644 --- a/packages/plugins/@tachybase/plugin-workflow/src/locale/zh-CN.json +++ b/packages/plugins/@tachybase/plugin-workflow/src/locale/zh-CN.json @@ -123,6 +123,7 @@ "Delete record": "删除数据", "Delete records of a collection. Could use variables in workflow context as filter. All records match the filter will be deleted.": "删除一个数据表中的数据。可以使用上游节点里的变量作为过滤条件。所有满足条件的数据都将被删除。", "Disabled": "已失效", + "Dispatcher": "调度器", "Distinct": "去重", "Draft": "草稿", "Duplicate to new workflow": "复制为新工作流", diff --git a/packages/plugins/@tachybase/plugin-workflow/src/server/features/webhook/Plugin.ts b/packages/plugins/@tachybase/plugin-workflow/src/server/features/webhook/Plugin.ts index c81f7c0da..ac3c1e0fd 100644 --- a/packages/plugins/@tachybase/plugin-workflow/src/server/features/webhook/Plugin.ts +++ b/packages/plugins/@tachybase/plugin-workflow/src/server/features/webhook/Plugin.ts @@ -12,8 +12,9 @@ export class PluginWebhook extends Plugin { name: 'webhooks', actions: { trigger: new WebhookController().getLink, + test: new WebhookController().test, }, }); - this.app.acl.allow('webhooks', 'trigger', 'loggedIn'); + this.app.acl.allow('webhooks', ['trigger', 'test'], 'loggedIn'); } } diff --git a/packages/plugins/@tachybase/plugin-workflow/src/server/features/webhook/webhooks.ts b/packages/plugins/@tachybase/plugin-workflow/src/server/features/webhook/webhooks.ts index e09cda16b..c5133c301 100644 --- a/packages/plugins/@tachybase/plugin-workflow/src/server/features/webhook/webhooks.ts +++ b/packages/plugins/@tachybase/plugin-workflow/src/server/features/webhook/webhooks.ts @@ -11,6 +11,7 @@ export class WebhookController { if (name) { where['filter'] = { name: name, + type: 'code', enabled: true, }; } @@ -52,6 +53,14 @@ export class WebhookController { return; } } + async test(ctx: Context) { + const { name, params, body } = ctx.action.params.values; + ctx.request.query = params; + ctx.action.params = params || {}; + ctx.action.params.name = name; + ctx.action.params.values = body; + await new WebhookController().getLink(ctx); + } } function run(jsCode: string, { ctx, lib }) {