rename all incident entities to alert

Signed-off-by: Marko Simon <marko@ilert.com>
This commit is contained in:
Marko Simon
2022-09-23 17:25:50 +02:00
parent 7506c95e94
commit 3328ae8889
24 changed files with 430 additions and 474 deletions
+54 -75
View File
@@ -13,30 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AuthenticationError, ResponseError } from '@backstage/errors';
import {
ConfigApi,
createApiRef,
DiscoveryApi,
} from '@backstage/core-plugin-api';
import { AuthenticationError, ResponseError } from '@backstage/errors';
import { DateTime as dt } from 'luxon';
import {
Alert,
AlertAction,
AlertResponder,
AlertSource,
EscalationPolicy,
Incident,
IncidentAction,
IncidentResponder,
OnCall,
Schedule,
UptimeMonitor,
User,
} from '../types';
import {
ILertApi,
GetIncidentsOpts,
GetIncidentsCountOpts,
EventRequest,
GetAlertsCountOpts,
GetAlertsOpts,
ILertApi,
} from './types';
import { DateTime as dt } from 'luxon';
import {
ConfigApi,
createApiRef,
DiscoveryApi,
} from '@backstage/core-plugin-api';
/** @public */
export const ilertApiRef = createApiRef<ILertApi>({
@@ -102,7 +102,7 @@ export class ILertClient implements ILertApi {
return await response.json();
}
async fetchIncidents(opts?: GetIncidentsOpts): Promise<Incident[]> {
async fetchAlerts(opts?: GetAlertsOpts): Promise<Alert[]> {
const init = {
headers: JSON_HEADERS,
};
@@ -126,15 +126,12 @@ export class ILertClient implements ILertApi {
query.append('state', state);
});
}
const response = await this.fetch(
`/api/v1/incidents?${query.toString()}`,
init,
);
const response = await this.fetch(`/api/alerts?${query.toString()}`, init);
return response;
}
async fetchIncidentsCount(opts?: GetIncidentsCountOpts): Promise<number> {
async fetchAlertsCount(opts?: GetAlertsCountOpts): Promise<number> {
const init = {
headers: JSON_HEADERS,
};
@@ -145,96 +142,85 @@ export class ILertClient implements ILertApi {
});
}
const response = await this.fetch(
`/api/v1/incidents/count?${query.toString()}`,
`/api/alerts/count?${query.toString()}`,
init,
);
return response && response.count ? response.count : 0;
}
async fetchIncident(id: number): Promise<Incident> {
async fetchAlert(id: number): Promise<Alert> {
const init = {
headers: JSON_HEADERS,
};
const response = await this.fetch(
`/api/v1/incidents/${encodeURIComponent(id)}`,
`/api/alerts/${encodeURIComponent(id)}`,
init,
);
return response;
}
async fetchIncidentResponders(
incident: Incident,
): Promise<IncidentResponder[]> {
async fetchAlertResponders(alert: Alert): Promise<AlertResponder[]> {
const init = {
headers: JSON_HEADERS,
};
const response = await this.fetch(
`/api/v1/incidents/${encodeURIComponent(incident.id)}/responders`,
`/api/alerts/${encodeURIComponent(alert.id)}/responders`,
init,
);
return response;
}
async fetchIncidentActions(incident: Incident): Promise<IncidentAction[]> {
async fetchAlertActions(alert: Alert): Promise<AlertAction[]> {
const init = {
headers: JSON_HEADERS,
};
const response = await this.fetch(
`/api/v1/incidents/${encodeURIComponent(incident.id)}/actions`,
`/api/alerts/${encodeURIComponent(alert.id)}/actions`,
init,
);
return response;
}
async acceptIncident(
incident: Incident,
userName: string,
): Promise<Incident> {
async acceptAlert(alert: Alert, userName: string): Promise<Alert> {
const init = {
method: 'POST',
headers: JSON_HEADERS,
body: JSON.stringify({
apiKey: incident.alertSource?.integrationKey || '',
incidentKey: incident.incidentKey,
apiKey: alert.alertSource?.integrationKey || '',
alertKey: alert.alertKey,
summary: `from ${userName} via Backstage plugin`,
eventType: 'ACCEPT',
}),
};
await this.fetch('/api/v1/events', init);
return this.fetchIncident(incident.id);
await this.fetch('/api/events', init);
return this.fetchAlert(alert.id);
}
async resolveIncident(
incident: Incident,
userName: string,
): Promise<Incident> {
async resolveAlert(alert: Alert, userName: string): Promise<Alert> {
const init = {
method: 'POST',
headers: JSON_HEADERS,
body: JSON.stringify({
apiKey: incident.alertSource?.integrationKey || '',
incidentKey: incident.incidentKey,
apiKey: alert.alertSource?.integrationKey || '',
alertKey: alert.alertKey,
summary: `from ${userName} via Backstage plugin`,
eventType: 'RESOLVE',
}),
};
await this.fetch('/api/v1/events', init);
return this.fetchIncident(incident.id);
await this.fetch('/api/events', init);
return this.fetchAlert(alert.id);
}
async assignIncident(
incident: Incident,
responder: IncidentResponder,
): Promise<Incident> {
async assignAlert(alert: Alert, responder: AlertResponder): Promise<Alert> {
const init = {
method: 'PUT',
headers: JSON_HEADERS,
@@ -254,19 +240,14 @@ export class ILertClient implements ILertApi {
}
const response = await this.fetch(
`/api/v1/incidents/${encodeURIComponent(
incident.id,
)}/assign?${query.toString()}`,
`/api/alerts/${encodeURIComponent(alert.id)}/assign?${query.toString()}`,
init,
);
return response;
}
async triggerIncidentAction(
incident: Incident,
action: IncidentAction,
): Promise<void> {
async triggerAlertAction(alert: Alert, action: AlertAction): Promise<void> {
const init = {
method: 'POST',
headers: JSON_HEADERS,
@@ -279,12 +260,12 @@ export class ILertClient implements ILertApi {
};
await this.fetch(
`/api/v1/incidents/${encodeURIComponent(incident.id)}/actions`,
`/api/alerts/${encodeURIComponent(alert.id)}/actions`,
init,
);
}
async createIncident(eventRequest: EventRequest): Promise<boolean> {
async createAlert(eventRequest: EventRequest): Promise<boolean> {
const init = {
method: 'POST',
headers: JSON_HEADERS,
@@ -305,7 +286,7 @@ export class ILertClient implements ILertApi {
}),
};
const response = await this.fetch('/api/v1/events', init);
const response = await this.fetch('/api/events', init);
return response;
}
@@ -314,7 +295,7 @@ export class ILertClient implements ILertApi {
headers: JSON_HEADERS,
};
const response = await this.fetch('/api/v1/uptime-monitors', init);
const response = await this.fetch('/api/uptime-monitors', init);
return response;
}
@@ -325,7 +306,7 @@ export class ILertClient implements ILertApi {
};
const response: UptimeMonitor = await this.fetch(
`/api/v1/uptime-monitors/${encodeURIComponent(id)}`,
`/api/uptime-monitors/${encodeURIComponent(id)}`,
init,
);
@@ -342,7 +323,7 @@ export class ILertClient implements ILertApi {
};
const response = await this.fetch(
`/api/v1/uptime-monitors/${encodeURIComponent(uptimeMonitor.id)}`,
`/api/uptime-monitors/${encodeURIComponent(uptimeMonitor.id)}`,
init,
);
@@ -359,7 +340,7 @@ export class ILertClient implements ILertApi {
};
const response = await this.fetch(
`/api/v1/uptime-monitors/${encodeURIComponent(uptimeMonitor.id)}`,
`/api/uptime-monitors/${encodeURIComponent(uptimeMonitor.id)}`,
init,
);
@@ -371,7 +352,7 @@ export class ILertClient implements ILertApi {
headers: JSON_HEADERS,
};
const response = await this.fetch('/api/v1/alert-sources', init);
const response = await this.fetch('/api/alert-sources', init);
return response;
}
@@ -384,7 +365,7 @@ export class ILertClient implements ILertApi {
};
const response = await this.fetch(
`/api/v1/alert-sources/${encodeURIComponent(idOrIntegrationKey)}`,
`/api/alert-sources/${encodeURIComponent(idOrIntegrationKey)}`,
init,
);
@@ -397,7 +378,7 @@ export class ILertClient implements ILertApi {
};
const response = await this.fetch(
`/api/v1/on-calls?policies=${encodeURIComponent(
`/api/on-calls?policies=${encodeURIComponent(
alertSource.escalationPolicy.id,
)}&expand=user&expand=escalationPolicy&timezone=${dt.local().zoneName}`,
init,
@@ -414,7 +395,7 @@ export class ILertClient implements ILertApi {
};
const response = await this.fetch(
`/api/v1/alert-sources/${encodeURIComponent(alertSource.id)}`,
`/api/alert-sources/${encodeURIComponent(alertSource.id)}`,
init,
);
@@ -429,7 +410,7 @@ export class ILertClient implements ILertApi {
};
const response = await this.fetch(
`/api/v1/alert-sources/${encodeURIComponent(alertSource.id)}`,
`/api/alert-sources/${encodeURIComponent(alertSource.id)}`,
init,
);
@@ -453,7 +434,7 @@ export class ILertClient implements ILertApi {
}),
};
const response = await this.fetch('/api/v1/maintenance-windows', init);
const response = await this.fetch('/api/maintenance-windows', init);
return response;
}
@@ -463,7 +444,7 @@ export class ILertClient implements ILertApi {
headers: JSON_HEADERS,
};
const response = await this.fetch('/api/v1/schedules', init);
const response = await this.fetch('/api/schedules', init);
return response;
}
@@ -473,7 +454,7 @@ export class ILertClient implements ILertApi {
headers: JSON_HEADERS,
};
const response = await this.fetch('/api/v1/users', init);
const response = await this.fetch('/api/users', init);
return response;
}
@@ -491,17 +472,15 @@ export class ILertClient implements ILertApi {
};
const response = await this.fetch(
`/api/v1/schedules/${encodeURIComponent(scheduleId)}/overrides`,
`/api/schedules/${encodeURIComponent(scheduleId)}/overrides`,
init,
);
return response;
}
getIncidentDetailsURL(incident: Incident): string {
return `${this.baseUrl}/incident/view.jsf?id=${encodeURIComponent(
incident.id,
)}`;
getAlertDetailsURL(alert: Alert): string {
return `${this.baseUrl}/alert/view.jsf?id=${encodeURIComponent(alert.id)}`;
}
getAlertSourceDetailsURL(alertSource: AlertSource | null): string {
+4 -4
View File
@@ -14,11 +14,11 @@
* limitations under the License.
*/
export { ILertClient, ilertApiRef } from './client';
export { ilertApiRef, ILertClient } from './client';
export type {
ILertApi,
EventRequest,
GetIncidentsCountOpts,
GetIncidentsOpts,
GetAlertsCountOpts,
GetAlertsOpts,
ILertApi,
TableState,
} from './types';
+22 -28
View File
@@ -15,16 +15,16 @@
*/
import {
Alert,
AlertAction,
AlertResponder,
AlertSource,
Incident,
User,
IncidentStatus,
UptimeMonitor,
AlertStatus,
EscalationPolicy,
Schedule,
IncidentResponder,
IncidentAction,
OnCall,
Schedule,
UptimeMonitor,
User,
} from '../types';
/** @public */
@@ -34,16 +34,16 @@ export type TableState = {
};
/** @public */
export type GetIncidentsOpts = {
export type GetAlertsOpts = {
maxResults?: number;
startIndex?: number;
states?: IncidentStatus[];
states?: AlertStatus[];
alertSources?: number[];
};
/** @public */
export type GetIncidentsCountOpts = {
states?: IncidentStatus[];
export type GetAlertsCountOpts = {
states?: AlertStatus[];
};
/** @public */
@@ -57,22 +57,16 @@ export type EventRequest = {
/** @public */
export interface ILertApi {
fetchIncidents(opts?: GetIncidentsOpts): Promise<Incident[]>;
fetchIncidentsCount(opts?: GetIncidentsCountOpts): Promise<number>;
fetchIncident(id: number): Promise<Incident>;
fetchIncidentResponders(incident: Incident): Promise<IncidentResponder[]>;
fetchIncidentActions(incident: Incident): Promise<IncidentAction[]>;
acceptIncident(incident: Incident, userName: string): Promise<Incident>;
resolveIncident(incident: Incident, userName: string): Promise<Incident>;
assignIncident(
incident: Incident,
responder: IncidentResponder,
): Promise<Incident>;
createIncident(eventRequest: EventRequest): Promise<boolean>;
triggerIncidentAction(
incident: Incident,
action: IncidentAction,
): Promise<void>;
fetchAlerts(opts?: GetAlertsOpts): Promise<Alert[]>;
fetchAlertsCount(opts?: GetAlertsCountOpts): Promise<number>;
fetchAlert(id: number): Promise<Alert>;
fetchAlertResponders(alert: Alert): Promise<AlertResponder[]>;
fetchAlertActions(alert: Alert): Promise<AlertAction[]>;
acceptAlert(alert: Alert, userName: string): Promise<Alert>;
resolveAlert(alert: Alert, userName: string): Promise<Alert>;
assignAlert(alert: Alert, responder: AlertResponder): Promise<Alert>;
createAlert(eventRequest: EventRequest): Promise<boolean>;
triggerAlertAction(alert: Alert, action: AlertAction): Promise<void>;
fetchUptimeMonitors(): Promise<UptimeMonitor[]>;
pauseUptimeMonitor(uptimeMonitor: UptimeMonitor): Promise<UptimeMonitor>;
@@ -100,7 +94,7 @@ export interface ILertApi {
end: string,
): Promise<Schedule>;
getIncidentDetailsURL(incident: Incident): string;
getAlertDetailsURL(alert: Alert): string;
getAlertSourceDetailsURL(alertSource: AlertSource | null): string;
getEscalationPolicyDetailsURL(escalationPolicy: EscalationPolicy): string;
getUptimeMonitorDetailsURL(uptimeMonitor: UptimeMonitor): string;
@@ -13,42 +13,42 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { IconButton, Menu, MenuItem, Typography } from '@material-ui/core';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import React from 'react';
import { ilertApiRef } from '../../api';
import { Incident, IncidentAction } from '../../types';
import { IncidentAssignModal } from './IncidentAssignModal';
import { useIncidentActions } from '../../hooks/useIncidentActions';
import { useAlertActions } from '../../hooks/useAlertActions';
import { Alert, AlertAction } from '../../types';
import { AlertAssignModal } from './AlertAssignModal';
import { DEFAULT_NAMESPACE, parseEntityRef } from '@backstage/catalog-model';
import { Link, Progress } from '@backstage/core-components';
import {
alertApiRef,
useApi,
identityApiRef,
useApi,
} from '@backstage/core-plugin-api';
import { Progress, Link } from '@backstage/core-components';
import { DEFAULT_NAMESPACE, parseEntityRef } from '@backstage/catalog-model';
export const IncidentActionsMenu = ({
incident,
onIncidentChanged,
export const AlertActionsMenu = ({
alert,
onAlertChanged,
setIsLoading,
}: {
incident: Incident;
onIncidentChanged?: (incident: Incident) => void;
alert: Alert;
onAlertChanged?: (alert: Alert) => void;
setIsLoading?: (isLoading: boolean) => void;
}) => {
const ilertApi = useApi(ilertApiRef);
const alertApi = useApi(alertApiRef);
const identityApi = useApi(identityApiRef);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const callback = onIncidentChanged || ((_: Incident): void => {});
const callback = onAlertChanged || ((_: Alert): void => {});
const setProcessing = setIsLoading || ((_: boolean): void => {});
const [isAssignIncidentModalOpened, setIsAssignIncidentModalOpened] =
const [isAssignAlertModalOpened, setIsAssignAlertModalOpened] =
React.useState(false);
const [{ incidentActions, isLoading }] = useIncidentActions(
incident,
const [{ alertActions, isLoading }] = useAlertActions(
alert,
Boolean(anchorEl),
);
@@ -70,10 +70,10 @@ export const IncidentActionsMenu = ({
defaultKind: 'User',
defaultNamespace: DEFAULT_NAMESPACE,
});
const newIncident = await ilertApi.acceptIncident(incident, userName);
alertApi.post({ message: 'Incident accepted.' });
const newAlert = await ilertApi.acceptAlert(alert, userName);
alertApi.post({ message: 'Alert accepted.' });
callback(newIncident);
callback(newAlert);
setProcessing(false);
} catch (err) {
setProcessing(false);
@@ -90,10 +90,10 @@ export const IncidentActionsMenu = ({
defaultKind: 'User',
defaultNamespace: DEFAULT_NAMESPACE,
});
const newIncident = await ilertApi.resolveIncident(incident, userName);
alertApi.post({ message: 'Incident resolved.' });
const newAlert = await ilertApi.resolveAlert(alert, userName);
alertApi.post({ message: 'Alert resolved.' });
callback(newIncident);
callback(newAlert);
setProcessing(false);
} catch (err) {
setProcessing(false);
@@ -103,15 +103,15 @@ export const IncidentActionsMenu = ({
const handleAssign = () => {
handleCloseMenu();
setIsAssignIncidentModalOpened(true);
setIsAssignAlertModalOpened(true);
};
const handleTriggerAction = (action: IncidentAction) => async () => {
const handleTriggerAction = (action: AlertAction) => async () => {
try {
handleCloseMenu();
setProcessing(true);
await ilertApi.triggerIncidentAction(incident, action);
alertApi.post({ message: 'Incident action triggered.' });
await ilertApi.triggerAlertAction(alert, action);
alertApi.post({ message: 'Alert action triggered.' });
setProcessing(false);
} catch (err) {
setProcessing(false);
@@ -119,7 +119,7 @@ export const IncidentActionsMenu = ({
}
};
const actions: React.ReactNode[] = incidentActions.map(a => {
const actions: React.ReactNode[] = alertActions.map(a => {
const successTrigger = a.history
? a.history.find(h => h.success)
: undefined;
@@ -152,7 +152,7 @@ export const IncidentActionsMenu = ({
<MoreVertIcon />
</IconButton>
<Menu
id={`incident-actions-menu-${incident.id}`}
id={`alert-actions-menu-${alert.id}`}
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
@@ -161,7 +161,7 @@ export const IncidentActionsMenu = ({
style: { maxHeight: 48 * 5.5 },
}}
>
{incident.status === 'PENDING' ? (
{alert.status === 'PENDING' ? (
<MenuItem key="ack" onClick={handleAccept}>
<Typography variant="inherit" noWrap>
Accept
@@ -169,7 +169,7 @@ export const IncidentActionsMenu = ({
</MenuItem>
) : null}
{incident.status !== 'RESOLVED' ? (
{alert.status !== 'RESOLVED' ? (
<MenuItem key="close" onClick={handleResolve}>
<Typography variant="inherit" noWrap>
Resolve
@@ -177,7 +177,7 @@ export const IncidentActionsMenu = ({
</MenuItem>
) : null}
{incident.status !== 'RESOLVED' ? (
{alert.status !== 'RESOLVED' ? (
<MenuItem key="assign" onClick={handleAssign}>
<Typography variant="inherit" noWrap>
Assign
@@ -195,17 +195,15 @@ export const IncidentActionsMenu = ({
<MenuItem key="details" onClick={handleCloseMenu}>
<Typography variant="inherit" noWrap>
<Link to={ilertApi.getIncidentDetailsURL(incident)}>
View in iLert
</Link>
<Link to={ilertApi.getAlertDetailsURL(alert)}>View in iLert</Link>
</Typography>
</MenuItem>
</Menu>
<IncidentAssignModal
incident={incident}
setIsModalOpened={setIsAssignIncidentModalOpened}
isModalOpened={isAssignIncidentModalOpened}
onIncidentChanged={onIncidentChanged}
<AlertAssignModal
alert={alert}
setIsModalOpened={setIsAssignAlertModalOpened}
isModalOpened={isAssignAlertModalOpened}
onAlertChanged={onAlertChanged}
/>
</>
);
@@ -13,21 +13,21 @@
* 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 Alert from '@material-ui/lab/Alert';
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
import { Typography } from '@material-ui/core';
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 { makeStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import MUIAlert from '@material-ui/lab/Alert';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { useAssignIncident } from '../../hooks/useAssignIncident';
import { Typography } from '@material-ui/core';
import React from 'react';
import { ilertApiRef } from '../../api';
import { Incident } from '../../types';
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
import { useAssignAlert } from '../../hooks/useAssignAlert';
import { Alert } from '../../types';
const useStyles = makeStyles(() => ({
container: {
@@ -55,45 +55,42 @@ const useStyles = makeStyles(() => ({
},
}));
export const IncidentAssignModal = ({
incident,
export const AlertAssignModal = ({
alert,
isModalOpened,
setIsModalOpened,
onIncidentChanged,
onAlertChanged,
}: {
incident: Incident | null;
alert: Alert | null;
isModalOpened: boolean;
setIsModalOpened: (open: boolean) => void;
onIncidentChanged?: (incident: Incident) => void;
onAlertChanged?: (alert: Alert) => void;
}) => {
const [
{ incidentRespondersList, incidentResponder, isLoading },
{ setIsLoading, setIncidentResponder, setIncidentRespondersList },
] = useAssignIncident(incident, isModalOpened);
const callback = onIncidentChanged || ((_: Incident): void => {});
{ alertRespondersList, alertResponder, isLoading },
{ setIsLoading, setAlertResponder, setAlertRespondersList },
] = useAssignAlert(alert, isModalOpened);
const callback = onAlertChanged || ((_: Alert): void => {});
const ilertApi = useApi(ilertApiRef);
const alertApi = useApi(alertApiRef);
const classes = useStyles();
const handleClose = () => {
setIncidentRespondersList([]);
setAlertRespondersList([]);
setIsModalOpened(false);
};
const handleAssign = () => {
if (!incident || !incidentResponder) {
if (!alert || !alertResponder) {
return;
}
setIsLoading(true);
setIncidentRespondersList([]);
setAlertRespondersList([]);
setTimeout(async () => {
try {
const newIncident = await ilertApi.assignIncident(
incident,
incidentResponder,
);
callback(newIncident);
alertApi.post({ message: 'Incident assigned.' });
const newAlert = await ilertApi.assignAlert(alert, alertResponder);
callback(newAlert);
alertApi.post({ message: 'Alert assigned.' });
} catch (err) {
alertApi.post({ message: err, severity: 'error' });
}
@@ -102,33 +99,33 @@ export const IncidentAssignModal = ({
}, 250);
};
const canAssign = !!incidentResponder;
const canAssign = !!alertResponder;
return (
<Dialog
open={isModalOpened}
onClose={handleClose}
aria-labelledby="assign-incident-form-title"
aria-labelledby="assign-alert-form-title"
>
<DialogTitle id="assign-incident-form-title">
<DialogTitle id="assign-alert-form-title">
Select responder to assign
</DialogTitle>
<DialogContent>
<Alert severity="info">
<MUIAlert severity="info">
<Typography variant="body1" gutterBottom align="justify">
This action will assign the incident to the selected responder.
This action will assign the alert to the selected responder.
</Typography>
</Alert>
</MUIAlert>
<Autocomplete
disabled={isLoading}
options={incidentRespondersList}
value={incidentResponder}
options={alertRespondersList}
value={alertResponder}
classes={{
root: classes.formControl,
option: classes.option,
}}
onChange={(_event: any, newValue: any) => {
setIncidentResponder(newValue);
setAlertResponder(newValue);
}}
autoHighlight
groupBy={option => {
@@ -13,13 +13,13 @@
* 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 { Incident } from '../../types';
import React from 'react';
import { ilertApiRef } from '../../api';
import { Alert } from '../../types';
import { useApi } from '@backstage/core-plugin-api';
import { Link } from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
const useStyles = makeStyles({
link: {
@@ -27,20 +27,17 @@ const useStyles = makeStyles({
},
});
export const IncidentLink = ({ incident }: { incident: Incident | null }) => {
export const AlertLink = ({ alert }: { alert: Alert | null }) => {
const ilertApi = useApi(ilertApiRef);
const classes = useStyles();
if (!incident) {
if (!alert) {
return null;
}
return (
<Link
className={classes.link}
to={ilertApi.getIncidentDetailsURL(incident)}
>
#{incident.id}
<Link className={classes.link} to={ilertApi.getAlertDetailsURL(alert)}>
#{alert.id}
</Link>
);
};
@@ -13,27 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { DEFAULT_NAMESPACE, parseEntityRef } from '@backstage/catalog-model';
import { makeStyles } from '@material-ui/core/styles';
import Alert from '@material-ui/lab/Alert';
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 { useNewIncident } from '../../hooks/useNewIncident';
import { Typography } from '@material-ui/core';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import { ilertApiRef } from '../../api';
import { AlertSource } from '../../types';
import {
alertApiRef,
identityApiRef,
useApi,
} from '@backstage/core-plugin-api';
import { Typography } from '@material-ui/core';
import Button from '@material-ui/core/Button';
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 { makeStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import Alert from '@material-ui/lab/Alert';
import Autocomplete from '@material-ui/lab/Autocomplete';
import React from 'react';
import { ilertApiRef } from '../../api';
import { useNewAlert } from '../../hooks/useNewAlert';
import { AlertSource } from '../../types';
const useStyles = makeStyles(() => ({
container: {
@@ -61,23 +61,23 @@ const useStyles = makeStyles(() => ({
},
}));
export const IncidentNewModal = ({
export const AlertNewModal = ({
isModalOpened,
setIsModalOpened,
refetchIncidents,
refetchAlerts,
initialAlertSource,
entityName,
}: {
isModalOpened: boolean;
setIsModalOpened: (open: boolean) => void;
refetchIncidents: () => void;
refetchAlerts: () => void;
initialAlertSource?: AlertSource | null;
entityName?: string;
}) => {
const [
{ alertSources, alertSource, summary, details, isLoading },
{ setAlertSource, setSummary, setDetails, setIsLoading },
] = useNewIncident(isModalOpened, initialAlertSource);
] = useNewAlert(isModalOpened, initialAlertSource);
const ilertApi = useApi(ilertApiRef);
const alertApi = useApi(alertApiRef);
const identityApi = useApi(identityApiRef);
@@ -107,15 +107,15 @@ export const IncidentNewModal = ({
defaultKind: 'User',
defaultNamespace: DEFAULT_NAMESPACE,
});
await ilertApi.createIncident({
await ilertApi.createAlert({
integrationKey,
summary,
details,
userName,
source,
});
alertApi.post({ message: 'Incident created.' });
refetchIncidents();
alertApi.post({ message: 'Alert created.' });
refetchAlerts();
} catch (err) {
alertApi.post({ message: err, severity: 'error' });
}
@@ -129,16 +129,16 @@ export const IncidentNewModal = ({
<Dialog
open={isModalOpened}
onClose={handleClose}
aria-labelledby="create-incident-form-title"
aria-labelledby="create-alert-form-title"
>
<DialogTitle id="create-incident-form-title">
<DialogTitle id="create-alert-form-title">
{entityName ? (
<div>
This action will trigger an incident for{' '}
This action will trigger an alert for{' '}
<strong>"{entityName}"</strong>.
</div>
) : (
'New incident'
'New alert'
)}
</DialogTitle>
<DialogContent>
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { StatusError, StatusOK } from '@backstage/core-components';
import { makeStyles } from '@material-ui/core/styles';
import Tooltip from '@material-ui/core/Tooltip';
import { ACCEPTED, Incident, PENDING, RESOLVED } from '../../types';
import { StatusError, StatusOK } from '@backstage/core-components';
import React from 'react';
import { ACCEPTED, Alert, PENDING, RESOLVED } from '../../types';
const useStyles = makeStyles({
denseListIcon: {
@@ -29,19 +29,19 @@ const useStyles = makeStyles({
},
});
export const incidentStatusLabels = {
export const alertStatusLabels = {
[RESOLVED]: 'Resolved',
[ACCEPTED]: 'Accepted',
[PENDING]: 'Pending',
} as Record<string, string>;
export const IncidentStatus = ({ incident }: { incident: Incident }) => {
export const AlertStatus = ({ alert }: { alert: Alert }) => {
const classes = useStyles();
return (
<Tooltip title={incident.status} placement="top">
<Tooltip title={alert.status} placement="top">
<div className={classes.denseListIcon}>
{incident.status === 'PENDING' ? <StatusError /> : <StatusOK />}
{alert.status === 'PENDING' ? <StatusError /> : <StatusOK />}
</div>
</Tooltip>
);
@@ -13,5 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './IncidentsPage';
export * from './IncidentsTable';
export * from './AlertActionsMenu';
export * from './AlertStatus';
@@ -13,37 +13,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { AuthenticationError } from '@backstage/errors';
import Button from '@material-ui/core/Button';
import AddIcon from '@material-ui/icons/Add';
import { IncidentsTable } from './IncidentsTable';
import { MissingAuthorizationHeaderError } from '../Errors';
import { useIncidents } from '../../hooks/useIncidents';
import { IncidentNewModal } from '../Incident/IncidentNewModal';
import {
Content,
ContentHeader,
SupportButton,
ResponseErrorPanel,
SupportButton,
} from '@backstage/core-components';
import { AuthenticationError } from '@backstage/errors';
import Button from '@material-ui/core/Button';
import AddIcon from '@material-ui/icons/Add';
import React from 'react';
import { useAlerts } from '../../hooks/useAlerts';
import { AlertNewModal } from '../Alert/AlertNewModal';
import { MissingAuthorizationHeaderError } from '../Errors';
import { AlertsTable } from './AlertsTable';
export const IncidentsPage = () => {
export const AlertsPage = () => {
const [
{ tableState, states, incidents, incidentsCount, isLoading, error },
{ tableState, states, alerts, alertsCount, isLoading, error },
{
onIncidentStatesChange,
onAlertStatesChange,
onChangePage,
onChangeRowsPerPage,
onIncidentChanged,
refetchIncidents,
onAlertChanged,
refetchAlerts,
setIsLoading,
},
] = useIncidents(true);
] = useAlerts(true);
const [isModalOpened, setIsModalOpened] = React.useState(false);
const handleCreateNewIncidentClick = () => {
const handleCreateNewAlertClick = () => {
setIsModalOpened(true);
};
@@ -65,32 +65,32 @@ export const IncidentsPage = () => {
return (
<Content>
<ContentHeader title="Incidents">
<ContentHeader title="Alerts">
<Button
variant="contained"
color="primary"
size="small"
startIcon={<AddIcon />}
onClick={handleCreateNewIncidentClick}
onClick={handleCreateNewAlertClick}
>
Create Incident
Create Alert
</Button>
<IncidentNewModal
<AlertNewModal
isModalOpened={isModalOpened}
setIsModalOpened={setIsModalOpened}
refetchIncidents={refetchIncidents}
refetchAlerts={refetchAlerts}
/>
<SupportButton>
This helps you to bring iLert into your developer portal.
</SupportButton>
</ContentHeader>
<IncidentsTable
incidents={incidents}
incidentsCount={incidentsCount}
<AlertsTable
alerts={alerts}
alertsCount={alertsCount}
tableState={tableState}
states={states}
onIncidentChanged={onIncidentChanged}
onIncidentStatesChange={onIncidentStatesChange}
onAlertChanged={onAlertChanged}
onAlertStatesChange={onAlertStatesChange}
onChangePage={onChangePage}
onChangeRowsPerPage={onChangeRowsPerPage}
isLoading={isLoading}
@@ -13,18 +13,18 @@
* 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 { ilertApiRef, TableState } from '../../api';
import { Incident, IncidentStatus } from '../../types';
import { StatusChip } from './StatusChip';
import { AlertSourceLink } from '../AlertSource/AlertSourceLink';
import { TableTitle } from './TableTitle';
import Typography from '@material-ui/core/Typography';
import { DateTime as dt, Interval } from 'luxon';
import humanizeDuration from 'humanize-duration';
import { IncidentActionsMenu } from '../Incident/IncidentActionsMenu';
import { IncidentLink } from '../Incident/IncidentLink';
import { DateTime as dt, Interval } from 'luxon';
import React from 'react';
import { ilertApiRef, TableState } from '../../api';
import { Alert, AlertStatus } from '../../types';
import { AlertActionsMenu } from '../Alert/AlertActionsMenu';
import { AlertLink } from '../Alert/AlertLink';
import { AlertSourceLink } from '../AlertSource/AlertSourceLink';
import { StatusChip } from './StatusChip';
import { TableTitle } from './TableTitle';
import { Table, TableColumn } from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
@@ -37,27 +37,27 @@ const useStyles = makeStyles(theme => ({
},
}));
export const IncidentsTable = ({
incidents,
incidentsCount,
export const AlertsTable = ({
alerts,
alertsCount,
tableState,
states,
isLoading,
onIncidentChanged,
onAlertChanged,
setIsLoading,
onIncidentStatesChange,
onAlertStatesChange,
onChangePage,
onChangeRowsPerPage,
compact,
}: {
incidents: Incident[];
incidentsCount: number;
alerts: Alert[];
alertsCount: number;
tableState: TableState;
states: IncidentStatus[];
states: AlertStatus[];
isLoading: boolean;
onIncidentChanged: (incident: Incident) => void;
onAlertChanged: (alert: Alert) => void;
setIsLoading: (isLoading: boolean) => void;
onIncidentStatesChange: (states: IncidentStatus[]) => void;
onAlertStatesChange: (states: AlertStatus[]) => void;
onChangePage: (page: number) => void;
onChangeRowsPerPage: (pageSize: number) => void;
compact?: boolean;
@@ -92,14 +92,14 @@ export const IncidentsTable = ({
highlight: true,
cellStyle: smColumnStyle,
headerStyle: smColumnStyle,
render: rowData => <IncidentLink incident={rowData as Incident} />,
render: rowData => <AlertLink alert={rowData as Alert} />,
};
const summaryColumn: TableColumn = {
title: 'Summary',
field: 'summary',
cellStyle: !compact ? xlColumnStyle : undefined,
headerStyle: !compact ? xlColumnStyle : undefined,
render: rowData => <Typography>{(rowData as Incident).summary}</Typography>,
render: rowData => <Typography>{(rowData as Alert).summary}</Typography>,
};
const sourceColumn: TableColumn = {
title: 'Source',
@@ -107,7 +107,7 @@ export const IncidentsTable = ({
cellStyle: mdColumnStyle,
headerStyle: mdColumnStyle,
render: rowData => (
<AlertSourceLink alertSource={(rowData as Incident).alertSource} />
<AlertSourceLink alertSource={(rowData as Alert).alertSource} />
),
};
const durationColumn: TableColumn = {
@@ -118,10 +118,10 @@ export const IncidentsTable = ({
headerStyle: smColumnStyle,
render: rowData => (
<Typography noWrap>
{(rowData as Incident).status !== 'RESOLVED'
{(rowData as Alert).status !== 'RESOLVED'
? humanizeDuration(
Interval.fromDateTimes(
dt.fromISO((rowData as Incident).reportTime),
dt.fromISO((rowData as Alert).reportTime),
dt.now(),
)
.toDuration()
@@ -130,8 +130,8 @@ export const IncidentsTable = ({
)
: humanizeDuration(
Interval.fromDateTimes(
dt.fromISO((rowData as Incident).reportTime),
dt.fromISO((rowData as Incident).resolvedOn),
dt.fromISO((rowData as Alert).reportTime),
dt.fromISO((rowData as Alert).resolvedOn),
)
.toDuration()
.valueOf(),
@@ -147,7 +147,7 @@ export const IncidentsTable = ({
headerStyle: !compact ? mdColumnStyle : lgColumnStyle,
render: rowData => (
<Typography noWrap>
{ilertApi.getUserInitials((rowData as Incident).assignedTo)}
{ilertApi.getUserInitials((rowData as Alert).assignedTo)}
</Typography>
),
};
@@ -158,7 +158,7 @@ export const IncidentsTable = ({
headerStyle: smColumnStyle,
render: rowData => (
<Typography noWrap>
{(rowData as Incident).priority === 'HIGH' ? 'High' : 'Low'}
{(rowData as Alert).priority === 'HIGH' ? 'High' : 'Low'}
</Typography>
),
};
@@ -167,7 +167,7 @@ export const IncidentsTable = ({
field: 'status',
cellStyle: xsColumnStyle,
headerStyle: xsColumnStyle,
render: rowData => <StatusChip incident={rowData as Incident} />,
render: rowData => <StatusChip alert={rowData as Alert} />,
};
const actionsColumn: TableColumn = {
title: '',
@@ -175,9 +175,9 @@ export const IncidentsTable = ({
cellStyle: xsColumnStyle,
headerStyle: xsColumnStyle,
render: rowData => (
<IncidentActionsMenu
incident={rowData as Incident}
onIncidentChanged={onIncidentChanged}
<AlertActionsMenu
alert={rowData as Alert}
onAlertChanged={onAlertChanged}
setIsLoading={setIsLoading}
/>
),
@@ -236,14 +236,14 @@ export const IncidentsTable = ({
}}
emptyContent={
<Typography color="textSecondary" className={classes.empty}>
No incidents right now
No alerts right now
</Typography>
}
title={
!compact ? (
<TableTitle
incidentStates={states}
onIncidentStatesChange={onIncidentStatesChange}
alertStates={states}
onAlertStatesChange={onAlertStatesChange}
/>
) : (
<Typography variant="button" color="textSecondary">
@@ -252,12 +252,12 @@ export const IncidentsTable = ({
)
}
page={tableState.page}
totalCount={incidentsCount}
totalCount={alertsCount}
onPageChange={onChangePage}
onRowsPerPageChange={onChangeRowsPerPage}
// localization={{ header: { actions: undefined } }}
columns={columns}
data={incidents}
data={alerts}
isLoading={isLoading}
/>
);
@@ -13,10 +13,10 @@
* 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';
import React from 'react';
import { ACCEPTED, Alert, PENDING, RESOLVED } from '../../types';
import { alertStatusLabels } from '../Alert/AlertStatus';
const ResolvedChip = withStyles({
root: {
@@ -41,10 +41,10 @@ const PendingChip = withStyles({
},
})(Chip);
export const StatusChip = ({ incident }: { incident: Incident }) => {
const label = `${incidentStatusLabels[incident.status]}`;
export const StatusChip = ({ alert }: { alert: Alert }) => {
const label = `${alertStatusLabels[alert.status]}`;
switch (incident.status) {
switch (alert.status) {
case RESOLVED:
return <ResolvedChip label={label} size="small" />;
case ACCEPTED:
@@ -13,16 +13,16 @@
* 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 Checkbox from '@material-ui/core/Checkbox';
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 Select from '@material-ui/core/Select';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import React from 'react';
import { ACCEPTED, AlertStatus, PENDING, RESOLVED } from '../../types';
import { alertStatusLabels } from '../Alert/AlertStatus';
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
@@ -53,15 +53,15 @@ const useStyles = makeStyles({
});
export const TableTitle = ({
incidentStates,
onIncidentStatesChange,
alertStates,
onAlertStatesChange,
}: {
incidentStates: IncidentStatus[];
onIncidentStatesChange: (states: IncidentStatus[]) => void;
alertStates: AlertStatus[];
onAlertStatesChange: (states: AlertStatus[]) => void;
}) => {
const classes = useStyles();
const handleIncidentStatusSelectChange = (event: any) => {
onIncidentStatesChange(event.target.value);
const handleAlertStatusSelectChange = (event: any) => {
onAlertStatesChange(event.target.value);
};
return (
@@ -75,19 +75,19 @@ export const TableTitle = ({
size="small"
>
<Select
id="incidents-status-select"
id="alerts-status-select"
multiple
value={incidentStates}
onChange={handleIncidentStatusSelectChange}
value={alertStates}
onChange={handleAlertStatusSelectChange}
renderValue={(selected: any) => selected.join(', ')}
MenuProps={MenuProps}
>
{[PENDING, ACCEPTED, RESOLVED].map(state => (
<MenuItem key={state} value={state}>
<Checkbox
checked={incidentStates.indexOf(state as IncidentStatus) > -1}
checked={alertStates.indexOf(state as AlertStatus) > -1}
/>
<ListItemText primary={incidentStatusLabels[state]} />
<ListItemText primary={alertStatusLabels[state]} />
</MenuItem>
))}
</Select>
@@ -13,5 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './IncidentActionsMenu';
export * from './IncidentStatus';
export * from './AlertsPage';
export * from './AlertsTable';
@@ -13,27 +13,27 @@
* 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 { ResponseErrorPanel } from '@backstage/core-components';
import { AuthenticationError } from '@backstage/errors';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardHeader from '@material-ui/core/CardHeader';
import Divider from '@material-ui/core/Divider';
import { makeStyles } from '@material-ui/core/styles';
import React from 'react';
import { ILERT_INTEGRATION_KEY_ANNOTATION } from '../../constants';
import { MissingAuthorizationHeaderError } from '../Errors';
import { useIncidents } from '../../hooks/useIncidents';
import { IncidentsTable } from '../IncidentsPage';
import { IncidentNewModal } from '../Incident/IncidentNewModal';
import { ILertCardActionsHeader } from './ILertCardActionsHeader';
import { useAlertSource } from '../../hooks/useAlertSource';
import { useILertEntity } from '../../hooks';
import { useAlerts } from '../../hooks/useAlerts';
import { useAlertSource } from '../../hooks/useAlertSource';
import { AlertNewModal } from '../Alert/AlertNewModal';
import { AlertsTable } from '../AlertsPage';
import { MissingAuthorizationHeaderError } from '../Errors';
import { ILertCardActionsHeader } from './ILertCardActionsHeader';
import { ILertCardEmptyState } from './ILertCardEmptyState';
import { ILertCardHeaderStatus } from './ILertCardHeaderStatus';
import { ILertCardMaintenanceModal } from './ILertCardMaintenanceModal';
import { ILertCardEmptyState } from './ILertCardEmptyState';
import { ILertCardOnCall } from './ILertCardOnCall';
import { ResponseErrorPanel } from '@backstage/core-components';
/** @public */
export const isPluginApplicableToEntity = (entity: Entity) =>
@@ -60,18 +60,18 @@ export const ILertCard = () => {
{ setAlertSource, refetchAlertSource },
] = useAlertSource(integrationKey);
const [
{ tableState, states, incidents, incidentsCount, isLoading, error },
{ tableState, states, alerts, alertsCount, isLoading, error },
{
onIncidentStatesChange,
onAlertStatesChange,
onChangePage,
onChangeRowsPerPage,
onIncidentChanged,
refetchIncidents,
onAlertChanged,
refetchAlerts,
setIsLoading,
},
] = useIncidents(false, true, alertSource);
] = useAlerts(false, true, alertSource);
const [isNewIncidentModalOpened, setIsNewIncidentModalOpened] =
const [isNewAlertModalOpened, setIsNewAlertModalOpened] =
React.useState(false);
const [isMaintenanceModalOpened, setIsMaintenanceModalOpened] =
React.useState(false);
@@ -97,7 +97,7 @@ export const ILertCard = () => {
<ILertCardActionsHeader
alertSource={alertSource}
setAlertSource={setAlertSource}
setIsNewIncidentModalOpened={setIsNewIncidentModalOpened}
setIsNewAlertModalOpened={setIsNewAlertModalOpened}
setIsMaintenanceModalOpened={setIsMaintenanceModalOpened}
uptimeMonitor={uptimeMonitor}
/>
@@ -107,13 +107,13 @@ export const ILertCard = () => {
<Divider />
<CardContent className={classes.content}>
<ILertCardOnCall alertSource={alertSource} />
<IncidentsTable
incidents={incidents}
incidentsCount={incidentsCount}
<AlertsTable
alerts={alerts}
alertsCount={alertsCount}
tableState={tableState}
states={states}
onIncidentChanged={onIncidentChanged}
onIncidentStatesChange={onIncidentStatesChange}
onAlertChanged={onAlertChanged}
onAlertStatesChange={onAlertStatesChange}
onChangePage={onChangePage}
onChangeRowsPerPage={onChangeRowsPerPage}
isLoading={isLoading}
@@ -122,10 +122,10 @@ export const ILertCard = () => {
/>
</CardContent>
</Card>
<IncidentNewModal
isModalOpened={isNewIncidentModalOpened}
setIsModalOpened={setIsNewIncidentModalOpened}
refetchIncidents={refetchIncidents}
<AlertNewModal
isModalOpened={isNewAlertModalOpened}
setIsModalOpened={setIsNewAlertModalOpened}
refetchAlerts={refetchAlerts}
initialAlertSource={alertSource}
entityName={name}
/>
@@ -13,20 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import Alert from '@material-ui/lab/Alert';
import Button from '@material-ui/core/Button';
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 Typography from '@material-ui/core/Typography';
import AlarmAddIcon from '@material-ui/icons/AlarmAdd';
import BuildIcon from '@material-ui/icons/Build';
import PauseIcon from '@material-ui/icons/Pause';
import PlayArrowIcon from '@material-ui/icons/PlayArrow';
import TimelineIcon from '@material-ui/icons/Timeline';
import WebIcon from '@material-ui/icons/Web';
import Typography from '@material-ui/core/Typography';
import Alert from '@material-ui/lab/Alert';
import React from 'react';
import { ilertApiRef } from '../../api';
import { AlertSource, UptimeMonitor } from '../../types';
@@ -34,18 +34,18 @@ import {
HeaderIconLinkRow,
IconLinkVerticalProps,
} from '@backstage/core-components';
import { useApi, alertApiRef } from '@backstage/core-plugin-api';
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
export const ILertCardActionsHeader = ({
alertSource,
setAlertSource,
setIsNewIncidentModalOpened,
setIsNewAlertModalOpened,
setIsMaintenanceModalOpened,
uptimeMonitor,
}: {
alertSource: AlertSource | null;
setAlertSource: (alertSource: AlertSource) => void;
setIsNewIncidentModalOpened: (isOpen: boolean) => void;
setIsNewAlertModalOpened: (isOpen: boolean) => void;
setIsMaintenanceModalOpened: (isOpen: boolean) => void;
uptimeMonitor: UptimeMonitor | null;
}) => {
@@ -54,8 +54,8 @@ export const ILertCardActionsHeader = ({
const [isLoading, setIsLoading] = React.useState(false);
const [isDisableModalOpened, setIsDisableModalOpened] = React.useState(false);
const handleCreateNewIncident = () => {
setIsNewIncidentModalOpened(true);
const handleCreateNewAlert = () => {
setIsNewAlertModalOpened(true);
};
const handleEnableAlertSource = async () => {
@@ -108,9 +108,9 @@ export const ILertCardActionsHeader = ({
icon: <WebIcon />,
};
const createIncidentLink: IconLinkVerticalProps = {
label: 'Create Incident',
onClick: handleCreateNewIncident,
const createAlertLink: IconLinkVerticalProps = {
label: 'Create Alert',
onClick: handleCreateNewAlert,
icon: <AlarmAddIcon />,
color: 'secondary',
disabled:
@@ -149,7 +149,7 @@ export const ILertCardActionsHeader = ({
const links: IconLinkVerticalProps[] = [
alertSourceLink,
createIncidentLink,
createAlertLink,
alertSource && alertSource.active
? disableAlertSourceLink
: enableAlertSourceLink,
@@ -178,7 +178,7 @@ export const ILertCardActionsHeader = ({
<Alert severity="info">
<Typography variant="body1" align="justify">
Do you really want to disable this alert source? A disabled alert
source cannot create new incidents.
source cannot create new alerts.
</Typography>
</Alert>
</DialogContent>
@@ -13,24 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { IncidentsPage } from '../IncidentsPage';
import { UptimeMonitorsPage } from '../UptimeMonitorsPage';
import { OnCallSchedulesPage } from '../OnCallSchedulesPage';
import {
Page,
Header,
HeaderTabs,
HeaderLabel,
Content,
Header,
HeaderLabel,
HeaderTabs,
Page,
} from '@backstage/core-components';
import React from 'react';
import { AlertsPage } from '../AlertsPage';
import { OnCallSchedulesPage } from '../OnCallSchedulesPage';
import { UptimeMonitorsPage } from '../UptimeMonitorsPage';
/** @public */
export const ILertPage = () => {
const [selectedTab, setSelectedTab] = React.useState<number>(0);
const tabs = [
{ label: 'Who is on call?' },
{ label: 'Incidents' },
{ label: 'Alerts' },
{ label: 'Uptime Monitors' },
];
const renderTab = () => {
@@ -38,7 +38,7 @@ export const ILertPage = () => {
case 0:
return <OnCallSchedulesPage />;
case 1:
return <IncidentsPage />;
return <AlertsPage />;
case 2:
return <UptimeMonitorsPage />;
default:
@@ -13,48 +13,45 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { ilertApiRef } from '../api';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { AuthenticationError } from '@backstage/errors';
import React from 'react';
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import { Incident, IncidentAction } from '../types';
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
import { ilertApiRef } from '../api';
import { Alert, AlertAction } from '../types';
export const useIncidentActions = (
incident: Incident | null,
open: boolean,
) => {
export const useAlertActions = (alert: Alert | null, open: boolean) => {
const ilertApi = useApi(ilertApiRef);
const errorApi = useApi(errorApiRef);
const [incidentActionsList, setIncidentActionsList] = React.useState<
IncidentAction[]
>([]);
const [alertActionsList, setAlertActionsList] = React.useState<AlertAction[]>(
[],
);
const [isLoading, setIsLoading] = React.useState(false);
const { error, retry } = useAsyncRetry(async () => {
try {
if (!incident || !open) {
if (!alert || !open) {
return;
}
const data = await ilertApi.fetchIncidentActions(incident);
setIncidentActionsList(data);
const data = await ilertApi.fetchAlertActions(alert);
setAlertActionsList(data);
} catch (e) {
if (!(e instanceof AuthenticationError)) {
errorApi.post(e);
}
throw e;
}
}, [incident, open]);
}, [alert, open]);
return [
{
incidentActions: incidentActionsList,
alertActions: alertActionsList,
error,
isLoading,
},
{
setIncidentActionsList,
setAlertActionsList,
setIsLoading,
retry,
},
+3 -3
View File
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { ilertApiRef } from '../api';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { AuthenticationError } from '@backstage/errors';
import React from 'react';
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import { ilertApiRef } from '../api';
import { AlertSource, UptimeMonitor } from '../types';
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
export const useAlertSource = (integrationKey: string) => {
const ilertApi = useApi(ilertApiRef);
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { ilertApiRef } from '../api';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { AuthenticationError } from '@backstage/errors';
import React from 'react';
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import { ilertApiRef } from '../api';
import { AlertSource, OnCall } from '../types';
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
export const useAlertSourceOnCalls = (alertSource?: AlertSource | null) => {
const ilertApi = useApi(ilertApiRef);
@@ -13,20 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { GetIncidentsOpts, ilertApiRef, TableState } from '../api';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { AuthenticationError } from '@backstage/errors';
import React from 'react';
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import {
ACCEPTED,
PENDING,
Incident,
IncidentStatus,
AlertSource,
} from '../types';
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
import { GetAlertsOpts, ilertApiRef, TableState } from '../api';
import { ACCEPTED, Alert, AlertSource, AlertStatus, PENDING } from '../types';
export const useIncidents = (
export const useAlerts = (
paging: boolean,
singleSource?: boolean,
alertSource?: AlertSource | null,
@@ -38,21 +32,21 @@ export const useIncidents = (
page: 0,
pageSize: 10,
});
const [states, setStates] = React.useState<IncidentStatus[]>([
const [states, setStates] = React.useState<AlertStatus[]>([
ACCEPTED,
PENDING,
]);
const [incidentsList, setIncidentsList] = React.useState<Incident[]>([]);
const [incidentsCount, setIncidentsCount] = React.useState(0);
const [alertsList, setAlertsList] = React.useState<Alert[]>([]);
const [alertsCount, setAlertsCount] = React.useState(0);
const [isLoading, setIsLoading] = React.useState(false);
const fetchIncidentsCall = async () => {
const fetchAlertsCall = async () => {
try {
if (singleSource && !alertSource) {
return;
}
setIsLoading(true);
const opts: GetIncidentsOpts = {
const opts: GetAlertsOpts = {
states,
alertSources: alertSource ? [alertSource.id] : [],
};
@@ -60,8 +54,8 @@ export const useIncidents = (
opts.maxResults = tableState.pageSize;
opts.startIndex = tableState.page * tableState.pageSize;
}
const data = await ilertApi.fetchIncidents(opts);
setIncidentsList(data || []);
const data = await ilertApi.fetchAlerts(opts);
setAlertsList(data || []);
setIsLoading(false);
} catch (e) {
if (!(e instanceof AuthenticationError)) {
@@ -72,10 +66,10 @@ export const useIncidents = (
}
};
const fetchIncidentsCountCall = async () => {
const fetchAlertsCountCall = async () => {
try {
const count = await ilertApi.fetchIncidentsCount({ states });
setIncidentsCount(count || 0);
const count = await ilertApi.fetchAlertsCount({ states });
setAlertsCount(count || 0);
} catch (e) {
if (!(e instanceof AuthenticationError)) {
errorApi.post(e);
@@ -83,44 +77,44 @@ export const useIncidents = (
throw e;
}
};
const fetchIncidents = useAsyncRetry(fetchIncidentsCall, [
const fetchAlerts = useAsyncRetry(fetchAlertsCall, [
tableState,
states,
singleSource,
alertSource,
]);
const refetchIncidents = () => {
const refetchAlerts = () => {
setTableState({ ...tableState, page: 0 });
Promise.all([fetchIncidentsCall(), fetchIncidentsCountCall()]);
Promise.all([fetchAlertsCall(), fetchAlertsCountCall()]);
};
const fetchIncidentsCount = useAsyncRetry(fetchIncidentsCountCall, [states]);
const fetchAlertsCount = useAsyncRetry(fetchAlertsCountCall, [states]);
const error = fetchIncidents.error || fetchIncidentsCount.error;
const error = fetchAlerts.error || fetchAlertsCount.error;
const retry = () => {
fetchIncidents.retry();
fetchIncidentsCount.retry();
fetchAlerts.retry();
fetchAlertsCount.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);
const onAlertChanged = (newAlert: Alert) => {
let shouldRefetchAlerts = false;
setAlertsList(
alertsList.reduce((acc: Alert[], alert: Alert) => {
if (newAlert.id === alert.id) {
if (states.includes(newAlert.status)) {
acc.push(newAlert);
} else {
shouldRefetchIncidents = true;
shouldRefetchAlerts = true;
}
return acc;
}
acc.push(incident);
acc.push(alert);
return acc;
}, []),
);
if (shouldRefetchIncidents) {
refetchIncidents();
if (shouldRefetchAlerts) {
refetchAlerts();
}
};
@@ -130,7 +124,7 @@ export const useIncidents = (
const onChangeRowsPerPage = (p: number) => {
setTableState({ ...tableState, pageSize: p });
};
const onIncidentStatesChange = (s: IncidentStatus[]) => {
const onAlertStatesChange = (s: AlertStatus[]) => {
setStates(s);
};
@@ -138,22 +132,22 @@ export const useIncidents = (
{
tableState,
states,
incidents: incidentsList,
incidentsCount,
alerts: alertsList,
alertsCount,
error,
isLoading,
},
{
setTableState,
setStates,
setIncidentsList,
setAlertsList,
setIsLoading,
retry,
onIncidentChanged,
refetchIncidents,
onAlertChanged,
refetchAlerts,
onChangePage,
onChangeRowsPerPage,
onIncidentStatesChange,
onAlertStatesChange,
},
] as const;
};
@@ -13,30 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { ilertApiRef } from '../api';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { AuthenticationError } from '@backstage/errors';
import React from 'react';
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import { Incident, IncidentResponder } from '../types';
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
import { ilertApiRef } from '../api';
import { Alert, AlertResponder } from '../types';
export const useAssignIncident = (incident: Incident | null, open: boolean) => {
export const useAssignAlert = (alert: Alert | null, open: boolean) => {
const ilertApi = useApi(ilertApiRef);
const errorApi = useApi(errorApiRef);
const [incidentRespondersList, setIncidentRespondersList] = React.useState<
IncidentResponder[]
const [alertRespondersList, setAlertRespondersList] = React.useState<
AlertResponder[]
>([]);
const [incidentResponder, setIncidentResponder] =
React.useState<IncidentResponder | null>(null);
const [alertResponder, setAlertResponder] =
React.useState<AlertResponder | null>(null);
const [isLoading, setIsLoading] = React.useState(false);
const { error, retry } = useAsyncRetry(async () => {
try {
if (!incident || !open) {
if (!alert || !open) {
return;
}
const data = await ilertApi.fetchIncidentResponders(incident);
const data = await ilertApi.fetchAlertResponders(alert);
if (data && Array.isArray(data)) {
const groups = [
'SUGGESTED',
@@ -45,7 +45,7 @@ export const useAssignIncident = (incident: Incident | null, open: boolean) => {
'ON_CALL_SCHEDULE',
];
data.sort((a, b) => groups.indexOf(a.group) - groups.indexOf(b.group));
setIncidentRespondersList(data);
setAlertRespondersList(data);
}
} catch (e) {
if (!(e instanceof AuthenticationError)) {
@@ -53,18 +53,18 @@ export const useAssignIncident = (incident: Incident | null, open: boolean) => {
}
throw e;
}
}, [incident, open]);
}, [alert, open]);
return [
{
incidentRespondersList,
incidentResponder,
alertRespondersList,
alertResponder,
error,
isLoading,
},
{
setIncidentRespondersList,
setIncidentResponder,
setAlertRespondersList,
setAlertResponder,
setIsLoading,
retry,
},
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { ilertApiRef } from '../api';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { AuthenticationError } from '@backstage/errors';
import React from 'react';
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import { ilertApiRef } from '../api';
import { AlertSource } from '../types';
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
export const useNewIncident = (
export const useNewAlert = (
open: boolean,
initialAlertSource?: AlertSource | null,
) => {
+22 -22
View File
@@ -15,15 +15,15 @@
*/
/** @public */
export interface Incident {
export interface Alert {
id: number;
summary: string;
details: string;
reportTime: string;
resolvedOn: string;
status: IncidentStatus;
priority: IncidentPriority;
incidentKey: string;
status: AlertStatus;
priority: AlertPriority;
alertKey: string;
alertSource: AlertSource | null;
assignedTo: User | null;
logEntries: LogEntry[];
@@ -42,10 +42,10 @@ export const ACCEPTED = 'ACCEPTED';
export const RESOLVED = 'RESOLVED';
/** @public */
export type IncidentStatus = typeof PENDING | typeof ACCEPTED | typeof RESOLVED;
export type AlertStatus = typeof PENDING | typeof ACCEPTED | typeof RESOLVED;
/** @public */
export type IncidentPriority = 'HIGH' | 'LOW';
export type AlertPriority = 'HIGH' | 'LOW';
/** @public */
export interface Link {
@@ -76,7 +76,7 @@ export interface LogEntry {
timestamp: string;
logEntryType: string;
text: string;
incidentId?: number;
alertId?: number;
iconName?: string;
iconClass?: string;
filterTypes?: string[];
@@ -125,8 +125,8 @@ export interface AlertSource {
iconUrl?: string;
lightIconUrl?: string;
darkIconUrl?: string;
incidentCreation?: AlertSourceIncidentCreation;
incidentPriorityRule?: AlertSourceIncidentPriorityRule;
alertCreation?: AlertSourceAlertCreation;
alertPriorityRule?: AlertSourceAlertPriorityRule;
emailFiltered?: boolean;
emailResolveFiltered?: boolean;
active?: boolean;
@@ -209,16 +209,16 @@ export type AlertSourceIntegrationType =
| 'CORTEXXSOAR'
| string;
/** @public */
export type AlertSourceIncidentCreation =
| 'ONE_INCIDENT_PER_EMAIL'
| 'ONE_INCIDENT_PER_EMAIL_SUBJECT'
| 'ONE_PENDING_INCIDENT_ALLOWED'
| 'ONE_OPEN_INCIDENT_ALLOWED'
export type AlertSourceAlertCreation =
| 'ONE_ALERT_PER_EMAIL'
| 'ONE_ALERT_PER_EMAIL_SUBJECT'
| 'ONE_PENDING_ALERT_ALLOWED'
| 'ONE_OPEN_ALERT_ALLOWED'
| 'OPEN_RESOLVE_ON_EXTRACTION';
/** @public */
export type AlertSourceFilterOperator = 'AND' | 'OR';
/** @public */
export type AlertSourceIncidentPriorityRule =
export type AlertSourceAlertPriorityRule =
| 'HIGH'
| 'LOW'
| 'HIGH_DURING_SUPPORT_HOURS'
@@ -251,7 +251,7 @@ export interface AlertSourceSupportDay {
/** @public */
export interface AlertSourceSupportHours {
timezone: AlertSourceTimeZone;
autoRaiseIncidents: boolean;
autoRaiseAlerts: boolean;
supportDays: {
MONDAY: AlertSourceSupportDay;
TUESDAY: AlertSourceSupportDay;
@@ -324,7 +324,7 @@ export interface UptimeMonitor {
checkParams: UptimeMonitorCheckParams;
intervalSec: number;
timeoutMs: number;
createIncidentAfterFailedChecks: number;
createAlertAfterFailedChecks: number;
paused: boolean;
embedUrl: string;
shareUrl: string;
@@ -342,7 +342,7 @@ export interface UptimeMonitorCheckParams {
}
/** @public */
export interface IncidentResponder {
export interface AlertResponder {
group: 'SUGGESTED' | 'USER' | 'ESCALATION_POLICY' | 'ON_CALL_SCHEDULE';
id: number;
name: string;
@@ -350,19 +350,19 @@ export interface IncidentResponder {
}
/** @public */
export interface IncidentAction {
export interface AlertAction {
name: string;
type: string;
webhookId: string;
extensionId?: string;
history?: IncidentActionHistory[];
history?: AlertActionHistory[];
}
/** @public */
export interface IncidentActionHistory {
export interface AlertActionHistory {
id: string;
webhookId: string;
incidentId: number;
alertId: number;
actor: User;
success: boolean;
}