diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/omni-trigger/OmniActionTrigger.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/omni-trigger/OmniActionTrigger.tsx index da7c3cbfe..4d47b8dfd 100644 --- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/omni-trigger/OmniActionTrigger.tsx +++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/omni-trigger/OmniActionTrigger.tsx @@ -55,8 +55,11 @@ export class OmniActionTrigger extends Trigger { }; scope = { useCollectionDataSource }; components = {}; - isActionTriggerable = (config, context) => { - return context.buttonAction === 'customize:triggerWorkflows'; + isActionTriggerable = (config, context: any = {}) => { + const { buttonAction } = context; + const conditionList = [buttonAction === 'customize:triggerWorkflows', buttonAction === 'submit']; + const isAvailable = conditionList.some((val) => val === true); + return isAvailable; }; useVariables(config: Record, options?: UseVariableOptions) { // eslint-disable-next-line react-hooks/rules-of-hooks diff --git a/packages/plugins/@tachybase/plugin-workflow/src/server/features/omni-trigger/CustomActionTrigger.ts b/packages/plugins/@tachybase/plugin-workflow/src/server/features/omni-trigger/CustomActionTrigger.ts index 4c9eb7361..d49b7e55f 100644 --- a/packages/plugins/@tachybase/plugin-workflow/src/server/features/omni-trigger/CustomActionTrigger.ts +++ b/packages/plugins/@tachybase/plugin-workflow/src/server/features/omni-trigger/CustomActionTrigger.ts @@ -1,7 +1,9 @@ +import { Context, Next } from '@tachybase/actions'; import { joinCollectionName, parseCollectionName } from '@tachybase/data-source-manager'; +import { Model, modelAssociationByKey } from '@tachybase/database'; import PluginErrorHandler from '@tachybase/plugin-error-handler'; -import _, { isArray } from 'lodash'; +import _, { get, isArray } from 'lodash'; import { EXECUTION_STATUS } from '../../constants'; import Trigger from '../../triggers'; @@ -17,6 +19,21 @@ class CustomActionInterceptionError extends Error { } export class OmniTrigger extends Trigger { static TYPE = 'general-action'; + constructor(workflow) { + super(workflow); + this.workflow.app.resourcer.registerActionHandler('trigger', this.triggerAction); + this.workflow.app.acl.allow('*', 'trigger', 'loggedIn'); + (this.workflow.app.pm.get(PluginErrorHandler) as PluginErrorHandler).errorHandler.register( + (err) => err instanceof CustomActionInterceptionError, + async (err, ctx) => { + ctx.body = { + errors: err.messages, + }; + ctx.status = err.status; + }, + ); + workflow.app.use(this.middleware, { after: 'dataSource' }); + } triggerAction = async (context, next) => { const { params: { filterByTk, values, triggerWorkflows = '', filter, resourceName, actionName }, @@ -123,20 +140,127 @@ export class OmniTrigger extends Trigger { } await next(); }; - constructor(workflow) { - super(workflow); - this.workflow.app.resourcer.registerActionHandler('trigger', this.triggerAction); - this.workflow.app.acl.allow('*', 'trigger', 'loggedIn'); - (this.workflow.app.pm.get(PluginErrorHandler) as PluginErrorHandler).errorHandler.register( - (err) => err instanceof CustomActionInterceptionError, - async (err, ctx) => { - ctx.body = { - errors: err.messages, - }; - ctx.status = err.status; - }, - ); + + middleware = async (context: Context, next: Next) => { + const { + resourceName, + actionName, + params: { triggerWorkflows }, + } = context.action; + + if (resourceName === 'workflows' && actionName === 'trigger') { + return this.triggerAction(context, next); + } + + await next(); + + if (!triggerWorkflows) { + return; + } + + if (!['create', 'update'].includes(actionName)) { + return; + } + + return this.trigger(context); + }; + + private async trigger(context: Context) { + const { triggerWorkflows = '', values } = context.action.params; + const dataSourceHeader = context.get('x-data-source') || 'main'; + + const { currentUser, currentRole } = context.state; + const { model: UserModel } = this.workflow.db.getCollection('users'); + const userInfo = { + user: UserModel.build(currentUser).desensitize(), + roleName: currentRole, + }; + + const triggers = triggerWorkflows.split(',').map((trigger) => trigger.split('!')); + const workflowRepo = this.workflow.db.getRepository('workflows'); + const workflows = ( + await workflowRepo.find({ + filter: { + key: triggers.map((trigger) => trigger[0]), + current: true, + type: 'general-action', + enabled: true, + }, + }) + ).filter((workflow) => Boolean(workflow.config.collection)); + const syncGroup = []; + const asyncGroup = []; + for (const workflow of workflows) { + const { collection, appends = [] } = workflow.config; + const [dataSourceName, collectionName] = parseCollectionName(collection); + const trigger = triggers.find((trigger) => trigger[0] == workflow.key); + const event = [workflow]; + if (context.action.resourceName !== 'workflows') { + if (!context.body) { + continue; + } + if (dataSourceName !== dataSourceHeader) { + continue; + } + const { body: data } = context; + for (const row of Array.isArray(data) ? data : [data]) { + let payload = row; + if (trigger[1]) { + const paths = trigger[1].split('.'); + for (const field of paths) { + if (payload.get(field)) { + payload = payload.get(field); + } else { + const association: any = modelAssociationByKey(payload, field); + payload = await payload[association.accessors.get](); + } + } + } + const model = payload.constructor; + if (payload instanceof Model) { + if (collectionName !== model.collection.name) { + continue; + } + if (appends.length) { + payload = await model.collection.repository.findOne({ + filterByTk: payload.get(model.primaryKeyAttribute), + appends, + }); + } + } + // this.workflow.trigger(workflow, { data: toJSON(payload), ...userInfo }); + event.push({ data: toJSON(payload), ...userInfo }); + } + } else { + const { model, repository } = (context.app).dataSourceManager.dataSources + .get(dataSourceName) + .collectionManager.getCollection(collectionName); + let data = trigger[1] ? get(values, trigger[1]) : values; + const pk = get(data, model.primaryKeyAttribute); + if (appends.length && pk != null) { + data = await repository.findOne({ + filterByTk: pk, + appends, + }); + } + // this.workflow.trigger(workflow, { + // data, + // ...userInfo, + // }); + event.push({ data, ...userInfo }); + } + (workflow.sync ? syncGroup : asyncGroup).push(event); + } + + for (const event of syncGroup) { + await this.workflow.trigger(event[0], event[1], { httpContext: context }); + } + + for (const event of asyncGroup) { + this.workflow.trigger(event[0], event[1]); + } } + on() {} off() {} }