Skip to content

Commit 10b4de2

Browse files
committed
Fix double-JSON.parse() call
1 parent 186a444 commit 10b4de2

4 files changed

Lines changed: 46 additions & 7 deletions

File tree

pages/dashboard.vue

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,8 @@ const currentWorkspaceTdeiRoles = computed(() =>
136136
: [],
137137
);
138138
139-
for (const w of workspaces) {
140-
if (w.tdeiMetadata && w.tdeiMetadata.length > 0) {
141-
w.tdeiMetadata = JSON.parse(w.tdeiMetadata);
142-
}
143-
}
139+
// `tdeiMetadata` is already parsed into an object by `normalizeWorkspace`
140+
// (in `getMyWorkspaces`), so no per-workspace re-parse is needed here.
144141
145142
onMounted(() => {
146143
watch(currentWorkspace, (val) => {

pages/workspace/[id]/export/tdei.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ if (canExport && !eligibleProjectGroups.some(pg => pg.tdei_project_group_id ===
266266
workspace.tdeiProjectGroupId = eligibleProjectGroups[0]!.tdei_project_group_id;
267267
}
268268
269-
const oldMetadata = workspace.tdeiMetadata ? JSON.parse(workspace.tdeiMetadata) : {};
269+
// Already parsed into an object by `normalizeWorkspace` in `getWorkspace`.
270+
const oldMetadata = (workspace.tdeiMetadata as any) ?? {};
270271
271272
const datasetName = ref(workspace.title);
272273
const datasetVersion = ref(oldMetadata.metadata?.dataset_detail?.version);

services/workspaces.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,29 @@ export function compareWorkspaceCreatedAtDesc(a: Workspace, b: Workspace) {
3030
return b.createdAt.getTime() - a.createdAt.getTime();
3131
}
3232

33+
/**
34+
* The API is inconsistent about `tdeiMetadata`: some responses send it as a
35+
* JSON-encoded string, others as an already-parsed object. Tolerate both (and
36+
* malformed values) so we never call `JSON.parse` on a non-string — doing so
37+
* coerces the object to "[object Object]" and throws a SyntaxError.
38+
*/
39+
function parseTdeiMetadata(value: Workspace['tdeiMetadata']): unknown {
40+
if (value == null || value === '') return {};
41+
if (typeof value !== 'string') return value;
42+
43+
try {
44+
return JSON.parse(value);
45+
}
46+
catch {
47+
return {};
48+
}
49+
}
50+
3351
function normalizeWorkspace(workspace: Workspace): Workspace {
3452
return {
3553
...workspace,
3654
createdAt: new Date(workspace.createdAt),
37-
tdeiMetadata: JSON.parse(workspace.tdeiMetadata || '{}'),
55+
tdeiMetadata: parseTdeiMetadata(workspace.tdeiMetadata) as Workspace['tdeiMetadata'],
3856
};
3957
}
4058

test/unit/services/workspaces.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,29 @@ describe('WorkspacesClient.getMyWorkspaces', () => {
3333
expect(workspaces[0]!.createdAt.toISOString()).toBe('2026-01-15T12:00:00.000Z');
3434
});
3535

36+
// Regression: the API sends `tdeiMetadata` inconsistently — sometimes a
37+
// JSON-encoded string, sometimes an already-parsed object. Blindly calling
38+
// JSON.parse() on the object form coerces it to "[object Object]" and throws
39+
// `SyntaxError: "[object Object]" is not valid JSON`, which used to blow up
40+
// the dashboard's <script setup>. Normalize must tolerate every form.
41+
it.each([
42+
['a JSON-encoded string', '{"metadata":{"foo":"bar"}}', { metadata: { foo: 'bar' } }],
43+
['an already-parsed object', { metadata: { foo: 'bar' } }, { metadata: { foo: 'bar' } }],
44+
['null', null, {}],
45+
['an empty string', '', {}],
46+
['a malformed JSON string', '{not json', {}]
47+
])('parses tdeiMetadata when the API returns %s', async (_label, raw, expected) => {
48+
server.use(
49+
http.get(`${TEST_API_BASE}workspaces/mine`, () => {
50+
return HttpResponse.json([{ id: 1, title: 'W', createdAt: '2026-01-15T12:00:00.000Z', tdeiMetadata: raw }]);
51+
})
52+
);
53+
54+
const [workspace] = await makeClient().getMyWorkspaces();
55+
56+
expect(workspace!.tdeiMetadata).toEqual(expected);
57+
});
58+
3659
it('throws a WorkspacesClientError on a non-2xx response', async () => {
3760
// Per-test override: make the endpoint fail. Reset automatically afterEach.
3861
server.use(

0 commit comments

Comments
 (0)