Skip to content

Commit

Permalink
release: 0.10.0 (#83)
Browse files Browse the repository at this point in the history
### Description

This PR introduces a new UI to the PagerDutyCard that is used on each
Entity Page. This new UI introduces:
- official PagerDuty logo
- clear visibility on incident status and urgency
- service status
- service standards
- visibility on service metrics (total incidents, high urgency
incidents, total number of interruptions)
- escalation policy information on the on-call section

This PR also updates the behaviour of some components from the
PagerDutyCard.
- the button to contact the on-call user by email is now removed
- the "create incident" button is now completely hidden in case of
`read-only` mode, instead of just disabling the button.

<img width="1285" alt="image"
src="https://github.com/PagerDuty/backstage-plugin/assets/2689939/a137b3e6-ac06-4e8d-9b2f-cdc49ca58781">


## Acknowledgement

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

**Disclaimer:** We value your time and bandwidth. As such, any pull
requests created on non-triaged issues might not be successful.
  • Loading branch information
t1agob authored Mar 22, 2024
2 parents f370eae + 564aab3 commit 9a88ea0
Show file tree
Hide file tree
Showing 27 changed files with 1,624 additions and 454 deletions.
78 changes: 69 additions & 9 deletions dev/mockPagerDutyApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ export const mockPagerDutyApi: PagerDutyApi = {
service: {
name: pagerDutyEntity.name,
id: "SERV1CE1D",
html_url: "www.example.com",
html_url: "https://www.example.com",
escalation_policy: {
id: "ESCALAT1ONP01ICY1D",
name: "ep-one",
html_url: "http://www.example.com/escalation-policy/ESCALAT1ONP01ICY1D",
},
status: "critical",
},
};
},
Expand All @@ -42,12 +43,29 @@ export const mockPagerDutyApi: PagerDutyApi = {
service: {
name: entity.metadata.name,
id: "SERV1CE1D",
html_url: "www.example.com",
html_url: "https://www.example.com",
escalation_policy: {
id: "ESCALAT1ONP01ICY1D",
name: "ep-one",
html_url: "http://www.example.com/escalation-policy/ESCALAT1ONP01ICY1D",
},
status: "warning",
},
};
},

