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 5f0988bfac..87b66e3d78 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 { serializeAttributes, trimAttributeStrings } from './insert' +import { serializeAttributes, trimAttributeStrings, tryToUnwrapAttributes } from './insert' describe('trimAttributeStrings', () => { it('keeps every key of an oversized attributes object instead of wiping it', () => { @@ -47,6 +47,26 @@ describe('trimAttributeStrings', () => { }) }) +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({}) + }, + ) +}) + describe('serializeAttributes', () => { const MAX_BYTES = 16 * 1024 const expectUnderCap = (json: string) => diff --git a/services/libs/data-access-layer/src/activities/insert.ts b/services/libs/data-access-layer/src/activities/insert.ts index 6927b9c892..040878e58d 100644 --- a/services/libs/data-access-layer/src/activities/insert.ts +++ b/services/libs/data-access-layer/src/activities/insert.ts @@ -236,12 +236,10 @@ function pruneToFit(value: object, maxBytes: number): object { return isArray ? kept.map((e) => e.value) : Object.fromEntries(kept.map((e) => [e.key, e.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 : {} }