Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 59 additions & 34 deletions src/features/dashboard/layouts/status-indicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,62 @@ export const STATUS_PAGE_LINK_URL = 'https://status.e2b.dev'
const INCIDENT_IO_STATUS_PAGE_URL = 'https://statuspage.incident.io/e2b-service'
export const STATUS_PAGE_SUMMARY_URL = `${INCIDENT_IO_STATUS_PAGE_URL}/api/v2/summary.json`

function stateFromIndicator(indicator: string | undefined) {
if (indicator === 'none') return 'operational'
if (indicator === 'minor') return 'degraded'
if (indicator === 'major') return 'degraded'
if (indicator === 'critical') return 'downtime'
if (indicator === 'maintenance') return 'maintenance'
const STATUS_PRIORITY: Record<AggregateState, number> = {
unknown: 0,
operational: 1,
maintenance: 2,
degraded: 3,
downtime: 4,
}

return undefined
const INDICATOR_STATE: Record<string, AggregateState> = {
none: 'operational',
minor: 'degraded',
major: 'degraded',
critical: 'downtime',
maintenance: 'maintenance',
}

function getWorstComponentState(
components: IncidentIOStatusPageSummaryResponse['components']
const COMPONENT_STATE: Record<string, AggregateState> = {
operational: 'operational',
under_maintenance: 'maintenance',
degraded_performance: 'degraded',
partial_outage: 'degraded',
full_outage: 'downtime',
major_outage: 'downtime',
}

const MAINTENANCE_IN_PROGRESS_STATUSES = new Set([
'in_progress',
'maintenance_in_progress',
])

function stateFromValue(
value: string | undefined,
stateMap: Record<string, AggregateState>
) {
return value ? stateMap[value] : undefined
}

function highestPriorityState(
states: Array<AggregateState | undefined>
): AggregateState | undefined {
const componentStatuses =
components?.map((component) => component.status) ?? []
return states.reduce<AggregateState | undefined>((highest, state) => {
if (!state) return highest
if (!highest) return state

if (componentStatuses.includes('major_outage')) return 'downtime'
if (componentStatuses.includes('partial_outage')) return 'degraded'
if (componentStatuses.includes('degraded_performance')) return 'degraded'
if (componentStatuses.includes('under_maintenance')) return 'maintenance'
return STATUS_PRIORITY[state] > STATUS_PRIORITY[highest] ? state : highest
}, undefined)
}

return undefined
function getWorstComponentState(
components: IncidentIOStatusPageSummaryResponse['components']
): AggregateState | undefined {
return highestPriorityState(
components?.map((component) =>
stateFromValue(component.status, COMPONENT_STATE)
) ?? []
)
}

function hasMaintenanceInProgress(
Expand All @@ -51,32 +85,23 @@ function hasMaintenanceInProgress(
return (
maintenances?.some(
(maintenance) =>
maintenance.status === 'in_progress' ||
maintenance.status === 'maintenance_in_progress'
!!maintenance.status &&
MAINTENANCE_IN_PROGRESS_STATUSES.has(maintenance.status)
) ?? false
)
}

export function getStatusPageStateFromSummary(
data: IncidentIOStatusPageSummaryResponse
): AggregateState {
const indicatorState = stateFromIndicator(data.status?.indicator)
const indicatorState = stateFromValue(data.status?.indicator, INDICATOR_STATE)
const componentState = getWorstComponentState(data.components)
const maintenanceState = hasMaintenanceInProgress(data.scheduled_maintenances)
? 'maintenance'
: undefined

if (indicatorState === 'downtime' || componentState === 'downtime')
return 'downtime'

if (indicatorState === 'degraded' || componentState === 'degraded')
return 'degraded'

if (
indicatorState === 'maintenance' ||
componentState === 'maintenance' ||
hasMaintenanceInProgress(data.scheduled_maintenances)
return (
highestPriorityState([indicatorState, componentState, maintenanceState]) ??
'unknown'
)
return 'maintenance'

if (indicatorState === 'operational') return 'operational'

return 'unknown'
}
17 changes: 16 additions & 1 deletion tests/unit/status-indicator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('status-indicator', () => {
).toBe('downtime')
})

it('should report downtime for major outage components', () => {
it('should report downtime for full outage components', () => {
expect(
getStatusPageStateFromSummary({
status: {
Expand All @@ -61,6 +61,21 @@ describe('status-indicator', () => {
{
status: 'degraded_performance',
},
{
status: 'full_outage',
},
],
})
).toBe('downtime')
})

it('should support major outage as a Statuspage compatibility alias', () => {
expect(
getStatusPageStateFromSummary({
status: {
indicator: 'none',
},
components: [
{
status: 'major_outage',
},
Expand Down
Loading