+ No incidents right now
+
+ }
+ title={
+ !compact ? (
+
+ ) : (
+
+ INCIDENTS
+
+ )
+ }
+ page={tableState.page}
+ totalCount={incidentsCount}
+ onChangePage={onChangePage}
+ onChangeRowsPerPage={onChangeRowsPerPage}
+ // localization={{ header: { actions: undefined } }}
+ columns={columns}
+ data={incidents}
+ isLoading={isLoading}
+ />
+ );
+};
diff --git a/plugins/ilert/src/components/IncidentsPage/StatusChip.tsx b/plugins/ilert/src/components/IncidentsPage/StatusChip.tsx
new file mode 100644
index 0000000000..9713b16a55
--- /dev/null
+++ b/plugins/ilert/src/components/IncidentsPage/StatusChip.tsx
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 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 { Chip, withStyles } from '@material-ui/core';
+import { Incident, PENDING, ACCEPTED, RESOLVED } from '../../types';
+import { incidentStatusLabels } from '../Incident/IncidentStatus';
+
+const ResolvedChip = withStyles({
+ root: {
+ backgroundColor: '#4caf50',
+ color: 'white',
+ margin: 0,
+ },
+})(Chip);
+
+const AcceptedChip = withStyles({
+ root: {
+ backgroundColor: '#ffb74d',
+ color: 'white',
+ margin: 0,
+ },
+})(Chip);
+const PendingChip = withStyles({
+ root: {
+ backgroundColor: '#d32f2f',
+ color: 'white',
+ margin: 0,
+ },
+})(Chip);
+
+export const StatusChip = ({ incident }: { incident: Incident }) => {
+ const label = `${incidentStatusLabels[incident.status]}`;
+
+ switch (incident.status) {
+ case RESOLVED:
+ return ;
+ case ACCEPTED:
+ return ;
+ case PENDING:
+ return ;
+ default:
+ return ;
+ }
+};
diff --git a/plugins/ilert/src/components/IncidentsPage/TableTitle.tsx b/plugins/ilert/src/components/IncidentsPage/TableTitle.tsx
new file mode 100644
index 0000000000..f35981dec2
--- /dev/null
+++ b/plugins/ilert/src/components/IncidentsPage/TableTitle.tsx
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 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 { PENDING, ACCEPTED, RESOLVED, IncidentStatus } from '../../types';
+import { incidentStatusLabels } from '../Incident/IncidentStatus';
+import FormControl from '@material-ui/core/FormControl';
+import ListItemText from '@material-ui/core/ListItemText';
+import Select from '@material-ui/core/Select';
+import Typography from '@material-ui/core/Typography';
+import MenuItem from '@material-ui/core/MenuItem';
+import Checkbox from '@material-ui/core/Checkbox';
+import { makeStyles } from '@material-ui/core/styles';
+
+const ITEM_HEIGHT = 48;
+const ITEM_PADDING_TOP = 8;
+const MenuProps = {
+ PaperProps: {
+ style: {
+ maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
+ width: 250,
+ },
+ },
+};
+
+const useStyles = makeStyles({
+ root: {
+ display: 'flex',
+ },
+ label: {
+ marginTop: 8,
+ marginRight: 4,
+ },
+ formControl: {
+ minWidth: 120,
+ maxWidth: 300,
+ },
+ grow: {
+ flexGrow: 1,
+ },
+});
+
+export const TableTitle = ({
+ incidentStates,
+ onIncidentStatesChange,
+}: {
+ incidentStates: IncidentStatus[];
+ onIncidentStatesChange: (states: IncidentStatus[]) => void;
+}) => {
+ const classes = useStyles();
+ const handleIncidentStatusSelectChange = (event: any) => {
+ onIncidentStatesChange(event.target.value);
+ };
+
+ return (
+
+
+ Status:
+
+
+
+
+
+ );
+};
diff --git a/plugins/ilert/src/components/IncidentsPage/index.ts b/plugins/ilert/src/components/IncidentsPage/index.ts
new file mode 100644
index 0000000000..7159247c68
--- /dev/null
+++ b/plugins/ilert/src/components/IncidentsPage/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 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.
+ */
+export * from './IncidentsPage';
+export * from './IncidentsTable';
diff --git a/plugins/ilert/src/components/OnCallSchedulesPage/OnCallSchedulesGrid.tsx b/plugins/ilert/src/components/OnCallSchedulesPage/OnCallSchedulesGrid.tsx
new file mode 100644
index 0000000000..34601009ff
--- /dev/null
+++ b/plugins/ilert/src/components/OnCallSchedulesPage/OnCallSchedulesGrid.tsx
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2021 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 { useApi, ItemCardGrid, Progress } from '@backstage/core';
+import { makeStyles } from '@material-ui/core/styles';
+import Card from '@material-ui/core/Card';
+import CardContent from '@material-ui/core/CardContent';
+import CardHeader from '@material-ui/core/CardHeader';
+import Typography from '@material-ui/core/Typography';
+import Link from '@material-ui/core/Link';
+import { Schedule } from '../../types';
+import { ilertApiRef } from '../../api';
+import { OnCallShiftItem } from './OnCallShiftItem';
+
+const useStyles = makeStyles(() => ({
+ card: {
+ margin: 16,
+ width: 'calc(100% - 32px)',
+ },
+
+ cardHeader: {
+ maxWidth: '100%',
+ },
+
+ cardContent: {
+ marginLeft: 80,
+ borderLeft: '1px #808289 solid',
+ position: 'relative',
+ },
+
+ indicatorNext: {
+ position: 'absolute',
+ top: 'calc(40% - 10px)',
+ left: -8,
+ width: 16,
+ height: 16,
+ background: '#92949c !important',
+ borderRadius: '50%',
+ },
+
+ indicatorCurrent: {
+ position: 'absolute',
+ top: 'calc(40% - 10px)',
+ left: -8,
+ width: 16,
+ height: 16,
+ background: '#ffb74d !important',
+ borderRadius: '50%',
+ },
+
+ beforeText: {
+ position: 'absolute',
+ top: 'calc(31% - 10px)',
+ left: -78,
+ width: 65,
+ height: 20,
+ textAlign: 'center',
+ color: '#808289',
+ },
+
+ marginBottom: {
+ marginBottom: 16,
+ },
+
+ link: {
+ fontSize: '1.5rem',
+ fontWeight: 700,
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+ textOverflow: 'ellipsis',
+ display: 'block',
+ },
+}));
+
+export const OnCallSchedulesGrid = ({
+ onCallSchedules,
+ isLoading,
+ refetchOnCallSchedules,
+}: {
+ onCallSchedules: Schedule[];
+ isLoading: boolean;
+ refetchOnCallSchedules: () => void;
+}) => {
+ const ilertApi = useApi(ilertApiRef);
+ const classes = useStyles();
+
+ if (isLoading) {
+ return ;
+ }
+ return (
+
+ {!onCallSchedules?.length
+ ? null
+ : onCallSchedules.map((schedule, index) => (
+
+
+ {schedule.name}
+
+ }
+ />
+
+
+
+
+
+ On call now
+
+
+
+
+
+
+
+ Next on call
+
+
+
+ ))}
+
+ );
+};
diff --git a/plugins/ilert/src/components/OnCallSchedulesPage/OnCallSchedulesPage.tsx b/plugins/ilert/src/components/OnCallSchedulesPage/OnCallSchedulesPage.tsx
new file mode 100644
index 0000000000..09c2f2fc63
--- /dev/null
+++ b/plugins/ilert/src/components/OnCallSchedulesPage/OnCallSchedulesPage.tsx
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021 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 { Content, ContentHeader, SupportButton } from '@backstage/core';
+import { UnauthorizedError } from '../../api';
+import Alert from '@material-ui/lab/Alert';
+import { OnCallSchedulesGrid } from './OnCallSchedulesGrid';
+import { MissingAuthorizationHeaderError } from '../Errors';
+import { useOnCallSchedules } from '../../hooks/useOnCallSchedules';
+
+export const OnCallSchedulesPage = () => {
+ const [
+ { onCallSchedules, isLoading, error },
+ { refetchOnCallSchedules },
+ ] = useOnCallSchedules();
+
+ if (error) {
+ if (error instanceof UnauthorizedError) {
+ return ;
+ }
+
+ return (
+
+ {error.message}
+
+ );
+ }
+
+ return (
+
+
+
+ This helps you to bring iLert into your developer portal.
+
+
+
+
+ );
+};
diff --git a/plugins/ilert/src/components/OnCallSchedulesPage/OnCallShiftItem.tsx b/plugins/ilert/src/components/OnCallSchedulesPage/OnCallShiftItem.tsx
new file mode 100644
index 0000000000..c636609781
--- /dev/null
+++ b/plugins/ilert/src/components/OnCallSchedulesPage/OnCallShiftItem.tsx
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2021 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 Button from '@material-ui/core/Button';
+import Grid from '@material-ui/core/Grid';
+import Typography from '@material-ui/core/Typography';
+import RepeatIcon from '@material-ui/icons/Repeat';
+import { Shift } from '../../types';
+import moment from 'moment';
+import { makeStyles } from '@material-ui/core/styles';
+import { ShiftOverrideModal } from '../Shift/ShiftOverrideModal';
+
+const useStyles = makeStyles({
+ button: {
+ marginTop: 4,
+ padding: 0,
+ lineHeight: 1.8,
+ '& span': {
+ lineHeight: 1.8,
+ fontSize: '0.65rem',
+ },
+ '& svg': {
+ fontSize: '0.85rem !important',
+ },
+ },
+});
+
+export const OnCallShiftItem = ({
+ scheduleId,
+ shift,
+ refetchOnCallSchedules,
+}: {
+ scheduleId: number;
+ shift: Shift;
+ refetchOnCallSchedules: () => void;
+}) => {
+ const classes = useStyles();
+ const [isModalOpened, setIsModalOpened] = React.useState(false);
+
+ const handleOverride = () => {
+ setIsModalOpened(true);
+ };
+
+ if (!shift || !shift.start) {
+ return (
+
+
+
+ Nobody
+
+
+
+ );
+ }
+
+ return (
+
+ {shift && shift.user ? (
+
+
+ {`${shift.user.firstName} ${shift.user.lastName} (${shift.user.username})`}
+
+
+ ) : null}
+
+
+ {`${moment(shift.start).format('D MMM, HH:mm')} - ${moment(
+ shift.end,
+ ).format('D MMM, HH:mm')}`}
+
+
+
+ }
+ onClick={handleOverride}
+ >
+ Override shift
+
+
+
+
+ );
+};
diff --git a/plugins/ilert/src/components/OnCallSchedulesPage/index.ts b/plugins/ilert/src/components/OnCallSchedulesPage/index.ts
new file mode 100644
index 0000000000..a52accc2bb
--- /dev/null
+++ b/plugins/ilert/src/components/OnCallSchedulesPage/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 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.
+ */
+export * from './OnCallSchedulesPage';
+export * from './OnCallSchedulesGrid';
diff --git a/plugins/ilert/src/components/Shift/ShiftOverrideModal.tsx b/plugins/ilert/src/components/Shift/ShiftOverrideModal.tsx
new file mode 100644
index 0000000000..1fdd05cb4c
--- /dev/null
+++ b/plugins/ilert/src/components/Shift/ShiftOverrideModal.tsx
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2021 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 { alertApiRef, useApi } from '@backstage/core';
+import { makeStyles } from '@material-ui/core/styles';
+import Button from '@material-ui/core/Button';
+import TextField from '@material-ui/core/TextField';
+import Dialog from '@material-ui/core/Dialog';
+import DialogActions from '@material-ui/core/DialogActions';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogTitle from '@material-ui/core/DialogTitle';
+import Autocomplete from '@material-ui/lab/Autocomplete';
+import { Typography } from '@material-ui/core';
+import { ilertApiRef } from '../../api';
+import { useShiftOverride } from '../../hooks/useShiftOverride';
+import { Shift } from '../../types';
+import { DateTimePicker, MuiPickersUtilsProvider } from '@material-ui/pickers';
+import DateFnsUtils from '@date-io/date-fns';
+
+const useStyles = makeStyles(() => ({
+ container: {
+ display: 'flex',
+ flexWrap: 'wrap',
+ },
+ formControl: {
+ minWidth: 120,
+ width: '100%',
+ },
+ option: {
+ fontSize: 15,
+ '& > span': {
+ marginRight: 10,
+ fontSize: 18,
+ },
+ },
+ optionWrapper: {
+ display: 'flex',
+ width: '100%',
+ },
+ sourceImage: {
+ height: 22,
+ paddingRight: 4,
+ },
+ grow: {
+ flexGrow: 1,
+ },
+}));
+
+export const ShiftOverrideModal = ({
+ scheduleId,
+ shift,
+ refetchOnCallSchedules,
+ isModalOpened,
+ setIsModalOpened,
+}: {
+ scheduleId: number;
+ shift: Shift;
+ refetchOnCallSchedules: () => void;
+ isModalOpened: boolean;
+ setIsModalOpened: (isModalOpened: boolean) => void;
+}) => {
+ const [
+ { isLoading, users, user, start, end },
+ { setUser, setStart, setEnd, setIsLoading },
+ ] = useShiftOverride(shift, isModalOpened);
+ const ilertApi = useApi(ilertApiRef);
+ const alertApi = useApi(alertApiRef);
+ const classes = useStyles();
+
+ const handleClose = () => {
+ setIsModalOpened(false);
+ };
+
+ const handleOverride = () => {
+ if (!shift || !shift.user) {
+ return;
+ }
+ setIsLoading(true);
+ setTimeout(async () => {
+ try {
+ const success = await ilertApi.overrideShift(
+ scheduleId,
+ user.id,
+ start,
+ end,
+ );
+ if (success) {
+ alertApi.post({ message: 'Shift overridden.' });
+ refetchOnCallSchedules();
+ }
+ } catch (err) {
+ alertApi.post({ message: err, severity: 'error' });
+ }
+ setIsModalOpened(false);
+ }, 250);
+ };
+
+ if (!shift) {
+ return null;
+ }
+
+ return (
+
+ );
+};
diff --git a/plugins/ilert/src/components/UptimeMonitor/UptimeMonitorActionsMenu.tsx b/plugins/ilert/src/components/UptimeMonitor/UptimeMonitorActionsMenu.tsx
new file mode 100644
index 0000000000..efd69a4acf
--- /dev/null
+++ b/plugins/ilert/src/components/UptimeMonitor/UptimeMonitorActionsMenu.tsx
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 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 { alertApiRef, useApi } from '@backstage/core';
+import { IconButton, Menu, MenuItem, Typography } from '@material-ui/core';
+import Link from '@material-ui/core/Link';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+
+import { ilertApiRef } from '../../api';
+import { UptimeMonitor } from '../../types';
+
+export const UptimeMonitorActionsMenu = ({
+ uptimeMonitor,
+ onUptimeMonitorChanged,
+}: {
+ uptimeMonitor: UptimeMonitor;
+ onUptimeMonitorChanged?: (uptimeMonitor: UptimeMonitor) => void;
+}) => {
+ const ilertApi = useApi(ilertApiRef);
+ const alertApi = useApi(alertApiRef);
+ const [anchorEl, setAnchorEl] = React.useState(null);
+ const callback = onUptimeMonitorChanged || ((_: UptimeMonitor): void => {});
+
+ const handleClick = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget);
+ };
+
+ const handleCloseMenu = () => {
+ setAnchorEl(null);
+ };
+
+ const handlePause = async (): Promise => {
+ try {
+ const newUptimeMonitor = await ilertApi.pauseUptimeMonitor(uptimeMonitor);
+ handleCloseMenu();
+ alertApi.post({ message: 'Uptime monitor paused.' });
+
+ callback(newUptimeMonitor);
+ } catch (err) {
+ alertApi.post({ message: err, severity: 'error' });
+ }
+ };
+
+ const handleResume = async (): Promise => {
+ try {
+ const newUptimeMonitor = await ilertApi.resumeUptimeMonitor(
+ uptimeMonitor,
+ );
+ handleCloseMenu();
+ alertApi.post({ message: 'Uptime monitor resumed.' });
+
+ callback(newUptimeMonitor);
+ } catch (err) {
+ alertApi.post({ message: err, severity: 'error' });
+ }
+ };
+
+ const handleOpenReport = async (): Promise => {
+ try {
+ const um = await ilertApi.fetchUptimeMonitor(uptimeMonitor.id);
+ handleCloseMenu();
+ window.open(um.shareUrl, '_blank');
+ } catch (err) {
+ alertApi.post({ message: err, severity: 'error' });
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
diff --git a/plugins/ilert/src/components/UptimeMonitor/UptimeMonitorLink.tsx b/plugins/ilert/src/components/UptimeMonitor/UptimeMonitorLink.tsx
new file mode 100644
index 0000000000..0af5cd89df
--- /dev/null
+++ b/plugins/ilert/src/components/UptimeMonitor/UptimeMonitorLink.tsx
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 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 { useApi } from '@backstage/core';
+import Link from '@material-ui/core/Link';
+import { makeStyles } from '@material-ui/core/styles';
+import { UptimeMonitor } from '../../types';
+import { ilertApiRef } from '../../api';
+
+const useStyles = makeStyles({
+ link: {
+ lineHeight: '22px',
+ },
+});
+
+export const UptimeMonitorLink = ({
+ uptimeMonitor,
+}: {
+ uptimeMonitor: UptimeMonitor | null;
+}) => {
+ const ilertApi = useApi(ilertApiRef);
+ const classes = useStyles();
+
+ if (!uptimeMonitor) {
+ return null;
+ }
+
+ return (
+
+ #{uptimeMonitor.id}
+
+ );
+};
diff --git a/plugins/ilert/src/components/UptimeMonitor/index.ts b/plugins/ilert/src/components/UptimeMonitor/index.ts
new file mode 100644
index 0000000000..f30d1ef15a
--- /dev/null
+++ b/plugins/ilert/src/components/UptimeMonitor/index.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2021 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.
+ */
+export * from './UptimeMonitorActionsMenu';
diff --git a/plugins/ilert/src/components/UptimeMonitorsPage/StatusChip.tsx b/plugins/ilert/src/components/UptimeMonitorsPage/StatusChip.tsx
new file mode 100644
index 0000000000..dd17b1758b
--- /dev/null
+++ b/plugins/ilert/src/components/UptimeMonitorsPage/StatusChip.tsx
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2021 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 Chip from '@material-ui/core/Chip';
+import { withStyles } from '@material-ui/core/styles';
+import { UptimeMonitor } from '../../types';
+
+const UpChip = withStyles({
+ root: {
+ backgroundColor: '#4caf50',
+ color: 'white',
+ margin: 0,
+ },
+})(Chip);
+
+const DownChip = withStyles({
+ root: {
+ backgroundColor: '#d32f2f',
+ color: 'white',
+ margin: 0,
+ },
+})(Chip);
+
+const UnknownChip = withStyles({
+ root: {
+ backgroundColor: '#92949c',
+ color: 'white',
+ margin: 0,
+ },
+})(Chip);
+
+export const uptimeMonitorStatusLabels = {
+ ['up']: 'Up',
+ ['down']: 'Down',
+ ['unknown']: 'Unknown',
+} as Record;
+
+export const StatusChip = ({
+ uptimeMonitor,
+}: {
+ uptimeMonitor: UptimeMonitor;
+}) => {
+ let label = `${uptimeMonitorStatusLabels[uptimeMonitor.status]}`;
+
+ if (uptimeMonitor.paused) {
+ label = 'Paused';
+ return ;
+ }
+
+ switch (uptimeMonitor.status) {
+ case 'up':
+ return ;
+ case 'down':
+ return ;
+ case 'unknown':
+ return ;
+ default:
+ return ;
+ }
+};
diff --git a/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorCheckType.tsx b/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorCheckType.tsx
new file mode 100644
index 0000000000..3fdecd65e9
--- /dev/null
+++ b/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorCheckType.tsx
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 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 { UptimeMonitor } from '../../types';
+import Typography from '@material-ui/core/Typography';
+
+export const UptimeMonitorCheckType = ({
+ uptimeMonitor,
+}: {
+ uptimeMonitor: UptimeMonitor;
+}) => {
+ switch (uptimeMonitor.region) {
+ case 'EU':
+ return (
+ {`${uptimeMonitor.checkType.toUpperCase()} π©πͺ`}
+ );
+ default:
+ return (
+ {`${uptimeMonitor.checkType.toUpperCase()} πΊπΈ`}
+ );
+ }
+};
diff --git a/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorsPage.tsx b/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorsPage.tsx
new file mode 100644
index 0000000000..dc7ea77a6c
--- /dev/null
+++ b/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorsPage.tsx
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2021 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 { Content, ContentHeader, SupportButton } from '@backstage/core';
+import { UnauthorizedError } from '../../api';
+import Alert from '@material-ui/lab/Alert';
+import { UptimeMonitorsTable } from './UptimeMonitorsTable';
+import { MissingAuthorizationHeaderError } from '../Errors';
+import { useUptimeMonitors } from '../../hooks/useUptimeMonitors';
+
+export const UptimeMonitorsPage = () => {
+ const [
+ { tableState, uptimeMonitors, isLoading, error },
+ { onChangePage, onChangeRowsPerPage, onUptimeMonitorChanged },
+ ] = useUptimeMonitors();
+
+ if (error) {
+ if (error instanceof UnauthorizedError) {
+ return ;
+ }
+
+ return (
+
+ {error.message}
+
+ );
+ }
+
+ return (
+
+
+
+ This helps you to bring iLert into your developer portal.
+
+
+
+
+ );
+};
diff --git a/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorsTable.tsx b/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorsTable.tsx
new file mode 100644
index 0000000000..bd55310ad7
--- /dev/null
+++ b/plugins/ilert/src/components/UptimeMonitorsPage/UptimeMonitorsTable.tsx
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2021 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 { makeStyles } from '@material-ui/core/styles';
+import { Table, TableColumn } from '@backstage/core';
+import { TableState } from '../../api';
+import { UptimeMonitor } from '../../types';
+import { StatusChip } from './StatusChip';
+import Typography from '@material-ui/core/Typography';
+import moment from 'moment';
+import { EscalationPolicyLink } from '../EscalationPolicy/EscalationPolicyLink';
+import { UptimeMonitorCheckType } from './UptimeMonitorCheckType';
+import { UptimeMonitorActionsMenu } from '../UptimeMonitor/UptimeMonitorActionsMenu';
+import { UptimeMonitorLink } from '../UptimeMonitor/UptimeMonitorLink';
+
+const useStyles = makeStyles(theme => ({
+ empty: {
+ padding: theme.spacing(2),
+ display: 'flex',
+ justifyContent: 'center',
+ },
+}));
+
+export const UptimeMonitorsTable = ({
+ uptimeMonitors,
+ tableState,
+ isLoading,
+ onChangePage,
+ onChangeRowsPerPage,
+ onUptimeMonitorChanged,
+}: {
+ uptimeMonitors: UptimeMonitor[];
+ tableState: TableState;
+ isLoading: boolean;
+ onChangePage: (page: number) => void;
+ onChangeRowsPerPage: (pageSize: number) => void;
+ onUptimeMonitorChanged: (uptimeMonitor: UptimeMonitor) => void;
+}) => {
+ const classes = useStyles();
+
+ const smColumnStyle = {
+ width: '5%',
+ maxWidth: '5%',
+ };
+ const mdColumnStyle = {
+ width: '10%',
+ maxWidth: '10%',
+ };
+ const lgColumnStyle = {
+ width: '15%',
+ maxWidth: '15%',
+ };
+
+ const columns: TableColumn[] = [
+ {
+ title: 'ID',
+ field: 'id',
+ highlight: true,
+ cellStyle: mdColumnStyle,
+ headerStyle: mdColumnStyle,
+ render: rowData => (
+
+ ),
+ },
+ {
+ title: 'Name',
+ field: 'name',
+ render: rowData => (
+ {(rowData as UptimeMonitor).name}
+ ),
+ },
+ {
+ title: 'Check Type',
+ field: 'checkType',
+ cellStyle: lgColumnStyle,
+ headerStyle: lgColumnStyle,
+ render: rowData => (
+
+ ),
+ },
+ {
+ title: 'Last state change',
+ field: 'lastStatusChange',
+ type: 'datetime',
+ cellStyle: mdColumnStyle,
+ headerStyle: mdColumnStyle,
+ render: rowData => (
+
+ {moment
+ .duration(
+ moment((rowData as UptimeMonitor).lastStatusChange).diff(
+ moment(),
+ ),
+ )
+ .humanize()}
+
+ ),
+ },
+ {
+ title: 'Escalation policy',
+ field: 'assignedTo',
+ cellStyle: lgColumnStyle,
+ headerStyle: lgColumnStyle,
+ render: rowData => (
+
+ ),
+ },
+ {
+ title: 'Status',
+ field: 'status',
+ cellStyle: smColumnStyle,
+ headerStyle: smColumnStyle,
+ render: rowData => (
+
+ ),
+ },
+ {
+ title: '',
+ field: '',
+ cellStyle: smColumnStyle,
+ headerStyle: smColumnStyle,
+ render: rowData => (
+
+ ),
+ },
+ ];
+
+ return (
+
+ No uptime monitor
+
+ }
+ page={tableState.page}
+ onChangePage={onChangePage}
+ onChangeRowsPerPage={onChangeRowsPerPage}
+ localization={{ header: { actions: undefined } }}
+ isLoading={isLoading}
+ columns={columns}
+ data={uptimeMonitors}
+ />
+ );
+};
diff --git a/plugins/ilert/src/components/UptimeMonitorsPage/index.ts b/plugins/ilert/src/components/UptimeMonitorsPage/index.ts
new file mode 100644
index 0000000000..d1a8809a4e
--- /dev/null
+++ b/plugins/ilert/src/components/UptimeMonitorsPage/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 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.
+ */
+export * from './UptimeMonitorsPage';
+export * from './UptimeMonitorsTable';
diff --git a/plugins/ilert/src/components/index.ts b/plugins/ilert/src/components/index.ts
new file mode 100644
index 0000000000..81f7e02e23
--- /dev/null
+++ b/plugins/ilert/src/components/index.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 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.
+ */
+
+export * from './ILertPage';
+export * from './ILertCard';
diff --git a/plugins/ilert/src/constants.ts b/plugins/ilert/src/constants.ts
new file mode 100644
index 0000000000..edaa06b293
--- /dev/null
+++ b/plugins/ilert/src/constants.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2021 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.
+ */
+export const ILERT_INTEGRATION_KEY = 'ilert.com/integration-key';
diff --git a/plugins/ilert/src/hooks/index.ts b/plugins/ilert/src/hooks/index.ts
new file mode 100644
index 0000000000..e21c9584d2
--- /dev/null
+++ b/plugins/ilert/src/hooks/index.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 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 { useEntity } from '@backstage/plugin-catalog-react';
+
+import { ILERT_INTEGRATION_KEY } from '../constants';
+
+export function useILertEntity() {
+ const { entity } = useEntity();
+ const integrationKey =
+ entity.metadata.annotations?.[ILERT_INTEGRATION_KEY] || '';
+ const name = entity.metadata.name;
+
+ return { integrationKey, name };
+}
diff --git a/plugins/ilert/src/hooks/useAlertSource.ts b/plugins/ilert/src/hooks/useAlertSource.ts
new file mode 100644
index 0000000000..950f8f41c6
--- /dev/null
+++ b/plugins/ilert/src/hooks/useAlertSource.ts
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2021 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 { ilertApiRef, UnauthorizedError } from '../api';
+import { useApi, errorApiRef } from '@backstage/core';
+import { useAsyncRetry } from 'react-use';
+import { AlertSource, UptimeMonitor } from '../types';
+
+export const useAlertSource = (integrationKey: string) => {
+ const ilertApi = useApi(ilertApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const [alertSource, setAlertSource] = React.useState(
+ null,
+ );
+ const [isAlertSourceLoading, setIsAlertSourceLoading] = React.useState(false);
+ const [
+ uptimeMonitor,
+ setUptimeMonitor,
+ ] = React.useState(null);
+ const [isUptimeMonitorLoading, setIsUptimeMonitorLoading] = React.useState(
+ false,
+ );
+
+ const fetchAlertSourceCall = async () => {
+ try {
+ if (!integrationKey) {
+ return;
+ }
+ setIsAlertSourceLoading(true);
+ const data = await ilertApi.fetchAlertSource(integrationKey);
+ setAlertSource(data || null);
+ setIsAlertSourceLoading(false);
+ } catch (e) {
+ setIsAlertSourceLoading(false);
+ if (!(e instanceof UnauthorizedError)) {
+ errorApi.post(e);
+ }
+ throw e;
+ }
+ };
+
+ const {
+ error: alertSourceError,
+ retry: alertSourceRetry,
+ } = useAsyncRetry(fetchAlertSourceCall, [integrationKey]);
+
+ const fetchUptimeMonitorCall = async () => {
+ try {
+ if (!alertSource || alertSource.integrationType !== 'MONITOR') {
+ return;
+ }
+ setIsUptimeMonitorLoading(true);
+ const data = await ilertApi.fetchUptimeMonitor(alertSource.id);
+ setUptimeMonitor(data || null);
+ setIsUptimeMonitorLoading(false);
+ } catch (e) {
+ setIsUptimeMonitorLoading(false);
+ if (!(e instanceof UnauthorizedError)) {
+ errorApi.post(e);
+ }
+ throw e;
+ }
+ };
+
+ const {
+ error: uptimeMonitorError,
+ retry: uptimeMonitorRetry,
+ } = useAsyncRetry(fetchUptimeMonitorCall, [alertSource]);
+
+ const retry = () => {
+ alertSourceRetry();
+ uptimeMonitorRetry();
+ };
+
+ return [
+ {
+ alertSource,
+ uptimeMonitor,
+ error: alertSourceError || uptimeMonitorError,
+ isLoading: isAlertSourceLoading || isUptimeMonitorLoading,
+ },
+ {
+ retry,
+ setIsLoading: setIsAlertSourceLoading,
+ refetchAlertSource: fetchAlertSourceCall,
+ setAlertSource,
+ },
+ ] as const;
+};
diff --git a/plugins/ilert/src/hooks/useAssignIncident.ts b/plugins/ilert/src/hooks/useAssignIncident.ts
new file mode 100644
index 0000000000..1671acdc51
--- /dev/null
+++ b/plugins/ilert/src/hooks/useAssignIncident.ts
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2021 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 { ilertApiRef, UnauthorizedError } from '../api';
+import { useApi, errorApiRef } from '@backstage/core';
+import { useAsyncRetry } from 'react-use';
+import { Incident, IncidentResponder } from '../types';
+
+export const useAssignIncident = (incident: Incident | null, open: boolean) => {
+ const ilertApi = useApi(ilertApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const [incidentRespondersList, setIncidentRespondersList] = React.useState<
+ IncidentResponder[]
+ >([]);
+ const [
+ incidentResponder,
+ setIncidentResponder,
+ ] = React.useState(null);
+ const [isLoading, setIsLoading] = React.useState(false);
+
+ const { error, retry } = useAsyncRetry(async () => {
+ try {
+ if (!incident || !open) {
+ return;
+ }
+ const data = await ilertApi.fetchIncidentResponders(incident);
+ if (data && Array.isArray(data)) {
+ const groups = [
+ 'SUGGESTED',
+ 'USER',
+ 'ESCALATION_POLICY',
+ 'ON_CALL_SCHEDULE',
+ ];
+ data.sort((a, b) => groups.indexOf(a.group) - groups.indexOf(b.group));
+ setIncidentRespondersList(data);
+ }
+ } catch (e) {
+ if (!(e instanceof UnauthorizedError)) {
+ errorApi.post(e);
+ }
+ throw e;
+ }
+ }, [incident, open]);
+
+ return [
+ {
+ incidentRespondersList,
+ incidentResponder,
+ error,
+ isLoading,
+ },
+ {
+ setIncidentRespondersList,
+ setIncidentResponder,
+ setIsLoading,
+ retry,
+ },
+ ] as const;
+};
diff --git a/plugins/ilert/src/hooks/useIncidents.ts b/plugins/ilert/src/hooks/useIncidents.ts
new file mode 100644
index 0000000000..a252dfb14f
--- /dev/null
+++ b/plugins/ilert/src/hooks/useIncidents.ts
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2021 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 {
+ GetIncidentsOpts,
+ ilertApiRef,
+ TableState,
+ UnauthorizedError,
+} from '../api';
+import { useApi, errorApiRef } from '@backstage/core';
+import { useAsyncRetry } from 'react-use';
+import { ACCEPTED, PENDING, Incident, IncidentStatus } from '../types';
+
+export const useIncidents = (
+ paging: boolean,
+ alertSources?: number[] | string[],
+) => {
+ const ilertApi = useApi(ilertApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const [tableState, setTableState] = React.useState({
+ page: 0,
+ pageSize: 10,
+ });
+ const [states, setStates] = React.useState([
+ ACCEPTED,
+ PENDING,
+ ]);
+ const [incidentsList, setIncidentsList] = React.useState([]);
+ const [incidentsCount, setIncidentsCount] = React.useState(0);
+ const [isLoading, setIsLoading] = React.useState(false);
+
+ const fetchIncidentsCall = async () => {
+ try {
+ setIsLoading(true);
+ const opts: GetIncidentsOpts = {
+ states,
+ alertSources,
+ };
+ if (paging) {
+ opts.maxResults = tableState.pageSize;
+ opts.startIndex = tableState.page * tableState.pageSize;
+ }
+ const data = await ilertApi.fetchIncidents(opts);
+ setIncidentsList(data || []);
+ setIsLoading(false);
+ } catch (e) {
+ if (!(e instanceof UnauthorizedError)) {
+ errorApi.post(e);
+ }
+ setIsLoading(false);
+ throw e;
+ }
+ };
+
+ const fetchIncidentsCountCall = async () => {
+ try {
+ const count = await ilertApi.fetchIncidentsCount({ states });
+ setIncidentsCount(count || 0);
+ } catch (e) {
+ if (!(e instanceof UnauthorizedError)) {
+ errorApi.post(e);
+ }
+ throw e;
+ }
+ };
+ const fetchIncidents = useAsyncRetry(fetchIncidentsCall, [
+ tableState,
+ states,
+ ]);
+
+ const refetchIncidents = () => {
+ setTableState({ ...tableState, page: 0 });
+ Promise.all([fetchIncidentsCall(), fetchIncidentsCountCall()]);
+ };
+
+ const fetchIncidentsCount = useAsyncRetry(fetchIncidentsCountCall, [states]);
+
+ const error = fetchIncidents.error || fetchIncidentsCount.error;
+ const retry = () => {
+ fetchIncidents.retry();
+ fetchIncidentsCount.retry();
+ };
+
+ const onIncidentChanged = (newIncident: Incident) => {
+ let shouldRefetchIncidents = false;
+ setIncidentsList(
+ incidentsList.reduce((acc: Incident[], incident: Incident) => {
+ if (newIncident.id === incident.id) {
+ if (states.includes(newIncident.status)) {
+ acc.push(newIncident);
+ } else {
+ shouldRefetchIncidents = true;
+ }
+ return acc;
+ }
+ acc.push(incident);
+ return acc;
+ }, []),
+ );
+ if (shouldRefetchIncidents) {
+ refetchIncidents();
+ }
+ };
+
+ const onChangePage = (page: number) => {
+ setTableState({ ...tableState, page });
+ };
+ const onChangeRowsPerPage = (p: number) => {
+ setTableState({ ...tableState, pageSize: p });
+ };
+ const onIncidentStatesChange = (s: IncidentStatus[]) => {
+ setStates(s);
+ };
+
+ return [
+ {
+ tableState,
+ states,
+ incidents: incidentsList,
+ incidentsCount,
+ error,
+ isLoading,
+ },
+ {
+ setTableState,
+ setStates,
+ setIncidentsList,
+ setIsLoading,
+ retry,
+ onIncidentChanged,
+ refetchIncidents,
+ onChangePage,
+ onChangeRowsPerPage,
+ onIncidentStatesChange,
+ },
+ ] as const;
+};
diff --git a/plugins/ilert/src/hooks/useNewIncident.ts b/plugins/ilert/src/hooks/useNewIncident.ts
new file mode 100644
index 0000000000..669fa74b63
--- /dev/null
+++ b/plugins/ilert/src/hooks/useNewIncident.ts
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021 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 { ilertApiRef, UnauthorizedError } from '../api';
+import { useApi, errorApiRef } from '@backstage/core';
+import { useAsyncRetry } from 'react-use';
+import { AlertSource } from '../types';
+
+export const useNewIncident = (
+ open: boolean,
+ initialAlertSource?: AlertSource | null,
+) => {
+ const ilertApi = useApi(ilertApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const [alertSourcesList, setAlertSourcesList] = React.useState(
+ [],
+ );
+ const [alertSource, setAlertSource] = React.useState(
+ null,
+ );
+ const [summary, setSummary] = React.useState('');
+ const [details, setDetails] = React.useState('');
+ const [isLoading, setIsLoading] = React.useState(false);
+
+ const fetchAlertSources = useAsyncRetry(async () => {
+ try {
+ if (!open || initialAlertSource) {
+ return;
+ }
+ const count = await ilertApi.fetchAlertSources();
+ setAlertSourcesList(count || 0);
+ } catch (e) {
+ if (!(e instanceof UnauthorizedError)) {
+ errorApi.post(e);
+ }
+ throw e;
+ }
+ }, [open]);
+
+ const error = fetchAlertSources.error;
+ const retry = () => {
+ fetchAlertSources.retry();
+ };
+
+ return [
+ {
+ alertSources: alertSourcesList,
+ alertSource: initialAlertSource ? initialAlertSource : alertSource,
+ summary,
+ details,
+ error,
+ isLoading,
+ },
+ {
+ setAlertSourcesList,
+ setAlertSource,
+ setSummary,
+ setDetails,
+ setIsLoading,
+ retry,
+ },
+ ] as const;
+};
diff --git a/plugins/ilert/src/hooks/useOnCallSchedules.ts b/plugins/ilert/src/hooks/useOnCallSchedules.ts
new file mode 100644
index 0000000000..5e0538a370
--- /dev/null
+++ b/plugins/ilert/src/hooks/useOnCallSchedules.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021 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 { ilertApiRef, UnauthorizedError } from '../api';
+import { useApi, errorApiRef } from '@backstage/core';
+import { useAsyncRetry } from 'react-use';
+import { Schedule } from '../types';
+
+export const useOnCallSchedules = () => {
+ const ilertApi = useApi(ilertApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const [onCallSchedulesList, setOnCallSchedulesList] = React.useState<
+ Schedule[]
+ >([]);
+ const [isLoading, setIsLoading] = React.useState(false);
+
+ const fetchOnCallSchedulesCall = async () => {
+ try {
+ setIsLoading(true);
+ const data = await ilertApi.fetchOnCallSchedules();
+ setOnCallSchedulesList(data || []);
+ setIsLoading(false);
+ } catch (e) {
+ setIsLoading(false);
+ if (!(e instanceof UnauthorizedError)) {
+ errorApi.post(e);
+ }
+ throw e;
+ }
+ };
+
+ const { error, retry } = useAsyncRetry(fetchOnCallSchedulesCall, []);
+
+ return [
+ {
+ onCallSchedules: onCallSchedulesList,
+ error,
+ isLoading,
+ },
+ {
+ retry,
+ setIsLoading,
+ refetchOnCallSchedules: fetchOnCallSchedulesCall,
+ },
+ ] as const;
+};
diff --git a/plugins/ilert/src/hooks/useShiftOverride.ts b/plugins/ilert/src/hooks/useShiftOverride.ts
new file mode 100644
index 0000000000..fd1da7423b
--- /dev/null
+++ b/plugins/ilert/src/hooks/useShiftOverride.ts
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021 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 { ilertApiRef, UnauthorizedError } from '../api';
+import { useApi, errorApiRef } from '@backstage/core';
+import { useAsyncRetry } from 'react-use';
+import { User, Shift } from '../types';
+
+export const useShiftOverride = (s: Shift, isModalOpened: boolean) => {
+ const ilertApi = useApi(ilertApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const [shift, setShift] = React.useState(s);
+ const [usersList, setUsersList] = React.useState([]);
+ const [isLoading, setIsLoading] = React.useState(false);
+
+ const { error, retry } = useAsyncRetry(async () => {
+ try {
+ if (!isModalOpened) {
+ return;
+ }
+ setIsLoading(true);
+ const data = await ilertApi.fetchUsers();
+ setUsersList(data || []);
+ setIsLoading(false);
+ } catch (e) {
+ setIsLoading(false);
+ if (!(e instanceof UnauthorizedError)) {
+ errorApi.post(e);
+ }
+ throw e;
+ }
+ }, [isModalOpened]);
+
+ const setUser = (user: User) => {
+ setShift({ ...shift, user });
+ };
+
+ const setStart = (start: string) => {
+ setShift({ ...shift, start });
+ };
+
+ const setEnd = (end: string) => {
+ setShift({ ...shift, end });
+ };
+
+ return [
+ {
+ shift,
+ users: usersList,
+ user: shift.user,
+ start: shift.start,
+ end: shift.end,
+ error,
+ isLoading,
+ },
+ {
+ retry,
+ setIsLoading,
+ setUser,
+ setStart,
+ setEnd,
+ },
+ ] as const;
+};
diff --git a/plugins/ilert/src/hooks/useUptimeMonitors.ts b/plugins/ilert/src/hooks/useUptimeMonitors.ts
new file mode 100644
index 0000000000..02cd5aaa2f
--- /dev/null
+++ b/plugins/ilert/src/hooks/useUptimeMonitors.ts
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021 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 { ilertApiRef, TableState, UnauthorizedError } from '../api';
+import { useApi, errorApiRef } from '@backstage/core';
+import { useAsyncRetry } from 'react-use';
+import { UptimeMonitor } from '../types';
+
+export const useUptimeMonitors = () => {
+ const ilertApi = useApi(ilertApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const [tableState, setTableState] = React.useState({
+ page: 0,
+ pageSize: 10,
+ });
+ const [uptimeMonitorsList, setUptimeMonitorsList] = React.useState<
+ UptimeMonitor[]
+ >([]);
+ const [isLoading, setIsLoading] = React.useState(false);
+
+ const { error, retry } = useAsyncRetry(async () => {
+ try {
+ setIsLoading(true);
+ const data = await ilertApi.fetchUptimeMonitors();
+ setUptimeMonitorsList(data || []);
+ setIsLoading(false);
+ } catch (e) {
+ setIsLoading(false);
+ if (!(e instanceof UnauthorizedError)) {
+ errorApi.post(e);
+ }
+ throw e;
+ }
+ }, [tableState]);
+
+ const onUptimeMonitorChanged = (newUptimeMonitor: UptimeMonitor) => {
+ setUptimeMonitorsList(
+ uptimeMonitorsList.map(
+ (uptimeMonitor: UptimeMonitor): UptimeMonitor => {
+ if (newUptimeMonitor.id === uptimeMonitor.id) {
+ return newUptimeMonitor;
+ }
+
+ return uptimeMonitor;
+ },
+ ),
+ );
+ };
+
+ const onChangePage = (page: number) => {
+ setTableState({ ...tableState, page });
+ };
+ const onChangeRowsPerPage = (pageSize: number) => {
+ setTableState({ ...tableState, pageSize });
+ };
+
+ return [
+ {
+ tableState,
+ uptimeMonitors: uptimeMonitorsList,
+ error,
+ isLoading,
+ },
+ {
+ setTableState,
+ setUptimeMonitorsList,
+ retry,
+ onUptimeMonitorChanged,
+ onChangePage,
+ onChangeRowsPerPage,
+ setIsLoading,
+ },
+ ] as const;
+};
diff --git a/plugins/ilert/src/index.ts b/plugins/ilert/src/index.ts
new file mode 100644
index 0000000000..2e5301f151
--- /dev/null
+++ b/plugins/ilert/src/index.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2021 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 { IconComponent } from '@backstage/core';
+import ILertIconComponent from './assets/ilert.icon.svg';
+
+export {
+ ilertPlugin,
+ ilertPlugin as plugin,
+ ILertPage,
+ EntityILertCard,
+} from './plugin';
+export {
+ ILertPage as Router,
+ isPluginApplicableToEntity,
+ isPluginApplicableToEntity as isILertAvailable,
+ ILertCard,
+} from './components';
+export * from './api';
+export * from './route-refs';
+export const ILertIcon: IconComponent = ILertIconComponent;
diff --git a/plugins/ilert/src/plugin.test.ts b/plugins/ilert/src/plugin.test.ts
new file mode 100644
index 0000000000..89a0cb231f
--- /dev/null
+++ b/plugins/ilert/src/plugin.test.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021 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 { ilertPlugin } from './plugin';
+
+describe('plugin-ilert', () => {
+ it('should export plugin', () => {
+ expect(ilertPlugin).toBeDefined();
+ });
+});
diff --git a/plugins/ilert/src/plugin.ts b/plugins/ilert/src/plugin.ts
new file mode 100644
index 0000000000..546b77911d
--- /dev/null
+++ b/plugins/ilert/src/plugin.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021 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 {
+ configApiRef,
+ createApiFactory,
+ createPlugin,
+ discoveryApiRef,
+ identityApiRef,
+ createRoutableExtension,
+ createComponentExtension,
+} from '@backstage/core';
+import { ILertClient, ilertApiRef } from './api';
+import { iLertRouteRef } from './route-refs';
+
+export const ilertPlugin = createPlugin({
+ id: 'ilert',
+ apis: [
+ createApiFactory({
+ api: ilertApiRef,
+ deps: {
+ discoveryApi: discoveryApiRef,
+ identityApi: identityApiRef,
+ configApi: configApiRef,
+ },
+ factory: ({ discoveryApi, configApi }) =>
+ ILertClient.fromConfig(configApi, discoveryApi),
+ }),
+ ],
+ routes: {
+ root: iLertRouteRef,
+ },
+});
+
+export const ILertPage = ilertPlugin.provide(
+ createRoutableExtension({
+ component: () => import('./components').then(m => m.ILertPage),
+ mountPoint: iLertRouteRef,
+ }),
+);
+
+export const EntityILertCard = ilertPlugin.provide(
+ createComponentExtension({
+ component: {
+ lazy: () => import('./components/ILertCard').then(m => m.ILertCard),
+ },
+ }),
+);
diff --git a/plugins/ilert/src/route-refs.tsx b/plugins/ilert/src/route-refs.tsx
new file mode 100644
index 0000000000..b63ba15315
--- /dev/null
+++ b/plugins/ilert/src/route-refs.tsx
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 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 { createRouteRef } from '@backstage/core';
+import ILertIcon from './assets/ilert.icon.svg';
+
+export const iLertRouteRef = createRouteRef({
+ icon: ILertIcon,
+ path: '/ilert',
+ title: 'iLert',
+});
diff --git a/plugins/ilert/src/setupTests.ts b/plugins/ilert/src/setupTests.ts
new file mode 100644
index 0000000000..0cec5b395d
--- /dev/null
+++ b/plugins/ilert/src/setupTests.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 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 '@testing-library/jest-dom';
+import 'cross-fetch/polyfill';
diff --git a/plugins/ilert/src/types.ts b/plugins/ilert/src/types.ts
new file mode 100644
index 0000000000..dd9aab42d7
--- /dev/null
+++ b/plugins/ilert/src/types.ts
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2021 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.
+ */
+
+export interface Incident {
+ id: number;
+ summary: string;
+ details: string;
+ reportTime: string;
+ resolvedOn: string;
+ status: IncidentStatus;
+ priority: IncidentPriority;
+ incidentKey: string;
+ alertSource: AlertSource | null;
+ assignedTo: User | null;
+ logEntries: LogEntry[];
+ links: Link[];
+ images: Image[];
+ subscribers: Subscriber[];
+ commentText: string;
+ commentPublishToSubscribers: boolean;
+}
+
+export const PENDING = 'PENDING';
+export const ACCEPTED = 'ACCEPTED';
+export const RESOLVED = 'RESOLVED';
+export type IncidentStatus = typeof PENDING | typeof ACCEPTED | typeof RESOLVED;
+export type IncidentPriority = 'HIGH' | 'LOW';
+
+export interface Link {
+ href: string;
+ text: string;
+}
+
+export interface Image {
+ src: string;
+ href: string;
+ alt: string;
+}
+
+export type SubscriberType = 'TEAM' | 'USER';
+
+export interface Subscriber {
+ id: number;
+ name: string;
+ type: SubscriberType;
+}
+
+export interface LogEntry {
+ id: number;
+ timestamp: string;
+ logEntryType: string;
+ text: string;
+ incidentId?: number;
+ iconName?: string;
+ iconClass?: string;
+ filterTypes?: string[];
+}
+
+export interface User {
+ id: number;
+ username: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ mobile: Phone;
+ landline: Phone;
+ timezone?: string;
+ language?: Language;
+ role?: UserRole;
+ notificationPreferences?: any[];
+ position: string;
+ department: string;
+}
+
+export type UserRole =
+ | 'USER'
+ | 'ADMIN'
+ | 'STAKEHOLDER'
+ | 'ACCOUNT_OWNER'
+ | 'RESPONDER';
+export type Language = 'de' | 'en';
+export interface Phone {
+ regionCode: string;
+ number: string;
+}
+
+export interface AlertSource {
+ id: number;
+ name: string;
+ status: AlertSourceStatus;
+ escalationPolicy: EscalationPolicy;
+ integrationType: AlertSourceIntegrationType;
+ integrationKey?: string;
+ iconUrl?: string;
+ lightIconUrl?: string;
+ darkIconUrl?: string;
+ incidentCreation?: AlertSourceIncidentCreation;
+ incidentPriorityRule?: AlertSourceIncidentPriorityRule;
+ emailFiltered?: boolean;
+ emailResolveFiltered?: boolean;
+ active?: boolean;
+ emailPredicates?: AlertSourceEmailPredicate[];
+ emailResolvePredicates?: AlertSourceEmailPredicate[];
+ filterOperator?: AlertSourceFilterOperator;
+ resolveFilterOperator?: AlertSourceFilterOperator;
+ supportHours?: AlertSourceSupportHours;
+ heartbeat?: AlertSourceHeartbeat;
+ autotaskMetadata?: AlertSourceAutotaskMetadata;
+ autoResolutionTimeout?: string;
+ teams: TeamShort[];
+}
+
+export interface TeamShort {
+ id: number;
+ name: string;
+}
+
+export interface TeamMember {
+ user: User;
+ role: 'STAKEHOLDER' | 'RESPONDER' | 'USER' | 'ADMIN';
+}
+
+export type AlertSourceStatus =
+ | 'PENDING'
+ | 'ALL_ACCEPTED'
+ | 'ALL_RESOLVED'
+ | 'IN_MAINTENANCE'
+ | 'DISABLED';
+export type AlertSourceIntegrationType =
+ | 'NAGIOS'
+ | 'ICINGA'
+ | 'EMAIL'
+ | 'SMS'
+ | 'API'
+ | 'CRN'
+ | 'HEARTBEAT'
+ | 'PRTG'
+ | 'PINGDOM'
+ | 'CLOUDWATCH'
+ | 'AWSPHD'
+ | 'STACKDRIVER'
+ | 'INSTANA'
+ | 'ZABBIX'
+ | 'SOLARWINDS'
+ | 'PROMETHEUS'
+ | 'NEWRELIC'
+ | 'GRAFANA'
+ | 'GITHUB'
+ | 'DATADOG'
+ | 'UPTIMEROBOT'
+ | 'APPDYNAMICS'
+ | 'DYNATRACE'
+ | 'TOPDESK'
+ | 'STATUSCAKE'
+ | 'MONITOR'
+ | 'TOOL'
+ | 'CHECKMK'
+ | 'AUTOTASK'
+ | 'AWSBUDGET'
+ | 'KENTIXAM'
+ | 'CONSUL'
+ | 'ZAMMAD'
+ | 'SIGNALFX'
+ | 'SPLUNK'
+ | 'KUBERNETES'
+ | 'SEMATEXT'
+ | 'SENTRY'
+ | 'SUMOLOGIC'
+ | 'RAYGUN'
+ | 'MXTOOLBOX'
+ | 'ESWATCHER'
+ | 'AMAZONSNS'
+ | 'KAPACITOR'
+ | 'CORTEXXSOAR'
+ | string;
+export type AlertSourceIncidentCreation =
+ | 'ONE_INCIDENT_PER_EMAIL'
+ | 'ONE_INCIDENT_PER_EMAIL_SUBJECT'
+ | 'ONE_PENDING_INCIDENT_ALLOWED'
+ | 'ONE_OPEN_INCIDENT_ALLOWED'
+ | 'OPEN_RESOLVE_ON_EXTRACTION';
+export type AlertSourceFilterOperator = 'AND' | 'OR';
+export type AlertSourceIncidentPriorityRule =
+ | 'HIGH'
+ | 'LOW'
+ | 'HIGH_DURING_SUPPORT_HOURS'
+ | 'LOW_DURING_SUPPORT_HOURS';
+export interface AlertSourceEmailPredicate {
+ field: 'EMAIL_FROM' | 'EMAIL_SUBJECT' | 'EMAIL_BODY';
+ criteria:
+ | 'CONTAINS_ANY_WORDS'
+ | 'CONTAINS_NOT_WORDS'
+ | 'CONTAINS_STRING'
+ | 'CONTAINS_NOT_STRING'
+ | 'IS_STRING'
+ | 'IS_NOT_STRING'
+ | 'MATCHES_REGEX'
+ | 'MATCHES_NOT_REGEX';
+ value: string;
+}
+export type AlertSourceTimeZone =
+ | 'Europe/Berlin'
+ | 'America/New_York'
+ | 'America/Los_Angeles'
+ | 'Asia/Istanbul';
+export interface AlertSourceSupportDay {
+ start: string;
+ end: string;
+}
+export interface AlertSourceSupportHours {
+ timezone: AlertSourceTimeZone;
+ autoRaiseIncidents: boolean;
+ supportDays: {
+ MONDAY: AlertSourceSupportDay;
+ TUESDAY: AlertSourceSupportDay;
+ WEDNESDAY: AlertSourceSupportDay;
+ THURSDAY: AlertSourceSupportDay;
+ FRIDAY: AlertSourceSupportDay;
+ SATURDAY: AlertSourceSupportDay;
+ SUNDAY: AlertSourceSupportDay;
+ };
+}
+export interface AlertSourceHeartbeat {
+ summary: string;
+ intervalSec: number;
+ status: 'OVERDUE' | 'ON_TIME' | 'NEVER_RECEIVED';
+}
+
+export interface AlertSourceAutotaskMetadata {
+ userName: string;
+ secret: string;
+ apiIntegrationCode: string;
+ webServer: string;
+}
+
+export interface EscalationPolicy {
+ id: number;
+ name: string;
+ escalationRules: EscalationRule[];
+ newEscalationRule: EscalationRule;
+ repeating?: boolean;
+ frequency?: number;
+ teams: TeamShort[];
+}
+
+export interface EscalationRule {
+ user: User | null;
+ schedule: Schedule | null;
+ escalationTimeout: number;
+}
+
+export interface Schedule {
+ id: number;
+ name: string;
+ timezone: string;
+ startsOn: string;
+ currentShift: Shift;
+ nextShift: Shift;
+ shifts: Shift[];
+ overrides: Shift[];
+ teams: TeamShort[];
+}
+
+export interface Shift {
+ user: User;
+ start: string;
+ end: string;
+}
+
+export interface UptimeMonitor {
+ id: number;
+ name: string;
+ region: 'EU' | 'US';
+ checkType: 'http' | 'tcp' | 'udp' | 'ping';
+ checkParams: UptimeMonitorCheckParams;
+ intervalSec: number;
+ timeoutMs: number;
+ createIncidentAfterFailedChecks: number;
+ paused: boolean;
+ embedUrl: string;
+ shareUrl: string;
+ status: string;
+ lastStatusChange: string;
+ escalationPolicy: EscalationPolicy;
+ teams: TeamShort[];
+}
+
+export interface UptimeMonitorCheckParams {
+ host?: string;
+ port?: number;
+ url?: string;
+}
+
+export interface IncidentResponder {
+ group: 'SUGGESTED' | 'USER' | 'ESCALATION_POLICY' | 'ON_CALL_SCHEDULE';
+ id: number;
+ name: string;
+ disabled: boolean;
+}