biz-a-cli 2.3.72 → 2.3.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/app.js +23 -0
- package/bin/directHubEvent.js +93 -79
- package/bin/hub.js +42 -2
- package/bin/hubEvent.js +102 -21
- package/bin/migrate.js +169 -0
- package/callbackController.js +58 -36
- package/db/db.js +37 -0
- package/db/ds.js +189 -0
- package/engine/bpm/routes.js +61 -0
- package/engine/bpm/workflow-runtime.js +1136 -0
- package/engine/bpm/workflow.js +235 -0
- package/migrations/1777727873750__apps.sql +16 -0
- package/migrations/1777727892577__bpm.sql +274 -0
- package/package.json +18 -2
- package/scheduler/datalib.js +267 -183
- package/worker/cliScriptWorker.js +63 -0
- package/worker/cliWorkerPool.js +71 -0
- package/.editorconfig +0 -16
- package/bin/log/debug.log +0 -12
- package/bin/log/error.log +0 -12
- package/bin/log/exception.log +0 -6
- package/bin/log/info.log +0 -12
- package/log/debug.log +0 -181
- package/log/error.log +0 -7
- package/log/exception.log +0 -3
- package/log/info.log +0 -6
- package/tests/app.test.js +0 -1208
- package/tests/callback.test.js +0 -42
- package/tests/config.test.js +0 -39
- package/tests/converter.test.js +0 -106
- package/tests/data.test.js +0 -487
- package/tests/deployment.test.js +0 -339
- package/tests/hub.test.js +0 -657
- package/tests/hubPublish.test.js +0 -231
- package/tests/mailCtl.test.js +0 -44
- package/tests/timer.test.js +0 -187
- package/tests/watcher.test.js +0 -352
- package/tests/watcherCtl.test.js +0 -124
package/callbackController.js
CHANGED
|
@@ -1,42 +1,64 @@
|
|
|
1
|
-
import { loadCliScript, extractFunctionScript } from "./scheduler/datalib.js";
|
|
2
|
-
|
|
1
|
+
// import { loadCliScript, extractFunctionScript } from "./scheduler/datalib.js";
|
|
2
|
+
import { execCLIScriptWorker } from "./worker/cliWorkerPool.js";
|
|
3
3
|
|
|
4
4
|
export function getInputScript(req) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
const args = req.app.settings.args;
|
|
6
|
+
return {
|
|
7
|
+
data: {
|
|
8
|
+
arguments: args,
|
|
9
|
+
body: req.body.body,
|
|
10
|
+
path: req.path,
|
|
11
|
+
query: req.body.query,
|
|
12
|
+
headers: req.body.headers,
|
|
13
|
+
// socket: req.app.settings.socket, // seem never used, and it can not be serialized to worker thread
|
|
14
|
+
},
|
|
15
|
+
selectedConfig: {
|
|
16
|
+
url: `http://${args.hostname}:${args.port}`,
|
|
17
|
+
dbindex: args.dbindex,
|
|
18
|
+
subdomain: args.subdomain,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// export async function runCliScript(req, res) {
|
|
24
|
+
// try {
|
|
25
|
+
// const { data, selectedConfig } = getInputScript(req);
|
|
26
|
+
// let script = await loadCliScript(selectedConfig, 'ID', req.body.query.scriptid);
|
|
27
|
+
// let functions = await extractFunctionScript(selectedConfig, script);
|
|
28
|
+
// if (functions) {
|
|
29
|
+
// console.info(`## Callback ID ${req.body.query.scriptid} Starts. ##`);
|
|
30
|
+
|
|
31
|
+
// let respon = await functions.onInit(data);
|
|
32
|
+
// res.send(respon);
|
|
33
|
+
|
|
34
|
+
// console.info(`## Callback ID ${req.body.query.scriptid} Finish. ##`);
|
|
35
|
+
// } else {
|
|
36
|
+
// res.send('CLI Script does not exist.');
|
|
37
|
+
// }
|
|
38
|
+
// } catch (error) {
|
|
39
|
+
// console.error(error);
|
|
40
|
+
// throw new Error(error);
|
|
41
|
+
// }
|
|
42
|
+
// }
|
|
43
|
+
|
|
23
44
|
export async function runCliScript(req, res) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
try {
|
|
46
|
+
const { data, selectedConfig } = getInputScript(req);
|
|
47
|
+
|
|
48
|
+
console.info(
|
|
49
|
+
`## Callback ID ${req.body.query.scriptid} Starts. ##`,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
let respon = await execCLIScriptWorker(
|
|
53
|
+
selectedConfig,
|
|
54
|
+
{ searchBy: "ID", searchValue: req.body.query.scriptid },
|
|
55
|
+
data,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
res.send(respon);
|
|
59
|
+
console.info(`## Callback ID ${req.body.query.scriptid} Finish. ##`);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(error);
|
|
62
|
+
res.status(500).send(error.message || error);
|
|
63
|
+
}
|
|
42
64
|
}
|
package/db/db.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as adapter from "./ds.js";
|
|
2
|
+
|
|
3
|
+
export const queryData = async (param, config = {}) => {
|
|
4
|
+
return await adapter.queryData(param, config);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} payload - The data to save.
|
|
10
|
+
* @param {Object} [config] - Optional configuration overrides.
|
|
11
|
+
* @param {string} [tableName] - Optional. If provided, wraps the payload for standard ORM (sendModel).
|
|
12
|
+
* If omitted, sends the raw payload (for Sibling/ACID orchestration) (sendORMPayload).
|
|
13
|
+
*/
|
|
14
|
+
export const save = async (payload, config = {}) => {
|
|
15
|
+
return await adapter.save(payload, config);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const deleteMD = async (id, masterTable, detailTables, config = {}) => {
|
|
19
|
+
const payload = {
|
|
20
|
+
id: id,
|
|
21
|
+
master: masterTable,
|
|
22
|
+
detail: detailTables,
|
|
23
|
+
};
|
|
24
|
+
return await adapter.removeMD(payload, config);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const remove = async (payload, config = {}) => {
|
|
28
|
+
return await adapter.remove(payload, config);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const list = async (payload, config = {}) => {
|
|
32
|
+
return await adapter.list(payload, config);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const executeBlock = async (sqlString, config = {}) => {
|
|
36
|
+
return await adapter.executeBlock(sqlString, config);
|
|
37
|
+
};
|
package/db/ds.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { IDLE_SOCKET_TIMEOUT_MILLISECONDS } from "../bin/hubEvent.js";
|
|
3
|
+
|
|
4
|
+
let injectedConfig = {};
|
|
5
|
+
|
|
6
|
+
export const setGlobalConfig = (cfg) => {
|
|
7
|
+
injectedConfig = cfg;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// ===============
|
|
11
|
+
// PRIVATE HELPERS
|
|
12
|
+
// ===============
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Configuration object for DataSnap requests.
|
|
16
|
+
* All properties are OPTIONAL.
|
|
17
|
+
* Fallback Priority: 1. Caller Config -> 2. CLI ARGV -> 3. Hardcoded Defaults
|
|
18
|
+
*
|
|
19
|
+
* @typedef {Object} DsConfig
|
|
20
|
+
* @property {string} [url] - Override full base URL (e.g., "http://localhost:212").
|
|
21
|
+
* @property {boolean} [secure] - Use HTTPS? Fallback: argv.secure or false.
|
|
22
|
+
* @property {string} [hostname] - FINA API Hostname. Fallback: argv.hostname or "127.0.0.1".
|
|
23
|
+
* @property {number} [port] - FINA API Port. Fallback: argv.port or 212.
|
|
24
|
+
* @property {number} [dbindex] - Firebird Database Index. Fallback: argv.dbindex or 2.
|
|
25
|
+
* @property {string} [methodClass] - DataSnap Method Class. Default: "TOrmMethod".
|
|
26
|
+
* @property {number} [timeout] - Axios timeout in ms. Default: IDLE_SOCKET_TIMEOUT_MILLISECONDS.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
function resolveConfig(config = {}) {
|
|
30
|
+
// 3-Tier Fallback: 1. Caller Config -> 2. Global Defaults (hub.js argv) -> 3. Hardcoded Default
|
|
31
|
+
const isSecure = config.secure ?? injectedConfig.secure ?? false;
|
|
32
|
+
const protocol = isSecure ? "https://" : "http://";
|
|
33
|
+
const hostname = config.hostname ?? injectedConfig.hostname ?? "127.0.0.1";
|
|
34
|
+
const port = config.port ?? injectedConfig.port ?? 212;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
url: config.url || `${protocol}${hostname}:${port}`,
|
|
38
|
+
dbindex: config.dbindex ?? injectedConfig.dbindex ?? 2,
|
|
39
|
+
methodClass: config.methodClass ?? "TOrmMethod",
|
|
40
|
+
timeout: config.timeout ?? IDLE_SOCKET_TIMEOUT_MILLISECONDS,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildDataSnapUrl(resolvedConfig, dsPath, isPost = true) {
|
|
45
|
+
const encodedPath = isPost ? `%22${dsPath}%22` : dsPath;
|
|
46
|
+
return `${resolvedConfig.url}/fina/rest/${resolvedConfig.methodClass}/${encodedPath}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function buildDataSnapPayload(resolvedConfig, ormMethod, payloadObject) {
|
|
50
|
+
const paramObj = {
|
|
51
|
+
dbIndex: resolvedConfig.dbindex, // Critical for MultiDBServerMethod.pas, if payloadObject not have dbIndex
|
|
52
|
+
method: ormMethod,
|
|
53
|
+
object: payloadObject,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return JSON.stringify({
|
|
57
|
+
_parameters: [JSON.stringify(paramObj)],
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const dsReq = async (url, data, timeoutMs, httpMethod = "POST") => {
|
|
62
|
+
// Universal DataSnap Requester using axios.request
|
|
63
|
+
const res = await axios.request({
|
|
64
|
+
method: httpMethod,
|
|
65
|
+
url: url,
|
|
66
|
+
data: data,
|
|
67
|
+
timeout: timeoutMs,
|
|
68
|
+
// MANDATORY: Prevents DataSnap from parsing JSON into multiple parameters
|
|
69
|
+
headers: { "content-type": "text/plain" },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Normalize response (Bypass the JSON stringify tax if DataSnap double-stringified)
|
|
73
|
+
const parsedRes =
|
|
74
|
+
typeof res.data === "string" ? JSON.parse(res.data) : res.data;
|
|
75
|
+
|
|
76
|
+
// GATEKEEPER: Catch Delphi's `Result.AddPair(KEY_ERROR, E.Message);`
|
|
77
|
+
if (parsedRes && parsedRes.error) {
|
|
78
|
+
throw new Error(parsedRes.error);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return parsedRes;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// PUBLIC API (Zero-Config Needed by Caller, but worker-safe via config param)
|
|
86
|
+
// ============================================================================
|
|
87
|
+
|
|
88
|
+
export const queryData = async (payload, config = {}) => {
|
|
89
|
+
const cfg = resolveConfig(config);
|
|
90
|
+
const url = buildDataSnapUrl(cfg, "list");
|
|
91
|
+
const data = buildDataSnapPayload(cfg, "list", payload);
|
|
92
|
+
|
|
93
|
+
const res = await dsReq(url, data, cfg.timeout);
|
|
94
|
+
let rawData = res;
|
|
95
|
+
if (res && res.data) {
|
|
96
|
+
rawData =
|
|
97
|
+
typeof res.data === "string" ? JSON.parse(res.data) : res.data;
|
|
98
|
+
}
|
|
99
|
+
if (payload && Array.isArray(payload.columns) && Array.isArray(rawData)) {
|
|
100
|
+
return rawData.map((row) => {
|
|
101
|
+
const mappedObj = {};
|
|
102
|
+
|
|
103
|
+
payload.columns.forEach((col, index) => {
|
|
104
|
+
if (!col.key) return;
|
|
105
|
+
|
|
106
|
+
if (Array.isArray(row)) {
|
|
107
|
+
mappedObj[col.key] = row[index];
|
|
108
|
+
} else {
|
|
109
|
+
const exactDataMatch = row[col.data];
|
|
110
|
+
const upperDataMatch = row[String(col.data).toUpperCase()];
|
|
111
|
+
const aliasMatch = col.alias ? row[col.alias] : undefined;
|
|
112
|
+
const upperAliasMatch = col.alias
|
|
113
|
+
? row[String(col.alias).toUpperCase()]
|
|
114
|
+
: undefined;
|
|
115
|
+
|
|
116
|
+
if (exactDataMatch !== undefined)
|
|
117
|
+
mappedObj[col.key] = exactDataMatch;
|
|
118
|
+
else if (upperDataMatch !== undefined)
|
|
119
|
+
mappedObj[col.key] = upperDataMatch;
|
|
120
|
+
else if (aliasMatch !== undefined)
|
|
121
|
+
mappedObj[col.key] = aliasMatch;
|
|
122
|
+
else if (upperAliasMatch !== undefined)
|
|
123
|
+
mappedObj[col.key] = upperAliasMatch;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
return mappedObj;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return rawData;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export const save = async (payload, config = {}) => {
|
|
133
|
+
const cfg = resolveConfig(config);
|
|
134
|
+
const url = buildDataSnapUrl(cfg, "orm");
|
|
135
|
+
const data = buildDataSnapPayload(cfg, "put", payload);
|
|
136
|
+
return await dsReq(url, data, cfg.timeout);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const remove = async (payload, config = {}) => {
|
|
140
|
+
const cfg = resolveConfig(config);
|
|
141
|
+
const url = buildDataSnapUrl(cfg, "orm");
|
|
142
|
+
const data = buildDataSnapPayload(cfg, "delete", payload);
|
|
143
|
+
return await dsReq(url, data, cfg.timeout);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const removeMD = async (payload, config = {}) => {
|
|
147
|
+
const cfg = resolveConfig(config);
|
|
148
|
+
const url = buildDataSnapUrl(cfg, "orm");
|
|
149
|
+
|
|
150
|
+
const { id, master, detail } = payload;
|
|
151
|
+
let delphiObj = { [master]: { id: Number(id) } };
|
|
152
|
+
|
|
153
|
+
const buildNestedDetail = (details) => {
|
|
154
|
+
let result = {};
|
|
155
|
+
for (const item of details) {
|
|
156
|
+
if (typeof item === "string") {
|
|
157
|
+
result[item] = [];
|
|
158
|
+
} else if (typeof item === "object" && item !== null) {
|
|
159
|
+
for (const key of Object.keys(item)) {
|
|
160
|
+
if (Array.isArray(item[key]) && item[key].length > 0) {
|
|
161
|
+
result[key] = [buildNestedDetail(item[key])];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
if (detail && detail.length > 0) {
|
|
170
|
+
Object.assign(delphiObj[master], buildNestedDetail(detail));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const data = buildDataSnapPayload(cfg, "deletemd", delphiObj);
|
|
174
|
+
return await dsReq(url, data, cfg.timeout);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const list = async (payload, config = {}) => {
|
|
178
|
+
const cfg = resolveConfig(config);
|
|
179
|
+
const url = buildDataSnapUrl(cfg, "list");
|
|
180
|
+
const data = buildDataSnapPayload(cfg, "list", payload);
|
|
181
|
+
return await dsReq(url, data, cfg.timeout);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export const executeBlock = async (sqlString, config = {}) => {
|
|
185
|
+
const cfg = resolveConfig(config);
|
|
186
|
+
const url = buildDataSnapUrl(cfg, "block");
|
|
187
|
+
const data = buildDataSnapPayload(cfg, "block", sqlString);
|
|
188
|
+
return await dsReq(url, data, cfg.timeout);
|
|
189
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import * as workflow from "./workflow.js";
|
|
3
|
+
import * as workflowRT from "./workflow-runtime.js";
|
|
4
|
+
|
|
5
|
+
const router = express.Router();
|
|
6
|
+
|
|
7
|
+
// The Wrapper: Translates HTTP requests into pure Engine payloads
|
|
8
|
+
const expressWrapper = (engineFunction) => async (req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
const result = await engineFunction(req.body);
|
|
11
|
+
res.json({ success: true, data: result });
|
|
12
|
+
} catch (error) {
|
|
13
|
+
res.status(400).json({ success: false, error: error.message });
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// ==========================================
|
|
18
|
+
// DESIGN TIME: /workflow/ Prefix
|
|
19
|
+
// ==========================================
|
|
20
|
+
router.post("/workflow/save", expressWrapper(workflow.save));
|
|
21
|
+
router.post("/workflow/publish", expressWrapper(workflow.publish));
|
|
22
|
+
router.post("/workflow/remove", expressWrapper(workflow.remove));
|
|
23
|
+
|
|
24
|
+
// ==========================================
|
|
25
|
+
// RUNTIME EXECUTION: /workflowRT/ Prefix
|
|
26
|
+
// ==========================================
|
|
27
|
+
router.post("/workflowRT/initiate", expressWrapper(workflowRT.initiate));
|
|
28
|
+
router.post(
|
|
29
|
+
"/workflowRT/availableTransitions",
|
|
30
|
+
expressWrapper(workflowRT.availableTransitions),
|
|
31
|
+
);
|
|
32
|
+
router.post(
|
|
33
|
+
"/workflowRT/executeTransition",
|
|
34
|
+
expressWrapper(workflowRT.executeTransition),
|
|
35
|
+
);
|
|
36
|
+
router.post("/workflowRT/terminate", expressWrapper(workflowRT.terminate));
|
|
37
|
+
router.post("/workflowRT/remove", expressWrapper(workflowRT.remove));
|
|
38
|
+
router.post("/workflowRT/myTasks", expressWrapper(workflowRT.myTasks));
|
|
39
|
+
router.post(
|
|
40
|
+
"/workflowRT/instanceState",
|
|
41
|
+
expressWrapper(workflowRT.instanceState),
|
|
42
|
+
);
|
|
43
|
+
router.post(
|
|
44
|
+
"/workflowRT/instanceLogs",
|
|
45
|
+
expressWrapper(workflowRT.instanceLogs),
|
|
46
|
+
);
|
|
47
|
+
router.post(
|
|
48
|
+
"/workflowRT/instanceTasks",
|
|
49
|
+
expressWrapper(workflowRT.instanceTasks),
|
|
50
|
+
);
|
|
51
|
+
router.post(
|
|
52
|
+
"/workflowRT/instanceTimers",
|
|
53
|
+
expressWrapper(workflowRT.instanceTimers),
|
|
54
|
+
);
|
|
55
|
+
router.post(
|
|
56
|
+
"/workflowRT/instanceEffectLogs",
|
|
57
|
+
expressWrapper(workflowRT.instanceEffectLogs),
|
|
58
|
+
);
|
|
59
|
+
router.post("/workflowRT/retryEffect", expressWrapper(workflowRT.retryEffect));
|
|
60
|
+
|
|
61
|
+
export default router;
|