From e5d36f8ccef0ce9c68502e0cb37fe520257339ec Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Thu, 29 Feb 2024 12:00:25 +0700 Subject: [PATCH 1/7] refactor: extract get incident function --- src/components/downtime-counter/index.ts | 10 +++++++--- src/components/notification/alert-message.ts | 4 ++-- src/components/probe/prober/index.ts | 10 +++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/components/downtime-counter/index.ts b/src/components/downtime-counter/index.ts index 9679f951c..cc6b88f36 100644 --- a/src/components/downtime-counter/index.ts +++ b/src/components/downtime-counter/index.ts @@ -33,6 +33,10 @@ type DowntimeCounter = { createdAt?: Date } +export function getIncident() { + return getContext().incidents +} + export function addIncident({ alert, createdAt, @@ -46,14 +50,14 @@ export function addIncident({ createdAt: createdAt || new Date(), } - setContext({ incidents: [...getContext().incidents, newIncident] }) + setContext({ incidents: [...getIncident(), newIncident] }) } export function getDowntimeDuration({ probeID, url, }: Omit): string { - const lastIncident = getContext().incidents.find( + const lastIncident = getIncident().find( (incident) => incident.probeID === probeID && incident.probeRequestURL === url ) @@ -68,7 +72,7 @@ export function getDowntimeDuration({ } export function removeIncident({ probeID, url }: DowntimeCounter): void { - const newIncidents = getContext().incidents.filter( + const newIncidents = getIncident().filter( (incident) => incident.probeID !== probeID || incident.probeRequestURL !== url // remove incidents with exact mach of probeID and url diff --git a/src/components/notification/alert-message.ts b/src/components/notification/alert-message.ts index e9aeb64a4..7d2679c41 100644 --- a/src/components/notification/alert-message.ts +++ b/src/components/notification/alert-message.ts @@ -33,7 +33,7 @@ import type { NotificationMessage } from '@hyperjumptech/monika-notification' import { ProbeRequestResponse } from '../../interfaces/request' import { ProbeAlert } from '../../interfaces/probe' import { publicIpAddress, publicNetworkInfo } from '../../utils/public-ip' -import { getDowntimeDuration } from '../downtime-counter' +import { getDowntimeDuration, getIncident } from '../downtime-counter' const getLinuxDistro = promisify(getos) @@ -144,7 +144,7 @@ Version: ${userAgent}` } function getRecoveryMessage(isRecovery: boolean, probeID: string, url: string) { - const incidentDateTime = getContext().incidents.find( + const incidentDateTime = getIncident().find( (incident) => incident.probeID === probeID && incident.probeRequestURL === url )?.createdAt diff --git a/src/components/probe/prober/index.ts b/src/components/probe/prober/index.ts index 47c66bb23..306a957ee 100644 --- a/src/components/probe/prober/index.ts +++ b/src/components/probe/prober/index.ts @@ -41,7 +41,11 @@ import { DEFAULT_INCIDENT_THRESHOLD, DEFAULT_RECOVERY_THRESHOLD, } from '../../config/validation/validator/default-values' -import { addIncident, removeIncident } from '../../downtime-counter' +import { + addIncident, + getIncident, + removeIncident, +} from '../../downtime-counter' import { saveNotificationLog, saveProbeRequestLog } from '../../logger/history' import { logResponseTime } from '../../logger/response-time-log' import { sendAlerts } from '../../notification' @@ -193,7 +197,7 @@ export abstract class BaseProber implements Prober { } protected hasIncident(): Incident | undefined { - return getContext().incidents.find( + return getIncident().find( (incident) => incident.probeID === this.probeConfig.id ) } @@ -363,7 +367,7 @@ export abstract class BaseProber implements Prober { protected handleRecovery( probeResults: Pick[] ): void { - const recoveredIncident = getContext().incidents.find( + const recoveredIncident = getIncident().find( (incident) => incident.probeID === this.probeConfig.id ) const requestIndex = From 9a52b176e869375c9c18c7547cb12826c653991b Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Thu, 29 Feb 2024 12:08:35 +0700 Subject: [PATCH 2/7] refactor: extract find incident function --- src/components/downtime-counter/index.test.ts | 12 ++++++++---- src/components/downtime-counter/index.ts | 12 ++++++++---- src/components/notification/alert-message.ts | 4 ++-- src/components/probe/prober/index.test.ts | 18 +++++++++--------- src/components/probe/prober/index.ts | 9 ++++----- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/components/downtime-counter/index.test.ts b/src/components/downtime-counter/index.test.ts index c9e4bdf69..474acce3f 100644 --- a/src/components/downtime-counter/index.test.ts +++ b/src/components/downtime-counter/index.test.ts @@ -23,8 +23,12 @@ **********************************************************************************/ import { expect } from '@oclif/test' -import { getDowntimeDuration, addIncident, removeIncident } from '.' -import { getContext } from '../../context' +import { + getDowntimeDuration, + addIncident, + removeIncident, + getIncidents, +} from '.' describe('Downtime counter', () => { it('should start counter', () => { @@ -104,7 +108,7 @@ describe('Downtime counter', () => { removeIncident(probeConfig) // assert - expect(getContext().incidents[0].probeID).eq(probeConfig2.probeID) + expect(getIncidents()[0].probeID).eq(probeConfig2.probeID) }) it('allow identical probe-ids but different urls', () => { @@ -126,6 +130,6 @@ describe('Downtime counter', () => { removeIncident(probeConfig) // assert - expect(getContext().incidents[0].probeRequestURL).eq(probeConfig2.url) + expect(getIncidents()[0].probeRequestURL).eq(probeConfig2.url) }) }) diff --git a/src/components/downtime-counter/index.ts b/src/components/downtime-counter/index.ts index cc6b88f36..9aa82fdfd 100644 --- a/src/components/downtime-counter/index.ts +++ b/src/components/downtime-counter/index.ts @@ -33,10 +33,14 @@ type DowntimeCounter = { createdAt?: Date } -export function getIncident() { +export function getIncidents() { return getContext().incidents } +export function findIncident(probeId: string) { + return getIncidents().find(({ probeID }) => probeID === probeId) +} + export function addIncident({ alert, createdAt, @@ -50,14 +54,14 @@ export function addIncident({ createdAt: createdAt || new Date(), } - setContext({ incidents: [...getIncident(), newIncident] }) + setContext({ incidents: [...getIncidents(), newIncident] }) } export function getDowntimeDuration({ probeID, url, }: Omit): string { - const lastIncident = getIncident().find( + const lastIncident = getIncidents().find( (incident) => incident.probeID === probeID && incident.probeRequestURL === url ) @@ -72,7 +76,7 @@ export function getDowntimeDuration({ } export function removeIncident({ probeID, url }: DowntimeCounter): void { - const newIncidents = getIncident().filter( + const newIncidents = getIncidents().filter( (incident) => incident.probeID !== probeID || incident.probeRequestURL !== url // remove incidents with exact mach of probeID and url diff --git a/src/components/notification/alert-message.ts b/src/components/notification/alert-message.ts index 7d2679c41..c978910cb 100644 --- a/src/components/notification/alert-message.ts +++ b/src/components/notification/alert-message.ts @@ -33,7 +33,7 @@ import type { NotificationMessage } from '@hyperjumptech/monika-notification' import { ProbeRequestResponse } from '../../interfaces/request' import { ProbeAlert } from '../../interfaces/probe' import { publicIpAddress, publicNetworkInfo } from '../../utils/public-ip' -import { getDowntimeDuration, getIncident } from '../downtime-counter' +import { getDowntimeDuration, getIncidents } from '../downtime-counter' const getLinuxDistro = promisify(getos) @@ -144,7 +144,7 @@ Version: ${userAgent}` } function getRecoveryMessage(isRecovery: boolean, probeID: string, url: string) { - const incidentDateTime = getIncident().find( + const incidentDateTime = getIncidents().find( (incident) => incident.probeID === probeID && incident.probeRequestURL === url )?.createdAt diff --git a/src/components/probe/prober/index.test.ts b/src/components/probe/prober/index.test.ts index 3ea2ca983..5339d2d8e 100644 --- a/src/components/probe/prober/index.test.ts +++ b/src/components/probe/prober/index.test.ts @@ -28,8 +28,8 @@ import { expect } from '@oclif/test' import type { ProberMetadata } from '.' -import { getContext } from '../../../context' import { createProber } from './factory' +import { getIncidents } from '../../downtime-counter' describe('Prober', () => { describe('Initial incident state', () => { @@ -88,7 +88,7 @@ describe('Prober', () => { // assert expect(probeRequestTotal).eq(1) expect(webhookBody).eq(null) - expect(getContext().incidents.length).eq(0) + expect(getIncidents().length).eq(0) }) it('should not initialize probe state if last event id is not specify', async () => { @@ -125,7 +125,7 @@ describe('Prober', () => { // assert expect(probeRequestTotal).eq(1) expect(webhookBody).eq(null) - expect(getContext().incidents.length).eq(0) + expect(getIncidents().length).eq(0) }) it('should not initialize probe state if last event recovered at is not null', async () => { @@ -163,7 +163,7 @@ describe('Prober', () => { // assert expect(probeRequestTotal).eq(1) expect(webhookBody).eq(null) - expect(getContext().incidents.length).eq(0) + expect(getIncidents().length).eq(0) }) it('should not initialize probe state if alert is not found', async () => { @@ -201,7 +201,7 @@ describe('Prober', () => { // assert expect(probeRequestTotal).eq(1) expect(webhookBody).eq(null) - expect(getContext().incidents.length).eq(0) + expect(getIncidents().length).eq(0) }) it('should send recovery notification if recovered_at is null and target is healthy', async () => { @@ -254,7 +254,7 @@ describe('Prober', () => { // assert expect(probeRequestTotal).eq(1) expect(webhookBody).not.eq(null) - expect(getContext().incidents.length).eq(0) + expect(getIncidents().length).eq(0) }) it('should not send incident notification if recovered_at is null and target is not healthy', async () => { @@ -313,7 +313,7 @@ describe('Prober', () => { // assert expect(probeRequestTotal).eq(1) expect(webhookBody).eq(null) - expect(getContext().incidents.length).eq(1) + expect(getIncidents().length).eq(1) }) it('should not send recovery notification if recovered_at is not null and target is healthy', async () => { @@ -365,7 +365,7 @@ describe('Prober', () => { // assert expect(probeRequestTotal).eq(1) expect(webhookBody).eq(null) - expect(getContext().incidents.length).eq(0) + expect(getIncidents().length).eq(0) }) it('should send incident notification if recovered_at is not null and target is not healthy', async () => { @@ -425,7 +425,7 @@ describe('Prober', () => { // assert expect(probeRequestTotal).eq(1) expect(webhookBody).not.eq(null) - expect(getContext().incidents.length).eq(1) + expect(getIncidents().length).eq(1) }) }) }) diff --git a/src/components/probe/prober/index.ts b/src/components/probe/prober/index.ts index 306a957ee..7d93fb30f 100644 --- a/src/components/probe/prober/index.ts +++ b/src/components/probe/prober/index.ts @@ -43,7 +43,8 @@ import { } from '../../config/validation/validator/default-values' import { addIncident, - getIncident, + findIncident, + getIncidents, removeIncident, } from '../../downtime-counter' import { saveNotificationLog, saveProbeRequestLog } from '../../logger/history' @@ -197,9 +198,7 @@ export abstract class BaseProber implements Prober { } protected hasIncident(): Incident | undefined { - return getIncident().find( - (incident) => incident.probeID === this.probeConfig.id - ) + return findIncident(this.probeConfig.id) } /** @@ -367,7 +366,7 @@ export abstract class BaseProber implements Prober { protected handleRecovery( probeResults: Pick[] ): void { - const recoveredIncident = getIncident().find( + const recoveredIncident = getIncidents().find( (incident) => incident.probeID === this.probeConfig.id ) const requestIndex = From b9947de721171a9d26c544eb4516f3d492c3028f Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Thu, 29 Feb 2024 12:34:17 +0700 Subject: [PATCH 3/7] refactor: allow remove incident by only probe id --- src/components/downtime-counter/index.test.ts | 8 ++++---- src/components/downtime-counter/index.ts | 18 +++++++++++++----- src/components/probe/prober/index.ts | 3 +-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/components/downtime-counter/index.test.ts b/src/components/downtime-counter/index.test.ts index 474acce3f..d4532ca6b 100644 --- a/src/components/downtime-counter/index.test.ts +++ b/src/components/downtime-counter/index.test.ts @@ -56,7 +56,7 @@ describe('Downtime counter', () => { // act addIncident(probeConfig) - removeIncident(probeConfig) + removeIncident({ probeId: probeConfig.probeID, url: probeConfig.url }) // assert expect(getDowntimeDuration(probeConfig)).eq('0 seconds') @@ -71,7 +71,7 @@ describe('Downtime counter', () => { } // act - removeIncident(probeConfig) + removeIncident({ probeId: probeConfig.probeID, url: probeConfig.url }) // assert expect(getDowntimeDuration(probeConfig)).eq('0 seconds') @@ -105,7 +105,7 @@ describe('Downtime counter', () => { // act addIncident(probeConfig) addIncident(probeConfig2) - removeIncident(probeConfig) + removeIncident({ probeId: probeConfig.probeID, url: probeConfig.url }) // assert expect(getIncidents()[0].probeID).eq(probeConfig2.probeID) @@ -127,7 +127,7 @@ describe('Downtime counter', () => { // act addIncident(probeConfig) addIncident(probeConfig2) - removeIncident(probeConfig) + removeIncident({ probeId: probeConfig.probeID, url: probeConfig.url }) // assert expect(getIncidents()[0].probeRequestURL).eq(probeConfig2.url) diff --git a/src/components/downtime-counter/index.ts b/src/components/downtime-counter/index.ts index 9aa82fdfd..3acaee463 100644 --- a/src/components/downtime-counter/index.ts +++ b/src/components/downtime-counter/index.ts @@ -75,12 +75,20 @@ export function getDowntimeDuration({ }) } -export function removeIncident({ probeID, url }: DowntimeCounter): void { - const newIncidents = getIncidents().filter( - (incident) => - incident.probeID !== probeID || incident.probeRequestURL !== url +type RemoveIncidentParams = { + probeId: string + url?: string +} + +export function removeIncident({ probeId, url }: RemoveIncidentParams): void { + const newIncidents = getIncidents().filter(({ probeID, probeRequestURL }) => { + if (!url) { + return probeId !== probeID + } + + return probeId !== probeID || probeRequestURL !== url // remove incidents with exact mach of probeID and url - ) + }) setContext({ incidents: newIncidents }) } diff --git a/src/components/probe/prober/index.ts b/src/components/probe/prober/index.ts index 7d93fb30f..f57ff68ba 100644 --- a/src/components/probe/prober/index.ts +++ b/src/components/probe/prober/index.ts @@ -376,8 +376,7 @@ export abstract class BaseProber implements Prober { if (recoveredIncident) { removeIncident({ - alert: recoveredIncident.alert, - probeID: this.probeConfig.id, + probeId: this.probeConfig.id, url: this.probeConfig?.requests?.[requestIndex].url || '', }) From ac93a82c4562291ff73a153c047ff751a6eaff13 Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Thu, 29 Feb 2024 14:14:40 +0700 Subject: [PATCH 4/7] fix: remove incident cache when probe are deleted or disabled --- src/symon/index.test.ts | 78 +++++++++++++++++++++++++++++++++++++++-- src/symon/index.ts | 2 ++ 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/symon/index.test.ts b/src/symon/index.test.ts index 9c3ea0f5c..2bb431c26 100644 --- a/src/symon/index.test.ts +++ b/src/symon/index.test.ts @@ -34,9 +34,11 @@ import SymonClient from '.' import { getContext, resetContext, setContext } from '../context' import { deleteProbe, findProbe, getProbes } from '../components/config/probe' import { validateProbes } from '../components/config/validation' +import { addIncident, findIncident } from '../components/downtime-counter' import events from '../events' import { getEventEmitter } from '../utils/events' import { getErrorMessage } from '../utils/catch-error-handler' +import { getProbeState, initializeProbeStates } from '../utils/probe-state' const config: Config = { version: 'asdfg123', @@ -80,9 +82,6 @@ const server = setupServer( }) ) ), - rest.post('http://localhost:4000/api/v1/monika/status', (_, res, ctx) => - res(ctx.status(200)) - ), rest.get('http://localhost:4000/api/v1/monika/1234/probes', (_, res, ctx) => res( ctx.set('etag', config.version || ''), @@ -129,6 +128,7 @@ describe('Symon initiate', () => { }) after(() => { server.close() + resetContext() }) it('should send handshake data on initiate', async () => { @@ -455,6 +455,78 @@ describe('Symon initiate', () => { await symon.stop() }).timeout(15_000) + + it('should disable a probe', async () => { + // arrange + server.use( + rest.get( + 'http://localhost:4000/api/v1/monika/1234/probe-changes', + (_, res, ctx) => + res( + ctx.json({ + message: 'Successfully get probe changes', + data: [ + { + type: 'disabled', + // eslint-disable-next-line camelcase + probe_id: '1', + probe: {}, + }, + ], + }) + ) + ) + ) + + const symonGetProbesIntervalMs = 100 + setContext({ + flags: { + symonUrl: 'http://localhost:4000', + symonKey: 'random-key', + symonGetProbesIntervalMs, + } as MonikaFlags, + }) + const symon = new SymonClient({ + symonUrl: 'http://localhost:4000', + symonKey: 'abcd', + }) + + // 1. Check initial probe cache + expect(getProbes()).deep.eq([]) + + // act + // 2. Initiate Symon and get all the probes + await symon.initiate() + + // assert + // 3. Check the probe data after connected to Symon + expect(getProbes().length).eq(2) + initializeProbeStates(getProbes()) + addIncident({ + alert: { assertion: '', id: '', message: '' }, + probeID: config.probes[0].id, + url: '', + }) + addIncident({ + alert: { assertion: '', id: '', message: '' }, + probeID: config.probes[1].id, + url: '', + }) + + // act + // 4. Wait for the probe fetch to run + await sleep(symonGetProbesIntervalMs) + + // assert + // 5. Check the updated probe cache and state + expect(getProbes().length).eq(1) + expect(getProbeState('1')).to.be.undefined + expect(getProbeState('2')).to.not.undefined + expect(findIncident('1')).to.be.undefined + expect(findIncident('2')).to.not.be.undefined + + await symon.stop() + }).timeout(15_000) }) function sleep(durationMs: number): Promise { diff --git a/src/symon/index.ts b/src/symon/index.ts index 26de2a45d..53ea8a861 100644 --- a/src/symon/index.ts +++ b/src/symon/index.ts @@ -42,6 +42,7 @@ import { } from '../components/config/probe' import { updateConfig } from '../components/config' import { validateProbes } from '../components/config/validation/validator/probe' +import { removeIncident } from '../components/downtime-counter' import { getOSName } from '../components/notification/alert-message' import { getContext } from '../context' import events from '../events' @@ -481,6 +482,7 @@ async function applyProbeChanges(probeChanges: ProbeChange[]) { case 'disabled': { deleteProbe(probeId) removeProbeState(probeId) + removeIncident({ probeId }) return } From 12e2dc70c81b5cadc0cc5c4aaffdc78e12d25fa4 Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Thu, 29 Feb 2024 14:27:28 +0700 Subject: [PATCH 5/7] refactor: move get downtime counter function --- src/components/downtime-counter/index.ts | 19 -------------- src/components/notification/alert-message.ts | 27 ++++++++++++++++++-- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/components/downtime-counter/index.ts b/src/components/downtime-counter/index.ts index 3acaee463..f82f2f19e 100644 --- a/src/components/downtime-counter/index.ts +++ b/src/components/downtime-counter/index.ts @@ -22,7 +22,6 @@ * SOFTWARE. * **********************************************************************************/ -import { formatDistanceToNow } from 'date-fns' import { getContext, setContext } from '../../context' import type { ProbeAlert } from '../../interfaces/probe' @@ -57,24 +56,6 @@ export function addIncident({ setContext({ incidents: [...getIncidents(), newIncident] }) } -export function getDowntimeDuration({ - probeID, - url, -}: Omit): string { - const lastIncident = getIncidents().find( - (incident) => - incident.probeID === probeID && incident.probeRequestURL === url - ) - - if (!lastIncident) { - return '0 seconds' - } - - return formatDistanceToNow(lastIncident.createdAt, { - includeSeconds: true, - }) -} - type RemoveIncidentParams = { probeId: string url?: string diff --git a/src/components/notification/alert-message.ts b/src/components/notification/alert-message.ts index c978910cb..3a7126d21 100644 --- a/src/components/notification/alert-message.ts +++ b/src/components/notification/alert-message.ts @@ -24,7 +24,7 @@ import { hostname, platform } from 'os' import { promisify } from 'util' -import { format } from 'date-fns' +import { format, formatDistanceToNow } from 'date-fns' import * as Handlebars from 'handlebars' import getos from 'getos' import osName from 'os-name' @@ -33,7 +33,7 @@ import type { NotificationMessage } from '@hyperjumptech/monika-notification' import { ProbeRequestResponse } from '../../interfaces/request' import { ProbeAlert } from '../../interfaces/probe' import { publicIpAddress, publicNetworkInfo } from '../../utils/public-ip' -import { getDowntimeDuration, getIncidents } from '../downtime-counter' +import { getIncidents } from '../downtime-counter' const getLinuxDistro = promisify(getos) @@ -161,6 +161,29 @@ function getRecoveryMessage(isRecovery: boolean, probeID: string, url: string) { return `Target is back to normal after ${incidentDuration}. The incident happened at ${humanReadableIncidentDateTime}. ` } +type GetDowntimeDurationParams = { + probeID: string + url?: string +} + +function getDowntimeDuration({ + probeID, + url, +}: GetDowntimeDurationParams): string { + const lastIncident = getIncidents().find( + (incident) => + incident.probeID === probeID && incident.probeRequestURL === url + ) + + if (!lastIncident) { + return '0 seconds' + } + + return formatDistanceToNow(lastIncident.createdAt, { + includeSeconds: true, + }) +} + export const getMessageForStart = async ( hostname: string, ip: string From 006f7460de3b1770cc2556a3831cc3113d91d447 Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Thu, 29 Feb 2024 14:28:52 +0700 Subject: [PATCH 6/7] refactor: rename incident module --- src/components/{downtime-counter => incident}/index.test.ts | 0 src/components/{downtime-counter => incident}/index.ts | 0 src/components/notification/alert-message.ts | 2 +- src/components/probe/prober/http/index.ts | 2 +- src/components/probe/prober/index.test.ts | 2 +- src/components/probe/prober/index.ts | 2 +- src/symon/index.test.ts | 2 +- src/symon/index.ts | 2 +- 8 files changed, 6 insertions(+), 6 deletions(-) rename src/components/{downtime-counter => incident}/index.test.ts (100%) rename src/components/{downtime-counter => incident}/index.ts (100%) diff --git a/src/components/downtime-counter/index.test.ts b/src/components/incident/index.test.ts similarity index 100% rename from src/components/downtime-counter/index.test.ts rename to src/components/incident/index.test.ts diff --git a/src/components/downtime-counter/index.ts b/src/components/incident/index.ts similarity index 100% rename from src/components/downtime-counter/index.ts rename to src/components/incident/index.ts diff --git a/src/components/notification/alert-message.ts b/src/components/notification/alert-message.ts index 3a7126d21..f1f04ffb6 100644 --- a/src/components/notification/alert-message.ts +++ b/src/components/notification/alert-message.ts @@ -33,7 +33,7 @@ import type { NotificationMessage } from '@hyperjumptech/monika-notification' import { ProbeRequestResponse } from '../../interfaces/request' import { ProbeAlert } from '../../interfaces/probe' import { publicIpAddress, publicNetworkInfo } from '../../utils/public-ip' -import { getIncidents } from '../downtime-counter' +import { getIncidents } from '../incident' const getLinuxDistro = promisify(getos) diff --git a/src/components/probe/prober/http/index.ts b/src/components/probe/prober/http/index.ts index 6b03e47d6..1506077f7 100644 --- a/src/components/probe/prober/http/index.ts +++ b/src/components/probe/prober/http/index.ts @@ -36,7 +36,7 @@ import responseChecker from '../../../../plugins/validate-response/checkers' import { getAlertID } from '../../../../utils/alert-id' import { getEventEmitter } from '../../../../utils/events' import { isSymonModeFrom } from '../../../config' -import { addIncident } from '../../../downtime-counter' +import { addIncident } from '../../../incident' import { saveProbeRequestLog } from '../../../logger/history' import { logResponseTime } from '../../../logger/response-time-log' import { httpRequest } from './request' diff --git a/src/components/probe/prober/index.test.ts b/src/components/probe/prober/index.test.ts index 5339d2d8e..6e7242538 100644 --- a/src/components/probe/prober/index.test.ts +++ b/src/components/probe/prober/index.test.ts @@ -29,7 +29,7 @@ import { expect } from '@oclif/test' import type { ProberMetadata } from '.' import { createProber } from './factory' -import { getIncidents } from '../../downtime-counter' +import { getIncidents } from '../../incident' describe('Prober', () => { describe('Initial incident state', () => { diff --git a/src/components/probe/prober/index.ts b/src/components/probe/prober/index.ts index f57ff68ba..10d2308c9 100644 --- a/src/components/probe/prober/index.ts +++ b/src/components/probe/prober/index.ts @@ -46,7 +46,7 @@ import { findIncident, getIncidents, removeIncident, -} from '../../downtime-counter' +} from '../../incident' import { saveNotificationLog, saveProbeRequestLog } from '../../logger/history' import { logResponseTime } from '../../logger/response-time-log' import { sendAlerts } from '../../notification' diff --git a/src/symon/index.test.ts b/src/symon/index.test.ts index 2bb431c26..9230153af 100644 --- a/src/symon/index.test.ts +++ b/src/symon/index.test.ts @@ -34,7 +34,7 @@ import SymonClient from '.' import { getContext, resetContext, setContext } from '../context' import { deleteProbe, findProbe, getProbes } from '../components/config/probe' import { validateProbes } from '../components/config/validation' -import { addIncident, findIncident } from '../components/downtime-counter' +import { addIncident, findIncident } from '../components/incident' import events from '../events' import { getEventEmitter } from '../utils/events' import { getErrorMessage } from '../utils/catch-error-handler' diff --git a/src/symon/index.ts b/src/symon/index.ts index 53ea8a861..c9830ad42 100644 --- a/src/symon/index.ts +++ b/src/symon/index.ts @@ -42,7 +42,7 @@ import { } from '../components/config/probe' import { updateConfig } from '../components/config' import { validateProbes } from '../components/config/validation/validator/probe' -import { removeIncident } from '../components/downtime-counter' +import { removeIncident } from '../components/incident' import { getOSName } from '../components/notification/alert-message' import { getContext } from '../context' import events from '../events' From fa80cb06c06587438647db5c73b48e8d757133f7 Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Thu, 29 Feb 2024 15:11:39 +0700 Subject: [PATCH 7/7] refactor: remove unused code --- src/components/incident/index.test.ts | 81 +++----------------- src/components/incident/index.ts | 47 +++++------- src/components/notification/alert-message.ts | 31 ++------ src/components/probe/prober/http/index.ts | 2 +- src/components/probe/prober/index.ts | 8 +- src/symon/index.test.ts | 4 +- src/symon/index.ts | 2 +- 7 files changed, 39 insertions(+), 136 deletions(-) diff --git a/src/components/incident/index.test.ts b/src/components/incident/index.test.ts index d4532ca6b..271d4abc0 100644 --- a/src/components/incident/index.test.ts +++ b/src/components/incident/index.test.ts @@ -23,89 +23,26 @@ **********************************************************************************/ import { expect } from '@oclif/test' -import { - getDowntimeDuration, - addIncident, - removeIncident, - getIncidents, -} from '.' - -describe('Downtime counter', () => { - it('should start counter', () => { - // arrange - const probeConfig = { - alert: { id: 'PHbCL', assertion: '', message: '' }, - probeID: 'APDpe', - url: 'https://example.com', - } - - // act - addIncident(probeConfig) - - // assert - expect(getDowntimeDuration(probeConfig)).not.eq('0 seconds') - }) - - it('should stop counter', () => { - // arrange - const probeConfig = { - alert: { id: 'VyYwG', assertion: '', message: '' }, - probeID: 'P1n9x', - url: 'https://example.com', - } - - // act - addIncident(probeConfig) - removeIncident({ probeId: probeConfig.probeID, url: probeConfig.url }) - - // assert - expect(getDowntimeDuration(probeConfig)).eq('0 seconds') - }) - - it('should stop inexistent counter', () => { - // arrange - const probeConfig = { - alert: { id: 'knUA4', assertion: '', message: '' }, - probeID: 'P1n9x', - url: 'https://example.com', - } - - // act - removeIncident({ probeId: probeConfig.probeID, url: probeConfig.url }) - - // assert - expect(getDowntimeDuration(probeConfig)).eq('0 seconds') - }) - - it('should return 0 seconds if not started yet', () => { - // arrange - const probeConfig = { - alert: { id: '9c92j', assertion: '', message: '' }, - probeID: 'rwrs8', - url: 'https://example.com', - } - - // assert - expect(getDowntimeDuration(probeConfig)).eq('0 seconds') - }) +import { addIncident, removeIncident, getIncidents } from '.' +describe('Incident', () => { it('allow identical urls but different probeID', () => { // arrange const probeConfig = { alert: { id: 'VyYwG', assertion: '', message: '' }, probeID: 'Pn9x', - url: 'https://example.com', + probeRequestURL: 'https://example.com', } const probeConfig2 = { alert: { id: 'VyYwG', assertion: '', message: '' }, probeID: 'Pn9x-2', - url: 'https://example.com', + probeRequestURL: 'https://example.com', } // act addIncident(probeConfig) addIncident(probeConfig2) - removeIncident({ probeId: probeConfig.probeID, url: probeConfig.url }) + removeIncident(probeConfig) // assert expect(getIncidents()[0].probeID).eq(probeConfig2.probeID) @@ -116,20 +53,20 @@ describe('Downtime counter', () => { const probeConfig = { alert: { id: 'VyYwG', assertion: '', message: '' }, probeID: 'Pn9x', - url: 'https://example.com', + probeRequestURL: 'https://example.com', } const probeConfig2 = { alert: { id: 'VyYwG', assertion: '', message: '' }, probeID: 'Pn9x', - url: 'https://sub.example.com', + probeRequestURL: 'https://sub.example.com', } // act addIncident(probeConfig) addIncident(probeConfig2) - removeIncident({ probeId: probeConfig.probeID, url: probeConfig.url }) + removeIncident(probeConfig) // assert - expect(getIncidents()[0].probeRequestURL).eq(probeConfig2.url) + expect(getIncidents()[0].probeRequestURL).eq(probeConfig2.probeRequestURL) }) }) diff --git a/src/components/incident/index.ts b/src/components/incident/index.ts index f82f2f19e..f1e8e2203 100644 --- a/src/components/incident/index.ts +++ b/src/components/incident/index.ts @@ -22,15 +22,7 @@ * SOFTWARE. * **********************************************************************************/ -import { getContext, setContext } from '../../context' -import type { ProbeAlert } from '../../interfaces/probe' - -type DowntimeCounter = { - alert: ProbeAlert - probeID: string - url: string - createdAt?: Date -} +import { type Incident, getContext, setContext } from '../../context' export function getIncidents() { return getContext().incidents @@ -40,34 +32,31 @@ export function findIncident(probeId: string) { return getIncidents().find(({ probeID }) => probeID === probeId) } -export function addIncident({ - alert, - createdAt, - probeID, - url, -}: DowntimeCounter): void { +export function addIncident( + incident: Omit & Partial> +): void { const newIncident = { - alert, - probeID, - probeRequestURL: url, - createdAt: createdAt || new Date(), + ...incident, + createdAt: incident?.createdAt || new Date(), } setContext({ incidents: [...getIncidents(), newIncident] }) } -type RemoveIncidentParams = { - probeId: string - url?: string -} - -export function removeIncident({ probeId, url }: RemoveIncidentParams): void { - const newIncidents = getIncidents().filter(({ probeID, probeRequestURL }) => { - if (!url) { - return probeId !== probeID +export function removeIncident({ + probeID, + probeRequestURL, +}: Pick & + Partial>): void { + const newIncidents = getIncidents().filter((incident) => { + if (!probeRequestURL) { + return probeID !== incident.probeID } - return probeId !== probeID || probeRequestURL !== url + return ( + probeID !== incident.probeID || + probeRequestURL !== incident.probeRequestURL + ) // remove incidents with exact mach of probeID and url }) diff --git a/src/components/notification/alert-message.ts b/src/components/notification/alert-message.ts index f1f04ffb6..6892d5c8f 100644 --- a/src/components/notification/alert-message.ts +++ b/src/components/notification/alert-message.ts @@ -147,43 +147,22 @@ function getRecoveryMessage(isRecovery: boolean, probeID: string, url: string) { const incidentDateTime = getIncidents().find( (incident) => incident.probeID === probeID && incident.probeRequestURL === url - )?.createdAt + ) if (!isRecovery || !incidentDateTime) { return '' } - const incidentDuration = getDowntimeDuration({ probeID, url }) + const incidentDuration = formatDistanceToNow(incidentDateTime.createdAt, { + includeSeconds: true, + }) const humanReadableIncidentDateTime = format( - incidentDateTime, + incidentDateTime.createdAt, 'yyyy-MM-dd HH:mm:ss XXX' ) return `Target is back to normal after ${incidentDuration}. The incident happened at ${humanReadableIncidentDateTime}. ` } -type GetDowntimeDurationParams = { - probeID: string - url?: string -} - -function getDowntimeDuration({ - probeID, - url, -}: GetDowntimeDurationParams): string { - const lastIncident = getIncidents().find( - (incident) => - incident.probeID === probeID && incident.probeRequestURL === url - ) - - if (!lastIncident) { - return '0 seconds' - } - - return formatDistanceToNow(lastIncident.createdAt, { - includeSeconds: true, - }) -} - export const getMessageForStart = async ( hostname: string, ip: string diff --git a/src/components/probe/prober/http/index.ts b/src/components/probe/prober/http/index.ts index 1506077f7..d488d34a6 100644 --- a/src/components/probe/prober/http/index.ts +++ b/src/components/probe/prober/http/index.ts @@ -203,7 +203,7 @@ export class HTTPProber extends BaseProber { addIncident({ alert: triggeredAlert, probeID, - url, + probeRequestURL: url, }) this.sendNotification({ diff --git a/src/components/probe/prober/index.ts b/src/components/probe/prober/index.ts index 10d2308c9..b6731a175 100644 --- a/src/components/probe/prober/index.ts +++ b/src/components/probe/prober/index.ts @@ -334,7 +334,7 @@ export abstract class BaseProber implements Prober { addIncident({ alert: failedRequestAssertion, probeID: this.probeConfig.id, - url: this.probeConfig?.requests?.[requestIndex].url || '', + probeRequestURL: this.probeConfig?.requests?.[requestIndex].url || '', }) saveProbeRequestLog({ @@ -376,8 +376,7 @@ export abstract class BaseProber implements Prober { if (recoveredIncident) { removeIncident({ - probeId: this.probeConfig.id, - url: this.probeConfig?.requests?.[requestIndex].url || '', + probeID: this.probeConfig.id, }) const url = this.probeConfig?.requests?.[requestIndex].url || '' @@ -387,7 +386,6 @@ export abstract class BaseProber implements Prober { response: probeResults[requestIndex].requestResponse, } const probeID = this.probeConfig.id - const alertId = getAlertID(url, validation, probeID) this.sendNotification({ @@ -451,7 +449,7 @@ export abstract class BaseProber implements Prober { addIncident({ alert, probeID: this.probeConfig.id, - url: request.url, + probeRequestURL: request.url, createdAt, }) } diff --git a/src/symon/index.test.ts b/src/symon/index.test.ts index 9230153af..1ee4fd3b9 100644 --- a/src/symon/index.test.ts +++ b/src/symon/index.test.ts @@ -505,12 +505,12 @@ describe('Symon initiate', () => { addIncident({ alert: { assertion: '', id: '', message: '' }, probeID: config.probes[0].id, - url: '', + probeRequestURL: '', }) addIncident({ alert: { assertion: '', id: '', message: '' }, probeID: config.probes[1].id, - url: '', + probeRequestURL: '', }) // act diff --git a/src/symon/index.ts b/src/symon/index.ts index c9830ad42..d024d9497 100644 --- a/src/symon/index.ts +++ b/src/symon/index.ts @@ -482,7 +482,7 @@ async function applyProbeChanges(probeChanges: ProbeChange[]) { case 'disabled': { deleteProbe(probeId) removeProbeState(probeId) - removeIncident({ probeId }) + removeIncident({ probeID: probeId }) return }