Skip to content

Commit

Permalink
Merge pull request #64 from PagerDuty/next
Browse files Browse the repository at this point in the history
release: 0.7.0
  • Loading branch information
t1agob authored Jul 4, 2024
2 parents 1d01f1c + 5f5d59a commit f24a311
Show file tree
Hide file tree
Showing 17 changed files with 8,154 additions and 5,364 deletions.
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/.yarn/** linguist-vendored
/.yarn/releases/* binary
/.yarn/plugins/**/* binary
/.pnp.* binary linguist-generated
874 changes: 0 additions & 874 deletions .yarn/releases/yarn-3.6.3.cjs

This file was deleted.

875 changes: 875 additions & 0 deletions .yarn/releases/yarn-3.8.2.cjs

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
nodeLinker: node-modules

npmRegistryServer: https://registry.npmjs.org/
npmRegistryServer: "https://registry.npmjs.org/"

npmScopes:
pagerduty:
npmAlwaysAuth: true
npmAuthToken: NPM_TOKEN

yarnPath: .yarn/releases/yarn-3.8.2.cjs
34 changes: 34 additions & 0 deletions migrations/20240527_init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* @param {import('knex').Knex} knex
*/
exports.up = async function up(knex) {
await knex.schema.createTable('pagerduty_entity_mapping', table => {
table
.bigIncrements('index')
.notNullable();
table.uuid('id').notNullable();
table
.string('serviceId')
.unique()
.notNullable();
table
.string('integrationKey');
table.string('entityRef');
table
.dateTime('processedDate')
.defaultTo(knex.fn.now());
table.index('index', 'entity_mapping_index_idx');
table.index(['serviceId', 'entityRef'], 'entity_mapping_service_id_idx');
});
};

/**
* @param {import('knex').Knex} knex
*/
exports.down = async function down(knex) {
await knex.schema.alterTable('pagerduty_entity_mapping', table => {
table.dropIndex([], 'entity_mapping_index_idx');
table.dropIndex([], 'entity_mapping_service_id_idx');
});
await knex.schema.dropTable('pagerduty_entity_mapping');
};
46 changes: 29 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "@pagerduty/backstage-plugin-backend",
"version": "NPM_PACKAGE_VERSION",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
Expand All @@ -12,6 +12,11 @@
"backstage": {
"role": "backend-plugin"
},
"repository": {
"type": "git",
"url": "https://github.com/pagerduty/backstage-plugin-backend"
},
"homepage": "https://pagerduty.github.io/backstage-plugin-docs/index.html",
"scripts": {
"start": "yarn tsc && backstage-cli package start",
"build": "yarn tsc && backstage-cli package build",
Expand All @@ -22,42 +27,49 @@
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/backend-common": "^0.21.4",
"@backstage/backend-defaults": "^0.2.16",
"@backstage/backend-plugin-api": "^0.6.16",
"@backstage/backend-common": "^0.22.0",
"@backstage/backend-defaults": "^0.2.18",
"@backstage/backend-plugin-api": "^0.6.18",
"@backstage/backend-tasks": "^0.5.25",
"@backstage/catalog-client": "^1.6.5",
"@backstage/catalog-model": "^1.5.0",
"@backstage/config": "^1.2.0",
"@backstage/core-plugin-api": "^1.9.1",
"@backstage/plugin-scaffolder-node": "^0.4.2",
"@backstage/core-plugin-api": "^1.9.2",
"@backstage/plugin-catalog-common": "^1.0.24",
"@backstage/plugin-catalog-node": "^1.12.0",
"@backstage/plugin-scaffolder-node": "^0.4.4",
"@material-ui/core": "^4.12.4",
"@pagerduty/backstage-plugin-common": "0.1.5",
"@rjsf/core": "^5.14.3",
"@types/express": "^4.17.6",
"express": "^4.19.2",
"express-promise-router": "^4.1.0",
"knex": "^3.0.0",
"luxon": "^3.4.4",
"node-fetch": "^2.6.7",
"uuid": "^9.0.1",
"winston": "^3.2.1",
"yn": "^4.0.0",
"zod": "^3.22.4"
},
"peerDependencies": {
"@pagerduty/backstage-plugin-common": "^0.1.3"
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.24.0",
"@pagerduty/backstage-plugin-common": "^0.1.3",
"@backstage/backend-test-utils": "^0.4.3",
"@backstage/cli": "^0.26.6",
"@types/node": "^20.9.2",
"@types/node-fetch": "2.6.11",
"@types/supertest": "^2.0.12",
"@types/uuid": "^9.0.8",
"@types/webpack-env": "1.18.4",
"better-sqlite3": "^10.0.0",
"jest-mock": "29.7.0",
"msw": "^1.0.0",
"supertest": "^6.2.4",
"typescript": "^4.8.4"
"typescript": "~5.3.0"
},
"files": [
"dist",
"config.d.ts"
"config.d.ts",
"migrations/**/*.{js,d.ts}"
],
"configSchema": "config.d.ts",
"packageManager": "yarn@3.6.3"
"packageManager": "yarn@3.8.2"
}
122 changes: 121 additions & 1 deletion src/apis/pagerduty.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable jest/no-conditional-expect */
import { HttpError, PagerDutyChangeEvent, PagerDutyIncident, PagerDutyIncidentsResponse, PagerDutyService } from "@pagerduty/backstage-plugin-common";
import { getAllEscalationPolicies, getChangeEvents, getIncidents, getOncallUsers, getServiceById, getServiceByIntegrationKey, getServiceMetrics, getServiceStandards } from "./pagerduty";
import { getAllEscalationPolicies, getAllServices, getChangeEvents, getIncidents, getOncallUsers, getServiceById, getServiceByIntegrationKey, getServiceMetrics, getServiceStandards } from "./pagerduty";

