@pagerduty/backstage-plugin-backend 0.13.0 → 0.14.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/CHANGELOG.md +11 -0
- package/config.d.ts +43 -0
- package/dist/apis/pagerduty.cjs.js +364 -24
- package/dist/apis/pagerduty.cjs.js.map +1 -1
- package/dist/controllers/mappings-controller.cjs.js +31 -7
- package/dist/controllers/mappings-controller.cjs.js.map +1 -1
- package/dist/db/PagerDutyBackendDatabase.cjs.js +202 -4
- package/dist/db/PagerDutyBackendDatabase.cjs.js.map +1 -1
- package/dist/index.d.ts +41 -1
- package/dist/plugin.cjs.js +20 -2
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/service/customFieldsController.cjs.js +459 -0
- package/dist/service/customFieldsController.cjs.js.map +1 -0
- package/dist/service/router.cjs.js +54 -18
- package/dist/service/router.cjs.js.map +1 -1
- package/dist/services/autoMatchRunner.cjs.js +3 -2
- package/dist/services/autoMatchRunner.cjs.js.map +1 -1
- package/dist/services/dataLoader.cjs.js +3 -3
- package/dist/services/dataLoader.cjs.js.map +1 -1
- package/dist/services/syncLogsCleanup.cjs.js +77 -0
- package/dist/services/syncLogsCleanup.cjs.js.map +1 -0
- package/migrations/20260225113251_add_pagerduty_custom_fields_table.js +31 -0
- package/migrations/20260313_add_custom_fields_unique_indexes.js +27 -0
- package/migrations/20260515_add_custom_field_sync_logs_table.js +32 -0
- package/migrations/20260612_add_sync_logs_timestamp_index.js +19 -0
- package/package.json +7 -7
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const DEFAULTS = {
|
|
4
|
+
enabled: true,
|
|
5
|
+
retentionDays: 30,
|
|
6
|
+
maxRows: 5e5,
|
|
7
|
+
frequencyMinutes: 15,
|
|
8
|
+
batchSize: 1e4,
|
|
9
|
+
maxBatchesPerRun: 500
|
|
10
|
+
};
|
|
11
|
+
const CONFIG_ROOT = "pagerDuty.customFieldsSyncLogs";
|
|
12
|
+
const SYNC_LOGS_CLEANUP_TASK_ID = "pagerduty-custom-field-sync-logs-cleanup";
|
|
13
|
+
const SYNC_LOGS_CLEANUP_TIMEOUT_MINUTES = 10;
|
|
14
|
+
const SYNC_LOGS_CLEANUP_INITIAL_DELAY_MINUTES = 1;
|
|
15
|
+
function readSyncLogsCleanupConfig(config) {
|
|
16
|
+
const clamp = (value, fallback, min) => Math.max(Math.floor(value ?? fallback), min);
|
|
17
|
+
return {
|
|
18
|
+
enabled: config.getOptionalBoolean(`${CONFIG_ROOT}.cleanup.enabled`) ?? DEFAULTS.enabled,
|
|
19
|
+
retentionDays: clamp(
|
|
20
|
+
config.getOptionalNumber(`${CONFIG_ROOT}.retentionDays`),
|
|
21
|
+
DEFAULTS.retentionDays,
|
|
22
|
+
1
|
|
23
|
+
),
|
|
24
|
+
maxRows: clamp(
|
|
25
|
+
config.getOptionalNumber(`${CONFIG_ROOT}.maxRows`),
|
|
26
|
+
DEFAULTS.maxRows,
|
|
27
|
+
1e3
|
|
28
|
+
),
|
|
29
|
+
frequencyMinutes: clamp(
|
|
30
|
+
config.getOptionalNumber(`${CONFIG_ROOT}.cleanup.frequencyMinutes`),
|
|
31
|
+
DEFAULTS.frequencyMinutes,
|
|
32
|
+
1
|
|
33
|
+
),
|
|
34
|
+
batchSize: clamp(
|
|
35
|
+
config.getOptionalNumber(`${CONFIG_ROOT}.cleanup.batchSize`),
|
|
36
|
+
DEFAULTS.batchSize,
|
|
37
|
+
100
|
|
38
|
+
),
|
|
39
|
+
maxBatchesPerRun: clamp(
|
|
40
|
+
config.getOptionalNumber(`${CONFIG_ROOT}.cleanup.maxBatchesPerRun`),
|
|
41
|
+
DEFAULTS.maxBatchesPerRun,
|
|
42
|
+
1
|
|
43
|
+
)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function runSyncLogsCleanup(options) {
|
|
47
|
+
const { store, logger, cleanupConfig } = options;
|
|
48
|
+
const startedAt = Date.now();
|
|
49
|
+
try {
|
|
50
|
+
const result = await store.cleanupSyncLogs({
|
|
51
|
+
olderThanDays: cleanupConfig.retentionDays,
|
|
52
|
+
maxRows: cleanupConfig.maxRows,
|
|
53
|
+
batchSize: cleanupConfig.batchSize,
|
|
54
|
+
maxBatchesPerRun: cleanupConfig.maxBatchesPerRun
|
|
55
|
+
});
|
|
56
|
+
const durationMs = Date.now() - startedAt;
|
|
57
|
+
logger.info(
|
|
58
|
+
`Sync log cleanup: deleted ${result.deletedByAge} expired and ${result.deletedByCap} over-cap row(s) in ${result.batchesUsed} batch(es) (${durationMs}ms)`
|
|
59
|
+
);
|
|
60
|
+
if (result.batchesUsed >= cleanupConfig.maxBatchesPerRun) {
|
|
61
|
+
logger.warn(
|
|
62
|
+
`Sync log cleanup exhausted its batch budget (${cleanupConfig.maxBatchesPerRun} batches of ${cleanupConfig.batchSize}); a backlog remains. To catch up faster, increase pagerDuty.customFieldsSyncLogs.cleanup.maxBatchesPerRun or decrease pagerDuty.customFieldsSyncLogs.cleanup.frequencyMinutes.`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
logger.error(
|
|
67
|
+
`Sync log cleanup failed: ${error instanceof Error ? error.message : String(error)}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
exports.SYNC_LOGS_CLEANUP_INITIAL_DELAY_MINUTES = SYNC_LOGS_CLEANUP_INITIAL_DELAY_MINUTES;
|
|
73
|
+
exports.SYNC_LOGS_CLEANUP_TASK_ID = SYNC_LOGS_CLEANUP_TASK_ID;
|
|
74
|
+
exports.SYNC_LOGS_CLEANUP_TIMEOUT_MINUTES = SYNC_LOGS_CLEANUP_TIMEOUT_MINUTES;
|
|
75
|
+
exports.readSyncLogsCleanupConfig = readSyncLogsCleanupConfig;
|
|
76
|
+
exports.runSyncLogsCleanup = runSyncLogsCleanup;
|
|
77
|
+
//# sourceMappingURL=syncLogsCleanup.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncLogsCleanup.cjs.js","sources":["../../src/services/syncLogsCleanup.ts"],"sourcesContent":["import {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { PagerDutyBackendStore } from '../db';\n\nexport interface SyncLogsCleanupConfig {\n enabled: boolean;\n retentionDays: number;\n maxRows: number;\n frequencyMinutes: number;\n batchSize: number;\n maxBatchesPerRun: number;\n}\n\nconst DEFAULTS: SyncLogsCleanupConfig = {\n enabled: true,\n retentionDays: 30,\n maxRows: 500_000,\n frequencyMinutes: 15,\n batchSize: 10_000,\n maxBatchesPerRun: 500,\n};\n\nconst CONFIG_ROOT = 'pagerDuty.customFieldsSyncLogs';\n\nexport const SYNC_LOGS_CLEANUP_TASK_ID =\n 'pagerduty-custom-field-sync-logs-cleanup';\nexport const SYNC_LOGS_CLEANUP_TIMEOUT_MINUTES = 10;\nexport const SYNC_LOGS_CLEANUP_INITIAL_DELAY_MINUTES = 1;\n\nexport function readSyncLogsCleanupConfig(\n config: RootConfigService,\n): SyncLogsCleanupConfig {\n const clamp = (value: number | undefined, fallback: number, min: number) =>\n Math.max(Math.floor(value ?? fallback), min);\n\n return {\n enabled:\n config.getOptionalBoolean(`${CONFIG_ROOT}.cleanup.enabled`) ??\n DEFAULTS.enabled,\n retentionDays: clamp(\n config.getOptionalNumber(`${CONFIG_ROOT}.retentionDays`),\n DEFAULTS.retentionDays,\n 1,\n ),\n maxRows: clamp(\n config.getOptionalNumber(`${CONFIG_ROOT}.maxRows`),\n DEFAULTS.maxRows,\n 1000,\n ),\n frequencyMinutes: clamp(\n config.getOptionalNumber(`${CONFIG_ROOT}.cleanup.frequencyMinutes`),\n DEFAULTS.frequencyMinutes,\n 1,\n ),\n batchSize: clamp(\n config.getOptionalNumber(`${CONFIG_ROOT}.cleanup.batchSize`),\n DEFAULTS.batchSize,\n 100,\n ),\n maxBatchesPerRun: clamp(\n config.getOptionalNumber(`${CONFIG_ROOT}.cleanup.maxBatchesPerRun`),\n DEFAULTS.maxBatchesPerRun,\n 1,\n ),\n };\n}\n\nexport async function runSyncLogsCleanup(options: {\n store: PagerDutyBackendStore;\n logger: LoggerService;\n cleanupConfig: SyncLogsCleanupConfig;\n}): Promise<void> {\n const { store, logger, cleanupConfig } = options;\n const startedAt = Date.now();\n\n try {\n const result = await store.cleanupSyncLogs({\n olderThanDays: cleanupConfig.retentionDays,\n maxRows: cleanupConfig.maxRows,\n batchSize: cleanupConfig.batchSize,\n maxBatchesPerRun: cleanupConfig.maxBatchesPerRun,\n });\n\n const durationMs = Date.now() - startedAt;\n logger.info(\n `Sync log cleanup: deleted ${result.deletedByAge} expired and ${result.deletedByCap} over-cap row(s) in ${result.batchesUsed} batch(es) (${durationMs}ms)`,\n );\n\n if (result.batchesUsed >= cleanupConfig.maxBatchesPerRun) {\n logger.warn(\n `Sync log cleanup exhausted its batch budget (${cleanupConfig.maxBatchesPerRun} batches of ${cleanupConfig.batchSize}); ` +\n `a backlog remains. To catch up faster, increase pagerDuty.customFieldsSyncLogs.cleanup.maxBatchesPerRun ` +\n `or decrease pagerDuty.customFieldsSyncLogs.cleanup.frequencyMinutes.`,\n );\n }\n } catch (error) {\n // A failed cleanup must never crash the backend; the scheduler retries on\n // the next cycle.\n logger.error(\n `Sync log cleanup failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n}\n"],"names":[],"mappings":";;AAeA,MAAM,QAAA,GAAkC;AAAA,EACtC,OAAA,EAAS,IAAA;AAAA,EACT,aAAA,EAAe,EAAA;AAAA,EACf,OAAA,EAAS,GAAA;AAAA,EACT,gBAAA,EAAkB,EAAA;AAAA,EAClB,SAAA,EAAW,GAAA;AAAA,EACX,gBAAA,EAAkB;AACpB,CAAA;AAEA,MAAM,WAAA,GAAc,gCAAA;AAEb,MAAM,yBAAA,GACX;AACK,MAAM,iCAAA,GAAoC;AAC1C,MAAM,uCAAA,GAA0C;AAEhD,SAAS,0BACd,MAAA,EACuB;AACvB,EAAA,MAAM,KAAA,GAAQ,CAAC,KAAA,EAA2B,QAAA,EAAkB,GAAA,KAC1D,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,KAAA,IAAS,QAAQ,CAAA,EAAG,GAAG,CAAA;AAE7C,EAAA,OAAO;AAAA,IACL,SACE,MAAA,CAAO,kBAAA,CAAmB,GAAG,WAAW,CAAA,gBAAA,CAAkB,KAC1D,QAAA,CAAS,OAAA;AAAA,IACX,aAAA,EAAe,KAAA;AAAA,MACb,MAAA,CAAO,iBAAA,CAAkB,CAAA,EAAG,WAAW,CAAA,cAAA,CAAgB,CAAA;AAAA,MACvD,QAAA,CAAS,aAAA;AAAA,MACT;AAAA,KACF;AAAA,IACA,OAAA,EAAS,KAAA;AAAA,MACP,MAAA,CAAO,iBAAA,CAAkB,CAAA,EAAG,WAAW,CAAA,QAAA,CAAU,CAAA;AAAA,MACjD,QAAA,CAAS,OAAA;AAAA,MACT;AAAA,KACF;AAAA,IACA,gBAAA,EAAkB,KAAA;AAAA,MAChB,MAAA,CAAO,iBAAA,CAAkB,CAAA,EAAG,WAAW,CAAA,yBAAA,CAA2B,CAAA;AAAA,MAClE,QAAA,CAAS,gBAAA;AAAA,MACT;AAAA,KACF;AAAA,IACA,SAAA,EAAW,KAAA;AAAA,MACT,MAAA,CAAO,iBAAA,CAAkB,CAAA,EAAG,WAAW,CAAA,kBAAA,CAAoB,CAAA;AAAA,MAC3D,QAAA,CAAS,SAAA;AAAA,MACT;AAAA,KACF;AAAA,IACA,gBAAA,EAAkB,KAAA;AAAA,MAChB,MAAA,CAAO,iBAAA,CAAkB,CAAA,EAAG,WAAW,CAAA,yBAAA,CAA2B,CAAA;AAAA,MAClE,QAAA,CAAS,gBAAA;AAAA,MACT;AAAA;AACF,GACF;AACF;AAEA,eAAsB,mBAAmB,OAAA,EAIvB;AAChB,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,aAAA,EAAc,GAAI,OAAA;AACzC,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,eAAA,CAAgB;AAAA,MACzC,eAAe,aAAA,CAAc,aAAA;AAAA,MAC7B,SAAS,aAAA,CAAc,OAAA;AAAA,MACvB,WAAW,aAAA,CAAc,SAAA;AAAA,MACzB,kBAAkB,aAAA,CAAc;AAAA,KACjC,CAAA;AAED,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAChC,IAAA,MAAA,CAAO,IAAA;AAAA,MACL,CAAA,0BAAA,EAA6B,MAAA,CAAO,YAAY,CAAA,aAAA,EAAgB,MAAA,CAAO,YAAY,CAAA,oBAAA,EAAuB,MAAA,CAAO,WAAW,CAAA,YAAA,EAAe,UAAU,CAAA,GAAA;AAAA,KACvJ;AAEA,IAAA,IAAI,MAAA,CAAO,WAAA,IAAe,aAAA,CAAc,gBAAA,EAAkB;AACxD,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,CAAA,6CAAA,EAAgD,aAAA,CAAc,gBAAgB,CAAA,YAAA,EAAe,cAAc,SAAS,CAAA,+KAAA;AAAA,OAGtH;AAAA,IACF;AAAA,EACF,SAAS,KAAA,EAAO;AAGd,IAAA,MAAA,CAAO,KAAA;AAAA,MACL,4BACE,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CACvD,CAAA;AAAA,KACF;AAAA,EACF;AACF;;;;;;;;"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
exports.up = async function up(knex) {
|
|
8
|
+
return knex.schema.createTable('pagerduty_custom_fields', table => {
|
|
9
|
+
table.increments('id').primary();
|
|
10
|
+
table.string('pagerdutyCustomFieldId').notNullable();
|
|
11
|
+
table.string('pagerdutyCustomFieldDisplayName').notNullable();
|
|
12
|
+
table.boolean('pagerdutyCustomFieldEnabled').notNullable().defaultTo(true);
|
|
13
|
+
table.string('backstageEntityMappingPath').notNullable();
|
|
14
|
+
table.string('pagerdutySubdomain').notNullable();
|
|
15
|
+
table.string('description');
|
|
16
|
+
table.index(['pagerdutyCustomFieldId'], 'pagerduty_custom_field_id_idx');
|
|
17
|
+
table.dateTime('updatedAt').defaultTo(knex.fn.now());
|
|
18
|
+
table.dateTime('createdAt').defaultTo(knex.fn.now());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param { import("knex").Knex } knex
|
|
24
|
+
* @returns { Promise<void> }
|
|
25
|
+
*/
|
|
26
|
+
exports.down = async function down(knex) {
|
|
27
|
+
await knex.schema.alterTable('pagerduty_custom_fields', table => {
|
|
28
|
+
table.dropIndex([], 'pagerduty_custom_field_id_idx');
|
|
29
|
+
});
|
|
30
|
+
return knex.schema.dropTable('pagerduty_custom_fields');
|
|
31
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = async function up(knex) {
|
|
6
|
+
await knex.schema.alterTable('pagerduty_custom_fields', table => {
|
|
7
|
+
table.unique(
|
|
8
|
+
['backstageEntityMappingPath', 'pagerdutySubdomain'],
|
|
9
|
+
{ indexName: 'pagerduty_cf_entitypath_subdomain_unique' },
|
|
10
|
+
);
|
|
11
|
+
table.unique(
|
|
12
|
+
['pagerdutyCustomFieldDisplayName', 'pagerdutySubdomain'],
|
|
13
|
+
{ indexName: 'pagerduty_cf_displayname_subdomain_unique' },
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param { import("knex").Knex } knex
|
|
20
|
+
* @returns { Promise<void> }
|
|
21
|
+
*/
|
|
22
|
+
exports.down = async function down(knex) {
|
|
23
|
+
await knex.schema.alterTable('pagerduty_custom_fields', table => {
|
|
24
|
+
table.dropUnique([], 'pagerduty_cf_entitypath_subdomain_unique');
|
|
25
|
+
table.dropUnique([], 'pagerduty_cf_displayname_subdomain_unique');
|
|
26
|
+
});
|
|
27
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = async function up(knex) {
|
|
6
|
+
return knex.schema.createTable('pagerduty_custom_field_sync_logs', table => {
|
|
7
|
+
table.increments('id').primary();
|
|
8
|
+
table.dateTime('timestamp').notNullable().defaultTo(knex.fn.now());
|
|
9
|
+
table.string('errorCode').notNullable();
|
|
10
|
+
table.string('customFieldId').notNullable();
|
|
11
|
+
table.string('customFieldName').notNullable();
|
|
12
|
+
table.string('entityPath').notNullable();
|
|
13
|
+
table.string('serviceId').notNullable();
|
|
14
|
+
table.string('serviceName').notNullable();
|
|
15
|
+
table.text('errorMessage').notNullable();
|
|
16
|
+
table.string('subdomain').notNullable();
|
|
17
|
+
table.index(['subdomain', 'timestamp'], 'sync_logs_subdomain_timestamp_idx');
|
|
18
|
+
table.index(['serviceId'], 'sync_logs_service_id_idx');
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param { import("knex").Knex } knex
|
|
24
|
+
* @returns { Promise<void> }
|
|
25
|
+
*/
|
|
26
|
+
exports.down = async function down(knex) {
|
|
27
|
+
await knex.schema.alterTable('pagerduty_custom_field_sync_logs', table => {
|
|
28
|
+
table.dropIndex([], 'sync_logs_subdomain_timestamp_idx');
|
|
29
|
+
table.dropIndex([], 'sync_logs_service_id_idx');
|
|
30
|
+
});
|
|
31
|
+
return knex.schema.dropTable('pagerduty_custom_field_sync_logs');
|
|
32
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = async function up(knex) {
|
|
6
|
+
return knex.schema.alterTable('pagerduty_custom_field_sync_logs', table => {
|
|
7
|
+
table.index(['timestamp'], 'sync_logs_timestamp_idx');
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param { import("knex").Knex } knex
|
|
13
|
+
* @returns { Promise<void> }
|
|
14
|
+
*/
|
|
15
|
+
exports.down = async function down(knex) {
|
|
16
|
+
return knex.schema.alterTable('pagerduty_custom_field_sync_logs', table => {
|
|
17
|
+
table.dropIndex([], 'sync_logs_timestamp_idx');
|
|
18
|
+
});
|
|
19
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagerduty/backstage-plugin-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"main": "dist/index.cjs.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"postpack": "backstage-cli package postpack"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@backstage/backend-defaults": "^0.
|
|
41
|
-
"@backstage/backend-plugin-api": "^1.
|
|
42
|
-
"@backstage/catalog-client": "^1.
|
|
43
|
-
"@pagerduty/backstage-plugin-common": "~0.
|
|
40
|
+
"@backstage/backend-defaults": "^0.17.2",
|
|
41
|
+
"@backstage/backend-plugin-api": "^1.9.1",
|
|
42
|
+
"@backstage/catalog-client": "^1.15.2",
|
|
43
|
+
"@pagerduty/backstage-plugin-common": "~0.5.0",
|
|
44
44
|
"@skyra/jaro-winkler": "^1.1.1",
|
|
45
45
|
"@types/express": "^4.17.6",
|
|
46
46
|
"express": "^4.20.0",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"uuid": "^9.0.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@backstage/backend-test-utils": "^1.
|
|
55
|
-
"@backstage/cli": "^0.
|
|
54
|
+
"@backstage/backend-test-utils": "^1.11.3",
|
|
55
|
+
"@backstage/cli": "^0.36.2",
|
|
56
56
|
"jest-mock": "^30.0.2",
|
|
57
57
|
"supertest": "^7.1.3"
|
|
58
58
|
},
|