feat: plugin-workfow, api regular (#1103)
Reviewed-on: daoyoucloud/tachybase#1103 Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
b2970245d9
commit
5251aec157
@ -152,7 +152,7 @@ class Spreadsheet {
|
||||
this.options = { showBottomBar: true, ...options };
|
||||
this.sheetIndex = 1;
|
||||
this.datas = [];
|
||||
// debugger;
|
||||
|
||||
if (typeof selectors === 'string') {
|
||||
this.targetEl = document.querySelector(selectors) as HTMLElement;
|
||||
} else {
|
||||
|
@ -0,0 +1,168 @@
|
||||
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');
|
||||
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',
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
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);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { Plugin } from '@tachybase/client';
|
||||
|
||||
import { KitAPIRegularConfiguration } from './configuration/kit';
|
||||
import { KitAPIRegularUsage } from './usage/kit';
|
||||
|
||||
export class PluginAPIRegularClient extends Plugin {
|
||||
async afterAdd() {
|
||||
await this.app.pm.add(KitAPIRegularConfiguration);
|
||||
await this.app.pm.add(KitAPIRegularUsage);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { BlockInitializer, useSchemaInitializerItem } from '@tachybase/client';
|
||||
|
||||
import { tval } from '../../../locale';
|
||||
|
||||
export const APIRegularInitializer = () => {
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
return <BlockInitializer {...itemConfig} schema={schema} item={itemConfig} />;
|
||||
};
|
||||
|
||||
const schema = {
|
||||
type: 'void',
|
||||
title: tval('Regular workflow'),
|
||||
'x-component': 'Action',
|
||||
'x-use-component-props': 'usePropsAPIRegular',
|
||||
'x-align': 'right',
|
||||
'x-acl-action': 'update',
|
||||
'x-decorator': 'ACLActionProvider',
|
||||
'x-acl-action-props': {
|
||||
skipScopeCheck: true,
|
||||
},
|
||||
'x-action': 'customize:APIRegular',
|
||||
'x-toolbar': 'ActionSchemaToolbar',
|
||||
'x-settings': 'actionSettings:APIRegular',
|
||||
'x-action-settings': {
|
||||
bindWorkflow: false,
|
||||
updateMode: 'selected',
|
||||
},
|
||||
'x-component-props': {
|
||||
icon: 'CarryOutOutlined',
|
||||
},
|
||||
};
|
@ -0,0 +1,105 @@
|
||||
import {
|
||||
ActionDesigner,
|
||||
SchemaSettings,
|
||||
SchemaSettingsItemType,
|
||||
useDesignable,
|
||||
useRequest,
|
||||
useSchemaToolbar,
|
||||
} from '@tachybase/client';
|
||||
import { useFieldSchema } from '@tachybase/schema';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { NAMESPACE_TRIGGER_API_REGULAR } from '../../../../common/constants';
|
||||
|
||||
const schemaSettingsItems: SchemaSettingsItemType[] = [
|
||||
{
|
||||
name: 'editButton',
|
||||
Component: ActionDesigner.ButtonEditor,
|
||||
useComponentProps() {
|
||||
const { buttonEditorProps } = useSchemaToolbar();
|
||||
return buttonEditorProps;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Date',
|
||||
type: 'select',
|
||||
useComponentProps() {
|
||||
const { dn } = useDesignable();
|
||||
const { t } = useTranslation();
|
||||
const fieldSchema = useFieldSchema();
|
||||
return {
|
||||
title: t('Data will be updated'),
|
||||
options: [
|
||||
{ label: t('Selected'), value: 'selected' },
|
||||
{ label: t('All'), value: 'all' },
|
||||
],
|
||||
value: fieldSchema['x-action-settings']?.['updateMode'],
|
||||
onChange: (value) => {
|
||||
fieldSchema['x-action-settings']['updateMode'] = value;
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
'x-uid': fieldSchema['x-uid'],
|
||||
'x-action-settings': fieldSchema['x-action-settings'],
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Bind workflow',
|
||||
type: 'select',
|
||||
useComponentProps() {
|
||||
const { dn } = useDesignable();
|
||||
const { t } = useTranslation();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { data } = useRequest<any>({
|
||||
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,
|
||||
Component: ActionDesigner.RemoveButton as any,
|
||||
useComponentProps() {
|
||||
const { removeButtonProps } = useSchemaToolbar();
|
||||
return removeButtonProps;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const APIRegularActionSettings = new SchemaSettings({
|
||||
name: 'actionSettings:APIRegular',
|
||||
items: schemaSettingsItems,
|
||||
});
|
@ -0,0 +1,36 @@
|
||||
import { Plugin, useCollection } from '@tachybase/client';
|
||||
|
||||
import { tval } from '../../../locale';
|
||||
import { APIRegularInitializer } from './APIRegular.schema';
|
||||
import { APIRegularActionSettings } from './BulkUpdateAction.Settings';
|
||||
import { usePropsAPIRegular } from './utils';
|
||||
|
||||
export class KitAPIRegularUsage extends Plugin {
|
||||
async load() {
|
||||
this.app.addComponents({
|
||||
APIRegularInitializer,
|
||||
});
|
||||
|
||||
this.app.addScopes({
|
||||
usePropsAPIRegular,
|
||||
});
|
||||
|
||||
this.app.schemaSettingsManager.add(APIRegularActionSettings);
|
||||
|
||||
['table', 'details'].forEach((block) => {
|
||||
this.app.schemaInitializerManager.addItem(`${block}:configureActions`, 'customize.APIRegular', {
|
||||
title: tval('Regular workflow'),
|
||||
Component: 'APIRegularInitializer',
|
||||
name: 'apiRegular',
|
||||
useVisible() {
|
||||
const collection = useCollection();
|
||||
return (
|
||||
(collection.template !== 'view' || collection?.writableView) &&
|
||||
collection.template !== 'file' &&
|
||||
collection.template !== 'sql'
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
import { useContext } from 'react';
|
||||
import {
|
||||
isVariable,
|
||||
transformVariableValue,
|
||||
useBlockRequestContext,
|
||||
useCollection_deprecated,
|
||||
useCompile,
|
||||
useLocalVariables,
|
||||
useRecord,
|
||||
useRequest,
|
||||
useTableBlockContext,
|
||||
useVariables,
|
||||
} from '@tachybase/client';
|
||||
import { SchemaExpressionScopeContext, useField, useFieldSchema } from '@tachybase/schema';
|
||||
|
||||
import { App } from 'antd';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { lang } from '../../../locale';
|
||||
|
||||
export const usePropsAPIRegular = () => {
|
||||
const { field, resource, __parent, service } = useBlockRequestContext();
|
||||
const actionSchema = useFieldSchema();
|
||||
const expressionScope = useContext(SchemaExpressionScopeContext);
|
||||
const tableBlockContext = useTableBlockContext();
|
||||
const { rowKey } = tableBlockContext;
|
||||
|
||||
const navigate = useNavigate();
|
||||
const compile = useCompile();
|
||||
const actionField: any = useField();
|
||||
const { modal } = App.useApp();
|
||||
const variables = useVariables();
|
||||
const record = useRecord();
|
||||
const { name, getField } = useCollection_deprecated();
|
||||
const localVariables = useLocalVariables();
|
||||
const { run } = useRequest(
|
||||
{
|
||||
resource: 'workflows',
|
||||
action: 'trigger',
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
async onClick() {
|
||||
const selectedRecordKeys =
|
||||
tableBlockContext.field?.data?.selectedRowKeys ?? expressionScope?.selectedRecordKeys ?? [];
|
||||
|
||||
const {
|
||||
bindWorkflow = false,
|
||||
assignedValues: originalAssignedValues = {},
|
||||
updateMode,
|
||||
} = actionSchema?.['x-action-settings'] ?? {};
|
||||
|
||||
actionField.data = field.data || {};
|
||||
actionField.data.loading = true;
|
||||
|
||||
if (!bindWorkflow) {
|
||||
return modal.info({
|
||||
title: lang('Not bind workflow!'),
|
||||
});
|
||||
}
|
||||
|
||||
const assignedValues = {};
|
||||
const waitList = Object.keys(originalAssignedValues).map(async (key) => {
|
||||
const value = originalAssignedValues[key];
|
||||
const collectionField = getField(key);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!collectionField) {
|
||||
throw new Error(`usePropsAPIRegular: field "${key}" not found in collection "${name}"`);
|
||||
}
|
||||
}
|
||||
|
||||
if (isVariable(value)) {
|
||||
const result = await variables?.parseVariable(value, localVariables);
|
||||
if (result) {
|
||||
assignedValues[key] = transformVariableValue(result, { targetCollectionField: collectionField });
|
||||
}
|
||||
} else if (value != null && value !== '') {
|
||||
assignedValues[key] = value;
|
||||
}
|
||||
});
|
||||
await Promise.all(waitList);
|
||||
|
||||
modal.confirm({
|
||||
title: lang('Bulk update', { ns: 'client' }),
|
||||
content:
|
||||
updateMode === 'selected'
|
||||
? lang('Update selected data?', { ns: 'client' })
|
||||
: lang('Update all data?', { ns: 'client' }),
|
||||
async onOk() {
|
||||
run({
|
||||
filterByTk: bindWorkflow,
|
||||
});
|
||||
actionField.data.loading = false;
|
||||
},
|
||||
async onCancel() {
|
||||
actionField.data.loading = false;
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function updateData() {
|
||||
// const updateData: { filter?: any; values: any; forceUpdate: boolean } = {
|
||||
// values,
|
||||
// filter,
|
||||
// forceUpdate: false,
|
||||
// };
|
||||
// if (updateMode === 'selected') {
|
||||
// if (!selectedRecordKeys?.length) {
|
||||
// message.error(t('Please select the records to be updated'));
|
||||
// return;
|
||||
// }
|
||||
// updateData.filter = { $and: [{ [rowKey || 'id']: { $in: selectedRecordKeys } }] };
|
||||
// }
|
||||
// if (!updateData.filter) {
|
||||
// updateData.forceUpdate = true;
|
||||
// }
|
||||
}
|
@ -28,7 +28,6 @@ export class JSONParseInstruction extends Instruction {
|
||||
title: tval('Query expression'),
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
required: true,
|
||||
},
|
||||
model: {
|
||||
type: 'array',
|
||||
|
@ -5,6 +5,7 @@ import { Registry } from '@tachybase/utils/client';
|
||||
import { ExecutionPage } from './ExecutionPage';
|
||||
import { PluginActionTrigger } from './features/action-trigger';
|
||||
import { PluginAggregate } from './features/aggregate';
|
||||
import { PluginAPIRegularClient } from './features/api-regular';
|
||||
import { PluginDelay } from './features/delay';
|
||||
import { PluginDaynamicCalculation } from './features/dynamic-calculation';
|
||||
import PluginWorkflowJSParseClient from './features/js-parse';
|
||||
@ -79,6 +80,7 @@ export default class PluginWorkflowClient extends Plugin {
|
||||
await this.pm.add(PluginActionTrigger);
|
||||
await this.pm.add(PluginWorkflowJsonParseClient);
|
||||
await this.pm.add(PluginWorkflowJSParseClient);
|
||||
await this.pm.add(PluginAPIRegularClient);
|
||||
}
|
||||
|
||||
async load() {
|
||||
|
@ -1,2 +1,5 @@
|
||||
export const NAMESPACE_INSTRUCTION_JSON_PARSE = 'json-parse';
|
||||
export const NAMESPACE_INSTRUCTION_JS_PARSE = 'js-parse';
|
||||
|
||||
// api-fetch
|
||||
export const NAMESPACE_TRIGGER_API_REGULAR = 'api-regular';
|
||||
|
@ -10,6 +10,7 @@ import initActions from './actions';
|
||||
import { EXECUTION_STATUS } from './constants';
|
||||
import { PluginActionTrigger } from './features/action-trigger/Plugin';
|
||||
import { PluginAggregate } from './features/aggregate/Plugin';
|
||||
import PluginWorkflowAPIRegularServer from './features/api-regular/plugin';
|
||||
import { PluginDelay } from './features/delay/Plugin';
|
||||
import { PluginDynamicCalculation } from './features/dynamic-calculation/Plugin';
|
||||
import PluginWorkflowJSParseServer from './features/js-parse/plugin';
|
||||
@ -66,6 +67,7 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
pluginActionTrigger: PluginActionTrigger;
|
||||
pluginJSONParse: PluginWorkflowJSONParseServer;
|
||||
pluginJSParse: PluginWorkflowJSParseServer;
|
||||
pluginAPIRegular: PluginWorkflowAPIRegularServer;
|
||||
|
||||
constructor(app: Application, options?: PluginOptions) {
|
||||
super(app, options);
|
||||
@ -80,6 +82,7 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
this.pluginActionTrigger = new PluginActionTrigger(app, options);
|
||||
this.pluginJSONParse = new PluginWorkflowJSONParseServer(app, options);
|
||||
this.pluginJSParse = new PluginWorkflowJSParseServer(app, options);
|
||||
this.pluginAPIRegular = new PluginWorkflowAPIRegularServer(app, options);
|
||||
}
|
||||
|
||||
getLogger(workflowId: ID): Logger {
|
||||
@ -305,6 +308,7 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
await this.pluginRequest.load();
|
||||
await this.pluginJSONParse.load();
|
||||
await this.pluginJSParse.load();
|
||||
await this.pluginAPIRegular.load();
|
||||
}
|
||||
|
||||
toggle(workflow: WorkflowModel, enable?: boolean) {
|
||||
|
@ -0,0 +1,156 @@
|
||||
import { Context as ActionContext, Next } from '@tachybase/actions';
|
||||
import { parseCollectionName } from '@tachybase/data-source-manager';
|
||||
import { Model, modelAssociationByKey } from '@tachybase/database';
|
||||
import Application, { DefaultContext } from '@tachybase/server';
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { BelongsTo, HasOne } from 'sequelize';
|
||||
|
||||
import WorkflowPlugin, { toJSON, WorkflowModel } from '../..';
|
||||
import { NAMESPACE_TRIGGER_API_REGULAR } from '../../../common/constants';
|
||||
import Trigger from '../../triggers';
|
||||
|
||||
interface Context extends ActionContext, DefaultContext {}
|
||||
|
||||
export class APIRegularTrigger extends Trigger {
|
||||
constructor(workflow: WorkflowPlugin) {
|
||||
super(workflow);
|
||||
workflow.app.use(this.middleware, { after: 'dataSource' });
|
||||
}
|
||||
|
||||
async middleware(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);
|
||||
}
|
||||
|
||||
async triggerAction(context: Context, next: Next) {
|
||||
const { triggerWorkflows } = context.action.params;
|
||||
|
||||
if (!triggerWorkflows) {
|
||||
return context.throw(400);
|
||||
}
|
||||
|
||||
context.status = 202;
|
||||
await next();
|
||||
|
||||
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: NAMESPACE_TRIGGER_API_REGULAR,
|
||||
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 = <HasOne | BelongsTo>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 } = (<Application>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(workflow: WorkflowModel): void {}
|
||||
off(workflow: WorkflowModel): void {}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export { default } from './plugin';
|
@ -0,0 +1,14 @@
|
||||
import { Plugin } from '@tachybase/server';
|
||||
|
||||
import WorkflowPlugin from '../..';
|
||||
import { NAMESPACE_TRIGGER_API_REGULAR } from '../../../common/constants';
|
||||
import { APIRegularTrigger } from './APIRegular.trigger';
|
||||
|
||||
export class PluginWorkflowAPIRegularServer extends Plugin {
|
||||
async load() {
|
||||
const pluginWorkflow: any = this.app.pm.get(WorkflowPlugin);
|
||||
pluginWorkflow.registerTrigger(NAMESPACE_TRIGGER_API_REGULAR, new APIRegularTrigger(pluginWorkflow));
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginWorkflowAPIRegularServer;
|
@ -10,7 +10,10 @@ export class JSONParseInstruction extends Instruction {
|
||||
const data = processor.getParsedValue(source, node.id);
|
||||
const query = this.engine;
|
||||
try {
|
||||
let result = query ? await query(expression, data) : data;
|
||||
let result = data;
|
||||
if (expression) {
|
||||
result = query ? await query(expression, data) : data;
|
||||
}
|
||||
|
||||
if (typeof result === 'object' && result && model?.length) {
|
||||
if (Array.isArray(result)) {
|
||||
@ -39,7 +42,7 @@ function mapModel(data, model) {
|
||||
}
|
||||
|
||||
const result = model.reduce((acc, { path, alias }) => {
|
||||
const key = alias ?? path.replace(/\./g, '_');
|
||||
const key = alias || path.replace(/\./g, '_');
|
||||
const value = _.get(data, path);
|
||||
acc[key] = value;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
|
||||
import { Processor, Instruction, JOB_STATUS, FlowNodeModel } from '../..';
|
||||
import { FlowNodeModel, Instruction, JOB_STATUS, Processor } from '../..';
|
||||
|
||||
export interface Header {
|
||||
name: string;
|
||||
@ -42,13 +42,13 @@ async function request(config) {
|
||||
export default class extends Instruction {
|
||||
async run(node: FlowNodeModel, prevJob, processor: Processor) {
|
||||
const config = processor.getParsedValue(node.config, node.id) as RequestConfig;
|
||||
|
||||
const { workflow } = processor.execution;
|
||||
const sync = this.workflow.isWorkflowSync(workflow);
|
||||
|
||||
if (sync) {
|
||||
try {
|
||||
const response = await request(config);
|
||||
|
||||
return {
|
||||
status: JOB_STATUS.RESOLVED,
|
||||
result: response.data,
|
||||
|
Loading…
Reference in New Issue
Block a user