Fix: Handle circular references in flattenAttributes function (#1433)

* Fix: Handle circular references in flattenAttributes function

* Update flattenAttributes.test.ts

* Update packages/core/src/v3/utils/flattenAttributes.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update packages/core/src/v3/utils/flattenAttributes.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Create brown-laws-rest.md

---------

Co-authored-by: Eric Allam <eallam@icloud.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Eric Allam <eric@trigger.dev>
This commit is contained in:
shubham yadav
2024-10-24 15:14:04 +05:30
committed by GitHub
parent c31700ae5f
commit 9a5e6e58be
3 changed files with 69 additions and 4 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/core": patch
---
Fix: Handle circular references in flattenAttributes function
@@ -1,10 +1,12 @@
import { Attributes } from "@opentelemetry/api";
export const NULL_SENTINEL = "$@null((";
export const CIRCULAR_REFERENCE_SENTINEL = "$@circular((";
export function flattenAttributes(
obj: Record<string, unknown> | Array<unknown> | string | boolean | number | null | undefined,
prefix?: string
prefix?: string ,
seen: WeakSet<object> = new WeakSet()
): Attributes {
const result: Attributes = {};
@@ -38,13 +40,25 @@ export function flattenAttributes(
return result;
}
// Check for circular reference
if (obj !== null && typeof obj === "object" && seen.has(obj)) {
result[prefix || ""] = CIRCULAR_REFERENCE_SENTINEL;
return result;
}
// Add object to seen set
if (obj !== null && typeof obj === "object") {
seen.add(obj);
}
for (const [key, value] of Object.entries(obj)) {
const newPrefix = `${prefix ? `${prefix}.` : ""}${Array.isArray(obj) ? `[${key}]` : key}`;
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
if (typeof value[i] === "object" && value[i] !== null) {
// update null check here as well
Object.assign(result, flattenAttributes(value[i], `${newPrefix}.[${i}]`));
Object.assign(result, flattenAttributes(value[i], `${newPrefix}.[${i}]`,seen));
} else {
if (value[i] === null) {
result[`${newPrefix}.[${i}]`] = NULL_SENTINEL;
@@ -55,7 +69,7 @@ export function flattenAttributes(
}
} else if (isRecord(value)) {
// update null check here
Object.assign(result, flattenAttributes(value, newPrefix));
Object.assign(result, flattenAttributes(value, newPrefix, seen));
} else {
if (typeof value === "number" || typeof value === "string" || typeof value === "boolean") {
result[newPrefix] = value;
@@ -135,8 +149,10 @@ export function unflattenAttributes(
}
const lastPart = parts[parts.length - 1];
if (lastPart !== undefined) {
current[lastPart] = rehydrateNull(value);
current[lastPart] = rehydrateNull(rehydrateCircular(value));
}
}
@@ -153,6 +169,13 @@ export function unflattenAttributes(
return result;
}
function rehydrateCircular(value: any): any {
if (value === CIRCULAR_REFERENCE_SENTINEL) {
return "[Circular Reference]";
}
return value;
}
export function primitiveValueOrflattenedAttributes(
obj: Record<string, unknown> | Array<unknown> | string | boolean | number | undefined,
prefix: string | undefined
@@ -156,6 +156,29 @@ describe("flattenAttributes", () => {
expect(flattenAttributes(obj, "retry.byStatus")).toEqual(expected);
});
it("handles circular references correctly", () => {
const user = { name: "Alice" };
user["blogPosts"] = [{ title: "Post 1", author: user }]; // Circular reference
const result = flattenAttributes(user);
expect(result).toEqual({
"name": "Alice",
"blogPosts.[0].title": "Post 1",
"blogPosts.[0].author": "$@circular((",
});
});
it("handles nested circular references correctly", () => {
const user = { name: "Bob" };
user["friends"] = [user]; // Circular reference
const result = flattenAttributes(user);
expect(result).toEqual({
"name": "Bob",
"friends.[0]": "$@circular((",
});
});
});
describe("unflattenAttributes", () => {
@@ -223,4 +246,18 @@ describe("unflattenAttributes", () => {
};
expect(unflattenAttributes(flattened)).toEqual(expected);
});
it("rehydrates circular references correctly", () => {
const flattened = {
"name": "Alice",
"blogPosts.[0].title": "Post 1",
"blogPosts.[0].author": "$@circular((",
};
const result = unflattenAttributes(flattened);
expect(result).toEqual({
name: "Alice",
blogPosts: [{ title: "Post 1", author: "[Circular Reference]" }],
});
});
});