diff --git a/.changeset/giant-maps-compete.md b/.changeset/giant-maps-compete.md
new file mode 100644
index 0000000000..776332b308
--- /dev/null
+++ b/.changeset/giant-maps-compete.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-pagerduty': patch
+---
+
+Add a homepage widget for the `Pagerduty` component.
diff --git a/packages/app/src/components/home/HomePage.tsx b/packages/app/src/components/home/HomePage.tsx
index da1d0d425c..aa3739728e 100644
--- a/packages/app/src/components/home/HomePage.tsx
+++ b/packages/app/src/components/home/HomePage.tsx
@@ -30,6 +30,7 @@ import { HomePageCalendar } from '@backstage/plugin-gcalendar';
import { MicrosoftCalendarCard } from '@backstage/plugin-microsoft-calendar';
import React from 'react';
import HomeIcon from '@material-ui/icons/Home';
+import { HomePagePagerDutyCard } from '@backstage/plugin-pagerduty';
const clockConfigs: ClockConfig[] = [
{
@@ -93,6 +94,7 @@ export const homePage = (
+
diff --git a/plugins/pagerduty/README.md b/plugins/pagerduty/README.md
index 2affe1c423..c0ba5c8fe0 100644
--- a/plugins/pagerduty/README.md
+++ b/plugins/pagerduty/README.md
@@ -95,6 +95,30 @@ annotations:
pagerduty.com/integration-key: [INTEGRATION_KEY]
```
+### The homepage component
+
+You may also add the component to the homepage of Backstage. Edit the `HomePage.tsx` file, import `PagerDutyHomepageCard` and add it to the home page. e.g.
+
+```typescript jsx
+...
+export const homePage = (
+
+ ...
+
+
+ ...
+
+
+
+
+);
+```
+
Next, provide the [API token](https://support.pagerduty.com/docs/generating-api-keys#generating-a-general-access-rest-api-key) that the client will use to make requests to the [PagerDuty API](https://developer.pagerduty.com/docs/rest-api-v2/rest-api/).
Add the proxy configuration in `app-config.yaml`:
diff --git a/plugins/pagerduty/api-report.md b/plugins/pagerduty/api-report.md
index b460086976..dd24aa9bec 100644
--- a/plugins/pagerduty/api-report.md
+++ b/plugins/pagerduty/api-report.md
@@ -7,6 +7,7 @@
import { ApiRef } from '@backstage/core-plugin-api';
import { BackstagePlugin } from '@backstage/core-plugin-api';
+import { CardExtensionProps } from '@backstage/plugin-home-react';
import { ConfigApi } from '@backstage/core-plugin-api';
import { DiscoveryApi } from '@backstage/core-plugin-api';
import { Entity } from '@backstage/catalog-model';
@@ -14,7 +15,24 @@ import { FetchApi } from '@backstage/core-plugin-api';
import { ReactNode } from 'react';
// @public (undocumented)
-export const EntityPagerDutyCard: (props: PagerDutyCardProps) => JSX.Element;
+export const EntityPagerDutyCard: (
+ props: EntityPagerDutyCardProps,
+) => JSX.Element;
+
+// @public (undocumented)
+export type EntityPagerDutyCardProps = {
+ readOnly?: boolean;
+};
+
+// @public (undocumented)
+export const HomePagePagerDutyCard: (
+ props: CardExtensionProps,
+) => JSX.Element;
+
+// @public (undocumented)
+export type HomePagePagerDutyCardProps = PagerDutyEntity & {
+ readOnly?: boolean;
+};
// @public (undocumented)
const isPluginApplicableToEntity: (entity: Entity) => boolean;
@@ -31,6 +49,9 @@ export interface PagerDutyApi {
): Promise;
getOnCallByPolicyId(policyId: string): Promise;
getServiceByEntity(entity: Entity): Promise;
+ getServiceByPagerDutyEntity(
+ pagerDutyEntity: PagerDutyEntity,
+ ): Promise;
triggerAlarm(request: PagerDutyTriggerAlarmRequest): Promise;
}
@@ -44,13 +65,11 @@ export type PagerDutyAssignee = {
html_url: string;
};
-// @public (undocumented)
-export const PagerDutyCard: (props: PagerDutyCardProps) => JSX.Element;
+// @public @deprecated (undocumented)
+export const PagerDutyCard: (props: EntityPagerDutyCardProps) => JSX.Element;
-// @public (undocumented)
-export type PagerDutyCardProps = {
- readOnly?: boolean;
-};
+// @public @deprecated (undocumented)
+export type PagerDutyCardProps = EntityPagerDutyCardProps;
// @public (undocumented)
export type PagerDutyChangeEvent = {
@@ -98,6 +117,10 @@ export class PagerDutyClient implements PagerDutyApi {
// (undocumented)
getServiceByEntity(entity: Entity): Promise;
// (undocumented)
+ getServiceByPagerDutyEntity(
+ pagerDutyEntity: PagerDutyEntity,
+ ): Promise;
+ // (undocumented)
triggerAlarm(request: PagerDutyTriggerAlarmRequest): Promise;
}
diff --git a/plugins/pagerduty/dev/index.tsx b/plugins/pagerduty/dev/index.tsx
index e45e0ffd8f..4ed8e07263 100644
--- a/plugins/pagerduty/dev/index.tsx
+++ b/plugins/pagerduty/dev/index.tsx
@@ -15,136 +15,12 @@
*/
import React from 'react';
-import { Entity } from '@backstage/catalog-model';
import { EntityProvider } from '@backstage/plugin-catalog-react';
import { createDevApp } from '@backstage/dev-utils';
import { pagerDutyPlugin, EntityPagerDutyCard } from '../src/plugin';
import { pagerDutyApiRef } from '../src/api';
-import { PagerDutyApi, PagerDutyTriggerAlarmRequest } from '../src/api/types';
-import {
- PagerDutyIncident,
- PagerDutyChangeEvent,
-} from '../src/components/types';
-
-const mockEntity: Entity = {
- apiVersion: 'backstage.io/v1alpha1',
- kind: 'Component',
- metadata: {
- name: 'backstage',
- description: 'backstage.io',
- annotations: {
- 'github.com/project-slug': 'backstage/backstage',
- 'pagerduty.com/service-id': 'foo',
- 'pagerduty.com/integration-key': 'foo',
- },
- },
- spec: {
- lifecycle: 'production',
- type: 'website',
- owner: 'user:guest',
- },
-};
-
-const mockPagerDutyApi: PagerDutyApi = {
- async getServiceByEntity(entity: Entity) {
- return {
- service: {
- name: entity.metadata.name,
- integrationKey: 'key',
- id: '123',
- html_url: 'http://service',
- escalation_policy: {
- id: '123',
- html_url: 'http://escalationpolicy',
- user: {
- id: '123',
- summary: 'summary',
- email: 'email@email.com',
- html_url: 'http://user',
- name: 'some-user',
- },
- },
- },
- };
- },
-
- async getIncidentsByServiceId(serviceId: string) {
- const incident = (title: string) => {
- return {
- id: '123',
- title: title,
- status: 'acknowledged',
- html_url: 'http://incident',
- assignments: [
- {
- assignee: {
- id: '123',
- summary: 'Jane Doe',
- html_url: 'http://assignee',
- },
- },
- ],
- serviceId: serviceId,
- created_at: '2015-10-06T21:30:42Z',
- } as PagerDutyIncident;
- };
-
- return {
- incidents: [
- incident('Some Alerting Incident'),
- incident('Another Alerting Incident'),
- ],
- };
- },
-
- async getChangeEventsByServiceId(serviceId: string) {
- const changeEvent = (description: string) => {
- return {
- id: serviceId,
- source: 'some-source',
- html_url: 'http://changeevent',
- links: [
- {
- href: 'http://link',
- text: 'link text',
- },
- ],
- summary: description,
- timestamp: '2018-10-06T21:30:42Z',
- } as PagerDutyChangeEvent;
- };
-
- return {
- change_events: [
- changeEvent('us-east-1 deployment'),
- changeEvent('us-west-2 deployment'),
- ],
- };
- },
-
- async getOnCallByPolicyId() {
- const oncall = (name: string, escalation: number) => {
- return {
- user: {
- id: '123',
- name: name,
- html_url: 'http://assignee',
- summary: 'summary',
- email: 'email@email.com',
- },
- escalation_level: escalation,
- };
- };
-
- return {
- oncalls: [oncall('Jane Doe', 1), oncall('John Doe', 2)],
- };
- },
-
- async triggerAlarm(request: PagerDutyTriggerAlarmRequest) {
- return new Response(request.description);
- },
-};
+import { mockPagerDutyApi } from './mockPagerDutyApi';
+import { mockEntity } from './mockEntity';
createDevApp()
.registerApi({
diff --git a/plugins/pagerduty/dev/mockEntity.ts b/plugins/pagerduty/dev/mockEntity.ts
new file mode 100644
index 0000000000..251ba80706
--- /dev/null
+++ b/plugins/pagerduty/dev/mockEntity.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 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 { Entity } from '@backstage/catalog-model';
+
+export const mockEntity: Entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Component',
+ metadata: {
+ name: 'backstage',
+ description: 'backstage.io',
+ annotations: {
+ 'github.com/project-slug': 'backstage/backstage',
+ 'pagerduty.com/service-id': 'foo',
+ 'pagerduty.com/integration-key': 'foo',
+ },
+ },
+ spec: {
+ lifecycle: 'production',
+ type: 'website',
+ owner: 'user:guest',
+ },
+};
diff --git a/plugins/pagerduty/dev/mockPagerDutyApi.ts b/plugins/pagerduty/dev/mockPagerDutyApi.ts
new file mode 100644
index 0000000000..f8836d7043
--- /dev/null
+++ b/plugins/pagerduty/dev/mockPagerDutyApi.ts
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2023 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 {
+ PagerDutyApi,
+ PagerDutyChangeEvent,
+ PagerDutyEntity,
+ PagerDutyIncident,
+ PagerDutyTriggerAlarmRequest,
+} from '../src';
+import { Entity } from '@backstage/catalog-model';
+
+export const mockPagerDutyApi: PagerDutyApi = {
+ async getServiceByPagerDutyEntity(pagerDutyEntity: PagerDutyEntity) {
+ return {
+ service: {
+ name: pagerDutyEntity.name,
+ integrationKey: 'key',
+ id: '123',
+ html_url: 'http://service',
+ escalation_policy: {
+ id: '123',
+ html_url: 'http://escalationpolicy',
+ user: {
+ id: '123',
+ summary: 'summary',
+ email: 'email@email.com',
+ html_url: 'http://user',
+ name: 'some-user',
+ },
+ },
+ },
+ };
+ },
+
+ async getServiceByEntity(entity: Entity) {
+ return {
+ service: {
+ name: entity.metadata.name,
+ integrationKey: 'key',
+ id: '123',
+ html_url: 'http://service',
+ escalation_policy: {
+ id: '123',
+ html_url: 'http://escalationpolicy',
+ user: {
+ id: '123',
+ summary: 'summary',
+ email: 'email@email.com',
+ html_url: 'http://user',
+ name: 'some-user',
+ },
+ },
+ },
+ };
+ },
+
+ async getIncidentsByServiceId(serviceId: string) {
+ const incident = (title: string) => {
+ return {
+ id: '123',
+ title: title,
+ status: 'acknowledged',
+ html_url: 'http://incident',
+ assignments: [
+ {
+ assignee: {
+ id: '123',
+ summary: 'Jane Doe',
+ html_url: 'http://assignee',
+ },
+ },
+ ],
+ serviceId: serviceId,
+ created_at: '2015-10-06T21:30:42Z',
+ } as PagerDutyIncident;
+ };
+
+ return {
+ incidents: [
+ incident('Some Alerting Incident'),
+ incident('Another Alerting Incident'),
+ ],
+ };
+ },
+
+ async getChangeEventsByServiceId(serviceId: string) {
+ const changeEvent = (description: string) => {
+ return {
+ id: serviceId,
+ source: 'some-source',
+ html_url: 'http://changeevent',
+ links: [
+ {
+ href: 'http://link',
+ text: 'link text',
+ },
+ ],
+ summary: description,
+ timestamp: '2018-10-06T21:30:42Z',
+ } as PagerDutyChangeEvent;
+ };
+
+ return {
+ change_events: [
+ changeEvent('us-east-1 deployment'),
+ changeEvent('us-west-2 deployment'),
+ ],
+ };
+ },
+
+ async getOnCallByPolicyId() {
+ const oncall = (name: string, escalation: number) => {
+ return {
+ user: {
+ id: '123',
+ name: name,
+ html_url: 'http://assignee',
+ summary: 'summary',
+ email: 'email@email.com',
+ },
+ escalation_level: escalation,
+ };
+ };
+
+ return {
+ oncalls: [oncall('Jane Doe', 1), oncall('John Doe', 2)],
+ };
+ },
+
+ async triggerAlarm(request: PagerDutyTriggerAlarmRequest) {
+ return new Response(request.description);
+ },
+};
diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json
index 5437764465..dc40d566ce 100644
--- a/plugins/pagerduty/package.json
+++ b/plugins/pagerduty/package.json
@@ -38,6 +38,7 @@
"@backstage/core-plugin-api": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/plugin-catalog-react": "workspace:^",
+ "@backstage/plugin-home-react": "workspace:^",
"@backstage/theme": "workspace:^",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
diff --git a/plugins/pagerduty/src/api/client.ts b/plugins/pagerduty/src/api/client.ts
index 7d99be219d..350b838951 100644
--- a/plugins/pagerduty/src/api/client.ts
+++ b/plugins/pagerduty/src/api/client.ts
@@ -30,6 +30,7 @@ import { createApiRef, ConfigApi } from '@backstage/core-plugin-api';
import { NotFoundError } from '@backstage/errors';
import { Entity } from '@backstage/catalog-model';
import { getPagerDutyEntity } from '../components/pagerDutyEntity';
+import { PagerDutyEntity } from '../types';
/** @public */
export class UnauthorizedError extends Error {}
@@ -63,8 +64,10 @@ export class PagerDutyClient implements PagerDutyApi {
constructor(private readonly config: PagerDutyClientApiConfig) {}
- async getServiceByEntity(entity: Entity): Promise {
- const { integrationKey, serviceId } = getPagerDutyEntity(entity);
+ async getServiceByPagerDutyEntity(
+ pagerDutyEntity: PagerDutyEntity,
+ ): Promise {
+ const { integrationKey, serviceId } = pagerDutyEntity;
let response: PagerDutyServiceResponse;
let url: string;
@@ -92,6 +95,9 @@ export class PagerDutyClient implements PagerDutyApi {
return response;
}
+ async getServiceByEntity(entity: Entity): Promise {
+ return await this.getServiceByPagerDutyEntity(getPagerDutyEntity(entity));
+ }
async getIncidentsByServiceId(
serviceId: string,
): Promise {
diff --git a/plugins/pagerduty/src/api/types.ts b/plugins/pagerduty/src/api/types.ts
index f1971d53d5..a878afc447 100644
--- a/plugins/pagerduty/src/api/types.ts
+++ b/plugins/pagerduty/src/api/types.ts
@@ -22,6 +22,7 @@ import {
} from '../components/types';
import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
import { Entity } from '@backstage/catalog-model';
+import { PagerDutyEntity } from '../types';
export type PagerDutyServicesResponse = {
services: PagerDutyService[];
@@ -57,6 +58,14 @@ export type PagerDutyTriggerAlarmRequest = {
/** @public */
export interface PagerDutyApi {
+ /**
+ * Fetches the service for the provided pager duty Entity.
+ *
+ */
+ getServiceByPagerDutyEntity(
+ pagerDutyEntity: PagerDutyEntity,
+ ): Promise;
+
/**
* Fetches the service for the provided Entity.
*
diff --git a/plugins/pagerduty/src/components/EntityPagerDutyCard/index.test.tsx b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.test.tsx
new file mode 100644
index 0000000000..3f3a4ca1a8
--- /dev/null
+++ b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.test.tsx
@@ -0,0 +1,386 @@
+/*
+ * 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 React from 'react';
+import { render, waitFor, fireEvent, act } from '@testing-library/react';
+import {
+ EntityPagerDutyCard,
+ isPluginApplicableToEntity,
+} from '../EntityPagerDutyCard';
+import { Entity } from '@backstage/catalog-model';
+import { EntityProvider } from '@backstage/plugin-catalog-react';
+import { NotFoundError } from '@backstage/errors';
+import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils';
+import { pagerDutyApiRef, UnauthorizedError, PagerDutyClient } from '../../api';
+import { PagerDutyService, PagerDutyUser } from '../types';
+
+import { alertApiRef } from '@backstage/core-plugin-api';
+import { ApiProvider } from '@backstage/core-app-api';
+
+const entity: Entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Component',
+ metadata: {
+ name: 'pagerduty-test',
+ annotations: {
+ 'pagerduty.com/integration-key': 'abc123',
+ },
+ },
+};
+
+const entityWithoutAnnotations: Entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Component',
+ metadata: {
+ name: 'pagerduty-test',
+ annotations: {},
+ },
+};
+
+const entityWithServiceId: Entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Component',
+ metadata: {
+ name: 'pagerduty-test',
+ annotations: {
+ 'pagerduty.com/service-id': 'def456',
+ },
+ },
+};
+
+const entityWithAllAnnotations: Entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Component',
+ metadata: {
+ name: 'pagerduty-test',
+ annotations: {
+ 'pagerduty.com/integration-key': 'abc123',
+ 'pagerduty.com/service-id': 'def456',
+ },
+ },
+};
+
+const user: PagerDutyUser = {
+ name: 'person1',
+ id: 'p1',
+ summary: 'person1',
+ email: 'person1@example.com',
+ html_url: 'http://a.com/id1',
+};
+
+const service: PagerDutyService = {
+ 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 mockPagerDutyApi: Partial = {
+ getServiceByEntity: async () => ({ service }),
+ getServiceByPagerDutyEntity: async () => ({ service }),
+ getOnCallByPolicyId: async () => ({ oncalls: [] }),
+ getIncidentsByServiceId: async () => ({ incidents: [] }),
+};
+
+const apis = TestApiRegistry.from(
+ [pagerDutyApiRef, mockPagerDutyApi],
+ [alertApiRef, {}],
+);
+
+describe('isPluginApplicableToEntity', () => {
+ describe('when entity has no annotations', () => {
+ it('returns false', () => {
+ expect(isPluginApplicableToEntity(entityWithoutAnnotations)).toBe(false);
+ });
+ });
+
+ describe('when entity has the pagerduty.com/integration-key annotation', () => {
+ it('returns true', () => {
+ expect(isPluginApplicableToEntity(entity)).toBe(true);
+ });
+ });
+
+ describe('when entity has the pagerduty.com/service-id annotation', () => {
+ it('returns true', () => {
+ expect(isPluginApplicableToEntity(entityWithServiceId)).toBe(true);
+ });
+ });
+
+ describe('when entity has all annotations', () => {
+ it('returns true', () => {
+ expect(isPluginApplicableToEntity(entityWithAllAnnotations)).toBe(true);
+ });
+ });
+});
+
+describe('EntityPagerDutyCard', () => {
+ it('Render pagerduty', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockImplementationOnce(async () => ({ service }));
+
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(getByText('Service Directory')).toBeInTheDocument();
+ expect(getByText('Create Incident')).toBeInTheDocument();
+ expect(getByText('Nice! No incidents found!')).toBeInTheDocument();
+ expect(getByText('Empty escalation policy')).toBeInTheDocument();
+ });
+
+ it('Handles custom error for missing token', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockRejectedValueOnce(new UnauthorizedError());
+
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(getByText('Missing or invalid PagerDuty Token')).toBeInTheDocument();
+ });
+
+ it('Handles custom NotFoundError', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockRejectedValueOnce(new NotFoundError());
+
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(getByText('PagerDuty Service Not Found')).toBeInTheDocument();
+ });
+
+ it('handles general error', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockRejectedValueOnce(new Error('An error occurred'));
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+
+ expect(
+ getByText(
+ 'Error encountered while fetching information. An error occurred',
+ ),
+ ).toBeInTheDocument();
+ });
+
+ it('opens the dialog when trigger button is clicked', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockImplementationOnce(async () => ({ service }));
+
+ const { getByText, queryByTestId, getByRole } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(getByText('Service Directory')).toBeInTheDocument();
+
+ const triggerLink = getByText('Create Incident');
+ await act(async () => {
+ fireEvent.click(triggerLink);
+ });
+ expect(getByRole('dialog')).toBeInTheDocument();
+ });
+
+ describe('when entity has the pagerduty.com/service-id annotation', () => {
+ it('Renders PagerDuty service information', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockImplementationOnce(async () => ({ service }));
+
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(getByText('Service Directory')).toBeInTheDocument();
+ expect(getByText('Create Incident')).toBeInTheDocument();
+ expect(getByText('Nice! No incidents found!')).toBeInTheDocument();
+ expect(getByText('Empty escalation policy')).toBeInTheDocument();
+ });
+
+ it('Handles custom error for missing token', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockRejectedValueOnce(new UnauthorizedError());
+
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(
+ getByText('Missing or invalid PagerDuty Token'),
+ ).toBeInTheDocument();
+ });
+
+ it('Handles custom NotFoundError', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockRejectedValueOnce(new NotFoundError());
+
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(getByText('PagerDuty Service Not Found')).toBeInTheDocument();
+ });
+
+ it('handles general error', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockRejectedValueOnce(new Error('An error occurred'));
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+
+ expect(
+ getByText(
+ 'Error encountered while fetching information. An error occurred',
+ ),
+ ).toBeInTheDocument();
+ });
+
+ it('disables the Create Incident button', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockImplementationOnce(async () => ({ service }));
+
+ const { queryByTestId, getByTitle } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(
+ getByTitle('Must provide an integration-key to create incidents')
+ .className,
+ ).toMatch('disabled');
+ });
+ });
+
+ describe('when entity has all annotations', () => {
+ it('queries by integration key', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockImplementationOnce(async () => ({ service }));
+
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(getByText('Service Directory')).toBeInTheDocument();
+ expect(getByText('Create Incident')).toBeInTheDocument();
+ expect(getByText('Nice! No incidents found!')).toBeInTheDocument();
+ expect(getByText('Empty escalation policy')).toBeInTheDocument();
+ });
+ });
+
+ describe('when entity has all annotations but the plugin has been configured to be "read only"', () => {
+ it('queries by integration key but does not render the "Create Incident" button', async () => {
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
+ .fn()
+ .mockImplementationOnce(async () => ({ service }));
+
+ const { getByText, queryByTestId } = render(
+ wrapInTestApp(
+
+
+
+
+ ,
+ ),
+ );
+ await waitFor(() => !queryByTestId('progress'));
+ expect(getByText('Service Directory')).toBeInTheDocument();
+ expect(getByText('Nice! No incidents found!')).toBeInTheDocument();
+ expect(getByText('Empty escalation policy')).toBeInTheDocument();
+ expect(() => getByText('Create Incident')).toThrow();
+ });
+ });
+});
diff --git a/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx
new file mode 100644
index 0000000000..50e6f3a096
--- /dev/null
+++ b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx
@@ -0,0 +1,41 @@
+/*
+ * 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 React from 'react';
+import { Entity } from '@backstage/catalog-model';
+import { PAGERDUTY_INTEGRATION_KEY, PAGERDUTY_SERVICE_ID } from '../constants';
+import { useEntity } from '@backstage/plugin-catalog-react';
+import { getPagerDutyEntity } from '../pagerDutyEntity';
+import { PagerDutyCard } from '../PagerDutyCard';
+
+/** @public */
+export const isPluginApplicableToEntity = (entity: Entity) =>
+ Boolean(
+ entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY] ||
+ entity.metadata.annotations?.[PAGERDUTY_SERVICE_ID],
+ );
+
+/** @public */
+export type EntityPagerDutyCardProps = {
+ readOnly?: boolean;
+};
+
+/** @public */
+export const EntityPagerDutyCard = (props: EntityPagerDutyCardProps) => {
+ const { readOnly } = props;
+ const { entity } = useEntity();
+ const pagerDutyEntity = getPagerDutyEntity(entity);
+ return ;
+};
diff --git a/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx b/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx
index d8b61ef7cd..236585735e 100644
--- a/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx
+++ b/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx
@@ -21,7 +21,7 @@ export const ServiceNotFoundError = () => (
{
+ return ;
+};
diff --git a/plugins/pagerduty/src/components/HomePagePagerDutyCard/index.ts b/plugins/pagerduty/src/components/HomePagePagerDutyCard/index.ts
new file mode 100644
index 0000000000..e9038852a6
--- /dev/null
+++ b/plugins/pagerduty/src/components/HomePagePagerDutyCard/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2023 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.
+ */
+export { Content } from './Content';
+export type { HomePagePagerDutyCardProps } from './Content';
diff --git a/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx b/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx
index 34c8914fde..6f4501450c 100644
--- a/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx
+++ b/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx
@@ -15,9 +15,7 @@
*/
import React from 'react';
import { render, waitFor, fireEvent, act } from '@testing-library/react';
-import { PagerDutyCard, isPluginApplicableToEntity } from '../PagerDutyCard';
-import { Entity } from '@backstage/catalog-model';
-import { EntityProvider } from '@backstage/plugin-catalog-react';
+import { PagerDutyCard } from '../PagerDutyCard';
import { NotFoundError } from '@backstage/errors';
import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils';
import { pagerDutyApiRef, UnauthorizedError, PagerDutyClient } from '../../api';
@@ -26,49 +24,6 @@ import { PagerDutyService, PagerDutyUser } from '../types';
import { alertApiRef } from '@backstage/core-plugin-api';
import { ApiProvider } from '@backstage/core-app-api';
-const entity: Entity = {
- apiVersion: 'backstage.io/v1alpha1',
- kind: 'Component',
- metadata: {
- name: 'pagerduty-test',
- annotations: {
- 'pagerduty.com/integration-key': 'abc123',
- },
- },
-};
-
-const entityWithoutAnnotations: Entity = {
- apiVersion: 'backstage.io/v1alpha1',
- kind: 'Component',
- metadata: {
- name: 'pagerduty-test',
- annotations: {},
- },
-};
-
-const entityWithServiceId: Entity = {
- apiVersion: 'backstage.io/v1alpha1',
- kind: 'Component',
- metadata: {
- name: 'pagerduty-test',
- annotations: {
- 'pagerduty.com/service-id': 'def456',
- },
- },
-};
-
-const entityWithAllAnnotations: Entity = {
- apiVersion: 'backstage.io/v1alpha1',
- kind: 'Component',
- metadata: {
- name: 'pagerduty-test',
- annotations: {
- 'pagerduty.com/integration-key': 'abc123',
- 'pagerduty.com/service-id': 'def456',
- },
- },
-};
-
const user: PagerDutyUser = {
name: 'person1',
id: 'p1',
@@ -100,44 +55,16 @@ const apis = TestApiRegistry.from(
[alertApiRef, {}],
);
-describe('isPluginApplicableToEntity', () => {
- describe('when entity has no annotations', () => {
- it('returns false', () => {
- expect(isPluginApplicableToEntity(entityWithoutAnnotations)).toBe(false);
- });
- });
-
- describe('when entity has the pagerduty.com/integration-key annotation', () => {
- it('returns true', () => {
- expect(isPluginApplicableToEntity(entity)).toBe(true);
- });
- });
-
- describe('when entity has the pagerduty.com/service-id annotation', () => {
- it('returns true', () => {
- expect(isPluginApplicableToEntity(entityWithServiceId)).toBe(true);
- });
- });
-
- describe('when entity has all annotations', () => {
- it('returns true', () => {
- expect(isPluginApplicableToEntity(entityWithAllAnnotations)).toBe(true);
- });
- });
-});
-
-describe('PageDutyCard', () => {
+describe('PagerDutyCard', () => {
it('Render pagerduty', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockImplementationOnce(async () => ({ service }));
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -149,16 +76,14 @@ describe('PageDutyCard', () => {
});
it('Handles custom error for missing token', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockRejectedValueOnce(new UnauthorizedError());
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -167,16 +92,14 @@ describe('PageDutyCard', () => {
});
it('Handles custom NotFoundError', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockRejectedValueOnce(new NotFoundError());
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -185,15 +108,13 @@ describe('PageDutyCard', () => {
});
it('handles general error', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockRejectedValueOnce(new Error('An error occurred'));
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -207,16 +128,14 @@ describe('PageDutyCard', () => {
});
it('opens the dialog when trigger button is clicked', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockImplementationOnce(async () => ({ service }));
const { getByText, queryByTestId, getByRole } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -232,16 +151,14 @@ describe('PageDutyCard', () => {
describe('when entity has the pagerduty.com/service-id annotation', () => {
it('Renders PagerDuty service information', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockImplementationOnce(async () => ({ service }));
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -253,16 +170,18 @@ describe('PageDutyCard', () => {
});
it('Handles custom error for missing token', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockRejectedValueOnce(new UnauthorizedError());
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -273,16 +192,18 @@ describe('PageDutyCard', () => {
});
it('Handles custom NotFoundError', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockRejectedValueOnce(new NotFoundError());
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -291,15 +212,17 @@ describe('PageDutyCard', () => {
});
it('handles general error', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockRejectedValueOnce(new Error('An error occurred'));
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -313,16 +236,14 @@ describe('PageDutyCard', () => {
});
it('disables the Create Incident button', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockImplementationOnce(async () => ({ service }));
const { queryByTestId, getByTitle } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -336,16 +257,18 @@ describe('PageDutyCard', () => {
describe('when entity has all annotations', () => {
it('queries by integration key', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockImplementationOnce(async () => ({ service }));
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
@@ -359,16 +282,19 @@ describe('PageDutyCard', () => {
describe('when entity has all annotations but the plugin has been configured to be "read only"', () => {
it('queries by integration key but does not render the "Create Incident" button', async () => {
- mockPagerDutyApi.getServiceByEntity = jest
+ mockPagerDutyApi.getServiceByPagerDutyEntity = jest
.fn()
.mockImplementationOnce(async () => ({ service }));
const { getByText, queryByTestId } = render(
wrapInTestApp(
-
-
-
+
,
),
);
diff --git a/plugins/pagerduty/src/components/PagerDutyCard/index.tsx b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx
index fd23166d88..bb89865c5f 100644
--- a/plugins/pagerduty/src/components/PagerDutyCard/index.tsx
+++ b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import React, { ReactNode, useState, useCallback } from 'react';
-import { Entity } from '@backstage/catalog-model';
import { Card, CardHeader, Divider, CardContent } from '@material-ui/core';
import { Incidents } from '../Incident';
import { EscalationPolicy } from '../Escalation';
@@ -25,7 +24,6 @@ import AlarmAddIcon from '@material-ui/icons/AlarmAdd';
import { MissingTokenError, ServiceNotFoundError } from '../Errors';
import WebIcon from '@material-ui/icons/Web';
import DateRangeIcon from '@material-ui/icons/DateRange';
-import { PAGERDUTY_INTEGRATION_KEY, PAGERDUTY_SERVICE_ID } from '../constants';
import { TriggerDialog } from '../TriggerDialog';
import { ChangeEvents } from '../ChangeEvents';
@@ -39,30 +37,20 @@ import {
CardTab,
InfoCard,
} from '@backstage/core-components';
-import { useEntity } from '@backstage/plugin-catalog-react';
-import { getPagerDutyEntity } from '../pagerDutyEntity';
+import { PagerDutyEntity } from '../../types';
const BasicCard = ({ children }: { children: ReactNode }) => (
{children}
);
/** @public */
-export const isPluginApplicableToEntity = (entity: Entity) =>
- Boolean(
- entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY] ||
- entity.metadata.annotations?.[PAGERDUTY_SERVICE_ID],
- );
-
-/** @public */
-export type PagerDutyCardProps = {
+export type PagerDutyCardProps = PagerDutyEntity & {
readOnly?: boolean;
};
/** @public */
export const PagerDutyCard = (props: PagerDutyCardProps) => {
- const { readOnly } = props;
- const { entity } = useEntity();
- const pagerDutyEntity = getPagerDutyEntity(entity);
+ const { readOnly, integrationKey, name } = props;
const api = useApi(pagerDutyApiRef);
const [refreshIncidents, setRefreshIncidents] = useState(false);
const [refreshChangeEvents, setRefreshChangeEvents] =
@@ -86,7 +74,9 @@ export const PagerDutyCard = (props: PagerDutyCardProps) => {
loading,
error,
} = useAsync(async () => {
- const { service: foundService } = await api.getServiceByEntity(entity);
+ const { service: foundService } = await api.getServiceByPagerDutyEntity(
+ props,
+ );
return {
id: foundService.id,
@@ -137,7 +127,7 @@ export const PagerDutyCard = (props: PagerDutyCardProps) => {
* There is no guarantee the current user entity has a valid email association, so instead just
* only allow triggering incidents when an integration key is present.
*/
- const createIncidentDisabled = !pagerDutyEntity.integrationKey;
+ const createIncidentDisabled = !integrationKey;
const triggerLink: IconLinkVerticalProps = {
label: 'Create Incident',
onClick: showDialog,
@@ -195,6 +185,8 @@ export const PagerDutyCard = (props: PagerDutyCardProps) => {
showDialog={dialogShown}
handleDialog={hideDialog}
onIncidentCreated={handleRefresh}
+ name={name}
+ integrationKey={integrationKey}
/>
)}
>
diff --git a/plugins/pagerduty/src/components/TriggerButton/index.tsx b/plugins/pagerduty/src/components/TriggerButton/index.tsx
index 732298c6d8..1c12103d73 100644
--- a/plugins/pagerduty/src/components/TriggerButton/index.tsx
+++ b/plugins/pagerduty/src/components/TriggerButton/index.tsx
@@ -33,7 +33,7 @@ const useStyles = makeStyles(theme => ({
/** @public */
export function TriggerButton(props: { children?: ReactNode }) {
const { buttonStyle } = useStyles();
- const { integrationKey } = usePagerdutyEntity();
+ const { integrationKey, name } = usePagerdutyEntity();
const [dialogShown, setDialogShown] = useState(false);
const showDialog = useCallback(() => {
@@ -57,7 +57,12 @@ export function TriggerButton(props: { children?: ReactNode }) {
: 'Missing integration key'}
{integrationKey && (
-
+
)}
>
);
diff --git a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx
index c6874cc7f3..74cd5100fa 100644
--- a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx
+++ b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx
@@ -17,8 +17,6 @@ import React from 'react';
import { fireEvent, act } from '@testing-library/react';
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
import { pagerDutyApiRef } from '../../api';
-import { Entity } from '@backstage/catalog-model';
-import { EntityProvider } from '@backstage/plugin-catalog-react';
import { TriggerDialog } from './TriggerDialog';
import { ApiProvider } from '@backstage/core-app-api';
@@ -49,26 +47,15 @@ describe('TriggerDialog', () => {
);
it('open the dialog and trigger an alarm', async () => {
- const entity: Entity = {
- apiVersion: 'backstage.io/v1alpha1',
- kind: 'Component',
- metadata: {
- name: 'pagerduty-test',
- annotations: {
- 'pagerduty.com/integration-key': 'abc123',
- },
- },
- };
-
const { getByText, getByRole, getByTestId } = await renderInTestApp(
-
- {}}
- onIncidentCreated={() => {}}
- />
-
+ {}}
+ onIncidentCreated={() => {}}
+ />
,
);
@@ -89,8 +76,7 @@ describe('TriggerDialog', () => {
});
expect(mockTriggerAlarmFn).toHaveBeenCalled();
expect(mockTriggerAlarmFn).toHaveBeenCalledWith({
- integrationKey:
- entity!.metadata!.annotations!['pagerduty.com/integration-key'],
+ integrationKey: 'abc123',
source: window.location.toString(),
description,
userName: 'guest',
diff --git a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx
index 4dc4f04699..a1e88b8fa6 100644
--- a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx
+++ b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx
@@ -28,7 +28,6 @@ import {
import useAsyncFn from 'react-use/lib/useAsyncFn';
import { pagerDutyApiRef } from '../../api';
import { Alert } from '@material-ui/lab';
-import { usePagerdutyEntity } from '../../hooks';
import {
useApi,
alertApiRef,
@@ -40,14 +39,17 @@ type Props = {
showDialog: boolean;
handleDialog: () => void;
onIncidentCreated?: () => void;
+ name: string;
+ integrationKey: string;
};
export const TriggerDialog = ({
showDialog,
handleDialog,
onIncidentCreated: onIncidentCreated,
+ name,
+ integrationKey,
}: Props) => {
- const { name, integrationKey } = usePagerdutyEntity();
const alertApi = useApi(alertApiRef);
const identityApi = useApi(identityApiRef);
const api = useApi(pagerDutyApiRef);
diff --git a/plugins/pagerduty/src/components/index.ts b/plugins/pagerduty/src/components/index.ts
index e7c1ff4e72..264d7195e9 100644
--- a/plugins/pagerduty/src/components/index.ts
+++ b/plugins/pagerduty/src/components/index.ts
@@ -23,12 +23,13 @@ export type {
PagerDutyUser,
} from './types';
-export type { PagerDutyCardProps } from './PagerDutyCard';
+export type { EntityPagerDutyCardProps } from './EntityPagerDutyCard';
+export type { HomePagePagerDutyCardProps } from './HomePagePagerDutyCard';
export {
isPluginApplicableToEntity,
isPluginApplicableToEntity as isPagerDutyAvailable,
- PagerDutyCard,
-} from './PagerDutyCard';
+ EntityPagerDutyCard,
+} from './EntityPagerDutyCard';
export { TriggerButton } from './TriggerButton';
diff --git a/plugins/pagerduty/src/deprecated.ts b/plugins/pagerduty/src/deprecated.ts
new file mode 100644
index 0000000000..e9f60f6894
--- /dev/null
+++ b/plugins/pagerduty/src/deprecated.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 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 { EntityPagerDutyCardProps, EntityPagerDutyCard } from './components';
+
+/**
+ * @public
+ * @deprecated Please use EntityPagerDutyCard
+ */
+export const PagerDutyCard = EntityPagerDutyCard;
+
+/**
+ * @public
+ * @deprecated Please use EntityPagerDutyCardProps
+ */
+export type PagerDutyCardProps = EntityPagerDutyCardProps;
diff --git a/plugins/pagerduty/src/index.ts b/plugins/pagerduty/src/index.ts
index 2e0eddc9a8..c6f7aef89a 100644
--- a/plugins/pagerduty/src/index.ts
+++ b/plugins/pagerduty/src/index.ts
@@ -24,9 +24,12 @@ export {
pagerDutyPlugin,
pagerDutyPlugin as plugin,
EntityPagerDutyCard,
+ HomePagePagerDutyCard,
} from './plugin';
export * from './components';
+
export * from './api';
+export * from './deprecated';
export type { PagerDutyEntity } from './types';
diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts
index 73e7e0b397..59cc5cd8cc 100644
--- a/plugins/pagerduty/src/plugin.ts
+++ b/plugins/pagerduty/src/plugin.ts
@@ -23,6 +23,8 @@ import {
configApiRef,
createComponentExtension,
} from '@backstage/core-plugin-api';
+import { createCardExtension } from '@backstage/plugin-home-react';
+import { HomePagePagerDutyCardProps } from './components/HomePagePagerDutyCard/Content';
export const rootRouteRef = createRouteRef({
id: 'pagerduty',
@@ -51,7 +53,38 @@ export const EntityPagerDutyCard = pagerDutyPlugin.provide(
name: 'EntityPagerDutyCard',
component: {
lazy: () =>
- import('./components/PagerDutyCard').then(m => m.PagerDutyCard),
+ import('./components/EntityPagerDutyCard').then(
+ m => m.EntityPagerDutyCard,
+ ),
+ },
+ }),
+);
+
+/** @public */
+export const HomePagePagerDutyCard = pagerDutyPlugin.provide(
+ createCardExtension({
+ name: 'HomePagePagerDutyCard',
+ title: 'PagerDuty Homepage Card',
+ components: () => import('./components/HomePagePagerDutyCard'),
+ settings: {
+ schema: {
+ title: 'PagerDuty',
+ type: 'object',
+ properties: {
+ integrationKey: {
+ title: 'PagerDuty integration key',
+ type: 'string',
+ },
+ serviceId: {
+ title: 'PagerDuty service id',
+ type: 'string',
+ },
+ name: {
+ title: 'PagerDuty service name',
+ type: 'string',
+ },
+ },
+ },
},
}),
);
diff --git a/yarn.lock b/yarn.lock
index ee0025fdda..712fd10e6f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7890,6 +7890,7 @@ __metadata:
"@backstage/dev-utils": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/plugin-catalog-react": "workspace:^"
+ "@backstage/plugin-home-react": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"
"@material-ui/core": ^4.12.2