fix(acadenice): use camelCase row keys in sync-block repo — Patch 027
The DatabaseModule registers a global CamelCasePlugin which converts
every column name (including SELECT aliases) from snake_case to
camelCase at runtime. The sync-block repo declared sql<{...}> result
types in snake_case (workspace_id, created_at, etc.) and accessed those
keys in mapRow / findUsages. At runtime kysely returned camelCase keys
so every property read was undefined, causing 'Invalid time value' on
new Date(undefined).toISOString().
Same pattern is likely present in graph.service.ts and backlinks
services — Patch 028 will sweep those.
Verified via curl: POST /api/acadenice/sync-blocks now returns 201 with
the full DTO.
Patch 027.
This commit is contained in:
parent
7fba3c0452
commit
8c3d55024b
1 changed files with 33 additions and 30 deletions
|
|
@ -24,11 +24,11 @@ export class SyncBlockRepo {
|
|||
): Promise<SyncBlockResponseDto> {
|
||||
const result = await sql<{
|
||||
id: string;
|
||||
workspace_id: string;
|
||||
workspaceId: string;
|
||||
content: Record<string, unknown>;
|
||||
created_by: string;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
createdBy: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>`
|
||||
INSERT INTO acadenice_sync_block (workspace_id, content, created_by)
|
||||
VALUES (${workspaceId}, ${JSON.stringify(content)}::jsonb, ${createdBy})
|
||||
|
|
@ -44,11 +44,11 @@ export class SyncBlockRepo {
|
|||
): Promise<SyncBlockResponseDto | null> {
|
||||
const result = await sql<{
|
||||
id: string;
|
||||
workspace_id: string;
|
||||
workspaceId: string;
|
||||
content: Record<string, unknown>;
|
||||
created_by: string;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
createdBy: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>`
|
||||
SELECT id, workspace_id, content, created_by, created_at, updated_at
|
||||
FROM acadenice_sync_block
|
||||
|
|
@ -67,11 +67,11 @@ export class SyncBlockRepo {
|
|||
): Promise<SyncBlockResponseDto | null> {
|
||||
const result = await sql<{
|
||||
id: string;
|
||||
workspace_id: string;
|
||||
workspaceId: string;
|
||||
content: Record<string, unknown>;
|
||||
created_by: string;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
createdBy: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>`
|
||||
UPDATE acadenice_sync_block
|
||||
SET content = ${JSON.stringify(content)}::jsonb, updated_at = NOW()
|
||||
|
|
@ -101,7 +101,7 @@ export class SyncBlockRepo {
|
|||
id: string,
|
||||
workspaceId: string,
|
||||
): Promise<Buffer | null> {
|
||||
const result = await sql<{ yjs_state: Buffer | null }>`
|
||||
const result = await sql<{ yjsState: Buffer | null }>`
|
||||
SELECT yjs_state
|
||||
FROM acadenice_sync_block
|
||||
WHERE id = ${id}
|
||||
|
|
@ -109,7 +109,7 @@ export class SyncBlockRepo {
|
|||
`.execute(this.db);
|
||||
|
||||
if (result.rows.length === 0) return null;
|
||||
return result.rows[0].yjs_state ?? null;
|
||||
return result.rows[0].yjsState ?? null;
|
||||
}
|
||||
|
||||
async delete(id: string, workspaceId: string): Promise<boolean> {
|
||||
|
|
@ -137,12 +137,15 @@ export class SyncBlockRepo {
|
|||
// Pages store their content as JSONB. We search for any object in the
|
||||
// content tree where type='syncBlock' and attrs->>'masterId' = blockId.
|
||||
// Using jsonb_path_exists for recursive search.
|
||||
// CamelCasePlugin (in DatabaseModule) converts snake_case columns to
|
||||
// camelCase at runtime, including SELECT aliases. So even though the SQL
|
||||
// says "page_id" the row property is "pageId".
|
||||
const result = await sql<{
|
||||
page_id: string;
|
||||
pageId: string;
|
||||
title: string | null;
|
||||
slug_id: string;
|
||||
space_id: string;
|
||||
workspace_id: string;
|
||||
slugId: string;
|
||||
spaceId: string;
|
||||
workspaceId: string;
|
||||
}>`
|
||||
SELECT DISTINCT p.id AS page_id, p.title, p.slug_id, p.space_id, s.workspace_id
|
||||
FROM pages p
|
||||
|
|
@ -158,11 +161,11 @@ export class SyncBlockRepo {
|
|||
`.execute(this.db);
|
||||
|
||||
return result.rows.map((r) => ({
|
||||
pageId: r.page_id,
|
||||
pageId: r.pageId,
|
||||
pageTitle: r.title,
|
||||
slugId: r.slug_id,
|
||||
spaceId: r.space_id,
|
||||
workspaceId: r.workspace_id,
|
||||
slugId: r.slugId,
|
||||
spaceId: r.spaceId,
|
||||
workspaceId: r.workspaceId,
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -178,19 +181,19 @@ export class SyncBlockRepo {
|
|||
|
||||
private mapRow(row: {
|
||||
id: string;
|
||||
workspace_id: string;
|
||||
workspaceId: string;
|
||||
content: Record<string, unknown>;
|
||||
created_by: string;
|
||||
created_at: Date | string;
|
||||
updated_at: Date | string;
|
||||
createdBy: string;
|
||||
createdAt: Date | string;
|
||||
updatedAt: Date | string;
|
||||
}): SyncBlockResponseDto {
|
||||
return {
|
||||
id: row.id,
|
||||
workspaceId: row.workspace_id,
|
||||
workspaceId: row.workspaceId,
|
||||
content: row.content,
|
||||
createdBy: row.created_by,
|
||||
createdAt: new Date(row.created_at).toISOString(),
|
||||
updatedAt: new Date(row.updated_at).toISOString(),
|
||||
createdBy: row.createdBy,
|
||||
createdAt: new Date(row.createdAt).toISOString(),
|
||||
updatedAt: new Date(row.updatedAt).toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue