@pagerduty/backstage-plugin-backend 0.8.2 → 0.9.0-next.1

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 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
- if (!authPersistence?.accountTokens) {
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") ?? "us"
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?.length === 1) {
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?.forEach(async (account) => {
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 ?? "us"
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 ?? "https://events.pagerduty.com/v2",
206
- apiBaseUrl: account.apiBaseUrl ?? "https://api.pagerduty.com"
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 ?? "https://events.pagerduty.com/v2",
212
- apiBaseUrl: account.apiBaseUrl ?? "https://api.pagerduty.com"
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?.length === 1) {
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?.forEach((account) => {
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 fetch__default.default(`${baseUrl}?${params}`, options);
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 ?? false, result.escalation_policies];
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 fetch__default.default(`${baseUrl}?${params}`, options);
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 fetch__default.default(`${baseUrl}/${serviceId}?${params}`, options);
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 fetch__default.default(`${baseUrl}?${params}`, options);
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 fetch__default.default(paginatedUrl, options);
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 fetch__default.default(`${baseUrl}/${serviceId}/change_events?${params}`, options);
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 fetch__default.default(`${baseUrl}?${params}`, options);
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 fetch__default.default(baseUrl, options);
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 fetch__default.default(baseUrl, options);
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
- const serviceId = entity.metadata.annotations?.["pagerduty.com/service-id"];
655
- const integrationKey = entity.metadata.annotations?.["pagerduty.com/integration-key"];
656
- const account = entity.metadata.annotations?.["pagerduty.com/account"];
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
- const entityRef = componentEntitiesDict[service.id]?.ref;
680
- const entityName = componentEntitiesDict[service.id]?.name;
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?.[0]?.name ?? "",
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)?.metadata.name ?? "";
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?.[0]?.name ?? "",
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)?.metadata.name ?? "";
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?.[0]?.name ?? "",
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?.[0]?.name ?? "",
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?.find((integration) => integration.vendor?.id === backstageVendorId)?.integration_key ?? "";
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?.[0]?.name ?? "",
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?.[0]?.name ?? "",
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({});
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?.refreshEntity(entity.entityRef);
1240
+ await (catalogApi == null ? void 0 : catalogApi.refreshEntity(entity.entityRef));
804
1241
  }
805
1242
  if (oldMapping && oldMapping.entityRef !== "") {
806
- await catalogApi?.refreshEntity(oldMapping.entityRef);
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?.skipMigrations) {
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 {