@certd/pipeline 1.0.0
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/.eslintrc +21 -0
- package/.mocharc.json +5 -0
- package/.prettierrc +3 -0
- package/README.md +16 -0
- package/index.ts +1 -0
- package/package.json +61 -0
- package/src/access/api.ts +18 -0
- package/src/access/decorator.ts +39 -0
- package/src/access/index.ts +3 -0
- package/src/access/registry.ts +4 -0
- package/src/context/index.ts +6 -0
- package/src/core/context.ts +88 -0
- package/src/core/executor.ts +207 -0
- package/src/core/index.ts +4 -0
- package/src/core/run-history.ts +158 -0
- package/src/core/storage.ts +140 -0
- package/src/d.ts/fast-crud.ts +115 -0
- package/src/d.ts/index.ts +2 -0
- package/src/d.ts/pipeline.ts +119 -0
- package/src/decorator/common.ts +17 -0
- package/src/decorator/index.ts +2 -0
- package/src/decorator/utils.ts +42 -0
- package/src/index.ts +9 -0
- package/src/midway/configuration.ts +52 -0
- package/src/midway/index.ts +5 -0
- package/src/plugin/api.ts +60 -0
- package/src/plugin/decorator.ts +63 -0
- package/src/plugin/index.ts +3 -0
- package/src/plugin/registry.ts +4 -0
- package/src/plugin/test/echo-plugin.ts +31 -0
- package/src/registry/index.ts +1 -0
- package/src/registry/registry.ts +57 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/util.log.ts +35 -0
- package/src/utils/util.request.ts +58 -0
- package/src/utils/util.sleep.ts +7 -0
- package/test/cert.fake.test.ts +58 -0
- package/test/echo-plugin.ts +30 -0
- package/test/index.test.ts +16 -0
- package/test/pipeline/access-service-test.ts +10 -0
- package/test/pipeline/init.test.ts +15 -0
- package/test/pipeline/pipeline.define.ts +66 -0
- package/test/pipeline/pipeline.test.ts +18 -0
- package/tsconfig.json +20 -0
- package/vite.config.js +35 -0
package/.eslintrc
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"parser": "@typescript-eslint/parser",
|
|
3
|
+
"plugins": [
|
|
4
|
+
"@typescript-eslint"
|
|
5
|
+
],
|
|
6
|
+
"extends": [
|
|
7
|
+
"plugin:@typescript-eslint/recommended",
|
|
8
|
+
"plugin:prettier/recommended",
|
|
9
|
+
"prettier"
|
|
10
|
+
],
|
|
11
|
+
"env": {
|
|
12
|
+
"mocha": true
|
|
13
|
+
},
|
|
14
|
+
"rules": {
|
|
15
|
+
"@typescript-eslint/ban-ts-comment": "off",
|
|
16
|
+
"@typescript-eslint/ban-ts-ignore": "off",
|
|
17
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
18
|
+
// "no-unused-expressions": "off",
|
|
19
|
+
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
|
20
|
+
}
|
|
21
|
+
}
|
package/.mocharc.json
ADDED
package/.prettierrc
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Vue 3 + TypeScript + Vite
|
|
2
|
+
|
|
3
|
+
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
|
4
|
+
|
|
5
|
+
## Recommended IDE Setup
|
|
6
|
+
|
|
7
|
+
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
|
|
8
|
+
|
|
9
|
+
## Type Support For `.vue` Imports in TS
|
|
10
|
+
|
|
11
|
+
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps:
|
|
12
|
+
|
|
13
|
+
1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled.
|
|
14
|
+
2. Reload the VS Code window by running `Developer: Reload Window` from the command palette.
|
|
15
|
+
|
|
16
|
+
You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471).
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./src";
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@certd/pipeline",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"main": "./dist/pipeline.umd.js",
|
|
6
|
+
"module": "./dist/pipeline.mjs",
|
|
7
|
+
"types": "./dist/d/index.d.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"main": "./dist/pipeline.umd.js",
|
|
10
|
+
"module": "./dist/pipeline.mjs",
|
|
11
|
+
"types": "./dist/d/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "vite",
|
|
15
|
+
"build": "vue-tsc --noEmit && vite build",
|
|
16
|
+
"preview": "vite preview"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@certd/acme-client": "^1.0.0",
|
|
20
|
+
"axios": "^0.21.1",
|
|
21
|
+
"dayjs": "^1.11.6",
|
|
22
|
+
"lodash": "^4.17.21",
|
|
23
|
+
"log4js": "^6.3.0",
|
|
24
|
+
"node-forge": "^0.10.0",
|
|
25
|
+
"qs": "^6.9.4",
|
|
26
|
+
"reflect-metadata": "^0.1.13"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@alicloud/cs20151215": "^3.0.3",
|
|
30
|
+
"@alicloud/openapi-client": "^0.4.0",
|
|
31
|
+
"@alicloud/pop-core": "^1.7.10",
|
|
32
|
+
"@midwayjs/bootstrap": "^3.9.1",
|
|
33
|
+
"@midwayjs/cache": "^3.9.0",
|
|
34
|
+
"@midwayjs/cli": "^1.3.21",
|
|
35
|
+
"@midwayjs/core": "^3.0.0",
|
|
36
|
+
"@midwayjs/decorator": "^3.0.0",
|
|
37
|
+
"@midwayjs/koa": "^3.9.0",
|
|
38
|
+
"@midwayjs/logger": "^2.17.0",
|
|
39
|
+
"@midwayjs/typeorm": "^3.9.0",
|
|
40
|
+
"@midwayjs/validate": "^3.9.0",
|
|
41
|
+
"@rollup/plugin-typescript": "^11.0.0",
|
|
42
|
+
"@types/chai": "^4.3.3",
|
|
43
|
+
"@types/lodash": "^4.14.194",
|
|
44
|
+
"@types/mocha": "^10.0.0",
|
|
45
|
+
"@types/node-forge": "^1.3.0",
|
|
46
|
+
"@typescript-eslint/eslint-plugin": "^5.38.1",
|
|
47
|
+
"@typescript-eslint/parser": "^5.38.1",
|
|
48
|
+
"chai": "^4.3.6",
|
|
49
|
+
"eslint": "^8.24.0",
|
|
50
|
+
"eslint-config-prettier": "^8.5.0",
|
|
51
|
+
"eslint-plugin-import": "^2.26.0",
|
|
52
|
+
"eslint-plugin-node": "^11.1.0",
|
|
53
|
+
"eslint-plugin-prettier": "^4.2.1",
|
|
54
|
+
"mocha": "^10.1.0",
|
|
55
|
+
"ts-node": "^10.9.1",
|
|
56
|
+
"typescript": "^4.8.4",
|
|
57
|
+
"vite": "^3.1.0",
|
|
58
|
+
"vue-tsc": "^0.38.9"
|
|
59
|
+
},
|
|
60
|
+
"gitHead": "5950e1cae7cf30ebfc5128c15c7d1b0d101cbbb8"
|
|
61
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Registrable } from "../registry";
|
|
2
|
+
import { FormItemProps } from "../d.ts";
|
|
3
|
+
|
|
4
|
+
export type AccessInputDefine = FormItemProps & {
|
|
5
|
+
title: string;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type AccessDefine = Registrable & {
|
|
9
|
+
input?: {
|
|
10
|
+
[key: string]: AccessInputDefine;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export interface IAccessService {
|
|
14
|
+
getById(id: any): Promise<any>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
18
|
+
export interface IAccess {}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/decorator/memoryCache.decorator.ts
|
|
2
|
+
import { AccessDefine, AccessInputDefine } from "./api";
|
|
3
|
+
import { Decorator } from "../decorator";
|
|
4
|
+
import _ from "lodash";
|
|
5
|
+
import { accessRegistry } from "./registry";
|
|
6
|
+
|
|
7
|
+
// 提供一个唯一 key
|
|
8
|
+
export const ACCESS_CLASS_KEY = "pipeline:access";
|
|
9
|
+
export const ACCESS_INPUT_KEY = "pipeline:access:input";
|
|
10
|
+
|
|
11
|
+
export function IsAccess(define: AccessDefine): ClassDecorator {
|
|
12
|
+
return (target: any) => {
|
|
13
|
+
target = Decorator.target(target);
|
|
14
|
+
|
|
15
|
+
const inputs: any = {};
|
|
16
|
+
const properties = Decorator.getClassProperties(target);
|
|
17
|
+
for (const property in properties) {
|
|
18
|
+
const input = Reflect.getMetadata(ACCESS_INPUT_KEY, target, property);
|
|
19
|
+
if (input) {
|
|
20
|
+
inputs[property] = input;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
_.merge(define, { input: inputs });
|
|
24
|
+
Reflect.defineMetadata(ACCESS_CLASS_KEY, define, target);
|
|
25
|
+
target.define = define;
|
|
26
|
+
accessRegistry.register(define.name, {
|
|
27
|
+
define,
|
|
28
|
+
target,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function AccessInput(input?: AccessInputDefine): PropertyDecorator {
|
|
34
|
+
return (target, propertyKey) => {
|
|
35
|
+
target = Decorator.target(target, propertyKey);
|
|
36
|
+
// const _type = Reflect.getMetadata("design:type", target, propertyKey);
|
|
37
|
+
Reflect.defineMetadata(ACCESS_INPUT_KEY, input, target, propertyKey);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { IStorage, MemoryStorage } from "./storage";
|
|
2
|
+
const CONTEXT_VERSION_KEY = "contextVersion";
|
|
3
|
+
export interface IContext {
|
|
4
|
+
getInt(key: string): Promise<number>;
|
|
5
|
+
get(key: string): Promise<string | null>;
|
|
6
|
+
set(key: string, value: string): Promise<void>;
|
|
7
|
+
getObj<T = any>(key: string): Promise<T | null>;
|
|
8
|
+
setObj<T = any>(key: string, value: T): Promise<void>;
|
|
9
|
+
updateVersion(): Promise<void>;
|
|
10
|
+
initVersion(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class ContextFactory {
|
|
14
|
+
storage: IStorage;
|
|
15
|
+
memoryStorage: IStorage;
|
|
16
|
+
|
|
17
|
+
constructor(storage: IStorage) {
|
|
18
|
+
this.storage = storage;
|
|
19
|
+
this.memoryStorage = new MemoryStorage();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getContext(scope: string, namespace: string): IContext {
|
|
23
|
+
const context = new StorageContext(scope, namespace, this.storage);
|
|
24
|
+
return context;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getMemoryContext(scope: string, namespace: string): IContext {
|
|
28
|
+
const context = new StorageContext(scope, namespace, this.memoryStorage);
|
|
29
|
+
return context;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class StorageContext implements IContext {
|
|
34
|
+
storage: IStorage;
|
|
35
|
+
namespace: string;
|
|
36
|
+
scope: string;
|
|
37
|
+
|
|
38
|
+
_version = 0;
|
|
39
|
+
_initialVersion = 0;
|
|
40
|
+
constructor(scope: string, namespace: string, storage: IStorage) {
|
|
41
|
+
this.storage = storage;
|
|
42
|
+
this.scope = scope;
|
|
43
|
+
this.namespace = namespace;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async initVersion() {
|
|
47
|
+
const version = await this.getInt(CONTEXT_VERSION_KEY);
|
|
48
|
+
this._initialVersion = version;
|
|
49
|
+
this._version = version;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async updateVersion() {
|
|
53
|
+
if (this._version === this._initialVersion) {
|
|
54
|
+
this._version++;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await this.set(CONTEXT_VERSION_KEY, this._version.toString());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async get(key: string) {
|
|
61
|
+
const version = key === CONTEXT_VERSION_KEY ? 0 : this._version;
|
|
62
|
+
return await this.storage.get(this.scope, this.namespace, version.toString(), key);
|
|
63
|
+
}
|
|
64
|
+
async set(key: string, value: string) {
|
|
65
|
+
const version = key === CONTEXT_VERSION_KEY ? 0 : this._version;
|
|
66
|
+
return await this.storage.set(this.scope, this.namespace, version.toString(), key, value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async getInt(key: string): Promise<number> {
|
|
70
|
+
const str = await this.get(key);
|
|
71
|
+
if (str) {
|
|
72
|
+
return parseInt(str);
|
|
73
|
+
}
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
async getObj<T = any>(key: string): Promise<T | null> {
|
|
77
|
+
const str = await this.get(key);
|
|
78
|
+
if (str) {
|
|
79
|
+
const store = JSON.parse(str);
|
|
80
|
+
return store.value;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async setObj<T = any>(key: string, value: T) {
|
|
86
|
+
await this.set(key, JSON.stringify({ value }));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { ConcurrencyStrategy, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { RunHistory, RunnableCollection } from "./run-history";
|
|
4
|
+
import { AbstractTaskPlugin, PluginDefine, pluginRegistry } from "../plugin";
|
|
5
|
+
import { ContextFactory, IContext } from "./context";
|
|
6
|
+
import { IStorage } from "./storage";
|
|
7
|
+
import { logger } from "../utils/util.log";
|
|
8
|
+
import { Logger } from "log4js";
|
|
9
|
+
import { request } from "../utils/util.request";
|
|
10
|
+
import { IAccessService } from "../access";
|
|
11
|
+
import { RegistryItem } from "../registry";
|
|
12
|
+
import { Decorator } from "../decorator";
|
|
13
|
+
|
|
14
|
+
export class Executor {
|
|
15
|
+
userId: any;
|
|
16
|
+
pipeline: Pipeline;
|
|
17
|
+
runtime!: RunHistory;
|
|
18
|
+
accessService: IAccessService;
|
|
19
|
+
contextFactory: ContextFactory;
|
|
20
|
+
logger: Logger;
|
|
21
|
+
pipelineContext!: IContext;
|
|
22
|
+
lastStatusMap!: RunnableCollection;
|
|
23
|
+
onChanged: (history: RunHistory) => void;
|
|
24
|
+
constructor(options: {
|
|
25
|
+
userId: any;
|
|
26
|
+
pipeline: Pipeline;
|
|
27
|
+
storage: IStorage;
|
|
28
|
+
onChanged: (history: RunHistory) => Promise<void>;
|
|
29
|
+
accessService: IAccessService;
|
|
30
|
+
}) {
|
|
31
|
+
this.pipeline = _.cloneDeep(options.pipeline);
|
|
32
|
+
this.onChanged = async (history: RunHistory) => {
|
|
33
|
+
await options.onChanged(history);
|
|
34
|
+
};
|
|
35
|
+
this.accessService = options.accessService;
|
|
36
|
+
this.userId = options.userId;
|
|
37
|
+
this.pipeline.userId = this.userId;
|
|
38
|
+
this.contextFactory = new ContextFactory(options.storage);
|
|
39
|
+
this.logger = logger;
|
|
40
|
+
this.pipelineContext = this.contextFactory.getContext("pipeline", this.pipeline.id);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async init() {
|
|
44
|
+
const lastRuntime = await this.pipelineContext.getObj(`lastRuntime`);
|
|
45
|
+
this.lastStatusMap = new RunnableCollection(lastRuntime?.pipeline);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async run(runtimeId: any = 0, triggerType: string) {
|
|
49
|
+
try {
|
|
50
|
+
await this.init();
|
|
51
|
+
const trigger = { type: triggerType };
|
|
52
|
+
// 读取last
|
|
53
|
+
this.runtime = new RunHistory(runtimeId, trigger, this.pipeline);
|
|
54
|
+
this.logger.info(`pipeline.${this.pipeline.id} start`);
|
|
55
|
+
await this.runWithHistory(this.pipeline, "pipeline", async () => {
|
|
56
|
+
await this.runStages(this.pipeline);
|
|
57
|
+
});
|
|
58
|
+
} catch (e) {
|
|
59
|
+
this.logger.error("pipeline 执行失败", e);
|
|
60
|
+
} finally {
|
|
61
|
+
await this.pipelineContext.setObj("lastRuntime", this.runtime);
|
|
62
|
+
this.logger.info(`pipeline.${this.pipeline.id} end`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async runWithHistory(runnable: Runnable, runnableType: string, run: () => Promise<void>) {
|
|
67
|
+
runnable.runnableType = runnableType;
|
|
68
|
+
this.runtime.start(runnable);
|
|
69
|
+
await this.onChanged(this.runtime);
|
|
70
|
+
|
|
71
|
+
if (runnable.strategy?.runStrategy === RunStrategy.SkipWhenSucceed) {
|
|
72
|
+
//如果是成功后跳过策略
|
|
73
|
+
const lastNode = this.lastStatusMap.get(runnable.id);
|
|
74
|
+
const lastResult = lastNode?.status?.status;
|
|
75
|
+
const lastInput = JSON.stringify(lastNode?.status?.input);
|
|
76
|
+
let inputChanged = false;
|
|
77
|
+
if (runnableType === "step") {
|
|
78
|
+
const step = runnable as Step;
|
|
79
|
+
const input = JSON.stringify(step.input);
|
|
80
|
+
if (input != null && lastInput !== input) {
|
|
81
|
+
//参数有变化
|
|
82
|
+
inputChanged = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (lastResult != null && lastResult === ResultType.success && !inputChanged) {
|
|
86
|
+
this.runtime.skip(runnable);
|
|
87
|
+
await this.onChanged(this.runtime);
|
|
88
|
+
return ResultType.skip;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
await run();
|
|
93
|
+
this.runtime.success(runnable);
|
|
94
|
+
await this.onChanged(this.runtime);
|
|
95
|
+
return ResultType.success;
|
|
96
|
+
} catch (e: any) {
|
|
97
|
+
this.runtime.error(runnable, e);
|
|
98
|
+
await this.onChanged(this.runtime);
|
|
99
|
+
throw e;
|
|
100
|
+
} finally {
|
|
101
|
+
this.runtime.finally(runnable);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private async runStages(pipeline: Pipeline) {
|
|
106
|
+
const resList: ResultType[] = [];
|
|
107
|
+
for (const stage of pipeline.stages) {
|
|
108
|
+
const res: ResultType = await this.runWithHistory(stage, "stage", async () => {
|
|
109
|
+
await this.runStage(stage);
|
|
110
|
+
});
|
|
111
|
+
resList.push(res);
|
|
112
|
+
}
|
|
113
|
+
return this.compositionResultType(resList);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async runStage(stage: Stage) {
|
|
117
|
+
const runnerList = [];
|
|
118
|
+
for (const task of stage.tasks) {
|
|
119
|
+
const runner = this.runWithHistory(task, "task", async () => {
|
|
120
|
+
await this.runTask(task);
|
|
121
|
+
});
|
|
122
|
+
runnerList.push(runner);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let resList: ResultType[] = [];
|
|
126
|
+
if (stage.concurrency === ConcurrencyStrategy.Parallel) {
|
|
127
|
+
resList = await Promise.all(runnerList);
|
|
128
|
+
} else {
|
|
129
|
+
for (let i = 0; i < runnerList.length; i++) {
|
|
130
|
+
const runner = runnerList[i];
|
|
131
|
+
resList[i] = await runner;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return this.compositionResultType(resList);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
compositionResultType(resList: ResultType[]) {
|
|
138
|
+
let hasSuccess = false;
|
|
139
|
+
for (const type of resList) {
|
|
140
|
+
if (type === ResultType.error) {
|
|
141
|
+
return ResultType.error;
|
|
142
|
+
}
|
|
143
|
+
if (type === ResultType.success) {
|
|
144
|
+
hasSuccess = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (hasSuccess) {
|
|
148
|
+
return ResultType.success;
|
|
149
|
+
}
|
|
150
|
+
return ResultType.error;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private async runTask(task: Task) {
|
|
154
|
+
const resList: ResultType[] = [];
|
|
155
|
+
for (const step of task.steps) {
|
|
156
|
+
step.runnableType = "step";
|
|
157
|
+
const res: ResultType = await this.runWithHistory(step, "step", async () => {
|
|
158
|
+
await this.runStep(step);
|
|
159
|
+
});
|
|
160
|
+
resList.push(res);
|
|
161
|
+
}
|
|
162
|
+
return this.compositionResultType(resList);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private async runStep(step: Step) {
|
|
166
|
+
const lastStatus = this.lastStatusMap.get(step.id);
|
|
167
|
+
//执行任务
|
|
168
|
+
const plugin: RegistryItem<AbstractTaskPlugin> = pluginRegistry.get(step.type);
|
|
169
|
+
|
|
170
|
+
// @ts-ignore
|
|
171
|
+
const instance: ITaskPlugin = new plugin.target();
|
|
172
|
+
// @ts-ignore
|
|
173
|
+
const define: PluginDefine = plugin.define;
|
|
174
|
+
//从outputContext读取输入参数
|
|
175
|
+
Decorator.inject(define.input, instance, step.input, (item, key) => {
|
|
176
|
+
if (item.component?.name === "pi-output-selector") {
|
|
177
|
+
const contextKey = step.input[key];
|
|
178
|
+
if (contextKey != null) {
|
|
179
|
+
step.input[key] = this.runtime.context[contextKey];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const context: any = {
|
|
185
|
+
logger: this.runtime._loggers[step.id],
|
|
186
|
+
accessService: this.accessService,
|
|
187
|
+
pipelineContext: this.pipelineContext,
|
|
188
|
+
lastStatus,
|
|
189
|
+
userContext: this.contextFactory.getContext("user", this.userId),
|
|
190
|
+
http: request,
|
|
191
|
+
};
|
|
192
|
+
Decorator.inject(define.autowire, instance, context);
|
|
193
|
+
|
|
194
|
+
await instance.onInstance();
|
|
195
|
+
await instance.execute();
|
|
196
|
+
|
|
197
|
+
if (instance.result.clearLastStatus) {
|
|
198
|
+
this.lastStatusMap.clear();
|
|
199
|
+
}
|
|
200
|
+
//输出到output context
|
|
201
|
+
_.forEach(define.output, (item, key) => {
|
|
202
|
+
step!.status!.output[key] = instance[key];
|
|
203
|
+
const stepOutputKey = `step.${step.id}.${key}`;
|
|
204
|
+
this.runtime.context[stepOutputKey] = instance[key];
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Context, HistoryResult, Pipeline, ResultType, Runnable, RunnableMap, Stage, Step, Task } from "../d.ts";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { buildLogger } from "../utils/util.log";
|
|
4
|
+
import { Logger } from "log4js";
|
|
5
|
+
|
|
6
|
+
export type HistoryStatus = {
|
|
7
|
+
result: HistoryResult;
|
|
8
|
+
logs: string[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type RunTrigger = {
|
|
12
|
+
type: string; // user , timer
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function NewRunHistory(obj: any) {
|
|
16
|
+
const history = new RunHistory(obj.id, obj.trigger, obj.pipeline);
|
|
17
|
+
history.context = obj.context;
|
|
18
|
+
history.logs = obj.logs;
|
|
19
|
+
history._loggers = obj.loggers;
|
|
20
|
+
return history;
|
|
21
|
+
}
|
|
22
|
+
export class RunHistory {
|
|
23
|
+
id!: string;
|
|
24
|
+
//运行时上下文变量
|
|
25
|
+
context: Context = {};
|
|
26
|
+
pipeline!: Pipeline;
|
|
27
|
+
logs: {
|
|
28
|
+
[runnableId: string]: string[];
|
|
29
|
+
} = {};
|
|
30
|
+
_loggers: {
|
|
31
|
+
[runnableId: string]: Logger;
|
|
32
|
+
} = {};
|
|
33
|
+
trigger!: RunTrigger;
|
|
34
|
+
|
|
35
|
+
constructor(runtimeId: any, trigger: RunTrigger, pipeline: Pipeline) {
|
|
36
|
+
this.id = runtimeId;
|
|
37
|
+
this.pipeline = pipeline;
|
|
38
|
+
this.trigger = trigger;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
start(runnable: Runnable): HistoryResult {
|
|
42
|
+
const now = new Date().getTime();
|
|
43
|
+
this.logs[runnable.id] = [];
|
|
44
|
+
this._loggers[runnable.id] = buildLogger((text) => {
|
|
45
|
+
this.logs[runnable.id].push(text);
|
|
46
|
+
});
|
|
47
|
+
const input = (runnable as Step).input;
|
|
48
|
+
const status: HistoryResult = {
|
|
49
|
+
output: {},
|
|
50
|
+
input: _.cloneDeep(input),
|
|
51
|
+
status: ResultType.start,
|
|
52
|
+
startTime: now,
|
|
53
|
+
result: ResultType.start,
|
|
54
|
+
};
|
|
55
|
+
runnable.status = status;
|
|
56
|
+
this.log(runnable, `开始执行`);
|
|
57
|
+
return status;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
success(runnable: Runnable) {
|
|
61
|
+
const now = new Date().getTime();
|
|
62
|
+
const status = runnable.status;
|
|
63
|
+
_.merge(status, {
|
|
64
|
+
status: "success",
|
|
65
|
+
endTime: now,
|
|
66
|
+
result: "success",
|
|
67
|
+
});
|
|
68
|
+
this.log(runnable, `执行成功`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
skip(runnable: Runnable) {
|
|
72
|
+
const now = new Date().getTime();
|
|
73
|
+
const status = runnable.status;
|
|
74
|
+
_.merge(status, {
|
|
75
|
+
status: "success",
|
|
76
|
+
endTime: now,
|
|
77
|
+
result: "skip",
|
|
78
|
+
});
|
|
79
|
+
this.log(runnable, `跳过`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
error(runnable: Runnable, e: Error) {
|
|
83
|
+
const now = new Date().getTime();
|
|
84
|
+
const status = runnable.status;
|
|
85
|
+
_.merge(status, {
|
|
86
|
+
status: ResultType.error,
|
|
87
|
+
endTime: now,
|
|
88
|
+
result: ResultType.error,
|
|
89
|
+
message: e.message,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.logError(runnable, e);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
log(runnable: Runnable, text: string) {
|
|
96
|
+
// @ts-ignore
|
|
97
|
+
this._loggers[runnable.id].info(`[${runnable.title}]<id:${runnable.id}> [${runnable.runnableType}]`, text);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
logError(runnable: Runnable, e: Error) {
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
this._loggers[runnable.id].error(`[${runnable.title}]<id:${runnable.id}> [${runnable.runnableType}]`, e);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
finally(runnable: Runnable) {
|
|
106
|
+
//
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export class RunnableCollection {
|
|
111
|
+
private collection: RunnableMap = {};
|
|
112
|
+
private pipeline!: Pipeline;
|
|
113
|
+
constructor(pipeline?: Pipeline) {
|
|
114
|
+
if (!pipeline) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this.pipeline = pipeline;
|
|
118
|
+
const map = this.toMap(pipeline);
|
|
119
|
+
this.collection = map;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private each<T extends Runnable>(list: T[], exec: (item: Runnable) => void) {
|
|
123
|
+
list.forEach((item) => {
|
|
124
|
+
exec(item);
|
|
125
|
+
if (item.runnableType === "pipeline") {
|
|
126
|
+
// @ts-ignore
|
|
127
|
+
this.each<Stage>(item.stages, exec);
|
|
128
|
+
} else if (item.runnableType === "stage") {
|
|
129
|
+
// @ts-ignore
|
|
130
|
+
this.each<Task>(item.tasks, exec);
|
|
131
|
+
} else if (item.runnableType === "task") {
|
|
132
|
+
// @ts-ignore
|
|
133
|
+
this.each<Step>(item.steps, exec);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
private toMap(pipeline: Pipeline) {
|
|
138
|
+
const map: RunnableMap = {};
|
|
139
|
+
|
|
140
|
+
this.each(pipeline.stages, (item) => {
|
|
141
|
+
map[item.id] = item;
|
|
142
|
+
});
|
|
143
|
+
return map;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
get(id: string): Runnable | undefined {
|
|
147
|
+
return this.collection[id];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
clear() {
|
|
151
|
+
if (!this.pipeline) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.each(this.pipeline.stages, (item) => {
|
|
155
|
+
item.status = undefined;
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|