@coherentglobal/spark-execute-sdk 0.3.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/.babelrc +12 -0
- package/.eslintrc.json +21 -0
- package/.github/workflows/build-publish.yml +46 -0
- package/.prettierrc +0 -0
- package/README.md +92 -0
- package/dist/browser.js +6925 -0
- package/examples/UsingBearerTokenFNAsync/bearerTokenExample.js +22 -0
- package/examples/UsingBearerTokenFNAsync/config.js +88 -0
- package/examples/UsingBearerTokenFNAsync/index.html +205 -0
- package/examples/UsingBearerTokenFNAsync/modelCInput.json +15 -0
- package/examples/UsingBearerTokenFNAsync/models/Model_C.zip +0 -0
- package/examples/UsingBearerTokenString/bearerTokenExample.js +22 -0
- package/examples/UsingBearerTokenString/config.js +182 -0
- package/examples/UsingBearerTokenString/index.html +191 -0
- package/examples/UsingBearerTokenString/modelCInput.json +15 -0
- package/examples/UsingBearerTokenString/models/Model_C.zip +0 -0
- package/examples/UsingSyntheticKeyFNAsync/bearerTokenExample.js +22 -0
- package/examples/UsingSyntheticKeyFNAsync/config.js +195 -0
- package/examples/UsingSyntheticKeyFNAsync/index.html +205 -0
- package/examples/UsingSyntheticKeyFNAsync/modelCInput.json +15 -0
- package/examples/UsingSyntheticKeyFNAsync/models/Model_C.zip +0 -0
- package/examples/UsingSyntheticKeyString/bearerTokenExample.js +22 -0
- package/examples/UsingSyntheticKeyString/config.js +181 -0
- package/examples/UsingSyntheticKeyString/index.html +192 -0
- package/examples/UsingSyntheticKeyString/modelCInput.json +15 -0
- package/examples/UsingSyntheticKeyString/models/Model_C.zip +0 -0
- package/examples/asyncFunctionModel/config.js +96 -0
- package/examples/asyncFunctionModel/modelCInput.json +15 -0
- package/examples/asyncFunctionModel/models/Model_C.zip +0 -0
- package/examples/asyncFunctionModel/node.js +125 -0
- package/examples/asyncFunctionModel/tool.js +17 -0
- package/examples/base64Model/config.js +96 -0
- package/examples/base64Model/modelCInput.json +15 -0
- package/examples/base64Model/models/Model_C.zip +0 -0
- package/examples/base64Model/node.js +125 -0
- package/examples/base64Model/tool.js +17 -0
- package/examples/base64modelBrowser/config.js +96 -0
- package/examples/base64modelBrowser/index.html +194 -0
- package/examples/base64modelBrowser/modelCInput.json +15 -0
- package/examples/base64modelBrowser/models/Model_C.zip +0 -0
- package/examples/base64modelBrowser/models/base64model.txt +1 -0
- package/examples/base64modelBrowser/node.js +126 -0
- package/examples/base64modelBrowser/tool.js +17 -0
- package/examples/binary/config.js +96 -0
- package/examples/binary/modelCInput.json +15 -0
- package/examples/binary/models/Model_C.zip +0 -0
- package/examples/binary/models/binary.txt +1 -0
- package/examples/binary/node.js +131 -0
- package/examples/binary/tool.js +18 -0
- package/examples/functionModel/config.js +96 -0
- package/examples/functionModel/modelCInput.json +15 -0
- package/examples/functionModel/models/Model_C.zip +0 -0
- package/examples/functionModel/node.js +138 -0
- package/examples/functionModel/tool.js +17 -0
- package/examples/nodejs/node.js +250 -0
- package/examples/nodejs/test_models/Archive.zip +0 -0
- package/examples/nodejs/test_models/BlackScholes.zip +0 -0
- package/examples/nodejs/test_models/Model_A.zip +0 -0
- package/examples/nodejs/test_models/Par7.zip +0 -0
- package/package.json +79 -0
- package/src/browser.js +198 -0
- package/src/error.js +87 -0
- package/src/helpers/utils.js +79 -0
- package/src/logger.js +3 -0
- package/src/models.js +26 -0
- package/src/node.js +254 -0
- package/src/resolver/externalResolver.js +118 -0
- package/src/services/entityStore.js +260 -0
- package/src/services/resolver.js +134 -0
- package/src/services/spark.js +80 -0
- package/src/tenant_resolver/externalResolver_coherent.js +118 -0
- package/src/tenant_resolver/externalResolver_test.js +118 -0
- package/src/validate.js +38 -0
- package/test/config.test.js +124 -0
- package/test/mock-data/dummy-config.json +102 -0
- package/test/mock-data/valid.json +21 -0
- package/test/spark-sdk.test.js +28 -0
- package/tsconfig.json +105 -0
- package/types/browser.d.ts +72 -0
- package/types/browser.d.ts.map +1 -0
- package/types/error.d.ts +42 -0
- package/types/error.d.ts.map +1 -0
- package/types/helpers/utils.d.ts +11 -0
- package/types/helpers/utils.d.ts.map +1 -0
- package/types/logger.d.ts +3 -0
- package/types/logger.d.ts.map +1 -0
- package/types/models.d.ts +8 -0
- package/types/models.d.ts.map +1 -0
- package/types/node.d.ts +78 -0
- package/types/node.d.ts.map +1 -0
- package/types/resolver/externalResolver.d.ts +2 -0
- package/types/resolver/externalResolver.d.ts.map +1 -0
- package/types/services/entityStore.d.ts +51 -0
- package/types/services/entityStore.d.ts.map +1 -0
- package/types/services/resolver.d.ts +20 -0
- package/types/services/resolver.d.ts.map +1 -0
- package/types/services/spark.d.ts +6 -0
- package/types/services/spark.d.ts.map +1 -0
- package/types/tenant_resolver/externalResolver_coherent.d.ts +2 -0
- package/types/tenant_resolver/externalResolver_coherent.d.ts.map +1 -0
- package/types/tenant_resolver/externalResolver_test.d.ts +2 -0
- package/types/tenant_resolver/externalResolver_test.d.ts.map +1 -0
- package/types/validate.d.ts +4 -0
- package/types/validate.d.ts.map +1 -0
package/src/node.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
const utils = require("./helpers/utils.js");
|
|
2
|
+
const WasmRunnerErrors = require("./error.js");
|
|
3
|
+
const { WasmRunner } = require("@coherentglobal/wasm-runner");
|
|
4
|
+
const logger = require("./logger");
|
|
5
|
+
const validate = require("./validate.js");
|
|
6
|
+
const processModels = require("./models.js");
|
|
7
|
+
const got = require("got");
|
|
8
|
+
|
|
9
|
+
// const resolverDir = path.join(__dirname, "/tenant_resolver");
|
|
10
|
+
let registry = [];
|
|
11
|
+
let INVOCATION_CACHE = [];
|
|
12
|
+
class Spark {
|
|
13
|
+
/**
|
|
14
|
+
* @param {object} config
|
|
15
|
+
*/
|
|
16
|
+
constructor(config) {
|
|
17
|
+
/**
|
|
18
|
+
* @private
|
|
19
|
+
*/
|
|
20
|
+
this.config = this.validateConfig(config);
|
|
21
|
+
const {
|
|
22
|
+
sparkEndpoint: { syntheticKey, bearerToken, authType, tenant, url },
|
|
23
|
+
nodeGenModels,
|
|
24
|
+
} = this.config;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
this.tenant = tenant;
|
|
30
|
+
/**
|
|
31
|
+
* @private
|
|
32
|
+
*/
|
|
33
|
+
this.authType = authType;
|
|
34
|
+
/**
|
|
35
|
+
* @private
|
|
36
|
+
*/
|
|
37
|
+
this.token = syntheticKey || bearerToken;
|
|
38
|
+
/**
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
this.url = url;
|
|
42
|
+
/**
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
this.isCompatible = utils.isWasmSupported();
|
|
46
|
+
/**
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
this.model = this._models(nodeGenModels);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @private
|
|
53
|
+
*/
|
|
54
|
+
this.fallbackEnabled = false;
|
|
55
|
+
|
|
56
|
+
// (async () => {
|
|
57
|
+
// if (!(await utils.isPathExist(resolverDir))) {
|
|
58
|
+
// await fs.promises.mkdir(resolverDir, { mode: 0o750 });
|
|
59
|
+
// }
|
|
60
|
+
// })();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @param {object} nodegen
|
|
66
|
+
* @returns {object}
|
|
67
|
+
*/
|
|
68
|
+
_models(nodegen) {
|
|
69
|
+
const models = nodegen.map(processModels);
|
|
70
|
+
return models;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
*
|
|
75
|
+
* @param {object} config
|
|
76
|
+
* @returns {object}
|
|
77
|
+
*/
|
|
78
|
+
validateConfig(config) {
|
|
79
|
+
const { value, error } = validate(config);
|
|
80
|
+
if (!error) {
|
|
81
|
+
return value;
|
|
82
|
+
} else {
|
|
83
|
+
const message = JSON.stringify(error.details);
|
|
84
|
+
logger.error({
|
|
85
|
+
EventType: "ValidationError",
|
|
86
|
+
TextMessage: message,
|
|
87
|
+
});
|
|
88
|
+
throw new Error(`ValidationError: ${message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
*
|
|
94
|
+
* @param {string} id
|
|
95
|
+
* @returns {boolean}
|
|
96
|
+
*/
|
|
97
|
+
_getModel(id) {
|
|
98
|
+
const model = this.model.find((m) => m.versionId === id);
|
|
99
|
+
if (!model) throw new WasmRunnerErrors.MissingModelError(id);
|
|
100
|
+
|
|
101
|
+
return model;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
*
|
|
106
|
+
* @param {string} versionId
|
|
107
|
+
*/
|
|
108
|
+
async load(versionId) {
|
|
109
|
+
const { versionId: versionUuid, binary: modelPath } =
|
|
110
|
+
this._getModel(versionId);
|
|
111
|
+
|
|
112
|
+
logger.info({
|
|
113
|
+
EventType: "ModelInfo",
|
|
114
|
+
TextMessage: `Append model tenant: ${this.tenant} uuid: ${versionUuid}`,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// if (!registry[this.tenant]) {
|
|
118
|
+
if (!utils.isEmpty(registry)) {
|
|
119
|
+
// const resolverPath = path.join(
|
|
120
|
+
// resolverDir,
|
|
121
|
+
// `externalResolver_${this.tenant}.js`
|
|
122
|
+
// );
|
|
123
|
+
|
|
124
|
+
// const template = await fs.promises.readFile(
|
|
125
|
+
// require.resolve("./resolver/externalResolver"),
|
|
126
|
+
// {
|
|
127
|
+
// encoding: "utf8",
|
|
128
|
+
// flag: "r",
|
|
129
|
+
// }
|
|
130
|
+
// );
|
|
131
|
+
|
|
132
|
+
// await fs.promises.writeFile(
|
|
133
|
+
// resolverPath,
|
|
134
|
+
// template.replace("%%tenant_to_replace%%", this.tenant)
|
|
135
|
+
// );
|
|
136
|
+
|
|
137
|
+
registry = new WasmRunner();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
console.log("APPENDING");
|
|
141
|
+
await registry.append({
|
|
142
|
+
id: versionUuid,
|
|
143
|
+
url: modelPath,
|
|
144
|
+
// size,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
INVOCATION_CACHE = [
|
|
148
|
+
...INVOCATION_CACHE,
|
|
149
|
+
{ id: versionUuid },
|
|
150
|
+
// { id: versionUuid, tenant: this.tenant },
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Check for exiting Model
|
|
156
|
+
*
|
|
157
|
+
* @param {string} model
|
|
158
|
+
* @returns {boolean}
|
|
159
|
+
*/
|
|
160
|
+
isExist = (model) => {
|
|
161
|
+
return registry.isExist(model);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Execute model
|
|
166
|
+
*
|
|
167
|
+
* @param {object} input
|
|
168
|
+
* @param {string} version_id
|
|
169
|
+
* @returns {object}
|
|
170
|
+
*/
|
|
171
|
+
async execute(input, version_id) {
|
|
172
|
+
if (!input.request_meta.version_id && !input.request_meta.version_uuid) {
|
|
173
|
+
input.request_meta.version_id = version_id;
|
|
174
|
+
}
|
|
175
|
+
const versionID =
|
|
176
|
+
input.request_meta.version_id || input.request_meta.version_uuid;
|
|
177
|
+
|
|
178
|
+
const model = this._getModel(versionID);
|
|
179
|
+
|
|
180
|
+
await this.load(model.versionId);
|
|
181
|
+
|
|
182
|
+
logger.info({
|
|
183
|
+
EventType: "ExecuteModel",
|
|
184
|
+
TextMessage: `Execute uuid: ${model.versionId}`,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (this.isExist(model.versionId)) {
|
|
188
|
+
let modelUrl = "";
|
|
189
|
+
|
|
190
|
+
if (
|
|
191
|
+
!this.isCompatible &&
|
|
192
|
+
!utils.isEmpty(this.url) &&
|
|
193
|
+
this.fallbackEnabled
|
|
194
|
+
) {
|
|
195
|
+
let token = "";
|
|
196
|
+
if (this.token.constructor.name === "AsyncFunction") {
|
|
197
|
+
token = await this.token();
|
|
198
|
+
} else {
|
|
199
|
+
token = this.token;
|
|
200
|
+
}
|
|
201
|
+
console.log("TOKEN", token);
|
|
202
|
+
|
|
203
|
+
if (utils.isEmpty(this.token)) {
|
|
204
|
+
throw new WasmRunnerErrors.UnauthorizedError();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const options = {
|
|
208
|
+
headers: utils.getAuthHeaders(token, this.authType, this.tenant),
|
|
209
|
+
json: {
|
|
210
|
+
request_data: {},
|
|
211
|
+
request_meta: input.request_meta,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
console.log("OPTIONS", options);
|
|
216
|
+
modelUrl = new URL(
|
|
217
|
+
`/${this.tenant}/api/v3/version/${input.request_meta.version_id}`,
|
|
218
|
+
this.url
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
const response = await got.post(modelUrl, options).json();
|
|
223
|
+
|
|
224
|
+
return response;
|
|
225
|
+
} catch (err) {
|
|
226
|
+
throw new WasmRunnerErrors.UnauthorizedError();
|
|
227
|
+
}
|
|
228
|
+
} else if (this.isCompatible) {
|
|
229
|
+
const modelIndex = INVOCATION_CACHE.findIndex(
|
|
230
|
+
(item) => item.id === model.versionId
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
INVOCATION_CACHE[modelIndex].lastUsed = Date.now();
|
|
234
|
+
|
|
235
|
+
const result = await registry.execute(input, model.versionId, true);
|
|
236
|
+
|
|
237
|
+
return result;
|
|
238
|
+
} else {
|
|
239
|
+
throw new WasmRunnerErrors.NotSupportedError(
|
|
240
|
+
"WebAssembly is not supported"
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
throw new WasmRunnerErrors.BadRequestError("Missing request input.");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Remove Model
|
|
250
|
+
*/
|
|
251
|
+
// async remove(versionId) {}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = Spark;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const { StatusCodes } = require("http-status-codes");
|
|
2
|
+
const { logger } = require("@coherentglobal/node-sdk");
|
|
3
|
+
// const config = require("../config"); env vars
|
|
4
|
+
const {
|
|
5
|
+
handleDirectRequest,
|
|
6
|
+
modelServiceValidation,
|
|
7
|
+
getLatestVersionFromServiceMapTable,
|
|
8
|
+
} = require("../logic/runnerLogic");
|
|
9
|
+
const { getModelViaFolder } = require("../services/entityStore");
|
|
10
|
+
const tenant = "%%tenant_to_replace%%";
|
|
11
|
+
|
|
12
|
+
const CALL_TYPE = {
|
|
13
|
+
0: "SparkService",
|
|
14
|
+
1: "ExternalApi",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const ERROR_CODES = {
|
|
18
|
+
A1: {
|
|
19
|
+
code: "a1",
|
|
20
|
+
message: "ERROR:[Main item] missing in Xcall [NAME] table.",
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const XcallResBuilder = {
|
|
25
|
+
Json: function (callType, name, status, errorCode, responseTime, data) {
|
|
26
|
+
return JSON.stringify({
|
|
27
|
+
metadata: {
|
|
28
|
+
calltype: callType,
|
|
29
|
+
name: name,
|
|
30
|
+
status: status,
|
|
31
|
+
error_code: errorCode,
|
|
32
|
+
response_time: responseTime,
|
|
33
|
+
},
|
|
34
|
+
data: data || {},
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
XML: function (callType, name, status, errorCode, responseTime, data) {
|
|
38
|
+
const trimmedData = data.replace(/\<\?xml.+\?\>/gm, "");
|
|
39
|
+
|
|
40
|
+
const response = `
|
|
41
|
+
<data calltype="${callType}" name="${name}" status="${status}" error_code="${errorCode}" response_time = "${responseTime}">
|
|
42
|
+
${trimmedData}
|
|
43
|
+
</data>
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
return response;
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
sparkService: async (requestData, context) => {
|
|
52
|
+
const modelLocation = config().modelLocation;
|
|
53
|
+
const headers = context.headers;
|
|
54
|
+
const folderName = requestData.folder_name;
|
|
55
|
+
const serviceName = requestData.service_name;
|
|
56
|
+
|
|
57
|
+
logger.info({
|
|
58
|
+
eventType: "SparkService",
|
|
59
|
+
msg: `WASMSERVER:DISPATCH_TYPE.SPARK_SERVICE ${tenant}`,
|
|
60
|
+
});
|
|
61
|
+
logger.info({ eventType: "RequestData", msg: requestData });
|
|
62
|
+
const requestBodyFromEngine = JSON.parse(requestData.request_body);
|
|
63
|
+
const requestMetaFromEngine = requestBodyFromEngine.request_meta;
|
|
64
|
+
const findAvailableLatestModelFromLocal =
|
|
65
|
+
getLatestVersionFromServiceMapTable(
|
|
66
|
+
context.servicemap,
|
|
67
|
+
headers.tenant,
|
|
68
|
+
decodeURI(folderName),
|
|
69
|
+
decodeURI(serviceName)
|
|
70
|
+
);
|
|
71
|
+
requestMetaFromEngine.version_id = findAvailableLatestModelFromLocal;
|
|
72
|
+
const serviceMap = modelServiceValidation(
|
|
73
|
+
context.servicemap,
|
|
74
|
+
headers.tenant,
|
|
75
|
+
requestMetaFromEngine
|
|
76
|
+
);
|
|
77
|
+
// const modelVersion = modelLocation
|
|
78
|
+
// ? // ? await resolveOffline(requestMetaFromEngine, headers.tenant, headers, serviceMap)
|
|
79
|
+
// findAvailableLatestModelFromLocal
|
|
80
|
+
// : await getModelViaFolder(folderName, serviceName, headers);
|
|
81
|
+
|
|
82
|
+
const modelVersion = await getModelViaFolder(
|
|
83
|
+
folderName,
|
|
84
|
+
serviceName,
|
|
85
|
+
headers
|
|
86
|
+
);
|
|
87
|
+
const params = {
|
|
88
|
+
tenant: headers.tenant,
|
|
89
|
+
};
|
|
90
|
+
const body = {
|
|
91
|
+
...requestBodyFromEngine,
|
|
92
|
+
request_meta: {
|
|
93
|
+
...requestBodyFromEngine.request_meta,
|
|
94
|
+
version_id: modelVersion,
|
|
95
|
+
call_purpose: "Spark - API Tester",
|
|
96
|
+
source_system: "SPARK",
|
|
97
|
+
correlation_id: "",
|
|
98
|
+
requested_output: null,
|
|
99
|
+
service_category: "",
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
const payload = {
|
|
103
|
+
body,
|
|
104
|
+
headers,
|
|
105
|
+
params,
|
|
106
|
+
};
|
|
107
|
+
const results = await handleDirectRequest(payload);
|
|
108
|
+
const response = XcallResBuilder.Json(
|
|
109
|
+
CALL_TYPE[0],
|
|
110
|
+
serviceName,
|
|
111
|
+
StatusCodes.ACCEPTED,
|
|
112
|
+
"",
|
|
113
|
+
0,
|
|
114
|
+
results
|
|
115
|
+
);
|
|
116
|
+
return response;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
const tmp = require("tmp");
|
|
2
|
+
const cuid = require("cuid");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const got = require("got");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { logger } = require("@coherentglobal/node-sdk");
|
|
7
|
+
const { EntityStoreError } = require("../error");
|
|
8
|
+
const config = require("../config");
|
|
9
|
+
|
|
10
|
+
const request = got.extend({
|
|
11
|
+
throwHttpErrors: false, // allows us to throw our own error based off got response
|
|
12
|
+
handlers: [
|
|
13
|
+
(options, next) =>
|
|
14
|
+
(async () => {
|
|
15
|
+
const response = await next(options);
|
|
16
|
+
// return an http error of error response but with local stacktrace and not from got api response
|
|
17
|
+
if (response && response.statusCode >= 400 && response.body) {
|
|
18
|
+
let extendError;
|
|
19
|
+
try {
|
|
20
|
+
// eslint-disable-next-line prefer-destructuring
|
|
21
|
+
extendError = JSON.parse(response.body).errors[0];
|
|
22
|
+
} catch (err) {
|
|
23
|
+
// eslint-disable-next-line prefer-destructuring
|
|
24
|
+
extendError = response.body.errors[0];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (response.statusCode === 404) {
|
|
28
|
+
extendError = "MODEL_NOT_FOUND";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
logger.info({
|
|
32
|
+
EventType: "EntityStoreError",
|
|
33
|
+
TextMessage: `EntityStoreError: ${JSON.stringify(response.body)}`,
|
|
34
|
+
});
|
|
35
|
+
throw new EntityStoreError(extendError, response.statusCode);
|
|
36
|
+
}
|
|
37
|
+
return response;
|
|
38
|
+
})(),
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Fetch Temporary file.
|
|
44
|
+
*
|
|
45
|
+
* @param {String} url
|
|
46
|
+
* Temporary blob download url.
|
|
47
|
+
*
|
|
48
|
+
* @param {String} token
|
|
49
|
+
* Keycloak Token.
|
|
50
|
+
*
|
|
51
|
+
* @returns {Buffer}
|
|
52
|
+
*/
|
|
53
|
+
const getFromTempUrl = async (url, headers) => {
|
|
54
|
+
let options = {};
|
|
55
|
+
|
|
56
|
+
if (headers) {
|
|
57
|
+
options = {
|
|
58
|
+
headers,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const output = await request.get(url, options);
|
|
62
|
+
return output.rawBody;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Fetch documnet using Document Version Id.
|
|
67
|
+
*
|
|
68
|
+
* @param {String} versionUuid
|
|
69
|
+
* Document Version Id.
|
|
70
|
+
*
|
|
71
|
+
* @param {String} token
|
|
72
|
+
* Keycloak token.
|
|
73
|
+
*
|
|
74
|
+
* @returns
|
|
75
|
+
*/
|
|
76
|
+
const getDocumentByVersionId = async (versionUuid, token) => {
|
|
77
|
+
const { entityStoreUrl } = config();
|
|
78
|
+
const url = new URL(
|
|
79
|
+
`/docstore/api/v1/documents/versions/${versionUuid}`,
|
|
80
|
+
entityStoreUrl
|
|
81
|
+
).toString();
|
|
82
|
+
logger.info({
|
|
83
|
+
EventType: "GetVersionID",
|
|
84
|
+
TextMessage: `getDocumentByVersionId , ${url}`,
|
|
85
|
+
});
|
|
86
|
+
const options = {
|
|
87
|
+
headers: {
|
|
88
|
+
Authorization: token,
|
|
89
|
+
},
|
|
90
|
+
responseType: "json",
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const output = await request.get(url, options);
|
|
94
|
+
|
|
95
|
+
return output.body;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const getParameterSetById = async (versionUuid, token, tenant = "coherent") => {
|
|
99
|
+
if (!parameterSetCache[`${tenant}::${versionUuid}`]) {
|
|
100
|
+
const { sparkUrl } = config();
|
|
101
|
+
const url = new URL(
|
|
102
|
+
`/${tenant}/api/v3/parameter/versions/${versionUuid}`,
|
|
103
|
+
sparkUrl
|
|
104
|
+
).toString();
|
|
105
|
+
logger.info({
|
|
106
|
+
EventType: "ParameterSet",
|
|
107
|
+
TextMessage: `getParameterSetById , ${url}`,
|
|
108
|
+
});
|
|
109
|
+
const options = {
|
|
110
|
+
headers: {
|
|
111
|
+
Authorization: token,
|
|
112
|
+
},
|
|
113
|
+
responseType: "json",
|
|
114
|
+
};
|
|
115
|
+
const output = await request.get(url, options);
|
|
116
|
+
parameterSetCache[`${tenant}::${versionUuid}`] = output.body.response_data;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return parameterSetCache[`${tenant}::${versionUuid}`];
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const TMP_DIR = tmp.dirSync().name;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Save Temporary file.
|
|
126
|
+
*
|
|
127
|
+
* @param {String} versionUuid
|
|
128
|
+
* Document version ID.
|
|
129
|
+
*
|
|
130
|
+
* @param {Buffer} content
|
|
131
|
+
* Temporary file content.
|
|
132
|
+
*
|
|
133
|
+
* @param {String} token
|
|
134
|
+
* Keycloak token.
|
|
135
|
+
*
|
|
136
|
+
* @returns
|
|
137
|
+
*/
|
|
138
|
+
const downloadModelFile = async (url, versionUuid, headers) => {
|
|
139
|
+
const getWasmDocument = await getFromTempUrl(url, headers);
|
|
140
|
+
|
|
141
|
+
logger.info({
|
|
142
|
+
EventType: "DownloadModelFile",
|
|
143
|
+
TextMessage: `downloadModelFile url:${url}, versionUuid:${versionUuid}`,
|
|
144
|
+
});
|
|
145
|
+
const tmpWasm = tmp.fileSync({
|
|
146
|
+
dir: TMP_DIR,
|
|
147
|
+
name: `${versionUuid}-${Date.now()}.zip`,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const wasmDestination = fs.createWriteStream(tmpWasm.name.toString());
|
|
151
|
+
wasmDestination.write(getWasmDocument);
|
|
152
|
+
logger.info({
|
|
153
|
+
EventType: "DownloadComplete",
|
|
154
|
+
TextMessage: `downloadModelFile complete url:${url}, versionUuid:${versionUuid}`,
|
|
155
|
+
});
|
|
156
|
+
return tmpWasm.name;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const downloadJSON = async (url, versionUuid, headers) => {
|
|
160
|
+
try {
|
|
161
|
+
const getDocument = await getFromTempUrl(url, headers);
|
|
162
|
+
|
|
163
|
+
const filePath = path.join(TMP_DIR, `${versionUuid}.json`);
|
|
164
|
+
if (!fs.existsSync(filePath)) {
|
|
165
|
+
logger.info({
|
|
166
|
+
EventType: "DownloadJson",
|
|
167
|
+
TextMessage: `downloadJSON url:${url}, versionUuid:${versionUuid}`,
|
|
168
|
+
});
|
|
169
|
+
const tmpWasm = tmp.fileSync({
|
|
170
|
+
dir: TMP_DIR,
|
|
171
|
+
name: `${versionUuid}.json`,
|
|
172
|
+
});
|
|
173
|
+
const wasmDestination = fs.createWriteStream(tmpWasm.name.toString());
|
|
174
|
+
wasmDestination.write(getDocument);
|
|
175
|
+
logger.info({
|
|
176
|
+
EventType: "JsonDownloaded",
|
|
177
|
+
TextMessage: `downloadJSON complete url:${url}, versionUuid:${versionUuid}`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const jsonString = await fs.promises.readFile(filePath, "utf8");
|
|
182
|
+
|
|
183
|
+
return JSON.parse(jsonString);
|
|
184
|
+
} catch (err) {
|
|
185
|
+
throw new Error("File is not a JSON document");
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/* istanbul ignore next */
|
|
190
|
+
const downloadModel = async (versionUuid, token) => {
|
|
191
|
+
const { entityStoreUrl } = config();
|
|
192
|
+
const { url: documentURL } = await getDocumentByVersionId(versionUuid, token);
|
|
193
|
+
const downloadUrl = new URL(documentURL, entityStoreUrl).toString();
|
|
194
|
+
const modelFile = await downloadModelFile(downloadUrl, versionUuid);
|
|
195
|
+
logger.info({
|
|
196
|
+
EventType: "ModelDownloaded",
|
|
197
|
+
TextMessage: `downloadModel complete versionUuid:${versionUuid}`,
|
|
198
|
+
});
|
|
199
|
+
return modelFile;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/* istanbul ignore next */
|
|
203
|
+
const parameterSetCache = {};
|
|
204
|
+
|
|
205
|
+
/* istanbul ignore next */
|
|
206
|
+
const getParameterSet = async (versionUuid, tenant, token) => {
|
|
207
|
+
const { entityStoreUrl } = config();
|
|
208
|
+
const url = `https://excel.dev.coherent.global/coherent/api/v3/parameter/versions/${versionUuid}`;
|
|
209
|
+
if (!parameterSetCache[`${tenant}::${versionUuid}`]) {
|
|
210
|
+
const { url: documentURL } = await getDocumentByVersionId(
|
|
211
|
+
versionUuid,
|
|
212
|
+
token
|
|
213
|
+
);
|
|
214
|
+
const downloadUrl = new URL(documentURL, entityStoreUrl).toString();
|
|
215
|
+
const parameterSet = await downloadJSON(downloadUrl, versionUuid);
|
|
216
|
+
parameterSetCache[`${tenant}::${versionUuid}`] = parameterSet;
|
|
217
|
+
return parameterSet;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return parameterSetCache[`${tenant}::${versionUuid}`];
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
*
|
|
225
|
+
* @param {*} folder
|
|
226
|
+
* @param {*} model
|
|
227
|
+
*
|
|
228
|
+
* https://excel.dev.coherent.global/api/filemanager/GetFolderDocs?folderPath=/products/FJ%20Test/productfactory/engines/DownstreamService_example
|
|
229
|
+
*/
|
|
230
|
+
const getModelViaFolder = async (folder, model, headers) => {
|
|
231
|
+
const { sparkUrl } = config();
|
|
232
|
+
let options = {}
|
|
233
|
+
const folderPath = `/products/${folder}/productfactory/engines/${model}`
|
|
234
|
+
const folderUrl = `/api/filemanager/GetFolderDocs?folderPath=${folderPath}`
|
|
235
|
+
const folderSparkUrl = new URL(folderUrl, sparkUrl).toString();
|
|
236
|
+
if (headers) {
|
|
237
|
+
options = {
|
|
238
|
+
headers,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const output = await request.get(folderSparkUrl, options);
|
|
242
|
+
const body = output.body ? JSON.parse(output.body) : {}
|
|
243
|
+
let response = ""
|
|
244
|
+
if(body) {
|
|
245
|
+
const documents = body.data.documents
|
|
246
|
+
const findModelFromDocuments = documents.find( name => name.name === `${model}.c1engine`)
|
|
247
|
+
if(findModelFromDocuments) response = findModelFromDocuments.latestVersionId
|
|
248
|
+
}
|
|
249
|
+
return response
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
module.exports = {
|
|
253
|
+
getDocumentByVersionId,
|
|
254
|
+
getFromTempUrl,
|
|
255
|
+
downloadModelFile,
|
|
256
|
+
downloadModel,
|
|
257
|
+
getParameterSet,
|
|
258
|
+
getParameterSetById,
|
|
259
|
+
getModelViaFolder
|
|
260
|
+
};
|