add main functionality

This commit is contained in:
Samira Mokaram
2020-10-20 09:55:32 +02:00
parent 5539625f64
commit 47e9c6d56b
3 changed files with 311 additions and 0 deletions
@@ -0,0 +1,294 @@
/*
* Copyright 2020 Spotify AB
*
* 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 {
InfoCard,
StatusError,
StatusWarning,
StatusOK,
} from '@backstage/core';
import { Entity } from '@backstage/catalog-model';
import {
List,
ListSubheader,
ListItem,
ListItemIcon,
ListItemSecondaryAction,
Tooltip,
ListItemText,
makeStyles,
IconButton,
} from '@material-ui/core';
import moment from 'moment';
import Pagerduty from './pd.svg';
import UserIcon from '@material-ui/icons/Person';
import EmailIcon from '@material-ui/icons/Email';
const useStyles = makeStyles({
buttonContainer: {},
denseListIcon: {
marginRight: 0,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
},
svgButtonImage: {
height: '1em',
},
});
type IncidentListProps = {
incidents: any;
};
const IncidentList = ({ incidents }: IncidentListProps) => {
const classes = useStyles();
return incidents.map((incident: any) => (
<ListItem key={incident.id}>
<ListItemIcon>
<Tooltip title={incident.status} placement="top">
<div className={classes.denseListIcon}>
{incident.status === 'triggered' ? (
<StatusError />
) : (
<StatusWarning />
)}
</div>
</Tooltip>
</ListItemIcon>
<ListItemText
primary={incident.title}
secondary={
<span style={{ wordBreak: 'break-all', whiteSpace: 'normal' }}>
Created {moment(incident.createdAt).fromNow()}, assigned to{' '}
{(incident.assignees.length && incident.assignees[0].name) ||
'nobody'}
</span>
}
/>
<ListItemSecondaryAction>
<Tooltip title="View in PagerDuty" placement="left">
<IconButton
href={incident.homepageUrl}
target="_blank"
rel="noopener noreferrer"
>
<img
src={Pagerduty}
alt="View in PagerDuty"
className={classes.svgButtonImage}
/>
</IconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
));
};
const IncidentsEmptyState = () => {
const classes = useStyles();
return (
<ListItem>
<ListItemIcon>
<div className={classes.denseListIcon}>
<StatusOK />
</div>
</ListItemIcon>
<ListItemText primary="No incidents" secondary="All clear!" />
</ListItem>
);
};
type IncidentsProps = {
incidents: Array<any>;
};
export const Incidents = ({ incidents }: IncidentsProps) => (
<List dense subheader={<ListSubheader>Incidents</ListSubheader>}>
{incidents.length ? (
<IncidentList incidents={incidents} />
) : (
<IncidentsEmptyState />
)}
</List>
);
type EscalationProps = {
escalation: PagerDutyUserData[];
};
const EscalationUsers = ({ escalation }: EscalationProps) => {
const classes = useStyles();
const escalations = escalation.map((user, index) => (
<ListItem key={user.id + index}>
<ListItemIcon>
<UserIcon />
</ListItemIcon>
<ListItemText primary={user.name} secondary={user.email} />
<ListItemSecondaryAction>
<Tooltip title="Send e-mail to user" placement="left">
<IconButton href={`mailto:${user.email}`}>
<EmailIcon />
</IconButton>
</Tooltip>
<Tooltip title="View in PagerDuty" placement="left">
<IconButton
href={user.homepageUrl}
target="_blank"
rel="noopener noreferrer"
>
<img
src={Pagerduty}
alt="View in PagerDuty"
className={classes.svgButtonImage}
/>
</IconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
));
return <div>{escalations}</div>;
};
const EscalationUsersEmptyState = () => {
const classes = useStyles();
return (
<ListItem>
<ListItemIcon>
<div className={classes.denseListIcon}>
<StatusWarning />
</div>
</ListItemIcon>
<ListItemText primary="Empty escalation policy" />
</ListItem>
);
};
const EscalationPolicy = ({ escalation }: EscalationProps) => (
<List dense subheader={<ListSubheader>Escalation Policy</ListSubheader>}>
{escalation.length ? (
<EscalationUsers escalation={escalation} />
) : (
<EscalationUsersEmptyState />
)}
</List>
);
type CardBodyProps = {
entity: Entity;
data: PagerDutyData;
};
const CardBody = ({ entity, data }: CardBodyProps) => {
const { pagerDutyServices } = data;
const classes = useStyles();
if (!pagerDutyServices.length) {
return (
<div>
'The specified PagerDuty integration key does not match any PagerDuty
service.'
</div>
);
}
const { activeIncidents, escalationPolicy } = pagerDutyServices[0];
return (
<>
<Incidents key="1" incidents={activeIncidents} />
<EscalationPolicy key="2" escalation={escalationPolicy} />
<div key="3" className={classes.buttonContainer}>
{/* <TriggerButton component={entity} /> */}
</div>
</>
);
};
type PagerDutyIncident = {
id: string;
title: string;
status: string;
createdAt: string;
homepageUrl: string;
assignees: Partial<PagerDutyUserData>[];
};
type PagerDutyUserData = {
email: string;
name: string;
homepageUrl: string;
id: string;
};
type PagerDutyServicesData = {
activeIncidents: PagerDutyIncident[];
escalationPolicy: PagerDutyUserData[];
id: string;
name: string;
homepageUrl: string;
};
type PagerDutyData = {
pagerDutyServices: PagerDutyServicesData[];
};
type Props = {
entity: Entity;
};
export const PagerDutyServiceCard = ({ entity }: Props) => {
// TODO: fetch this data
const mockData: PagerDutyData = {
pagerDutyServices: [
{
activeIncidents: [
{
id: 'id',
title: 'something happened',
status: 'triggered',
createdAt: '2020:01:01',
homepageUrl: 'url',
assignees: [
{
name: 'name',
},
],
},
],
escalationPolicy: [
{
email: 'user@example.com',
name: 'Name',
homepageUrl: 'https://spotify.pagerduty.com/users/FOO',
id: 'user id',
},
],
id: 'id',
name: 'name',
homepageUrl: 'https://spotify.pagety.com/service-directory/BAR',
},
],
};
return (
<InfoCard title="PagerDuty">
<CardBody entity={entity} data={mockData} />
</InfoCard>
);
};
export const isPluginApplicableToEntity = (entity: Entity) => true;
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100px" height="85px" viewBox="0 0 100 85" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
<title>pd_icon</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="main" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="pd_icon" fill="#7A7A7A" fill-rule="nonzero">
<path d="M10.441698,84.9793579 L0,84.9793579 L0,32.5700658 C0,27.1885753 2.20882074,23.9355847 4.05619808,22.1283678 C8.15255655,18.1123301 13.6946886,17.9918489 14.6183773,17.9918489 L31.0439715,17.9918489 C36.9073865,17.9918489 40.2808582,20.3613112 42.1282355,22.36933 C45.7828299,26.3452074 45.8229902,31.4857356 45.7426695,32.8511884 L45.7426695,52.610094 C45.7426695,58.3128675 43.4133676,61.6863392 41.4856695,63.4935561 C37.5097922,67.2284712 32.2487828,67.308792 31.0038111,67.2686316 L10.441698,67.2686316 L10.441698,84.9793579 Z M31.4054149,56.7867732 C31.9676601,56.7867732 33.5339149,56.6261317 34.3772828,55.8229241 C35.0198488,55.2205185 35.3411318,54.1361883 35.3411318,52.5699336 L35.3411318,32.2889432 C35.3411318,31.7266979 35.2206507,30.2407639 34.4174431,29.397396 C33.654396,28.5941885 32.168462,28.433547 31.0439715,28.433547 L14.5380565,28.433547 C10.441698,28.433547 10.441698,31.525896 10.441698,32.5700658 L10.441698,56.7867732 L31.4054149,56.7867732 Z" id="Shape"></path>
<path d="M89.2363579,0.200801885 L99.6780559,0.200801885 L99.6780559,52.6502543 C99.6780559,58.0317449 97.4692352,61.2847354 95.6218578,63.0919524 C91.5254993,67.1079901 85.9833673,67.2284712 85.0596786,67.2284712 L68.6340844,67.2284712 C62.7706694,67.2284712 59.3971977,64.859009 57.5498203,62.8509901 C53.895226,58.8751128 53.8550657,53.7345845 53.9353864,52.3691317 L53.9353864,32.6503866 C53.9353864,26.947613 56.2646883,23.5741413 58.1923864,21.7669244 C62.1682637,18.0320093 67.4292731,17.9516886 68.6742448,17.9918489 L89.2363579,17.9918489 L89.2363579,0.200801885 Z M68.272641,28.433547 C67.7103957,28.433547 66.144141,28.5941885 65.3007731,29.397396 C64.6582071,29.9998017 64.3369241,31.0841319 64.3369241,32.6503866 L64.3369241,52.931377 C64.3369241,53.4936223 64.4574052,54.9795562 65.2606127,55.8229241 C66.0236599,56.6261317 67.5095939,56.7867732 68.6340844,56.7867732 L85.1399994,56.7867732 C89.2765182,56.7466128 89.2765182,53.6542638 89.2765182,52.610094 L89.2765182,28.433547 L68.272641,28.433547 Z" id="Shape"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+4
View File
@@ -14,3 +14,7 @@
* limitations under the License.
*/
export { plugin } from './plugin';
export {
isPluginApplicableToEntity,
PagerDutyServiceCard,
} from './components/PagerDutyServiceCard';