X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Futils%2FUtils.ts;h=f174407500db95556d6d1c04fb0b4c95355923b7;hb=91ecff2da29c7d1930ab1e8d90d159ae7a8c722e;hp=b394713392d2b6c8b90887f16459bd5f36aefc71;hpb=b322b8b4712bd412ea2d1e27924dcc6f1bb08c63;p=e-mobility-charging-stations-simulator.git diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index b3947133..f1744075 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -1,30 +1,40 @@ -import Configuration from './Configuration'; -import { WebSocketCloseEventStatusString } from '../types/WebSocket'; -import { WorkerProcessType } from '../types/Worker'; import crypto from 'crypto'; -import { v4 as uuid } from 'uuid'; + +import clone from 'just-clone'; + +import { WebSocketCloseEventStatusString } from '../types/WebSocket'; export default class Utils { + private constructor() { + // This is intentional + } + public static logPrefix(prefixString = ''): string { return new Date().toLocaleString() + prefixString; } public static generateUUID(): string { - return uuid(); + return crypto.randomUUID(); + } + + public static validateUUID(uuid: string): boolean { + return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test( + uuid + ); } public static async sleep(milliSeconds: number): Promise { - return new Promise((resolve) => setTimeout(resolve, milliSeconds)); + return new Promise((resolve) => setTimeout(resolve as () => void, milliSeconds)); } public static formatDurationMilliSeconds(duration: number): string { duration = Utils.convertToInt(duration); const hours = Math.floor(duration / (3600 * 1000)); - const minutes = Math.floor((duration / 1000 - (hours * 3600)) / 60); - const seconds = duration / 1000 - (hours * 3600) - (minutes * 60); - let hoursStr: string = hours.toString(); - let minutesStr: string = minutes.toString(); - let secondsStr: string = seconds.toString(); + const minutes = Math.floor((duration / 1000 - hours * 3600) / 60); + const seconds = duration / 1000 - hours * 3600 - minutes * 60; + let hoursStr = hours.toString(); + let minutesStr = minutes.toString(); + let secondsStr = seconds.toString(); if (hours < 10) { hoursStr = '0' + hours.toString(); @@ -42,42 +52,42 @@ export default class Utils { return Utils.formatDurationMilliSeconds(duration * 1000); } - public static convertToDate(value: unknown): Date { - // Check - if (!value) { - return value as Date; + public static convertToDate(value: unknown): Date | null | undefined { + if (Utils.isNullOrUndefined(value)) { + return value as null | undefined; + } + if (value instanceof Date) { + return value; } - // Check Type - if (!(value instanceof Date)) { - return new Date(value as string); + if (Utils.isString(value) || typeof value === 'number') { + return new Date(value as string | number); } - return value; + return null; } public static convertToInt(value: unknown): number { - let changedValue: number = value as number; if (!value) { return 0; } + let changedValue: number = value as number; if (Number.isSafeInteger(value)) { return value as number; } - // Check + if (typeof value === 'number') { + changedValue = Math.trunc(value); + } if (Utils.isString(value)) { - // Create Object changedValue = parseInt(value as string); } return changedValue; } public static convertToFloat(value: unknown): number { - let changedValue: number = value as number; if (!value) { return 0; } - // Check + let changedValue: number = value as number; if (Utils.isString(value)) { - // Create Object changedValue = parseFloat(value as string); } return changedValue; @@ -85,32 +95,37 @@ export default class Utils { public static convertToBoolean(value: unknown): boolean { let result = false; - // Check boolean if (value) { // Check the type if (typeof value === 'boolean') { - // Already a boolean result = value; - } else { - // Convert - result = (value === 'true'); + } else if ( + Utils.isString(value) && + ((value as string).toLowerCase() === 'true' || value === '1') + ) { + result = true; + } else if (typeof value === 'number' && value === 1) { + result = true; } } return result; } - public static getRandomFloat(max: number, min = 0, negative = false): number { - if (max < min || min < 0 || max < 0) { + public static getRandomFloat(max = Number.MAX_VALUE, min = 0, negative = false): number { + if (max < min || max < 0 || min < 0) { throw new RangeError('Invalid interval'); } const randomPositiveFloat = crypto.randomBytes(4).readUInt32LE() / 0xffffffff; - const sign = (negative && randomPositiveFloat < 0.5) ? 1 : -1; + const sign = negative && randomPositiveFloat < 0.5 ? -1 : 1; return sign * (randomPositiveFloat * (max - min) + min); } - public static getRandomInt(max: number, min = 0): number { + public static getRandomInteger(max = Number.MAX_SAFE_INTEGER, min = 0): number { + if (max < min || max < 0 || min < 0) { + throw new RangeError('Invalid interval'); + } max = Math.floor(max); - if (min) { + if (!Utils.isNullOrUndefined(min) && min !== 0) { min = Math.ceil(min); return Math.floor(Utils.secureRandom() * (max - min + 1)) + min; } @@ -127,63 +142,82 @@ export default class Utils { return Math.trunc(numberValue * truncPower) / truncPower; } - public static getRandomFloatRounded(max: number, min = 0, scale = 2): number { + public static getRandomFloatRounded(max = Number.MAX_VALUE, min = 0, scale = 2): number { if (min) { return Utils.roundTo(Utils.getRandomFloat(max, min), scale); } return Utils.roundTo(Utils.getRandomFloat(max), scale); } - public static getRandomFloatFluctuatedRounded(staticValue: number, fluctuationPercent: number, scale = 2): number { + public static getRandomFloatFluctuatedRounded( + staticValue: number, + fluctuationPercent: number, + scale = 2 + ): number { if (fluctuationPercent === 0) { return Utils.roundTo(staticValue, scale); } const fluctuationRatio = fluctuationPercent / 100; - return Utils.getRandomFloatRounded(staticValue * (1 + fluctuationRatio), staticValue * (1 - fluctuationRatio), scale); + return Utils.getRandomFloatRounded( + staticValue * (1 + fluctuationRatio), + staticValue * (1 - fluctuationRatio), + scale + ); } - public static cloneObject(object: T): T { - return JSON.parse(JSON.stringify(object)) as T; + public static isObject(item: unknown): boolean { + return item && typeof item === 'object' && Array.isArray(item) === false; + } + + public static cloneObject(object: T): T { + return clone(object); } public static isIterable(obj: T): boolean { - if (obj) { - return typeof obj[Symbol.iterator] === 'function'; - } - return false; + return obj ? typeof obj[Symbol.iterator] === 'function' : false; } public static isString(value: unknown): boolean { return typeof value === 'string'; } + public static isEmptyString(value: unknown): boolean { + return Utils.isString(value) && (value as string).trim().length === 0; + } + public static isUndefined(value: unknown): boolean { return typeof value === 'undefined'; } public static isNullOrUndefined(value: unknown): boolean { - // eslint-disable-next-line no-eq-null, eqeqeq - if (value == null) { - return true; - } - return false; + // eslint-disable-next-line eqeqeq, no-eq-null + return value == null ? true : false; } - public static isEmptyArray(object: unknown): boolean { - if (!object) { + public static isEmptyArray(object: unknown | unknown[]): boolean { + if (!Array.isArray(object)) { return true; } - if (Array.isArray(object) && object.length > 0) { + if (object.length > 0) { return false; } return true; } - public static isEmptyObject(obj: Record): boolean { - return !Object.keys(obj).length; + public static isEmptyObject(obj: object): boolean { + if (obj?.constructor !== Object) { + return false; + } + // Iterates over the keys of an object, if + // any exist, return false. + for (const _ in obj) { + return false; + } + return true; } - public static insertAt = (str: string, subStr: string, pos: number): string => `${str.slice(0, pos)}${subStr}${str.slice(pos)}`; + public static insertAt = (str: string, subStr: string, pos: number): string => + `${str.slice(0, pos)}${subStr}${str.slice(pos)}`; /** * @param [retryNumber=0] @@ -195,6 +229,54 @@ export default class Utils { return delay + randomSum; } + public static async promiseWithTimeout( + promise: Promise, + timeoutMs: number, + timeoutError: Error, + timeoutCallback: () => void = () => { + /* This is intentional */ + } + ): Promise { + // Create a timeout promise that rejects in timeout milliseconds + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + timeoutCallback(); + reject(timeoutError); + }, timeoutMs); + }); + + // Returns a race between timeout promise and the passed promise + return Promise.race([promise, timeoutPromise]); + } + + /** + * Generate a cryptographically secure random number in the [0,1[ range + * + * @returns + */ + public static secureRandom(): number { + return crypto.randomBytes(4).readUInt32LE() / 0x100000000; + } + + public static JSONStringifyWithMapSupport( + obj: Record | Record[], + space?: number + ): string { + return JSON.stringify( + obj, + (key, value: Record) => { + if (value instanceof Map) { + return { + dataType: 'Map', + value: [...value], + }; + } + return value; + }, + space + ); + } + /** * Convert websocket error code to human readable string message * @@ -220,21 +302,4 @@ export default class Utils { } return '(Unknown)'; } - - public static workerPoolInUse(): boolean { - return [WorkerProcessType.DYNAMIC_POOL, WorkerProcessType.STATIC_POOL].includes(Configuration.getWorkerProcess()); - } - - public static workerDynamicPoolInUse(): boolean { - return Configuration.getWorkerProcess() === WorkerProcessType.DYNAMIC_POOL; - } - - /** - * Generate a cryptographically secure random number in the [0,1[ range - * - * @returns - */ - public static secureRandom(): number { - return crypto.randomBytes(4).readUInt32LE() / 0x100000000; - } }