@pagerduty/backstage-plugin-backend 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/dist/apis/pagerduty.cjs.js +285 -0
- package/dist/apis/pagerduty.cjs.js.map +1 -1
- package/dist/auth/auth.cjs.js +5 -4
- package/dist/auth/auth.cjs.js.map +1 -1
- package/dist/controllers/mappings-controller.cjs.js +388 -0
- package/dist/controllers/mappings-controller.cjs.js.map +1 -0
- package/dist/db/PagerDutyBackendDatabase.cjs.js +16 -0
- package/dist/db/PagerDutyBackendDatabase.cjs.js.map +1 -1
- package/dist/index.cjs.js +0 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +5 -7
- package/dist/plugin.cjs.js +12 -4
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/service/router.cjs.js +235 -131
- package/dist/service/router.cjs.js.map +1 -1
- package/dist/services/autoMatchJobs.cjs.js +53 -0
- package/dist/services/autoMatchJobs.cjs.js.map +1 -0
- package/dist/services/autoMatchRunner.cjs.js +72 -0
- package/dist/services/autoMatchRunner.cjs.js.map +1 -0
- package/dist/services/dataLoader.cjs.js +13 -5
- package/dist/services/dataLoader.cjs.js.map +1 -1
- package/dist/services/pagerduty/index.cjs.js +11 -0
- package/dist/services/pagerduty/index.cjs.js.map +1 -0
- package/dist/utils/catalog-entity.cjs.js +50 -0
- package/dist/utils/catalog-entity.cjs.js.map +1 -0
- package/dist/utils/normalization.cjs.js +3 -2
- package/dist/utils/normalization.cjs.js.map +1 -1
- package/package.json +6 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @pagerduty/backstage-plugin-backend
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9941800: Refactor the service mappings screen and add an auto-matching functionality to mass map PagerDuty service to Backstage entities
|
|
8
|
+
- e1f99a7: Remove deprecated @backstage/backend-common library. This is a potential breaking change for users who are still on the old backend system:
|
|
9
|
+
|
|
10
|
+
- `createPagerDutyServiceAction(...)` now requires its `config` prop.
|
|
11
|
+
- `createRouter(...)` now requires its `auth` prop.
|
|
12
|
+
|
|
13
|
+
Note that none of this is breaking for users of the new backend system.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- ef0af04: Moved to a process/polling based loading of the automatic mapping matches
|
|
18
|
+
- Updated dependencies [9941800]
|
|
19
|
+
- Updated dependencies [ef0af04]
|
|
20
|
+
- @pagerduty/backstage-plugin-common@0.4.0
|
|
21
|
+
|
|
22
|
+
## 0.12.0
|
|
23
|
+
|
|
24
|
+
### Minor Changes
|
|
25
|
+
|
|
26
|
+
- ae2a9c3: Refactor the service mappings screen and add an auto-matching functionality to mass map PagerDuty service to Backstage entities
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [ae2a9c3]
|
|
31
|
+
- @pagerduty/backstage-plugin-common@0.3.0
|
|
32
|
+
|
|
3
33
|
## 0.11.0
|
|
4
34
|
|
|
5
35
|
### Minor Changes
|
|
@@ -350,6 +350,92 @@ async function getAllEscalationPolicies() {
|
|
|
350
350
|
);
|
|
351
351
|
return results;
|
|
352
352
|
}
|
|
353
|
+
async function getTeams(offset, limit, account) {
|
|
354
|
+
let response;
|
|
355
|
+
const params = `total=true&sort_by=name&offset=${offset}&limit=${limit}`;
|
|
356
|
+
const options = {
|
|
357
|
+
method: "GET",
|
|
358
|
+
headers: {
|
|
359
|
+
Authorization: await auth.getAuthToken(account),
|
|
360
|
+
Accept: "application/vnd.pagerduty+json;version=2",
|
|
361
|
+
"Content-Type": "application/json"
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const apiBaseUrl = getApiBaseUrl(account);
|
|
365
|
+
const baseUrl = `${apiBaseUrl}/teams`;
|
|
366
|
+
try {
|
|
367
|
+
response = await fetchWithRetries(`${baseUrl}?${params}`, options);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
throw new Error(`Failed to retrieve teams: ${error}`);
|
|
370
|
+
}
|
|
371
|
+
if (response.status >= 500) {
|
|
372
|
+
throw new backstagePluginCommon.HttpError(
|
|
373
|
+
`Failed to list teams. PagerDuty API returned a server error. Retrying with the same arguments will not work.`,
|
|
374
|
+
response.status
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
switch (response.status) {
|
|
378
|
+
case 400:
|
|
379
|
+
throw new backstagePluginCommon.HttpError(
|
|
380
|
+
"Failed to list teams. Caller provided invalid arguments.",
|
|
381
|
+
400
|
|
382
|
+
);
|
|
383
|
+
case 401:
|
|
384
|
+
throw new backstagePluginCommon.HttpError(
|
|
385
|
+
"Failed to list teams. Caller did not supply credentials or did not provide the correct credentials.",
|
|
386
|
+
401
|
|
387
|
+
);
|
|
388
|
+
case 403:
|
|
389
|
+
throw new backstagePluginCommon.HttpError(
|
|
390
|
+
"Failed to list teams. Caller is not authorized to view the requested resource.",
|
|
391
|
+
403
|
|
392
|
+
);
|
|
393
|
+
case 429:
|
|
394
|
+
throw new backstagePluginCommon.HttpError("Failed to list teams. Rate limit exceeded.", 429);
|
|
395
|
+
}
|
|
396
|
+
let result;
|
|
397
|
+
try {
|
|
398
|
+
result = await response.json();
|
|
399
|
+
return [result.more ?? false, result.teams];
|
|
400
|
+
} catch (error) {
|
|
401
|
+
throw new backstagePluginCommon.HttpError(`Failed to parse team information: ${error}`, 500);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
async function getAllTeams(account) {
|
|
405
|
+
const limit = 50;
|
|
406
|
+
let offset = 0;
|
|
407
|
+
let moreResults = false;
|
|
408
|
+
let results = [];
|
|
409
|
+
const accountsToFetch = account ? [account] : Object.keys(EndpointConfig);
|
|
410
|
+
await Promise.all(
|
|
411
|
+
accountsToFetch.map(async (acc) => {
|
|
412
|
+
try {
|
|
413
|
+
offset = 0;
|
|
414
|
+
do {
|
|
415
|
+
const res = await getTeams(offset, limit, acc);
|
|
416
|
+
res[1].forEach((team) => {
|
|
417
|
+
team.account = acc;
|
|
418
|
+
});
|
|
419
|
+
results = results.concat(res[1]);
|
|
420
|
+
if (res[0] === true) {
|
|
421
|
+
moreResults = true;
|
|
422
|
+
offset += limit;
|
|
423
|
+
} else {
|
|
424
|
+
moreResults = false;
|
|
425
|
+
}
|
|
426
|
+
} while (moreResults === true);
|
|
427
|
+
} catch (error) {
|
|
428
|
+
if (error instanceof backstagePluginCommon.HttpError) {
|
|
429
|
+
throw error;
|
|
430
|
+
} else {
|
|
431
|
+
throw new backstagePluginCommon.HttpError(`${error}`, 500);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
})
|
|
435
|
+
);
|
|
436
|
+
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
437
|
+
return results;
|
|
438
|
+
}
|
|
353
439
|
async function getOncallUsers(escalationPolicy, account) {
|
|
354
440
|
let response;
|
|
355
441
|
const params = `time_zone=UTC&include[]=users&escalation_policy_ids[]=${escalationPolicy}`;
|
|
@@ -469,6 +555,63 @@ async function getServiceById(serviceId, account) {
|
|
|
469
555
|
throw new backstagePluginCommon.HttpError(`Failed to parse service information: ${error}`, 500);
|
|
470
556
|
}
|
|
471
557
|
}
|
|
558
|
+
async function getSerivcesByIdsAndAccount(serviceIds, account) {
|
|
559
|
+
let response;
|
|
560
|
+
const token = await auth.getAuthToken(account);
|
|
561
|
+
const options = {
|
|
562
|
+
method: "GET",
|
|
563
|
+
headers: {
|
|
564
|
+
Authorization: token,
|
|
565
|
+
Accept: "application/vnd.pagerduty+json;version=2",
|
|
566
|
+
"Content-Type": "application/json"
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
const apiBaseUrl = getApiBaseUrl(account);
|
|
570
|
+
const baseUrl = `${apiBaseUrl}/services`;
|
|
571
|
+
try {
|
|
572
|
+
response = await fetchWithRetries(
|
|
573
|
+
`${baseUrl}?id[]=${serviceIds.join("&id[]=")}`,
|
|
574
|
+
options
|
|
575
|
+
);
|
|
576
|
+
} catch (error) {
|
|
577
|
+
throw new Error(`Failed to retrieve service: ${error}`);
|
|
578
|
+
}
|
|
579
|
+
if (response.status >= 500) {
|
|
580
|
+
throw new backstagePluginCommon.HttpError(
|
|
581
|
+
`Failed to get service. PagerDuty API returned a server error. Retrying with the same arguments will not work.`,
|
|
582
|
+
response.status
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
switch (response.status) {
|
|
586
|
+
case 400:
|
|
587
|
+
throw new backstagePluginCommon.HttpError(
|
|
588
|
+
"Failed to get service. Caller provided invalid arguments.",
|
|
589
|
+
400
|
|
590
|
+
);
|
|
591
|
+
case 401:
|
|
592
|
+
throw new backstagePluginCommon.HttpError(
|
|
593
|
+
"Failed to get service. Caller did not supply credentials or did not provide the correct credentials.",
|
|
594
|
+
401
|
|
595
|
+
);
|
|
596
|
+
case 403:
|
|
597
|
+
throw new backstagePluginCommon.HttpError(
|
|
598
|
+
"Failed to get service. Caller is not authorized to view the requested resource.",
|
|
599
|
+
403
|
|
600
|
+
);
|
|
601
|
+
case 404:
|
|
602
|
+
throw new backstagePluginCommon.HttpError(
|
|
603
|
+
"Failed to get service. The requested resource was not found.",
|
|
604
|
+
404
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
let result;
|
|
608
|
+
try {
|
|
609
|
+
result = await response.json();
|
|
610
|
+
return result.services ?? [];
|
|
611
|
+
} catch (error) {
|
|
612
|
+
throw new backstagePluginCommon.HttpError(`Failed to parse service information: ${error}`, 500);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
472
615
|
async function getServiceByIntegrationKey(integrationKey, account) {
|
|
473
616
|
let response;
|
|
474
617
|
const params = `query=${integrationKey}&time_zone=UTC&include[]=integrations&include[]=escalation_policies`;
|
|
@@ -525,6 +668,15 @@ async function getServiceByIntegrationKey(integrationKey, account) {
|
|
|
525
668
|
}
|
|
526
669
|
return result.services[0];
|
|
527
670
|
}
|
|
671
|
+
async function getServicesByIds(ids) {
|
|
672
|
+
let services = [];
|
|
673
|
+
await Promise.all(
|
|
674
|
+
Object.entries(EndpointConfig).map(async ([account, _]) => {
|
|
675
|
+
services = await getSerivcesByIdsAndAccount(ids, account);
|
|
676
|
+
})
|
|
677
|
+
);
|
|
678
|
+
return services;
|
|
679
|
+
}
|
|
528
680
|
async function getAllServices() {
|
|
529
681
|
const allServices = [];
|
|
530
682
|
await Promise.all(
|
|
@@ -583,6 +735,134 @@ async function getAllServices() {
|
|
|
583
735
|
);
|
|
584
736
|
return allServices;
|
|
585
737
|
}
|
|
738
|
+
async function getServicesByPartialName(partialName) {
|
|
739
|
+
const allServices = [];
|
|
740
|
+
await Promise.all(
|
|
741
|
+
Object.entries(EndpointConfig).map(async ([account, _]) => {
|
|
742
|
+
let response;
|
|
743
|
+
const params = `query=${encodeURIComponent(partialName)}&time_zone=UTC&include[]=integrations&include[]=escalation_policies&include[]=teams&total=true`;
|
|
744
|
+
const token = await auth.getAuthToken(account);
|
|
745
|
+
const options = {
|
|
746
|
+
method: "GET",
|
|
747
|
+
headers: {
|
|
748
|
+
Authorization: token,
|
|
749
|
+
Accept: "application/vnd.pagerduty+json;version=2",
|
|
750
|
+
"Content-Type": "application/json"
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
const apiBaseUrl = getApiBaseUrl(account);
|
|
754
|
+
const baseUrl = `${apiBaseUrl}/services`;
|
|
755
|
+
let offset = 0;
|
|
756
|
+
const limit = 50;
|
|
757
|
+
let result;
|
|
758
|
+
try {
|
|
759
|
+
do {
|
|
760
|
+
const paginatedUrl = `${baseUrl}?${params}&offset=${offset}&limit=${limit}`;
|
|
761
|
+
response = await fetchWithRetries(paginatedUrl, options);
|
|
762
|
+
if (response.status >= 500) {
|
|
763
|
+
throw new backstagePluginCommon.HttpError(
|
|
764
|
+
`Failed to get services. PagerDuty API returned a server error. Retrying with the same arguments will not work.`,
|
|
765
|
+
response.status
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
switch (response.status) {
|
|
769
|
+
case 400:
|
|
770
|
+
throw new backstagePluginCommon.HttpError(
|
|
771
|
+
"Failed to get services. Caller provided invalid arguments.",
|
|
772
|
+
400
|
|
773
|
+
);
|
|
774
|
+
case 401:
|
|
775
|
+
throw new backstagePluginCommon.HttpError(
|
|
776
|
+
"Failed to get services. Caller did not supply credentials or did not provide the correct credentials.",
|
|
777
|
+
401
|
|
778
|
+
);
|
|
779
|
+
case 403:
|
|
780
|
+
throw new backstagePluginCommon.HttpError(
|
|
781
|
+
"Failed to get services. Caller is not authorized to view the requested resource.",
|
|
782
|
+
403
|
|
783
|
+
);
|
|
784
|
+
default:
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
result = await response.json();
|
|
788
|
+
result.services.forEach((service) => {
|
|
789
|
+
service.account = account;
|
|
790
|
+
});
|
|
791
|
+
allServices.push(...result.services);
|
|
792
|
+
offset += limit;
|
|
793
|
+
} while (offset < result.total);
|
|
794
|
+
} catch (error) {
|
|
795
|
+
throw error;
|
|
796
|
+
}
|
|
797
|
+
})
|
|
798
|
+
);
|
|
799
|
+
return allServices;
|
|
800
|
+
}
|
|
801
|
+
async function getFilteredServices(teamIds, query, maxLimit, account) {
|
|
802
|
+
const allServices = [];
|
|
803
|
+
const limit = maxLimit;
|
|
804
|
+
const accountsToFetch = account ? [account] : Object.keys(EndpointConfig);
|
|
805
|
+
await Promise.all(
|
|
806
|
+
accountsToFetch.map(async (acc) => {
|
|
807
|
+
let response;
|
|
808
|
+
let params = `time_zone=UTC&limit=${limit}`;
|
|
809
|
+
if (teamIds && teamIds.length > 0) {
|
|
810
|
+
const teamIdsParam = teamIds.map((id) => `team_ids[]=${id}`).join("&");
|
|
811
|
+
params += `&${teamIdsParam}`;
|
|
812
|
+
}
|
|
813
|
+
if (query && query.trim() !== "") {
|
|
814
|
+
params += `&query=${encodeURIComponent(query.trim())}`;
|
|
815
|
+
}
|
|
816
|
+
const token = await auth.getAuthToken(acc);
|
|
817
|
+
const options = {
|
|
818
|
+
method: "GET",
|
|
819
|
+
headers: {
|
|
820
|
+
Authorization: token,
|
|
821
|
+
Accept: "application/vnd.pagerduty+json;version=2",
|
|
822
|
+
"Content-Type": "application/json"
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
const apiBaseUrl = getApiBaseUrl(acc);
|
|
826
|
+
const baseUrl = `${apiBaseUrl}/services`;
|
|
827
|
+
try {
|
|
828
|
+
response = await fetchWithRetries(`${baseUrl}?${params}`, options);
|
|
829
|
+
if (response.status >= 500) {
|
|
830
|
+
throw new backstagePluginCommon.HttpError(
|
|
831
|
+
`Failed to get services. PagerDuty API returned a server error. Retrying with the same arguments will not work.`,
|
|
832
|
+
response.status
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
switch (response.status) {
|
|
836
|
+
case 400:
|
|
837
|
+
throw new backstagePluginCommon.HttpError(
|
|
838
|
+
"Failed to get services. Caller provided invalid arguments.",
|
|
839
|
+
400
|
|
840
|
+
);
|
|
841
|
+
case 401:
|
|
842
|
+
throw new backstagePluginCommon.HttpError(
|
|
843
|
+
"Failed to get services. Caller did not supply credentials or did not provide the correct credentials.",
|
|
844
|
+
401
|
|
845
|
+
);
|
|
846
|
+
case 403:
|
|
847
|
+
throw new backstagePluginCommon.HttpError(
|
|
848
|
+
"Failed to get services. Caller is not authorized to view the requested resource.",
|
|
849
|
+
403
|
|
850
|
+
);
|
|
851
|
+
default:
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
const result = await response.json();
|
|
855
|
+
result.services.forEach((service) => {
|
|
856
|
+
service.account = acc;
|
|
857
|
+
});
|
|
858
|
+
allServices.push(...result.services);
|
|
859
|
+
} catch (error) {
|
|
860
|
+
throw error;
|
|
861
|
+
}
|
|
862
|
+
})
|
|
863
|
+
);
|
|
864
|
+
return allServices;
|
|
865
|
+
}
|
|
586
866
|
async function getChangeEvents(serviceId, account) {
|
|
587
867
|
let response;
|
|
588
868
|
const params = `limit=5&time_zone=UTC&sort_by=timestamp`;
|
|
@@ -884,14 +1164,19 @@ exports.createServiceIntegration = createServiceIntegration;
|
|
|
884
1164
|
exports.fetchWithRetries = fetchWithRetries;
|
|
885
1165
|
exports.getAllEscalationPolicies = getAllEscalationPolicies;
|
|
886
1166
|
exports.getAllServices = getAllServices;
|
|
1167
|
+
exports.getAllTeams = getAllTeams;
|
|
887
1168
|
exports.getChangeEvents = getChangeEvents;
|
|
1169
|
+
exports.getFilteredServices = getFilteredServices;
|
|
888
1170
|
exports.getIncidents = getIncidents;
|
|
889
1171
|
exports.getOncallUsers = getOncallUsers;
|
|
1172
|
+
exports.getSerivcesByIdsAndAccount = getSerivcesByIdsAndAccount;
|
|
890
1173
|
exports.getServiceById = getServiceById;
|
|
891
1174
|
exports.getServiceByIntegrationKey = getServiceByIntegrationKey;
|
|
892
1175
|
exports.getServiceMetrics = getServiceMetrics;
|
|
893
1176
|
exports.getServiceRelationshipsById = getServiceRelationshipsById;
|
|
894
1177
|
exports.getServiceStandards = getServiceStandards;
|
|
1178
|
+
exports.getServicesByIds = getServicesByIds;
|
|
1179
|
+
exports.getServicesByPartialName = getServicesByPartialName;
|
|
895
1180
|
exports.insertAccountConfig = insertAccountConfig;
|
|
896
1181
|
exports.loadPagerDutyEndpointsFromConfig = loadPagerDutyEndpointsFromConfig;
|
|
897
1182
|
exports.removeServiceRelationsFromService = removeServiceRelationsFromService;
|