feat(plugins/pagerduty): add getServiceByEntity client function

Signed-off-by: Alec Jacobs <cajacobs5401@gmail.com>
This commit is contained in:
Alec Jacobs
2022-06-03 14:30:22 -07:00
parent 05da90db84
commit 4d755224cc
3 changed files with 400 additions and 0 deletions
+359
View File
@@ -0,0 +1,359 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MockFetchApi } from '@backstage/test-utils';
import { DiscoveryApi } from '@backstage/core-plugin-api';
import { PagerDutyClient, UnauthorizedError } from './client';
import { PagerDutyEntity } from '../types';
import { Service, User } from '../components/types';
import { NotFoundError } from '@backstage/errors';
const mockFetch = jest.fn().mockName('fetch');
const mockDiscoveryApi: jest.Mocked<DiscoveryApi> = {
getBaseUrl: jest
.fn()
.mockName('discoveryApi')
.mockResolvedValue('http://localhost:7007/proxy'),
};
const mockFetchApi: MockFetchApi = new MockFetchApi({
baseImplementation: mockFetch,
});
let client: PagerDutyClient;
let pagerDutyEntity: PagerDutyEntity;
const user: User = {
name: 'person1',
id: 'p1',
summary: 'person1',
email: 'person1@example.com',
html_url: 'http://a.com/id1',
};
const service: Service = {
id: 'def456',
name: 'pagerduty-name',
html_url: 'www.example.com',
escalation_policy: {
id: 'def',
user: user,
html_url: 'http://a.com/id1',
},
integrationKey: 'abc123',
};
const requestHeaders = {
headers: {
Accept: 'application/vnd.pagerduty+json;version=2',
'Content-Type': 'application/json',
},
method: 'GET',
};
describe('PagerDutyClient', () => {
beforeEach(() => {
mockFetch.mockReset();
client = new PagerDutyClient({
eventsBaseUrl: 'https://events.pagerduty.com/v2',
discoveryApi: mockDiscoveryApi,
fetchApi: mockFetchApi,
});
});
describe('getServiceByEntity', () => {
describe('when provided entity has an integrationKey value', () => {
beforeEach(() => {
pagerDutyEntity = {
name: 'pagerduty-test',
integrationKey: 'abc123',
};
});
it('queries proxy path by integration id', async () => {
mockFetch.mockResolvedValueOnce({
status: 200,
ok: true,
json: () => Promise.resolve({ services: [service] }),
});
expect(await client.getServiceByEntity(pagerDutyEntity)).toEqual({
service,
});
expect(mockFetch).toHaveBeenCalledWith(
'http://localhost:7007/proxy/pagerduty/services?time_zone=UTC&include[]=integrations&include[]=escalation_policies&query=abc123',
requestHeaders,
);
});
describe('on 401 response code', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 401,
ok: false,
json: () => Promise.resolve({}),
});
});
it('throws UnauthorizedError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(UnauthorizedError);
});
});
describe('on 404 response code', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 404,
ok: false,
json: () => Promise.resolve({}),
});
});
it('throws NotFoundError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(NotFoundError);
});
});
describe('on other non-ok response', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 500,
ok: false,
json: () =>
Promise.resolve({
errors: ['Not valid request', 'internal error occurred'],
}),
});
});
it('throws NotFoundError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(
'Request failed with 500, Not valid request internal error occurred',
);
});
});
describe('when services response is empty', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 200,
ok: true,
json: () => Promise.resolve({ services: [] }),
});
});
it('throws NotFoundError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(NotFoundError);
});
});
});
describe('when provided entity has a serviceId value', () => {
beforeEach(() => {
pagerDutyEntity = {
name: 'pagerduty-test',
serviceId: 'def456',
};
});
it('queries proxy path by integration id', async () => {
mockFetch.mockResolvedValueOnce({
status: 200,
ok: true,
json: () => Promise.resolve({ service }),
});
expect(await client.getServiceByEntity(pagerDutyEntity)).toEqual({
service,
});
expect(mockFetch).toHaveBeenCalledWith(
'http://localhost:7007/proxy/pagerduty/services/def456?time_zone=UTC&include[]=integrations&include[]=escalation_policies',
requestHeaders,
);
});
describe('on 401 response code', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 401,
ok: false,
json: () => Promise.resolve({}),
});
});
it('throws UnauthorizedError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(UnauthorizedError);
});
});
describe('on 404 response code', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 404,
ok: false,
json: () => Promise.resolve({}),
});
});
it('throws NotFoundError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(NotFoundError);
});
});
describe('on other non-ok response', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 500,
ok: false,
json: () =>
Promise.resolve({
errors: ['Not valid request', 'internal error occurred'],
}),
});
});
it('throws NotFoundError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(
'Request failed with 500, Not valid request internal error occurred',
);
});
});
});
describe('when provided entity has both integrationKey and serviceId', () => {
beforeEach(() => {
pagerDutyEntity = {
name: 'pagerduty-test',
integrationKey: 'abc123',
serviceId: 'def456',
};
});
it('queries proxy path by integration id', async () => {
mockFetch.mockResolvedValueOnce({
status: 200,
ok: true,
json: () => Promise.resolve({ services: [service] }),
});
expect(await client.getServiceByEntity(pagerDutyEntity)).toEqual({
service,
});
expect(mockFetch).toHaveBeenCalledWith(
'http://localhost:7007/proxy/pagerduty/services?time_zone=UTC&include[]=integrations&include[]=escalation_policies&query=abc123',
requestHeaders,
);
});
describe('on 401 response code', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 401,
ok: false,
json: () => Promise.resolve({}),
});
});
it('throws UnauthorizedError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(UnauthorizedError);
});
});
describe('on 404 response code', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 404,
ok: false,
json: () => Promise.resolve({}),
});
});
it('throws NotFoundError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(NotFoundError);
});
});
describe('on other non-ok response', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 500,
ok: false,
json: () =>
Promise.resolve({
errors: ['Not valid request', 'internal error occurred'],
}),
});
});
it('throws NotFoundError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(
'Request failed with 500, Not valid request internal error occurred',
);
});
});
describe('when services response is empty', () => {
beforeEach(() => {
mockFetch.mockResolvedValueOnce({
status: 200,
ok: true,
json: () => Promise.resolve({ services: [] }),
});
});
it('throws NotFoundError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(NotFoundError);
});
});
});
describe('when provided entity has no integrationKey or serviceId values', () => {
beforeEach(() => {
pagerDutyEntity = {
name: 'pagerduty-test',
};
});
it('throws NotFoundError', async () => {
await expect(
client.getServiceByEntity(pagerDutyEntity),
).rejects.toThrowError(NotFoundError);
expect(mockFetch).not.toHaveBeenCalled();
});
});
});
});
+32
View File
@@ -29,6 +29,7 @@ import {
} from './types';
import { createApiRef, ConfigApi } from '@backstage/core-plugin-api';
import { NotFoundError } from '@backstage/errors';
import { PagerDutyEntity } from '../types';
export class UnauthorizedError extends Error {}
@@ -76,6 +77,37 @@ export class PagerDutyClient implements PagerDutyApi {
return service;
}
async getServiceByEntity(
pagerDutyEntity: PagerDutyEntity,
): Promise<ServiceResponse> {
const { integrationKey, serviceId } = pagerDutyEntity;
let response: ServiceResponse;
let url: string;
if (integrationKey) {
url = `${await this.config.discoveryApi.getBaseUrl(
'proxy',
)}/pagerduty/services?${commonGetServiceParams}&query=${integrationKey}`;
const { services } = await this.getByUrl<ServicesResponse>(url);
const service = services[0];
if (!service) throw new NotFoundError();
response = { service };
} else if (serviceId) {
url = `${await this.config.discoveryApi.getBaseUrl(
'proxy',
)}/pagerduty/services/${serviceId}?${commonGetServiceParams}`;
response = await this.getByUrl<ServiceResponse>(url);
} else {
throw new NotFoundError();
}
return response;
}
async getIncidentsByServiceId(serviceId: string): Promise<Incident[]> {
const params = `time_zone=UTC&sort_by=created_at&statuses[]=triggered&statuses[]=acknowledged&service_ids[]=${serviceId}`;
const url = `${await this.config.discoveryApi.getBaseUrl(
+9
View File
@@ -16,6 +16,7 @@
import { Incident, ChangeEvent, OnCall, Service } from '../components/types';
import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
import { PagerDutyEntity } from '../types';
export type TriggerAlarmRequest = {
integrationKey: string;
@@ -37,6 +38,14 @@ export interface PagerDutyApi {
*/
getServiceByServiceId(serviceId: string): Promise<Service>;
/**
* Fetches the service for the provided PagerDutyEntity.
*
*/
getServiceByEntity(
pagerDutyEntity: PagerDutyEntity,
): Promise<ServiceResponse>;
/**
* Fetches a list of incidents a provided service has.
*