@ductape/sdk 0.0.3-beta9 → 0.0.4-v11
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/dist/api/services/appApi.service.d.ts +3 -1
- package/dist/api/services/appApi.service.js +41 -2
- package/dist/api/services/appApi.service.js.map +1 -1
- package/dist/api/services/processorApi.service.d.ts +15 -1
- package/dist/api/services/processorApi.service.js +28 -0
- package/dist/api/services/processorApi.service.js.map +1 -1
- package/dist/api/services/productsApi.service.d.ts +7 -2
- package/dist/api/services/productsApi.service.js +23 -1
- package/dist/api/services/productsApi.service.js.map +1 -1
- package/dist/api/services/userApi.service.d.ts +2 -1
- package/dist/api/services/userApi.service.js +1 -1
- package/dist/api/services/userApi.service.js.map +1 -1
- package/dist/api/services/webhooksApi.service.d.ts +4 -1
- package/dist/api/services/webhooksApi.service.js +6 -3
- package/dist/api/services/webhooksApi.service.js.map +1 -1
- package/dist/api/services/workspaceApi.service.d.ts +2 -1
- package/dist/api/services/workspaceApi.service.js +1 -1
- package/dist/api/services/workspaceApi.service.js.map +1 -1
- package/dist/api/urls.d.ts +5 -0
- package/dist/api/urls.js +6 -1
- package/dist/api/urls.js.map +1 -1
- package/dist/api/utils/cache.utils.d.ts +3 -0
- package/dist/api/utils/cache.utils.js +18 -0
- package/dist/api/utils/cache.utils.js.map +1 -0
- package/dist/apps/services/app.service.d.ts +3 -3
- package/dist/apps/services/app.service.js +153 -75
- package/dist/apps/services/app.service.js.map +1 -1
- package/dist/apps/utils/objects.utils.d.ts +1 -1
- package/dist/apps/utils/objects.utils.js +5 -3
- package/dist/apps/utils/objects.utils.js.map +1 -1
- package/dist/apps/validators/joi-validators/create.appWebhook.validator.js +1 -1
- package/dist/apps/validators/joi-validators/create.appWebhook.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/create.appWebhookEvent.validator.js +1 -0
- package/dist/apps/validators/joi-validators/create.appWebhookEvent.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/update.appAction.validator.d.ts +2 -2
- package/dist/apps/validators/joi-validators/update.appAction.validator.js +11 -11
- package/dist/apps/validators/joi-validators/update.appAction.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/update.appWebhookEvent.validator.js +2 -0
- package/dist/apps/validators/joi-validators/update.appWebhookEvent.validator.js.map +1 -1
- package/dist/clients/apps.client.js +1 -1
- package/dist/clients/apps.client.js.map +1 -1
- package/dist/clients/webhooks.client.d.ts +2 -1
- package/dist/clients/webhooks.client.js +2 -2
- package/dist/clients/webhooks.client.js.map +1 -1
- package/dist/imports/imports.service.d.ts +2 -3
- package/dist/imports/imports.service.js +4 -45
- package/dist/imports/imports.service.js.map +1 -1
- package/dist/imports/imports.types.d.ts +238 -0
- package/dist/imports/repos/openApi.repo.d.ts +4 -11
- package/dist/imports/repos/openApi.repo.js +50 -28
- package/dist/imports/repos/openApi.repo.js.map +1 -1
- package/dist/imports/repos/postmanV21.repo.js +1 -1
- package/dist/imports/repos/postmanV21.repo.js.map +1 -1
- package/dist/index.d.ts +33 -6
- package/dist/index.js +121 -11
- package/dist/index.js.map +1 -1
- package/dist/inputs/inputs.service.js +2 -2
- package/dist/inputs/inputs.service.js.map +1 -1
- package/dist/inputs/validators/inputs.validator.parse.js +1 -0
- package/dist/inputs/validators/inputs.validator.parse.js.map +1 -1
- package/dist/logs/logs.service.d.ts +1 -1
- package/dist/logs/logs.service.js +1 -0
- package/dist/logs/logs.service.js.map +1 -1
- package/dist/logs/logs.types.d.ts +4 -0
- package/dist/logs/logs.types.js.map +1 -1
- package/dist/processor/repos/sms.repo.d.ts +4 -4
- package/dist/processor/repos/sms.repo.js +23 -10
- package/dist/processor/repos/sms.repo.js.map +1 -1
- package/dist/processor/services/fallback.service.d.ts +0 -1
- package/dist/processor/services/fallback.service.js +0 -16
- package/dist/processor/services/fallback.service.js.map +1 -1
- package/dist/processor/services/processor.service.d.ts +61 -18
- package/dist/processor/services/processor.service.js +714 -316
- package/dist/processor/services/processor.service.js.map +1 -1
- package/dist/processor/services/quota.service.d.ts +6 -13
- package/dist/processor/services/quota.service.js +12 -3
- package/dist/processor/services/quota.service.js.map +1 -1
- package/dist/processor/utils/processor.utils.d.ts +6 -230
- package/dist/processor/utils/processor.utils.js +212 -122
- package/dist/processor/utils/processor.utils.js.map +1 -1
- package/dist/processor/utils/storage.util.js +54 -40
- package/dist/processor/utils/storage.util.js.map +1 -1
- package/dist/products/services/products.service.d.ts +26 -7
- package/dist/products/services/products.service.js +427 -147
- package/dist/products/services/products.service.js.map +1 -1
- package/dist/products/utils/functions.utils.d.ts +1 -0
- package/dist/products/utils/functions.utils.js +11 -0
- package/dist/products/utils/functions.utils.js.map +1 -1
- package/dist/products/validators/index.d.ts +3 -1
- package/dist/products/validators/index.js +5 -1
- package/dist/products/validators/index.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productFallback.validator.js +7 -1
- package/dist/products/validators/joi-validators/create.productFallback.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productFeature.validator.js +2 -0
- package/dist/products/validators/joi-validators/create.productFeature.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productQuota.validator.js +10 -2
- package/dist/products/validators/joi-validators/create.productQuota.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.userAuth.validator.d.ts +3 -0
- package/dist/products/validators/joi-validators/create.userAuth.validator.js +48 -0
- package/dist/products/validators/joi-validators/create.userAuth.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/update.dataValue.validator.d.ts +1 -1
- package/dist/products/validators/joi-validators/update.dataValue.validator.js +3 -2
- package/dist/products/validators/joi-validators/update.dataValue.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.productFallback.validator.js +7 -2
- package/dist/products/validators/joi-validators/update.productFallback.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.productFeature.validator.js +2 -0
- package/dist/products/validators/joi-validators/update.productFeature.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.productQuota.validator.js +12 -7
- package/dist/products/validators/joi-validators/update.productQuota.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.userAuth.validator.d.ts +3 -0
- package/dist/products/validators/joi-validators/update.userAuth.validator.js +48 -0
- package/dist/products/validators/joi-validators/update.userAuth.validator.js.map +1 -0
- package/dist/test/test.import.openapi.d.ts +1 -0
- package/dist/test/test.import.openapi.js +76 -0
- package/dist/test/test.import.openapi.js.map +1 -0
- package/dist/test/test.logs.js +1 -2
- package/dist/test/test.logs.js.map +1 -1
- package/dist/types/appBuilder.types.d.ts +20 -12
- package/dist/types/enums.d.ts +6 -0
- package/dist/types/enums.js +8 -1
- package/dist/types/enums.js.map +1 -1
- package/dist/types/index.types.d.ts +5 -0
- package/dist/types/index.types.js.map +1 -1
- package/dist/types/processor.types.d.ts +89 -4
- package/dist/types/processor.types.js.map +1 -1
- package/dist/types/productsBuilder.types.d.ts +51 -8
- package/dist/types/productsBuilder.types.js +3 -0
- package/dist/types/productsBuilder.types.js.map +1 -1
- package/package.json +11 -2
|
@@ -40,16 +40,18 @@ const update_productNotificationMessage_validator_1 = __importDefault(require(".
|
|
|
40
40
|
const create_productNotification_validator_1 = require("../validators/joi-validators/create.productNotification.validator");
|
|
41
41
|
const functions_utils_1 = require("../utils/functions.utils");
|
|
42
42
|
const objects_utils_2 = require("../../apps/utils/objects.utils");
|
|
43
|
+
const webhooksApi_service_1 = require("../../api/services/webhooksApi.service");
|
|
43
44
|
class ProductsBuilderService {
|
|
44
|
-
constructor({ workspace_id, public_key, user_id, token, env_type }) {
|
|
45
|
+
constructor({ workspace_id, public_key, user_id, token, env_type, redis_client }) {
|
|
45
46
|
this.workspace_id = workspace_id;
|
|
46
47
|
this.public_key = public_key;
|
|
47
48
|
this.user_id = user_id;
|
|
48
49
|
this.token = token;
|
|
49
|
-
this.userApi = new userApi_service_1.UserApiService(env_type);
|
|
50
|
-
this.productApi = new productsApi_service_1.ProductsApiService(env_type);
|
|
51
|
-
this.workspaceApi = new workspaceApi_service_1.WorkspaceApiService(env_type);
|
|
52
|
-
this.
|
|
50
|
+
this.userApi = new userApi_service_1.UserApiService(env_type, redis_client);
|
|
51
|
+
this.productApi = new productsApi_service_1.ProductsApiService(env_type, redis_client);
|
|
52
|
+
this.workspaceApi = new workspaceApi_service_1.WorkspaceApiService(env_type, redis_client);
|
|
53
|
+
this.webhooksApi = new webhooksApi_service_1.WebhooksApiService(env_type);
|
|
54
|
+
this.appApi = new appApi_service_1.AppApiService(env_type, redis_client);
|
|
53
55
|
this.inputsService = new inputs_service_1.default();
|
|
54
56
|
this.thirdPartyApps = [];
|
|
55
57
|
}
|
|
@@ -190,6 +192,105 @@ class ProductsBuilderService {
|
|
|
190
192
|
}
|
|
191
193
|
return this.product;
|
|
192
194
|
}
|
|
195
|
+
async createSession(data, throwErrorIfExists = false) {
|
|
196
|
+
try {
|
|
197
|
+
await validators_1.CreateProductSessionSchema.validateAsync(data);
|
|
198
|
+
if (!data.tag) {
|
|
199
|
+
throw new Error('tag field is required');
|
|
200
|
+
}
|
|
201
|
+
const exists = this.fetchSession(data.tag);
|
|
202
|
+
if (!exists) {
|
|
203
|
+
if (!data.selector.startsWith('$Session{')) {
|
|
204
|
+
throw new Error('Selector should be in the format $Session{...}{key}');
|
|
205
|
+
}
|
|
206
|
+
const stages = (0, string_utils_1.extractStages)(data.selector);
|
|
207
|
+
let current = data.schema;
|
|
208
|
+
for (const stage of stages) {
|
|
209
|
+
if (current && typeof current === 'object' && stage in current) {
|
|
210
|
+
current = current[stage];
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
throw new Error(`${data.selector} not found in event sample`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (current === null ||
|
|
217
|
+
typeof current === "undefined" ||
|
|
218
|
+
typeof current === "object") {
|
|
219
|
+
throw new Error("Selector value is not allowed to be an object|array|null|undefined");
|
|
220
|
+
}
|
|
221
|
+
data.schema_data = await this.inputsService.parseJson({
|
|
222
|
+
data: data.schema,
|
|
223
|
+
expected: inputs_types_1.ExpectedValues.PARSESAMPLE
|
|
224
|
+
});
|
|
225
|
+
data.selectorValue = current;
|
|
226
|
+
const payload = Object.assign(Object.assign({}, data), { component: enums_1.ProductComponents.SESSION, action: enums_1.RequestAction.CREATE });
|
|
227
|
+
await this.productApi.updateProduct(this.product_id, payload, this.getUserAccess());
|
|
228
|
+
await this.initializeProduct(this.product_id);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
if (throwErrorIfExists)
|
|
232
|
+
throw new Error(`Session ${data.tag} already exists`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
throw e;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async updateSession(tag, data) {
|
|
240
|
+
try {
|
|
241
|
+
const session = this.fetchSession(tag);
|
|
242
|
+
if (!session) {
|
|
243
|
+
throw new Error(`Session with tag: ${tag} not found`);
|
|
244
|
+
}
|
|
245
|
+
//const { _id } = auth;
|
|
246
|
+
await validators_1.UpdateProductSessionSchema.validateAsync(data); // Change to update;
|
|
247
|
+
if (data.tag && this.fetchSession(data.tag) && data.tag !== tag) {
|
|
248
|
+
throw new Error(`tag ${data.tag} is in use`); // TODO: also check on the backend
|
|
249
|
+
}
|
|
250
|
+
if (!data.tag) {
|
|
251
|
+
data.tag = tag;
|
|
252
|
+
}
|
|
253
|
+
if (data.schema) {
|
|
254
|
+
data.schema_data = (await this.inputsService.parseJson({
|
|
255
|
+
data: data.schema,
|
|
256
|
+
expected: inputs_types_1.ExpectedValues.PARSESAMPLE,
|
|
257
|
+
category: enums_1.Categories.DATA,
|
|
258
|
+
}));
|
|
259
|
+
if (!data.selector) {
|
|
260
|
+
throw new Error('Selector is expected when updating schema');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (data.selector) {
|
|
264
|
+
const stages = (0, string_utils_1.extractStages)(data.selector);
|
|
265
|
+
let current = data.schema;
|
|
266
|
+
for (const stage of stages) {
|
|
267
|
+
if (current && typeof current === 'object' && stage in current) {
|
|
268
|
+
current = current[stage];
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
throw new Error(`${data.selector} not found in event sample`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (current === null ||
|
|
275
|
+
typeof current === "undefined" ||
|
|
276
|
+
typeof current === "object") {
|
|
277
|
+
throw new Error("Selector value is not allowed to be an object|array|null|undefined");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign({}, Object.assign(Object.assign({}, session), data)), { component: enums_1.ProductComponents.SESSION, action: enums_1.RequestAction.UPDATE }), this.getUserAccess());
|
|
281
|
+
await this.initializeProduct(this.product_id);
|
|
282
|
+
}
|
|
283
|
+
catch (e) {
|
|
284
|
+
throw e;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
fetchSession(tag) {
|
|
288
|
+
var _a;
|
|
289
|
+
return (_a = this.product.sessions) === null || _a === void 0 ? void 0 : _a.find((auth) => auth.tag === tag);
|
|
290
|
+
}
|
|
291
|
+
fetchSessions() {
|
|
292
|
+
return this.product.sessions;
|
|
293
|
+
}
|
|
193
294
|
async createMessageBrokerTopic(data, throwErrorIfExists = false) {
|
|
194
295
|
try {
|
|
195
296
|
if (!data.tag) {
|
|
@@ -277,15 +378,15 @@ class ProductsBuilderService {
|
|
|
277
378
|
throw new Error(`Message Broker ${messageBrokerTag} not found`);
|
|
278
379
|
return messageBroker.topics;
|
|
279
380
|
}
|
|
280
|
-
async validateQuotaFallbackInputAndOutput(data) {
|
|
381
|
+
async validateQuotaFallbackInputAndOutput(data, type) {
|
|
281
382
|
const input = data.input;
|
|
282
383
|
await Promise.all(data.options.map(async (d, index) => {
|
|
283
|
-
await this.validateQuotaFallbackInput(d, input);
|
|
384
|
+
await this.validateQuotaFallbackInput(d, input, type);
|
|
284
385
|
const pastOutput = index > 0 ? data.options[index - 1].output : null;
|
|
285
|
-
await this.validateQuotaFallbackOutput(d, pastOutput, input);
|
|
386
|
+
await this.validateQuotaFallbackOutput(d, pastOutput, input, type);
|
|
286
387
|
}));
|
|
287
388
|
}
|
|
288
|
-
async validateQuotaFallbackInput(option, input) {
|
|
389
|
+
async validateQuotaFallbackInput(option, input, type) {
|
|
289
390
|
if (option.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
|
|
290
391
|
const _a = await this.fetchThirdPartyAppByAccessTag(option.app), { version } = _a, appData = __rest(_a, ["version"]);
|
|
291
392
|
if (!appData) {
|
|
@@ -297,55 +398,170 @@ class ProductsBuilderService {
|
|
|
297
398
|
if (!appData) {
|
|
298
399
|
throw new Error(`Action ${option.event} not found in ${option.app} version ${version}`);
|
|
299
400
|
}
|
|
300
|
-
await this.validateActionDataInput({ input }, action, option.input, 0, 0);
|
|
401
|
+
//await this.validateActionDataInput({ input }, action, option.input, 0, 0);
|
|
402
|
+
// const keyValues = traverseObject(input.option);
|
|
403
|
+
//query
|
|
404
|
+
this.checkActionQuotaFallbackInput(action.query, option.input.query, input, 'query');
|
|
405
|
+
//headers
|
|
406
|
+
this.checkActionQuotaFallbackInput(action.headers, option.input.headers, input, 'headers');
|
|
407
|
+
// params
|
|
408
|
+
this.checkActionQuotaFallbackInput(action.params, option.input.params, input, 'params');
|
|
409
|
+
//body
|
|
410
|
+
this.checkActionQuotaFallbackInput(action.body, option.input.body, input, 'body');
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
const feature = this.fetchFeature(option.event);
|
|
414
|
+
if (!feature) {
|
|
415
|
+
throw new Error(`Feature ${option.event} not found`);
|
|
416
|
+
}
|
|
417
|
+
const map = (0, functions_utils_1.convertInputToValueMap)(feature.input);
|
|
418
|
+
(0, objects_utils_2.validateObjectKeys)(map, option.input, '', 'option input', 'feature input definition');
|
|
419
|
+
}
|
|
420
|
+
const keyValues = (0, objects_utils_1.traverseObject)(option.input);
|
|
421
|
+
this.checkKeyValuesInput(keyValues, input, type);
|
|
422
|
+
this.isQuotaFallbackInput = false;
|
|
423
|
+
}
|
|
424
|
+
checkKeyValuesInput(keyValues, input, type) {
|
|
425
|
+
for (let keyValue of keyValues) {
|
|
426
|
+
const { key, value } = keyValue;
|
|
427
|
+
if (!String(value).startsWith('$')) {
|
|
428
|
+
}
|
|
429
|
+
else if (String(value).startsWith('$Auth{')) {
|
|
430
|
+
// TODO: validate auth input later
|
|
431
|
+
}
|
|
432
|
+
else if (String(value).startsWith('$') && !String(value).startsWith('$Input{')) {
|
|
433
|
+
this.checkFuncArgsForInputs(String(value).trim(), input, type);
|
|
434
|
+
}
|
|
435
|
+
else if (!String(value).startsWith('$Input{')) {
|
|
436
|
+
throw new Error(`Feature input ${value} is invalid, all feature Inputs are expected to start with $Input{}`);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
this.checkInput(String(value).trim(), input, type);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
checkActionQuotaFallbackInput(sample, obj, featureInput, type) {
|
|
444
|
+
const data = (0, objects_utils_1.traverseObject)(obj);
|
|
445
|
+
//compare obj keys to sample keys
|
|
446
|
+
(0, objects_utils_2.validateObjectKeys)(sample.sample ? JSON.parse(String(sample.sample)) : {}, obj, '', `action ${type} input`, `action ${type} input sample`);
|
|
447
|
+
// check if values in data can be found inside featureInput
|
|
448
|
+
this.checkKeyValuesInput(data, featureInput, type);
|
|
449
|
+
}
|
|
450
|
+
checkFuncArgsForInputs(value, input, type) {
|
|
451
|
+
const isFunc = (0, functions_utils_1.startsWithFunctionAndEndsWithParen)(value);
|
|
452
|
+
if (!isFunc) {
|
|
453
|
+
throw new Error(`Invalid function call ${value}`);
|
|
454
|
+
}
|
|
455
|
+
const funcArgs = (0, functions_utils_1.extractFunctionAndArgs)(value);
|
|
456
|
+
if (funcArgs && funcArgs.args.length) {
|
|
457
|
+
funcArgs.args.map((arg) => {
|
|
458
|
+
const args = arg.trim().split(',');
|
|
459
|
+
args.map((argz) => {
|
|
460
|
+
const arg = argz.trim();
|
|
461
|
+
if (arg.startsWith('$') && !arg.startsWith('$Input{')) {
|
|
462
|
+
this.checkFuncArgsForInputs(arg, input, type);
|
|
463
|
+
}
|
|
464
|
+
else if (arg.startsWith('$Input{')) {
|
|
465
|
+
this.checkInput(arg, input, type);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
checkInput(value, input, type) {
|
|
472
|
+
const stages = this.extractStages(value);
|
|
473
|
+
if (stages.length > 1) {
|
|
474
|
+
throw new Error(`Invalid input ${value}, nested locators not allowed in $Input`);
|
|
475
|
+
}
|
|
476
|
+
if (!input[stages[0]]) {
|
|
477
|
+
throw new Error(`${value} not found in ${type} input definition`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
async validateQuotaFallbackOutput(option, pastResponse, input, type) {
|
|
481
|
+
if (pastResponse) {
|
|
482
|
+
(0, objects_utils_2.validateObjectKeys)(option.output, pastResponse, `previous outputs`, `${option.event} ${type} output`);
|
|
483
|
+
}
|
|
484
|
+
if (option.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
|
|
485
|
+
const _a = await this.fetchThirdPartyAppByAccessTag(option.app), { version } = _a, appData = __rest(_a, ["version"]);
|
|
486
|
+
if (!appData) {
|
|
487
|
+
throw new Error(`App ${option.app} not found`);
|
|
488
|
+
}
|
|
489
|
+
const { actions } = appData.versions.find((data) => data.tag === version);
|
|
490
|
+
const action = actions.find((data) => data.tag === option.event);
|
|
491
|
+
if (!appData) {
|
|
492
|
+
throw new Error(`Action ${event} not found in ${option.app} version ${version}`);
|
|
493
|
+
}
|
|
494
|
+
const response = action.responses.find((item) => item.success === true);
|
|
495
|
+
// fetch success result
|
|
496
|
+
if (!response) {
|
|
497
|
+
throw new Error(`event ${event} does not have a success response`);
|
|
498
|
+
}
|
|
499
|
+
// validate that each output item starts with response or Input and then validate the stages are valid;
|
|
301
500
|
}
|
|
302
501
|
else {
|
|
502
|
+
// fetch feature
|
|
303
503
|
const feature = this.fetchFeature(option.event);
|
|
304
504
|
if (!feature) {
|
|
305
505
|
throw new Error(`Feature ${option.event} not found`);
|
|
306
506
|
}
|
|
307
|
-
|
|
308
|
-
const keyValues = (0, objects_utils_1.traverseObject)(option.
|
|
507
|
+
const { output } = feature;
|
|
508
|
+
const keyValues = (0, objects_utils_1.traverseObject)(option.output);
|
|
309
509
|
for (let keyValue of keyValues) {
|
|
310
510
|
const { key, value } = keyValue;
|
|
311
|
-
if (!value.startsWith('$
|
|
312
|
-
|
|
511
|
+
if (!value.startsWith('$')) {
|
|
512
|
+
}
|
|
513
|
+
else if (value.startsWith('$') && !value.startsWith('$Response{') && !value.startsWith('$Input')) {
|
|
514
|
+
this.checkFuncArgsForResponse(value.trim(), output, input, type);
|
|
515
|
+
}
|
|
516
|
+
else if (!value.startsWith('$Response{') && !value.startsWith('$Input{')) {
|
|
517
|
+
throw new Error(`Feature output ${value} is invalid, all feature outputs for ${type} are expected to start with $Response or $Input{}`);
|
|
518
|
+
}
|
|
519
|
+
else if (value.startsWith('$Response')) {
|
|
520
|
+
this.checkResponse(value.trim(), output, input, type);
|
|
521
|
+
}
|
|
522
|
+
else if (value.startsWith('$Input')) {
|
|
523
|
+
this.checkInput(value, input, type);
|
|
313
524
|
}
|
|
314
525
|
}
|
|
315
526
|
}
|
|
316
|
-
|
|
527
|
+
// validate that each response matches the expected format
|
|
528
|
+
/**await this.validateActionResponse(response, res)*/
|
|
317
529
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
input: Record<string, IFeatureInput>,*/
|
|
323
|
-
option, pastResponse, input) {
|
|
324
|
-
/*const { version, ...appData } = await this.fetchThirdPartyAppByAccessTag(option.app);
|
|
325
|
-
|
|
326
|
-
if (!appData) {
|
|
327
|
-
throw new Error(`App ${option.app} not found`);
|
|
530
|
+
checkFuncArgsForResponse(value, output, input, type) {
|
|
531
|
+
const isFunc = (0, functions_utils_1.startsWithFunctionAndEndsWithParen)(value);
|
|
532
|
+
if (!isFunc) {
|
|
533
|
+
throw new Error(`Invalid function call ${value}`);
|
|
328
534
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
535
|
+
const funcArgs = (0, functions_utils_1.extractFunctionAndArgs)(value);
|
|
536
|
+
if (funcArgs && funcArgs.args.length) {
|
|
537
|
+
funcArgs.args.map((arg) => {
|
|
538
|
+
const args = arg.trim().split(',');
|
|
539
|
+
args.map((argz) => {
|
|
540
|
+
const arg = argz.trim();
|
|
541
|
+
if (arg.startsWith('$') && !arg.startsWith('$Response{') && !arg.startsWith('$Input{')) {
|
|
542
|
+
this.checkFuncArgsForResponse(arg, output, input, type);
|
|
543
|
+
}
|
|
544
|
+
else if (arg.startsWith('$Response{')) {
|
|
545
|
+
this.checkResponse(arg, output, input, type);
|
|
546
|
+
}
|
|
547
|
+
else if (arg.startsWith('$Input{')) {
|
|
548
|
+
this.checkInput(arg, input, type);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
});
|
|
339
552
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
553
|
+
}
|
|
554
|
+
checkResponse(value, output, input, type) {
|
|
555
|
+
const stages = this.extractStages(value);
|
|
556
|
+
let current = output;
|
|
557
|
+
for (const stage of stages) {
|
|
558
|
+
if (current && typeof current === 'object' && stage in current) {
|
|
559
|
+
current = current[stage];
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
throw new Error(`${value} not found in ${type} response definition`);
|
|
563
|
+
}
|
|
346
564
|
}
|
|
347
|
-
|
|
348
|
-
await this.validateActionResponse(response, res)*/
|
|
349
565
|
}
|
|
350
566
|
async createQuota(data) {
|
|
351
567
|
try {
|
|
@@ -354,27 +570,32 @@ class ProductsBuilderService {
|
|
|
354
570
|
}
|
|
355
571
|
if (!this.fetchQuota(data.tag)) {
|
|
356
572
|
await validators_1.CreateProductQuotaSchema.validateAsync(data);
|
|
357
|
-
await this.validateQuotaFallbackInputAndOutput(data);
|
|
573
|
+
await this.validateQuotaFallbackInputAndOutput(data, 'quota');
|
|
358
574
|
data.total_quota = 0;
|
|
359
575
|
data.total_init = 0;
|
|
360
576
|
await Promise.all(data.options.map(async (d) => {
|
|
361
577
|
data.total_quota += d.quota;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const version = app.versions.find((version) => version.tag === app.version);
|
|
367
|
-
const action = version.actions.find((action) => action.tag === d.event);
|
|
368
|
-
if (!action) {
|
|
369
|
-
throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
|
|
370
|
-
}
|
|
371
|
-
if (d.healthcheck) {
|
|
372
|
-
const action = version.actions.find((action) => action.tag === d.healthcheck);
|
|
373
|
-
if (!d.check_interval) {
|
|
374
|
-
d.check_interval = 10000;
|
|
578
|
+
if (d.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
|
|
579
|
+
const app = await this.fetchThirdPartyAppByAccessTag(d.app);
|
|
580
|
+
if (!app) {
|
|
581
|
+
throw new Error(`App with access tag ${d.app} not found`);
|
|
375
582
|
}
|
|
583
|
+
const version = app.versions.find((version) => version.tag === app.version);
|
|
584
|
+
const action = version.actions.find((action) => action.tag === d.event);
|
|
376
585
|
if (!action) {
|
|
377
|
-
throw new Error(`Cannot find
|
|
586
|
+
throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
|
|
587
|
+
}
|
|
588
|
+
if (d.healthcheck) {
|
|
589
|
+
const action = version.actions.find((action) => action.tag === d.healthcheck);
|
|
590
|
+
if (!d.check_interval) {
|
|
591
|
+
d.check_interval = 10000;
|
|
592
|
+
}
|
|
593
|
+
if (!action) {
|
|
594
|
+
throw new Error(`Cannot find healthcheck action ${d.healthcheck} on app ${app.tag} version ${app.version}`);
|
|
595
|
+
}
|
|
596
|
+
if (action.headers.data.length > 0 || action.body.data.length > 0 || action.params.data.length > 0 || action.query.data.length > 0) {
|
|
597
|
+
throw new Error('Healthcheck action is expected to have no headers, body, params or query');
|
|
598
|
+
}
|
|
378
599
|
}
|
|
379
600
|
}
|
|
380
601
|
d.last_checked = new Date();
|
|
@@ -383,15 +604,61 @@ class ProductsBuilderService {
|
|
|
383
604
|
}));
|
|
384
605
|
await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign({}, data), { component: enums_1.ProductComponents.QUOTA, action: enums_1.RequestAction.CREATE }), this.getUserAccess());
|
|
385
606
|
}
|
|
607
|
+
}
|
|
608
|
+
catch (e) {
|
|
609
|
+
throw e;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async updateQuota(tag, data) {
|
|
613
|
+
try {
|
|
614
|
+
const quota = this.fetchQuota(tag);
|
|
615
|
+
if (quota) {
|
|
616
|
+
await validators_1.UpdateProductQuotaSchema.validateAsync(data);
|
|
617
|
+
if (data.options) {
|
|
618
|
+
await this.validateQuotaFallbackInputAndOutput(data, 'quota');
|
|
619
|
+
data.total_quota = 0;
|
|
620
|
+
data.total_init = 0;
|
|
621
|
+
data.tag = tag;
|
|
622
|
+
await Promise.all(data.options.map(async (d) => {
|
|
623
|
+
data.total_quota += d.quota;
|
|
624
|
+
if (d.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
|
|
625
|
+
const app = await this.fetchThirdPartyAppByAccessTag(d.app);
|
|
626
|
+
if (!app) {
|
|
627
|
+
throw new Error(`App with access tag ${d.app} not found`);
|
|
628
|
+
}
|
|
629
|
+
const version = app.versions.find((version) => version.tag === app.version);
|
|
630
|
+
const action = version.actions.find((action) => action.tag === d.event);
|
|
631
|
+
if (!action) {
|
|
632
|
+
throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
|
|
633
|
+
}
|
|
634
|
+
if (d.healthcheck) {
|
|
635
|
+
const action = version.actions.find((action) => action.tag === d.healthcheck);
|
|
636
|
+
if (!d.check_interval) {
|
|
637
|
+
d.check_interval = 10000;
|
|
638
|
+
}
|
|
639
|
+
if (!action) {
|
|
640
|
+
throw new Error(`Cannot find healthcheck action ${d.healthcheck} on app ${app.tag} version ${app.version}`);
|
|
641
|
+
}
|
|
642
|
+
if (action.headers.data.length > 0 || action.body.data.length > 0 || action.params.data.length > 0 || action.query.data.length > 0) {
|
|
643
|
+
throw new Error('Healthcheck action is expected to have no headers, body, params or query');
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
d.last_checked = new Date();
|
|
648
|
+
d.provider_status = productsBuilder_types_1.ProviderStatus.AVAILABLE;
|
|
649
|
+
d.last_available = true;
|
|
650
|
+
}));
|
|
651
|
+
}
|
|
652
|
+
await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign(Object.assign({}, quota), data), { component: enums_1.ProductComponents.QUOTA, action: enums_1.RequestAction.UPDATE }), this.getUserAccess());
|
|
653
|
+
}
|
|
386
654
|
else {
|
|
387
|
-
throw new Error(`Quota with tag ${data.tag}
|
|
655
|
+
throw new Error(`Quota with tag ${data.tag} not found`);
|
|
388
656
|
}
|
|
389
657
|
}
|
|
390
658
|
catch (e) {
|
|
391
659
|
throw e;
|
|
392
660
|
}
|
|
393
661
|
}
|
|
394
|
-
async updateQuota(tag, data) { }
|
|
395
662
|
fetchQuota(tag) {
|
|
396
663
|
return this.fetchQuotas().find((data) => data.tag === tag);
|
|
397
664
|
}
|
|
@@ -403,26 +670,31 @@ class ProductsBuilderService {
|
|
|
403
670
|
if (!data.tag) {
|
|
404
671
|
throw new Error('tag field is required');
|
|
405
672
|
}
|
|
406
|
-
if (!this.
|
|
673
|
+
if (!this.fetchFallback(data.tag)) {
|
|
407
674
|
await validators_1.CreateProductFallbackSchema.validateAsync(data);
|
|
408
|
-
await this.validateQuotaFallbackInputAndOutput(data);
|
|
675
|
+
await this.validateQuotaFallbackInputAndOutput(data, 'fallback');
|
|
409
676
|
await Promise.all(data.options.map(async (d) => {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const version = app.versions.find((version) => version.tag === app.version);
|
|
415
|
-
const action = version.actions.find((action) => action.tag === d.event);
|
|
416
|
-
if (!action) {
|
|
417
|
-
throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
|
|
418
|
-
}
|
|
419
|
-
if (d.healthcheck) {
|
|
420
|
-
const action = version.actions.find((action) => action.tag === d.healthcheck);
|
|
421
|
-
if (!d.check_interval) {
|
|
422
|
-
d.check_interval = 10000;
|
|
677
|
+
if (d.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
|
|
678
|
+
const app = await this.fetchThirdPartyAppByAccessTag(d.app);
|
|
679
|
+
if (!app) {
|
|
680
|
+
throw new Error(`App with access tag ${d.app} not found`);
|
|
423
681
|
}
|
|
682
|
+
const version = app.versions.find((version) => version.tag === app.version);
|
|
683
|
+
const action = version.actions.find((action) => action.tag === d.event);
|
|
424
684
|
if (!action) {
|
|
425
|
-
throw new Error(`Cannot find
|
|
685
|
+
throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
|
|
686
|
+
}
|
|
687
|
+
if (d.healthcheck) {
|
|
688
|
+
const action = version.actions.find((action) => action.tag === d.healthcheck);
|
|
689
|
+
if (!d.check_interval) {
|
|
690
|
+
d.check_interval = 10000;
|
|
691
|
+
}
|
|
692
|
+
if (!action) {
|
|
693
|
+
throw new Error(`Cannot find healthcheck action ${d.healthcheck} on app ${app.tag} version ${app.version}`);
|
|
694
|
+
}
|
|
695
|
+
if (action.headers.data.length > 0 || action.body.data.length > 0 || action.params.data.length > 0 || action.query.data.length > 0) {
|
|
696
|
+
throw new Error('Healthcheck action is expected to have no headers, body, params or query');
|
|
697
|
+
}
|
|
426
698
|
}
|
|
427
699
|
}
|
|
428
700
|
d.last_checked = new Date();
|
|
@@ -431,83 +703,57 @@ class ProductsBuilderService {
|
|
|
431
703
|
}));
|
|
432
704
|
await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign({}, data), { component: enums_1.ProductComponents.FALLBACK, action: enums_1.RequestAction.CREATE }), this.getUserAccess());
|
|
433
705
|
}
|
|
434
|
-
else {
|
|
435
|
-
throw new Error(`Quota with tag ${data.tag} already exists`);
|
|
436
|
-
}
|
|
437
706
|
}
|
|
438
707
|
catch (e) {
|
|
439
708
|
throw e;
|
|
440
709
|
}
|
|
441
710
|
}
|
|
442
711
|
async updateFallback(tag, data) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
712
|
+
try {
|
|
713
|
+
const fallback = this.fetchFallback(tag);
|
|
714
|
+
if (fallback) {
|
|
715
|
+
await validators_1.UpdateProductFallbackSchema.validateAsync(data);
|
|
716
|
+
if (data.options) {
|
|
717
|
+
await this.validateQuotaFallbackInputAndOutput(data, 'fallback');
|
|
718
|
+
data.tag = tag;
|
|
719
|
+
await Promise.all(data.options.map(async (d) => {
|
|
720
|
+
if (d.type === productsBuilder_types_1.FeatureEventTypes.ACTION) {
|
|
721
|
+
const app = await this.fetchThirdPartyAppByAccessTag(d.app);
|
|
722
|
+
if (!app) {
|
|
723
|
+
throw new Error(`App with access tag ${d.app} not found`);
|
|
724
|
+
}
|
|
725
|
+
const version = app.versions.find((version) => version.tag === app.version);
|
|
726
|
+
const action = version.actions.find((action) => action.tag === d.event);
|
|
727
|
+
if (!action) {
|
|
728
|
+
throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
|
|
729
|
+
}
|
|
730
|
+
if (d.healthcheck) {
|
|
731
|
+
const action = version.actions.find((action) => action.tag === d.healthcheck);
|
|
732
|
+
if (!d.check_interval) {
|
|
733
|
+
d.check_interval = 10000;
|
|
734
|
+
}
|
|
735
|
+
if (!action) {
|
|
736
|
+
throw new Error(`Cannot find healthcheck action ${d.healthcheck} on app ${app.tag} version ${app.version}`);
|
|
737
|
+
}
|
|
738
|
+
if (action.headers.data.length > 0 || action.body.data.length > 0 || action.params.data.length > 0 || action.query.data.length > 0) {
|
|
739
|
+
throw new Error('Healthcheck action is expected to have no headers, body, params or query');
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
d.last_checked = new Date();
|
|
744
|
+
d.provider_status = productsBuilder_types_1.ProviderStatus.AVAILABLE;
|
|
745
|
+
d.last_available = true;
|
|
746
|
+
}));
|
|
747
|
+
}
|
|
748
|
+
await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign(Object.assign({}, fallback), data), { component: enums_1.ProductComponents.FALLBACK, action: enums_1.RequestAction.UPDATE }), this.getUserAccess());
|
|
453
749
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
data.tag = tag;
|
|
750
|
+
else {
|
|
751
|
+
throw new Error(`Fallback with tag ${data.tag} not found`);
|
|
457
752
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
await Promise.all(data.options.map(async (d) => {
|
|
464
|
-
|
|
465
|
-
const app = await this.fetchThirdPartyAppByAccessTag(d.app);
|
|
466
|
-
|
|
467
|
-
if (!app) {
|
|
468
|
-
throw new Error(`App with access tag ${d.app} not found`)
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const version = app.versions.find((version) => version.tag === app.version);
|
|
472
|
-
const action = version.actions.find((action) => action.tag === d.event)
|
|
473
|
-
|
|
474
|
-
if (!action) {
|
|
475
|
-
throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`)
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
if (d.healthcheck) {
|
|
479
|
-
const action = version.actions.find((action) => action.tag === d.healthcheck)
|
|
480
|
-
|
|
481
|
-
if (!d.check_interval) {
|
|
482
|
-
d.check_interval = 10000;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
if (!action) {
|
|
486
|
-
throw new Error(`Cannot find healthcheck action ${d.healthcheck} on app ${app.tag} version ${app.version}`)
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
d.last_checked = new Date();
|
|
491
|
-
d.provider_status = ProviderStatus.AVAILABLE;
|
|
492
|
-
d.last_available = true;
|
|
493
|
-
}))
|
|
494
|
-
|
|
495
|
-
await this.productApi.updateProduct(
|
|
496
|
-
this.product_id,
|
|
497
|
-
{
|
|
498
|
-
...data,
|
|
499
|
-
component: ProductComponents.FALLBACK,
|
|
500
|
-
},
|
|
501
|
-
this.getUserAccess(),
|
|
502
|
-
);
|
|
503
|
-
|
|
504
|
-
} else {
|
|
505
|
-
throw new Error(`Quota with tag ${data.tag} already exists`)
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
} catch (e) {
|
|
509
|
-
throw e;
|
|
510
|
-
}*/
|
|
753
|
+
}
|
|
754
|
+
catch (e) {
|
|
755
|
+
throw e;
|
|
756
|
+
}
|
|
511
757
|
}
|
|
512
758
|
fetchFallback(tag) {
|
|
513
759
|
return this.fetchFallbacks().find((data) => data.tag === tag);
|
|
@@ -662,11 +908,43 @@ class ProductsBuilderService {
|
|
|
662
908
|
return messageBroker;
|
|
663
909
|
}
|
|
664
910
|
fetchMessageBrokers() {
|
|
665
|
-
console.log("PROD!", this.product);
|
|
666
911
|
return this.product.messageBrokers.map((broker) => {
|
|
667
912
|
return this.fetchMessageBroker(broker.tag);
|
|
668
913
|
});
|
|
669
914
|
}
|
|
915
|
+
async fetchStorageFiles(filter) {
|
|
916
|
+
try {
|
|
917
|
+
const result = await this.productApi.fetchProductStorageFiles(Object.assign(Object.assign({}, filter), { product: this.product.tag }), this.getUserAccess());
|
|
918
|
+
if (!result)
|
|
919
|
+
return [];
|
|
920
|
+
return result.map((res) => {
|
|
921
|
+
delete res._id;
|
|
922
|
+
delete res.workspace_id;
|
|
923
|
+
delete res.__v;
|
|
924
|
+
res.url = (0, processor_utils_1.decrypt)(res.url, this.product.private_key);
|
|
925
|
+
return res;
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
catch (e) {
|
|
929
|
+
throw (e);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
async fetchSessionUsers(filter) {
|
|
933
|
+
try {
|
|
934
|
+
const result = await this.productApi.fetchProductSessionUsers(Object.assign(Object.assign({}, filter), { product: this.product.tag }), this.getUserAccess());
|
|
935
|
+
if (!result)
|
|
936
|
+
return [];
|
|
937
|
+
return result.map((res) => {
|
|
938
|
+
delete res._id;
|
|
939
|
+
delete res.workspace_id;
|
|
940
|
+
delete res.__v;
|
|
941
|
+
return res;
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
catch (e) {
|
|
945
|
+
throw (e);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
670
948
|
async createStorage(data, throwErrorIfExists = false) {
|
|
671
949
|
if (!this.fetchStorage(data.tag, false)) {
|
|
672
950
|
await create_productStorage_validator_1.CreateProductStorageSchema.validateAsync(data);
|
|
@@ -960,6 +1238,8 @@ class ProductsBuilderService {
|
|
|
960
1238
|
throw new Error(`App with access tag ${access_tag} not found`);
|
|
961
1239
|
}
|
|
962
1240
|
const version = app.versions.find((version) => version.tag === app.version);
|
|
1241
|
+
//const status = await this.webhooksApi.fetchWebhooks({access_tag, app_tag: app.tag, version: app.version}, this.getUserAccess())
|
|
1242
|
+
console.log(status);
|
|
963
1243
|
if (!version) {
|
|
964
1244
|
throw new Error(`Required app version not found`);
|
|
965
1245
|
}
|
|
@@ -1206,7 +1486,7 @@ class ProductsBuilderService {
|
|
|
1206
1486
|
if (!notificationTag || !messageTag) {
|
|
1207
1487
|
throw new Error(`tag is expected to be defined as "notification_tag:message_tag"`);
|
|
1208
1488
|
}
|
|
1209
|
-
const notification = this.
|
|
1489
|
+
const notification = this.fetchNotification(notificationTag);
|
|
1210
1490
|
if (!notification)
|
|
1211
1491
|
throw new Error(`Notification ${notificationTag} not found`);
|
|
1212
1492
|
const message = notification.messages.find((data) => data.tag === messageTag);
|