From bfd6ca1d26ec7d869addf39d201caac7eda497de Mon Sep 17 00:00:00 2001 From: baldo Date: Tue, 23 Aug 2022 21:38:37 +0200 Subject: [PATCH] ESLint: Fix more warnings and errors. --- server/resources/nodeResource.ts | 21 +++++++---- server/services/mailService.ts | 24 +++++++------ server/services/mailTemplateService.ts | 3 ++ server/services/monitoringService.test.ts | 9 ----- server/services/monitoringService.ts | 23 ++++++------ server/services/nodeService.ts | 3 +- server/shared/types/index.ts | 11 ++++++ server/shared/validation/validator.ts | 3 ++ server/utils/resources.ts | 43 +++++++++++++---------- 9 files changed, 84 insertions(+), 56 deletions(-) diff --git a/server/resources/nodeResource.ts b/server/resources/nodeResource.ts index 5239680..45964c6 100644 --- a/server/resources/nodeResource.ts +++ b/server/resources/nodeResource.ts @@ -10,6 +10,7 @@ import { Request, Response } from "express"; import { CreateOrUpdateNode, DomainSpecificNodeResponse, + filterUndefinedFromJSON, isCreateOrUpdateNode, isNodeSortField, isString, @@ -108,7 +109,7 @@ export const get = handleJSONWithData(async (data) => { async function doGetAll( req: Request -): Promise<{ total: number; pageNodes: any }> { +): Promise<{ total: number; pageNodes: DomainSpecificNodeResponse[] }> { const restParams = await Resources.getValidRestParams("list", "node", req); const nodes = await NodeService.getAllNodes(); @@ -159,9 +160,17 @@ async function doGetAll( export function getAll(req: Request, res: Response): void { doGetAll(req) - .then((result: { total: number; pageNodes: any[] }) => { - res.set("X-Total-Count", result.total.toString(10)); - return Resources.success(res, result.pageNodes); - }) - .catch((err: any) => Resources.error(res, err)); + .then( + (result: { + total: number; + pageNodes: DomainSpecificNodeResponse[]; + }) => { + res.set("X-Total-Count", result.total.toString(10)); + return Resources.success( + res, + result.pageNodes.map(filterUndefinedFromJSON) + ); + } + ) + .catch((err) => Resources.error(res, err)); } diff --git a/server/services/mailService.ts b/server/services/mailService.ts index ab653df..f45f535 100644 --- a/server/services/mailService.ts +++ b/server/services/mailService.ts @@ -196,21 +196,23 @@ export async function sendPendingMails(): Promise { const startTime = moment(); - while (true) { + let pendingMails = await findPendingMailsBefore( + startTime, + MAIL_QUEUE_DB_BATCH_SIZE + ); + + while (!_.isEmpty(pendingMails)) { Logger.tag("mail", "queue").debug("Sending next batch..."); - const pendingMails = await findPendingMailsBefore( - startTime, - MAIL_QUEUE_DB_BATCH_SIZE - ); - - if (_.isEmpty(pendingMails)) { - Logger.tag("mail", "queue").debug("Done sending pending mails."); - return; - } - for (const pendingMail of pendingMails) { await sendPendingMail(pendingMail); } + + pendingMails = await findPendingMailsBefore( + startTime, + MAIL_QUEUE_DB_BATCH_SIZE + ); } + + Logger.tag("mail", "queue").debug("Done sending pending mails."); } diff --git a/server/services/mailTemplateService.ts b/server/services/mailTemplateService.ts index 1ca457a..9605115 100644 --- a/server/services/mailTemplateService.ts +++ b/server/services/mailTemplateService.ts @@ -21,6 +21,7 @@ const templateFunctions: { | ((unix: number) => string); } = {}; +// eslint-disable-next-line @typescript-eslint/no-explicit-any function renderSnippet(this: any, name: string, data: MailData): string { const snippetFile = snippetsBasePath + "/" + name + ".html"; @@ -34,7 +35,9 @@ function renderSnippet(this: any, name: string, data: MailData): string { ); } +// eslint-disable-next-line @typescript-eslint/no-explicit-any function snippet(name: string): (this: any, data: MailData) => string { + // eslint-disable-next-line @typescript-eslint/no-explicit-any return function (this: any, data: MailData): string { return renderSnippet.bind(this)(name, data); }; diff --git a/server/services/monitoringService.test.ts b/server/services/monitoringService.test.ts index 8e8d5da..cbb8e59 100644 --- a/server/services/monitoringService.test.ts +++ b/server/services/monitoringService.test.ts @@ -25,15 +25,6 @@ beforeEach(() => { mockedLogger.reset(); }); -test("parseNode() should fail parsing node for undefined node data", () => { - // given - const importTimestamp = now(); - const nodeData = undefined; - - // then - expect(() => parseNode(importTimestamp, nodeData)).toThrowError(); -}); - test("parseNode() should fail parsing node for empty node data", () => { // given const importTimestamp = now(); diff --git a/server/services/monitoringService.ts b/server/services/monitoringService.ts index 4e44acb..ef6bb56 100644 --- a/server/services/monitoringService.ts +++ b/server/services/monitoringService.ts @@ -24,9 +24,11 @@ import { isDomain, isMonitoringSortField, isOnlineState, + isPlainObject, isSite, isString, isUndefined, + JSONValue, MAC, MailType, MonitoringSortField, @@ -35,6 +37,7 @@ import { NodeId, NodeStateData, OnlineState, + parseJSON, RunResult, Site, StoredNode, @@ -206,13 +209,13 @@ const isValidMac = forConstraint(CONSTRAINTS.node.mac, false); export function parseNode( importTimestamp: UnixTimestampSeconds, - nodeData: any + nodeData: JSONValue ): ParsedNode { - if (!_.isPlainObject(nodeData)) { + if (!isPlainObject(nodeData)) { throw new Error("Unexpected node type: " + typeof nodeData); } - if (!_.isPlainObject(nodeData.nodeinfo)) { + if (!isPlainObject(nodeData.nodeinfo)) { throw new Error( "Unexpected nodeinfo type: " + typeof nodeData.nodeinfo ); @@ -225,7 +228,7 @@ export function parseNode( ); } - if (!_.isPlainObject(nodeData.nodeinfo.network)) { + if (!isPlainObject(nodeData.nodeinfo.network)) { throw new Error( "Node " + nodeId + @@ -239,9 +242,9 @@ export function parseNode( "Node " + nodeId + ": Invalid MAC: " + nodeData.nodeinfo.network.mac ); } - const mac = normalizeMac(nodeData.nodeinfo.network.mac) as MAC; + const mac = normalizeMac(nodeData.nodeinfo.network.mac as MAC); - if (!_.isPlainObject(nodeData.flags)) { + if (!isPlainObject(nodeData.flags)) { throw new Error( "Node " + nodeId + @@ -271,7 +274,7 @@ export function parseNode( let site: Site | undefined; if ( - _.isPlainObject(nodeData.nodeinfo.system) && + isPlainObject(nodeData.nodeinfo.system) && isSite(nodeData.nodeinfo.system.site_code) ) { site = nodeData.nodeinfo.system.site_code; @@ -279,7 +282,7 @@ export function parseNode( let domain: Domain | undefined; if ( - _.isPlainObject(nodeData.nodeinfo.system) && + isPlainObject(nodeData.nodeinfo.system) && isDomain(nodeData.nodeinfo.system.domain_code) ) { domain = nodeData.nodeinfo.system.domain_code; @@ -300,9 +303,9 @@ export function parseNodesJson(body: string): NodesParsingResult { "Parsing nodes.json..." ); - const json = JSON.parse(body); + const json = parseJSON(body); - if (!_.isPlainObject(json)) { + if (!isPlainObject(json)) { throw new Error( `Expecting a JSON object as the nodes.json root, but got: ${typeof json}` ); diff --git a/server/services/nodeService.ts b/server/services/nodeService.ts index b931c76..3e9c06e 100644 --- a/server/services/nodeService.ts +++ b/server/services/nodeService.ts @@ -405,7 +405,7 @@ function setNodeValue( case LINE_PREFIX.TOKEN: node.token = value as Token; break; - case LINE_PREFIX.MONITORING: + case LINE_PREFIX.MONITORING: { const active = value === "aktiv"; const pending = value === "pending"; node.monitoringState = active @@ -414,6 +414,7 @@ function setNodeValue( ? MonitoringState.PENDING : MonitoringState.DISABLED; break; + } case LINE_PREFIX.MONITORING_TOKEN: nodeSecrets.monitoringToken = value as MonitoringToken; break; diff --git a/server/shared/types/index.ts b/server/shared/types/index.ts index faa8fe8..1b06342 100644 --- a/server/shared/types/index.ts +++ b/server/shared/types/index.ts @@ -77,6 +77,10 @@ export function isObject(arg: unknown): arg is object { return arg !== null && typeof arg === "object"; } +export function isPlainObject(arg: unknown): arg is { [key: string]: unknown } { + return isObject(arg) && !Array.isArray(arg); +} + export function hasOwnProperty( arg: unknown, key: Key @@ -84,6 +88,13 @@ export function hasOwnProperty( return isObject(arg) && key in arg; } +export function getFieldIfExists( + arg: unknown, + key: PropertyKey +): unknown | undefined { + return hasOwnProperty(arg, key) ? arg[key] : undefined; +} + export function isArray(arg: unknown, isT: TypeGuard): arg is Array { if (!Array.isArray(arg)) { return false; diff --git a/server/shared/validation/validator.ts b/server/shared/validation/validator.ts index 572b7c8..02940cd 100644 --- a/server/shared/validation/validator.ts +++ b/server/shared/validation/validator.ts @@ -25,6 +25,9 @@ export interface Constraint { } export type Constraints = { [key: string]: Constraint }; +export type NestedConstraints = { + [key: string]: Constraint | Constraints | NestedConstraints; +}; export type Values = { [key: string]: unknown }; export function isConstraint(arg: unknown): arg is Constraint { diff --git a/server/utils/resources.ts b/server/utils/resources.ts index 3ef0335..c57e945 100644 --- a/server/utils/resources.ts +++ b/server/utils/resources.ts @@ -7,12 +7,14 @@ import { Constraints, forConstraints, isConstraints, + NestedConstraints, } from "../shared/validation/validator"; import { Request, Response } from "express"; import { EnumTypeGuard, EnumValue, type GenericSortField, + getFieldIfExists, isJSONObject, isNumber, isString, @@ -26,7 +28,7 @@ import { export type RequestData = JSONObject; export type RequestHandler = (request: Request, response: Response) => void; -export type Entity = { [key: string]: any }; +export type Entity = { [key: string]: unknown }; export type RestParams = { q?: string; @@ -180,7 +182,7 @@ export async function getValidRestParams( subtype: string | null, req: Request ): Promise { - const restConstraints = CONSTRAINTS.rest as { [key: string]: any }; + const restConstraints = CONSTRAINTS.rest as { [key: string]: Constraints }; if (!(type in restConstraints) || !isConstraints(restConstraints[type])) { Logger.tag("validation", "rest").error( "Unknown REST resource type: {}", @@ -193,18 +195,16 @@ export async function getValidRestParams( let filterConstraints: Constraints = {}; if (subtype) { const subtypeFilters = subtype + "Filters"; - const constraintsObj = CONSTRAINTS as { [key: string]: any }; - if ( - !(subtypeFilters in constraintsObj) || - !isConstraints(constraintsObj[subtypeFilters]) - ) { + const nestedConstraints = CONSTRAINTS as NestedConstraints; + const subConstraints = nestedConstraints[subtypeFilters]; + if (!isConstraints(subConstraints)) { Logger.tag("validation", "rest").error( "Unknown REST resource subtype: {}", subtype ); throw { data: "Internal error.", type: ErrorTypes.internalError }; } - filterConstraints = constraintsObj[subtypeFilters]; + filterConstraints = subConstraints; } const data = getData(req); @@ -232,7 +232,7 @@ export function filter( query = query.trim().toLowerCase(); } - function queryMatches(entity: Entity): boolean { + function queryMatches(entity: E): boolean { if (!query) { return true; } @@ -240,7 +240,7 @@ export function filter( if (!query) { return true; } - let value = entity[field]; + let value = getFieldIfExists(entity, field); if (isNumber(value)) { value = value.toString(); } @@ -249,21 +249,21 @@ export function filter( return false; } - value = value.toLowerCase(); + const lowerCaseValue = value.toLowerCase(); if (field === "mac") { return _.includes( - value.replace(/:/g, ""), + lowerCaseValue.replace(/:/g, ""), query.replace(/:/g, "") ); } - return _.includes(value, query); + return _.includes(lowerCaseValue, query); }); } const filters = restParams.filters; - function filtersMatch(entity: Entity): boolean { + function filtersMatch(entity: E): boolean { if (isUndefined(filters) || _.isEmpty(filters)) { return true; } @@ -275,9 +275,13 @@ export function filter( if (key.startsWith("has")) { const entityKey = key.substring(3, 4).toLowerCase() + key.substring(4); - return _.isEmpty(entity[entityKey]).toString() !== value; + return ( + _.isEmpty( + getFieldIfExists(entity, entityKey) + ).toString() !== value + ); } - return entity[key] === value; + return getFieldIfExists(entity, key) === value; }); } @@ -287,7 +291,8 @@ export function filter( } export function sort< - Type extends { [Key in SortField]: unknown }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Type extends { [Key in SortField]: any }, SortField extends string >( entities: Type[], @@ -303,8 +308,8 @@ export function sort< const sorted = entities.slice(0); sorted.sort((a, b) => { - let as: any = a[sortField]; - let bs: any = b[sortField]; + let as = a[sortField]; + let bs = b[sortField]; if (isString(as)) { as = as.toLowerCase();