@pagerduty/backstage-plugin-backend 0.8.2 → 0.9.0-next.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/dist/index.cjs.js +549 -42
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/migrations/20240722_add_settings_table.js +27 -0
- package/package.json +12 -14
package/dist/index.cjs.js
CHANGED
|
@@ -39,7 +39,8 @@ var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
|
39
39
|
let authPersistence;
|
|
40
40
|
let isLegacyConfig$1 = false;
|
|
41
41
|
async function getAuthToken(accountId) {
|
|
42
|
-
|
|
42
|
+
var _a;
|
|
43
|
+
if (!(authPersistence == null ? void 0 : authPersistence.accountTokens)) {
|
|
43
44
|
await loadAuthConfig(authPersistence.config, authPersistence.logger);
|
|
44
45
|
}
|
|
45
46
|
if (isLegacyConfig$1) {
|
|
@@ -52,7 +53,7 @@ async function getAuthToken(accountId) {
|
|
|
52
53
|
return authPersistence.accountTokens[accountId].authToken;
|
|
53
54
|
}
|
|
54
55
|
} else {
|
|
55
|
-
const defaultFallback = authPersistence.defaultAccount
|
|
56
|
+
const defaultFallback = (_a = authPersistence.defaultAccount) != null ? _a : "";
|
|
56
57
|
if (authPersistence.accountTokens[defaultFallback].authToken !== "" && authPersistence.accountTokens[defaultFallback].authToken.includes("Bearer") && authPersistence.accountTokens[defaultFallback].authTokenExpiryDate > Date.now() || authPersistence.accountTokens[defaultFallback].authToken !== "" && authPersistence.accountTokens[defaultFallback].authToken.includes("Token")) {
|
|
57
58
|
return authPersistence.accountTokens[defaultFallback].authToken;
|
|
58
59
|
}
|
|
@@ -61,6 +62,7 @@ async function getAuthToken(accountId) {
|
|
|
61
62
|
return "";
|
|
62
63
|
}
|
|
63
64
|
async function loadAuthConfig(config, logger) {
|
|
65
|
+
var _a;
|
|
64
66
|
try {
|
|
65
67
|
const defaultAccountId = "default";
|
|
66
68
|
authPersistence = {
|
|
@@ -82,7 +84,7 @@ async function loadAuthConfig(config, logger) {
|
|
|
82
84
|
config.getString("pagerDuty.oauth.clientId"),
|
|
83
85
|
config.getString("pagerDuty.oauth.clientSecret"),
|
|
84
86
|
config.getString("pagerDuty.oauth.subDomain"),
|
|
85
|
-
config.getOptionalString("pagerDuty.oauth.region")
|
|
87
|
+
(_a = config.getOptionalString("pagerDuty.oauth.region")) != null ? _a : "us"
|
|
86
88
|
);
|
|
87
89
|
authPersistence.accountTokens[defaultAccountId] = tokenInfo;
|
|
88
90
|
logger.info("PagerDuty OAuth configuration loaded successfully.");
|
|
@@ -99,11 +101,12 @@ async function loadAuthConfig(config, logger) {
|
|
|
99
101
|
logger.info("New PagerDuty accounts configuration found in config file.");
|
|
100
102
|
isLegacyConfig$1 = false;
|
|
101
103
|
const accounts = config.getOptional("pagerDuty.accounts");
|
|
102
|
-
if (accounts && accounts
|
|
104
|
+
if (accounts && (accounts == null ? void 0 : accounts.length) === 1) {
|
|
103
105
|
logger.info("Only one account found in config file. Setting it as default.");
|
|
104
106
|
authPersistence.defaultAccount = accounts[0].id;
|
|
105
107
|
}
|
|
106
|
-
accounts
|
|
108
|
+
accounts == null ? void 0 : accounts.forEach(async (account) => {
|
|
109
|
+
var _a2;
|
|
107
110
|
const maskedAccountId = maskString(account.id);
|
|
108
111
|
if (account.isDefault && !authPersistence.defaultAccount) {
|
|
109
112
|
logger.info(`Default account found in config file. Setting it as default.`);
|
|
@@ -120,7 +123,7 @@ async function loadAuthConfig(config, logger) {
|
|
|
120
123
|
account.oauth.clientId,
|
|
121
124
|
account.oauth.clientSecret,
|
|
122
125
|
account.oauth.subDomain,
|
|
123
|
-
account.oauth.region
|
|
126
|
+
(_a2 = account.oauth.region) != null ? _a2 : "us"
|
|
124
127
|
);
|
|
125
128
|
authPersistence.accountTokens[account.id] = tokenInfo;
|
|
126
129
|
logger.info(`PagerDuty OAuth configuration loaded successfully for account ${maskedAccountId}.`);
|
|
@@ -201,15 +204,17 @@ const EndpointConfig = {};
|
|
|
201
204
|
let fallbackEndpointConfig;
|
|
202
205
|
let isLegacyConfig = false;
|
|
203
206
|
function setFallbackEndpointConfig(account) {
|
|
207
|
+
var _a, _b;
|
|
204
208
|
fallbackEndpointConfig = {
|
|
205
|
-
eventsBaseUrl: account.eventsBaseUrl
|
|
206
|
-
apiBaseUrl: account.apiBaseUrl
|
|
209
|
+
eventsBaseUrl: (_a = account.eventsBaseUrl) != null ? _a : "https://events.pagerduty.com/v2",
|
|
210
|
+
apiBaseUrl: (_b = account.apiBaseUrl) != null ? _b : "https://api.pagerduty.com"
|
|
207
211
|
};
|
|
208
212
|
}
|
|
209
213
|
function insertEndpointConfig(account) {
|
|
214
|
+
var _a, _b;
|
|
210
215
|
EndpointConfig[account.id] = {
|
|
211
|
-
eventsBaseUrl: account.eventsBaseUrl
|
|
212
|
-
apiBaseUrl: account.apiBaseUrl
|
|
216
|
+
eventsBaseUrl: (_a = account.eventsBaseUrl) != null ? _a : "https://events.pagerduty.com/v2",
|
|
217
|
+
apiBaseUrl: (_b = account.apiBaseUrl) != null ? _b : "https://api.pagerduty.com"
|
|
213
218
|
};
|
|
214
219
|
}
|
|
215
220
|
function loadPagerDutyEndpointsFromConfig(config, logger) {
|
|
@@ -217,7 +222,7 @@ function loadPagerDutyEndpointsFromConfig(config, logger) {
|
|
|
217
222
|
logger.debug(`New accounts configuration detected. Loading PagerDuty endpoints from config.`);
|
|
218
223
|
isLegacyConfig = false;
|
|
219
224
|
const accounts = config.getOptional("pagerDuty.accounts");
|
|
220
|
-
if (accounts
|
|
225
|
+
if ((accounts == null ? void 0 : accounts.length) === 1) {
|
|
221
226
|
logger.debug(`Single account configuration detected. Loading PagerDuty endpoints from config to 'default'.`);
|
|
222
227
|
EndpointConfig.default = {
|
|
223
228
|
eventsBaseUrl: accounts[0].eventsBaseUrl !== void 0 ? accounts[0].eventsBaseUrl : "https://events.pagerduty.com/v2",
|
|
@@ -225,7 +230,7 @@ function loadPagerDutyEndpointsFromConfig(config, logger) {
|
|
|
225
230
|
};
|
|
226
231
|
} else {
|
|
227
232
|
logger.debug(`Multiple account configuration detected. Loading PagerDuty endpoints from config.`);
|
|
228
|
-
accounts
|
|
233
|
+
accounts == null ? void 0 : accounts.forEach((account) => {
|
|
229
234
|
if (account.isDefault) {
|
|
230
235
|
setFallbackEndpointConfig(account);
|
|
231
236
|
}
|
|
@@ -250,7 +255,130 @@ function getApiBaseUrl(account) {
|
|
|
250
255
|
}
|
|
251
256
|
return fallbackEndpointConfig.apiBaseUrl;
|
|
252
257
|
}
|
|
258
|
+
async function addServiceRelationsToService(serviceRelations, account) {
|
|
259
|
+
let response;
|
|
260
|
+
const options = {
|
|
261
|
+
method: "POST",
|
|
262
|
+
headers: {
|
|
263
|
+
Authorization: await getAuthToken(account),
|
|
264
|
+
"Accept": "application/vnd.pagerduty+json;version=2",
|
|
265
|
+
"Content-Type": "application/json"
|
|
266
|
+
},
|
|
267
|
+
body: JSON.stringify({
|
|
268
|
+
relationships: serviceRelations
|
|
269
|
+
})
|
|
270
|
+
};
|
|
271
|
+
const apiBaseUrl = getApiBaseUrl(account);
|
|
272
|
+
const baseUrl = `${apiBaseUrl}/service_dependencies/associate`;
|
|
273
|
+
console.log(`Adding service relations: ${JSON.stringify({ relationships: serviceRelations })}`);
|
|
274
|
+
try {
|
|
275
|
+
response = await fetchWithRetries(`${baseUrl}`, options);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
throw new Error(`Failed to retrieve service dependencies: ${error}`);
|
|
278
|
+
}
|
|
279
|
+
if (response.status >= 500) {
|
|
280
|
+
throw new backstagePluginCommon.HttpError(`Failed to add service dependencies. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
281
|
+
}
|
|
282
|
+
switch (response.status) {
|
|
283
|
+
case 400:
|
|
284
|
+
throw new backstagePluginCommon.HttpError("Failed to add service dependencies. Caller provided invalid arguments. Please review the response for error details. Retrying with the same arguments will not work.", 400);
|
|
285
|
+
case 401:
|
|
286
|
+
throw new backstagePluginCommon.HttpError("Failed to add service dependencies. Caller did not supply credentials or did not provide the correct credentials. If you are using an API key, it may be invalid or your Authorization header may be malformed.", 401);
|
|
287
|
+
case 403:
|
|
288
|
+
throw new backstagePluginCommon.HttpError("Failed to add service dependencies. Caller is not authorized to view the requested resource. While your authentication is valid, the authenticated user or token does not have permission to perform this action.", 403);
|
|
289
|
+
case 404:
|
|
290
|
+
throw new backstagePluginCommon.HttpError("Failed to add service dependencies. The requested resource was not found.", 404);
|
|
291
|
+
}
|
|
292
|
+
let result;
|
|
293
|
+
try {
|
|
294
|
+
result = await response.json();
|
|
295
|
+
return result.relationships;
|
|
296
|
+
} catch (error) {
|
|
297
|
+
throw new backstagePluginCommon.HttpError(`Failed to parse service dependency information: ${error}`, 500);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async function removeServiceRelationsFromService(serviceRelations, account) {
|
|
301
|
+
let response;
|
|
302
|
+
const options = {
|
|
303
|
+
method: "POST",
|
|
304
|
+
headers: {
|
|
305
|
+
Authorization: await getAuthToken(account),
|
|
306
|
+
"Accept": "application/vnd.pagerduty+json;version=2",
|
|
307
|
+
"Content-Type": "application/json"
|
|
308
|
+
},
|
|
309
|
+
body: JSON.stringify({
|
|
310
|
+
relationships: serviceRelations
|
|
311
|
+
})
|
|
312
|
+
};
|
|
313
|
+
const apiBaseUrl = getApiBaseUrl(account);
|
|
314
|
+
const baseUrl = `${apiBaseUrl}/service_dependencies/disassociate`;
|
|
315
|
+
console.log(`Removing service relations: ${JSON.stringify({ relationships: serviceRelations })}`);
|
|
316
|
+
try {
|
|
317
|
+
response = await fetchWithRetries(`${baseUrl}`, options);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
throw new Error(`Failed to retrieve service dependencies: ${error}`);
|
|
320
|
+
}
|
|
321
|
+
if (response.status >= 500) {
|
|
322
|
+
throw new backstagePluginCommon.HttpError(`Failed to remove service dependencies. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
323
|
+
}
|
|
324
|
+
switch (response.status) {
|
|
325
|
+
case 400:
|
|
326
|
+
throw new backstagePluginCommon.HttpError("Failed to remove service dependencies. Caller provided invalid arguments. Please review the response for error details. Retrying with the same arguments will not work.", 400);
|
|
327
|
+
case 401:
|
|
328
|
+
throw new backstagePluginCommon.HttpError("Failed to remove service dependencies. Caller did not supply credentials or did not provide the correct credentials. If you are using an API key, it may be invalid or your Authorization header may be malformed.", 401);
|
|
329
|
+
case 403:
|
|
330
|
+
throw new backstagePluginCommon.HttpError("Failed to remove service dependencies. Caller is not authorized to view the requested resource. While your authentication is valid, the authenticated user or token does not have permission to perform this action.", 403);
|
|
331
|
+
case 404:
|
|
332
|
+
throw new backstagePluginCommon.HttpError("Failed to remove service dependencies. The requested resource was not found.", 404);
|
|
333
|
+
}
|
|
334
|
+
let result;
|
|
335
|
+
try {
|
|
336
|
+
result = await response.json();
|
|
337
|
+
return result.relationships;
|
|
338
|
+
} catch (error) {
|
|
339
|
+
throw new backstagePluginCommon.HttpError(`Failed to parse service dependency information: ${error}`, 500);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
async function getServiceRelationshipsById(serviceId, account) {
|
|
343
|
+
let response;
|
|
344
|
+
const options = {
|
|
345
|
+
method: "GET",
|
|
346
|
+
headers: {
|
|
347
|
+
Authorization: await getAuthToken(account),
|
|
348
|
+
"Accept": "application/vnd.pagerduty+json;version=2",
|
|
349
|
+
"Content-Type": "application/json"
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const apiBaseUrl = getApiBaseUrl(account);
|
|
353
|
+
const baseUrl = `${apiBaseUrl}/service_dependencies/technical_services/${serviceId}`;
|
|
354
|
+
try {
|
|
355
|
+
response = await fetchWithRetries(`${baseUrl}`, options);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
throw new Error(`Failed to retrieve service dependencies: ${error}`);
|
|
358
|
+
}
|
|
359
|
+
if (response.status >= 500) {
|
|
360
|
+
throw new backstagePluginCommon.HttpError(`Failed to list service dependencies. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
361
|
+
}
|
|
362
|
+
switch (response.status) {
|
|
363
|
+
case 400:
|
|
364
|
+
throw new backstagePluginCommon.HttpError("Failed to list service dependencies. Caller provided invalid arguments. Please review the response for error details. Retrying with the same arguments will not work.", 400);
|
|
365
|
+
case 401:
|
|
366
|
+
throw new backstagePluginCommon.HttpError("Failed to list service dependencies. Caller did not supply credentials or did not provide the correct credentials. If you are using an API key, it may be invalid or your Authorization header may be malformed.", 401);
|
|
367
|
+
case 403:
|
|
368
|
+
throw new backstagePluginCommon.HttpError("Failed to list service dependencies. Caller is not authorized to view the requested resource. While your authentication is valid, the authenticated user or token does not have permission to perform this action.", 403);
|
|
369
|
+
case 404:
|
|
370
|
+
throw new backstagePluginCommon.HttpError("Failed to list service dependencies. The requested resource was not found.", 404);
|
|
371
|
+
}
|
|
372
|
+
let result;
|
|
373
|
+
try {
|
|
374
|
+
result = await response.json();
|
|
375
|
+
return result.relationships;
|
|
376
|
+
} catch (error) {
|
|
377
|
+
throw new backstagePluginCommon.HttpError(`Failed to parse service dependency information: ${error}`, 500);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
253
380
|
async function getEscalationPolicies(offset, limit, account) {
|
|
381
|
+
var _a;
|
|
254
382
|
let response;
|
|
255
383
|
const params = `total=true&sort_by=name&offset=${offset}&limit=${limit}`;
|
|
256
384
|
const options = {
|
|
@@ -264,10 +392,13 @@ async function getEscalationPolicies(offset, limit, account) {
|
|
|
264
392
|
const apiBaseUrl = getApiBaseUrl(account);
|
|
265
393
|
const baseUrl = `${apiBaseUrl}/escalation_policies`;
|
|
266
394
|
try {
|
|
267
|
-
response = await
|
|
395
|
+
response = await fetchWithRetries(`${baseUrl}?${params}`, options);
|
|
268
396
|
} catch (error) {
|
|
269
397
|
throw new Error(`Failed to retrieve escalation policies: ${error}`);
|
|
270
398
|
}
|
|
399
|
+
if (response.status >= 500) {
|
|
400
|
+
throw new backstagePluginCommon.HttpError(`Failed to list escalation policies. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
401
|
+
}
|
|
271
402
|
switch (response.status) {
|
|
272
403
|
case 400:
|
|
273
404
|
throw new backstagePluginCommon.HttpError("Failed to list escalation policies. Caller provided invalid arguments.", 400);
|
|
@@ -281,7 +412,7 @@ async function getEscalationPolicies(offset, limit, account) {
|
|
|
281
412
|
let result;
|
|
282
413
|
try {
|
|
283
414
|
result = await response.json();
|
|
284
|
-
return [result.more
|
|
415
|
+
return [(_a = result.more) != null ? _a : false, result.escalation_policies];
|
|
285
416
|
} catch (error) {
|
|
286
417
|
throw new backstagePluginCommon.HttpError(`Failed to parse escalation policy information: ${error}`, 500);
|
|
287
418
|
}
|
|
@@ -333,10 +464,13 @@ async function getOncallUsers(escalationPolicy, account) {
|
|
|
333
464
|
const apiBaseUrl = getApiBaseUrl(account);
|
|
334
465
|
const baseUrl = `${apiBaseUrl}/oncalls`;
|
|
335
466
|
try {
|
|
336
|
-
response = await
|
|
467
|
+
response = await fetchWithRetries(`${baseUrl}?${params}`, options);
|
|
337
468
|
} catch (error) {
|
|
338
469
|
throw new Error(`Failed to retrieve oncalls: ${error}`);
|
|
339
470
|
}
|
|
471
|
+
if (response.status >= 500) {
|
|
472
|
+
throw new backstagePluginCommon.HttpError(`Failed to list oncalls. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
473
|
+
}
|
|
340
474
|
switch (response.status) {
|
|
341
475
|
case 400:
|
|
342
476
|
throw new backstagePluginCommon.HttpError("Failed to list oncalls. Caller provided invalid arguments.", 400);
|
|
@@ -389,10 +523,13 @@ async function getServiceById(serviceId, account) {
|
|
|
389
523
|
const apiBaseUrl = getApiBaseUrl(account);
|
|
390
524
|
const baseUrl = `${apiBaseUrl}/services`;
|
|
391
525
|
try {
|
|
392
|
-
response = await
|
|
526
|
+
response = await fetchWithRetries(`${baseUrl}/${serviceId}?${params}`, options);
|
|
393
527
|
} catch (error) {
|
|
394
528
|
throw new Error(`Failed to retrieve service: ${error}`);
|
|
395
529
|
}
|
|
530
|
+
if (response.status >= 500) {
|
|
531
|
+
throw new backstagePluginCommon.HttpError(`Failed to get service. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
532
|
+
}
|
|
396
533
|
switch (response.status) {
|
|
397
534
|
case 400:
|
|
398
535
|
throw new backstagePluginCommon.HttpError("Failed to get service. Caller provided invalid arguments.", 400);
|
|
@@ -426,10 +563,13 @@ async function getServiceByIntegrationKey(integrationKey, account) {
|
|
|
426
563
|
const apiBaseUrl = getApiBaseUrl(account);
|
|
427
564
|
const baseUrl = `${apiBaseUrl}/services`;
|
|
428
565
|
try {
|
|
429
|
-
response = await
|
|
566
|
+
response = await fetchWithRetries(`${baseUrl}?${params}`, options);
|
|
430
567
|
} catch (error) {
|
|
431
568
|
throw new Error(`Failed to retrieve service: ${error}`);
|
|
432
569
|
}
|
|
570
|
+
if (response.status >= 500) {
|
|
571
|
+
throw new backstagePluginCommon.HttpError(`Failed to get service. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
572
|
+
}
|
|
433
573
|
switch (response.status) {
|
|
434
574
|
case 400:
|
|
435
575
|
throw new backstagePluginCommon.HttpError("Failed to get service. Caller provided invalid arguments.", 400);
|
|
@@ -474,7 +614,10 @@ async function getAllServices() {
|
|
|
474
614
|
try {
|
|
475
615
|
do {
|
|
476
616
|
const paginatedUrl = `${baseUrl}?${params}&offset=${offset}&limit=${limit}`;
|
|
477
|
-
response = await
|
|
617
|
+
response = await fetchWithRetries(paginatedUrl, options);
|
|
618
|
+
if (response.status >= 500) {
|
|
619
|
+
throw new backstagePluginCommon.HttpError(`Failed to get services. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
620
|
+
}
|
|
478
621
|
switch (response.status) {
|
|
479
622
|
case 400:
|
|
480
623
|
throw new backstagePluginCommon.HttpError("Failed to get services. Caller provided invalid arguments.", 400);
|
|
@@ -513,10 +656,13 @@ async function getChangeEvents(serviceId, account) {
|
|
|
513
656
|
const apiBaseUrl = getApiBaseUrl(account);
|
|
514
657
|
const baseUrl = `${apiBaseUrl}/services`;
|
|
515
658
|
try {
|
|
516
|
-
response = await
|
|
659
|
+
response = await fetchWithRetries(`${baseUrl}/${serviceId}/change_events?${params}`, options);
|
|
517
660
|
} catch (error) {
|
|
518
661
|
throw new Error(`Failed to retrieve change events for service: ${error}`);
|
|
519
662
|
}
|
|
663
|
+
if (response.status >= 500) {
|
|
664
|
+
throw new backstagePluginCommon.HttpError(`Failed to get change events for service. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
665
|
+
}
|
|
520
666
|
switch (response.status) {
|
|
521
667
|
case 400:
|
|
522
668
|
throw new backstagePluginCommon.HttpError("Failed to get change events for service. Caller provided invalid arguments.", 400);
|
|
@@ -549,10 +695,13 @@ async function getIncidents(serviceId, account) {
|
|
|
549
695
|
const apiBaseUrl = getApiBaseUrl(account);
|
|
550
696
|
const baseUrl = `${apiBaseUrl}/incidents`;
|
|
551
697
|
try {
|
|
552
|
-
response = await
|
|
698
|
+
response = await fetchWithRetries(`${baseUrl}?${params}`, options);
|
|
553
699
|
} catch (error) {
|
|
554
700
|
throw new Error(`Failed to retrieve incidents for service: ${error}`);
|
|
555
701
|
}
|
|
702
|
+
if (response.status >= 500) {
|
|
703
|
+
throw new backstagePluginCommon.HttpError(`Failed to get incidents for service. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
704
|
+
}
|
|
556
705
|
switch (response.status) {
|
|
557
706
|
case 400:
|
|
558
707
|
throw new backstagePluginCommon.HttpError("Failed to get incidents for service. Caller provided invalid arguments.", 400);
|
|
@@ -586,10 +735,13 @@ async function getServiceStandards(serviceId, account) {
|
|
|
586
735
|
const apiBaseUrl = getApiBaseUrl(account);
|
|
587
736
|
const baseUrl = `${apiBaseUrl}/standards/scores/technical_services/${serviceId}`;
|
|
588
737
|
try {
|
|
589
|
-
response = await
|
|
738
|
+
response = await fetchWithRetries(baseUrl, options);
|
|
590
739
|
} catch (error) {
|
|
591
740
|
throw new Error(`Failed to retrieve service standards for service: ${error}`);
|
|
592
741
|
}
|
|
742
|
+
if (response.status >= 500) {
|
|
743
|
+
throw new backstagePluginCommon.HttpError(`Failed to get service standards for service. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
744
|
+
}
|
|
593
745
|
switch (response.status) {
|
|
594
746
|
case 401:
|
|
595
747
|
throw new backstagePluginCommon.HttpError("Failed to get service standards for service. Caller did not supply credentials or did not provide the correct credentials.", 401);
|
|
@@ -630,10 +782,13 @@ async function getServiceMetrics(serviceId, account) {
|
|
|
630
782
|
const apiBaseUrl = getApiBaseUrl(account);
|
|
631
783
|
const baseUrl = `${apiBaseUrl}/analytics/metrics/incidents/services`;
|
|
632
784
|
try {
|
|
633
|
-
response = await
|
|
785
|
+
response = await fetchWithRetries(baseUrl, options);
|
|
634
786
|
} catch (error) {
|
|
635
787
|
throw new Error(`Failed to retrieve service metrics for service: ${error}`);
|
|
636
788
|
}
|
|
789
|
+
if (response.status >= 500) {
|
|
790
|
+
throw new backstagePluginCommon.HttpError(`Failed to get service metrics for service. PagerDuty API returned a server error. Retrying with the same arguments will not work.`, response.status);
|
|
791
|
+
}
|
|
637
792
|
switch (response.status) {
|
|
638
793
|
case 400:
|
|
639
794
|
throw new backstagePluginCommon.HttpError("Failed to get service metrics for service. Caller provided invalid arguments. Please review the response for error details. Retrying with the same arguments will not work.", 400);
|
|
@@ -647,13 +802,84 @@ async function getServiceMetrics(serviceId, account) {
|
|
|
647
802
|
throw new backstagePluginCommon.HttpError(`Failed to parse service metrics information: ${error}`, 500);
|
|
648
803
|
}
|
|
649
804
|
}
|
|
805
|
+
async function createServiceIntegration({ serviceId, vendorId, account }) {
|
|
806
|
+
var _a;
|
|
807
|
+
let response;
|
|
808
|
+
const apiBaseUrl = getApiBaseUrl(account);
|
|
809
|
+
const baseUrl = `${apiBaseUrl}/services`;
|
|
810
|
+
const token = await getAuthToken(account);
|
|
811
|
+
const options = {
|
|
812
|
+
method: "POST",
|
|
813
|
+
body: JSON.stringify({
|
|
814
|
+
integration: {
|
|
815
|
+
name: "Backstage",
|
|
816
|
+
service: {
|
|
817
|
+
id: serviceId,
|
|
818
|
+
type: "service_reference"
|
|
819
|
+
},
|
|
820
|
+
vendor: {
|
|
821
|
+
id: vendorId,
|
|
822
|
+
type: "vendor_reference"
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}),
|
|
826
|
+
headers: {
|
|
827
|
+
Authorization: token,
|
|
828
|
+
"Accept": "application/vnd.pagerduty+json;version=2",
|
|
829
|
+
"Content-Type": "application/json"
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
try {
|
|
833
|
+
response = await fetchWithRetries(`${baseUrl}/${serviceId}/integrations`, options);
|
|
834
|
+
} catch (error) {
|
|
835
|
+
throw new Error(`Failed to create service integration: ${error}`);
|
|
836
|
+
}
|
|
837
|
+
if (response.status >= 500) {
|
|
838
|
+
throw new Error(`Failed to create service integration. PagerDuty API returned a server error. Retrying with the same arguments will not work.`);
|
|
839
|
+
}
|
|
840
|
+
switch (response.status) {
|
|
841
|
+
case 400:
|
|
842
|
+
throw new Error(`Failed to create service integration. Caller provided invalid arguments.`);
|
|
843
|
+
case 401:
|
|
844
|
+
throw new Error(`Failed to create service integration. Caller did not supply credentials or did not provide the correct credentials.`);
|
|
845
|
+
case 403:
|
|
846
|
+
throw new Error(`Failed to create service integration. Caller is not authorized to view the requested resource.`);
|
|
847
|
+
case 429:
|
|
848
|
+
throw new Error(`Failed to create service integration. Rate limit exceeded.`);
|
|
849
|
+
}
|
|
850
|
+
let result;
|
|
851
|
+
try {
|
|
852
|
+
result = await response.json();
|
|
853
|
+
return (_a = result.integration.integration_key) != null ? _a : "";
|
|
854
|
+
} catch (error) {
|
|
855
|
+
throw new Error(`Failed to parse service information: ${error}`);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
async function fetchWithRetries(url, options) {
|
|
859
|
+
let response;
|
|
860
|
+
let error = new Error();
|
|
861
|
+
const maxRetries = 5;
|
|
862
|
+
const delay = 1e3;
|
|
863
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
864
|
+
try {
|
|
865
|
+
response = await fetch__default.default(url, options);
|
|
866
|
+
return response;
|
|
867
|
+
} catch (e) {
|
|
868
|
+
error = e;
|
|
869
|
+
}
|
|
870
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
871
|
+
}
|
|
872
|
+
throw new Error(`Failed to fetch data after ${maxRetries} retries. Last error: ${error}`);
|
|
873
|
+
}
|
|
650
874
|
|
|
651
875
|
async function createComponentEntitiesReferenceDict({ items: componentEntities }) {
|
|
652
876
|
const componentEntitiesDict = {};
|
|
653
877
|
await Promise.all(componentEntities.map(async (entity) => {
|
|
654
|
-
|
|
655
|
-
const
|
|
656
|
-
const
|
|
878
|
+
var _a;
|
|
879
|
+
const annotations = JSON.parse(JSON.stringify(entity.metadata.annotations));
|
|
880
|
+
const serviceId = annotations["pagerduty.com/service-id"];
|
|
881
|
+
const integrationKey = annotations["pagerduty.com/integration-key"];
|
|
882
|
+
const account = (_a = annotations["pagerduty.com/account"]) != null ? _a : "";
|
|
657
883
|
if (serviceId !== void 0 && serviceId !== "") {
|
|
658
884
|
componentEntitiesDict[serviceId] = {
|
|
659
885
|
ref: `${entity.kind}:${entity.metadata.namespace}/${entity.metadata.name}`.toLowerCase(),
|
|
@@ -676,8 +902,9 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
676
902
|
mappings: []
|
|
677
903
|
};
|
|
678
904
|
pagerDutyServices.forEach((service) => {
|
|
679
|
-
|
|
680
|
-
const
|
|
905
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A;
|
|
906
|
+
const entityRef = (_a = componentEntitiesDict[service.id]) == null ? void 0 : _a.ref;
|
|
907
|
+
const entityName = (_b = componentEntitiesDict[service.id]) == null ? void 0 : _b.name;
|
|
681
908
|
const entityMapping = entityMappings.find((mapping) => mapping.serviceId === service.id);
|
|
682
909
|
if (entityMapping) {
|
|
683
910
|
if (entityRef === void 0) {
|
|
@@ -689,13 +916,13 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
689
916
|
serviceId: entityMapping.serviceId,
|
|
690
917
|
status: "NotMapped",
|
|
691
918
|
serviceName: service.name,
|
|
692
|
-
team: service.teams
|
|
919
|
+
team: (_e = (_d = (_c = service.teams) == null ? void 0 : _c[0]) == null ? void 0 : _d.name) != null ? _e : "",
|
|
693
920
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
694
921
|
serviceUrl: service.html_url,
|
|
695
922
|
account: service.account
|
|
696
923
|
});
|
|
697
924
|
} else {
|
|
698
|
-
const entityRefName = componentEntities.items.find((entity) => `${entity.kind}:${entity.metadata.namespace}/${entity.metadata.name}`.toLowerCase() === entityMapping.entityRef)
|
|
925
|
+
const entityRefName = (_g = (_f = componentEntities.items.find((entity) => `${entity.kind}:${entity.metadata.namespace}/${entity.metadata.name}`.toLowerCase() === entityMapping.entityRef)) == null ? void 0 : _f.metadata.name) != null ? _g : "";
|
|
699
926
|
result.mappings.push({
|
|
700
927
|
entityRef: entityMapping.entityRef,
|
|
701
928
|
entityName: entityRefName,
|
|
@@ -703,14 +930,14 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
703
930
|
integrationKey: entityMapping.integrationKey,
|
|
704
931
|
status: "OutOfSync",
|
|
705
932
|
serviceName: service.name,
|
|
706
|
-
team: service.teams
|
|
933
|
+
team: (_j = (_i = (_h = service.teams) == null ? void 0 : _h[0]) == null ? void 0 : _i.name) != null ? _j : "",
|
|
707
934
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
708
935
|
serviceUrl: service.html_url,
|
|
709
936
|
account: service.account
|
|
710
937
|
});
|
|
711
938
|
}
|
|
712
939
|
} else if (entityRef !== entityMapping.entityRef) {
|
|
713
|
-
const entityRefName = componentEntities.items.find((entity) => `${entity.kind}:${entity.metadata.namespace}/${entity.metadata.name}`.toLowerCase() === entityMapping.entityRef)
|
|
940
|
+
const entityRefName = (_l = (_k = componentEntities.items.find((entity) => `${entity.kind}:${entity.metadata.namespace}/${entity.metadata.name}`.toLowerCase() === entityMapping.entityRef)) == null ? void 0 : _k.metadata.name) != null ? _l : "";
|
|
714
941
|
result.mappings.push({
|
|
715
942
|
entityRef: entityMapping.entityRef !== "" ? entityMapping.entityRef : "",
|
|
716
943
|
entityName: entityMapping.entityRef !== "" ? entityRefName : "",
|
|
@@ -718,7 +945,7 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
718
945
|
integrationKey: entityMapping.integrationKey,
|
|
719
946
|
status: "OutOfSync",
|
|
720
947
|
serviceName: service.name,
|
|
721
|
-
team: service.teams
|
|
948
|
+
team: (_o = (_n = (_m = service.teams) == null ? void 0 : _m[0]) == null ? void 0 : _n.name) != null ? _o : "",
|
|
722
949
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
723
950
|
serviceUrl: service.html_url,
|
|
724
951
|
account: service.account
|
|
@@ -731,7 +958,7 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
731
958
|
integrationKey: entityMapping.integrationKey,
|
|
732
959
|
status: "InSync",
|
|
733
960
|
serviceName: service.name,
|
|
734
|
-
team: service.teams
|
|
961
|
+
team: (_r = (_q = (_p = service.teams) == null ? void 0 : _p[0]) == null ? void 0 : _q.name) != null ? _r : "",
|
|
735
962
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
736
963
|
serviceUrl: service.html_url,
|
|
737
964
|
account: service.account
|
|
@@ -739,7 +966,10 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
739
966
|
}
|
|
740
967
|
} else {
|
|
741
968
|
const backstageVendorId = "PRO19CT";
|
|
742
|
-
const backstageIntegrationKey = service.integrations
|
|
969
|
+
const backstageIntegrationKey = (_u = (_t = (_s = service.integrations) == null ? void 0 : _s.find((integration) => {
|
|
970
|
+
var _a2;
|
|
971
|
+
return ((_a2 = integration.vendor) == null ? void 0 : _a2.id) === backstageVendorId;
|
|
972
|
+
})) == null ? void 0 : _t.integration_key) != null ? _u : "";
|
|
743
973
|
if (entityRef !== void 0) {
|
|
744
974
|
result.mappings.push({
|
|
745
975
|
entityRef,
|
|
@@ -748,7 +978,7 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
748
978
|
integrationKey: backstageIntegrationKey,
|
|
749
979
|
status: "InSync",
|
|
750
980
|
serviceName: service.name,
|
|
751
|
-
team: service.teams
|
|
981
|
+
team: (_x = (_w = (_v = service.teams) == null ? void 0 : _v[0]) == null ? void 0 : _w.name) != null ? _x : "",
|
|
752
982
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
753
983
|
serviceUrl: service.html_url,
|
|
754
984
|
account: service.account
|
|
@@ -761,7 +991,7 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
761
991
|
integrationKey: backstageIntegrationKey,
|
|
762
992
|
status: "NotMapped",
|
|
763
993
|
serviceName: service.name,
|
|
764
|
-
team: service.teams
|
|
994
|
+
team: (_A = (_z = (_y = service.teams) == null ? void 0 : _y[0]) == null ? void 0 : _z.name) != null ? _A : "",
|
|
765
995
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
766
996
|
serviceUrl: service.html_url,
|
|
767
997
|
account: service.account
|
|
@@ -790,7 +1020,196 @@ async function createRouter(options) {
|
|
|
790
1020
|
loadPagerDutyEndpointsFromConfig(config, logger);
|
|
791
1021
|
const router = Router__default.default();
|
|
792
1022
|
router.use(express__namespace.json());
|
|
1023
|
+
router.delete("/dependencies/service/:serviceId", async (request, response) => {
|
|
1024
|
+
try {
|
|
1025
|
+
const serviceId = request.params.serviceId || "";
|
|
1026
|
+
const account = request.query.account || "";
|
|
1027
|
+
if (serviceId === "") {
|
|
1028
|
+
logger.info(`Bad Request: ':serviceId' must be provided as part of the path`);
|
|
1029
|
+
response.status(400).json("Bad Request: ':serviceId' must be provided as part of the path");
|
|
1030
|
+
}
|
|
1031
|
+
const dependencies = request.body;
|
|
1032
|
+
if (!dependencies || dependencies.length === 0) {
|
|
1033
|
+
logger.info(`Bad Request: 'dependencies' must be provided as part of the request body`);
|
|
1034
|
+
response.status(400).json("Bad Request: 'dependencies' must be provided as part of the request body");
|
|
1035
|
+
}
|
|
1036
|
+
logger.info(`Received dependencies to remove from PagerDuty: ${JSON.stringify(dependencies)}`);
|
|
1037
|
+
const serviceRelations = [];
|
|
1038
|
+
dependencies.forEach(async (dependency) => {
|
|
1039
|
+
serviceRelations.push({
|
|
1040
|
+
supporting_service: {
|
|
1041
|
+
id: dependency,
|
|
1042
|
+
type: "service"
|
|
1043
|
+
},
|
|
1044
|
+
dependent_service: {
|
|
1045
|
+
id: serviceId,
|
|
1046
|
+
type: "service"
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
});
|
|
1050
|
+
await removeServiceRelationsFromService(serviceRelations, account);
|
|
1051
|
+
response.sendStatus(200);
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1054
|
+
logger.error(`Error occurred while processing request: ${error.message}`);
|
|
1055
|
+
response.status(error.status).json({
|
|
1056
|
+
errors: [
|
|
1057
|
+
`${error.message}`
|
|
1058
|
+
]
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
router.post("/dependencies/service/:serviceId", async (request, response) => {
|
|
1064
|
+
try {
|
|
1065
|
+
logger.info(`Received params : ${JSON.stringify(request.params)}`);
|
|
1066
|
+
const serviceId = request.params.serviceId || "";
|
|
1067
|
+
const account = request.query.account || "";
|
|
1068
|
+
if (serviceId === "") {
|
|
1069
|
+
logger.info(`Bad Request: ':serviceId' must be provided as part of the path`);
|
|
1070
|
+
response.status(400).json("Bad Request: ':serviceId' must be provided as part of the path");
|
|
1071
|
+
}
|
|
1072
|
+
const dependencies = request.body;
|
|
1073
|
+
if (!dependencies || dependencies.length === 0) {
|
|
1074
|
+
logger.info(`Bad Request: 'dependencies' must be provided as part of the request body`);
|
|
1075
|
+
response.status(400).json("Bad Request: 'dependencies' must be provided as part of the request body");
|
|
1076
|
+
}
|
|
1077
|
+
logger.info(`Received dependencies to add to PagerDuty: ${JSON.stringify(dependencies)}`);
|
|
1078
|
+
const serviceRelations = [];
|
|
1079
|
+
dependencies.forEach(async (dependency) => {
|
|
1080
|
+
serviceRelations.push({
|
|
1081
|
+
supporting_service: {
|
|
1082
|
+
id: dependency,
|
|
1083
|
+
type: "service"
|
|
1084
|
+
},
|
|
1085
|
+
dependent_service: {
|
|
1086
|
+
id: serviceId,
|
|
1087
|
+
type: "service"
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
});
|
|
1091
|
+
await addServiceRelationsToService(serviceRelations, account);
|
|
1092
|
+
response.sendStatus(200);
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1095
|
+
logger.error(`Error occurred while processing request: ${error.message}`);
|
|
1096
|
+
response.status(error.status).json({
|
|
1097
|
+
errors: [
|
|
1098
|
+
`${error.message}`
|
|
1099
|
+
]
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
router.get("/dependencies/service/:serviceId", async (request, response) => {
|
|
1105
|
+
try {
|
|
1106
|
+
const serviceId = request.params.serviceId;
|
|
1107
|
+
const account = request.query.account || "";
|
|
1108
|
+
if (serviceId) {
|
|
1109
|
+
const serviceRelationships = await getServiceRelationshipsById(serviceId, account);
|
|
1110
|
+
if (serviceRelationships && serviceRelationships.length > 0) {
|
|
1111
|
+
response.json({
|
|
1112
|
+
relationships: serviceRelationships
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
} else {
|
|
1116
|
+
response.status(400).json("Bad Request: ':serviceId' must be provided as part of the path");
|
|
1117
|
+
}
|
|
1118
|
+
response.status(404);
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1121
|
+
response.status(error.status).json({
|
|
1122
|
+
errors: [
|
|
1123
|
+
`${error.message}`
|
|
1124
|
+
]
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
router.get("/catalog/entity/:type/:namespace/:name", async (request, response) => {
|
|
1130
|
+
var _a;
|
|
1131
|
+
const type = request.params.type;
|
|
1132
|
+
const namespace = request.params.namespace;
|
|
1133
|
+
const name = request.params.name;
|
|
1134
|
+
try {
|
|
1135
|
+
if (type && namespace && name) {
|
|
1136
|
+
const entityRef = `${type}:${namespace}/${name}`.toLowerCase();
|
|
1137
|
+
const foundEntity = await (catalogApi == null ? void 0 : catalogApi.getEntityByRef(entityRef));
|
|
1138
|
+
if (foundEntity) {
|
|
1139
|
+
response.json((_a = foundEntity.metadata.annotations) == null ? void 0 : _a["pagerduty.com/service-id"]);
|
|
1140
|
+
} else {
|
|
1141
|
+
response.status(404);
|
|
1142
|
+
}
|
|
1143
|
+
} else {
|
|
1144
|
+
response.status(400).json("Bad Request: ':entityRef' must be provided as part of the path");
|
|
1145
|
+
}
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1148
|
+
response.status(error.status).json({
|
|
1149
|
+
errors: [
|
|
1150
|
+
`${error.message}`
|
|
1151
|
+
]
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
router.post("/settings", async (request, response) => {
|
|
1157
|
+
try {
|
|
1158
|
+
const settings = request.body;
|
|
1159
|
+
logger.info(`Received settings: ${JSON.stringify(settings)}`);
|
|
1160
|
+
await Promise.all(settings.map(async (setting) => {
|
|
1161
|
+
logger.info(`Processing setting: ${JSON.stringify(setting)}`);
|
|
1162
|
+
if (setting.id === void 0 || setting.value === void 0) {
|
|
1163
|
+
logger.info(`Bad Request: 'id' and 'value' are required`);
|
|
1164
|
+
response.status(400).json("Bad Request: 'id' and 'value' are required");
|
|
1165
|
+
}
|
|
1166
|
+
if (!isValidSetting(setting.value)) {
|
|
1167
|
+
logger.info(`Bad Request: 'value' is invalid`);
|
|
1168
|
+
response.status(400).json("Bad Request: 'value' is invalid. Valid options are 'backstage', 'pagerduty', 'both' or 'disabled'");
|
|
1169
|
+
}
|
|
1170
|
+
logger.info(`Setting value is valid: ${setting.value}`);
|
|
1171
|
+
await store.updateSetting(setting);
|
|
1172
|
+
logger.info(`Setting updated: ${JSON.stringify(setting)}`);
|
|
1173
|
+
}));
|
|
1174
|
+
response.sendStatus(200);
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1177
|
+
logger.error(`Error occurred while processing request: ${error.message}`);
|
|
1178
|
+
response.status(error.status).json({
|
|
1179
|
+
errors: [
|
|
1180
|
+
`${error.message}`
|
|
1181
|
+
]
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
router.get("/settings/:settingId", async (request, response) => {
|
|
1187
|
+
try {
|
|
1188
|
+
const settingId = request.params.settingId;
|
|
1189
|
+
const setting = await store.findSetting(settingId);
|
|
1190
|
+
if (!setting) {
|
|
1191
|
+
response.status(404).json(`Setting with id ${settingId} not found.`);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
response.json(setting);
|
|
1195
|
+
} catch (error) {
|
|
1196
|
+
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1197
|
+
response.status(error.status).json({
|
|
1198
|
+
errors: [
|
|
1199
|
+
`${error.message}`
|
|
1200
|
+
]
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
function isValidSetting(value) {
|
|
1206
|
+
if (value === "backstage" || value === "pagerduty" || value === "both" || value === "disabled") {
|
|
1207
|
+
return true;
|
|
1208
|
+
}
|
|
1209
|
+
return false;
|
|
1210
|
+
}
|
|
793
1211
|
router.post("/mapping/entity", async (request, response) => {
|
|
1212
|
+
var _a;
|
|
794
1213
|
try {
|
|
795
1214
|
const entity = request.body;
|
|
796
1215
|
if (!entity.serviceId) {
|
|
@@ -798,12 +1217,30 @@ async function createRouter(options) {
|
|
|
798
1217
|
}
|
|
799
1218
|
const entityMappings = await store.getAllEntityMappings();
|
|
800
1219
|
const oldMapping = entityMappings.find((mapping) => mapping.serviceId === entity.serviceId);
|
|
1220
|
+
if (entity.entityRef !== "" && (entity.integrationKey === "" || entity.integrationKey === void 0)) {
|
|
1221
|
+
const backstageVendorId = "PRO19CT";
|
|
1222
|
+
const service = await getServiceById(entity.serviceId, entity.account);
|
|
1223
|
+
const backstageIntegration = (_a = service.integrations) == null ? void 0 : _a.find((integration) => {
|
|
1224
|
+
var _a2;
|
|
1225
|
+
return ((_a2 = integration.vendor) == null ? void 0 : _a2.id) === backstageVendorId;
|
|
1226
|
+
});
|
|
1227
|
+
if (!backstageIntegration) {
|
|
1228
|
+
const integrationKey = await createServiceIntegration({
|
|
1229
|
+
serviceId: entity.serviceId,
|
|
1230
|
+
vendorId: backstageVendorId,
|
|
1231
|
+
account: entity.account
|
|
1232
|
+
});
|
|
1233
|
+
entity.integrationKey = integrationKey;
|
|
1234
|
+
} else {
|
|
1235
|
+
entity.integrationKey = backstageIntegration.integration_key;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
801
1238
|
const entityMappingId = await store.insertEntityMapping(entity);
|
|
802
1239
|
if (entity.entityRef !== "") {
|
|
803
|
-
await catalogApi
|
|
1240
|
+
await (catalogApi == null ? void 0 : catalogApi.refreshEntity(entity.entityRef));
|
|
804
1241
|
}
|
|
805
1242
|
if (oldMapping && oldMapping.entityRef !== "") {
|
|
806
|
-
await catalogApi
|
|
1243
|
+
await (catalogApi == null ? void 0 : catalogApi.refreshEntity(oldMapping.entityRef));
|
|
807
1244
|
}
|
|
808
1245
|
response.json({
|
|
809
1246
|
id: entityMappingId,
|
|
@@ -827,16 +1264,13 @@ async function createRouter(options) {
|
|
|
827
1264
|
router.get("/mapping/entity", async (_, response) => {
|
|
828
1265
|
try {
|
|
829
1266
|
const entityMappings = await store.getAllEntityMappings();
|
|
830
|
-
logger.info(`Retrieved ${entityMappings.length} entity mappings from the database.`);
|
|
831
1267
|
const componentEntities = await catalogApi.getEntities({
|
|
832
1268
|
filter: {
|
|
833
1269
|
kind: "Component"
|
|
834
1270
|
}
|
|
835
1271
|
});
|
|
836
|
-
logger.info(`Retrieved ${componentEntities.items.length} entities from the catalog.`);
|
|
837
1272
|
const componentEntitiesDict = await createComponentEntitiesReferenceDict(componentEntities);
|
|
838
1273
|
const pagerDutyServices = await getAllServices();
|
|
839
|
-
logger.info(`Retrieved ${pagerDutyServices.length} services from PagerDuty.`);
|
|
840
1274
|
const result = await buildEntityMappingsResponse(entityMappings, componentEntitiesDict, componentEntities, pagerDutyServices);
|
|
841
1275
|
response.json(result);
|
|
842
1276
|
} catch (error) {
|
|
@@ -877,6 +1311,32 @@ async function createRouter(options) {
|
|
|
877
1311
|
}
|
|
878
1312
|
}
|
|
879
1313
|
});
|
|
1314
|
+
router.get("/mapping/entity/service/:serviceId", async (request, response) => {
|
|
1315
|
+
var _a;
|
|
1316
|
+
try {
|
|
1317
|
+
const serviceId = (_a = request.params.serviceId) != null ? _a : "";
|
|
1318
|
+
if (serviceId === "") {
|
|
1319
|
+
response.status(400).json("Required params not specified.");
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
const entityMapping = await store.findEntityMappingByServiceId(serviceId);
|
|
1323
|
+
if (!entityMapping) {
|
|
1324
|
+
response.status(404).json(`Mapping for serviceId ${serviceId} not found.`);
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
response.json({
|
|
1328
|
+
mapping: entityMapping
|
|
1329
|
+
});
|
|
1330
|
+
} catch (error) {
|
|
1331
|
+
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1332
|
+
response.status(error.status).json({
|
|
1333
|
+
errors: [
|
|
1334
|
+
`${error.message}`
|
|
1335
|
+
]
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
880
1340
|
router.get("/escalation_policies", async (_, response) => {
|
|
881
1341
|
try {
|
|
882
1342
|
let escalationPolicyList = await getAllEscalationPolicies();
|
|
@@ -978,6 +1438,31 @@ async function createRouter(options) {
|
|
|
978
1438
|
}
|
|
979
1439
|
}
|
|
980
1440
|
});
|
|
1441
|
+
router.post("/services/:serviceId/integration/:vendorId", async (request, response) => {
|
|
1442
|
+
try {
|
|
1443
|
+
const serviceId = request.params.serviceId || "";
|
|
1444
|
+
const vendorId = request.params.vendorId || "";
|
|
1445
|
+
const account = request.query.account || "";
|
|
1446
|
+
if (serviceId === "" || vendorId === "") {
|
|
1447
|
+
response.status(400).json("Bad Request: ':serviceId' and ':vendorId' must be provided as part of the path");
|
|
1448
|
+
}
|
|
1449
|
+
const integrationKey = await createServiceIntegration({
|
|
1450
|
+
serviceId,
|
|
1451
|
+
vendorId,
|
|
1452
|
+
account
|
|
1453
|
+
});
|
|
1454
|
+
response.json(integrationKey);
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1457
|
+
logger.error(`Error occurred while processing request: ${error.message}`);
|
|
1458
|
+
response.status(error.status).json({
|
|
1459
|
+
errors: [
|
|
1460
|
+
`${error.message}`
|
|
1461
|
+
]
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
});
|
|
981
1466
|
router.get("/services/:serviceId/change-events", async (request, response) => {
|
|
982
1467
|
try {
|
|
983
1468
|
const serviceId = request.params.serviceId || "";
|
|
@@ -1066,7 +1551,7 @@ class PagerDutyBackendDatabase {
|
|
|
1066
1551
|
this.db = db;
|
|
1067
1552
|
}
|
|
1068
1553
|
static async create(knex, options) {
|
|
1069
|
-
if (options
|
|
1554
|
+
if (options == null ? void 0 : options.skipMigrations) {
|
|
1070
1555
|
const migrationsDir = backendPluginApi.resolvePackagePath("@pagerduty/backstage-plugin-backend", "migrations");
|
|
1071
1556
|
await knex.migrate.latest({
|
|
1072
1557
|
directory: migrationsDir
|
|
@@ -1097,6 +1582,28 @@ class PagerDutyBackendDatabase {
|
|
|
1097
1582
|
const rawEntity = await this.db("pagerduty_entity_mapping").where("entityRef", entityRef).first();
|
|
1098
1583
|
return rawEntity;
|
|
1099
1584
|
}
|
|
1585
|
+
async findEntityMappingByServiceId(serviceId) {
|
|
1586
|
+
const rawEntity = await this.db("pagerduty_entity_mapping").where("serviceId", serviceId).first();
|
|
1587
|
+
return rawEntity;
|
|
1588
|
+
}
|
|
1589
|
+
async updateSetting(setting) {
|
|
1590
|
+
const [result] = await this.db("pagerduty_settings").insert({
|
|
1591
|
+
id: setting.id,
|
|
1592
|
+
value: setting.value
|
|
1593
|
+
}).onConflict(["id"]).merge(["value"]).returning("id");
|
|
1594
|
+
return result.id;
|
|
1595
|
+
}
|
|
1596
|
+
async findSetting(settingId) {
|
|
1597
|
+
const rawEntity = await this.db("pagerduty_settings").where("id", settingId).first();
|
|
1598
|
+
return rawEntity;
|
|
1599
|
+
}
|
|
1600
|
+
async getAllSettings() {
|
|
1601
|
+
const rawEntities = await this.db("pagerduty_settings");
|
|
1602
|
+
if (!rawEntities) {
|
|
1603
|
+
return [];
|
|
1604
|
+
}
|
|
1605
|
+
return rawEntities;
|
|
1606
|
+
}
|
|
1100
1607
|
}
|
|
1101
1608
|
|
|
1102
1609
|
class CatalogFetchApi {
|