-
-
Notifications
You must be signed in to change notification settings - Fork 729
Fix: Handle circular references in flattenAttributes function #1433
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
Changes from all commits
6915d9e
f508bf5
b29ba40
e58686c
400dbb2
c908f3a
cb3f598
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@trigger.dev/core": patch | ||
--- | ||
|
||
Fix: Handle circular references in flattenAttributes function |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
Comment on lines
+172
to
+177
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider parameterizing the circular reference placeholder. The hardcoded string -function rehydrateCircular(value: any): any {
+function rehydrateCircular(
+ value: any,
+ placeholder: string = "[Circular Reference]"
+): any {
if (value === CIRCULAR_REFERENCE_SENTINEL) {
- return "[Circular Reference]";
+ return placeholder;
}
return value;
} Then update the call site: -current[lastPart] = rehydrateNull(rehydrateCircular(value));
+current[lastPart] = rehydrateNull(rehydrateCircular(value, options?.circularPlaceholder));
|
||
|
||
export function primitiveValueOrflattenedAttributes( | ||
obj: Record<string, unknown> | Array<unknown> | string | boolean | number | undefined, | ||
prefix: string | undefined | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure the uniqueness of
CIRCULAR_REFERENCE_SENTINEL
to prevent data conflicts.While
CIRCULAR_REFERENCE_SENTINEL
is used to indicate circular references, there's a possibility that user data may contain this exact string, leading to incorrect interpretation during unflattening. Consider using a unique symbol or a less common string to minimize the risk of conflicts with actual data values.