import { mocked } from "jest-mock";
import fetch, { Response } from "node-fetch";
Expand Down Expand Up @@ -688,6 +688,126 @@ describe("PagerDuty API", () => {
});
});

describe("getAllServices", () => {
it.each(testInputs)("show return list of PagerDuty services", async () => {
const expectedResponse: PagerDutyService[] = [{
id: "S3RV1CE1D",
name: "Test Service",
description: "Test Service Description",
html_url: "https://testaccount.pagerduty.com/services/S3RV1CE1D",
escalation_policy: {
id: "P0L1CY1D",
name: "Test Escalation Policy",
html_url: "https://testaccount.pagerduty.com/escalation_policies/P0L1CY1D",
type: "escalation_policy_reference",
},
status: "active",
},
{
id: "S3RV1CE2D",
name: "Test Service",
description: "Test Service Description",
html_url: "https://testaccount.pagerduty.com/services/S3RV1CE2D",
escalation_policy: {
id: "P0L1CY1D",
name: "Test Escalation Policy",
html_url: "https://testaccount.pagerduty.com/escalation_policies/P0L1CY1D",
type: "escalation_policy_reference",
},
status: "active",
}];

const mockAPIResponse = {
"services": [
{
"id": expectedResponse[0].id,
"name": expectedResponse[0].name,
"description": expectedResponse[0].description,
"status": expectedResponse[0].status,
"escalation_policy": {
"id": expectedResponse[0].escalation_policy.id,
"name": expectedResponse[0].escalation_policy.name,
"type": expectedResponse[0].escalation_policy.type,
"html_url": expectedResponse[0].escalation_policy.html_url
},
"html_url": expectedResponse[0].html_url
},
{
"id": expectedResponse[1].id,
"name": expectedResponse[1].name,
"description": expectedResponse[1].description,
"status": expectedResponse[1].status,
"escalation_policy": {
"id": expectedResponse[1].escalation_policy.id,
"name": expectedResponse[1].escalation_policy.name,
"type": expectedResponse[1].escalation_policy.type,
"html_url": expectedResponse[1].escalation_policy.html_url
},
"html_url": expectedResponse[1].html_url
}]
};

mocked(fetch).mockReturnValue(
mockedResponse(200, mockAPIResponse)
);

const result = await getAllServices();

expect(result).toEqual(expectedResponse);
expect(fetch).toHaveBeenCalledTimes(1);
});


it.each(testInputs)("should NOT get services when caller provides invalid arguments", async () => {
mocked(fetch).mockReturnValue(
mockedResponse(400, {})
);

const expectedStatusCode = 400;
const expectedErrorMessage = "Failed to get services. Caller provided invalid arguments.";

try {
await getAllServices();
} catch (error) {
expect(fetch).toHaveBeenCalledTimes(1);
expect(((error as HttpError).status)).toEqual(expectedStatusCode);
expect(((error as HttpError).message)).toEqual(expectedErrorMessage);
}
});

it.each(testInputs)("should NOT get services when correct credentials are not provided", async () => {
mocked(fetch).mockReturnValue(
mockedResponse(401, {})
);

const expectedStatusCode = 401;
const expectedErrorMessage = "Failed to get services. Caller did not supply credentials or did not provide the correct credentials.";

try {
await getAllServices();
} catch (error) {
expect(((error as HttpError).status)).toEqual(expectedStatusCode);
expect(((error as HttpError).message)).toEqual(expectedErrorMessage);
}
});

it.each(testInputs)("should NOT get services when credentials are provided but don't have the necessary permissions", async () => {
mocked(fetch).mockReturnValue(
mockedResponse(403, {})
);

const expectedStatusCode = 403;
const expectedErrorMessage = "Failed to get services. Caller is not authorized to view the requested resource.";

try {
await getAllServices();
} catch (error) {
expect(((error as HttpError).status)).toEqual(expectedStatusCode);
expect(((error as HttpError).message)).toEqual(expectedErrorMessage);
}
});
});

