From 00d93c33b402295c21af3f823d552e7a8af648ad Mon Sep 17 00:00:00 2001 From: baldo Date: Wed, 24 Aug 2022 18:20:49 +0200 Subject: [PATCH] Introduce NodeMonitoringStateResponse to get rid of some any --- server/resources/monitoringResource.ts | 11 +++-- server/services/monitoringService.ts | 59 ++++++++++++++++++++------ server/shared/types/index.ts | 49 +++++++++++++++++++++ server/types/index.ts | 29 ------------- 4 files changed, 100 insertions(+), 48 deletions(-) diff --git a/server/resources/monitoringResource.ts b/server/resources/monitoringResource.ts index 467b168..e81ec55 100644 --- a/server/resources/monitoringResource.ts +++ b/server/resources/monitoringResource.ts @@ -11,23 +11,22 @@ import { JSONObject, MonitoringResponse, MonitoringToken, + NodeMonitoringStateResponse, toMonitoringResponse, } from "../types"; const isValidToken = forConstraint(CONSTRAINTS.token, false); -// FIXME: Get rid of any -async function doGetAll(req: Request): Promise<{ total: number; result: any }> { +async function doGetAll( + req: Request +): Promise<{ total: number; result: NodeMonitoringStateResponse[] }> { const restParams = await Resources.getValidRestParams("list", null, req); const { monitoringStates, total } = await MonitoringService.getAll( restParams ); return { total, - result: monitoringStates.map((state) => { - state.mapId = state.mac.toLowerCase().replace(/:/g, ""); - return state; - }), + result: monitoringStates, }; } diff --git a/server/services/monitoringService.ts b/server/services/monitoringService.ts index ef6bb56..6fddf29 100644 --- a/server/services/monitoringService.ts +++ b/server/services/monitoringService.ts @@ -22,7 +22,9 @@ import { Hostname, isBoolean, isDomain, + isMailType, isMonitoringSortField, + isMonitoringState, isOnlineState, isPlainObject, isSite, @@ -30,12 +32,17 @@ import { isUndefined, JSONValue, MAC, + MailId, MailType, + MapId, + mapIdFromMAC, MonitoringSortField, MonitoringState, MonitoringToken, NodeId, + NodeMonitoringStateResponse, NodeStateData, + NodeStateId, OnlineState, parseJSON, RunResult, @@ -55,13 +62,13 @@ import { } from "../utils/time"; type NodeStateRow = { - id: number; + id: NodeStateId; created_at: UnixTimestampSeconds; domain: Domain | null; hostname: Hostname | null; import_timestamp: UnixTimestampSeconds; last_seen: UnixTimestampSeconds; - last_status_mail_sent: string | null; + last_status_mail_sent: UnixTimestampSeconds | null; last_status_mail_type: string | null; mac: MAC; modified_at: UnixTimestampSeconds; @@ -356,7 +363,7 @@ export function parseNodesJson(body: string): NodesParsingResult { } async function updateSkippedNode( - id: NodeId, + id: NodeStateId, node?: StoredNode ): Promise { return await db.run( @@ -370,7 +377,7 @@ async function updateSkippedNode( async function sendMonitoringMailsBatched( name: string, mailType: MailType, - findBatchFun: () => Promise + findBatchFun: () => Promise ): Promise { Logger.tag("monitoring", "mail-sending").debug( 'Sending "%s" mails...', @@ -477,8 +484,8 @@ async function sendOnlineAgainMails( await sendMonitoringMailsBatched( "online again", MailType.MONITORING_ONLINE_AGAIN, - async (): Promise => - await db.all( + async (): Promise => + await db.all( "SELECT * FROM node_state " + "WHERE modified_at < ? AND state = ? AND last_status_mail_type IN (" + "'monitoring-offline-1', 'monitoring-offline-2', 'monitoring-offline-3'" + @@ -497,7 +504,7 @@ async function sendOfflineMails( await sendMonitoringMailsBatched( "offline " + mailNumber, mailType, - async (): Promise => { + async (): Promise => { const previousType = mailNumber === 1 ? "monitoring-online-again" @@ -510,7 +517,7 @@ async function sendOfflineMails( const schedule = MONITORING_OFFLINE_MAILS_SCHEDULE[mailNumber]; const scheduledTimeBefore = subtract(now(), schedule); - return await db.all( + return await db.all( "SELECT * FROM node_state " + "WHERE modified_at < ? AND state = ? AND (last_status_mail_type = ?" + allowNull + @@ -667,10 +674,33 @@ async function retrieveNodeInformationForUrls( }; } -// FIXME: Replace any[] by type. +function toResponse(row: NodeStateRow): NodeMonitoringStateResponse { + // TODO: Handle conversion errors. + return { + id: row.id, + created_at: row.created_at, + domain: row.domain || undefined, + hostname: row.hostname || undefined, + import_timestamp: row.import_timestamp, + last_seen: row.last_seen, + last_status_mail_sent: row.last_status_mail_sent || undefined, + last_status_mail_type: isMailType(row.last_status_mail_type) + ? row.last_status_mail_type + : undefined, + mac: row.mac, + modified_at: row.modified_at, + monitoring_state: isMonitoringState(row.monitoring_state) + ? row.monitoring_state + : undefined, + site: row.site || undefined, + state: isOnlineState(row.state) ? row.state : OnlineState.OFFLINE, + mapId: mapIdFromMAC(row.mac), + }; +} + export async function getAll( restParams: RestParams -): Promise<{ total: number; monitoringStates: any[] }> { +): Promise<{ total: number; monitoringStates: NodeMonitoringStateResponse[] }> { const filterFields = [ "hostname", "mac", @@ -695,12 +725,15 @@ export async function getAll( filterFields ); - const monitoringStates = await db.all( + const monitoringStates = await db.all( "SELECT * FROM node_state WHERE " + filter.query, filter.params ); - return { monitoringStates, total }; + return { + monitoringStates: monitoringStates.map(toResponse), + total, + }; } export async function getByMacs( @@ -877,7 +910,7 @@ async function deleteNeverOnlineNodesBefore( const placeholders = macs.map(() => "?").join(","); - const rows: { mac: MAC }[] = await db.all( + const rows = await db.all( `SELECT * FROM node_state WHERE mac IN (${placeholders})`, macs ); diff --git a/server/shared/types/index.ts b/server/shared/types/index.ts index 1b06342..0421aad 100644 --- a/server/shared/types/index.ts +++ b/server/shared/types/index.ts @@ -368,6 +368,12 @@ export const isFastdKey = toIsNewtype(isString, "" as FastdKey); export type MAC = string & { readonly __tag: unique symbol }; export const isMAC = toIsNewtype(isString, "" as MAC); +export type MapId = string & { readonly __tag: unique symbol }; +export const isMapId = toIsNewtype(isString, "" as MapId); +export function mapIdFromMAC(mac: MAC): MapId { + return mac.toLowerCase().replace(/:/g, "") as MapId; +} + export type DurationSeconds = number & { readonly __tag: unique symbol }; export const isDurationSeconds = toIsNewtype(isNumber, NaN as DurationSeconds); @@ -572,6 +578,49 @@ export function isMonitoringResponse(arg: unknown): arg is MonitoringResponse { ); } +export type NodeStateId = number & { readonly __tag: unique symbol }; +export const isNodeStateId = toIsNewtype(isNumber, 0 as NodeStateId); + +export type NodeMonitoringStateResponse = { + id: NodeStateId; + created_at: UnixTimestampSeconds; + domain?: Domain; + hostname?: Hostname; + import_timestamp: UnixTimestampSeconds; + last_seen: UnixTimestampSeconds; + last_status_mail_sent?: UnixTimestampSeconds; + last_status_mail_type?: MailType; + mac: MAC; + modified_at: UnixTimestampSeconds; + monitoring_state?: MonitoringState; + site?: Site; + state: OnlineState; + mapId: MapId; +}; + +export type MailId = number & { readonly __tag: unique symbol }; +export const isMailId = toIsNewtype(isNumber, NaN as MailId); + +export type MailData = JSONObject; + +export enum MailType { + MONITORING_OFFLINE_1 = "monitoring-offline-1", + MONITORING_OFFLINE_2 = "monitoring-offline-2", + MONITORING_OFFLINE_3 = "monitoring-offline-3", + MONITORING_ONLINE_AGAIN = "monitoring-online-again", + MONITORING_CONFIRMATION = "monitoring-confirmation", +} +export const isMailType = toIsEnum(MailType); + +export type Mail = { + id: MailId; + email: MailType; + sender: EmailAddress; + recipient: EmailAddress; + data: MailData; + failures: number; +}; + // noinspection JSUnusedGlobalSymbols enum NodeSortFieldEnum { HOSTNAME = "hostname", diff --git a/server/types/index.ts b/server/types/index.ts index 98375e0..cba4a55 100644 --- a/server/types/index.ts +++ b/server/types/index.ts @@ -2,9 +2,6 @@ import { CreateOrUpdateNode, Domain, DomainSpecificNodeResponse, - EmailAddress, - isNumber, - JSONObject, MonitoringResponse, MonitoringState, MonitoringToken, @@ -13,8 +10,6 @@ import { OnlineState, Site, StoredNode, - toIsEnum, - toIsNewtype, } from "../shared/types"; export * from "./config"; @@ -98,27 +93,3 @@ export function toMonitoringResponse(node: StoredNode): MonitoringResponse { export type NodeSecrets = { monitoringToken?: MonitoringToken; }; - -export type MailId = number & { readonly __tag: unique symbol }; -export const isMailId = toIsNewtype(isNumber, NaN as MailId); - -export type MailData = JSONObject; - -export enum MailType { - MONITORING_OFFLINE_1 = "monitoring-offline-1", - MONITORING_OFFLINE_2 = "monitoring-offline-2", - MONITORING_OFFLINE_3 = "monitoring-offline-3", - MONITORING_ONLINE_AGAIN = "monitoring-online-again", - MONITORING_CONFIRMATION = "monitoring-confirmation", -} - -export const isMailType = toIsEnum(MailType); - -export type Mail = { - id: MailId; - email: MailType; - sender: EmailAddress; - recipient: EmailAddress; - data: MailData; - failures: number; -};