@ductape/sdk 0.1.102 → 0.1.103
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/index.d.ts +1 -1
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/processor/services/processor.service.d.ts +5 -3
- package/dist/processor/services/processor.service.js +11 -10
- package/dist/processor/services/processor.service.js.map +1 -1
- package/dist/products/services/products.service.d.ts +23 -2
- package/dist/products/services/products.service.js +79 -2
- package/dist/products/services/products.service.js.map +1 -1
- package/dist/resilience/healthcheck-cache-key.d.ts +10 -0
- package/dist/resilience/healthcheck-cache-key.js +19 -0
- package/dist/resilience/healthcheck-cache-key.js.map +1 -0
- package/dist/resilience/healthcheck.service.js +16 -9
- package/dist/resilience/healthcheck.service.js.map +1 -1
- package/dist/resilience/resilience.service.js +15 -2
- package/dist/resilience/resilience.service.js.map +1 -1
- package/dist/resilience/types/index.d.ts +8 -0
- package/dist/resilience/types/index.js +2 -0
- package/dist/resilience/types/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/productsBuilder.types.d.ts +13 -1
- package/dist/types/productsBuilder.types.js +5 -0
- package/dist/types/productsBuilder.types.js.map +1 -1
- package/package.json +1 -1
|
@@ -30,7 +30,7 @@ export interface IProductsBuilderService {
|
|
|
30
30
|
generateExecutablePayload(params: IGenerateExecutablePayloadParams): Promise<IGenerateExecutablePayloadResponse>;
|
|
31
31
|
createEnv(data: Partial<IProductEnv>): Promise<void>;
|
|
32
32
|
updateEnv(slug: string, data: Partial<IProductEnv>): Promise<void>;
|
|
33
|
-
fetchEnv(slug: string): Promise<IProductEnv | null>;
|
|
33
|
+
fetchEnv(slug: string, refresh?: boolean): Promise<IProductEnv | null>;
|
|
34
34
|
fetchEnvs(): Promise<Array<IProductEnv>>;
|
|
35
35
|
createSession(data: IProductSession): Promise<void>;
|
|
36
36
|
updateSession(tag: string, data: Partial<IProductSession>): Promise<void>;
|
|
@@ -122,6 +122,12 @@ export interface IProductsBuilderService {
|
|
|
122
122
|
fetchJobs(): Promise<Array<IProductJobs>>;
|
|
123
123
|
createHealthcheck(data: Partial<IProductAppHealth>): Promise<void>;
|
|
124
124
|
updateHealthcheck(tag: string, data: Partial<IProductAppHealth>): Promise<void>;
|
|
125
|
+
updateHealthcheckStatuses(productTag: string, checks: Array<{
|
|
126
|
+
tag: string;
|
|
127
|
+
environments: Array<Record<string, any> & {
|
|
128
|
+
slug: string;
|
|
129
|
+
}>;
|
|
130
|
+
}>): Promise<void>;
|
|
125
131
|
fetchHealthcheck(access_tag: string, tag: string): Promise<IProductAppHealth | null>;
|
|
126
132
|
fetchHealthchecks(access_tag: string): Promise<Array<IProductAppHealth>>;
|
|
127
133
|
fetchProductHealthchecks(): Promise<Array<IProductAppHealth>>;
|
|
@@ -175,6 +181,16 @@ export default class ProductsBuilderService implements IProductsBuilderService {
|
|
|
175
181
|
fetchProductId(): string;
|
|
176
182
|
createHealthcheck(data: Partial<IProductAppHealth>): Promise<void>;
|
|
177
183
|
updateHealthcheck(tag: string, data: Partial<IProductAppHealth>): Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* Persist monitor results without re-validating or re-encrypting the probe payload.
|
|
186
|
+
* This is used by the local SDK's batched Redis-to-backend status flush.
|
|
187
|
+
*/
|
|
188
|
+
updateHealthcheckStatuses(productTag: string, checks: Array<{
|
|
189
|
+
tag: string;
|
|
190
|
+
environments: Array<Record<string, any> & {
|
|
191
|
+
slug: string;
|
|
192
|
+
}>;
|
|
193
|
+
}>): Promise<void>;
|
|
178
194
|
fetchHealthcheck(access_tag: string, tag: string, throwError?: boolean): Promise<IProductAppHealth | null>;
|
|
179
195
|
fetchHealthchecks(access_tag: string, throwError?: boolean): Promise<Array<IProductAppHealth>>;
|
|
180
196
|
fetchProductHealthchecks(): Promise<Array<IProductAppHealth>>;
|
|
@@ -193,6 +209,11 @@ export default class ProductsBuilderService implements IProductsBuilderService {
|
|
|
193
209
|
}): Promise<void>;
|
|
194
210
|
/** Keep in-memory product component arrays in sync after component CRUD (components API can lag). */
|
|
195
211
|
private mergeProductComponent;
|
|
212
|
+
/**
|
|
213
|
+
* Validate app healthchecks against the product's connected app and its selected action.
|
|
214
|
+
* Every environment carries an explicit payload, including `{}` for actions with no inputs.
|
|
215
|
+
*/
|
|
216
|
+
private validateAppHealthcheckSchema;
|
|
196
217
|
/**
|
|
197
218
|
* Create a healthcheck from a schema (e.g. from resilience health.define/create).
|
|
198
219
|
* Persists as product component so processor healthcheck workers and health.list/fetch see it.
|
|
@@ -313,7 +334,7 @@ export default class ProductsBuilderService implements IProductsBuilderService {
|
|
|
313
334
|
createEnv(data: IProductEnv, throwErrorIfExists?: boolean): Promise<void>;
|
|
314
335
|
updateEnv(slug: string, data: Partial<IProductEnv>): Promise<void>;
|
|
315
336
|
fetchEnvs(version?: string): Promise<Array<IProductEnv>>;
|
|
316
|
-
fetchEnv(slug: string): Promise<IProductEnv | null>;
|
|
337
|
+
fetchEnv(slug: string, refresh?: boolean): Promise<IProductEnv | null>;
|
|
317
338
|
deleteEnv(slug: string): Promise<void>;
|
|
318
339
|
createMessageBroker(data: Partial<IProductMessageBroker>, throwErrorIfExists?: boolean): Promise<void>;
|
|
319
340
|
updateMessageBroker(tag: string, data: Partial<IProductMessageBroker>): Promise<void>;
|
|
@@ -234,6 +234,37 @@ class ProductsBuilderService {
|
|
|
234
234
|
throw e;
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Persist monitor results without re-validating or re-encrypting the probe payload.
|
|
239
|
+
* This is used by the local SDK's batched Redis-to-backend status flush.
|
|
240
|
+
*/
|
|
241
|
+
async updateHealthcheckStatuses(productTag, checks) {
|
|
242
|
+
await this.initializeProductByTag(productTag);
|
|
243
|
+
const healthchecks = await this.fetchProductHealthchecks();
|
|
244
|
+
for (const check of checks) {
|
|
245
|
+
const existing = healthchecks.find((healthcheck) => healthcheck.tag === check.tag);
|
|
246
|
+
if (!existing) {
|
|
247
|
+
console.warn(`[MONITOR] Healthcheck "${check.tag}" no longer exists on ${productTag}; skipping status update`);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const updates = new Map((check.environments || []).map((environment) => [
|
|
251
|
+
environment.slug,
|
|
252
|
+
environment,
|
|
253
|
+
]));
|
|
254
|
+
const envs = (existing.envs || []).map((environment) => {
|
|
255
|
+
var _a, _b, _c;
|
|
256
|
+
const update = updates.get(environment.slug);
|
|
257
|
+
if (!update)
|
|
258
|
+
return environment;
|
|
259
|
+
const checkedAt = (_a = update.checkedAt) !== null && _a !== void 0 ? _a : update.lastChecked;
|
|
260
|
+
const latency = (_b = update.latency) !== null && _b !== void 0 ? _b : update.lastLatency;
|
|
261
|
+
return Object.assign(Object.assign({}, environment), { status: (_c = update.status) !== null && _c !== void 0 ? _c : environment.status, lastChecked: checkedAt !== null && checkedAt !== void 0 ? checkedAt : environment.lastChecked, lastLatency: latency !== null && latency !== void 0 ? latency : environment.lastLatency, lastAvailable: update.status === 'available'
|
|
262
|
+
? checkedAt !== null && checkedAt !== void 0 ? checkedAt : new Date().toISOString()
|
|
263
|
+
: environment.lastAvailable });
|
|
264
|
+
});
|
|
265
|
+
await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign({}, existing), { tag: check.tag, envs, component: enums_1.ProductComponents.HEALTHCHECK, action: enums_1.RequestAction.UPDATE }), this.getUserAccess());
|
|
266
|
+
}
|
|
267
|
+
}
|
|
237
268
|
async fetchHealthcheck(access_tag, tag, throwError = false) {
|
|
238
269
|
const healthchecks = await this.productApi.fetchProductComponents(this.product_id, 'healthcheck', this.getUserAccess());
|
|
239
270
|
const health = healthchecks.find((data) => data.tag === tag && data.app === access_tag);
|
|
@@ -306,6 +337,47 @@ class ProductsBuilderService {
|
|
|
306
337
|
list.push(item);
|
|
307
338
|
this.product[key] = list;
|
|
308
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Validate app healthchecks against the product's connected app and its selected action.
|
|
342
|
+
* Every environment carries an explicit payload, including `{}` for actions with no inputs.
|
|
343
|
+
*/
|
|
344
|
+
async validateAppHealthcheckSchema(schema) {
|
|
345
|
+
var _a, _b, _c, _d;
|
|
346
|
+
const probe = schema.probe;
|
|
347
|
+
const probeType = (_a = probe === null || probe === void 0 ? void 0 : probe.type) !== null && _a !== void 0 ? _a : ((probe === null || probe === void 0 ? void 0 : probe.app) || schema.app ? 'app' : undefined);
|
|
348
|
+
if (probeType !== 'app')
|
|
349
|
+
return;
|
|
350
|
+
const appTag = (_b = probe === null || probe === void 0 ? void 0 : probe.app) !== null && _b !== void 0 ? _b : schema.app;
|
|
351
|
+
const actionTag = (_c = probe === null || probe === void 0 ? void 0 : probe.event) !== null && _c !== void 0 ? _c : schema.event;
|
|
352
|
+
if (!appTag) {
|
|
353
|
+
throw new Error(`App healthcheck "${schema.tag}" requires a connected app access tag`);
|
|
354
|
+
}
|
|
355
|
+
if (!actionTag) {
|
|
356
|
+
throw new Error(`App healthcheck "${schema.tag}" requires an app action`);
|
|
357
|
+
}
|
|
358
|
+
if (!Array.isArray(schema.envs) || schema.envs.length === 0) {
|
|
359
|
+
throw new Error(`App healthcheck "${schema.tag}" requires at least one environment payload`);
|
|
360
|
+
}
|
|
361
|
+
const app = await this.fetchThirdPartyAppByAccessTag(appTag);
|
|
362
|
+
const version = app.versions.find((candidate) => candidate.tag === app.version);
|
|
363
|
+
if (!version) {
|
|
364
|
+
throw new Error(`Version ${app.version} not found for connected app ${appTag}`);
|
|
365
|
+
}
|
|
366
|
+
const action = version.actions.find((candidate) => candidate.tag === actionTag);
|
|
367
|
+
if (!action) {
|
|
368
|
+
throw new Error(`Action ${actionTag} not found on connected app ${appTag}`);
|
|
369
|
+
}
|
|
370
|
+
for (const env of schema.envs || []) {
|
|
371
|
+
if (!(await this.fetchEnv(env.slug))) {
|
|
372
|
+
throw new Error(`Env ${env.slug} does not exist`);
|
|
373
|
+
}
|
|
374
|
+
const payload = (_d = env.input) !== null && _d !== void 0 ? _d : probe === null || probe === void 0 ? void 0 : probe.input;
|
|
375
|
+
if (payload == null) {
|
|
376
|
+
throw new Error(`App healthcheck "${schema.tag}" requires an input payload for env ${env.slug}; use {} when the action has no inputs`);
|
|
377
|
+
}
|
|
378
|
+
await this.validateActionDataInput({ input: payload }, action, payload, 0, 0);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
309
381
|
/**
|
|
310
382
|
* Create a healthcheck from a schema (e.g. from resilience health.define/create).
|
|
311
383
|
* Persists as product component so processor healthcheck workers and health.list/fetch see it.
|
|
@@ -318,6 +390,7 @@ class ProductsBuilderService {
|
|
|
318
390
|
const probeInput = (probe === null || probe === void 0 ? void 0 : probe.input) != null ? probe.input : undefined;
|
|
319
391
|
const app = (_a = probe === null || probe === void 0 ? void 0 : probe.app) !== null && _a !== void 0 ? _a : schema.app;
|
|
320
392
|
const event = (_b = probe === null || probe === void 0 ? void 0 : probe.event) !== null && _b !== void 0 ? _b : schema.event;
|
|
393
|
+
await this.validateAppHealthcheckSchema(schema);
|
|
321
394
|
if (probeInput != null && !((_c = this.product) === null || _c === void 0 ? void 0 : _c.private_key)) {
|
|
322
395
|
throw new Error('Healthcheck inputs must be encrypted with the product private key; product not initialized or missing private_key');
|
|
323
396
|
}
|
|
@@ -339,6 +412,7 @@ class ProductsBuilderService {
|
|
|
339
412
|
const probeInput = (probe === null || probe === void 0 ? void 0 : probe.input) != null ? probe.input : undefined;
|
|
340
413
|
const app = (_a = probe === null || probe === void 0 ? void 0 : probe.app) !== null && _a !== void 0 ? _a : schema.app;
|
|
341
414
|
const event = (_b = probe === null || probe === void 0 ? void 0 : probe.event) !== null && _b !== void 0 ? _b : schema.event;
|
|
415
|
+
await this.validateAppHealthcheckSchema(schema);
|
|
342
416
|
if (probeInput != null && !((_c = this.product) === null || _c === void 0 ? void 0 : _c.private_key)) {
|
|
343
417
|
throw new Error('Healthcheck inputs must be encrypted with the product private key; product not initialized or missing private_key');
|
|
344
418
|
}
|
|
@@ -1371,6 +1445,9 @@ class ProductsBuilderService {
|
|
|
1371
1445
|
data.slug = slug;
|
|
1372
1446
|
}
|
|
1373
1447
|
await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign({}, Object.assign(Object.assign({}, env), data)), { component: enums_1.ProductComponents.ENV, action: enums_1.RequestAction.UPDATE }), this.getUserAccess());
|
|
1448
|
+
this.envCache.delete(slug);
|
|
1449
|
+
if (data.slug !== slug)
|
|
1450
|
+
this.envCache.delete(data.slug);
|
|
1374
1451
|
}
|
|
1375
1452
|
catch (e) {
|
|
1376
1453
|
throw e;
|
|
@@ -1380,8 +1457,8 @@ class ProductsBuilderService {
|
|
|
1380
1457
|
const components = await this.productApi.fetchProductComponents(this.product_id, 'env', this.getUserAccess());
|
|
1381
1458
|
return components;
|
|
1382
1459
|
}
|
|
1383
|
-
async fetchEnv(slug) {
|
|
1384
|
-
if (this.product_id && this.envCache.has(slug)) {
|
|
1460
|
+
async fetchEnv(slug, refresh = false) {
|
|
1461
|
+
if (!refresh && this.product_id && this.envCache.has(slug)) {
|
|
1385
1462
|
return this.envCache.get(slug);
|
|
1386
1463
|
}
|
|
1387
1464
|
const component = await this.productApi.fetchProductComponentByTag(this.product_id, 'env', slug, this.getUserAccess());
|