Merge pull request #14110 from iLert/feature/iLert-services-statuspages-multiple-responders
Feature: add iLert services, status pages and multiple-responders for alerts
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-ilert': minor
|
||||
---
|
||||
|
||||
Added support for multiple responders in alert list, added new tab with list to support iLert resource 'service', added new tab with list to support iLert resource 'status page'
|
||||
+244
-204
@@ -16,11 +16,96 @@ import { RouteRef } from '@backstage/core-plugin-api';
|
||||
// @public (undocumented)
|
||||
export const ACCEPTED = 'ACCEPTED';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Alert {
|
||||
// (undocumented)
|
||||
alertKey: string;
|
||||
// (undocumented)
|
||||
alertSource: AlertSource | null;
|
||||
// (undocumented)
|
||||
assignedTo: User | null;
|
||||
// (undocumented)
|
||||
commentPublishToSubscribers: boolean;
|
||||
// (undocumented)
|
||||
commentText: string;
|
||||
// (undocumented)
|
||||
details: string;
|
||||
// (undocumented)
|
||||
id: number;
|
||||
// (undocumented)
|
||||
images: Image_2[];
|
||||
// (undocumented)
|
||||
links: Link[];
|
||||
// (undocumented)
|
||||
logEntries: LogEntry[];
|
||||
// (undocumented)
|
||||
priority: AlertPriority;
|
||||
// (undocumented)
|
||||
reportTime: string;
|
||||
// (undocumented)
|
||||
resolvedOn: string;
|
||||
// (undocumented)
|
||||
responders: Responder[];
|
||||
// (undocumented)
|
||||
status: AlertStatus;
|
||||
// (undocumented)
|
||||
subscribers: Subscriber[];
|
||||
// (undocumented)
|
||||
summary: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface AlertAction {
|
||||
// (undocumented)
|
||||
extensionId?: string;
|
||||
// (undocumented)
|
||||
history?: AlertActionHistory[];
|
||||
// (undocumented)
|
||||
name: string;
|
||||
// (undocumented)
|
||||
type: string;
|
||||
// (undocumented)
|
||||
webhookId: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface AlertActionHistory {
|
||||
// (undocumented)
|
||||
actor: User;
|
||||
// (undocumented)
|
||||
alertId: number;
|
||||
// (undocumented)
|
||||
id: string;
|
||||
// (undocumented)
|
||||
success: boolean;
|
||||
// (undocumented)
|
||||
webhookId: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlertPriority = 'HIGH' | 'LOW';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface AlertResponder {
|
||||
// (undocumented)
|
||||
disabled: boolean;
|
||||
// (undocumented)
|
||||
group: 'SUGGESTED' | 'USER' | 'ESCALATION_POLICY' | 'ON_CALL_SCHEDULE';
|
||||
// (undocumented)
|
||||
id: number;
|
||||
// (undocumented)
|
||||
name: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface AlertSource {
|
||||
// (undocumented)
|
||||
active?: boolean;
|
||||
// (undocumented)
|
||||
alertCreation?: AlertSourceAlertCreation;
|
||||
// (undocumented)
|
||||
alertPriorityRule?: AlertSourceAlertPriorityRule;
|
||||
// (undocumented)
|
||||
autoResolutionTimeout?: string;
|
||||
// (undocumented)
|
||||
autotaskMetadata?: AlertSourceAutotaskMetadata;
|
||||
@@ -45,10 +130,6 @@ export interface AlertSource {
|
||||
// (undocumented)
|
||||
id: number;
|
||||
// (undocumented)
|
||||
incidentCreation?: AlertSourceIncidentCreation;
|
||||
// (undocumented)
|
||||
incidentPriorityRule?: AlertSourceIncidentPriorityRule;
|
||||
// (undocumented)
|
||||
integrationKey?: string;
|
||||
// (undocumented)
|
||||
integrationType: AlertSourceIntegrationType;
|
||||
@@ -66,6 +147,21 @@ export interface AlertSource {
|
||||
teams: TeamShort[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
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 (undocumented)
|
||||
export type AlertSourceAlertPriorityRule =
|
||||
| 'HIGH'
|
||||
| 'LOW'
|
||||
| 'HIGH_DURING_SUPPORT_HOURS'
|
||||
| 'LOW_DURING_SUPPORT_HOURS';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface AlertSourceAutotaskMetadata {
|
||||
// (undocumented)
|
||||
@@ -109,21 +205,6 @@ export interface AlertSourceHeartbeat {
|
||||
summary: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlertSourceIncidentCreation =
|
||||
| 'ONE_INCIDENT_PER_EMAIL'
|
||||
| 'ONE_INCIDENT_PER_EMAIL_SUBJECT'
|
||||
| 'ONE_PENDING_INCIDENT_ALLOWED'
|
||||
| 'ONE_OPEN_INCIDENT_ALLOWED'
|
||||
| 'OPEN_RESOLVE_ON_EXTRACTION';
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlertSourceIncidentPriorityRule =
|
||||
| 'HIGH'
|
||||
| 'LOW'
|
||||
| 'HIGH_DURING_SUPPORT_HOURS'
|
||||
| 'LOW_DURING_SUPPORT_HOURS';
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlertSourceIntegrationType =
|
||||
| 'NAGIOS'
|
||||
@@ -192,7 +273,7 @@ export interface AlertSourceSupportDay {
|
||||
// @public (undocumented)
|
||||
export interface AlertSourceSupportHours {
|
||||
// (undocumented)
|
||||
autoRaiseIncidents: boolean;
|
||||
autoRaiseAlerts: boolean;
|
||||
// (undocumented)
|
||||
supportDays: {
|
||||
MONDAY: AlertSourceSupportDay;
|
||||
@@ -214,6 +295,12 @@ export type AlertSourceTimeZone =
|
||||
| 'America/Los_Angeles'
|
||||
| 'Asia/Istanbul';
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlertStatus = typeof PENDING | typeof ACCEPTED | typeof RESOLVED;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DEGRADED = 'DEGRADED';
|
||||
|
||||
// @public (undocumented)
|
||||
export const EntityILertCard: () => JSX.Element;
|
||||
|
||||
@@ -255,72 +342,85 @@ export type EventRequest = {
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type GetIncidentsCountOpts = {
|
||||
states?: IncidentStatus[];
|
||||
export type GetAlertsCountOpts = {
|
||||
states?: AlertStatus[];
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type GetIncidentsOpts = {
|
||||
export type GetAlertsOpts = {
|
||||
maxResults?: number;
|
||||
startIndex?: number;
|
||||
states?: IncidentStatus[];
|
||||
states?: AlertStatus[];
|
||||
alertSources?: number[];
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type GetServicesOpts = {
|
||||
maxResults?: number;
|
||||
startIndex?: number;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type GetStatusPagesOpts = {
|
||||
maxResults?: number;
|
||||
startIndex?: number;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ILertApi {
|
||||
// (undocumented)
|
||||
acceptIncident(incident: Incident, userName: string): Promise<Incident>;
|
||||
acceptAlert(alert: Alert, userName: string): Promise<Alert>;
|
||||
// (undocumented)
|
||||
addImmediateMaintenance(
|
||||
alertSourceId: number,
|
||||
minutes: number,
|
||||
): Promise<void>;
|
||||
// (undocumented)
|
||||
assignIncident(
|
||||
incident: Incident,
|
||||
responder: IncidentResponder,
|
||||
): Promise<Incident>;
|
||||
assignAlert(alert: Alert, responder: AlertResponder): Promise<Alert>;
|
||||
// (undocumented)
|
||||
createIncident(eventRequest: EventRequest): Promise<boolean>;
|
||||
createAlert(eventRequest: EventRequest): Promise<boolean>;
|
||||
// (undocumented)
|
||||
disableAlertSource(alertSource: AlertSource): Promise<AlertSource>;
|
||||
// (undocumented)
|
||||
enableAlertSource(alertSource: AlertSource): Promise<AlertSource>;
|
||||
// (undocumented)
|
||||
fetchAlert(id: number): Promise<Alert>;
|
||||
// (undocumented)
|
||||
fetchAlertActions(alert: Alert): Promise<AlertAction[]>;
|
||||
// (undocumented)
|
||||
fetchAlertResponders(alert: Alert): Promise<AlertResponder[]>;
|
||||
// (undocumented)
|
||||
fetchAlerts(opts?: GetAlertsOpts): Promise<Alert[]>;
|
||||
// (undocumented)
|
||||
fetchAlertsCount(opts?: GetAlertsCountOpts): Promise<number>;
|
||||
// (undocumented)
|
||||
fetchAlertSource(idOrIntegrationKey: number | string): Promise<AlertSource>;
|
||||
// (undocumented)
|
||||
fetchAlertSourceOnCalls(alertSource: AlertSource): Promise<OnCall[]>;
|
||||
// (undocumented)
|
||||
fetchAlertSources(): Promise<AlertSource[]>;
|
||||
// (undocumented)
|
||||
fetchIncident(id: number): Promise<Incident>;
|
||||
// (undocumented)
|
||||
fetchIncidentActions(incident: Incident): Promise<IncidentAction[]>;
|
||||
// (undocumented)
|
||||
fetchIncidentResponders(incident: Incident): Promise<IncidentResponder[]>;
|
||||
// (undocumented)
|
||||
fetchIncidents(opts?: GetIncidentsOpts): Promise<Incident[]>;
|
||||
// (undocumented)
|
||||
fetchIncidentsCount(opts?: GetIncidentsCountOpts): Promise<number>;
|
||||
// (undocumented)
|
||||
fetchOnCallSchedules(): Promise<Schedule[]>;
|
||||
// (undocumented)
|
||||
fetchUptimeMonitor(id: number): Promise<UptimeMonitor>;
|
||||
fetchServices(opts?: GetServicesOpts): Promise<Service[]>;
|
||||
// (undocumented)
|
||||
fetchUptimeMonitors(): Promise<UptimeMonitor[]>;
|
||||
fetchStatusPages(opts?: GetStatusPagesOpts): Promise<StatusPage[]>;
|
||||
// (undocumented)
|
||||
fetchUsers(): Promise<User[]>;
|
||||
// (undocumented)
|
||||
getAlertDetailsURL(alert: Alert): string;
|
||||
// (undocumented)
|
||||
getAlertSourceDetailsURL(alertSource: AlertSource | null): string;
|
||||
// (undocumented)
|
||||
getEscalationPolicyDetailsURL(escalationPolicy: EscalationPolicy): string;
|
||||
// (undocumented)
|
||||
getIncidentDetailsURL(incident: Incident): string;
|
||||
// (undocumented)
|
||||
getScheduleDetailsURL(schedule: Schedule): string;
|
||||
// (undocumented)
|
||||
getUptimeMonitorDetailsURL(uptimeMonitor: UptimeMonitor): string;
|
||||
getServiceDetailsURL(service: Service): string;
|
||||
// (undocumented)
|
||||
getStatusPageDetailsURL(statusPage: StatusPage): string;
|
||||
// (undocumented)
|
||||
getStatusPageURL(statusPage: StatusPage): string;
|
||||
// (undocumented)
|
||||
getUserInitials(user: User | null): string;
|
||||
// (undocumented)
|
||||
@@ -333,16 +433,9 @@ export interface ILertApi {
|
||||
end: string,
|
||||
): Promise<Schedule>;
|
||||
// (undocumented)
|
||||
pauseUptimeMonitor(uptimeMonitor: UptimeMonitor): Promise<UptimeMonitor>;
|
||||
resolveAlert(alert: Alert, userName: string): Promise<Alert>;
|
||||
// (undocumented)
|
||||
resolveIncident(incident: Incident, userName: string): Promise<Incident>;
|
||||
// (undocumented)
|
||||
resumeUptimeMonitor(uptimeMonitor: UptimeMonitor): Promise<UptimeMonitor>;
|
||||
// (undocumented)
|
||||
triggerIncidentAction(
|
||||
incident: Incident,
|
||||
action: IncidentAction,
|
||||
): Promise<void>;
|
||||
triggerAlertAction(alert: Alert, action: AlertAction): Promise<void>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -359,45 +452,42 @@ export class ILertClient implements ILertApi {
|
||||
proxyPath: string;
|
||||
});
|
||||
// (undocumented)
|
||||
acceptIncident(incident: Incident, userName: string): Promise<Incident>;
|
||||
acceptAlert(alert: Alert, userName: string): Promise<Alert>;
|
||||
// (undocumented)
|
||||
addImmediateMaintenance(
|
||||
alertSourceId: number,
|
||||
minutes: number,
|
||||
): Promise<void>;
|
||||
// (undocumented)
|
||||
assignIncident(
|
||||
incident: Incident,
|
||||
responder: IncidentResponder,
|
||||
): Promise<Incident>;
|
||||
assignAlert(alert: Alert, responder: AlertResponder): Promise<Alert>;
|
||||
// (undocumented)
|
||||
createIncident(eventRequest: EventRequest): Promise<boolean>;
|
||||
createAlert(eventRequest: EventRequest): Promise<boolean>;
|
||||
// (undocumented)
|
||||
disableAlertSource(alertSource: AlertSource): Promise<AlertSource>;
|
||||
// (undocumented)
|
||||
enableAlertSource(alertSource: AlertSource): Promise<AlertSource>;
|
||||
// (undocumented)
|
||||
fetchAlert(id: number): Promise<Alert>;
|
||||
// (undocumented)
|
||||
fetchAlertActions(alert: Alert): Promise<AlertAction[]>;
|
||||
// (undocumented)
|
||||
fetchAlertResponders(alert: Alert): Promise<AlertResponder[]>;
|
||||
// (undocumented)
|
||||
fetchAlerts(opts?: GetAlertsOpts): Promise<Alert[]>;
|
||||
// (undocumented)
|
||||
fetchAlertsCount(opts?: GetAlertsCountOpts): Promise<number>;
|
||||
// (undocumented)
|
||||
fetchAlertSource(idOrIntegrationKey: number | string): Promise<AlertSource>;
|
||||
// (undocumented)
|
||||
fetchAlertSourceOnCalls(alertSource: AlertSource): Promise<OnCall[]>;
|
||||
// (undocumented)
|
||||
fetchAlertSources(): Promise<AlertSource[]>;
|
||||
// (undocumented)
|
||||
fetchIncident(id: number): Promise<Incident>;
|
||||
// (undocumented)
|
||||
fetchIncidentActions(incident: Incident): Promise<IncidentAction[]>;
|
||||
// (undocumented)
|
||||
fetchIncidentResponders(incident: Incident): Promise<IncidentResponder[]>;
|
||||
// (undocumented)
|
||||
fetchIncidents(opts?: GetIncidentsOpts): Promise<Incident[]>;
|
||||
// (undocumented)
|
||||
fetchIncidentsCount(opts?: GetIncidentsCountOpts): Promise<number>;
|
||||
// (undocumented)
|
||||
fetchOnCallSchedules(): Promise<Schedule[]>;
|
||||
// (undocumented)
|
||||
fetchUptimeMonitor(id: number): Promise<UptimeMonitor>;
|
||||
fetchServices(opts?: GetServicesOpts): Promise<Service[]>;
|
||||
// (undocumented)
|
||||
fetchUptimeMonitors(): Promise<UptimeMonitor[]>;
|
||||
fetchStatusPages(opts?: GetStatusPagesOpts): Promise<StatusPage[]>;
|
||||
// (undocumented)
|
||||
fetchUsers(): Promise<User[]>;
|
||||
// (undocumented)
|
||||
@@ -406,15 +496,19 @@ export class ILertClient implements ILertApi {
|
||||
discoveryApi: DiscoveryApi,
|
||||
): ILertClient;
|
||||
// (undocumented)
|
||||
getAlertDetailsURL(alert: Alert): string;
|
||||
// (undocumented)
|
||||
getAlertSourceDetailsURL(alertSource: AlertSource | null): string;
|
||||
// (undocumented)
|
||||
getEscalationPolicyDetailsURL(escalationPolicy: EscalationPolicy): string;
|
||||
// (undocumented)
|
||||
getIncidentDetailsURL(incident: Incident): string;
|
||||
// (undocumented)
|
||||
getScheduleDetailsURL(schedule: Schedule): string;
|
||||
// (undocumented)
|
||||
getUptimeMonitorDetailsURL(uptimeMonitor: UptimeMonitor): string;
|
||||
getServiceDetailsURL(service: Service): string;
|
||||
// (undocumented)
|
||||
getStatusPageDetailsURL(statusPage: StatusPage): string;
|
||||
// (undocumented)
|
||||
getStatusPageURL(statusPage: StatusPage): string;
|
||||
// (undocumented)
|
||||
getUserInitials(user: User | null): string;
|
||||
// (undocumented)
|
||||
@@ -427,16 +521,9 @@ export class ILertClient implements ILertApi {
|
||||
end: string,
|
||||
): Promise<Schedule>;
|
||||
// (undocumented)
|
||||
pauseUptimeMonitor(uptimeMonitor: UptimeMonitor): Promise<UptimeMonitor>;
|
||||
resolveAlert(alert: Alert, userName: string): Promise<Alert>;
|
||||
// (undocumented)
|
||||
resolveIncident(incident: Incident, userName: string): Promise<Incident>;
|
||||
// (undocumented)
|
||||
resumeUptimeMonitor(uptimeMonitor: UptimeMonitor): Promise<UptimeMonitor>;
|
||||
// (undocumented)
|
||||
triggerIncidentAction(
|
||||
incident: Incident,
|
||||
action: IncidentAction,
|
||||
): Promise<void>;
|
||||
triggerAlertAction(alert: Alert, action: AlertAction): Promise<void>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -470,88 +557,6 @@ interface Image_2 {
|
||||
}
|
||||
export { Image_2 as Image };
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Incident {
|
||||
// (undocumented)
|
||||
alertSource: AlertSource | null;
|
||||
// (undocumented)
|
||||
assignedTo: User | null;
|
||||
// (undocumented)
|
||||
commentPublishToSubscribers: boolean;
|
||||
// (undocumented)
|
||||
commentText: string;
|
||||
// (undocumented)
|
||||
details: string;
|
||||
// (undocumented)
|
||||
id: number;
|
||||
// (undocumented)
|
||||
images: Image_2[];
|
||||
// (undocumented)
|
||||
incidentKey: string;
|
||||
// (undocumented)
|
||||
links: Link[];
|
||||
// (undocumented)
|
||||
logEntries: LogEntry[];
|
||||
// (undocumented)
|
||||
priority: IncidentPriority;
|
||||
// (undocumented)
|
||||
reportTime: string;
|
||||
// (undocumented)
|
||||
resolvedOn: string;
|
||||
// (undocumented)
|
||||
status: IncidentStatus;
|
||||
// (undocumented)
|
||||
subscribers: Subscriber[];
|
||||
// (undocumented)
|
||||
summary: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface IncidentAction {
|
||||
// (undocumented)
|
||||
extensionId?: string;
|
||||
// (undocumented)
|
||||
history?: IncidentActionHistory[];
|
||||
// (undocumented)
|
||||
name: string;
|
||||
// (undocumented)
|
||||
type: string;
|
||||
// (undocumented)
|
||||
webhookId: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface IncidentActionHistory {
|
||||
// (undocumented)
|
||||
actor: User;
|
||||
// (undocumented)
|
||||
id: string;
|
||||
// (undocumented)
|
||||
incidentId: number;
|
||||
// (undocumented)
|
||||
success: boolean;
|
||||
// (undocumented)
|
||||
webhookId: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type IncidentPriority = 'HIGH' | 'LOW';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface IncidentResponder {
|
||||
// (undocumented)
|
||||
disabled: boolean;
|
||||
// (undocumented)
|
||||
group: 'SUGGESTED' | 'USER' | 'ESCALATION_POLICY' | 'ON_CALL_SCHEDULE';
|
||||
// (undocumented)
|
||||
id: number;
|
||||
// (undocumented)
|
||||
name: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type IncidentStatus = typeof PENDING | typeof ACCEPTED | typeof RESOLVED;
|
||||
|
||||
// @public (undocumented)
|
||||
const isPluginApplicableToEntity: (entity: Entity) => boolean;
|
||||
export { isPluginApplicableToEntity as isILertAvailable };
|
||||
@@ -570,6 +575,8 @@ export interface Link {
|
||||
|
||||
// @public (undocumented)
|
||||
export interface LogEntry {
|
||||
// (undocumented)
|
||||
alertId?: number;
|
||||
// (undocumented)
|
||||
filterTypes?: string[];
|
||||
// (undocumented)
|
||||
@@ -579,8 +586,6 @@ export interface LogEntry {
|
||||
// (undocumented)
|
||||
id: number;
|
||||
// (undocumented)
|
||||
incidentId?: number;
|
||||
// (undocumented)
|
||||
logEntryType: string;
|
||||
// (undocumented)
|
||||
text: string;
|
||||
@@ -588,6 +593,9 @@ export interface LogEntry {
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const MAJOR_OUTAGE = 'MAJOR_OUTAGE';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface OnCall {
|
||||
// (undocumented)
|
||||
@@ -604,6 +612,12 @@ export interface OnCall {
|
||||
user: User;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const OPERATIONAL = 'OPERATIONAL';
|
||||
|
||||
// @public (undocumented)
|
||||
export const PARTIAL_OUTAGE = 'PARTIAL_OUTAGE';
|
||||
|
||||
// @public (undocumented)
|
||||
export const PENDING = 'PENDING';
|
||||
|
||||
@@ -615,9 +629,25 @@ export interface Phone {
|
||||
regionCode: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const PRIVATE = 'PRIVATE';
|
||||
|
||||
// @public (undocumented)
|
||||
export const PUBLIC = 'PUBLIC';
|
||||
|
||||
// @public (undocumented)
|
||||
export const RESOLVED = 'RESOLVED';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Responder {
|
||||
// (undocumented)
|
||||
acceptedAt?: string;
|
||||
// (undocumented)
|
||||
status: string;
|
||||
// (undocumented)
|
||||
user: User;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const Router: () => JSX.Element;
|
||||
|
||||
@@ -643,6 +673,26 @@ export interface Schedule {
|
||||
timezone: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Service {
|
||||
// (undocumented)
|
||||
id: number;
|
||||
// (undocumented)
|
||||
name: string;
|
||||
// (undocumented)
|
||||
status: ServiceStatus;
|
||||
// (undocumented)
|
||||
uptime: Uptime;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type ServiceStatus =
|
||||
| typeof OPERATIONAL
|
||||
| typeof UNDER_MAINTENANCE
|
||||
| typeof DEGRADED
|
||||
| typeof PARTIAL_OUTAGE
|
||||
| typeof MAJOR_OUTAGE;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Shift {
|
||||
// (undocumented)
|
||||
@@ -653,6 +703,25 @@ export interface Shift {
|
||||
user: User;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface StatusPage {
|
||||
// (undocumented)
|
||||
domain: string;
|
||||
// (undocumented)
|
||||
id: number;
|
||||
// (undocumented)
|
||||
name: string;
|
||||
// (undocumented)
|
||||
status: ServiceStatus;
|
||||
// (undocumented)
|
||||
subdomain: string;
|
||||
// (undocumented)
|
||||
visibility: StatusPageVisibility;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type StatusPageVisibility = typeof PRIVATE | typeof PUBLIC;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Subscriber {
|
||||
// (undocumented)
|
||||
@@ -689,47 +758,18 @@ export interface TeamShort {
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface UptimeMonitor {
|
||||
export const UNDER_MAINTENANCE = 'UNDER_MAINTENANCE';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface Uptime {
|
||||
// (undocumented)
|
||||
checkParams: UptimeMonitorCheckParams;
|
||||
// (undocumented)
|
||||
checkType: 'http' | 'tcp' | 'udp' | 'ping';
|
||||
// (undocumented)
|
||||
createIncidentAfterFailedChecks: number;
|
||||
// (undocumented)
|
||||
embedUrl: string;
|
||||
// (undocumented)
|
||||
escalationPolicy: EscalationPolicy;
|
||||
// (undocumented)
|
||||
id: number;
|
||||
// (undocumented)
|
||||
intervalSec: number;
|
||||
// (undocumented)
|
||||
lastStatusChange: string;
|
||||
// (undocumented)
|
||||
name: string;
|
||||
// (undocumented)
|
||||
paused: boolean;
|
||||
// (undocumented)
|
||||
region: 'EU' | 'US';
|
||||
// (undocumented)
|
||||
shareUrl: string;
|
||||
// (undocumented)
|
||||
status: string;
|
||||
// (undocumented)
|
||||
teams: TeamShort[];
|
||||
// (undocumented)
|
||||
timeoutMs: number;
|
||||
uptimePercentage: UptimePercentage;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface UptimeMonitorCheckParams {
|
||||
export interface UptimePercentage {
|
||||
// (undocumented)
|
||||
host?: string;
|
||||
// (undocumented)
|
||||
port?: number;
|
||||
// (undocumented)
|
||||
url?: string;
|
||||
p90: number;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
+121
-142
@@ -13,30 +13,33 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { AuthenticationError, ResponseError } from '@backstage/errors';
|
||||
import {
|
||||
AlertSource,
|
||||
EscalationPolicy,
|
||||
Incident,
|
||||
IncidentAction,
|
||||
IncidentResponder,
|
||||
OnCall,
|
||||
Schedule,
|
||||
UptimeMonitor,
|
||||
User,
|
||||
} from '../types';
|
||||
import {
|
||||
ILertApi,
|
||||
GetIncidentsOpts,
|
||||
GetIncidentsCountOpts,
|
||||
EventRequest,
|
||||
} from './types';
|
||||
import { DateTime as dt } from 'luxon';
|
||||
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,
|
||||
OnCall,
|
||||
Schedule,
|
||||
Service,
|
||||
StatusPage,
|
||||
User,
|
||||
} from '../types';
|
||||
import {
|
||||
EventRequest,
|
||||
GetAlertsCountOpts,
|
||||
GetAlertsOpts,
|
||||
GetServicesOpts,
|
||||
GetStatusPagesOpts,
|
||||
ILertApi,
|
||||
} from './types';
|
||||
|
||||
/** @public */
|
||||
export const ilertApiRef = createApiRef<ILertApi>({
|
||||
@@ -102,7 +105,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 +129,11 @@ 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 +144,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 +242,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 +262,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,64 +288,7 @@ export class ILertClient implements ILertApi {
|
||||
}),
|
||||
};
|
||||
|
||||
const response = await this.fetch('/api/v1/events', init);
|
||||
return response;
|
||||
}
|
||||
|
||||
async fetchUptimeMonitors(): Promise<UptimeMonitor[]> {
|
||||
const init = {
|
||||
headers: JSON_HEADERS,
|
||||
};
|
||||
|
||||
const response = await this.fetch('/api/v1/uptime-monitors', init);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async fetchUptimeMonitor(id: number): Promise<UptimeMonitor> {
|
||||
const init = {
|
||||
headers: JSON_HEADERS,
|
||||
};
|
||||
|
||||
const response: UptimeMonitor = await this.fetch(
|
||||
`/api/v1/uptime-monitors/${encodeURIComponent(id)}`,
|
||||
init,
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async pauseUptimeMonitor(
|
||||
uptimeMonitor: UptimeMonitor,
|
||||
): Promise<UptimeMonitor> {
|
||||
const init = {
|
||||
method: 'PUT',
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({ ...uptimeMonitor, paused: true }),
|
||||
};
|
||||
|
||||
const response = await this.fetch(
|
||||
`/api/v1/uptime-monitors/${encodeURIComponent(uptimeMonitor.id)}`,
|
||||
init,
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async resumeUptimeMonitor(
|
||||
uptimeMonitor: UptimeMonitor,
|
||||
): Promise<UptimeMonitor> {
|
||||
const init = {
|
||||
method: 'PUT',
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({ ...uptimeMonitor, paused: false }),
|
||||
};
|
||||
|
||||
const response = await this.fetch(
|
||||
`/api/v1/uptime-monitors/${encodeURIComponent(uptimeMonitor.id)}`,
|
||||
init,
|
||||
);
|
||||
|
||||
const response = await this.fetch('/api/events', init);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -371,7 +297,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 +310,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 +323,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 +340,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 +355,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 +379,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 +389,10 @@ export class ILertClient implements ILertApi {
|
||||
headers: JSON_HEADERS,
|
||||
};
|
||||
|
||||
const response = await this.fetch('/api/v1/schedules', init);
|
||||
const response = await this.fetch(
|
||||
'/api/schedules?include=currentShift&include=nextShift',
|
||||
init,
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -473,7 +402,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 +420,57 @@ 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,
|
||||
)}`;
|
||||
async fetchServices(opts?: GetServicesOpts): Promise<Service[]> {
|
||||
const init = {
|
||||
headers: JSON_HEADERS,
|
||||
};
|
||||
const query = new URLSearchParams();
|
||||
if (opts?.maxResults !== undefined) {
|
||||
query.append('max-results', String(opts.maxResults));
|
||||
}
|
||||
if (opts?.startIndex !== undefined) {
|
||||
query.append('start-index', String(opts.startIndex));
|
||||
}
|
||||
|
||||
query.append('include', 'uptime');
|
||||
|
||||
const response = await this.fetch(
|
||||
`/api/services?${query.toString()}`,
|
||||
init,
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
async fetchStatusPages(opts?: GetStatusPagesOpts): Promise<StatusPage[]> {
|
||||
const init = {
|
||||
headers: JSON_HEADERS,
|
||||
};
|
||||
const query = new URLSearchParams();
|
||||
if (opts?.maxResults !== undefined) {
|
||||
query.append('max-results', String(opts.maxResults));
|
||||
}
|
||||
if (opts?.startIndex !== undefined) {
|
||||
query.append('start-index', String(opts.startIndex));
|
||||
}
|
||||
|
||||
query.append('include', 'subscribed');
|
||||
|
||||
const response = await this.fetch(
|
||||
`/api/status-pages?${query.toString()}`,
|
||||
init,
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
getAlertDetailsURL(alert: Alert): string {
|
||||
return `${this.baseUrl}/alert/view.jsf?id=${encodeURIComponent(alert.id)}`;
|
||||
}
|
||||
|
||||
getAlertSourceDetailsURL(alertSource: AlertSource | null): string {
|
||||
@@ -519,18 +488,28 @@ export class ILertClient implements ILertApi {
|
||||
)}`;
|
||||
}
|
||||
|
||||
getUptimeMonitorDetailsURL(uptimeMonitor: UptimeMonitor): string {
|
||||
return `${this.baseUrl}/uptime/view.jsf?id=${encodeURIComponent(
|
||||
uptimeMonitor.id,
|
||||
)}`;
|
||||
}
|
||||
|
||||
getScheduleDetailsURL(schedule: Schedule): string {
|
||||
return `${this.baseUrl}/schedule/view.jsf?id=${encodeURIComponent(
|
||||
schedule.id,
|
||||
)}`;
|
||||
}
|
||||
|
||||
getServiceDetailsURL(service: Service): string {
|
||||
return `${this.baseUrl}/service/view.jsf?id=${encodeURIComponent(
|
||||
service.id,
|
||||
)}`;
|
||||
}
|
||||
|
||||
getStatusPageDetailsURL(statusPage: StatusPage): string {
|
||||
return `${this.baseUrl}/status-page/view.jsf?id=${encodeURIComponent(
|
||||
statusPage.id,
|
||||
)}`;
|
||||
}
|
||||
|
||||
getStatusPageURL(statusPage: StatusPage): string {
|
||||
return statusPage.domain ? statusPage.domain : statusPage.subdomain;
|
||||
}
|
||||
|
||||
getUserPhoneNumber(user: User | null) {
|
||||
return user?.mobile?.number || user?.landline?.number || '';
|
||||
}
|
||||
@@ -542,7 +521,7 @@ export class ILertClient implements ILertApi {
|
||||
if (!user.firstName && !user.lastName) {
|
||||
return user.username;
|
||||
}
|
||||
return `${user.firstName} ${user.lastName} (${user.username})`;
|
||||
return `${user.firstName} ${user.lastName}`;
|
||||
}
|
||||
|
||||
private async apiUrl() {
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { ILertClient, ilertApiRef } from './client';
|
||||
export { ilertApiRef, ILertClient } from './client';
|
||||
export type {
|
||||
EventRequest as EventRequest,
|
||||
GetAlertsCountOpts,
|
||||
GetAlertsOpts,
|
||||
GetServicesOpts,
|
||||
GetStatusPagesOpts,
|
||||
ILertApi,
|
||||
EventRequest,
|
||||
GetIncidentsCountOpts,
|
||||
GetIncidentsOpts,
|
||||
TableState,
|
||||
} from './types';
|
||||
|
||||
@@ -15,16 +15,17 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
Alert,
|
||||
AlertAction,
|
||||
AlertResponder,
|
||||
AlertSource,
|
||||
Incident,
|
||||
User,
|
||||
IncidentStatus,
|
||||
UptimeMonitor,
|
||||
AlertStatus,
|
||||
EscalationPolicy,
|
||||
Schedule,
|
||||
IncidentResponder,
|
||||
IncidentAction,
|
||||
OnCall,
|
||||
Schedule,
|
||||
Service,
|
||||
StatusPage,
|
||||
User,
|
||||
} from '../types';
|
||||
|
||||
/** @public */
|
||||
@@ -34,16 +35,28 @@ 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 */
|
||||
export type GetServicesOpts = {
|
||||
maxResults?: number;
|
||||
startIndex?: number;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type GetStatusPagesOpts = {
|
||||
maxResults?: number;
|
||||
startIndex?: number;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
@@ -57,27 +70,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>;
|
||||
|
||||
fetchUptimeMonitors(): Promise<UptimeMonitor[]>;
|
||||
pauseUptimeMonitor(uptimeMonitor: UptimeMonitor): Promise<UptimeMonitor>;
|
||||
resumeUptimeMonitor(uptimeMonitor: UptimeMonitor): Promise<UptimeMonitor>;
|
||||
fetchUptimeMonitor(id: number): Promise<UptimeMonitor>;
|
||||
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>;
|
||||
|
||||
fetchAlertSources(): Promise<AlertSource[]>;
|
||||
fetchAlertSource(idOrIntegrationKey: number | string): Promise<AlertSource>;
|
||||
@@ -100,11 +102,17 @@ export interface ILertApi {
|
||||
end: string,
|
||||
): Promise<Schedule>;
|
||||
|
||||
getIncidentDetailsURL(incident: Incident): string;
|
||||
fetchServices(opts?: GetServicesOpts): Promise<Service[]>;
|
||||
|
||||
fetchStatusPages(opts?: GetStatusPagesOpts): Promise<StatusPage[]>;
|
||||
|
||||
getAlertDetailsURL(alert: Alert): string;
|
||||
getAlertSourceDetailsURL(alertSource: AlertSource | null): string;
|
||||
getEscalationPolicyDetailsURL(escalationPolicy: EscalationPolicy): string;
|
||||
getUptimeMonitorDetailsURL(uptimeMonitor: UptimeMonitor): string;
|
||||
getScheduleDetailsURL(schedule: Schedule): string;
|
||||
getServiceDetailsURL(service: Service): string;
|
||||
getStatusPageDetailsURL(statusPage: StatusPage): string;
|
||||
getStatusPageURL(statusPage: StatusPage): string;
|
||||
getUserPhoneNumber(user: User | null): string;
|
||||
getUserInitials(user: User | null): string;
|
||||
}
|
||||
|
||||
+37
-39
@@ -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}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
+32
-35
@@ -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 => {
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { ilertApiRef } from '../../api';
|
||||
import { Alert } from '../../types';
|
||||
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export const AlertLink = ({ alert }: { alert: Alert | null }) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
|
||||
if (!alert) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Link to={ilertApi.getAlertDetailsURL(alert)}>#{alert.id}</Link>;
|
||||
};
|
||||
+26
-26
@@ -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>
|
||||
+1
-1
@@ -13,4 +13,4 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './UptimeMonitorActionsMenu';
|
||||
export * from './AlertActionsMenu';
|
||||
+26
-27
@@ -13,42 +13,41 @@
|
||||
* 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 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);
|
||||
};
|
||||
|
||||
if (error) {
|
||||
if (error instanceof AuthenticationError) {
|
||||
if (error.name === 'AuthenticationError') {
|
||||
return (
|
||||
<Content>
|
||||
<MissingAuthorizationHeaderError />
|
||||
@@ -65,32 +64,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}
|
||||
+57
-75
@@ -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;
|
||||
@@ -86,31 +86,29 @@ export const IncidentsTable = ({
|
||||
maxWidth: '30%',
|
||||
};
|
||||
|
||||
const idColumn: TableColumn = {
|
||||
const idColumn: TableColumn<Alert> = {
|
||||
title: 'ID',
|
||||
field: 'id',
|
||||
highlight: true,
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => <IncidentLink incident={rowData as Incident} />,
|
||||
render: rowData => <AlertLink alert={rowData} />,
|
||||
};
|
||||
const summaryColumn: TableColumn = {
|
||||
const summaryColumn: TableColumn<Alert> = {
|
||||
title: 'Summary',
|
||||
field: 'summary',
|
||||
cellStyle: !compact ? xlColumnStyle : undefined,
|
||||
headerStyle: !compact ? xlColumnStyle : undefined,
|
||||
render: rowData => <Typography>{(rowData as Incident).summary}</Typography>,
|
||||
render: rowData => <Typography>{rowData.summary}</Typography>,
|
||||
};
|
||||
const sourceColumn: TableColumn = {
|
||||
const sourceColumn: TableColumn<Alert> = {
|
||||
title: 'Source',
|
||||
field: 'source',
|
||||
cellStyle: mdColumnStyle,
|
||||
headerStyle: mdColumnStyle,
|
||||
render: rowData => (
|
||||
<AlertSourceLink alertSource={(rowData as Incident).alertSource} />
|
||||
),
|
||||
render: rowData => <AlertSourceLink alertSource={rowData.alertSource} />,
|
||||
};
|
||||
const durationColumn: TableColumn = {
|
||||
const durationColumn: TableColumn<Alert> = {
|
||||
title: 'Duration',
|
||||
field: 'reportTime',
|
||||
type: 'datetime',
|
||||
@@ -118,20 +116,17 @@ export const IncidentsTable = ({
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => (
|
||||
<Typography noWrap>
|
||||
{(rowData as Incident).status !== 'RESOLVED'
|
||||
{rowData.status !== 'RESOLVED'
|
||||
? humanizeDuration(
|
||||
Interval.fromDateTimes(
|
||||
dt.fromISO((rowData as Incident).reportTime),
|
||||
dt.now(),
|
||||
)
|
||||
Interval.fromDateTimes(dt.fromISO(rowData.reportTime), dt.now())
|
||||
.toDuration()
|
||||
.valueOf(),
|
||||
{ units: ['h', 'm', 's'], largest: 2, round: true },
|
||||
)
|
||||
: humanizeDuration(
|
||||
Interval.fromDateTimes(
|
||||
dt.fromISO((rowData as Incident).reportTime),
|
||||
dt.fromISO((rowData as Incident).resolvedOn),
|
||||
dt.fromISO(rowData.reportTime),
|
||||
dt.fromISO(rowData.resolvedOn),
|
||||
)
|
||||
.toDuration()
|
||||
.valueOf(),
|
||||
@@ -140,54 +135,59 @@ export const IncidentsTable = ({
|
||||
</Typography>
|
||||
),
|
||||
};
|
||||
const assignedToColumn: TableColumn = {
|
||||
title: 'Assigned to',
|
||||
field: 'assignedTo',
|
||||
const respondersColumn: TableColumn<Alert> = {
|
||||
title: 'Responders',
|
||||
field: 'responders',
|
||||
cellStyle: !compact ? mdColumnStyle : lgColumnStyle,
|
||||
headerStyle: !compact ? mdColumnStyle : lgColumnStyle,
|
||||
render: rowData => (
|
||||
<Typography noWrap>
|
||||
{ilertApi.getUserInitials((rowData as Incident).assignedTo)}
|
||||
<Typography>
|
||||
{rowData.responders.map((value, i, arr) => {
|
||||
return (
|
||||
ilertApi.getUserInitials(value.user) +
|
||||
(arr.length - 1 !== i ? ', ' : '')
|
||||
);
|
||||
})}
|
||||
</Typography>
|
||||
),
|
||||
};
|
||||
const priorityColumn: TableColumn = {
|
||||
const priorityColumn: TableColumn<Alert> = {
|
||||
title: 'Priority',
|
||||
field: 'priority',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => (
|
||||
<Typography noWrap>
|
||||
{(rowData as Incident).priority === 'HIGH' ? 'High' : 'Low'}
|
||||
{rowData.priority === 'HIGH' ? 'High' : 'Low'}
|
||||
</Typography>
|
||||
),
|
||||
};
|
||||
const statusColumn: TableColumn = {
|
||||
const statusColumn: TableColumn<Alert> = {
|
||||
title: 'Status',
|
||||
field: 'status',
|
||||
cellStyle: xsColumnStyle,
|
||||
headerStyle: xsColumnStyle,
|
||||
render: rowData => <StatusChip incident={rowData as Incident} />,
|
||||
render: rowData => <StatusChip alert={rowData} />,
|
||||
};
|
||||
const actionsColumn: TableColumn = {
|
||||
const actionsColumn: TableColumn<Alert> = {
|
||||
title: '',
|
||||
field: '',
|
||||
cellStyle: xsColumnStyle,
|
||||
headerStyle: xsColumnStyle,
|
||||
render: rowData => (
|
||||
<IncidentActionsMenu
|
||||
incident={rowData as Incident}
|
||||
onIncidentChanged={onIncidentChanged}
|
||||
<AlertActionsMenu
|
||||
alert={rowData}
|
||||
onAlertChanged={onAlertChanged}
|
||||
setIsLoading={setIsLoading}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
const columns: TableColumn[] = compact
|
||||
const columns: TableColumn<Alert>[] = compact
|
||||
? [
|
||||
summaryColumn,
|
||||
durationColumn,
|
||||
assignedToColumn,
|
||||
respondersColumn,
|
||||
statusColumn,
|
||||
actionsColumn,
|
||||
]
|
||||
@@ -196,31 +196,14 @@ export const IncidentsTable = ({
|
||||
summaryColumn,
|
||||
sourceColumn,
|
||||
durationColumn,
|
||||
assignedToColumn,
|
||||
respondersColumn,
|
||||
priorityColumn,
|
||||
statusColumn,
|
||||
actionsColumn,
|
||||
];
|
||||
let tableStyle: React.CSSProperties = {};
|
||||
if (compact) {
|
||||
tableStyle = {
|
||||
width: '100%',
|
||||
maxWidth: '100%',
|
||||
minWidth: '0',
|
||||
height: 'calc(100% - 10px)',
|
||||
boxShadow: 'none !important',
|
||||
borderRadius: 'none !important',
|
||||
};
|
||||
} else {
|
||||
tableStyle = {
|
||||
width: '100%',
|
||||
maxWidth: '100%',
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Table
|
||||
style={tableStyle}
|
||||
options={{
|
||||
sorting: false,
|
||||
search: !compact,
|
||||
@@ -236,28 +219,27 @@ 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">
|
||||
INCIDENTS
|
||||
ALERTS
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
page={tableState.page}
|
||||
totalCount={incidentsCount}
|
||||
totalCount={alertsCount}
|
||||
onPageChange={onChangePage}
|
||||
onRowsPerPageChange={onChangeRowsPerPage}
|
||||
// localization={{ header: { actions: undefined } }}
|
||||
columns={columns}
|
||||
data={incidents}
|
||||
data={alerts}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
+11
-6
@@ -13,10 +13,9 @@
|
||||
* 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';
|
||||
|
||||
const ResolvedChip = withStyles({
|
||||
root: {
|
||||
@@ -41,10 +40,16 @@ const PendingChip = withStyles({
|
||||
},
|
||||
})(Chip);
|
||||
|
||||
export const StatusChip = ({ incident }: { incident: Incident }) => {
|
||||
const label = `${incidentStatusLabels[incident.status]}`;
|
||||
export const alertStatusLabels = {
|
||||
[RESOLVED]: 'Resolved',
|
||||
[ACCEPTED]: 'Accepted',
|
||||
[PENDING]: 'Pending',
|
||||
} as Record<string, string>;
|
||||
|
||||
switch (incident.status) {
|
||||
export const StatusChip = ({ alert }: { alert: Alert }) => {
|
||||
const label = `${alertStatusLabels[alert.status]}`;
|
||||
|
||||
switch (alert.status) {
|
||||
case RESOLVED:
|
||||
return <ResolvedChip label={label} size="small" />;
|
||||
case ACCEPTED:
|
||||
+17
-17
@@ -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 './StatusChip';
|
||||
|
||||
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>
|
||||
+2
-2
@@ -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 './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) =>
|
||||
@@ -55,23 +55,21 @@ const useStyles = makeStyles({
|
||||
export const ILertCard = () => {
|
||||
const classes = useStyles();
|
||||
const { integrationKey, name } = useILertEntity();
|
||||
const [{ alertSource }, { setAlertSource, refetchAlertSource }] =
|
||||
useAlertSource(integrationKey);
|
||||
const [
|
||||
{ alertSource, uptimeMonitor },
|
||||
{ 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,9 +95,8 @@ export const ILertCard = () => {
|
||||
<ILertCardActionsHeader
|
||||
alertSource={alertSource}
|
||||
setAlertSource={setAlertSource}
|
||||
setIsNewIncidentModalOpened={setIsNewIncidentModalOpened}
|
||||
setIsNewAlertModalOpened={setIsNewAlertModalOpened}
|
||||
setIsMaintenanceModalOpened={setIsMaintenanceModalOpened}
|
||||
uptimeMonitor={uptimeMonitor}
|
||||
/>
|
||||
}
|
||||
action={<ILertCardHeaderStatus alertSource={alertSource} />}
|
||||
@@ -107,13 +104,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 +119,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,49 +13,46 @@
|
||||
* 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';
|
||||
import { AlertSource } from '../../types';
|
||||
|
||||
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;
|
||||
}) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const alertApi = useApi(alertApiRef);
|
||||
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 +105,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:
|
||||
@@ -140,25 +137,14 @@ export const ILertCardActionsHeader = ({
|
||||
disabled: !alertSource || isLoading,
|
||||
};
|
||||
|
||||
const uptimeMonitorReportLink: IconLinkVerticalProps = {
|
||||
label: 'Uptime Report',
|
||||
href: uptimeMonitor ? uptimeMonitor.shareUrl : '',
|
||||
icon: <TimelineIcon />,
|
||||
disabled: !alertSource || !uptimeMonitor || isLoading,
|
||||
};
|
||||
|
||||
const links: IconLinkVerticalProps[] = [
|
||||
alertSourceLink,
|
||||
createIncidentLink,
|
||||
createAlertLink,
|
||||
alertSource && alertSource.active
|
||||
? disableAlertSourceLink
|
||||
: enableAlertSourceLink,
|
||||
];
|
||||
|
||||
if (alertSource && alertSource.integrationType === 'MONITOR') {
|
||||
links.push(uptimeMonitorReportLink);
|
||||
}
|
||||
|
||||
if (alertSource && alertSource.status !== 'IN_MAINTENANCE') {
|
||||
links.push(maintenanceAlertSourceLink);
|
||||
}
|
||||
@@ -178,7 +164,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,34 +13,38 @@
|
||||
* 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 { ServicesPage } from '../ServicesPage';
|
||||
import { StatusPagesPage } from '../StatusPagePage';
|
||||
|
||||
/** @public */
|
||||
export const ILertPage = () => {
|
||||
const [selectedTab, setSelectedTab] = React.useState<number>(0);
|
||||
const tabs = [
|
||||
{ label: 'Who is on call?' },
|
||||
{ label: 'Incidents' },
|
||||
{ label: 'Uptime Monitors' },
|
||||
{ label: 'Alerts' },
|
||||
{ label: 'Services' },
|
||||
{ label: 'Status pages' },
|
||||
];
|
||||
const renderTab = () => {
|
||||
switch (selectedTab) {
|
||||
case 0:
|
||||
return <OnCallSchedulesPage />;
|
||||
case 1:
|
||||
return <IncidentsPage />;
|
||||
return <AlertsPage />;
|
||||
case 2:
|
||||
return <UptimeMonitorsPage />;
|
||||
return <ServicesPage />;
|
||||
case 3:
|
||||
return <StatusPagesPage />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { 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';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
denseListIcon: {
|
||||
marginRight: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export const incidentStatusLabels = {
|
||||
[RESOLVED]: 'Resolved',
|
||||
[ACCEPTED]: 'Accepted',
|
||||
[PENDING]: 'Pending',
|
||||
} as Record<string, string>;
|
||||
|
||||
export const IncidentStatus = ({ incident }: { incident: Incident }) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Tooltip title={incident.status} placement="top">
|
||||
<div className={classes.denseListIcon}>
|
||||
{incident.status === 'PENDING' ? <StatusError /> : <StatusOK />}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@@ -13,14 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import RepeatIcon from '@material-ui/icons/Repeat';
|
||||
import { Shift } from '../../types';
|
||||
import { DateTime as dt } from 'luxon';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import React from 'react';
|
||||
import { ilertApiRef } from '../../api';
|
||||
import { Shift } from '../../types';
|
||||
import { ShiftOverrideModal } from '../Shift/ShiftOverrideModal';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
@@ -48,6 +50,7 @@ export const OnCallShiftItem = ({
|
||||
refetchOnCallSchedules: () => void;
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const [isModalOpened, setIsModalOpened] = React.useState(false);
|
||||
|
||||
const handleOverride = () => {
|
||||
@@ -71,7 +74,7 @@ export const OnCallShiftItem = ({
|
||||
{shift && shift.user ? (
|
||||
<Grid item sm={12}>
|
||||
<Typography variant="subtitle1" noWrap>
|
||||
{`${shift.user.firstName} ${shift.user.lastName} (${shift.user.username})`}
|
||||
{ilertApi.getUserInitials(shift.user)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
) : null}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { IconButton, Menu, MenuItem, Typography } from '@material-ui/core';
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
import React from 'react';
|
||||
import { ilertApiRef } from '../../api';
|
||||
import { Service } from '../../types';
|
||||
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export const ServiceActionsMenu = ({ service }: { service: Service }) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleCloseMenu = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={handleClick}
|
||||
size="small"
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id={`service-actions-menu-${service.id}`}
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleCloseMenu}
|
||||
PaperProps={{
|
||||
style: { maxHeight: 48 * 5.5 },
|
||||
}}
|
||||
>
|
||||
<MenuItem key="details" onClick={handleCloseMenu}>
|
||||
<Typography variant="inherit" noWrap>
|
||||
<Link to={ilertApi.getServiceDetailsURL(service)}>
|
||||
View in iLert
|
||||
</Link>
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { ilertApiRef } from '../../api';
|
||||
import { Service } from '../../types';
|
||||
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export const ServiceLink = ({ service }: { service: Service | null }) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
|
||||
if (!service) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Link to={ilertApi.getServiceDetailsURL(service)}>#{service.id}</Link>;
|
||||
};
|
||||
+1
-2
@@ -13,5 +13,4 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './IncidentActionsMenu';
|
||||
export * from './IncidentStatus';
|
||||
export * from './ServiceActionsMenu';
|
||||
+15
-16
@@ -13,26 +13,25 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { AuthenticationError } from '@backstage/errors';
|
||||
import { UptimeMonitorsTable } from './UptimeMonitorsTable';
|
||||
import { MissingAuthorizationHeaderError } from '../Errors';
|
||||
import { useUptimeMonitors } from '../../hooks/useUptimeMonitors';
|
||||
import {
|
||||
Content,
|
||||
ContentHeader,
|
||||
SupportButton,
|
||||
ResponseErrorPanel,
|
||||
SupportButton,
|
||||
} from '@backstage/core-components';
|
||||
import React from 'react';
|
||||
import { useServices } from '../../hooks/useServices';
|
||||
import { MissingAuthorizationHeaderError } from '../Errors';
|
||||
import { ServicesTable } from './ServicesTable';
|
||||
|
||||
export const UptimeMonitorsPage = () => {
|
||||
export const ServicesPage = () => {
|
||||
const [
|
||||
{ tableState, uptimeMonitors, isLoading, error },
|
||||
{ onChangePage, onChangeRowsPerPage, onUptimeMonitorChanged },
|
||||
] = useUptimeMonitors();
|
||||
{ tableState, services, isLoading, error },
|
||||
{ onChangePage, onChangeRowsPerPage, setIsLoading },
|
||||
] = useServices(true);
|
||||
|
||||
if (error) {
|
||||
if (error instanceof AuthenticationError) {
|
||||
if (error.name === 'AuthenticationError') {
|
||||
return (
|
||||
<Content>
|
||||
<MissingAuthorizationHeaderError />
|
||||
@@ -49,18 +48,18 @@ export const UptimeMonitorsPage = () => {
|
||||
|
||||
return (
|
||||
<Content>
|
||||
<ContentHeader title="Uptime Monitors">
|
||||
<ContentHeader title="Services">
|
||||
<SupportButton>
|
||||
This helps you to bring iLert into your developer portal.
|
||||
</SupportButton>
|
||||
</ContentHeader>
|
||||
<UptimeMonitorsTable
|
||||
uptimeMonitors={uptimeMonitors}
|
||||
<ServicesTable
|
||||
services={services}
|
||||
tableState={tableState}
|
||||
isLoading={isLoading}
|
||||
onChangePage={onChangePage}
|
||||
onChangeRowsPerPage={onChangeRowsPerPage}
|
||||
onUptimeMonitorChanged={onUptimeMonitorChanged}
|
||||
isLoading={isLoading}
|
||||
setIsLoading={setIsLoading}
|
||||
/>
|
||||
</Content>
|
||||
);
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import React from 'react';
|
||||
import { TableState } from '../../api';
|
||||
import { Service } from '../../types';
|
||||
import { StatusChip } from './StatusChip';
|
||||
|
||||
import { Table, TableColumn } from '@backstage/core-components';
|
||||
import { ServiceActionsMenu } from '../Service/ServiceActionsMenu';
|
||||
import { ServiceLink } from '../Service/ServiceLink';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
empty: {
|
||||
padding: theme.spacing(2),
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}));
|
||||
|
||||
export const ServicesTable = ({
|
||||
services,
|
||||
tableState,
|
||||
isLoading,
|
||||
onChangePage,
|
||||
onChangeRowsPerPage,
|
||||
compact,
|
||||
}: {
|
||||
services: Service[];
|
||||
tableState: TableState;
|
||||
isLoading: boolean;
|
||||
setIsLoading: (isLoading: boolean) => void;
|
||||
onChangePage: (page: number) => void;
|
||||
onChangeRowsPerPage: (pageSize: number) => void;
|
||||
compact?: boolean;
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
|
||||
const smColumnStyle = {
|
||||
width: '10%',
|
||||
maxWidth: '10%',
|
||||
};
|
||||
const xlColumnStyle = {
|
||||
width: '30%',
|
||||
maxWidth: '30%',
|
||||
};
|
||||
const idColumn: TableColumn<Service> = {
|
||||
title: 'ID',
|
||||
field: 'id',
|
||||
highlight: true,
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => <ServiceLink service={rowData} />,
|
||||
};
|
||||
const nameColumn: TableColumn<Service> = {
|
||||
title: 'Name',
|
||||
field: 'name',
|
||||
cellStyle: !compact ? xlColumnStyle : undefined,
|
||||
headerStyle: !compact ? xlColumnStyle : undefined,
|
||||
render: rowData => <Typography>{rowData.name}</Typography>,
|
||||
};
|
||||
const statusColumn: TableColumn<Service> = {
|
||||
title: 'Status',
|
||||
field: 'status',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => <StatusChip service={rowData} />,
|
||||
};
|
||||
const uptimeColumn: TableColumn<Service> = {
|
||||
title: 'Uptime in the last 90 days',
|
||||
field: 'uptimePercentage',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => (
|
||||
<Typography>{rowData.uptime.uptimePercentage.p90}</Typography>
|
||||
),
|
||||
};
|
||||
const actionsColumn: TableColumn<Service> = {
|
||||
title: '',
|
||||
field: '',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => <ServiceActionsMenu service={rowData} />,
|
||||
};
|
||||
|
||||
const columns: TableColumn<Service>[] = compact
|
||||
? [nameColumn, statusColumn, uptimeColumn, actionsColumn]
|
||||
: [idColumn, nameColumn, statusColumn, uptimeColumn, actionsColumn];
|
||||
|
||||
return (
|
||||
<Table
|
||||
options={{
|
||||
sorting: false,
|
||||
search: !compact,
|
||||
paging: !compact,
|
||||
actionsColumnIndex: -1,
|
||||
pageSize: tableState.pageSize,
|
||||
pageSizeOptions: !compact ? [10, 20, 50, 100] : [3, 10, 20, 50, 100],
|
||||
padding: 'dense',
|
||||
loadingType: 'overlay',
|
||||
showEmptyDataSourceMessage: !isLoading,
|
||||
showTitle: true,
|
||||
toolbar: true,
|
||||
}}
|
||||
emptyContent={
|
||||
<Typography color="textSecondary" className={classes.empty}>
|
||||
No services
|
||||
</Typography>
|
||||
}
|
||||
title={
|
||||
<Typography variant="button" color="textSecondary">
|
||||
SERVICES
|
||||
</Typography>
|
||||
}
|
||||
page={tableState.page}
|
||||
onPageChange={onChangePage}
|
||||
onRowsPerPageChange={onChangeRowsPerPage}
|
||||
columns={columns}
|
||||
data={services}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Chip, withStyles } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import {
|
||||
DEGRADED,
|
||||
MAJOR_OUTAGE,
|
||||
OPERATIONAL,
|
||||
PARTIAL_OUTAGE,
|
||||
Service,
|
||||
UNDER_MAINTENANCE,
|
||||
} from '../../types';
|
||||
|
||||
const OperationalChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#388E3D',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
|
||||
const UnderMaintenanceChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#616161',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
const DegradedChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#FBC02D',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
const PartialOutageChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#F57C02',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
const MajorOutageChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#D22F2E',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
|
||||
const serviceStatusLabels = {
|
||||
[OPERATIONAL]: 'Operational',
|
||||
[UNDER_MAINTENANCE]: 'Under maintenance',
|
||||
[DEGRADED]: 'Degraded',
|
||||
[PARTIAL_OUTAGE]: 'Partial outage',
|
||||
[MAJOR_OUTAGE]: 'Major outage',
|
||||
} as Record<string, string>;
|
||||
|
||||
export const StatusChip = ({ service }: { service: Service }) => {
|
||||
const label = `${serviceStatusLabels[service.status]}`;
|
||||
|
||||
switch (service.status) {
|
||||
case OPERATIONAL:
|
||||
return <OperationalChip label={label} size="small" />;
|
||||
case UNDER_MAINTENANCE:
|
||||
return <UnderMaintenanceChip label={label} size="small" />;
|
||||
case DEGRADED:
|
||||
return <DegradedChip label={label} size="small" />;
|
||||
case PARTIAL_OUTAGE:
|
||||
return <PartialOutageChip label={label} size="small" />;
|
||||
case MAJOR_OUTAGE:
|
||||
return <MajorOutageChip label={label} size="small" />;
|
||||
default:
|
||||
return <Chip label={label} size="small" />;
|
||||
}
|
||||
};
|
||||
+2
-2
@@ -13,5 +13,5 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './UptimeMonitorsPage';
|
||||
export * from './UptimeMonitorsTable';
|
||||
export * from './ServicesPage';
|
||||
export * from './ServicesTable';
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { IconButton, Menu, MenuItem, Typography } from '@material-ui/core';
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
import React from 'react';
|
||||
import { ilertApiRef } from '../../api';
|
||||
import { StatusPage } from '../../types';
|
||||
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export const StatusPageActionsMenu = ({
|
||||
statusPage,
|
||||
}: {
|
||||
statusPage: StatusPage;
|
||||
}) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleCloseMenu = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={handleClick}
|
||||
size="small"
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id={`statusPage-actions-menu-${statusPage.id}`}
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleCloseMenu}
|
||||
PaperProps={{
|
||||
style: { maxHeight: 48 * 5.5 },
|
||||
}}
|
||||
>
|
||||
<MenuItem key="details" onClick={handleCloseMenu}>
|
||||
<Typography variant="inherit" noWrap>
|
||||
<Link to={ilertApi.getStatusPageDetailsURL(statusPage)}>
|
||||
View in iLert
|
||||
</Link>
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<Typography variant="inherit" noWrap>
|
||||
<Link to={`https://${ilertApi.getStatusPageURL(statusPage)}`}>
|
||||
View status page
|
||||
</Link>
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+10
-17
@@ -14,33 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { Incident } from '../../types';
|
||||
import { ilertApiRef } from '../../api';
|
||||
import { StatusPage } 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: {
|
||||
lineHeight: '22px',
|
||||
},
|
||||
});
|
||||
|
||||
export const IncidentLink = ({ incident }: { incident: Incident | null }) => {
|
||||
export const StatusPageLink = ({
|
||||
statusPage,
|
||||
}: {
|
||||
statusPage: StatusPage | null;
|
||||
}) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const classes = useStyles();
|
||||
|
||||
if (!incident) {
|
||||
if (!statusPage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={classes.link}
|
||||
to={ilertApi.getIncidentDetailsURL(incident)}
|
||||
>
|
||||
#{incident.id}
|
||||
<Link to={ilertApi.getStatusPageDetailsURL(statusPage)}>
|
||||
#{statusPage.id}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
+9
-22
@@ -14,37 +14,24 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { UptimeMonitor } from '../../types';
|
||||
import { ilertApiRef } from '../../api';
|
||||
import { StatusPage } 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: {
|
||||
lineHeight: '22px',
|
||||
},
|
||||
});
|
||||
|
||||
export const UptimeMonitorLink = ({
|
||||
uptimeMonitor,
|
||||
export const StatusPageURL = ({
|
||||
statusPage,
|
||||
}: {
|
||||
uptimeMonitor: UptimeMonitor | null;
|
||||
statusPage: StatusPage | null;
|
||||
}) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const classes = useStyles();
|
||||
|
||||
if (!uptimeMonitor) {
|
||||
if (!statusPage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={classes.link}
|
||||
to={ilertApi.getUptimeMonitorDetailsURL(uptimeMonitor)}
|
||||
>
|
||||
#{uptimeMonitor.id}
|
||||
</Link>
|
||||
);
|
||||
const url = ilertApi.getStatusPageURL(statusPage);
|
||||
|
||||
return <Link to={`https://${url}`}>{url}</Link>;
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './StatusPageActionsMenu';
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Chip, withStyles } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import {
|
||||
DEGRADED,
|
||||
MAJOR_OUTAGE,
|
||||
OPERATIONAL,
|
||||
PARTIAL_OUTAGE,
|
||||
StatusPage,
|
||||
UNDER_MAINTENANCE,
|
||||
} from '../../types';
|
||||
|
||||
const OperationalChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#388E3D',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
|
||||
const UnderMaintenanceChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#616161',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
const DegradedChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#FBC02D',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
const PartialOutageChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#F57C02',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
const MajorOutageChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#D22F2E',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
|
||||
const statusPageStatusLabels = {
|
||||
[OPERATIONAL]: 'Operational',
|
||||
[UNDER_MAINTENANCE]: 'Under maintenance',
|
||||
[DEGRADED]: 'Degraded',
|
||||
[PARTIAL_OUTAGE]: 'Partial outage',
|
||||
[MAJOR_OUTAGE]: 'Major outage',
|
||||
} as Record<string, string>;
|
||||
|
||||
export const StatusChip = ({ statusPage }: { statusPage: StatusPage }) => {
|
||||
const label = `${statusPageStatusLabels[statusPage.status]}`;
|
||||
|
||||
switch (statusPage.status) {
|
||||
case OPERATIONAL:
|
||||
return <OperationalChip label={label} size="small" />;
|
||||
case UNDER_MAINTENANCE:
|
||||
return <UnderMaintenanceChip label={label} size="small" />;
|
||||
case DEGRADED:
|
||||
return <DegradedChip label={label} size="small" />;
|
||||
case PARTIAL_OUTAGE:
|
||||
return <PartialOutageChip label={label} size="small" />;
|
||||
case MAJOR_OUTAGE:
|
||||
return <MajorOutageChip label={label} size="small" />;
|
||||
default:
|
||||
return <Chip label={label} size="small" />;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
Content,
|
||||
ContentHeader,
|
||||
ResponseErrorPanel,
|
||||
SupportButton,
|
||||
} from '@backstage/core-components';
|
||||
import React from 'react';
|
||||
import { useStatusPages } from '../../hooks/useStatusPages';
|
||||
import { MissingAuthorizationHeaderError } from '../Errors';
|
||||
import { StatusPagesTable } from './StatusPagesTable';
|
||||
|
||||
export const StatusPagesPage = () => {
|
||||
const [
|
||||
{ tableState, statusPages, isLoading, error },
|
||||
{ onChangePage, onChangeRowsPerPage, setIsLoading },
|
||||
] = useStatusPages(true);
|
||||
|
||||
if (error) {
|
||||
if (error.name === 'AuthenticationError') {
|
||||
return (
|
||||
<Content>
|
||||
<MissingAuthorizationHeaderError />
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Content>
|
||||
<ResponseErrorPanel error={error} />
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Content>
|
||||
<ContentHeader title="Status pages">
|
||||
<SupportButton>
|
||||
This helps you to bring iLert into your developer portal.
|
||||
</SupportButton>
|
||||
</ContentHeader>
|
||||
<StatusPagesTable
|
||||
statusPages={statusPages}
|
||||
tableState={tableState}
|
||||
onChangePage={onChangePage}
|
||||
onChangeRowsPerPage={onChangeRowsPerPage}
|
||||
isLoading={isLoading}
|
||||
setIsLoading={setIsLoading}
|
||||
/>
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import React from 'react';
|
||||
import { TableState } from '../../api';
|
||||
import { StatusPage } from '../../types';
|
||||
import { VisibilityChip } from './VisibilityChip';
|
||||
|
||||
import { Table, TableColumn } from '@backstage/core-components';
|
||||
import { StatusPageActionsMenu } from '../StatusPage/StatusPageActionsMenu';
|
||||
import { StatusPageLink } from '../StatusPage/StatusPageLink';
|
||||
import { StatusPageURL } from '../StatusPage/StatusPageURL';
|
||||
import { StatusChip } from './StatusChip';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
empty: {
|
||||
padding: theme.spacing(2),
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}));
|
||||
|
||||
export const StatusPagesTable = ({
|
||||
statusPages,
|
||||
tableState,
|
||||
isLoading,
|
||||
onChangePage,
|
||||
onChangeRowsPerPage,
|
||||
compact,
|
||||
}: {
|
||||
statusPages: StatusPage[];
|
||||
tableState: TableState;
|
||||
isLoading: boolean;
|
||||
setIsLoading: (isLoading: boolean) => void;
|
||||
onChangePage: (page: number) => void;
|
||||
onChangeRowsPerPage: (pageSize: number) => void;
|
||||
compact?: boolean;
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
|
||||
const smColumnStyle = {
|
||||
width: '10%',
|
||||
maxWidth: '10%',
|
||||
};
|
||||
const xlColumnStyle = {
|
||||
width: '30%',
|
||||
maxWidth: '30%',
|
||||
};
|
||||
|
||||
const idColumn: TableColumn<StatusPage> = {
|
||||
title: 'ID',
|
||||
field: 'id',
|
||||
highlight: true,
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => <StatusPageLink statusPage={rowData} />,
|
||||
};
|
||||
const nameColumn: TableColumn<StatusPage> = {
|
||||
title: 'Name',
|
||||
field: 'name',
|
||||
cellStyle: !compact ? xlColumnStyle : undefined,
|
||||
headerStyle: !compact ? xlColumnStyle : undefined,
|
||||
render: rowData => <Typography>{rowData.name}</Typography>,
|
||||
};
|
||||
const urlColumn: TableColumn<StatusPage> = {
|
||||
title: 'URL',
|
||||
field: 'url',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => <StatusPageURL statusPage={rowData} />,
|
||||
};
|
||||
const visibilityColumn: TableColumn<StatusPage> = {
|
||||
title: 'Visibility',
|
||||
field: 'visibility',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => <VisibilityChip statusPage={rowData} />,
|
||||
};
|
||||
const statusColumn: TableColumn<StatusPage> = {
|
||||
title: 'Status',
|
||||
field: 'status',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => <StatusChip statusPage={rowData} />,
|
||||
};
|
||||
const actionsColumn: TableColumn<StatusPage> = {
|
||||
title: '',
|
||||
field: '',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => <StatusPageActionsMenu statusPage={rowData} />,
|
||||
};
|
||||
|
||||
const columns: TableColumn<StatusPage>[] = compact
|
||||
? [nameColumn, statusColumn, urlColumn, actionsColumn]
|
||||
: [
|
||||
idColumn,
|
||||
nameColumn,
|
||||
statusColumn,
|
||||
urlColumn,
|
||||
visibilityColumn,
|
||||
actionsColumn,
|
||||
];
|
||||
|
||||
return (
|
||||
<Table
|
||||
options={{
|
||||
sorting: false,
|
||||
search: !compact,
|
||||
paging: !compact,
|
||||
actionsColumnIndex: -1,
|
||||
pageSize: tableState.pageSize,
|
||||
pageSizeOptions: !compact ? [10, 20, 50, 100] : [3, 10, 20, 50, 100],
|
||||
padding: 'dense',
|
||||
loadingType: 'overlay',
|
||||
showEmptyDataSourceMessage: !isLoading,
|
||||
showTitle: true,
|
||||
toolbar: true,
|
||||
}}
|
||||
emptyContent={
|
||||
<Typography color="textSecondary" className={classes.empty}>
|
||||
No status pages
|
||||
</Typography>
|
||||
}
|
||||
title={
|
||||
<Typography variant="button" color="textSecondary">
|
||||
STATUS PAGES
|
||||
</Typography>
|
||||
}
|
||||
page={tableState.page}
|
||||
onPageChange={onChangePage}
|
||||
onRowsPerPageChange={onChangeRowsPerPage}
|
||||
columns={columns}
|
||||
data={statusPages}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Chip, withStyles } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { PRIVATE, PUBLIC, StatusPage } from '../../types';
|
||||
|
||||
const PrivateChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#4caf50',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
|
||||
const PublicChip = withStyles({
|
||||
root: {
|
||||
backgroundColor: '#ffb74d',
|
||||
color: 'white',
|
||||
margin: 0,
|
||||
},
|
||||
})(Chip);
|
||||
|
||||
const statusPageVisibilityLabels = {
|
||||
[PUBLIC]: 'Public',
|
||||
[PRIVATE]: 'Private',
|
||||
} as Record<string, string>;
|
||||
|
||||
export const VisibilityChip = ({ statusPage }: { statusPage: StatusPage }) => {
|
||||
const label = `${statusPageVisibilityLabels[statusPage.visibility]}`;
|
||||
|
||||
switch (statusPage.visibility) {
|
||||
case PRIVATE:
|
||||
return <PrivateChip label={label} size="small" />;
|
||||
case PUBLIC:
|
||||
return <PublicChip label={label} size="small" />;
|
||||
default:
|
||||
return <Chip label={label} size="small" />;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './StatusPagesPage';
|
||||
export * from './StatusPagesTable';
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { IconButton, Menu, MenuItem, Typography } from '@material-ui/core';
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
|
||||
import { ilertApiRef } from '../../api';
|
||||
import { UptimeMonitor } from '../../types';
|
||||
|
||||
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { Link } from '@backstage/core-components';
|
||||
|
||||
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 | HTMLElement>(null);
|
||||
const callback = onUptimeMonitorChanged || ((_: UptimeMonitor): void => {});
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleCloseMenu = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handlePause = async (): Promise<void> => {
|
||||
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<void> => {
|
||||
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<void> => {
|
||||
try {
|
||||
const um = await ilertApi.fetchUptimeMonitor(uptimeMonitor.id);
|
||||
handleCloseMenu();
|
||||
window.open(um.shareUrl, '_blank');
|
||||
} catch (err) {
|
||||
alertApi.post({ message: err, severity: 'error' });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={handleClick}
|
||||
size="small"
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id={`uptime-monitor-actions-menu-${uptimeMonitor.id}`}
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleCloseMenu}
|
||||
PaperProps={{
|
||||
style: { maxHeight: 48 * 4.5 },
|
||||
}}
|
||||
>
|
||||
{uptimeMonitor.paused ? (
|
||||
<MenuItem key="ack" onClick={handleResume}>
|
||||
<Typography variant="inherit" noWrap>
|
||||
Resume
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
) : null}
|
||||
|
||||
{!uptimeMonitor.paused ? (
|
||||
<MenuItem key="close" onClick={handlePause}>
|
||||
<Typography variant="inherit" noWrap>
|
||||
Pause
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
) : null}
|
||||
|
||||
<MenuItem key="report" onClick={handleCloseMenu}>
|
||||
<Typography variant="inherit" noWrap>
|
||||
<Link to="#" onClick={handleOpenReport}>
|
||||
View Report
|
||||
</Link>
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem key="details" onClick={handleCloseMenu}>
|
||||
<Typography variant="inherit" noWrap>
|
||||
<Link to={ilertApi.getUptimeMonitorDetailsURL(uptimeMonitor)}>
|
||||
View in iLert
|
||||
</Link>
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import 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<string, string>;
|
||||
|
||||
export const StatusChip = ({
|
||||
uptimeMonitor,
|
||||
}: {
|
||||
uptimeMonitor: UptimeMonitor;
|
||||
}) => {
|
||||
let label = `${uptimeMonitorStatusLabels[uptimeMonitor.status]}`;
|
||||
|
||||
if (uptimeMonitor.paused) {
|
||||
label = 'Paused';
|
||||
return <UnknownChip label={label} size="small" />;
|
||||
}
|
||||
|
||||
switch (uptimeMonitor.status) {
|
||||
case 'up':
|
||||
return <UpChip label={label} size="small" />;
|
||||
case 'down':
|
||||
return <DownChip label={label} size="small" />;
|
||||
case 'unknown':
|
||||
return <UnknownChip label={label} size="small" />;
|
||||
default:
|
||||
return <Chip label={label} size="small" />;
|
||||
}
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { UptimeMonitor } from '../../types';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
export const UptimeMonitorCheckType = ({
|
||||
uptimeMonitor,
|
||||
}: {
|
||||
uptimeMonitor: UptimeMonitor;
|
||||
}) => {
|
||||
switch (uptimeMonitor.region) {
|
||||
case 'EU':
|
||||
return (
|
||||
<Typography
|
||||
noWrap
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
>{`${uptimeMonitor.checkType.toUpperCase()} 🇩🇪`}</Typography>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Typography
|
||||
noWrap
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
>{`${uptimeMonitor.checkType.toUpperCase()} 🇺🇸`}</Typography>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { TableState } from '../../api';
|
||||
import { UptimeMonitor } from '../../types';
|
||||
import { StatusChip } from './StatusChip';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { DateTime as dt, Interval } from 'luxon';
|
||||
import humanizeDuration from 'humanize-duration';
|
||||
import { EscalationPolicyLink } from '../EscalationPolicy/EscalationPolicyLink';
|
||||
import { UptimeMonitorCheckType } from './UptimeMonitorCheckType';
|
||||
import { UptimeMonitorActionsMenu } from '../UptimeMonitor/UptimeMonitorActionsMenu';
|
||||
import { UptimeMonitorLink } from '../UptimeMonitor/UptimeMonitorLink';
|
||||
import { Table, TableColumn } from '@backstage/core-components';
|
||||
|
||||
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 => (
|
||||
<UptimeMonitorLink uptimeMonitor={rowData as UptimeMonitor} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
field: 'name',
|
||||
render: rowData => (
|
||||
<Typography>{(rowData as UptimeMonitor).name}</Typography>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Check Type',
|
||||
field: 'checkType',
|
||||
cellStyle: lgColumnStyle,
|
||||
headerStyle: lgColumnStyle,
|
||||
render: rowData => (
|
||||
<UptimeMonitorCheckType uptimeMonitor={rowData as UptimeMonitor} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Last state change',
|
||||
field: 'lastStatusChange',
|
||||
type: 'datetime',
|
||||
cellStyle: mdColumnStyle,
|
||||
headerStyle: mdColumnStyle,
|
||||
render: rowData => (
|
||||
<Typography noWrap>
|
||||
{humanizeDuration(
|
||||
Interval.fromDateTimes(
|
||||
dt.fromISO((rowData as UptimeMonitor).lastStatusChange),
|
||||
dt.now(),
|
||||
)
|
||||
.toDuration()
|
||||
.valueOf(),
|
||||
{ units: ['h', 'm', 's'], largest: 2, round: true },
|
||||
)}
|
||||
</Typography>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Escalation policy',
|
||||
field: 'assignedTo',
|
||||
cellStyle: lgColumnStyle,
|
||||
headerStyle: lgColumnStyle,
|
||||
render: rowData => (
|
||||
<EscalationPolicyLink
|
||||
escalationPolicy={(rowData as UptimeMonitor).escalationPolicy}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Status',
|
||||
field: 'status',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => (
|
||||
<StatusChip uptimeMonitor={rowData as UptimeMonitor} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
field: '',
|
||||
cellStyle: smColumnStyle,
|
||||
headerStyle: smColumnStyle,
|
||||
render: rowData => (
|
||||
<UptimeMonitorActionsMenu
|
||||
uptimeMonitor={rowData as UptimeMonitor}
|
||||
onUptimeMonitorChanged={onUptimeMonitorChanged}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Table
|
||||
options={{
|
||||
sorting: true,
|
||||
search: true,
|
||||
paging: true,
|
||||
actionsColumnIndex: -1,
|
||||
pageSize: tableState.pageSize,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
padding: 'dense',
|
||||
loadingType: 'overlay',
|
||||
showEmptyDataSourceMessage: !isLoading,
|
||||
}}
|
||||
emptyContent={
|
||||
<Typography color="textSecondary" className={classes.empty}>
|
||||
No uptime monitor
|
||||
</Typography>
|
||||
}
|
||||
page={tableState.page}
|
||||
onPageChange={onChangePage}
|
||||
onRowsPerPageChange={onChangeRowsPerPage}
|
||||
localization={{ header: { actions: undefined } }}
|
||||
isLoading={isLoading}
|
||||
columns={columns}
|
||||
data={uptimeMonitors}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+14
-17
@@ -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,
|
||||
},
|
||||
@@ -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 { AlertSource, UptimeMonitor } from '../types';
|
||||
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
|
||||
import { ilertApiRef } from '../api';
|
||||
import { AlertSource } from '../types';
|
||||
|
||||
export const useAlertSource = (integrationKey: string) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
@@ -28,10 +28,6 @@ export const useAlertSource = (integrationKey: string) => {
|
||||
null,
|
||||
);
|
||||
const [isAlertSourceLoading, setIsAlertSourceLoading] = React.useState(false);
|
||||
const [uptimeMonitor, setUptimeMonitor] =
|
||||
React.useState<UptimeMonitor | null>(null);
|
||||
const [isUptimeMonitorLoading, setIsUptimeMonitorLoading] =
|
||||
React.useState(false);
|
||||
|
||||
const fetchAlertSourceCall = async () => {
|
||||
try {
|
||||
@@ -56,38 +52,13 @@ export const useAlertSource = (integrationKey: string) => {
|
||||
[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 AuthenticationError)) {
|
||||
errorApi.post(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
const { error: uptimeMonitorError, retry: uptimeMonitorRetry } =
|
||||
useAsyncRetry(fetchUptimeMonitorCall, [alertSource]);
|
||||
|
||||
const retry = () => {
|
||||
alertSourceRetry();
|
||||
uptimeMonitorRetry();
|
||||
};
|
||||
const retry = () => alertSourceRetry();
|
||||
|
||||
return [
|
||||
{
|
||||
alertSource,
|
||||
uptimeMonitor,
|
||||
error: alertSourceError || uptimeMonitorError,
|
||||
isLoading: isAlertSourceLoading || isUptimeMonitorLoading,
|
||||
error: alertSourceError,
|
||||
isLoading: isAlertSourceLoading,
|
||||
},
|
||||
{
|
||||
retry,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
+17
-17
@@ -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,
|
||||
) => {
|
||||
+33
-29
@@ -13,14 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { 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 { UptimeMonitor } from '../types';
|
||||
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
|
||||
import { GetServicesOpts, ilertApiRef, TableState } from '../api';
|
||||
import { Service } from '../types';
|
||||
|
||||
export const useUptimeMonitors = () => {
|
||||
export const useServices = (paging: boolean) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const errorApi = useApi(errorApiRef);
|
||||
|
||||
@@ -28,60 +28,64 @@ export const useUptimeMonitors = () => {
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [uptimeMonitorsList, setUptimeMonitorsList] = React.useState<
|
||||
UptimeMonitor[]
|
||||
>([]);
|
||||
|
||||
const [servicesList, setServicesList] = React.useState<Service[]>([]);
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
|
||||
const { error, retry } = useAsyncRetry(async () => {
|
||||
const fetchServicesCall = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const data = await ilertApi.fetchUptimeMonitors();
|
||||
setUptimeMonitorsList(data || []);
|
||||
const opts: GetServicesOpts = {};
|
||||
if (paging) {
|
||||
opts.maxResults = tableState.pageSize;
|
||||
opts.startIndex = tableState.page * tableState.pageSize;
|
||||
}
|
||||
const data = await ilertApi.fetchServices(opts);
|
||||
setServicesList(data || []);
|
||||
setIsLoading(false);
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
if (!(e instanceof AuthenticationError)) {
|
||||
errorApi.post(e);
|
||||
}
|
||||
setIsLoading(false);
|
||||
throw e;
|
||||
}
|
||||
}, [tableState]);
|
||||
};
|
||||
|
||||
const onUptimeMonitorChanged = (newUptimeMonitor: UptimeMonitor) => {
|
||||
setUptimeMonitorsList(
|
||||
uptimeMonitorsList.map((uptimeMonitor: UptimeMonitor): UptimeMonitor => {
|
||||
if (newUptimeMonitor.id === uptimeMonitor.id) {
|
||||
return newUptimeMonitor;
|
||||
}
|
||||
const fetchServices = useAsyncRetry(fetchServicesCall, [tableState]);
|
||||
|
||||
return uptimeMonitor;
|
||||
}),
|
||||
);
|
||||
const refetchServices = () => {
|
||||
setTableState({ ...tableState, page: 0 });
|
||||
Promise.all([fetchServicesCall()]);
|
||||
};
|
||||
|
||||
const error = fetchServices.error;
|
||||
const retry = () => {
|
||||
fetchServices.retry();
|
||||
};
|
||||
|
||||
const onChangePage = (page: number) => {
|
||||
setTableState({ ...tableState, page });
|
||||
};
|
||||
const onChangeRowsPerPage = (pageSize: number) => {
|
||||
setTableState({ ...tableState, pageSize });
|
||||
const onChangeRowsPerPage = (p: number) => {
|
||||
setTableState({ ...tableState, pageSize: p });
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
tableState,
|
||||
uptimeMonitors: uptimeMonitorsList,
|
||||
error,
|
||||
services: servicesList,
|
||||
isLoading,
|
||||
error,
|
||||
},
|
||||
{
|
||||
setTableState,
|
||||
setUptimeMonitorsList,
|
||||
setServicesList,
|
||||
setIsLoading,
|
||||
retry,
|
||||
onUptimeMonitorChanged,
|
||||
refetchServices,
|
||||
onChangePage,
|
||||
onChangeRowsPerPage,
|
||||
setIsLoading,
|
||||
},
|
||||
] as const;
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { AuthenticationError } from '@backstage/errors';
|
||||
import React from 'react';
|
||||
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
|
||||
import { GetStatusPagesOpts, ilertApiRef, TableState } from '../api';
|
||||
import { StatusPage } from '../types';
|
||||
|
||||
export const useStatusPages = (paging: boolean) => {
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const errorApi = useApi(errorApiRef);
|
||||
|
||||
const [tableState, setTableState] = React.useState<TableState>({
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const [statusPagesList, setStatusPagesList] = React.useState<StatusPage[]>(
|
||||
[],
|
||||
);
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
|
||||
const fetchStatusPagesCall = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const opts: GetStatusPagesOpts = {};
|
||||
if (paging) {
|
||||
opts.maxResults = tableState.pageSize;
|
||||
opts.startIndex = tableState.page * tableState.pageSize;
|
||||
}
|
||||
const data = await ilertApi.fetchStatusPages(opts);
|
||||
setStatusPagesList(data || []);
|
||||
setIsLoading(false);
|
||||
} catch (e) {
|
||||
if (!(e instanceof AuthenticationError)) {
|
||||
errorApi.post(e);
|
||||
}
|
||||
setIsLoading(false);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchStatusPages = useAsyncRetry(fetchStatusPagesCall, [tableState]);
|
||||
|
||||
const refetchStatusPages = () => {
|
||||
setTableState({ ...tableState, page: 0 });
|
||||
Promise.all([fetchStatusPagesCall()]);
|
||||
};
|
||||
|
||||
const error = fetchStatusPages.error;
|
||||
const retry = () => {
|
||||
fetchStatusPages.retry();
|
||||
};
|
||||
|
||||
const onChangePage = (page: number) => {
|
||||
setTableState({ ...tableState, page });
|
||||
};
|
||||
const onChangeRowsPerPage = (p: number) => {
|
||||
setTableState({ ...tableState, pageSize: p });
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
tableState,
|
||||
statusPages: statusPagesList,
|
||||
isLoading,
|
||||
error,
|
||||
},
|
||||
{
|
||||
setTableState,
|
||||
setStatusPagesList,
|
||||
setIsLoading,
|
||||
retry,
|
||||
refetchStatusPages,
|
||||
onChangePage,
|
||||
onChangeRowsPerPage,
|
||||
},
|
||||
] as const;
|
||||
};
|
||||
+85
-47
@@ -15,17 +15,18 @@
|
||||
*/
|
||||
|
||||
/** @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;
|
||||
responders: Responder[];
|
||||
logEntries: LogEntry[];
|
||||
links: Link[];
|
||||
images: Image[];
|
||||
@@ -42,10 +43,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 +77,7 @@ export interface LogEntry {
|
||||
timestamp: string;
|
||||
logEntryType: string;
|
||||
text: string;
|
||||
incidentId?: number;
|
||||
alertId?: number;
|
||||
iconName?: string;
|
||||
iconClass?: string;
|
||||
filterTypes?: string[];
|
||||
@@ -99,6 +100,13 @@ export interface User {
|
||||
department: string;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface Responder {
|
||||
acceptedAt?: string;
|
||||
status: string;
|
||||
user: User;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type UserRole =
|
||||
| 'USER'
|
||||
@@ -125,8 +133,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,20 +217,48 @@ 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'
|
||||
| 'LOW_DURING_SUPPORT_HOURS';
|
||||
|
||||
/** @public */
|
||||
export const OPERATIONAL = 'OPERATIONAL';
|
||||
/** @public */
|
||||
export const UNDER_MAINTENANCE = 'UNDER_MAINTENANCE';
|
||||
/** @public */
|
||||
export const DEGRADED = 'DEGRADED';
|
||||
/** @public */
|
||||
export const PARTIAL_OUTAGE = 'PARTIAL_OUTAGE';
|
||||
/** @public */
|
||||
export const MAJOR_OUTAGE = 'MAJOR_OUTAGE';
|
||||
|
||||
/** @public */
|
||||
export type ServiceStatus =
|
||||
| typeof OPERATIONAL
|
||||
| typeof UNDER_MAINTENANCE
|
||||
| typeof DEGRADED
|
||||
| typeof PARTIAL_OUTAGE
|
||||
| typeof MAJOR_OUTAGE;
|
||||
|
||||
/** @public */
|
||||
export const PRIVATE = 'PRIVATE';
|
||||
/** @public */
|
||||
export const PUBLIC = 'PUBLIC';
|
||||
|
||||
/** @public */
|
||||
export type StatusPageVisibility = typeof PRIVATE | typeof PUBLIC;
|
||||
|
||||
/** @public */
|
||||
export interface AlertSourceEmailPredicate {
|
||||
field: 'EMAIL_FROM' | 'EMAIL_SUBJECT' | 'EMAIL_BODY';
|
||||
@@ -251,7 +287,7 @@ export interface AlertSourceSupportDay {
|
||||
/** @public */
|
||||
export interface AlertSourceSupportHours {
|
||||
timezone: AlertSourceTimeZone;
|
||||
autoRaiseIncidents: boolean;
|
||||
autoRaiseAlerts: boolean;
|
||||
supportDays: {
|
||||
MONDAY: AlertSourceSupportDay;
|
||||
TUESDAY: AlertSourceSupportDay;
|
||||
@@ -316,33 +352,7 @@ export interface Shift {
|
||||
}
|
||||
|
||||
/** @public */
|
||||
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[];
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface UptimeMonitorCheckParams {
|
||||
host?: string;
|
||||
port?: number;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface IncidentResponder {
|
||||
export interface AlertResponder {
|
||||
group: 'SUGGESTED' | 'USER' | 'ESCALATION_POLICY' | 'ON_CALL_SCHEDULE';
|
||||
id: number;
|
||||
name: string;
|
||||
@@ -350,19 +360,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;
|
||||
}
|
||||
@@ -376,3 +386,31 @@ export interface OnCall {
|
||||
end: string;
|
||||
escalationLevel: number;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface Service {
|
||||
id: number;
|
||||
name: string;
|
||||
status: ServiceStatus;
|
||||
uptime: Uptime;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface Uptime {
|
||||
uptimePercentage: UptimePercentage;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface UptimePercentage {
|
||||
p90: number;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface StatusPage {
|
||||
id: number;
|
||||
name: string;
|
||||
domain: string;
|
||||
subdomain: string;
|
||||
visibility: StatusPageVisibility;
|
||||
status: ServiceStatus;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user