Skip to content

Commit da0de24

Browse files
authored
fix(documents): return real element metadata from GET document endpoint (#1304)
1 parent a1e5f49 commit da0de24

3 files changed

Lines changed: 85 additions & 13 deletions

File tree

backend/openapi.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6864,6 +6864,16 @@
68646864
}
68656865
}
68666866
}
6867+
},
6868+
"500": {
6869+
"description": "Internal server error",
6870+
"content": {
6871+
"application/json": {
6872+
"schema": {
6873+
"$ref": "#/components/schemas/DocumentError"
6874+
}
6875+
}
6876+
}
68676877
}
68686878
}
68696879
}

backend/src/routes/document.routes.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ const getDocRoute = createRoute({
177177
},
178178
description: 'Document not found',
179179
},
180+
500: {
181+
content: {
182+
'application/json': {
183+
schema: ErrorSchema,
184+
},
185+
},
186+
description: 'Internal server error',
187+
},
180188
},
181189
});
182190

@@ -200,15 +208,21 @@ documentRoutes.openapi(getDocRoute, async (c) => {
200208
return c.json({ error: 'Access denied' }, 403);
201209
}
202210

203-
// Return placeholder - full implementation would query the element from Yjs
204-
return c.json(
205-
{
206-
id: docId,
207-
name: 'Document',
208-
type: 'ITEM',
209-
},
210-
200
211-
);
211+
// Look up the document element from Yjs
212+
try {
213+
const elements = await yjsService.getElements(username, slug);
214+
const document = elements.find((e) => e.id === docId && e.type === 'ITEM');
215+
216+
if (!document) {
217+
documentLog.warn(`Document ${docId} not found in project ${username}/${slug}`);
218+
return c.json({ error: 'Document not found' }, 404);
219+
}
220+
221+
return c.json(document, 200);
222+
} catch (error) {
223+
documentLog.error('Error fetching document', error);
224+
return c.json({ error: 'Failed to fetch document' }, 500);
225+
}
212226
});
213227

214228
// Render document as HTML

backend/test/document.routes.test.ts

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
enablePasswordLoginForTests,
1111
} from './server-test-helper';
1212
import { TEST_PASSWORDS } from './test-credentials';
13+
import { yjsService } from '../src/services/yjs.service';
1314

1415
describe('Document Routes', () => {
1516
let ownerUserId: string;
@@ -63,6 +64,10 @@ describe('Document Routes', () => {
6364
expect(response.status).toBe(201);
6465
const project = (await json()) as { slug: string };
6566
projectSlug = project.slug;
67+
68+
// Seed a real element into the project's Yjs elements document so the
69+
// GET /docs/:docId route has real data to return.
70+
await seedElement(ownerUsername, projectSlug, 'doc-test-element', 'Chapter One');
6671
});
6772

6873
afterAll(async () => {
@@ -118,13 +123,27 @@ describe('Document Routes', () => {
118123
expect(response.status).toBe(404);
119124
});
120125

121-
it('should return 200 for valid project and docId', async () => {
126+
it('should return 200 with real element metadata for a known document', async () => {
122127
const { response, json } = await client.request(
123-
`/api/v1/projects/${ownerUsername}/${projectSlug}/docs/some-doc`
128+
`/api/v1/projects/${ownerUsername}/${projectSlug}/docs/doc-test-element`
124129
);
125130
expect(response.status).toBe(200);
126-
const data = (await json()) as { id: string };
127-
expect(data).toHaveProperty('id', 'some-doc');
131+
const data = (await json()) as {
132+
id: string;
133+
name: string;
134+
type: string;
135+
};
136+
// Real element data is returned rather than a fabricated placeholder
137+
expect(data).toHaveProperty('id', 'doc-test-element');
138+
expect(data).toHaveProperty('name', 'Chapter One');
139+
expect(data).toHaveProperty('type', 'ITEM');
140+
});
141+
142+
it('should return 404 for a non-existent document id', async () => {
143+
const { response } = await client.request(
144+
`/api/v1/projects/${ownerUsername}/${projectSlug}/docs/nonexistent-doc`
145+
);
146+
expect(response.status).toBe(404);
128147
});
129148
});
130149

@@ -169,3 +188,32 @@ describe('Document Routes', () => {
169188
});
170189
});
171190
});
191+
192+
/**
193+
* Seed a single ITEM element into the project's Yjs elements document.
194+
*/
195+
async function seedElement(
196+
username: string,
197+
slug: string,
198+
id: string,
199+
name: string
200+
): Promise<void> {
201+
const docId = `${username}:${slug}:elements/`;
202+
const sharedDoc = await yjsService.getDocument(docId);
203+
sharedDoc.doc.transact(() => {
204+
const elements = sharedDoc.doc.getArray<Record<string, unknown>>('elements');
205+
elements.push([
206+
{
207+
id,
208+
name,
209+
type: 'ITEM',
210+
parentId: null,
211+
order: 0,
212+
level: 0,
213+
expandable: false,
214+
version: 1,
215+
metadata: {},
216+
},
217+
]);
218+
});
219+
}

0 commit comments

Comments
 (0)