import { extend } from 'lodash'; import { format } from 'date-fns'; /** * @export * @class EntityUtils */ export class EntityUtils { /** * @static * @param {any} obj * @param {any} f * @param {any} [ctx=null] * @returns * * @memberOf EntityUtils */ public static deepMap(obj, f, ctx = null) { if (Array.isArray(obj)) { return obj.map(function (val, key) { return (typeof val === 'object') ? this.deepMap(val, f, ctx) : f.call(ctx, val, key); }); } else if (typeof obj === 'object') { var res = {}; for (var key in obj) { var val = obj[key]; if (typeof val === 'object') { res[key] = this.deepMap(val, f, ctx); } else { res[key] = f.call(ctx, val, key); } } return res; } else { return obj; } } /** * Marks response that is signable. It has SigningObject with key signing available. * @static * @param {any} object * @param {string} idKey * * @memberOf EntityUtils */ public static addIdProperty(object: any, idKey: string): void { Object.defineProperty(object, "id", { get: function () { if (object[idKey]) { return object[idKey]; } return null; }, enumerable: true, configurable: true }); } /** * @static * @param {any} object * @returns {*} * * @memberOf EntityUtils */ public static copyData(object: any): any { return JSON.parse(JSON.stringify(object)) } /** * @static * @param {any} object * @param {string} oldName * @param {string} newName * * @memberOf EntityUtils */ public static renameProperty(object, oldName: string, newName: string) { if (object[oldName]) { object[newName] = object[oldName] delete object[oldName] } } /** * @static * @param {function} accessFunction * @param {any} key * @param {function} transformFunction * * @memberOf EntityUtils */ public static tryTransform(accessFunction: () => any, key, transformFunction: (val: any) => any) { var accessed: any = null; try { accessed = accessFunction()[key]; } catch (e) { //TODO: Make logging conditional console.log('INFO: unable to access property: ' + key); console.log(e); } accessFunction()[key] = transformFunction(accessed); } /** * @static * @param {function} accessFunction * @param {any} key * * @memberOf EntityUtils */ public static tryStringToDateTransform(accessFunction: () => any, key) { this.tryTransform(accessFunction, key, (val) => { var timestamp = this.parseISODate(val); if (typeof (timestamp) == 'number') { return new Date(timestamp) } else { return val; } }); } /** * @static * @param {function} accessFunction * @param {any} key * * @memberOf EntityUtils */ public static tryStringify(accessFunction: () => any, key) { this.tryTransform(accessFunction, key, (val) => { if (val != null) { return val.toString(); } else { return val; } }); } /** * Date.parse with progressive enhancement for ISO 8601 * NON-CONFORMANT EDITION. * © 2011 Colin Snover * Released under MIT license. * * @param {any} date * @returns {number} */ public static parseISODate(date: any): number { var origParse = Date.parse, numericKeys = [1, 4, 5, 6, 10, 11]; var timestamp, struct, minutesOffset = 0; // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm if ((struct = /^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/.exec(date))) { // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC for (var i = 0, k; (k = numericKeys[i]); ++i) { struct[k] = +struct[k] || 0; } // allow undefined days and months struct[2] = (+struct[2] || 1) - 1; struct[3] = +struct[3] || 1; // allow arbitrary sub-second precision beyond milliseconds struct[7] = struct[7] ? + (struct[7] + "00").substr(0, 3) : 0; // timestamps without timezone identifiers should be considered local time if ((struct[8] === undefined || struct[8] === '') && (struct[9] === undefined || struct[9] === '')) { timestamp = +new Date(struct[1], struct[2], struct[3], struct[4], struct[5], struct[6], struct[7]); } else { if (struct[8] !== 'Z' && struct[9] !== undefined) { minutesOffset = struct[10] * 60 + struct[11]; if (struct[9] === '+') { minutesOffset = 0 - minutesOffset; } } timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); } } else { timestamp = origParse ? origParse(date) : NaN; } return timestamp; } /** * Copy all of the properties in the source objects over to the destination object, * and return the destination object. It's in-order, so the last source will override * properties of the same name in previous arguments. * * @param {object} originalParams * @param {object} additionalParams * @returns {void} */ public static extend(originalParams: object, additionalParams: object): void { extend(originalParams, additionalParams); } /** * @static * @param {string|string[]} dates * @param {any} obj * @returns {void} * * @memberOf EntityUtils */ public static addDatesFromISO(dates: string | [string], obj): void { if (!obj) { return; } if (typeof dates === 'string') { this.addISODate(dates, obj); return; } (dates).forEach(date => { this.addISODate(date, obj); }); } /** * @static * @param {any} dates * @param {any} obj * @param {string} [items] itemsKey * @returns {void} * * @memberOf EntityUtils */ public static addDatesToItems(dates, obj, itemsKey: string = 'items'): void { if (!obj[itemsKey]) { return; } obj[itemsKey].forEach(item => { this.addDatesFromISO(dates, item); }); } /** * @static * @param {any} dates * @param {object} obj * @returns {void} * * @memberOf EntityUtils */ public static transformDatesToISO(dates, obj: object): void { if (!obj) { return; } if (typeof dates === 'string') { if (obj.hasOwnProperty(dates)) { obj[dates] = format(obj[dates], 'YYYY-MM-DDTHH:mm:ssZ'); } return; } dates.forEach(date => { if (obj.hasOwnProperty(date)) { obj[date] = format(obj[date], 'YYYY-MM-DDTHH:mm:ssZ'); } }); } /** * @static * @param {any} dates * @param {object} obj * @returns {void} * * @memberOf EntityUtils */ public static transformDatesToSimpleISO(dates, obj: object): void { if (typeof dates === 'string') { if (obj.hasOwnProperty(dates)) { obj[dates] = format(obj[dates], 'YYYY-MM-DD'); } return; } dates.forEach(date => { if (obj.hasOwnProperty(date)) { obj[date] = format(obj[date], 'YYYY-MM-DD'); } }); } /** * @static * @param {any} params * @param {any} paramKey * @returns {void} * * @memberOf EntityUtils */ public static transformArrayParamsToString(params: any, paramKey: any): void { if (Array.isArray(paramKey)) { paramKey.forEach(key => { this.transformParamsToString(params, key); }); return; } this.transformParamsToString(params, paramKey); } /** * @static * @param {string} path * @param {any} params * @returns {string} * * @memberOf EntityUtils */ public static addParamsToPath(path: string, params: any): string { if (!params || Object.keys(params).length === 0) { return path; } var queryParams = []; for (var prop in params) { queryParams.push(`${prop}=${params[prop]}`); } return encodeURI(`${path}?${queryParams.join('&')}`); } /** * @private * @static * @param {any} params * @param {any} paramKey * @returns {void} * * @memberOf EntityUtils */ private static transformParamsToString(params, paramKey): void { if (params && Array.isArray(params[paramKey])) { params[paramKey] = params[paramKey].join(','); } } /** * @private * @static * @param {any} date * @param {any} obj * @returns {void} * * @memberOf EntityUtils */ private static addISODate(date, obj): void { if (obj[date] !== undefined && typeof obj[date] === 'string') { obj[date] = new Date(this.parseISODate(obj[date])); } } }