UNPKG

@snickerdoodlelabs/common-utils

Version:
198 lines 7.76 kB
import { PagingRequest, JSONString, InvalidParametersError, } from "@snickerdoodlelabs/objects"; import { errAsync, okAsync } from "neverthrow"; import { ResultUtils } from "neverthrow-result-utils"; export class ObjectUtils { // Taken from https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge // eslint-disable-next-line @typescript-eslint/no-explicit-any static mergeDeep(...objects) { const isObject = (obj) => obj && typeof obj === "object"; return objects.reduce((prev, obj) => { Object.keys(obj).forEach((key) => { const pVal = prev[key]; const oVal = obj[key]; if (Array.isArray(pVal) && Array.isArray(oVal)) { prev[key] = pVal.concat(...oVal); } else if (isObject(pVal) && isObject(oVal)) { prev[key] = ObjectUtils.mergeDeep(pVal, oVal); } else { prev[key] = oVal; } }); return prev; }, {}); } static serialize(obj) { return JSONString(JSON.stringify(obj, (key, value) => { if (value instanceof Map) { return { dataType: "Map", value: Array.from(value.entries()), // or with spread: value: [...value] }; } else if (value instanceof Set) { return { dataType: "Set", value: [...value], }; } else if (value instanceof BigInt) { return { dataType: "BigInt", value: value.toString(), }; } else if (typeof value == "bigint") { return { dataType: "bigint", value: BigInt(value).toString(), }; } else { return value; } })); } static deserialize(json) { return JSON.parse(json, (key, value) => { if (typeof value === "object" && value !== null) { if (value.dataType === "Map") { return new Map(value.value); } else if (value.dataType === "Set") { return new Set(value.value); } else if (value.dataType === "BigInt") { return BigInt(value.value); } else if (value.dataType === "bigint") { return BigInt(value.value); } } return value; }); } static toGenericObject(obj) { return ObjectUtils.deserialize(ObjectUtils.serialize(obj)); } static iteratePages(readFunc, processFunc, pageSize = 25, serialize = false) { // This is a recursive method. We just start it off. return ObjectUtils.fetchAndProcessPage(1, readFunc, processFunc, pageSize, serialize); } static fetchAndProcessPage(pageNumber, readFunc, processFunc, pageSize = 25, serialize = false) { // Create the initial paging request const pagingRequest = new PagingRequest(pageNumber, pageSize); // Read the page return readFunc(pagingRequest).andThen((objectPage) => { // Iterate over the objects and run through the processor // We'll wait for all the processFuncs to complete before getting the next page let result; if (serialize) { result = ResultUtils.executeSerially(objectPage.response.map((obj) => () => { return processFunc(obj); })); } else { result = ResultUtils.combine(objectPage.response.map((obj) => { return processFunc(obj); })); } return result.andThen(() => { // Done processing all of those results. Check if we need to recurse // See what result we're on and how it compares to the total results const maxResult = objectPage.page * objectPage.pageSize; if (maxResult < objectPage.totalResults) { return ObjectUtils.fetchAndProcessPage(objectPage.page + 1, readFunc, processFunc, pageSize); } return okAsync(undefined); }); }); } static iterateCursor(readFunc, handler) { // Do the first read return ObjectUtils.processNextBatch(null, readFunc, handler).map(() => { }); } static parseBoolean(val) { switch (typeof val) { case "boolean": return val; case "number": return val !== 0; case "string": switch (val?.toLowerCase()?.trim()) { case "true": case "yes": case "1": return true; case "false": case "no": case "0": case null: case undefined: return false; default: console.log(`Don't have explicit check in parseBoolean for the string ${val}`); return false; } default: console.log(`Don't have check in parseBoolean for typeof ${typeof val} of value ${val}`); return false; } } static processNextBatch(cursor, readFunc, handler) { return readFunc(cursor).andThen((resp) => { if (resp.response.length == 0) { return okAsync(resp); } return handler(resp.response).andThen(() => { return ObjectUtils.processNextBatch(resp.cursor, readFunc, handler); }); }); } static removeNullValues(array) { function notEmpty(value) { if (value === null || value === undefined) return false; const testConversion = value; return true; } return array.filter(notEmpty); } static progressiveFallback(method, provider) { if (provider.length == 0) { return errAsync(new InvalidParametersError("No providers provided to progressiveFallback()")); } return method(provider[0]).orElse((err) => { // If we're on the last provider, just return the error if (provider.length == 1) { return errAsync(err); } // Otherwise, try the next provider return ObjectUtils.progressiveFallback(method, provider.slice(1)); }); } static verifyBigNumber(bigNumberString) { try { const bigNumber = BigInt(bigNumberString); // will fail if bigNumberString is a float return okAsync(bigNumber); } catch (e) { return errAsync(new InvalidParametersError(`Can't convert BigNumberString ${bigNumberString} to BigInt. Received error ${e}`)); } } static iterateThroughAllPages(readFunc) { const data = []; const processFunc = (model) => { data.push(model); return okAsync(undefined); }; return readFunc(new PagingRequest(1, 1)) .andThen((firstPage) => { const pageSize = firstPage.totalResults; return ObjectUtils.iteratePages(readFunc, processFunc, pageSize); }) .map(() => data); } } //# sourceMappingURL=ObjectUtils.js.map