From 15ae7f3aedf82cdb95aff60051477614ba9151a4 Mon Sep 17 00:00:00 2001 From: Gauravudia Date: Mon, 25 Sep 2023 12:31:36 +0530 Subject: [PATCH] feat: add circular reference check in flattern json --- src/v0/util/index.js | 30 +++++++++++++++++++++++++ src/v0/util/index.test.js | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/v0/util/index.js b/src/v0/util/index.js index 5ddd3cf5ff..00f98cafaf 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -246,9 +246,38 @@ const getValueFromPropertiesOrTraits = ({ message, key }) => { return !_.isNil(val) ? val : null; }; +/** + * Checks if an object contains a circular reference. + * + * @param {object} obj - The object to check for circular references. + * @param {array} [seen=[]] - An array that keeps track of objects already seen during the recursive traversal. Defaults to an empty array. + * @returns {boolean} - True if a circular reference is found, false otherwise. + */ +const hasCircularReference = (obj, seen = []) => { + if (typeof obj !== 'object' || obj === null) { + return false; + } + + if (seen.includes(obj)) { + return true; + } + + seen.push(obj); + for (const value of Object.values(obj)) { + if (hasCircularReference(value, seen)) { + return true; + } + } + seen.pop(); + return false; +}; + // function to flatten a json function flattenJson(data, separator = '.', mode = 'normal', flattenArrays = true) { const result = {}; + if (hasCircularReference(data)) { + throw new InstrumentationError("Event has circular reference. Can't flatten the event"); + } // a recursive function to loop through the array of the data function recurse(cur, prop) { @@ -2094,6 +2123,7 @@ module.exports = { getAccessToken, formatValues, groupEventsByType, + hasCircularReference, getAuthErrCategoryFromErrDetailsAndStCode, getAuthErrCategoryFromStCode, }; diff --git a/src/v0/util/index.test.js b/src/v0/util/index.test.js index 9c257d5fd3..ce341e8187 100644 --- a/src/v0/util/index.test.js +++ b/src/v0/util/index.test.js @@ -1,5 +1,6 @@ const utilities = require('.'); const { getFuncTestData } = require('../../../test/testHelper'); +const { hasCircularReference, flattenJson } = require('./index'); // Names of the utility functions to test const functionNames = [ @@ -68,3 +69,49 @@ describe('Utility Functions Tests', () => { }); //Test cases which can't be fit in above test suite, have individual function level test blocks + +describe('hasCircularReference', () => { + it('should return false when object has no circular reference', () => { + const obj = { a: 1, b: 2, c: 3 }; + expect(hasCircularReference(obj)).toBe(false); + }); + + it('should return true when object has circular reference', () => { + const obj = { a: 1, b: 2 }; + obj.c = obj; + expect(hasCircularReference(obj)).toBe(true); + }); + + it('should return true when object has nested objects containing circular reference', () => { + const obj1 = { a: 1 }; + const obj2 = { b: 2 }; + obj1.c = obj2; + obj2.d = obj1; + expect(hasCircularReference(obj1)).toBe(true); + }); + + it('should return false when input is null', () => { + expect(hasCircularReference(null)).toBe(false); + }); + + it('should return false when input is not an object', () => { + expect(hasCircularReference(123)).toBe(false); + }); + + it('should return true when object has self-reference', () => { + const obj = { a: 1 }; + obj.b = obj; + expect(hasCircularReference(obj)).toBe(true); + }); +}); + +// extra test cases for flattenJson +describe('flattenJson', () => { + it('should throw an error when flattening a json object with circular reference', () => { + const data = { name: 'John' }; + data.self = data; + expect(() => flattenJson(data)).toThrow( + "Event has circular reference. Can't flatten the event", + ); + }); +});