From e6948488c11f6a53b1ef1891045295a8cfd0ba59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uro=C5=A1=20Marolt?= Date: Thu, 15 Oct 2026 22:47:42 +0200 Subject: [PATCH] fix: stop unwrapping activity attributes once they are no longer a string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attributes that are a JSON-encoded number or boolean made the unwrap loop parse the same value forever and hang the data sink worker. Any value that doesn't unwrap to an object is now stored as empty attributes. Refs linuxfoundation/lfx-cdp-insights-ops#814 Signed-off-by: Uroš Marolt --- .../src/activities/insert.test.ts | 22 ++++++++++++++++++- .../src/activities/insert.ts | 12 +++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/services/libs/data-access-layer/src/activities/insert.test.ts b/services/libs/data-access-layer/src/activities/insert.test.ts index c8082870a4..004ad7b851 100644 --- a/services/libs/data-access-layer/src/activities/insert.test.ts +++ b/services/libs/data-access-layer/src/activities/insert.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { trimAttributeStrings } from './insert' +import { trimAttributeStrings, tryToUnwrapAttributes } from './insert' describe('trimAttributeStrings', () => { it('keeps every key of an oversized attributes object instead of wiping it', () => { @@ -46,3 +46,23 @@ describe('trimAttributeStrings', () => { expect(trimmed).toBe(`${'a'.repeat(499)}...`) }) }) + +describe('tryToUnwrapAttributes', () => { + it('returns an object as is', () => { + const attributes = { url: 'https://example.com' } + expect(tryToUnwrapAttributes(attributes)).toBe(attributes) + }) + + it('unwraps attributes encoded as JSON, also when encoded more than once', () => { + const attributes = { state: 'open' } + expect(tryToUnwrapAttributes(JSON.stringify(attributes))).toEqual(attributes) + expect(tryToUnwrapAttributes(JSON.stringify(JSON.stringify(attributes)))).toEqual(attributes) + }) + + it.each(['5', 'true', 'null', JSON.stringify('5')])( + 'returns empty attributes for %s instead of looping forever', + (encoded) => { + expect(tryToUnwrapAttributes(encoded)).toEqual({}) + }, + ) +}) diff --git a/services/libs/data-access-layer/src/activities/insert.ts b/services/libs/data-access-layer/src/activities/insert.ts index a5d5651fd7..2d4bf20311 100644 --- a/services/libs/data-access-layer/src/activities/insert.ts +++ b/services/libs/data-access-layer/src/activities/insert.ts @@ -162,12 +162,10 @@ export function trimAttributeStrings(value: unknown): unknown { return value } -function tryToUnwrapAttributes(attributes: string | object): object { - // eslint-disable-next-line no-constant-condition - while (true) { - if (typeof attributes === 'object') { - return attributes - } - attributes = JSON.parse(attributes) +export function tryToUnwrapAttributes(attributes: unknown): object { + let value = attributes + while (typeof value === 'string') { + value = JSON.parse(value) } + return value !== null && typeof value === 'object' ? value : {} }