Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add circular reference check in flattern json #2650

Merged
merged 1 commit into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/v0/util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -2094,6 +2123,7 @@ module.exports = {
getAccessToken,
formatValues,
groupEventsByType,
hasCircularReference,
getAuthErrCategoryFromErrDetailsAndStCode,
getAuthErrCategoryFromStCode,
};
47 changes: 47 additions & 0 deletions src/v0/util/index.test.js
Original file line number Diff line number Diff line change
@@ -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 = [
Expand Down Expand Up @@ -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",
);
});
});