feat: plugin-workflow-json-parse (#1091)
Reviewed-on: daoyoucloud/tachybase#1091 Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
1ff8bc8157
commit
a947d639fc
@ -12,6 +12,9 @@
|
|||||||
"./client": "./dist/client/index.js"
|
"./client": "./dist/client/index.js"
|
||||||
},
|
},
|
||||||
"main": "./dist/server/index.js",
|
"main": "./dist/server/index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"jsonata": "^2.0.5"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ant-design/icons": "~5.3.6",
|
"@ant-design/icons": "~5.3.6",
|
||||||
"@tachybase/components": "workspace:*",
|
"@tachybase/components": "workspace:*",
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
import { css } from '@tachybase/client';
|
||||||
|
import { ArrayTable } from '@tachybase/components';
|
||||||
|
import { Instruction, WorkflowVariableInput } from '@tachybase/plugin-workflow/client';
|
||||||
|
|
||||||
|
import { NAMESPACE_INSTRUCTION_JSON_PARSE } from '../../../common/constants';
|
||||||
|
import { tval } from '../../locale';
|
||||||
|
|
||||||
|
export class JSONParseInstruction extends Instruction {
|
||||||
|
title = tval('JSON Parse');
|
||||||
|
type = NAMESPACE_INSTRUCTION_JSON_PARSE;
|
||||||
|
// XXX: 这里应该定义在 workflow 里的一个统一的地方. workflow 本身没处理好这块, 先这样直接写了.
|
||||||
|
group = 'extended';
|
||||||
|
description = tval('Get specific data from JSON result of any node BY jsonata; https://jsonata.org/');
|
||||||
|
fieldset = {
|
||||||
|
source: {
|
||||||
|
type: 'string',
|
||||||
|
title: tval('Data source'),
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'WorkflowVariableInput',
|
||||||
|
'x-component-props': {
|
||||||
|
changeOnSelect: true,
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'string',
|
||||||
|
title: tval('Query expression'),
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
type: 'array',
|
||||||
|
title: tval('Properties mapping'),
|
||||||
|
description: tval(
|
||||||
|
'If the type of query result is object or array of object, could map the properties which to be accessed in subsequent nodes.',
|
||||||
|
),
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
path: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
title: tval('Property path'),
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
path: {
|
||||||
|
type: 'string',
|
||||||
|
name: 'path',
|
||||||
|
required: true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
alias: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
title: tval('Alias'),
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
alias: {
|
||||||
|
type: 'string',
|
||||||
|
name: 'alias',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
title: tval('Label'),
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
label: {
|
||||||
|
type: 'string',
|
||||||
|
name: 'label',
|
||||||
|
required: true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
operations: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
dataIndex: 'operations',
|
||||||
|
fixed: 'right',
|
||||||
|
className: css`
|
||||||
|
> *:not(:last-child) {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
remove: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Remove',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
add: {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('Add property'),
|
||||||
|
'x-component': 'ArrayTable.Addition',
|
||||||
|
'x-component-props': {
|
||||||
|
defaultValue: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
components = {
|
||||||
|
ArrayTable,
|
||||||
|
WorkflowVariableInput,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import { Plugin } from '@tachybase/client';
|
||||||
|
|
||||||
|
import PluginWorkflow from '../..';
|
||||||
|
import { NAMESPACE_INSTRUCTION_JSON_PARSE } from '../../../common/constants';
|
||||||
|
import { JSONParseInstruction } from './JSONParse.instruction';
|
||||||
|
|
||||||
|
export class PluginWorkflowJsonParseClient extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const pluginWorkflow = this.app.pm.get(PluginWorkflow);
|
||||||
|
pluginWorkflow.registerInstruction(NAMESPACE_INSTRUCTION_JSON_PARSE, JSONParseInstruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PluginWorkflowJsonParseClient;
|
@ -1,34 +1,34 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Plugin } from '@tachybase/client';
|
import { Plugin } from '@tachybase/client';
|
||||||
import { Registry } from '@tachybase/utils/client';
|
import { Registry } from '@tachybase/utils/client';
|
||||||
|
|
||||||
import { ExecutionPage } from './ExecutionPage';
|
import { ExecutionPage } from './ExecutionPage';
|
||||||
import { WorkflowPage } from './WorkflowPage';
|
import { PluginActionTrigger } from './features/action-trigger';
|
||||||
import { WorkflowPane } from './WorkflowPane';
|
import { PluginAggregate } from './features/aggregate';
|
||||||
import { Trigger } from './triggers';
|
import { PluginDelay } from './features/delay';
|
||||||
import CollectionTrigger from './triggers/collection';
|
import { PluginDaynamicCalculation } from './features/dynamic-calculation';
|
||||||
import ScheduleTrigger from './triggers/schedule';
|
import PluginWorkflowJsonParseClient from './features/json-parse';
|
||||||
|
import { PluginLoop } from './features/loop';
|
||||||
|
import { PluginManual } from './features/manual';
|
||||||
|
import { PluginParallel } from './features/parallel';
|
||||||
|
import { PluginRequest } from './features/request';
|
||||||
|
import { PluginSql } from './features/sql';
|
||||||
|
import { NAMESPACE } from './locale';
|
||||||
import { Instruction } from './nodes';
|
import { Instruction } from './nodes';
|
||||||
import CalculationInstruction from './nodes/calculation';
|
import CalculationInstruction from './nodes/calculation';
|
||||||
import ConditionInstruction from './nodes/condition';
|
import ConditionInstruction from './nodes/condition';
|
||||||
|
import CreateInstruction from './nodes/create';
|
||||||
|
import DestroyInstruction from './nodes/destroy';
|
||||||
import EndInstruction from './nodes/end';
|
import EndInstruction from './nodes/end';
|
||||||
import QueryInstruction from './nodes/query';
|
import QueryInstruction from './nodes/query';
|
||||||
import CreateInstruction from './nodes/create';
|
|
||||||
import UpdateInstruction from './nodes/update';
|
import UpdateInstruction from './nodes/update';
|
||||||
import DestroyInstruction from './nodes/destroy';
|
|
||||||
import { getWorkflowDetailPath, getWorkflowExecutionsPath } from './utils';
|
|
||||||
import { NAMESPACE } from './locale';
|
|
||||||
import { customizeSubmitToWorkflowActionSettings } from './settings/customizeSubmitToWorkflowActionSettings';
|
import { customizeSubmitToWorkflowActionSettings } from './settings/customizeSubmitToWorkflowActionSettings';
|
||||||
import { PluginSql } from './features/sql';
|
import { Trigger } from './triggers';
|
||||||
import { PluginRequest } from './features/request';
|
import CollectionTrigger from './triggers/collection';
|
||||||
import { PluginParallel } from './features/parallel';
|
import ScheduleTrigger from './triggers/schedule';
|
||||||
import { PluginLoop } from './features/loop';
|
import { getWorkflowDetailPath, getWorkflowExecutionsPath } from './utils';
|
||||||
import { PluginDaynamicCalculation } from './features/dynamic-calculation';
|
import { WorkflowPage } from './WorkflowPage';
|
||||||
import { PluginDelay } from './features/delay';
|
import { WorkflowPane } from './WorkflowPane';
|
||||||
import { PluginAggregate } from './features/aggregate';
|
|
||||||
import { PluginActionTrigger } from './features/action-trigger';
|
|
||||||
import { PluginManual } from './features/manual';
|
|
||||||
|
|
||||||
export default class PluginWorkflowClient extends Plugin {
|
export default class PluginWorkflowClient extends Plugin {
|
||||||
triggers = new Registry<Trigger>();
|
triggers = new Registry<Trigger>();
|
||||||
@ -76,6 +76,7 @@ export default class PluginWorkflowClient extends Plugin {
|
|||||||
await this.pm.add(PluginDelay);
|
await this.pm.add(PluginDelay);
|
||||||
await this.pm.add(PluginAggregate);
|
await this.pm.add(PluginAggregate);
|
||||||
await this.pm.add(PluginActionTrigger);
|
await this.pm.add(PluginActionTrigger);
|
||||||
|
await this.pm.add(PluginWorkflowJsonParseClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { i18n } from '@tachybase/client';
|
import { i18n, tval as nTval } from '@tachybase/client';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const NAMESPACE = 'workflow';
|
export const NAMESPACE = 'workflow';
|
||||||
@ -10,3 +10,6 @@ export function lang(key: string, options = {}) {
|
|||||||
export function useWorkflowTranslation() {
|
export function useWorkflowTranslation() {
|
||||||
return useTranslation(NAMESPACE);
|
return useTranslation(NAMESPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const tval = (key: string) => nTval(key, { ns: NAMESPACE });
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export const NAMESPACE_INSTRUCTION_JSON_PARSE = 'json-parse';
|
@ -1,38 +1,37 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import LRUCache from 'lru-cache';
|
|
||||||
|
|
||||||
import { Op, Transactionable } from '@tachybase/database';
|
import { Op, Transactionable } from '@tachybase/database';
|
||||||
|
import { Logger, LoggerOptions } from '@tachybase/logger';
|
||||||
import Application, { Plugin, PluginOptions } from '@tachybase/server';
|
import Application, { Plugin, PluginOptions } from '@tachybase/server';
|
||||||
import { Registry } from '@tachybase/utils';
|
import { Registry } from '@tachybase/utils';
|
||||||
|
|
||||||
import { Logger, LoggerOptions } from '@tachybase/logger';
|
import LRUCache from 'lru-cache';
|
||||||
import Processor from './Processor';
|
|
||||||
import initActions from './actions';
|
import initActions from './actions';
|
||||||
import { EXECUTION_STATUS } from './constants';
|
import { EXECUTION_STATUS } from './constants';
|
||||||
|
import { PluginActionTrigger } from './features/action-trigger/Plugin';
|
||||||
|
import { PluginAggregate } from './features/aggregate/Plugin';
|
||||||
|
import { PluginDelay } from './features/delay/Plugin';
|
||||||
|
import { PluginDynamicCalculation } from './features/dynamic-calculation/Plugin';
|
||||||
|
import PluginWorkflowJSONParseServer from './features/json-parse/plugin';
|
||||||
|
import { PluginLoop } from './features/loop/Plugin';
|
||||||
|
import { PluginManual } from './features/manual/Plugin';
|
||||||
|
import { PluginParallel } from './features/parallel/Plugin';
|
||||||
|
import { PluginRequest } from './features/request/Plugin';
|
||||||
|
import { PluginSql } from './features/sql/Plugin';
|
||||||
import initFunctions, { CustomFunction } from './functions';
|
import initFunctions, { CustomFunction } from './functions';
|
||||||
import Trigger from './triggers';
|
|
||||||
import CollectionTrigger from './triggers/CollectionTrigger';
|
|
||||||
import ScheduleTrigger from './triggers/ScheduleTrigger';
|
|
||||||
import { Instruction, InstructionInterface } from './instructions';
|
import { Instruction, InstructionInterface } from './instructions';
|
||||||
import CalculationInstruction from './instructions/CalculationInstruction';
|
import CalculationInstruction from './instructions/CalculationInstruction';
|
||||||
import ConditionInstruction from './instructions/ConditionInstruction';
|
import ConditionInstruction from './instructions/ConditionInstruction';
|
||||||
import EndInstruction from './instructions/EndInstruction';
|
|
||||||
import CreateInstruction from './instructions/CreateInstruction';
|
import CreateInstruction from './instructions/CreateInstruction';
|
||||||
import DestroyInstruction from './instructions/DestroyInstruction';
|
import DestroyInstruction from './instructions/DestroyInstruction';
|
||||||
|
import EndInstruction from './instructions/EndInstruction';
|
||||||
import QueryInstruction from './instructions/QueryInstruction';
|
import QueryInstruction from './instructions/QueryInstruction';
|
||||||
import UpdateInstruction from './instructions/UpdateInstruction';
|
import UpdateInstruction from './instructions/UpdateInstruction';
|
||||||
|
import Processor from './Processor';
|
||||||
|
import Trigger from './triggers';
|
||||||
|
import CollectionTrigger from './triggers/CollectionTrigger';
|
||||||
|
import ScheduleTrigger from './triggers/ScheduleTrigger';
|
||||||
import type { ExecutionModel, JobModel, WorkflowModel } from './types';
|
import type { ExecutionModel, JobModel, WorkflowModel } from './types';
|
||||||
import { PluginSql } from './features/sql/Plugin';
|
|
||||||
import { PluginRequest } from './features/request/Plugin';
|
|
||||||
import { PluginParallel } from './features/parallel/Plugin';
|
|
||||||
import { PluginManual } from './features/manual/Plugin';
|
|
||||||
import { PluginLoop } from './features/loop/Plugin';
|
|
||||||
import { PluginDynamicCalculation } from './features/dynamic-calculation/Plugin';
|
|
||||||
import { PluginDelay } from './features/delay/Plugin';
|
|
||||||
import { PluginAggregate } from './features/aggregate/Plugin';
|
|
||||||
import { PluginActionTrigger } from './features/action-trigger/Plugin';
|
|
||||||
|
|
||||||
type ID = number | string;
|
type ID = number | string;
|
||||||
|
|
||||||
@ -64,6 +63,7 @@ export default class PluginWorkflowServer extends Plugin {
|
|||||||
pluginDelay: PluginDelay;
|
pluginDelay: PluginDelay;
|
||||||
pluginAggregate: PluginAggregate;
|
pluginAggregate: PluginAggregate;
|
||||||
pluginActionTrigger: PluginActionTrigger;
|
pluginActionTrigger: PluginActionTrigger;
|
||||||
|
pluginJSONParse: PluginWorkflowJSONParseServer;
|
||||||
|
|
||||||
constructor(app: Application, options?: PluginOptions) {
|
constructor(app: Application, options?: PluginOptions) {
|
||||||
super(app, options);
|
super(app, options);
|
||||||
@ -76,6 +76,7 @@ export default class PluginWorkflowServer extends Plugin {
|
|||||||
this.pluginDelay = new PluginDelay(app, options);
|
this.pluginDelay = new PluginDelay(app, options);
|
||||||
this.pluginAggregate = new PluginAggregate(app, options);
|
this.pluginAggregate = new PluginAggregate(app, options);
|
||||||
this.pluginActionTrigger = new PluginActionTrigger(app, options);
|
this.pluginActionTrigger = new PluginActionTrigger(app, options);
|
||||||
|
this.pluginJSONParse = new PluginWorkflowJSONParseServer(app, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLogger(workflowId: ID): Logger {
|
getLogger(workflowId: ID): Logger {
|
||||||
@ -299,6 +300,7 @@ export default class PluginWorkflowServer extends Plugin {
|
|||||||
await this.pluginManual.load();
|
await this.pluginManual.load();
|
||||||
await this.pluginParallel.load();
|
await this.pluginParallel.load();
|
||||||
await this.pluginRequest.load();
|
await this.pluginRequest.load();
|
||||||
|
await this.pluginJSONParse.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle(workflow: WorkflowModel, enable?: boolean) {
|
toggle(workflow: WorkflowModel, enable?: boolean) {
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
import jsonata from 'jsonata';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import { FlowNodeModel, Instruction, JOB_STATUS, Processor } from '../..';
|
||||||
|
|
||||||
|
export class JSONParseInstruction extends Instruction {
|
||||||
|
engine = (expression, scope) => jsonata(expression).evaluate(scope);
|
||||||
|
async run(node: FlowNodeModel, input: any, processor: Processor) {
|
||||||
|
const { source = '', expression = '', model } = node.config;
|
||||||
|
const data = processor.getParsedValue(source, node.id);
|
||||||
|
const query = this.engine;
|
||||||
|
try {
|
||||||
|
let result = query ? await query(expression, data) : data;
|
||||||
|
|
||||||
|
if (typeof result === 'object' && result && model?.length) {
|
||||||
|
if (Array.isArray(result)) {
|
||||||
|
result = result.map((item) => mapModel(item, model));
|
||||||
|
} else {
|
||||||
|
result = mapModel(result, model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
status: JOB_STATUS.RESOLVED,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
result: err.toString(),
|
||||||
|
status: JOB_STATUS.ERROR,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapModel(data, model) {
|
||||||
|
if (typeof data !== 'object' || data === null) {
|
||||||
|
throw new Error('Invalid data: data should be a non-null object');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = model.reduce((acc, { path, alias }) => {
|
||||||
|
const key = alias ?? path.replace(/\./g, '_');
|
||||||
|
const value = _.get(data, path);
|
||||||
|
acc[key] = value;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export { default } from './plugin';
|
@ -0,0 +1,14 @@
|
|||||||
|
import { Plugin } from '@tachybase/server';
|
||||||
|
|
||||||
|
import WorkflowPlugin from '../..';
|
||||||
|
import { NAMESPACE_INSTRUCTION_JSON_PARSE } from '../../../common/constants';
|
||||||
|
import { JSONParseInstruction } from './JSONParse.instruction';
|
||||||
|
|
||||||
|
export class PluginWorkflowJSONParseServer extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const pluginWorkflow: any = this.app.pm.get(WorkflowPlugin);
|
||||||
|
pluginWorkflow.registerInstruction(NAMESPACE_INSTRUCTION_JSON_PARSE, JSONParseInstruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PluginWorkflowJSONParseServer;
|
@ -3946,6 +3946,9 @@ importers:
|
|||||||
'@tachybase/utils':
|
'@tachybase/utils':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../../core/utils
|
version: link:../../../core/utils
|
||||||
|
jsonata:
|
||||||
|
specifier: ^2.0.5
|
||||||
|
version: 2.0.5
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@ant-design/icons':
|
'@ant-design/icons':
|
||||||
specifier: ~5.3.6
|
specifier: ~5.3.6
|
||||||
@ -20859,6 +20862,11 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
/jsonata@2.0.5:
|
||||||
|
resolution: {integrity: sha512-wEse9+QLIIU5IaCgtJCPsFi/H4F3qcikWzF4bAELZiRz08ohfx3Q6CjDRf4ZPF5P/92RI3KIHtb7u3jqPaHXdQ==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jsonc-parser@3.2.0:
|
/jsonc-parser@3.2.0:
|
||||||
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
|
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user