tachybase_todo/packages/plugins/workflow/src/calculators/index.ts
ChengLei Shao 24ea83f0ff
Feat/create nocobase app (#273)
* create-nocobase-app template from [develop]

* change create-nocobase-app package.json config

* feat: load configuration from directory

* feat: configuration repository toObject

* feat: create application from configuration dir

* feat: application factory with plugins options

* export type

* feat: read application config &  application with plugins options

* feat: release command

* fix: database release

* chore: workflow package.json

* feat: nocobase cli package

* feat: console command

* chore: load application in command

* fix: load packages from process.cwd

* feat: cli load env file

* feat: create-nocobase-app

* fix: gitignore create-nocobase-app lib

* fix: sqlite path

* feat: create plugin

* chore: plugin files template

* chore: move cli into application

* chore: create-nocobase-app

* fix: create plugin

* chore: app-client && app-server

* chore: package.json

* feat: create-nocobase-app download template from npm

* chore: create-nocobase-app template

* fix: config of plugin-users

* fix: yarn.lock

* fix: database build error

* fix: yarn.lock

* fix: resourcer config

* chore: cross-env

* chore: app-client dependents

* fix: env

* chore: v0.6.0-alpha.1

* chore: verdaccio

* chore(versions): 😊 publish v0.6.0

* chore(versions): 😊 publish v0.6.1-alpha.0

* chore(versions): 😊 publish v0.6.2-alpha.0

* chore(versions): 😊 publish v0.6.2-alpha.1

* chore: 0.6.2-alpha.2

* feat: workspaces

* chore(versions): 😊 publish v0.6.2-alpha.3

* chore(versions): 😊 publish v0.6.2-alpha.4

* chore: create-nocobase-app

* chore: create-nocobase-app lib

* fix: update tsconfig.jest.json

* chore: .env

* chore(versions): 😊 publish v0.6.2-alpha.5

* chore(versions): 😊 publish v0.6.2-alpha.6

* feat: improve code

* chore(versions): 😊 publish v0.6.2-alpha.7

* fix: cleanup

* chore(versions): 😊 publish v0.6.2-alpha.8

* chore: tsconfig for app server package

* fix: move files

* fix: move files

Co-authored-by: chenos <chenlinxh@gmail.com>
2022-04-17 10:00:42 +08:00

213 lines
4.7 KiB
TypeScript

import { get as getWithPath } from 'lodash';
import { Registry } from "@nocobase/utils";
import ExecutionModel from '../models/Execution';
import JobModel from '../models/Job';
export const calculators = new Registry<Function>();
export default calculators;
export type OperandType = '$context' | '$input' | '$jobsMapByNodeId' | '$calculation';
export type ObjectGetterOptions = {
path?: string
};
export type JobGetterOptions = ObjectGetterOptions & {
nodeId: number
};
export type CalculationOptions = {
calculator: string,
operands: Operand[]
};
export type ConstantOperand = {
type?: 'constant';
value: any
};
export type ContextOperand = {
type: '$context';
options: ObjectGetterOptions;
};
export type InputOperand = {
type: '$input';
options: ObjectGetterOptions;
};
export type JobOperand = {
type: '$jobsMapByNodeId';
options: JobGetterOptions;
};
export type Calculation = {
type: '$calculation';
options: CalculationOptions
};
// TODO(type): union type here is wrong
export type Operand = ContextOperand | InputOperand | JobOperand | ConstantOperand | Calculation;
// @deprecated
// HACK: if no path provided, return self
// @see https://github.com/lodash/lodash/pull/1270
// TODO(question): should add default value as lodash?
function get(object, path?: string | Array<string>) {
return path == null || !path.length ? object : getWithPath(object, path);
}
// NOTE:
// this method could only be used in executing nodes.
// because type of 'job' need loaded jobs in runtime execution.
// or the execution should be prepared first.
export function calculate(operand: Operand, lastJob: JobModel, execution: ExecutionModel) {
switch (operand.type) {
// @Deprecated
// from execution context
case '$context':
return get(execution.context, operand.options.path);
// @Deprecated
// from last job (or input job)
case '$input':
return lastJob ?? get(lastJob.result, operand.options.path);
// @Deprecated
// from job in execution
case '$jobsMapByNodeId':
// assume jobs have been fetched from execution before
const job = execution.jobsMapByNodeId[operand.options.nodeId];
return job && get(job, operand.options.path);
case '$calculation':
const fn = calculators.get(operand.options.calculator);
if (!fn) {
throw new Error(`no calculator function registered for "${operand.options.calculator}"`);
}
return fn(...operand.options.operands.map(item => calculate(item, lastJob, execution)));
// constant
default:
return operand.value;
}
}
// built-in functions
function equal(a, b) {
return a === b;
}
function notEqual(a, b) {
return a !== b;
}
function gt(a, b) {
return a > b;
}
function gte(a, b) {
return a >= b;
}
function lt(a, b) {
return a < b;
}
function lte(a, b) {
return a <= b;
}
calculators.register('equal', equal);
calculators.register('notEqual', notEqual);
calculators.register('gt', gt);
calculators.register('gte', gte);
calculators.register('lt', lt);
calculators.register('lte', lte);
calculators.register('===', equal);
calculators.register('!==', notEqual);
calculators.register('>', gt);
calculators.register('>=', gte);
calculators.register('<', lt);
calculators.register('<=', lte);
function add(...args) {
return args.reduce((sum, a) => sum + a, 0);
}
function minus(a, b) {
return a - b;
}
function multipe(...args) {
return args.reduce((result, a) => result * a, 1);
}
function divide(a, b) {
return a / b;
}
function mod(a, b) {
return a % b;
}
calculators.register('add', add);
calculators.register('minus', minus);
calculators.register('multipe', multipe);
calculators.register('divide', divide);
calculators.register('mod', mod);
calculators.register('+', add);
calculators.register('-', minus);
calculators.register('*', multipe);
calculators.register('/', divide);
calculators.register('%', mod);
function includes(a, b) {
return a.includes(b);
}
function notIncludes(a, b) {
return !a.includes(b);
}
function startsWith(a: string, b: string) {
return a.startsWith(b);
}
function notStartsWith(a: string, b: string) {
return !a.startsWith(b);
}
function endsWith(a: string, b: string) {
return a.endsWith(b);
}
function notEndsWith(a: string, b: string) {
return !a.endsWith(b);
}
calculators.register('includes', includes);
calculators.register('notIncludes', notIncludes);
calculators.register('startsWith', startsWith);
calculators.register('notStartsWith', notStartsWith);
calculators.register('endsWith', endsWith);
calculators.register('notEndsWith', notEndsWith);
function before(a: string, b: string) {
return a < b;
}
calculators.register('now', () => new Date());
// TODO: add more common calculators