From fefc700075a5f7e494a781f177c87e73e54d3d2d Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Tue, 14 Nov 2023 18:30:09 +0700 Subject: [PATCH 1/7] test: add tests --- .../config/validation/validator/probe.test.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/components/config/validation/validator/probe.test.ts b/src/components/config/validation/validator/probe.test.ts index e801ec560..31738b2f8 100644 --- a/src/components/config/validation/validator/probe.test.ts +++ b/src/components/config/validation/validator/probe.test.ts @@ -28,6 +28,8 @@ import type { Probe } from '../../../../interfaces/probe' import { FAILED_REQUEST_ASSERTION } from '../../../../looper' import { validateProbes } from './probe' +import { resetContext, setContext } from '../../../../../src/context' +import type { MonikaFlags } from '../../../../../src/flag' describe('Probe validation', () => { describe('Probe sanitization', () => { @@ -156,5 +158,52 @@ describe('Probe validation', () => { expect(result[0].alerts[0].assertion).eq('') expect(result[0].alerts[0].message).eq(FAILED_REQUEST_ASSERTION.message) }) + + it('should throws an error if alert assertion is invalid', () => { + // arrange + const probe = { + id: 'Example', + requests: [{ url: 'https://example.com' }], + alerts: [{ assertion: 'response.time > 1000 ms' }], + } as unknown as Probe + + // act & assert + expect(validateProbes([probe])).to.eventually.throw() + }) + + it.only('should skips the probe if alert assertion is invalid', async () => { + // arrange + setContext({ + flags: { + symonKey: 'bDF8j', + symonUrl: 'https://example.com', + } as MonikaFlags, + }) + const probes = [ + { + id: 'Example', + requests: [{ url: 'https://example.com' }], + alerts: [{ assertion: 'response.time > 1000 ms' }], + }, + { + id: 'Example 2', + requests: [{ url: 'https://example.com' }], + alerts: [{ assertion: 'response.time > 1000' }], + }, + { + id: 'Example 3', + requests: [{ url: 'https://example.com' }], + alerts: [{ assertion: 'response.status == 200' }], + }, + ] as unknown as Probe[] + + // act + const validatedProbe = await validateProbes(probes) + + // assert + expect(validatedProbe).to.deep.eq(probes.filter((_, index) => index > 0)) + + resetContext() + }) }) }) From 86866c87b1675fd8caf0cf4cfeeaaac888c88498 Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Tue, 14 Nov 2023 18:31:00 +0700 Subject: [PATCH 2/7] fix: fix alert validation that did not work --- .../config/validation/validator/probe.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/components/config/validation/validator/probe.ts b/src/components/config/validation/validator/probe.ts index 7bc09f443..100e2271e 100644 --- a/src/components/config/validation/validator/probe.ts +++ b/src/components/config/validation/validator/probe.ts @@ -352,14 +352,28 @@ function validateAlerts(alerts: (ProbeAlert | string)[]) { try { isValidProbeAlert(alert) } catch { - throw new Error(`Probe alert format is invalid! (${alert})`) + throw new Error( + `Probe alert format is invalid! (${JSON.stringify(alert, null, 1)})` + ) } } } function isValidProbeAlert(alert: ProbeAlert | string) { if (typeof alert !== 'string') { - compileExpression(alert.assertion || alert.query || '') + const expression = alert.assertion || alert.query || '' + const data = { + response: { + size: 0, + status: 200, + time: 0, + body: '', + headers: {}, + }, + } + const filter = compileExpression(expression, Object.keys(data)) + + filter(data) return } From 6ceb427ea6e623c286006e94bee171b51f1a3f22 Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Tue, 14 Nov 2023 18:37:29 +0700 Subject: [PATCH 3/7] refactor: move probe request validation to joi --- .../config/validation/validator/probe.ts | 467 +++++++++--------- 1 file changed, 221 insertions(+), 246 deletions(-) diff --git a/src/components/config/validation/validator/probe.ts b/src/components/config/validation/validator/probe.ts index 100e2271e..91dfa933f 100644 --- a/src/components/config/validation/validator/probe.ts +++ b/src/components/config/validation/validator/probe.ts @@ -39,21 +39,37 @@ import { export async function validateProbes(probes: Probe[]): Promise { const alertSchema = joi.alternatives().try( joi.string(), - joi.object({ - assertion: joi - .string() - .allow('') - .default((parent) => { - if (!parent.query) { - return 'response.status < 200 or response.status > 299' + joi + .object({ + assertion: joi + .string() + .allow('') + .default((parent) => { + if (!parent.query) { + return 'response.status < 200 or response.status > 299' + } + + return parent.query + }), + id: joi.string().allow(''), + message: joi.string().allow(''), + query: joi.string().allow(''), + }) + .custom((alert) => { + try { + isValidProbeAlert(alert) + } catch { + if (isSymonModeFrom(getContext().flags)) { + return } - return parent.query - }), - id: joi.string().allow(''), - message: joi.string().allow(''), - query: joi.string().allow(''), - }) + throw new Error( + `Probe alert format is invalid! (${JSON.stringify(alert, null, 1)})` + ) + } + + return alert + }) ) const mysqlSchema = joi.object({ command: joi.string().allow(''), @@ -74,193 +90,204 @@ export async function validateProbes(probes: Probe[]): Promise { 'array.min': 'Probes object does not exists or has length lower than 1!', }) .items( - joi.object({ - alerts: joi - .array() - .items(alertSchema) - .default((parent) => { - if (isSymonModeFrom(getContext().flags)) { - return [] - } - - const isHTTPProbe = Boolean(parent.requests) - - if (!isHTTPProbe) { - return [{ id: uuid(), ...FAILED_REQUEST_ASSERTION }] - } - - return [ - { - id: uuid(), - assertion: 'response.status < 200 or response.status > 299', - message: 'HTTP Status is {{ response.status }}, expecting 2xx', - }, - { - id: uuid(), - assertion: 'response.time > 2000', - message: - 'Response time is {{ response.time }}ms, expecting less than 2000ms', - }, - { id: uuid(), ...FAILED_REQUEST_ASSERTION }, - ] - }), - description: joi.string().allow(''), - id: joi.string().required(), - incidentThreshold: joi - .number() - .default(DEFAULT_INCIDENT_THRESHOLD) - .min(1), - recoveryThreshold: joi - .number() - .default(DEFAULT_RECOVERY_THRESHOLD) - .min(1), - interval: joi.number().default(DEFAULT_INTERVAL).min(1), - lastEvent: joi.object({ - createdAt: joi.string().allow(''), - recoveredAt: joi.string().allow('', null), - }), - name: joi - .string() - .allow('') - .default((parent) => `monika_${parent.id}`), - mariadb: joi.array().items(mysqlSchema), - mongo: joi.array().items( - joi.alternatives([ - joi.object({ - alerts: joi.array().items(alertSchema), - uri: joi.string().required(), - }), - joi.object({ - alerts: joi.array().items(alertSchema), - host: joi - .alternatives() - .try( - joi.string().allow('').hostname(), - joi.string().allow('').ip() - ) - .required(), - password: joi.string().allow(''), - port: joi.number().default(27_017).min(0).max(65_536), - username: joi.string().allow(''), + joi + .object({ + alerts: joi + .array() + .items(alertSchema) + .default((parent) => { + if (isSymonModeFrom(getContext().flags)) { + return [] + } + + const isHTTPProbe = Boolean(parent.requests) + + if (!isHTTPProbe) { + return [{ id: uuid(), ...FAILED_REQUEST_ASSERTION }] + } + + return [ + { + id: uuid(), + assertion: 'response.status < 200 or response.status > 299', + message: + 'HTTP Status is {{ response.status }}, expecting 2xx', + }, + { + id: uuid(), + assertion: 'response.time > 2000', + message: + 'Response time is {{ response.time }}ms, expecting less than 2000ms', + }, + { id: uuid(), ...FAILED_REQUEST_ASSERTION }, + ] }), - ]) - ), - mysql: joi.array().items(mysqlSchema), - ping: joi.array().items( - joi.object({ - alerts: joi.array().items(alertSchema), - uri: joi.string().required(), - }) - ), - postgres: joi.array().items( - joi.alternatives([ + description: joi.string().allow(''), + id: joi.string().required(), + incidentThreshold: joi + .number() + .default(DEFAULT_INCIDENT_THRESHOLD) + .min(1), + recoveryThreshold: joi + .number() + .default(DEFAULT_RECOVERY_THRESHOLD) + .min(1), + interval: joi.number().default(DEFAULT_INTERVAL).min(1), + lastEvent: joi.object({ + createdAt: joi.string().allow(''), + recoveredAt: joi.string().allow('', null), + }), + name: joi + .string() + .allow('') + .default((parent) => `monika_${parent.id}`), + mariadb: joi.array().items(mysqlSchema), + mongo: joi.array().items( + joi.alternatives([ + joi.object({ + alerts: joi.array().items(alertSchema), + uri: joi.string().required(), + }), + joi.object({ + alerts: joi.array().items(alertSchema), + host: joi + .alternatives() + .try( + joi.string().allow('').hostname(), + joi.string().allow('').ip() + ) + .required(), + password: joi.string().allow(''), + port: joi.number().default(27_017).min(0).max(65_536), + username: joi.string().allow(''), + }), + ]) + ), + mysql: joi.array().items(mysqlSchema), + ping: joi.array().items( joi.object({ alerts: joi.array().items(alertSchema), uri: joi.string().required(), - }), - joi.object({ - alerts: joi.array().items(alertSchema), - command: joi.string().allow(''), - data: joi - .alternatives() - .try(joi.string().allow(''), joi.number()), - database: joi.string().allow(''), - host: joi - .alternatives() - .try( - joi.string().allow('').hostname(), - joi.string().allow('').ip() - ) - .required(), - password: joi.string().allow(''), - port: joi.number().default(5432), - username: joi.string().allow(''), - }), - ]) - ), - redis: joi.array().items( - joi - .object({ - alerts: joi.array().items(alertSchema), - command: joi.string().allow(''), - host: joi - .alternatives() - .try( - joi.string().allow('').hostname(), - joi.string().allow('').ip() - ), - password: joi.string().allow(''), - port: joi.number().min(0).max(65_536), - uri: joi.string().allow(''), - username: joi.string().allow(''), }) - .xor('host', 'uri') - .and('host', 'port') - ), - requests: joi - .array() - .min(isSymonModeFrom(getContext().flags) ? 0 : 1) - .items( - joi.object({ - alerts: joi.array().items(alertSchema), - allowUnauthorized: joi.bool(), - body: joi - .alternatives() - .try( - joi.string().allow('', null), - joi.object(), - joi.array(), - joi.number(), - joi.bool() - ), - headers: joi.object().allow(null), - id: joi.string().allow(''), - interval: joi.number().min(1), - method: joi - .string() - .valid( - 'CONNECT', - 'GET', - 'POST', - 'PUT', - 'PATCH', - 'DELETE', - 'HEAD', - 'OPTIONS', - 'PURGE', - 'LINK', - 'TRACE', - 'UNLINK' - ) - .default('GET') - .insensitive() - .label('Probe request method'), - ping: joi.bool(), - saveBody: joi.bool().default(false), - timeout: joi.number().default(10_000).min(1).allow(null), - url: joi - .string() - .custom((url) => { - if (!isValidURL(url)) { - throw new Error( - `Probe request URL (${url}) should start with http:// or https://` - ) - } + ), + postgres: joi.array().items( + joi.alternatives([ + joi.object({ + alerts: joi.array().items(alertSchema), + uri: joi.string().required(), + }), + joi.object({ + alerts: joi.array().items(alertSchema), + command: joi.string().allow(''), + data: joi + .alternatives() + .try(joi.string().allow(''), joi.number()), + database: joi.string().allow(''), + host: joi + .alternatives() + .try( + joi.string().allow('').hostname(), + joi.string().allow('').ip() + ) + .required(), + password: joi.string().allow(''), + port: joi.number().default(5432), + username: joi.string().allow(''), + }), + ]) + ), + redis: joi.array().items( + joi + .object({ + alerts: joi.array().items(alertSchema), + command: joi.string().allow(''), + host: joi + .alternatives() + .try( + joi.string().allow('').hostname(), + joi.string().allow('').ip() + ), + password: joi.string().allow(''), + port: joi.number().min(0).max(65_536), + uri: joi.string().allow(''), + username: joi.string().allow(''), + }) + .xor('host', 'uri') + .and('host', 'port') + ), + requests: joi + .array() + .min(isSymonModeFrom(getContext().flags) ? 0 : 1) + .items( + joi.object({ + alerts: joi.array().items(alertSchema), + allowUnauthorized: joi.bool(), + body: joi + .alternatives() + .try( + joi.string().allow('', null), + joi.object(), + joi.array(), + joi.number(), + joi.bool() + ), + headers: joi.object().allow(null), + id: joi.string().allow(''), + interval: joi.number().min(1), + method: joi + .string() + .valid( + 'CONNECT', + 'GET', + 'POST', + 'PUT', + 'PATCH', + 'DELETE', + 'HEAD', + 'OPTIONS', + 'PURGE', + 'LINK', + 'TRACE', + 'UNLINK' + ) + .default('GET') + .insensitive() + .label('Probe request method'), + ping: joi.bool(), + saveBody: joi.bool().default(false), + timeout: joi.number().default(10_000).min(1).allow(null), + url: joi + .string() + .custom((url) => { + if (!isValidURL(url)) { + throw new Error( + `Probe request URL (${url}) should start with http:// or https://` + ) + } + + return url + }) + .label('Probe request URL') + .required(), + }) + ) + .label('Probe requests'), + socket: joi.object({ + alerts: joi.array().items(alertSchema), + data: joi.string().allow('', null), + host: joi.string().required(), + port: joi.number().required(), + }), + }) + .custom((probe) => { + if (!isProbeRequestExists(probe)) { + throw new Error( + 'Probe requests does not exists or has length lower than 1!' + ) + } - return url - }) - .label('Probe request URL') - .required(), - }) - ) - .label('Probe requests'), - socket: joi.object({ - alerts: joi.array().items(alertSchema), - data: joi.string().allow('', null), - host: joi.string().required(), - port: joi.number().required(), - }), - }) + return probe + }) ) try { @@ -268,16 +295,6 @@ export async function validateProbes(probes: Probe[]): Promise { stripUnknown: true, }) - for (const probe of validatedProbes) { - if (!isProbeRequestExists(probe)) { - throw new Error( - 'Probe requests does not exists or has length lower than 1!' - ) - } - - validateAlerts(combineAlerts(probe)) - } - return transformDeprecatedAlerts(validatedProbes) } catch (error: any) { throw new Error(`Monika configuration is invalid. Probe: ${error?.message}`) @@ -297,36 +314,6 @@ function isProbeRequestExists(probe: Probe) { ) } -function combineAlerts(probe: Probe) { - const httpAlerts = - probe?.requests?.map(({ alerts }) => alerts).find(Boolean) || [] - const mariadbAlerts = - probe?.mariadb?.map(({ alerts }) => alerts).find(Boolean) || [] - const mongoAlerts = - probe?.mongo?.map(({ alerts }) => alerts).find(Boolean) || [] - const mysqlAlerts = - probe?.mysql?.map(({ alerts }) => alerts).find(Boolean) || [] - const pingAlerts = - probe?.ping?.map(({ alerts }) => alerts).find(Boolean) || [] - const postgresAlerts = - probe?.postgres?.map(({ alerts }) => alerts).find(Boolean) || [] - const redisAlerts = - probe?.redis?.map(({ alerts }) => alerts).find(Boolean) || [] - const socketAlerts = probe?.socket?.alerts || [] - - return [ - ...(probe?.alerts || []), - ...httpAlerts, - ...mariadbAlerts, - ...mongoAlerts, - ...mysqlAlerts, - ...pingAlerts, - ...postgresAlerts, - ...redisAlerts, - ...socketAlerts, - ] -} - const ALERT_QUERY = 'status-not-2xx' const RESPONSE_TIME_PREFIX = 'response-time-greater-than-' @@ -347,18 +334,6 @@ function parseAlertStringTime(str: string): number { return number } -function validateAlerts(alerts: (ProbeAlert | string)[]) { - for (const alert of alerts) { - try { - isValidProbeAlert(alert) - } catch { - throw new Error( - `Probe alert format is invalid! (${JSON.stringify(alert, null, 1)})` - ) - } - } -} - function isValidProbeAlert(alert: ProbeAlert | string) { if (typeof alert !== 'string') { const expression = alert.assertion || alert.query || '' From 44d03dce6f69cfa73d9ab380402cf3a09f77a1a6 Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Tue, 14 Nov 2023 19:12:03 +0700 Subject: [PATCH 4/7] fix: set default alert for Symon --- .../config/validation/validator/probe.test.ts | 14 +++++++++++--- .../config/validation/validator/probe.ts | 6 ++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/config/validation/validator/probe.test.ts b/src/components/config/validation/validator/probe.test.ts index 31738b2f8..ce1c512b2 100644 --- a/src/components/config/validation/validator/probe.test.ts +++ b/src/components/config/validation/validator/probe.test.ts @@ -171,7 +171,7 @@ describe('Probe validation', () => { expect(validateProbes([probe])).to.eventually.throw() }) - it.only('should skips the probe if alert assertion is invalid', async () => { + it('should set the alerts empty if the probe if alert assertion is invalid in Symon mode', async () => { // arrange setContext({ flags: { @@ -198,10 +198,18 @@ describe('Probe validation', () => { ] as unknown as Probe[] // act - const validatedProbe = await validateProbes(probes) + const validatedProbes = await validateProbes(probes) // assert - expect(validatedProbe).to.deep.eq(probes.filter((_, index) => index > 0)) + expect( + validatedProbes.find(({ id }) => id === 'Example')?.alerts + ).deep.eq([]) + expect( + validatedProbes.find(({ id }) => id === 'Example 2')?.alerts + ).deep.eq([{ assertion: 'response.time > 1000' }]) + expect( + validatedProbes.find(({ id }) => id === 'Example 3')?.alerts + ).deep.eq([{ assertion: 'response.status == 200' }]) resetContext() }) diff --git a/src/components/config/validation/validator/probe.ts b/src/components/config/validation/validator/probe.ts index 91dfa933f..bcd8bdbea 100644 --- a/src/components/config/validation/validator/probe.ts +++ b/src/components/config/validation/validator/probe.ts @@ -30,6 +30,7 @@ import { getContext } from '../../../../context' import { FAILED_REQUEST_ASSERTION } from '../../../../looper' import { compileExpression } from '../../../../utils/expression-parser' import { isValidURL } from '../../../../utils/is-valid-url' +import { log } from '../../../../utils/pino' import { DEFAULT_INCIDENT_THRESHOLD, DEFAULT_INTERVAL, @@ -58,9 +59,10 @@ export async function validateProbes(probes: Probe[]): Promise { .custom((alert) => { try { isValidProbeAlert(alert) - } catch { + } catch (error: unknown) { if (isSymonModeFrom(getContext().flags)) { - return + log.error((error as Error).message) + return '' } throw new Error( From 4821fd6f5669247b545a6c8d17f8d52ac8966b2f Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Tue, 14 Nov 2023 19:35:53 +0700 Subject: [PATCH 5/7] fix: handle empty alert assertion --- src/components/config/validation/validator/probe.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/config/validation/validator/probe.ts b/src/components/config/validation/validator/probe.ts index bcd8bdbea..7b94add16 100644 --- a/src/components/config/validation/validator/probe.ts +++ b/src/components/config/validation/validator/probe.ts @@ -61,7 +61,7 @@ export async function validateProbes(probes: Probe[]): Promise { isValidProbeAlert(alert) } catch (error: unknown) { if (isSymonModeFrom(getContext().flags)) { - log.error((error as Error).message) + log.error((error as Error)?.message) return '' } @@ -338,7 +338,11 @@ function parseAlertStringTime(str: string): number { function isValidProbeAlert(alert: ProbeAlert | string) { if (typeof alert !== 'string') { - const expression = alert.assertion || alert.query || '' + const expression = alert.assertion || alert.query + if (!expression) { + return + } + const data = { response: { size: 0, From e2270660e051f6b6b65e3f4f3f19b8a43f5b93db Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Wed, 15 Nov 2023 10:39:23 +0700 Subject: [PATCH 6/7] fix: validate legacy alerts --- .../config/validation/validator/probe.ts | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/components/config/validation/validator/probe.ts b/src/components/config/validation/validator/probe.ts index 7b94add16..5213c7f1a 100644 --- a/src/components/config/validation/validator/probe.ts +++ b/src/components/config/validation/validator/probe.ts @@ -37,9 +37,24 @@ import { DEFAULT_RECOVERY_THRESHOLD, } from './default-values' +const ALERT_QUERY = 'status-not-2xx' +const RESPONSE_TIME_PREFIX = 'response-time-greater-than-' + export async function validateProbes(probes: Probe[]): Promise { const alertSchema = joi.alternatives().try( - joi.string(), + // Legacy alerts + joi.string().custom((alert) => { + if (alert === ALERT_QUERY) { + return alert + } + + if (alert.startsWith(RESPONSE_TIME_PREFIX)) { + parseAlertStringTime(alert) + return alert + } + + throw new Error(`Probe alert format is invalid! (${alert})`) + }), joi .object({ assertion: joi @@ -316,9 +331,6 @@ function isProbeRequestExists(probe: Probe) { ) } -const ALERT_QUERY = 'status-not-2xx' -const RESPONSE_TIME_PREFIX = 'response-time-greater-than-' - // parse string like "response-time-greater-than-200-ms" and return the time in ms function parseAlertStringTime(str: string): number { // match any string that ends with digits followed by unit 's' or 'ms' @@ -336,39 +348,24 @@ function parseAlertStringTime(str: string): number { return number } -function isValidProbeAlert(alert: ProbeAlert | string) { - if (typeof alert !== 'string') { - const expression = alert.assertion || alert.query - if (!expression) { - return - } - - const data = { - response: { - size: 0, - status: 200, - time: 0, - body: '', - headers: {}, - }, - } - const filter = compileExpression(expression, Object.keys(data)) - - filter(data) +function isValidProbeAlert(alert: ProbeAlert) { + const expression = alert.assertion || alert.query + if (!expression) { return } - // Legacy alerts - if (alert === ALERT_QUERY) { - return - } - - if (alert.startsWith(RESPONSE_TIME_PREFIX)) { - parseAlertStringTime(alert) - return + const data = { + response: { + size: 0, + status: 200, + time: 0, + body: '', + headers: {}, + }, } + const filter = compileExpression(expression, Object.keys(data)) - throw new Error('Unknown alert') + filter(data) } function transformDeprecatedAlerts(probes: Probe[]) { From a5e610deacc6bcaea8903cfbacf1bcb47bf8eb3d Mon Sep 17 00:00:00 2001 From: haricnugraha Date: Wed, 15 Nov 2023 10:49:09 +0700 Subject: [PATCH 7/7] refactor: move legacy alert tranformation to Joi --- .../config/validation/validator/probe.test.ts | 2 +- .../config/validation/validator/probe.ts | 57 +++++-------------- 2 files changed, 14 insertions(+), 45 deletions(-) diff --git a/src/components/config/validation/validator/probe.test.ts b/src/components/config/validation/validator/probe.test.ts index ce1c512b2..231604b02 100644 --- a/src/components/config/validation/validator/probe.test.ts +++ b/src/components/config/validation/validator/probe.test.ts @@ -203,7 +203,7 @@ describe('Probe validation', () => { // assert expect( validatedProbes.find(({ id }) => id === 'Example')?.alerts - ).deep.eq([]) + ).deep.eq(['']) expect( validatedProbes.find(({ id }) => id === 'Example 2')?.alerts ).deep.eq([{ assertion: 'response.time > 1000' }]) diff --git a/src/components/config/validation/validator/probe.ts b/src/components/config/validation/validator/probe.ts index 5213c7f1a..28ba084b5 100644 --- a/src/components/config/validation/validator/probe.ts +++ b/src/components/config/validation/validator/probe.ts @@ -45,12 +45,21 @@ export async function validateProbes(probes: Probe[]): Promise { // Legacy alerts joi.string().custom((alert) => { if (alert === ALERT_QUERY) { - return alert + return { + id: uuid(), + assertion: 'response.status < 200 or response.status > 299', + message: 'HTTP status is {{ response.status }}, expecting 2xx', + } } if (alert.startsWith(RESPONSE_TIME_PREFIX)) { - parseAlertStringTime(alert) - return alert + const expectedTime = parseAlertStringTime(alert) + + return { + id: uuid(), + assertion: `response.time > ${expectedTime}`, + message: `Response time is {{ response.time }}ms, expecting less than ${expectedTime}ms`, + } } throw new Error(`Probe alert format is invalid! (${alert})`) @@ -308,11 +317,9 @@ export async function validateProbes(probes: Probe[]): Promise { ) try { - const validatedProbes = await schema.validateAsync(probes, { + return await schema.validateAsync(probes, { stripUnknown: true, }) - - return transformDeprecatedAlerts(validatedProbes) } catch (error: any) { throw new Error(`Monika configuration is invalid. Probe: ${error?.message}`) } @@ -367,41 +374,3 @@ function isValidProbeAlert(alert: ProbeAlert) { filter(data) } - -function transformDeprecatedAlerts(probes: Probe[]) { - return probes.map((probe) => { - const newAlerts = [] - - if (!probe?.alerts) { - return probe - } - - for (const alert of probe.alerts) { - if (typeof alert !== 'string') { - newAlerts.push(alert) - continue - } - - if (alert === ALERT_QUERY) { - newAlerts.push({ - id: uuid(), - assertion: 'response.status < 200 or response.status > 299', - message: 'HTTP status is {{ response.status }}, expecting 2xx', - }) - continue - } - - if ((alert as string).startsWith(RESPONSE_TIME_PREFIX)) { - const expectedTime = parseAlertStringTime(alert) - - newAlerts.push({ - id: uuid(), - assertion: `response.time > ${expectedTime}`, - message: `Response time is {{ response.time }}ms, expecting less than ${expectedTime}ms`, - }) - } - } - - return { ...probe, alerts: newAlerts } - }) -}