@pagerduty/backstage-plugin-backend 0.8.1-next.30 → 0.8.1-next.32
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 +31 -403
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -6
- package/package.json +14 -12
- package/migrations/20240722_add_settings_table.js +0 -27
package/dist/index.cjs.js
CHANGED
|
@@ -39,8 +39,7 @@ var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
|
39
39
|
let authPersistence;
|
|
40
40
|
let isLegacyConfig$1 = false;
|
|
41
41
|
async function getAuthToken(accountId) {
|
|
42
|
-
|
|
43
|
-
if (!(authPersistence == null ? void 0 : authPersistence.accountTokens)) {
|
|
42
|
+
if (!authPersistence?.accountTokens) {
|
|
44
43
|
await loadAuthConfig(authPersistence.config, authPersistence.logger);
|
|
45
44
|
}
|
|
46
45
|
if (isLegacyConfig$1) {
|
|
@@ -53,7 +52,7 @@ async function getAuthToken(accountId) {
|
|
|
53
52
|
return authPersistence.accountTokens[accountId].authToken;
|
|
54
53
|
}
|
|
55
54
|
} else {
|
|
56
|
-
const defaultFallback =
|
|
55
|
+
const defaultFallback = authPersistence.defaultAccount ?? "";
|
|
57
56
|
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")) {
|
|
58
57
|
return authPersistence.accountTokens[defaultFallback].authToken;
|
|
59
58
|
}
|
|
@@ -62,7 +61,6 @@ async function getAuthToken(accountId) {
|
|
|
62
61
|
return "";
|
|
63
62
|
}
|
|
64
63
|
async function loadAuthConfig(config, logger) {
|
|
65
|
-
var _a;
|
|
66
64
|
try {
|
|
67
65
|
const defaultAccountId = "default";
|
|
68
66
|
authPersistence = {
|
|
@@ -84,7 +82,7 @@ async function loadAuthConfig(config, logger) {
|
|
|
84
82
|
config.getString("pagerDuty.oauth.clientId"),
|
|
85
83
|
config.getString("pagerDuty.oauth.clientSecret"),
|
|
86
84
|
config.getString("pagerDuty.oauth.subDomain"),
|
|
87
|
-
|
|
85
|
+
config.getOptionalString("pagerDuty.oauth.region") ?? "us"
|
|
88
86
|
);
|
|
89
87
|
authPersistence.accountTokens[defaultAccountId] = tokenInfo;
|
|
90
88
|
logger.info("PagerDuty OAuth configuration loaded successfully.");
|
|
@@ -101,12 +99,11 @@ async function loadAuthConfig(config, logger) {
|
|
|
101
99
|
logger.info("New PagerDuty accounts configuration found in config file.");
|
|
102
100
|
isLegacyConfig$1 = false;
|
|
103
101
|
const accounts = config.getOptional("pagerDuty.accounts");
|
|
104
|
-
if (accounts &&
|
|
102
|
+
if (accounts && accounts?.length === 1) {
|
|
105
103
|
logger.info("Only one account found in config file. Setting it as default.");
|
|
106
104
|
authPersistence.defaultAccount = accounts[0].id;
|
|
107
105
|
}
|
|
108
|
-
accounts
|
|
109
|
-
var _a2;
|
|
106
|
+
accounts?.forEach(async (account) => {
|
|
110
107
|
const maskedAccountId = maskString(account.id);
|
|
111
108
|
if (account.isDefault && !authPersistence.defaultAccount) {
|
|
112
109
|
logger.info(`Default account found in config file. Setting it as default.`);
|
|
@@ -123,7 +120,7 @@ async function loadAuthConfig(config, logger) {
|
|
|
123
120
|
account.oauth.clientId,
|
|
124
121
|
account.oauth.clientSecret,
|
|
125
122
|
account.oauth.subDomain,
|
|
126
|
-
|
|
123
|
+
account.oauth.region ?? "us"
|
|
127
124
|
);
|
|
128
125
|
authPersistence.accountTokens[account.id] = tokenInfo;
|
|
129
126
|
logger.info(`PagerDuty OAuth configuration loaded successfully for account ${maskedAccountId}.`);
|
|
@@ -204,17 +201,15 @@ const EndpointConfig = {};
|
|
|
204
201
|
let fallbackEndpointConfig;
|
|
205
202
|
let isLegacyConfig = false;
|
|
206
203
|
function setFallbackEndpointConfig(account) {
|
|
207
|
-
var _a, _b;
|
|
208
204
|
fallbackEndpointConfig = {
|
|
209
|
-
eventsBaseUrl:
|
|
210
|
-
apiBaseUrl:
|
|
205
|
+
eventsBaseUrl: account.eventsBaseUrl ?? "https://events.pagerduty.com/v2",
|
|
206
|
+
apiBaseUrl: account.apiBaseUrl ?? "https://api.pagerduty.com"
|
|
211
207
|
};
|
|
212
208
|
}
|
|
213
209
|
function insertEndpointConfig(account) {
|
|
214
|
-
var _a, _b;
|
|
215
210
|
EndpointConfig[account.id] = {
|
|
216
|
-
eventsBaseUrl:
|
|
217
|
-
apiBaseUrl:
|
|
211
|
+
eventsBaseUrl: account.eventsBaseUrl ?? "https://events.pagerduty.com/v2",
|
|
212
|
+
apiBaseUrl: account.apiBaseUrl ?? "https://api.pagerduty.com"
|
|
218
213
|
};
|
|
219
214
|
}
|
|
220
215
|
function loadPagerDutyEndpointsFromConfig(config, logger) {
|
|
@@ -222,7 +217,7 @@ function loadPagerDutyEndpointsFromConfig(config, logger) {
|
|
|
222
217
|
logger.debug(`New accounts configuration detected. Loading PagerDuty endpoints from config.`);
|
|
223
218
|
isLegacyConfig = false;
|
|
224
219
|
const accounts = config.getOptional("pagerDuty.accounts");
|
|
225
|
-
if (
|
|
220
|
+
if (accounts?.length === 1) {
|
|
226
221
|
logger.debug(`Single account configuration detected. Loading PagerDuty endpoints from config to 'default'.`);
|
|
227
222
|
EndpointConfig.default = {
|
|
228
223
|
eventsBaseUrl: accounts[0].eventsBaseUrl !== void 0 ? accounts[0].eventsBaseUrl : "https://events.pagerduty.com/v2",
|
|
@@ -230,7 +225,7 @@ function loadPagerDutyEndpointsFromConfig(config, logger) {
|
|
|
230
225
|
};
|
|
231
226
|
} else {
|
|
232
227
|
logger.debug(`Multiple account configuration detected. Loading PagerDuty endpoints from config.`);
|
|
233
|
-
accounts
|
|
228
|
+
accounts?.forEach((account) => {
|
|
234
229
|
if (account.isDefault) {
|
|
235
230
|
setFallbackEndpointConfig(account);
|
|
236
231
|
}
|
|
@@ -255,82 +250,7 @@ function getApiBaseUrl(account) {
|
|
|
255
250
|
}
|
|
256
251
|
return fallbackEndpointConfig.apiBaseUrl;
|
|
257
252
|
}
|
|
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 fetch__default.default(`${baseUrl}`, options);
|
|
276
|
-
} catch (error) {
|
|
277
|
-
throw new Error(`Failed to retrieve service dependencies: ${error}`);
|
|
278
|
-
}
|
|
279
|
-
switch (response.status) {
|
|
280
|
-
case 400:
|
|
281
|
-
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);
|
|
282
|
-
case 401:
|
|
283
|
-
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);
|
|
284
|
-
case 403:
|
|
285
|
-
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);
|
|
286
|
-
case 404:
|
|
287
|
-
throw new backstagePluginCommon.HttpError("Failed to list service dependencies. The requested resource was not found.", 404);
|
|
288
|
-
}
|
|
289
|
-
let result;
|
|
290
|
-
try {
|
|
291
|
-
result = await response.json();
|
|
292
|
-
return result.relationships;
|
|
293
|
-
} catch (error) {
|
|
294
|
-
throw new backstagePluginCommon.HttpError(`Failed to parse service dependency information: ${error}`, 500);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
async function getServiceRelationshipsById(serviceId, account) {
|
|
298
|
-
let response;
|
|
299
|
-
const options = {
|
|
300
|
-
method: "GET",
|
|
301
|
-
headers: {
|
|
302
|
-
Authorization: await getAuthToken(account),
|
|
303
|
-
"Accept": "application/vnd.pagerduty+json;version=2",
|
|
304
|
-
"Content-Type": "application/json"
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
const apiBaseUrl = getApiBaseUrl(account);
|
|
308
|
-
const baseUrl = `${apiBaseUrl}/service_dependencies/technical_services/${serviceId}`;
|
|
309
|
-
try {
|
|
310
|
-
response = await fetch__default.default(`${baseUrl}`, options);
|
|
311
|
-
} catch (error) {
|
|
312
|
-
throw new Error(`Failed to retrieve service dependencies: ${error}`);
|
|
313
|
-
}
|
|
314
|
-
switch (response.status) {
|
|
315
|
-
case 400:
|
|
316
|
-
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);
|
|
317
|
-
case 401:
|
|
318
|
-
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);
|
|
319
|
-
case 403:
|
|
320
|
-
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);
|
|
321
|
-
case 404:
|
|
322
|
-
throw new backstagePluginCommon.HttpError("Failed to list service dependencies. The requested resource was not found.", 404);
|
|
323
|
-
}
|
|
324
|
-
let result;
|
|
325
|
-
try {
|
|
326
|
-
result = await response.json();
|
|
327
|
-
return result.relationships;
|
|
328
|
-
} catch (error) {
|
|
329
|
-
throw new backstagePluginCommon.HttpError(`Failed to parse service dependency information: ${error}`, 500);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
253
|
async function getEscalationPolicies(offset, limit, account) {
|
|
333
|
-
var _a;
|
|
334
254
|
let response;
|
|
335
255
|
const params = `total=true&sort_by=name&offset=${offset}&limit=${limit}`;
|
|
336
256
|
const options = {
|
|
@@ -361,7 +281,7 @@ async function getEscalationPolicies(offset, limit, account) {
|
|
|
361
281
|
let result;
|
|
362
282
|
try {
|
|
363
283
|
result = await response.json();
|
|
364
|
-
return [
|
|
284
|
+
return [result.more ?? false, result.escalation_policies];
|
|
365
285
|
} catch (error) {
|
|
366
286
|
throw new backstagePluginCommon.HttpError(`Failed to parse escalation policy information: ${error}`, 500);
|
|
367
287
|
}
|
|
@@ -727,65 +647,14 @@ async function getServiceMetrics(serviceId, account) {
|
|
|
727
647
|
throw new backstagePluginCommon.HttpError(`Failed to parse service metrics information: ${error}`, 500);
|
|
728
648
|
}
|
|
729
649
|
}
|
|
730
|
-
async function createServiceIntegration({ serviceId, vendorId, account }) {
|
|
731
|
-
var _a;
|
|
732
|
-
let response;
|
|
733
|
-
const apiBaseUrl = getApiBaseUrl(account);
|
|
734
|
-
const baseUrl = `${apiBaseUrl}/services`;
|
|
735
|
-
const token = await getAuthToken(account);
|
|
736
|
-
const options = {
|
|
737
|
-
method: "POST",
|
|
738
|
-
body: JSON.stringify({
|
|
739
|
-
integration: {
|
|
740
|
-
name: "Backstage",
|
|
741
|
-
service: {
|
|
742
|
-
id: serviceId,
|
|
743
|
-
type: "service_reference"
|
|
744
|
-
},
|
|
745
|
-
vendor: {
|
|
746
|
-
id: vendorId,
|
|
747
|
-
type: "vendor_reference"
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
}),
|
|
751
|
-
headers: {
|
|
752
|
-
Authorization: token,
|
|
753
|
-
"Accept": "application/vnd.pagerduty+json;version=2",
|
|
754
|
-
"Content-Type": "application/json"
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
try {
|
|
758
|
-
response = await fetch__default.default(`${baseUrl}/${serviceId}/integrations`, options);
|
|
759
|
-
} catch (error) {
|
|
760
|
-
throw new Error(`Failed to create service integration: ${error}`);
|
|
761
|
-
}
|
|
762
|
-
switch (response.status) {
|
|
763
|
-
case 400:
|
|
764
|
-
throw new Error(`Failed to create service integration. Caller provided invalid arguments.`);
|
|
765
|
-
case 401:
|
|
766
|
-
throw new Error(`Failed to create service integration. Caller did not supply credentials or did not provide the correct credentials.`);
|
|
767
|
-
case 403:
|
|
768
|
-
throw new Error(`Failed to create service integration. Caller is not authorized to view the requested resource.`);
|
|
769
|
-
case 429:
|
|
770
|
-
throw new Error(`Failed to create service integration. Rate limit exceeded.`);
|
|
771
|
-
}
|
|
772
|
-
let result;
|
|
773
|
-
try {
|
|
774
|
-
result = await response.json();
|
|
775
|
-
return (_a = result.integration.integration_key) != null ? _a : "";
|
|
776
|
-
} catch (error) {
|
|
777
|
-
throw new Error(`Failed to parse service information: ${error}`);
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
650
|
|
|
781
651
|
async function createComponentEntitiesReferenceDict({ items: componentEntities }) {
|
|
782
652
|
const componentEntitiesDict = {};
|
|
783
653
|
await Promise.all(componentEntities.map(async (entity) => {
|
|
784
|
-
var _a;
|
|
785
654
|
const annotations = JSON.parse(JSON.stringify(entity.metadata.annotations));
|
|
786
655
|
const serviceId = annotations["pagerduty.com/service-id"];
|
|
787
656
|
const integrationKey = annotations["pagerduty.com/integration-key"];
|
|
788
|
-
const account =
|
|
657
|
+
const account = annotations["pagerduty.com/account"] ?? "";
|
|
789
658
|
if (serviceId !== void 0 && serviceId !== "") {
|
|
790
659
|
componentEntitiesDict[serviceId] = {
|
|
791
660
|
ref: `${entity.kind}:${entity.metadata.namespace}/${entity.metadata.name}`.toLowerCase(),
|
|
@@ -808,9 +677,8 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
808
677
|
mappings: []
|
|
809
678
|
};
|
|
810
679
|
pagerDutyServices.forEach((service) => {
|
|
811
|
-
|
|
812
|
-
const
|
|
813
|
-
const entityName = (_b = componentEntitiesDict[service.id]) == null ? void 0 : _b.name;
|
|
680
|
+
const entityRef = componentEntitiesDict[service.id]?.ref;
|
|
681
|
+
const entityName = componentEntitiesDict[service.id]?.name;
|
|
814
682
|
const entityMapping = entityMappings.find((mapping) => mapping.serviceId === service.id);
|
|
815
683
|
if (entityMapping) {
|
|
816
684
|
if (entityRef === void 0) {
|
|
@@ -822,13 +690,13 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
822
690
|
serviceId: entityMapping.serviceId,
|
|
823
691
|
status: "NotMapped",
|
|
824
692
|
serviceName: service.name,
|
|
825
|
-
team:
|
|
693
|
+
team: service.teams?.[0]?.name ?? "",
|
|
826
694
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
827
695
|
serviceUrl: service.html_url,
|
|
828
696
|
account: service.account
|
|
829
697
|
});
|
|
830
698
|
} else {
|
|
831
|
-
const entityRefName =
|
|
699
|
+
const entityRefName = componentEntities.items.find((entity) => `${entity.kind}:${entity.metadata.namespace}/${entity.metadata.name}`.toLowerCase() === entityMapping.entityRef)?.metadata.name ?? "";
|
|
832
700
|
result.mappings.push({
|
|
833
701
|
entityRef: entityMapping.entityRef,
|
|
834
702
|
entityName: entityRefName,
|
|
@@ -836,14 +704,14 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
836
704
|
integrationKey: entityMapping.integrationKey,
|
|
837
705
|
status: "OutOfSync",
|
|
838
706
|
serviceName: service.name,
|
|
839
|
-
team:
|
|
707
|
+
team: service.teams?.[0]?.name ?? "",
|
|
840
708
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
841
709
|
serviceUrl: service.html_url,
|
|
842
710
|
account: service.account
|
|
843
711
|
});
|
|
844
712
|
}
|
|
845
713
|
} else if (entityRef !== entityMapping.entityRef) {
|
|
846
|
-
const entityRefName =
|
|
714
|
+
const entityRefName = componentEntities.items.find((entity) => `${entity.kind}:${entity.metadata.namespace}/${entity.metadata.name}`.toLowerCase() === entityMapping.entityRef)?.metadata.name ?? "";
|
|
847
715
|
result.mappings.push({
|
|
848
716
|
entityRef: entityMapping.entityRef !== "" ? entityMapping.entityRef : "",
|
|
849
717
|
entityName: entityMapping.entityRef !== "" ? entityRefName : "",
|
|
@@ -851,7 +719,7 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
851
719
|
integrationKey: entityMapping.integrationKey,
|
|
852
720
|
status: "OutOfSync",
|
|
853
721
|
serviceName: service.name,
|
|
854
|
-
team:
|
|
722
|
+
team: service.teams?.[0]?.name ?? "",
|
|
855
723
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
856
724
|
serviceUrl: service.html_url,
|
|
857
725
|
account: service.account
|
|
@@ -864,7 +732,7 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
864
732
|
integrationKey: entityMapping.integrationKey,
|
|
865
733
|
status: "InSync",
|
|
866
734
|
serviceName: service.name,
|
|
867
|
-
team:
|
|
735
|
+
team: service.teams?.[0]?.name ?? "",
|
|
868
736
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
869
737
|
serviceUrl: service.html_url,
|
|
870
738
|
account: service.account
|
|
@@ -872,10 +740,7 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
872
740
|
}
|
|
873
741
|
} else {
|
|
874
742
|
const backstageVendorId = "PRO19CT";
|
|
875
|
-
const backstageIntegrationKey =
|
|
876
|
-
var _a2;
|
|
877
|
-
return ((_a2 = integration.vendor) == null ? void 0 : _a2.id) === backstageVendorId;
|
|
878
|
-
})) == null ? void 0 : _t.integration_key) != null ? _u : "";
|
|
743
|
+
const backstageIntegrationKey = service.integrations?.find((integration) => integration.vendor?.id === backstageVendorId)?.integration_key ?? "";
|
|
879
744
|
if (entityRef !== void 0) {
|
|
880
745
|
result.mappings.push({
|
|
881
746
|
entityRef,
|
|
@@ -884,7 +749,7 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
884
749
|
integrationKey: backstageIntegrationKey,
|
|
885
750
|
status: "InSync",
|
|
886
751
|
serviceName: service.name,
|
|
887
|
-
team:
|
|
752
|
+
team: service.teams?.[0]?.name ?? "",
|
|
888
753
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
889
754
|
serviceUrl: service.html_url,
|
|
890
755
|
account: service.account
|
|
@@ -897,7 +762,7 @@ async function buildEntityMappingsResponse(entityMappings, componentEntitiesDict
|
|
|
897
762
|
integrationKey: backstageIntegrationKey,
|
|
898
763
|
status: "NotMapped",
|
|
899
764
|
serviceName: service.name,
|
|
900
|
-
team:
|
|
765
|
+
team: service.teams?.[0]?.name ?? "",
|
|
901
766
|
escalationPolicy: service.escalation_policy !== void 0 ? service.escalation_policy.name : "",
|
|
902
767
|
serviceUrl: service.html_url,
|
|
903
768
|
account: service.account
|
|
@@ -926,156 +791,7 @@ async function createRouter(options) {
|
|
|
926
791
|
loadPagerDutyEndpointsFromConfig(config, logger);
|
|
927
792
|
const router = Router__default.default();
|
|
928
793
|
router.use(express__namespace.json());
|
|
929
|
-
router.post("/dependencies/service/:serviceId", async (request, response) => {
|
|
930
|
-
try {
|
|
931
|
-
logger.info(`Received params : ${JSON.stringify(request.params)}`);
|
|
932
|
-
const serviceId = request.params.serviceId || "";
|
|
933
|
-
const account = request.query.account || "";
|
|
934
|
-
if (serviceId === "") {
|
|
935
|
-
logger.info(`Bad Request: ':serviceId' must be provided as part of the path`);
|
|
936
|
-
response.status(400).json("Bad Request: ':serviceId' must be provided as part of the path");
|
|
937
|
-
}
|
|
938
|
-
const dependencies = request.body;
|
|
939
|
-
if (!dependencies || dependencies.length === 0) {
|
|
940
|
-
logger.info(`Bad Request: 'dependencies' must be provided as part of the request body`);
|
|
941
|
-
response.status(400).json("Bad Request: 'dependencies' must be provided as part of the request body");
|
|
942
|
-
}
|
|
943
|
-
logger.info(`Received dependencies to add to PagerDuty: ${JSON.stringify(dependencies)}`);
|
|
944
|
-
const serviceRelations = [];
|
|
945
|
-
dependencies.forEach(async (dependency) => {
|
|
946
|
-
serviceRelations.push({
|
|
947
|
-
supporting_service: {
|
|
948
|
-
id: dependency,
|
|
949
|
-
type: "service"
|
|
950
|
-
},
|
|
951
|
-
dependent_service: {
|
|
952
|
-
id: serviceId,
|
|
953
|
-
type: "service"
|
|
954
|
-
}
|
|
955
|
-
});
|
|
956
|
-
});
|
|
957
|
-
await addServiceRelationsToService(serviceRelations, account);
|
|
958
|
-
response.sendStatus(200);
|
|
959
|
-
} catch (error) {
|
|
960
|
-
if (error instanceof backstagePluginCommon.HttpError) {
|
|
961
|
-
logger.error(`Error occurred while processing request: ${error.message}`);
|
|
962
|
-
response.status(error.status).json({
|
|
963
|
-
errors: [
|
|
964
|
-
`${error.message}`
|
|
965
|
-
]
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
});
|
|
970
|
-
router.get("/dependencies/service/:serviceId", async (request, response) => {
|
|
971
|
-
try {
|
|
972
|
-
const serviceId = request.params.serviceId;
|
|
973
|
-
const account = request.query.account || "";
|
|
974
|
-
if (serviceId) {
|
|
975
|
-
const serviceRelationships = await getServiceRelationshipsById(serviceId, account);
|
|
976
|
-
if (serviceRelationships && serviceRelationships.length > 0) {
|
|
977
|
-
response.json({
|
|
978
|
-
relationships: serviceRelationships
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
} else {
|
|
982
|
-
response.status(400).json("Bad Request: ':serviceId' must be provided as part of the path");
|
|
983
|
-
}
|
|
984
|
-
response.status(404);
|
|
985
|
-
} catch (error) {
|
|
986
|
-
if (error instanceof backstagePluginCommon.HttpError) {
|
|
987
|
-
response.status(error.status).json({
|
|
988
|
-
errors: [
|
|
989
|
-
`${error.message}`
|
|
990
|
-
]
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
});
|
|
995
|
-
router.get("/catalog/entity/:type/:namespace/:name", async (request, response) => {
|
|
996
|
-
var _a;
|
|
997
|
-
const type = request.params.type;
|
|
998
|
-
const namespace = request.params.namespace;
|
|
999
|
-
const name = request.params.name;
|
|
1000
|
-
try {
|
|
1001
|
-
if (type && namespace && name) {
|
|
1002
|
-
const entityRef = `${type}:${namespace}/${name}`.toLowerCase();
|
|
1003
|
-
const foundEntity = await (catalogApi == null ? void 0 : catalogApi.getEntityByRef(entityRef));
|
|
1004
|
-
if (foundEntity) {
|
|
1005
|
-
response.json((_a = foundEntity.metadata.annotations) == null ? void 0 : _a["pagerduty.com/service-id"]);
|
|
1006
|
-
} else {
|
|
1007
|
-
response.status(404);
|
|
1008
|
-
}
|
|
1009
|
-
} else {
|
|
1010
|
-
response.status(400).json("Bad Request: ':entityRef' must be provided as part of the path");
|
|
1011
|
-
}
|
|
1012
|
-
} catch (error) {
|
|
1013
|
-
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1014
|
-
response.status(error.status).json({
|
|
1015
|
-
errors: [
|
|
1016
|
-
`${error.message}`
|
|
1017
|
-
]
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
});
|
|
1022
|
-
router.post("/settings", async (request, response) => {
|
|
1023
|
-
try {
|
|
1024
|
-
const settings = request.body;
|
|
1025
|
-
logger.info(`Received settings: ${JSON.stringify(settings)}`);
|
|
1026
|
-
await Promise.all(settings.map(async (setting) => {
|
|
1027
|
-
logger.info(`Processing setting: ${JSON.stringify(setting)}`);
|
|
1028
|
-
if (setting.id === void 0 || setting.value === void 0) {
|
|
1029
|
-
logger.info(`Bad Request: 'id' and 'value' are required`);
|
|
1030
|
-
response.status(400).json("Bad Request: 'id' and 'value' are required");
|
|
1031
|
-
}
|
|
1032
|
-
if (!isValidSetting(setting.value)) {
|
|
1033
|
-
logger.info(`Bad Request: 'value' is invalid`);
|
|
1034
|
-
response.status(400).json("Bad Request: 'value' is invalid. Valid options are 'backstage', 'pagerduty', 'both' or 'disabled'");
|
|
1035
|
-
}
|
|
1036
|
-
logger.info(`Setting value is valid: ${setting.value}`);
|
|
1037
|
-
await store.updateSetting(setting);
|
|
1038
|
-
logger.info(`Setting updated: ${JSON.stringify(setting)}`);
|
|
1039
|
-
}));
|
|
1040
|
-
response.sendStatus(200);
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1043
|
-
logger.error(`Error occurred while processing request: ${error.message}`);
|
|
1044
|
-
response.status(error.status).json({
|
|
1045
|
-
errors: [
|
|
1046
|
-
`${error.message}`
|
|
1047
|
-
]
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
});
|
|
1052
|
-
router.get("/settings/:settingId", async (request, response) => {
|
|
1053
|
-
try {
|
|
1054
|
-
const settingId = request.params.settingId;
|
|
1055
|
-
const setting = await store.findSetting(settingId);
|
|
1056
|
-
if (!setting) {
|
|
1057
|
-
response.status(404).json(`Setting with id ${settingId} not found.`);
|
|
1058
|
-
return;
|
|
1059
|
-
}
|
|
1060
|
-
response.json(setting);
|
|
1061
|
-
} catch (error) {
|
|
1062
|
-
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1063
|
-
response.status(error.status).json({
|
|
1064
|
-
errors: [
|
|
1065
|
-
`${error.message}`
|
|
1066
|
-
]
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
});
|
|
1071
|
-
function isValidSetting(value) {
|
|
1072
|
-
if (value === "backstage" || value === "pagerduty" || value === "both" || value === "disabled") {
|
|
1073
|
-
return true;
|
|
1074
|
-
}
|
|
1075
|
-
return false;
|
|
1076
|
-
}
|
|
1077
794
|
router.post("/mapping/entity", async (request, response) => {
|
|
1078
|
-
var _a;
|
|
1079
795
|
try {
|
|
1080
796
|
const entity = request.body;
|
|
1081
797
|
if (!entity.serviceId) {
|
|
@@ -1083,30 +799,12 @@ async function createRouter(options) {
|
|
|
1083
799
|
}
|
|
1084
800
|
const entityMappings = await store.getAllEntityMappings();
|
|
1085
801
|
const oldMapping = entityMappings.find((mapping) => mapping.serviceId === entity.serviceId);
|
|
1086
|
-
if (entity.entityRef !== "" && (entity.integrationKey === "" || entity.integrationKey === void 0)) {
|
|
1087
|
-
const backstageVendorId = "PRO19CT";
|
|
1088
|
-
const service = await getServiceById(entity.serviceId, entity.account);
|
|
1089
|
-
const backstageIntegration = (_a = service.integrations) == null ? void 0 : _a.find((integration) => {
|
|
1090
|
-
var _a2;
|
|
1091
|
-
return ((_a2 = integration.vendor) == null ? void 0 : _a2.id) === backstageVendorId;
|
|
1092
|
-
});
|
|
1093
|
-
if (!backstageIntegration) {
|
|
1094
|
-
const integrationKey = await createServiceIntegration({
|
|
1095
|
-
serviceId: entity.serviceId,
|
|
1096
|
-
vendorId: backstageVendorId,
|
|
1097
|
-
account: entity.account
|
|
1098
|
-
});
|
|
1099
|
-
entity.integrationKey = integrationKey;
|
|
1100
|
-
} else {
|
|
1101
|
-
entity.integrationKey = backstageIntegration.integration_key;
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
802
|
const entityMappingId = await store.insertEntityMapping(entity);
|
|
1105
803
|
if (entity.entityRef !== "") {
|
|
1106
|
-
await
|
|
804
|
+
await catalogApi?.refreshEntity(entity.entityRef);
|
|
1107
805
|
}
|
|
1108
806
|
if (oldMapping && oldMapping.entityRef !== "") {
|
|
1109
|
-
await
|
|
807
|
+
await catalogApi?.refreshEntity(oldMapping.entityRef);
|
|
1110
808
|
}
|
|
1111
809
|
response.json({
|
|
1112
810
|
id: entityMappingId,
|
|
@@ -1130,13 +828,16 @@ async function createRouter(options) {
|
|
|
1130
828
|
router.get("/mapping/entity", async (_, response) => {
|
|
1131
829
|
try {
|
|
1132
830
|
const entityMappings = await store.getAllEntityMappings();
|
|
831
|
+
logger.info(`Retrieved ${entityMappings.length} entity mappings from the database.`);
|
|
1133
832
|
const componentEntities = await catalogApi.getEntities({
|
|
1134
833
|
filter: {
|
|
1135
834
|
kind: "Component"
|
|
1136
835
|
}
|
|
1137
836
|
});
|
|
837
|
+
logger.info(`Retrieved ${componentEntities.items.length} entities from the catalog.`);
|
|
1138
838
|
const componentEntitiesDict = await createComponentEntitiesReferenceDict(componentEntities);
|
|
1139
839
|
const pagerDutyServices = await getAllServices();
|
|
840
|
+
logger.info(`Retrieved ${pagerDutyServices.length} services from PagerDuty.`);
|
|
1140
841
|
const result = await buildEntityMappingsResponse(entityMappings, componentEntitiesDict, componentEntities, pagerDutyServices);
|
|
1141
842
|
response.json(result);
|
|
1142
843
|
} catch (error) {
|
|
@@ -1177,32 +878,6 @@ async function createRouter(options) {
|
|
|
1177
878
|
}
|
|
1178
879
|
}
|
|
1179
880
|
});
|
|
1180
|
-
router.get("/mapping/entity/service/:serviceId", async (request, response) => {
|
|
1181
|
-
var _a;
|
|
1182
|
-
try {
|
|
1183
|
-
const serviceId = (_a = request.params.serviceId) != null ? _a : "";
|
|
1184
|
-
if (serviceId === "") {
|
|
1185
|
-
response.status(400).json("Required params not specified.");
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
const entityMapping = await store.findEntityMappingByServiceId(serviceId);
|
|
1189
|
-
if (!entityMapping) {
|
|
1190
|
-
response.status(404).json(`Mapping for serviceId ${serviceId} not found.`);
|
|
1191
|
-
return;
|
|
1192
|
-
}
|
|
1193
|
-
response.json({
|
|
1194
|
-
mapping: entityMapping
|
|
1195
|
-
});
|
|
1196
|
-
} catch (error) {
|
|
1197
|
-
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1198
|
-
response.status(error.status).json({
|
|
1199
|
-
errors: [
|
|
1200
|
-
`${error.message}`
|
|
1201
|
-
]
|
|
1202
|
-
});
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
});
|
|
1206
881
|
router.get("/escalation_policies", async (_, response) => {
|
|
1207
882
|
try {
|
|
1208
883
|
let escalationPolicyList = await getAllEscalationPolicies();
|
|
@@ -1304,31 +979,6 @@ async function createRouter(options) {
|
|
|
1304
979
|
}
|
|
1305
980
|
}
|
|
1306
981
|
});
|
|
1307
|
-
router.post("/services/:serviceId/integration/:vendorId", async (request, response) => {
|
|
1308
|
-
try {
|
|
1309
|
-
const serviceId = request.params.serviceId || "";
|
|
1310
|
-
const vendorId = request.params.vendorId || "";
|
|
1311
|
-
const account = request.query.account || "";
|
|
1312
|
-
if (serviceId === "" || vendorId === "") {
|
|
1313
|
-
response.status(400).json("Bad Request: ':serviceId' and ':vendorId' must be provided as part of the path");
|
|
1314
|
-
}
|
|
1315
|
-
const integrationKey = await createServiceIntegration({
|
|
1316
|
-
serviceId,
|
|
1317
|
-
vendorId,
|
|
1318
|
-
account
|
|
1319
|
-
});
|
|
1320
|
-
response.json(integrationKey);
|
|
1321
|
-
} catch (error) {
|
|
1322
|
-
if (error instanceof backstagePluginCommon.HttpError) {
|
|
1323
|
-
logger.error(`Error occurred while processing request: ${error.message}`);
|
|
1324
|
-
response.status(error.status).json({
|
|
1325
|
-
errors: [
|
|
1326
|
-
`${error.message}`
|
|
1327
|
-
]
|
|
1328
|
-
});
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
});
|
|
1332
982
|
router.get("/services/:serviceId/change-events", async (request, response) => {
|
|
1333
983
|
try {
|
|
1334
984
|
const serviceId = request.params.serviceId || "";
|
|
@@ -1417,7 +1067,7 @@ class PagerDutyBackendDatabase {
|
|
|
1417
1067
|
this.db = db;
|
|
1418
1068
|
}
|
|
1419
1069
|
static async create(knex, options) {
|
|
1420
|
-
if (options
|
|
1070
|
+
if (options?.skipMigrations) {
|
|
1421
1071
|
const migrationsDir = backendPluginApi.resolvePackagePath("@pagerduty/backstage-plugin-backend", "migrations");
|
|
1422
1072
|
await knex.migrate.latest({
|
|
1423
1073
|
directory: migrationsDir
|
|
@@ -1448,28 +1098,6 @@ class PagerDutyBackendDatabase {
|
|
|
1448
1098
|
const rawEntity = await this.db("pagerduty_entity_mapping").where("entityRef", entityRef).first();
|
|
1449
1099
|
return rawEntity;
|
|
1450
1100
|
}
|
|
1451
|
-
async findEntityMappingByServiceId(serviceId) {
|
|
1452
|
-
const rawEntity = await this.db("pagerduty_entity_mapping").where("serviceId", serviceId).first();
|
|
1453
|
-
return rawEntity;
|
|
1454
|
-
}
|
|
1455
|
-
async updateSetting(setting) {
|
|
1456
|
-
const [result] = await this.db("pagerduty_settings").insert({
|
|
1457
|
-
id: setting.id,
|
|
1458
|
-
value: setting.value
|
|
1459
|
-
}).onConflict(["id"]).merge(["value"]).returning("id");
|
|
1460
|
-
return result.id;
|
|
1461
|
-
}
|
|
1462
|
-
async findSetting(settingId) {
|
|
1463
|
-
const rawEntity = await this.db("pagerduty_settings").where("id", settingId).first();
|
|
1464
|
-
return rawEntity;
|
|
1465
|
-
}
|
|
1466
|
-
async getAllSettings() {
|
|
1467
|
-
const rawEntities = await this.db("pagerduty_settings");
|
|
1468
|
-
if (!rawEntities) {
|
|
1469
|
-
return [];
|
|
1470
|
-
}
|
|
1471
|
-
return rawEntities;
|
|
1472
|
-
}
|
|
1473
1101
|
}
|
|
1474
1102
|
|
|
1475
1103
|
class CatalogFetchApi {
|