/* Copyright (C) 2017 Cloudbase Solutions SRL This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import configLoader from "./Config"; export type FileContent = { name: string; content: string; }; class ObjectUtils { static notEmpty(value: T | null | undefined): value is T { return value != null; } static flatten( object: any, appendParentPath?: boolean, parent?: string ): any { let result: any = {}; Object.keys(object).forEach(k => { if (typeof object[k] === "object" && !Array.isArray(object[k])) { if (object[k]) { result = { ...result, ...this.flatten(object[k], appendParentPath, k), }; } } else { let key = k; if (appendParentPath && parent) { key = `${parent}/${k}`; } result[key] = object[k]; } }); if (Object.keys(result).length === 0) { return null; } return result; } static skipFields(object: any, fieldNames: string[]) { const result: any = {}; if (Object.keys(object).length === 0) { return null; } Object.keys(object).forEach(k => { if (!fieldNames.find(fn => fn === k)) { result[k] = object[k]; } }); if (Object.keys(result).length === 0) { return null; } return result; } static async wait(ms: number) { return new Promise(r => { setTimeout(() => r(), ms); }); } static async waitFor( predicate: () => boolean, options?: { timeoutMs?: number; intervalMs?: number; silent?: boolean; } ) { const { timeoutMs = 15000, intervalMs = 1000, silent } = options || {}; const startTime = new Date().getTime(); const testLoop = async () => { if (predicate()) { return; } if (new Date().getTime() - startTime > timeoutMs) { if (!silent) { throw new Error(`Timeout: waiting for more than ${timeoutMs} ms`); } console.log(`Timeout: waiting for more than ${timeoutMs} ms`); return; } await this.wait(intervalMs); await testLoop(); }; await testLoop(); } static trim(fieldName: string, value: any): any { const isPassword = configLoader.config.passwordFields.find(p => p === fieldName) || fieldName.toLowerCase().indexOf("password") > -1; return typeof value === "string" && !isPassword ? value.trim() : value; } static capitalizeFirstLetter(value: string): string { return value.charAt(0).toUpperCase() + value.slice(1); } static async retry( retryFunction: () => Promise, retryEvery = 1000, retryCount = 3 ): Promise { let currentTry = 0; const retryLoop = async (): Promise => { try { currentTry += 1; if (currentTry > 1) { console.log("Retrying... ", currentTry); } const result = await retryFunction(); return result; } catch (err) { if (currentTry >= retryCount) { console.error(`Retry failed after ${retryCount} attempts.`); throw err; } await this.wait(retryEvery); return retryLoop(); } }; return retryLoop(); } static isObject(item: any) { return item && typeof item === "object" && !Array.isArray(item); } static mergeDeep(target: any, ...sources: any[]): any { if (!sources.length) { return target; } const source = sources.shift(); if (this.isObject(target) && this.isObject(source)) { Object.keys(source).forEach(key => { if (this.isObject(source[key])) { if (!target[key]) { Object.assign(target, { [key]: {} }); } this.mergeDeep(target[key], source[key]); } else { Object.assign(target, { [key]: source[key] }); } }); } return this.mergeDeep(target, ...sources); } static clone(obj: any) { return JSON.parse(JSON.stringify(obj)); } } export default ObjectUtils;