async getServiceById(serviceId: string) {
return {
service: {
name: "SERV1CENAME",
id: serviceId,
html_url: "https://www.example.com",
escalation_policy: {
id: "ESCALAT1ONP01ICY1D",
name: "ep-one",
html_url: "http://www.example.com/escalation-policy/ESCALAT1ONP01ICY1D",
},
status: "warning",
},
};
},
Expand All @@ -57,7 +75,8 @@ export const mockPagerDutyApi: PagerDutyApi = {
return {
id: '123',
title: title,
status: 'acknowledged',
urgency: 'low',
status: 'triggered',
html_url: 'http://incident',
assignments: [
{
Expand All @@ -72,6 +91,7 @@ export const mockPagerDutyApi: PagerDutyApi = {
id: serviceId,
summary: 'service summary',
html_url: 'http://service',
status: 'warning',
},
created_at: '2015-10-06T21:30:42Z',
} as PagerDutyIncident;
Expand Down Expand Up @@ -111,15 +131,55 @@ export const mockPagerDutyApi: PagerDutyApi = {
};
},

async getServiceStandardsByServiceId(serviceId: string) {
const standards = () => {
return {
resource_id: serviceId,
resource_type: 'technical_service',
score: {
total: 1,
passing: 1,
},
standards: [
{
active: true,
id: '123',
name: 'Service has a description',
description: 'A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.',
pass: true,
type: 'has_technical_service_description'
},
],
}
};

return {
standards: standards(),
}
},

async getServiceMetricsByServiceId(serviceId: string) {
return {
metrics: [
{
service_id: serviceId,
total_incident_count: 6,
total_high_urgency_incidents: 3,
total_interruptions: 2,
},
]
}
},

async getOnCallByPolicyId() {
const oncall = (id: string, name: string) => {
return {
id: id,
name: name,
html_url: 'http://assignee',
summary: 'summary',
email: '[email protected]',
avatar_url: 'http://avatar',
id: id,
name: name,
html_url: 'http://assignee',
summary: 'summary',
email: '[email protected]',
avatar_url: 'http://avatar',
};
};

Expand Down
16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"name": "@pagerduty/backstage-plugin",
"description": "A Backstage plugin that integrates towards PagerDuty",
"version": "NPM_PACKAGE_VERSION",
"main": "src/index.ts",
"types": "src/index.ts",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
Expand Down Expand Up @@ -45,10 +45,11 @@
"@material-ui/lab": "4.0.0-alpha.61",
"classnames": "^2.2.6",
"luxon": "^3.4.1",
"react-use": "^17.2.4"
"react-use": "^17.2.4",
"validate-color": "^2.2.4"
},
"peerDependencies": {
"@pagerduty/backstage-plugin-common": "^0.1.0",
"@pagerduty/backstage-plugin-common": "^0.1.2",
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
"react-router-dom": "6.0.0-beta.0 || ^6.3.0"
Expand All @@ -57,10 +58,10 @@
"@backstage/cli": "^0.24.0",
"@backstage/core-app-api": "^1.11.1",
"@backstage/dev-utils": "^1.0.24",
"@backstage/test-utils": "^1.4.5",
"@backstage/test-utils": "^1.5.1",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@pagerduty/backstage-plugin-common": "^0.1.0",
"@pagerduty/backstage-plugin-common": "^0.1.2",
"@testing-library/dom": "^8.0.0",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
Expand All @@ -81,5 +82,6 @@
"assets/**/*"
],
"configSchema": "config.d.ts",
"packageManager": "[email protected]"
"packageManager": "[email protected]",
"module": "./dist/index.esm.js"
}
33 changes: 32 additions & 1 deletion src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import { PagerDutyChangeEventsResponse,
PagerDutyOnCallUsersResponse,
PagerDutyUser,
PagerDutyServiceResponse,
PagerDutyIncidentsResponse
PagerDutyIncidentsResponse,
PagerDutyServiceStandardsResponse,
PagerDutyServiceMetricsResponse
} from '@pagerduty/backstage-plugin-common';
import { createApiRef, ConfigApi } from '@backstage/core-plugin-api';
import { NotFoundError } from '@backstage/errors';
Expand Down Expand Up @@ -98,6 +100,15 @@ export class PagerDutyClient implements PagerDutyApi {
async getServiceByEntity(entity: Entity): Promise<PagerDutyServiceResponse> {
return await this.getServiceByPagerDutyEntity(getPagerDutyEntity(entity));
}

async getServiceById(serviceId: string): Promise<PagerDutyServiceResponse> {
const url = `${await this.config.discoveryApi.getBaseUrl(
'pagerduty',
)}/services/${serviceId}`;

return await this.findByUrl<PagerDutyServiceResponse>(url);
}

async getIncidentsByServiceId(
serviceId: string,
): Promise<PagerDutyIncidentsResponse> {
Expand All @@ -118,6 +129,26 @@ export class PagerDutyClient implements PagerDutyApi {
return await this.findByUrl<PagerDutyChangeEventsResponse>(url);
}

async getServiceStandardsByServiceId(
serviceId: string,
): Promise<PagerDutyServiceStandardsResponse> {
const url = `${await this.config.discoveryApi.getBaseUrl(
'pagerduty',
)}/services/${serviceId}/standards`;

return await this.findByUrl<PagerDutyServiceStandardsResponse>(url);
}

async getServiceMetricsByServiceId(
serviceId: string,
): Promise<PagerDutyServiceMetricsResponse> {
const url = `${await this.config.discoveryApi.getBaseUrl(
'pagerduty',
)}/services/${serviceId}/metrics`;

return await this.findByUrl<PagerDutyServiceMetricsResponse>(url);
}

async getOnCallByPolicyId(
policyId: string,
): Promise<PagerDutyUser[]> {
Expand Down
43 changes: 42 additions & 1 deletion src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
import { PagerDutyChangeEventsResponse,
PagerDutyServiceResponse,
PagerDutyUser,
PagerDutyIncidentsResponse
PagerDutyIncidentsResponse,
PagerDutyServiceStandardsResponse,
PagerDutyServiceMetricsResponse,
PagerDutyServiceStandards,
PagerDutyServiceMetrics
} from '@pagerduty/backstage-plugin-common';
import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
import { Entity } from '@backstage/catalog-model';
Expand All @@ -31,6 +35,19 @@ export type PagerDutyTriggerAlarmRequest = {
userName: string;
};

/** @public */
export type PagerDutyCardServiceResponse = {
id: string;
name: string;
url: string;
policyId: string;
policyLink: string;
policyName: string;
status?: string;
standards?: PagerDutyServiceStandards;
metrics?: PagerDutyServiceMetrics[];
}

/** @public */
export interface PagerDutyApi {
/**
Expand All @@ -47,6 +64,14 @@ export interface PagerDutyApi {
*/
getServiceByEntity(entity: Entity): Promise<PagerDutyServiceResponse>;

/**
* Fetches service with the provided service id.
*
*/
getServiceById(
serviceId: string,
): Promise<PagerDutyServiceResponse>;

/**
* Fetches a list of incidents a provided service has.
*
Expand All @@ -63,6 +88,22 @@ export interface PagerDutyApi {
serviceId: string,
): Promise<PagerDutyChangeEventsResponse>;

/**
* Fetches a list of standards for a provided service.
*
*/
getServiceStandardsByServiceId(
serviceId: string,
): Promise<PagerDutyServiceStandardsResponse>;

/**
* Fetches a list of metrics for a provided service.
*
*/
getServiceMetricsByServiceId(
serviceId: string,
): Promise<PagerDutyServiceMetricsResponse>;

/**
* Fetches the list of users in an escalation policy.
*
Expand Down
21 changes: 21 additions & 0 deletions src/assets/PD-Green.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions src/assets/PD-White.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/ChangeEvents/ChangeEventEmptyState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import EmptyStateImage from '../../assets/emptystate.svg';

export const ChangeEventEmptyState = () => {
return (
<Grid container justify="center" direction="column" alignItems="center">
<Grid container justifyContent="center" direction="column" alignItems="center">
<Grid item xs={12}>
<Typography variant="h5">No change events to display yet.</Typography>
</Grid>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ChangeEvents/ChangeEventForbiddenState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import ForbiddenStateImage from '../../assets/forbiddenstate.svg';

export const ChangeEventForbiddenState = () => {
return (
<Grid container justify="center" direction="column" alignItems="center">
<Grid container justifyContent="center" direction="column" alignItems="center">
<Grid item xs={12}>
<Typography variant="h5">Feature not available with your account or token.</Typography>
</Grid>
Expand Down
Loading

0 comments on commit 9a88ea0

Please sign in to comment.