import child_process from 'child_process' import { createHash } from 'crypto' import { convertToAttr, marshall, unmarshall } from '@aws-sdk/util-dynamodb' import stringify from 'json-stringify-safe' /** * Utility class containing various static methods for common operations. */ export default class Utils { /** * Checks if the application is running in a hybridless container. * @returns {boolean} - True if the application is running in a hybridless container, false otherwise. */ public static isHybridlessContainer(): boolean { return process.env.HYBRIDLESS_RUNTIME == 'true' } /** * Checks if a given string is valid. * @param {string} string - The string to check. * @returns {boolean} - True if the string is valid, false otherwise. */ public static isValidString(string: string): boolean { return string?.length > 0 && !Array.isArray(string) } /** * Parses a string into an integer and returns null if the string is not a valid number. * @param {string} str - The string to parse into an integer. * @returns {number | null} - The parsed integer or null if the string is not a valid number. */ public static parseIntNullIfNaN(str?: string): number | null { const n = parseInt(str || '') return isNaN(n) ? null : n } /** * Parses a JSON string and returns the resulting object. If the string is empty or * cannot be parsed, null is returned. * @param {string} string - The JSON string to parse. * @returns {any | null} - The parsed object or null if the string is empty or invalid. */ public static parseObjectNullIfEmpty(string: string | undefined): any | null { let o = null try { o = string ? JSON.parse(string) : null if (o && Object.keys(o).length <= 0) o = null } catch (e) { /* empty */ } return o } /** * Checks if a given value is a valid number. * @param {string} number - The value to be checked. * @returns {boolean} - True if the value is a valid number, false otherwise. */ public static isValidNumber(number: string): boolean { let validNumb = NaN try { validNumb = parseInt(number + '') } catch (e) { console.error('Error while validating number', e) } return !isNaN(validNumb) && !Array.isArray(number) } /** * Retrieves the value from an object using a case-insensitive key lookup. * @param {any} obj - The object to search for the key. * @param {string} key - The key to search for in the object. * @returns {any | null} The value associated with the key, or null if the key is not found. */ public static caseInsensitiveObjectForKey(obj: any, key: string): any | null { if (!obj) return null const insensitiveKey = Object.keys(obj).find(k => k.toLowerCase() === key.toLowerCase()) if (insensitiveKey && insensitiveKey != '') return obj[insensitiveKey] return null } /** * Cleans out the /tmp directory asynchronously. */ public static async cleanTemporaryFolder() { return new Promise(resolve => { try { child_process.execSync('rm -rf /tmp/*') console.log('Cleaned tmp folder') } catch (err) { console.error('Error while cleaning tmp folder', err) } finally { resolve() } }) } /** * Marshalls the given item into a DynamoDB format. * If the item is an array, it maps over each element and marshalls it recursively. * If the item is an object, it marshalls the object using the marshall function with options to remove undefined values and convert class instances to maps. * If the item is neither an array nor an object, it converts the item to an attribute. * @param {any} item - The item to be marshalled. * @returns The marshalled item in DynamoDB format. */ public static ddbMarshall(item: T, rec?: boolean) { if (Array.isArray(item)) return { L: item.map(_i => this.ddbMarshall(_i, true)) } else if (typeof item === 'object' && isNaN(parseInt(item as any))) { const marshalled = marshall(item, { removeUndefinedValues: true, convertClassInstanceToMap: true, }) if (rec) return { M: marshalled } else return marshalled } else return convertToAttr(item, { removeUndefinedValues: true, convertClassInstanceToMap: true }) } /** * Recursively unmarshalls a DynamoDB item by converting it into a plain JavaScript object. * @param {any} item - The DynamoDB item to unmarshall. * @returns {any} The unmarshalled JavaScript object. */ public static ddbUnmarshall(item) { if (!item && item !== false) return null if (Array.isArray(item)) { return item.map(_item => this.ddbUnmarshall(_item)) } else if (typeof item === 'object') { return unmarshall(item) } return item } /** * helper that hashes values using SHA-256. * @param {unknown} raw - The raw item for conversion. * @returns {string} The hashed string. */ public static hashValue(raw: unknown): string { const s = typeof raw === 'string' ? raw : stringify(raw) return createHash('sha256').update(s).digest('hex') } }