refactor(plugin-workflow): adjust some api and packages (#3281)

* fix(plugin-workflow): fix locale and some api

* fix(plugin-workflow-test): fix test package

* fix(plugin-workflow): fix package and types

* test(plugin-workflow): test package
This commit is contained in:
Junyi 2023-12-29 09:29:37 +08:00 committed by GitHub
parent c9d5292d03
commit 7f0d699590
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 57 additions and 63 deletions

6
.vscode/launch.json vendored
View File

@ -14,14 +14,14 @@
{
"type": "node",
"request": "launch",
"name": "Debug Jest Tests",
"name": "Debug Tests",
"runtimeExecutable": "yarn",
"runtimeArgs": ["run", "--inspect-brk", "test", "--runInBand", "${file}"],
"runtimeArgs": ["run", "--inspect-brk", "test", "${file}"],
"skipFiles": ["<node_internals>/**"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"windows": {
"runtimeArgs": ["run", "--inspect-brk", "test", "--runInBand", "/${fileBasename}"]
"runtimeArgs": ["run", "--inspect-brk", "test", "/${fileBasename}"]
}
},
{

View File

@ -8,6 +8,7 @@
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"devDependencies": {
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"antd": "5.x",
"react": "18.x",
"react-i18next": "^11.15.1"
@ -16,7 +17,6 @@
"@nocobase/client": "0.x",
"@nocobase/database": "0.x",
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x"
},

View File

@ -8,6 +8,7 @@
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"devDependencies": {
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"antd": "5.x",
"react": "18.x",
"react-i18next": "^11.15.1"
@ -16,7 +17,6 @@
"@nocobase/client": "0.x",
"@nocobase/database": "0.x",
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x"
},

View File

@ -12,6 +12,7 @@
"@formily/antd-v5": "1.x",
"@formily/core": "2.x",
"@formily/react": "2.x",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"antd": "5.x",
"lodash": "4.17.21",
"react": "18.x",
@ -24,7 +25,6 @@
"@nocobase/evaluators": "0.x",
"@nocobase/plugin-collection-manager": "0.x",
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x",
"@nocobase/utils": "0.x"

View File

@ -8,6 +8,7 @@
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"devDependencies": {
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"antd": "5.x",
"react": "18.x",
"react-i18next": "^11.15.1"
@ -16,7 +17,6 @@
"@nocobase/client": "0.x",
"@nocobase/database": "0.x",
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x"
},

View File

@ -9,6 +9,7 @@
"main": "./dist/server/index.js",
"devDependencies": {
"@ant-design/icons": "5.x",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"react": "18.x",
"react-i18next": "^11.15.1"
},
@ -16,7 +17,6 @@
"@nocobase/client": "0.x",
"@nocobase/database": "0.x",
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x"
},

View File

@ -4,6 +4,5 @@
"Loop index": "当前索引",
"Loop length": "循环长度",
"By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.": "使用循环节点可以对多条数据进行同样的操作,多条数据的来源可以是查询节点的多条结果,或者一条数据的多条关系数据。也可以用于一定次数的循环,或者对字符串中每一个字符的循环处理。循环次数过高可能引起性能问题,请谨慎使用。",
"Scope variables": "局域变量",
"A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.": "单一数字值将被视为循环次数,单一字符串值将被视为字符数组,其他非数组值将被转换为数组。达到循环次数,或者将数组循环完成后,循环节点结束。你也可以在循环中添加条件节点,以终止循环。"
}

View File

@ -12,6 +12,7 @@
"@formily/antd-v5": "1.x",
"@formily/core": "2.x",
"@formily/react": "2.x",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"antd": "5.x",
"lodash": "4.17.21",
"react": "18.x",
@ -24,7 +25,6 @@
"@nocobase/plugin-collection-manager": "0.x",
"@nocobase/plugin-users": "0.x",
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x",
"@nocobase/utils": "0.x"

View File

@ -1,5 +1,4 @@
import Database from '@nocobase/database';
import UserPlugin from '@nocobase/plugin-users';
import { MockServer } from '@nocobase/test';
import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
@ -38,7 +37,6 @@ describe('workflow > instructions > manual', () => {
{ id: 2, nickname: 'b' },
]);
const userPlugin = app.getPlugin('users') as UserPlugin;
userAgents = users.map((user) => app.agent().login(user));
workflow = await WorkflowModel.create({

View File

@ -12,7 +12,7 @@ export async function submit(context: Context, next) {
return context.throw(401);
}
const plugin: WorkflowPlugin = context.app.pm.get('workflow') as WorkflowPlugin;
const plugin: WorkflowPlugin = context.app.getPlugin(WorkflowPlugin);
const instruction = plugin.instructions.get('manual') as ManualInstruction;
const userJob = await repository.findOne({

View File

@ -9,6 +9,7 @@
"main": "./dist/server/index.js",
"devDependencies": {
"@ant-design/icons": "5.x",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"antd": "5.x",
"react": "18.x",
"react-i18next": "^11.15.1"
@ -17,7 +18,6 @@
"@nocobase/client": "0.x",
"@nocobase/database": "0.x",
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x"
},

View File

@ -8,6 +8,7 @@
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"devDependencies": {
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"antd": "5.x",
"react": "18.x",
"react-i18next": "^11.15.1"
@ -16,7 +17,6 @@
"@nocobase/client": "0.x",
"@nocobase/database": "0.x",
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x"
},

View File

@ -8,6 +8,7 @@
"license": "AGPL-3.0",
"main": "./dist/server/index.js",
"devDependencies": {
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"antd": "5.x",
"react": "18.x",
"react-i18next": "^11.15.1"
@ -16,7 +17,6 @@
"@nocobase/client": "0.x",
"@nocobase/database": "0.x",
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x"
},

View File

@ -55,24 +55,24 @@ export async function getApp({
...options,
autoStart,
cleanDb,
plugins: ['workflow', 'workflow-test', ...plugins],
plugins: [
[
'workflow',
{
instructions,
functions,
},
],
WorkflowTestPlugin,
...plugins,
],
});
}
export default class extends Plugin {
export default class WorkflowTestPlugin extends Plugin {
async load() {
await this.db.import({
directory: path.resolve(__dirname, 'collections'),
});
const workflow = this.app.getPlugin<any>('workflow');
for (const [key, instruction] of Object.entries(instructions)) {
workflow.instructions.register(key, instruction);
}
for (const [key, func] of Object.entries(functions)) {
workflow.functions.register(key, func);
}
}
}

View File

@ -13,6 +13,7 @@ export default {
error: {
run(node, input, processor) {
throw new Error('definite error');
return null;
},
},
@ -45,6 +46,7 @@ export default {
},
resume(node, input, processor) {
throw new Error('input failed');
return null;
},
},

View File

@ -13,6 +13,7 @@
"@formily/core": "2.x",
"@formily/react": "2.x",
"@types/ejs": "^3.1.1",
"@nocobase/plugin-workflow-test": ">= 0.18.0-alpha.2",
"antd": "5.x",
"axios": "^0.26.1",
"classnames": "^2.3.1",
@ -24,8 +25,7 @@
"react-i18next": "^11.15.1",
"react-js-cron": "^3.1.0",
"react-router-dom": "^6.11.2",
"sequelize": "^6.26.0",
"winston": "^3.8.2"
"sequelize": "^6.26.0"
},
"peerDependencies": {
"@nocobase/actions": "0.x",
@ -36,11 +36,10 @@
"@nocobase/plugin-collection-manager": "0.x",
"@nocobase/plugin-error-handler": "0.x",
"@nocobase/plugin-users": "0.x",
"@nocobase/plugin-workflow-test": ">=0.17.0-alpha.3",
"@nocobase/resourcer": "0.x",
"@nocobase/server": "0.x",
"@nocobase/test": "0.x",
"@nocobase/utils": "0.x"
"@nocobase/utils": "0.x",
"@nocobase/test": "0.x"
},
"gitHead": "ce588eefb0bfc50f7d5bbee575e0b5e843bf6644"
}

View File

@ -162,7 +162,7 @@ function Calculation({ calculator, operands = [], onChange }) {
aria-label="select-operator-calc"
value={calculator}
onChange={(v) => onChange({ operands, calculator: v })}
placeholder={lang('Calculator')}
placeholder={lang('Operator')}
popupMatchSelectWidth={false}
>
{calculatorGroups

View File

@ -88,13 +88,15 @@ export function useNodeContext() {
return useContext(NodeContext);
}
export function useAvailableUpstreams(node) {
export function useAvailableUpstreams(node, filter?) {
const stack: any[] = [];
if (!node) {
return [];
}
for (let current = node.upstream; current; current = current.upstream) {
stack.push(current);
if (typeof filter !== 'function' || filter(current)) {
stack.push(current);
}
}
return stack;

View File

@ -58,7 +58,7 @@
"Null": "Null",
"Boolean": "Boolean",
"String": "String",
"Calculator": "Calculator",
"Operator": "Operator",
"Arithmetic calculation": "Arithmetic calculation",
"String operation": "String operation",
"Executed at": "Executed at",

View File

@ -58,7 +58,7 @@
"Null": "Null",
"Boolean": "Booleano",
"String": "Cadena",
"Calculator": "Calculadora",
"Operator": "Calculadora",
"Arithmetic calculation": "Cálculo aritmético",
"String operation": "Operación de Cadena",
"Executed at": "Ejecutado en",

View File

@ -58,7 +58,7 @@
"Null": "Null",
"Boolean": "Booléen",
"String": "Chaîne de caractères",
"Calculator": "Calculatrice",
"Operator": "Calculatrice",
"Arithmetic calculation": "Calcul arithmétique",
"String operation": "Opération sur les chaînes de caractères",
"Executed at": "Exécuté à",

View File

@ -58,7 +58,7 @@
"Null": "Nulo",
"Boolean": "Booleano",
"String": "String",
"Calculator": "Calculadora",
"Operator": "Calculadora",
"Arithmetic calculation": "Cálculo aritmético",
"String operation": "Operação de string",
"Executed at": "Executado em",

View File

@ -83,7 +83,9 @@
"End": "结束",
"Node result": "节点数据",
"Variable key of node": "节点变量标识",
"Calculator": "运算",
"Scope variables": "局域变量",
"Operator": "运算符",
"Calculate an expression based on a calculation engine and obtain a value as the result. Variables in the upstream nodes can be used in the expression. The expression can be static or dynamic one from an expression collections.":
"基于计算引擎对一个表达式进行计算,并获得一个值作为结果。表达式中可以使用上游节点里的变量。表达式可以是静态的,也可以是表达式表中的动态表达式。",
"String operation": "字符串",

View File

@ -1,13 +1,12 @@
import path from 'path';
import LRUCache from 'lru-cache';
import winston from 'winston';
import { Op } from '@nocobase/database';
import { Plugin } from '@nocobase/server';
import { Registry } from '@nocobase/utils';
import { createLogger, getLoggerFilePath, getLoggerLevel, Logger, LoggerOptions } from '@nocobase/logger';
import { Logger, LoggerOptions } from '@nocobase/logger';
import Processor from './Processor';
import initActions from './actions';
import { EXECUTION_STATUS } from './constants';
@ -15,7 +14,7 @@ import initFunctions, { CustomFunction } from './functions';
import Trigger from './triggers';
import CollectionTrigger from './triggers/CollectionTrigger';
import ScheduleTrigger from './triggers/ScheduleTrigger';
import Instruction from './instructions';
import { Instruction, InstructionInterface } from './instructions';
import CalculationInstruction from './instructions/CalculationInstruction';
import ConditionInstruction from './instructions/ConditionInstruction';
import CreateInstruction from './instructions/CreateInstruction';
@ -32,7 +31,7 @@ type Pending = [ExecutionModel, JobModel?];
type CachedEvent = [WorkflowModel, any, { context?: any }];
export default class WorkflowPlugin extends Plugin {
instructions: Registry<Instruction> = new Registry();
instructions: Registry<InstructionInterface> = new Registry();
triggers: Registry<Trigger> = new Registry();
functions: Registry<CustomFunction> = new Registry();
@ -118,7 +117,7 @@ export default class WorkflowPlugin extends Plugin {
}
}
registerInstruction<T extends Instruction>(type: string, instruction: T | { new (p: Plugin): T }) {
registerInstruction(type: string, instruction: InstructionInterface | { new (p: Plugin): InstructionInterface }) {
if (typeof instruction === 'function') {
this.instructions.register(type, new instruction(this));
} else if (instruction) {

View File

@ -6,22 +6,14 @@ import { JOB_STATUS } from '../constants';
import type { FlowNodeModel } from '../types';
export interface CalculationConfig {
dynamic?: boolean | string;
engine?: string;
expression?: string;
}
export class CalculationInstruction extends Instruction {
async run(node: FlowNodeModel, prevJob, processor: Processor) {
const { dynamic = false } = <CalculationConfig>node.config || {};
let { engine = 'math.js', expression = '' } = node.config;
let scope = processor.getScope(node.id);
if (dynamic) {
const parsed = parse(dynamic)(scope) ?? {};
engine = parsed.engine;
expression = parsed.expression;
scope = parse(node.config.scope ?? '')(scope) ?? {};
}
const { engine = 'math.js', expression = '' } = node.config;
const scope = processor.getScope(node.id);
const evaluator = <Evaluator | undefined>evaluators.get(engine);

View File

@ -15,18 +15,19 @@ export type InstructionResult = IJob | Promise<IJob> | null;
export type Runner = (node: FlowNodeModel, input: any, processor: Processor) => InstructionResult;
export type InstructionInterface = {
run: Runner;
resume?: Runner;
getScope?: (node: FlowNodeModel, data: any, processor: Processor) => any;
duplicateConfig?: (node: FlowNodeModel, options: Transactionable) => object | Promise<object>;
};
// what should a instruction do?
// - base on input and context, do any calculations or system call (io), and produce a result or pending.
export abstract class Instruction {
export abstract class Instruction implements InstructionInterface {
constructor(public workflow: Plugin) {}
abstract run(node: FlowNodeModel, input: any, processor: Processor): InstructionResult;
resume?(node: FlowNodeModel, input: any, processor: Processor): InstructionResult;
getScope?(node: FlowNodeModel, data: any, processor: Processor): any;
duplicateConfig?(node: FlowNodeModel, options: Transactionable): object | Promise<object>;
}
export default Instruction;