describe("getServicesById", () => {
it.each(testInputs)("should return service when 'service_id' is provided", async () => {
const serviceId = "SERV1C31D";
Expand Down
50 changes: 49 additions & 1 deletion src/apis/pagerduty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
PagerDutyIncidentsResponse,
PagerDutyServiceStandards,
PagerDutyServiceMetrics,
HttpError
HttpError,
PagerDutyServicesAPIResponse
} from '@pagerduty/backstage-plugin-common';

import { DateTime } from 'luxon';
Expand Down Expand Up @@ -301,6 +302,53 @@ export async function getServiceByIntegrationKey(integrationKey: string): Promis
return result.services[0];
}

export async function getAllServices(): Promise<PagerDutyService[]> {
let response: Response;
const params = `time_zone=UTC&include[]=integrations&include[]=escalation_policies&include[]=teams&total=true`;
const options: RequestInit = {
method: 'GET',
headers: {
Authorization: await getAuthToken(),
'Accept': 'application/vnd.pagerduty+json;version=2',
'Content-Type': 'application/json',
},
};
const baseUrl = `${apiBaseUrl}/services`;

const allServices: PagerDutyService[] = [];
let offset = 0;
const limit = 50;
let result: PagerDutyServicesAPIResponse;

try {
do {
const paginatedUrl = `${baseUrl}?${params}&offset=${offset}&limit=${limit}`;
response = await fetch(paginatedUrl, options);

switch (response.status) {
case 400:
throw new HttpError("Failed to get services. Caller provided invalid arguments.", 400);
case 401:
throw new HttpError("Failed to get services. Caller did not supply credentials or did not provide the correct credentials.", 401);
case 403:
throw new HttpError("Failed to get services. Caller is not authorized to view the requested resource.", 403);
default: // 200
break;
}

result = await response.json() as PagerDutyServicesAPIResponse;

allServices.push(...result.services);

offset += limit;
} while (offset < result.total!);
} catch (error) {
throw error;
}

return allServices;
}

export async function getChangeEvents(serviceId: string): Promise<PagerDutyChangeEvent[]> {
let response: Response;
const params = `limit=5&time_zone=UTC&sort_by=timestamp`;
Expand Down
Loading

0 comments on commit f24a311

Please sign in to comment.