creo-flow-extensions-base 1.0.0-dev.1
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/README.md +1 -0
- package/lib/api/index.d.ts +1 -0
- package/lib/api/index.js +22 -0
- package/lib/connection/index.d.ts +18 -0
- package/lib/connection/index.js +2 -0
- package/lib/context/index.d.ts +18 -0
- package/lib/context/index.js +178 -0
- package/lib/context/interfaces/index.d.ts +32 -0
- package/lib/context/interfaces/index.js +2 -0
- package/lib/context/utils/index.d.ts +7 -0
- package/lib/context/utils/index.js +69 -0
- package/lib/createExtension/index.d.ts +36 -0
- package/lib/createExtension/index.js +130 -0
- package/lib/enum/index.d.ts +7 -0
- package/lib/enum/index.js +11 -0
- package/lib/error/index.d.ts +3 -0
- package/lib/error/index.js +10 -0
- package/lib/fields/checkbox/index.d.ts +18 -0
- package/lib/fields/checkbox/index.js +16 -0
- package/lib/fields/code/index.d.ts +18 -0
- package/lib/fields/code/index.js +16 -0
- package/lib/fields/enum/index.d.ts +6 -0
- package/lib/fields/enum/index.js +11 -0
- package/lib/fields/input/index.d.ts +23 -0
- package/lib/fields/input/index.js +22 -0
- package/lib/fields/select/index.d.ts +40 -0
- package/lib/fields/select/index.js +18 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +22 -0
- package/lib/logger/index.d.ts +26 -0
- package/lib/logger/index.js +31 -0
- package/lib/nodes/index.d.ts +19 -0
- package/lib/nodes/index.js +13 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# cdh-flow-extension-base
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ownApi: import("axios").AxiosInstance;
|
package/lib/api/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ownApi = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const baseApiUrl = process.env.baseApiUrl;
|
|
9
|
+
exports.ownApi = axios_1.default.create({
|
|
10
|
+
baseURL: baseApiUrl,
|
|
11
|
+
timeout: 60000 * 2, // 120sec
|
|
12
|
+
maxContentLength: 200 * 1000 * 1024 * 1024, // 2 000 MB
|
|
13
|
+
});
|
|
14
|
+
const retryRequest = (error) => {
|
|
15
|
+
if (error.response && error.response.status === 502) {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
setTimeout(() => resolve((0, axios_1.default)(error.config)), 7000);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return Promise.reject(error);
|
|
21
|
+
};
|
|
22
|
+
exports.ownApi.interceptors.response.use(response => response, retryRequest);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { IInputField } from '../fields/input';
|
|
2
|
+
import { ISelectField } from '../fields/select';
|
|
3
|
+
import { ICheckboxField } from '../fields/checkbox';
|
|
4
|
+
export interface IConnectionStep {
|
|
5
|
+
title: string;
|
|
6
|
+
fields: (IInputField | ISelectField | ICheckboxField)[];
|
|
7
|
+
}
|
|
8
|
+
export interface IConnectionSchema {
|
|
9
|
+
type: string;
|
|
10
|
+
title: string;
|
|
11
|
+
name: string;
|
|
12
|
+
fields: (IInputField | ISelectField | ICheckboxField)[];
|
|
13
|
+
checkConnection: (...args: any) => Promise<any>;
|
|
14
|
+
}
|
|
15
|
+
export interface IConnectionSchemaField {
|
|
16
|
+
key: string;
|
|
17
|
+
value: string;
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { IDeleteContextToRedis, IGetContextFromRedis, IGetContextFromS3, IWriteContext, IWriteContextToRedis, IWriteContextToS3 } from './interfaces';
|
|
2
|
+
declare class ContextService {
|
|
3
|
+
getContext({ methodProps, token, urls }: {
|
|
4
|
+
methodProps: any;
|
|
5
|
+
token: string;
|
|
6
|
+
urls?: Record<string, string>;
|
|
7
|
+
}): Promise<Record<string, any> | undefined>;
|
|
8
|
+
writeContext({ contextKey, data, token, url }: IWriteContext): Promise<void>;
|
|
9
|
+
private getContextByKeys;
|
|
10
|
+
private receiveContext;
|
|
11
|
+
getContextFromRedis({ contextKey, token }: IGetContextFromRedis): Promise<import("axios").AxiosResponse<any, any>>;
|
|
12
|
+
getContextFromS3({ url }: IGetContextFromS3): Promise<any>;
|
|
13
|
+
sendContextToRedis({ contextKey, data, token }: IWriteContextToRedis): Promise<void>;
|
|
14
|
+
sendContextToS3({ data, url }: IWriteContextToS3): Promise<void>;
|
|
15
|
+
deleteContextFromRedis({ contextKey, token }: IDeleteContextToRedis): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare const contextService: ContextService;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.contextService = void 0;
|
|
7
|
+
const api_1 = require("../api");
|
|
8
|
+
const utils_1 = require("./utils");
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
const error_1 = require("../error");
|
|
11
|
+
const v8_1 = require("v8");
|
|
12
|
+
const zlib_1 = __importDefault(require("zlib"));
|
|
13
|
+
const logger_1 = require("../logger");
|
|
14
|
+
const redisSizeLimit = 1024 * 100; // 0.1mb
|
|
15
|
+
class ContextService {
|
|
16
|
+
async getContext({ methodProps, token, urls }) {
|
|
17
|
+
try {
|
|
18
|
+
const contextReadKeys = (0, utils_1.getContextKeys)(methodProps);
|
|
19
|
+
if (!contextReadKeys?.length || !urls) {
|
|
20
|
+
logger_1.logger.info({
|
|
21
|
+
message: 'No context to read (context keys not used)',
|
|
22
|
+
location: this.constructor.name,
|
|
23
|
+
payload: {
|
|
24
|
+
contextReadKeys,
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
logger_1.logger.info({
|
|
30
|
+
message: 'Get context',
|
|
31
|
+
location: this.constructor.name,
|
|
32
|
+
payload: {
|
|
33
|
+
contextReadKeys,
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
const context = await this.getContextByKeys({ contextKeys: contextReadKeys, token, urls });
|
|
37
|
+
logger_1.logger.info({
|
|
38
|
+
message: 'Context received',
|
|
39
|
+
location: this.constructor.name,
|
|
40
|
+
payload: {
|
|
41
|
+
contextReadKeys,
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return context;
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
logger_1.logger.error({
|
|
48
|
+
message: 'Error while getting context',
|
|
49
|
+
location: this.constructor.name,
|
|
50
|
+
payload: {
|
|
51
|
+
errorMessage: e.message,
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
throw new error_1.BaseError(`Error while getting context`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async writeContext({ contextKey, data, token, url }) {
|
|
58
|
+
try {
|
|
59
|
+
const dataSize = (0, utils_1.calculateBiteSize)(data);
|
|
60
|
+
const isShouldBeWrittenToS3 = dataSize > redisSizeLimit;
|
|
61
|
+
logger_1.logger.info({
|
|
62
|
+
message: 'Write context',
|
|
63
|
+
location: this.constructor.name,
|
|
64
|
+
payload: {
|
|
65
|
+
dataSize,
|
|
66
|
+
redisSizeLimit,
|
|
67
|
+
isShouldBeWrittenToS3,
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
if (!url) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (isShouldBeWrittenToS3) {
|
|
74
|
+
await this.deleteContextFromRedis({ contextKey, token });
|
|
75
|
+
const result = await this.sendContextToS3({ data, url });
|
|
76
|
+
logger_1.logger.info({
|
|
77
|
+
message: 'Context written to S3',
|
|
78
|
+
location: this.constructor.name,
|
|
79
|
+
});
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
const result = await this.sendContextToRedis({ contextKey, data, token });
|
|
83
|
+
logger_1.logger.info({
|
|
84
|
+
message: 'Context written to Redis',
|
|
85
|
+
location: this.constructor.name,
|
|
86
|
+
});
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
logger_1.logger.error({
|
|
91
|
+
message: 'Error while writing context',
|
|
92
|
+
location: this.constructor.name,
|
|
93
|
+
payload: {
|
|
94
|
+
errorMessage: e.message,
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
throw new error_1.BaseError(`Error while writing context`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async getContextByKeys({ contextKeys, token, urls }) {
|
|
101
|
+
const contextResults = {};
|
|
102
|
+
for (const contextKey of contextKeys) {
|
|
103
|
+
if (!contextKey)
|
|
104
|
+
return {};
|
|
105
|
+
const result = await this.receiveContext({ contextKey, token, url: urls[contextKey] });
|
|
106
|
+
contextResults[contextKey] = result;
|
|
107
|
+
}
|
|
108
|
+
return contextResults;
|
|
109
|
+
}
|
|
110
|
+
async receiveContext({ contextKey, token, url }) {
|
|
111
|
+
try {
|
|
112
|
+
logger_1.logger.info({
|
|
113
|
+
message: 'Get context by key from Redis',
|
|
114
|
+
location: this.constructor.name,
|
|
115
|
+
payload: {
|
|
116
|
+
contextKey,
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
const contextResponse = await this.getContextFromRedis({ contextKey, token });
|
|
120
|
+
logger_1.logger.info({
|
|
121
|
+
message: 'Context by key from Redis received',
|
|
122
|
+
location: this.constructor.name,
|
|
123
|
+
payload: {
|
|
124
|
+
contextKey,
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return contextResponse.data;
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
const axiosError = error;
|
|
131
|
+
if (axiosError?.response?.status === 404) {
|
|
132
|
+
logger_1.logger.info({
|
|
133
|
+
message: 'Context by key not found in Redis. Get context by key from S3',
|
|
134
|
+
location: this.constructor.name,
|
|
135
|
+
payload: {
|
|
136
|
+
contextKey,
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
const result = await this.getContextFromS3({ url });
|
|
140
|
+
logger_1.logger.info({
|
|
141
|
+
message: 'Context by key from S3 received',
|
|
142
|
+
location: this.constructor.name,
|
|
143
|
+
payload: {
|
|
144
|
+
contextKey,
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async getContextFromRedis({ contextKey, token }) {
|
|
153
|
+
const { flowContextId, flowId } = (0, utils_1.parseToken)(token);
|
|
154
|
+
return api_1.ownApi.get(`flows/${flowId}/contexts/${flowContextId}`, {
|
|
155
|
+
params: { contextKey },
|
|
156
|
+
headers: { 'context-token': token }
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async getContextFromS3({ url }) {
|
|
160
|
+
const { data: response } = await axios_1.default.get(url, { responseType: 'arraybuffer' });
|
|
161
|
+
const uncompressed = zlib_1.default.unzipSync(response);
|
|
162
|
+
return (0, v8_1.deserialize)(uncompressed);
|
|
163
|
+
}
|
|
164
|
+
async sendContextToRedis({ contextKey, data, token }) {
|
|
165
|
+
const { flowContextId, flowId } = (0, utils_1.parseToken)(token);
|
|
166
|
+
await api_1.ownApi.patch(`flows/${flowId}/contexts/${flowContextId}`, { contextKey, contextData: data }, { headers: { 'context-token': token } });
|
|
167
|
+
}
|
|
168
|
+
async sendContextToS3({ data, url }) {
|
|
169
|
+
const serialized = (0, v8_1.serialize)(data);
|
|
170
|
+
const compressed = zlib_1.default.gzipSync(serialized);
|
|
171
|
+
await axios_1.default.put(url, compressed);
|
|
172
|
+
}
|
|
173
|
+
async deleteContextFromRedis({ contextKey, token }) {
|
|
174
|
+
const { flowContextId, flowId } = (0, utils_1.parseToken)(token);
|
|
175
|
+
await api_1.ownApi.delete(`flows/${flowId}/contexts/${flowContextId}/${contextKey}`, { headers: { 'context-token': token } });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
exports.contextService = new ContextService();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface IGetContextProps {
|
|
2
|
+
contextKeys: string[];
|
|
3
|
+
token: string;
|
|
4
|
+
urls: {
|
|
5
|
+
[key: string]: string;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export type IWriteContext = IWriteContextToRedis & IWriteContextToS3;
|
|
9
|
+
export interface IWriteContextToRedis {
|
|
10
|
+
contextKey: string;
|
|
11
|
+
data: any;
|
|
12
|
+
token: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IWriteContextToS3 {
|
|
15
|
+
url: string;
|
|
16
|
+
data: any;
|
|
17
|
+
}
|
|
18
|
+
export interface IGetContextFromRedis {
|
|
19
|
+
contextKey: string;
|
|
20
|
+
token: string;
|
|
21
|
+
}
|
|
22
|
+
export interface IGetContextFromS3 {
|
|
23
|
+
url: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ContextTokenPayload {
|
|
26
|
+
flowContextId: string;
|
|
27
|
+
flowId: string;
|
|
28
|
+
}
|
|
29
|
+
export interface IDeleteContextToRedis {
|
|
30
|
+
contextKey: string;
|
|
31
|
+
token: string;
|
|
32
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const getContextKeys: (methodProps: any) => any[] | undefined;
|
|
2
|
+
export declare const parseToken: (token: string) => {
|
|
3
|
+
flowContextId: string;
|
|
4
|
+
flowId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const calculateBiteSize: (value: any) => number;
|
|
7
|
+
export declare const checkIsJSON: (myString: string) => boolean;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.checkIsJSON = exports.calculateBiteSize = exports.parseToken = exports.getContextKeys = void 0;
|
|
27
|
+
const jwt = __importStar(require("jsonwebtoken"));
|
|
28
|
+
const v8_1 = require("v8");
|
|
29
|
+
const getContextKeys = (methodProps) => {
|
|
30
|
+
const regex = /context\.([a-zA-Z0-9_]+)/g;
|
|
31
|
+
const matches = JSON.stringify(methodProps).match(regex);
|
|
32
|
+
const foundKeys = matches?.map((match) => match?.split('.')[1]) || undefined;
|
|
33
|
+
return foundKeys;
|
|
34
|
+
};
|
|
35
|
+
exports.getContextKeys = getContextKeys;
|
|
36
|
+
const parseToken = (token) => {
|
|
37
|
+
try {
|
|
38
|
+
const decodedPayload = jwt.decode(token, { complete: true });
|
|
39
|
+
if (!decodedPayload || !decodedPayload.payload) {
|
|
40
|
+
throw new Error('Invalid JWT format');
|
|
41
|
+
}
|
|
42
|
+
const { flowContextId, flowId } = decodedPayload.payload;
|
|
43
|
+
if (!flowContextId || !flowId) {
|
|
44
|
+
throw new Error('Missing params in JWT payload');
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
flowContextId,
|
|
48
|
+
flowId,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
throw new Error('Error parsing JWT payload');
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
exports.parseToken = parseToken;
|
|
56
|
+
const calculateBiteSize = (value) => {
|
|
57
|
+
return Buffer.from((0, v8_1.serialize)(value)).length;
|
|
58
|
+
};
|
|
59
|
+
exports.calculateBiteSize = calculateBiteSize;
|
|
60
|
+
const checkIsJSON = (myString) => {
|
|
61
|
+
try {
|
|
62
|
+
JSON.parse(myString);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
exports.checkIsJSON = checkIsJSON;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { IConnectionSchema } from '../connection';
|
|
2
|
+
import { INodeDescriptor } from '../nodes';
|
|
3
|
+
import { ExtensionTypes } from '../enum';
|
|
4
|
+
export interface ICreateExtensionParams {
|
|
5
|
+
nodes: INodeDescriptor[];
|
|
6
|
+
connections?: IConnectionSchema[];
|
|
7
|
+
title: string;
|
|
8
|
+
icon: string;
|
|
9
|
+
type: ExtensionTypes;
|
|
10
|
+
}
|
|
11
|
+
declare enum EventTypes {
|
|
12
|
+
EXECUTE = "execute",
|
|
13
|
+
GET_EXTENSION_INFO = "getExtensionInfo",
|
|
14
|
+
GET_FULL_NODE = "getFullNode"
|
|
15
|
+
}
|
|
16
|
+
interface IEvent {
|
|
17
|
+
method?: {
|
|
18
|
+
name: string;
|
|
19
|
+
uri: string;
|
|
20
|
+
databaseName: string;
|
|
21
|
+
collectionName: string;
|
|
22
|
+
query?: string;
|
|
23
|
+
};
|
|
24
|
+
writeContextUrl: string;
|
|
25
|
+
readContextUrls: {
|
|
26
|
+
[contextKey: string]: string;
|
|
27
|
+
};
|
|
28
|
+
type: EventTypes;
|
|
29
|
+
contextKey: string;
|
|
30
|
+
flowContextToken: string;
|
|
31
|
+
}
|
|
32
|
+
export declare const createExtension: (extension: ICreateExtensionParams) => (event: IEvent) => Promise<{
|
|
33
|
+
statusCode: number;
|
|
34
|
+
body: string;
|
|
35
|
+
}>;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createExtension = void 0;
|
|
4
|
+
const enum_1 = require("../fields/enum");
|
|
5
|
+
const context_1 = require("../context");
|
|
6
|
+
const error_1 = require("../error");
|
|
7
|
+
const logger_1 = require("../logger");
|
|
8
|
+
var EventTypes;
|
|
9
|
+
(function (EventTypes) {
|
|
10
|
+
EventTypes["EXECUTE"] = "execute";
|
|
11
|
+
EventTypes["GET_EXTENSION_INFO"] = "getExtensionInfo";
|
|
12
|
+
EventTypes["GET_FULL_NODE"] = "getFullNode";
|
|
13
|
+
})(EventTypes || (EventTypes = {}));
|
|
14
|
+
// @TODO: add dependencies between fields for smart rendering. Now on each field change we need to get node info;
|
|
15
|
+
// Disable node select fields without options.
|
|
16
|
+
const createExtension = (extension) => async (event) => {
|
|
17
|
+
try {
|
|
18
|
+
const EventCallbacks = {
|
|
19
|
+
[EventTypes.EXECUTE]: execute,
|
|
20
|
+
[EventTypes.GET_EXTENSION_INFO]: getExtensionInfo,
|
|
21
|
+
[EventTypes.GET_FULL_NODE]: getFullNode,
|
|
22
|
+
};
|
|
23
|
+
const eventCallback = EventCallbacks[event.type];
|
|
24
|
+
const result = await eventCallback(event, extension);
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
return {
|
|
29
|
+
statusCode: 500,
|
|
30
|
+
body: JSON.stringify({ message: `An error occurred ${e.message}` })
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
exports.createExtension = createExtension;
|
|
35
|
+
const execute = async (event, extension) => {
|
|
36
|
+
try {
|
|
37
|
+
const methodProps = event?.method;
|
|
38
|
+
const method = methodProps?.name ? extension.nodes.find(({ name }) => name === methodProps.name) : undefined;
|
|
39
|
+
const writeContextUrl = event?.writeContextUrl;
|
|
40
|
+
const readContextUrls = event?.readContextUrls;
|
|
41
|
+
logger_1.logger.info({
|
|
42
|
+
message: `Start extension execution, extension: ${extension.title}`,
|
|
43
|
+
location: 'execute method',
|
|
44
|
+
payload: {
|
|
45
|
+
method,
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
if (!method) {
|
|
49
|
+
throw new Error('Unknown extension method');
|
|
50
|
+
}
|
|
51
|
+
;
|
|
52
|
+
const contextWriteKey = event?.contextKey;
|
|
53
|
+
if (!contextWriteKey) {
|
|
54
|
+
throw new Error('Context key is not provided');
|
|
55
|
+
}
|
|
56
|
+
const contextToken = event?.flowContextToken;
|
|
57
|
+
if (!contextToken) {
|
|
58
|
+
throw new Error('Context token is not provided');
|
|
59
|
+
}
|
|
60
|
+
const context = await context_1.contextService.getContext({ methodProps, token: event.flowContextToken, urls: readContextUrls });
|
|
61
|
+
const response = await method.func(methodProps, context);
|
|
62
|
+
if (writeContextUrl && event.flowContextToken) {
|
|
63
|
+
await context_1.contextService.writeContext({ contextKey: contextWriteKey, data: response, token: event.flowContextToken, url: writeContextUrl });
|
|
64
|
+
}
|
|
65
|
+
logger_1.logger.info({
|
|
66
|
+
message: `Extension successfully executed, extension: ${extension.title}`,
|
|
67
|
+
location: 'execute method',
|
|
68
|
+
payload: {
|
|
69
|
+
method,
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
const logs = [...logger_1.logger.getLogs()];
|
|
73
|
+
logger_1.logger.clearLogs();
|
|
74
|
+
return { statusCode: 200, body: JSON.stringify({ logs }), };
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
logger_1.logger.error({
|
|
78
|
+
message: `Extension execution failed, extension: ${extension.title}`,
|
|
79
|
+
location: 'execute method',
|
|
80
|
+
payload: {
|
|
81
|
+
errorMessage: error.message,
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
// TODO: Use for better error handling.
|
|
85
|
+
const isCaughtError = error instanceof error_1.BaseError;
|
|
86
|
+
const statusCode = isCaughtError ? 400 : 500;
|
|
87
|
+
const message = isCaughtError ? error.message : `An execution error occurred`;
|
|
88
|
+
const logs = [...logger_1.logger.getLogs()];
|
|
89
|
+
logger_1.logger.clearLogs();
|
|
90
|
+
return {
|
|
91
|
+
statusCode,
|
|
92
|
+
body: JSON.stringify({ message, logs }),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const getExtensionInfo = async (event, extension) => {
|
|
97
|
+
return {
|
|
98
|
+
statusCode: 200,
|
|
99
|
+
body: JSON.stringify(extension),
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
const getFullNode = async (event, extension) => {
|
|
103
|
+
try {
|
|
104
|
+
const methodProps = event?.method;
|
|
105
|
+
const method = methodProps?.name ? extension.nodes.find(({ name }) => name === methodProps.name) : undefined;
|
|
106
|
+
if (!method) {
|
|
107
|
+
throw new error_1.BaseError('Unknown extension method');
|
|
108
|
+
}
|
|
109
|
+
const fullFields = await Promise.all(method.fields.map(async (field) => {
|
|
110
|
+
const isSelect = field.type === enum_1.FieldTypes.SELECT;
|
|
111
|
+
if (!isSelect)
|
|
112
|
+
return field;
|
|
113
|
+
const options = field?.getOptions ? await field.getOptions(methodProps) : [];
|
|
114
|
+
return { ...field, options };
|
|
115
|
+
}));
|
|
116
|
+
return {
|
|
117
|
+
statusCode: 200,
|
|
118
|
+
body: JSON.stringify({ ...method, fields: fullFields }),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const isCaughtError = error instanceof error_1.BaseError;
|
|
123
|
+
const statusCode = isCaughtError ? 400 : 500;
|
|
124
|
+
const message = isCaughtError ? error.message : 'An receiving node info error occurred';
|
|
125
|
+
return {
|
|
126
|
+
statusCode,
|
|
127
|
+
body: JSON.stringify({ message }),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExtensionTypes = void 0;
|
|
4
|
+
var ExtensionTypes;
|
|
5
|
+
(function (ExtensionTypes) {
|
|
6
|
+
ExtensionTypes["DB_CONNECTION"] = "db_connection";
|
|
7
|
+
ExtensionTypes["FILE"] = "file";
|
|
8
|
+
ExtensionTypes["CHOICE"] = "choice";
|
|
9
|
+
ExtensionTypes["PARALLEL"] = "parallel";
|
|
10
|
+
ExtensionTypes["CODE"] = "code";
|
|
11
|
+
})(ExtensionTypes || (exports.ExtensionTypes = ExtensionTypes = {}));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseError = void 0;
|
|
4
|
+
class BaseError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = this.constructor.name;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.BaseError = BaseError;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FieldTypes } from "../enum";
|
|
2
|
+
export interface ICheckboxField {
|
|
3
|
+
name: string;
|
|
4
|
+
label: string;
|
|
5
|
+
isRequired: boolean;
|
|
6
|
+
isChecked?: boolean;
|
|
7
|
+
type: FieldTypes.CHECKBOX;
|
|
8
|
+
dependsOn?: string[];
|
|
9
|
+
}
|
|
10
|
+
interface IProps {
|
|
11
|
+
name: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
isRequired: boolean;
|
|
14
|
+
isChecked: boolean;
|
|
15
|
+
dependsOn?: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare const createCheckboxField: (checkboxField: IProps) => ICheckboxField;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCheckboxField = void 0;
|
|
4
|
+
const enum_1 = require("../enum");
|
|
5
|
+
const createCheckboxField = (checkboxField) => {
|
|
6
|
+
const { name, label = '', isChecked = false, isRequired, dependsOn } = checkboxField;
|
|
7
|
+
return {
|
|
8
|
+
name,
|
|
9
|
+
label,
|
|
10
|
+
isRequired,
|
|
11
|
+
isChecked,
|
|
12
|
+
type: enum_1.FieldTypes.CHECKBOX,
|
|
13
|
+
dependsOn
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
exports.createCheckboxField = createCheckboxField;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FieldTypes } from "../enum";
|
|
2
|
+
export interface ICodeField {
|
|
3
|
+
label?: string;
|
|
4
|
+
name: string;
|
|
5
|
+
language: 'javascript' | 'python';
|
|
6
|
+
isRequired: boolean;
|
|
7
|
+
type: FieldTypes.CODE;
|
|
8
|
+
dependsOn?: string[];
|
|
9
|
+
}
|
|
10
|
+
interface IProps {
|
|
11
|
+
label?: string;
|
|
12
|
+
name: string;
|
|
13
|
+
language?: 'javascript';
|
|
14
|
+
isRequired: boolean;
|
|
15
|
+
dependsOn?: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare const createCodeField: (codeField: IProps) => ICodeField;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCodeField = void 0;
|
|
4
|
+
const enum_1 = require("../enum");
|
|
5
|
+
const createCodeField = (codeField) => {
|
|
6
|
+
const { name, label = '', language = 'javascript', isRequired, dependsOn, } = codeField;
|
|
7
|
+
return {
|
|
8
|
+
name,
|
|
9
|
+
label,
|
|
10
|
+
type: enum_1.FieldTypes.CODE,
|
|
11
|
+
language,
|
|
12
|
+
isRequired,
|
|
13
|
+
dependsOn,
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
exports.createCodeField = createCodeField;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FieldTypes = void 0;
|
|
4
|
+
var FieldTypes;
|
|
5
|
+
(function (FieldTypes) {
|
|
6
|
+
FieldTypes["CHECKBOX"] = "checkbox";
|
|
7
|
+
FieldTypes["CODE"] = "code";
|
|
8
|
+
FieldTypes["INPUT"] = "input";
|
|
9
|
+
FieldTypes["SELECT"] = "select";
|
|
10
|
+
})(FieldTypes || (exports.FieldTypes = FieldTypes = {}));
|
|
11
|
+
;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FieldTypes } from '../enum';
|
|
2
|
+
import { IBehavior } from '../select';
|
|
3
|
+
export interface IInputField {
|
|
4
|
+
name: string;
|
|
5
|
+
label: string;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
inputType?: 'text' | 'number' | 'password';
|
|
8
|
+
isRequired: boolean;
|
|
9
|
+
behavior?: IBehavior[];
|
|
10
|
+
type: FieldTypes.INPUT;
|
|
11
|
+
dependsOn?: string[];
|
|
12
|
+
}
|
|
13
|
+
interface IProps {
|
|
14
|
+
name: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
inputType?: 'text' | 'number' | 'password';
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
isRequired: boolean;
|
|
19
|
+
behavior?: IBehavior[];
|
|
20
|
+
dependsOn?: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare const createInputField: (inputField: IProps) => IInputField;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createInputField = void 0;
|
|
4
|
+
const enum_1 = require("../enum");
|
|
5
|
+
// const defaultValidation = {
|
|
6
|
+
// isRequired: true,
|
|
7
|
+
// type: 'string',
|
|
8
|
+
// };
|
|
9
|
+
const createInputField = (inputField) => {
|
|
10
|
+
const { name, label = '', placeholder = '', inputType = 'text', isRequired, dependsOn } = inputField; // validation = defaultValidation,
|
|
11
|
+
return {
|
|
12
|
+
type: enum_1.FieldTypes.INPUT,
|
|
13
|
+
name,
|
|
14
|
+
inputType,
|
|
15
|
+
label,
|
|
16
|
+
placeholder,
|
|
17
|
+
isRequired,
|
|
18
|
+
dependsOn,
|
|
19
|
+
// validation,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
exports.createInputField = createInputField;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { FieldTypes } from "../enum";
|
|
2
|
+
interface IOptions {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ISelectField {
|
|
7
|
+
type: FieldTypes.SELECT;
|
|
8
|
+
name: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
options?: {
|
|
12
|
+
label: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}[];
|
|
15
|
+
getOptions?: (...args: any) => Promise<IOptions[]> | IOptions[];
|
|
16
|
+
behavior?: IBehavior[];
|
|
17
|
+
isRequired: boolean;
|
|
18
|
+
dependsOn?: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface IBehavior {
|
|
21
|
+
action: 'disable';
|
|
22
|
+
condition: 'equals';
|
|
23
|
+
fieldName: string;
|
|
24
|
+
fieldValue: boolean | string | number | null | undefined;
|
|
25
|
+
}
|
|
26
|
+
interface IProps {
|
|
27
|
+
name: string;
|
|
28
|
+
label?: string;
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
options?: {
|
|
31
|
+
label: string;
|
|
32
|
+
value: string;
|
|
33
|
+
}[];
|
|
34
|
+
behavior?: IBehavior[];
|
|
35
|
+
getOptions?: () => Promise<IOptions[]> | IOptions[];
|
|
36
|
+
isRequired: boolean;
|
|
37
|
+
dependsOn?: string[];
|
|
38
|
+
}
|
|
39
|
+
export declare const createSelectField: (selectFieldData: IProps) => ISelectField;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSelectField = void 0;
|
|
4
|
+
const enum_1 = require("../enum");
|
|
5
|
+
const createSelectField = (selectFieldData) => {
|
|
6
|
+
const { name, label, placeholder, options, getOptions, isRequired, dependsOn } = selectFieldData;
|
|
7
|
+
return {
|
|
8
|
+
type: enum_1.FieldTypes.SELECT,
|
|
9
|
+
name,
|
|
10
|
+
label,
|
|
11
|
+
placeholder,
|
|
12
|
+
options,
|
|
13
|
+
getOptions,
|
|
14
|
+
isRequired,
|
|
15
|
+
dependsOn,
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
exports.createSelectField = createSelectField;
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./connection"), exports);
|
|
18
|
+
__exportStar(require("./createExtension"), exports);
|
|
19
|
+
__exportStar(require("./nodes"), exports);
|
|
20
|
+
__exportStar(require("./enum"), exports);
|
|
21
|
+
__exportStar(require("./fields/enum"), exports);
|
|
22
|
+
__exportStar(require("./error"), exports);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
declare enum LogLevels {
|
|
2
|
+
ERROR = "error",
|
|
3
|
+
INFO = "info"
|
|
4
|
+
}
|
|
5
|
+
interface ILogProps {
|
|
6
|
+
location: string;
|
|
7
|
+
message: string;
|
|
8
|
+
payload?: {
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
interface ILog extends ILogProps {
|
|
13
|
+
level: LogLevels;
|
|
14
|
+
timestamp: string;
|
|
15
|
+
}
|
|
16
|
+
declare class Logger {
|
|
17
|
+
private logs;
|
|
18
|
+
constructor();
|
|
19
|
+
info({ location, message, payload }: ILogProps): void;
|
|
20
|
+
error({ location, message, payload }: ILogProps): void;
|
|
21
|
+
getLogs(): ILog[];
|
|
22
|
+
clearLogs(): ILog[];
|
|
23
|
+
private getCurrentTimestamp;
|
|
24
|
+
}
|
|
25
|
+
export declare const logger: Logger;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logger = void 0;
|
|
4
|
+
const date_fns_1 = require("date-fns");
|
|
5
|
+
var LogLevels;
|
|
6
|
+
(function (LogLevels) {
|
|
7
|
+
LogLevels["ERROR"] = "error";
|
|
8
|
+
LogLevels["INFO"] = "info";
|
|
9
|
+
})(LogLevels || (LogLevels = {}));
|
|
10
|
+
class Logger {
|
|
11
|
+
logs;
|
|
12
|
+
constructor() {
|
|
13
|
+
this.logs = [];
|
|
14
|
+
}
|
|
15
|
+
info({ location, message, payload }) {
|
|
16
|
+
this.logs.push({ level: LogLevels.INFO, location, message, payload, timestamp: this.getCurrentTimestamp() });
|
|
17
|
+
}
|
|
18
|
+
error({ location, message, payload }) {
|
|
19
|
+
this.logs.push({ level: LogLevels.ERROR, location, message, payload, timestamp: this.getCurrentTimestamp() });
|
|
20
|
+
}
|
|
21
|
+
getLogs() {
|
|
22
|
+
return this.logs;
|
|
23
|
+
}
|
|
24
|
+
clearLogs() {
|
|
25
|
+
return this.logs = [];
|
|
26
|
+
}
|
|
27
|
+
getCurrentTimestamp() {
|
|
28
|
+
return (0, date_fns_1.format)(new Date(), "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.logger = new Logger();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ICheckboxField } from '../fields/checkbox';
|
|
2
|
+
import { IInputField } from '../fields/input';
|
|
3
|
+
import { ISelectField } from '../fields/select';
|
|
4
|
+
import { ICodeField } from '../fields/code';
|
|
5
|
+
type Field = IInputField | ISelectField | ICheckboxField | ICodeField;
|
|
6
|
+
export interface INodeDescriptor {
|
|
7
|
+
name: string;
|
|
8
|
+
title: string;
|
|
9
|
+
fields: Field[];
|
|
10
|
+
func: (args: any, context?: any) => any;
|
|
11
|
+
}
|
|
12
|
+
interface IProps {
|
|
13
|
+
name: string;
|
|
14
|
+
title: string;
|
|
15
|
+
fields: Field[];
|
|
16
|
+
func: (args: any, context?: any) => any;
|
|
17
|
+
}
|
|
18
|
+
export declare const createNodeDescriptor: (params: IProps) => INodeDescriptor;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createNodeDescriptor = void 0;
|
|
4
|
+
const createNodeDescriptor = (params) => {
|
|
5
|
+
const { name, fields, func, title } = params;
|
|
6
|
+
return {
|
|
7
|
+
name,
|
|
8
|
+
title,
|
|
9
|
+
fields: fields || [],
|
|
10
|
+
func: func || null,
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
exports.createNodeDescriptor = createNodeDescriptor;
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "creo-flow-extensions-base",
|
|
3
|
+
"version": "1.0.0-dev.1",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"repository": "git@github.com:CloudDataHub/cdh-flow-extension-base.git",
|
|
6
|
+
"author": "Ilia Kobaliia",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"clean": "rm -rf ./lib",
|
|
10
|
+
"build": "npm run clean && tsc",
|
|
11
|
+
"publishPackage": "npm run build && npm publish",
|
|
12
|
+
"publishPackageDev": "npm run build && npm publish --tag dev",
|
|
13
|
+
"publishPackageQa": "npm run build && npm publish --tag qa"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/jsonwebtoken": "^9.0.5",
|
|
17
|
+
"typescript": "^5.0.2"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib/**/*"
|
|
21
|
+
],
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"axios": "^1.6.2",
|
|
24
|
+
"date-fns": "^3.6.0",
|
|
25
|
+
"jsonwebtoken": "^9.0.2"
|
|
26
|
+
}
|
|
27
|
+
}
|