Skip to content

Commit 286a350

Browse files
committed
feat: Add get all documents
1 parent 3270f0e commit 286a350

File tree

9 files changed

+182
-65
lines changed

9 files changed

+182
-65
lines changed

config/env/.env.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
4747
GITHUB_REPO_OWNER=react-chatbotify
4848
GITHUB_REPO_NAME=core-library-documentation
4949
GITHUB_DOCS_PATH='docs'
50-
GITHUB_TOKEN=GITHUB_TOKEN; # optional, unlikely needed
50+
GITHUB_TOKEN=GITHUB_TOKEN # create a personal access token on github (no perms required, just for higher auth rate limits)

src/api/controllers/__tests__/ragManagement.test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Request, Response } from 'express';
22

33
import { initializedRagService } from '../../services/ragService';
4-
import { createDocument, deleteDocument, getDocument, updateDocument } from '../ragManagement'; // Adjust path as needed
4+
import { createDocument, deleteDocument, getDocument, getAllDocuments, updateDocument } from '../ragManagement'; // Adjust path as needed
55

66
// Mock the initializedRagService
77
jest.mock('../../services/ragService', () => ({
@@ -10,6 +10,7 @@ jest.mock('../../services/ragService', () => ({
1010
deleteDocument: jest.fn(),
1111
getParentDocumentContent: jest.fn(),
1212
updateDocument: jest.fn(),
13+
getAllDocumentIds: jest.fn(), // Added mock for getAllDocumentIds
1314
// Add other methods if they are called and need mocking, e.g. documentExists
1415
}),
1516
}));
@@ -213,4 +214,55 @@ describe('RAG Management Controllers', () => {
213214
expect(res.json).toHaveBeenCalledWith({ details: 'Service error', error: 'Internal Server Error' });
214215
});
215216
});
217+
218+
describe('getAllDocuments', () => {
219+
it('should return 200 and all document IDs', async () => {
220+
const req = mockRequest() as Request;
221+
const res = mockResponse() as Response;
222+
const expectedIds = ['id1', 'id2'];
223+
mockRagService.getAllDocumentIds.mockResolvedValue(expectedIds);
224+
225+
await getAllDocuments(req, res);
226+
227+
expect(mockRagService.getAllDocumentIds).toHaveBeenCalled();
228+
expect(res.status).toHaveBeenCalledWith(200);
229+
expect(res.json).toHaveBeenCalledWith({ documentIds: expectedIds });
230+
});
231+
232+
it('should return 200 and an empty array if no documents exist', async () => {
233+
const req = mockRequest() as Request;
234+
const res = mockResponse() as Response;
235+
mockRagService.getAllDocumentIds.mockResolvedValue([]);
236+
237+
await getAllDocuments(req, res);
238+
239+
expect(mockRagService.getAllDocumentIds).toHaveBeenCalled();
240+
expect(res.status).toHaveBeenCalledWith(200);
241+
expect(res.json).toHaveBeenCalledWith({ documentIds: [] });
242+
});
243+
244+
it('should return 503 if RAG service is not ready', async () => {
245+
const req = mockRequest() as Request;
246+
const res = mockResponse() as Response;
247+
mockRagService.getAllDocumentIds.mockRejectedValue(new Error('ChromaDB collection is not initialized'));
248+
249+
await getAllDocuments(req, res);
250+
251+
expect(mockRagService.getAllDocumentIds).toHaveBeenCalled();
252+
expect(res.status).toHaveBeenCalledWith(503);
253+
expect(res.json).toHaveBeenCalledWith({ error: 'Service Unavailable: RAG service is not ready.' });
254+
});
255+
256+
it('should return 500 for other errors', async () => {
257+
const req = mockRequest() as Request;
258+
const res = mockResponse() as Response;
259+
mockRagService.getAllDocumentIds.mockRejectedValue(new Error('Something went wrong'));
260+
261+
await getAllDocuments(req, res);
262+
263+
expect(mockRagService.getAllDocumentIds).toHaveBeenCalled();
264+
expect(res.status).toHaveBeenCalledWith(500);
265+
expect(res.json).toHaveBeenCalledWith({ error: 'Internal Server Error', details: 'Something went wrong' });
266+
});
267+
});
216268
});

src/api/controllers/ragManagement.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ export const createDocument = async (req: Request, res: Response) => {
2727
}
2828
};
2929

30+
export const getAllDocuments = async (req: Request, res: Response) => {
31+
try {
32+
const ragService = await initializedRagService;
33+
const documentIds = await ragService.getAllDocumentIds();
34+
return res.status(200).json({ documentIds });
35+
} catch (error: any) {
36+
Logger.error('Error in getAllDocuments:', error);
37+
// Check for specific service readiness errors if applicable
38+
if (error.message && error.message.includes('ChromaDB collection is not initialized')) {
39+
return res.status(503).json({ error: 'Service Unavailable: RAG service is not ready.' });
40+
}
41+
return res.status(500).json({ error: 'Internal Server Error', details: error.message });
42+
}
43+
};
44+
3045
export const getDocument = async (req: Request, res: Response) => {
3146
try {
3247
const { documentId } = req.params;

src/api/routers/ragManagement.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { Router } from 'express';
22

3-
import { createDocument, deleteDocument, getDocument, updateDocument } from '../controllers/ragManagement';
3+
import {
4+
createDocument,
5+
deleteDocument,
6+
getDocument,
7+
getAllDocuments, // <-- Import added here
8+
updateDocument,
9+
} from '../controllers/ragManagement';
410
import { apiKeyAuth } from '../middleware/auth';
511

612
const ragManagementRouter = Router();
@@ -10,6 +16,7 @@ ragManagementRouter.use(apiKeyAuth);
1016

1117
// Define routes
1218
ragManagementRouter.post('/documents', createDocument);
19+
ragManagementRouter.get('/documents:all', getAllDocuments);
1320
ragManagementRouter.get('/documents/:documentId', getDocument);
1421
ragManagementRouter.put('/documents/:documentId', updateDocument);
1522
ragManagementRouter.delete('/documents/:documentId', deleteDocument);

src/api/services/mongoService.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ class MongoService {
4343
const collection = await this.getParentDocumentCollection();
4444
await collection.deleteOne({ _id: documentId });
4545
}
46+
47+
async getAllDocumentIds(): Promise<string[]> {
48+
const collection = await this.getParentDocumentCollection();
49+
const documents = await collection.find({}, { projection: { _id: 1 } }).toArray();
50+
return documents.map((doc) => doc._id);
51+
}
4652
}
4753

4854
export const mongoService = new MongoService();

src/api/services/ragService.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { v4 as uuidv4 } from 'uuid';
44

55
import { config } from '../config';
66
import Logger from '../logger';
7-
import { generateEmbeddings } from './llmWrapper';
7+
import { generateEmbeddings } from '../services/llmWrapper';
88
import { mongoService } from './mongoService';
99

1010
export class RAGService {
@@ -293,6 +293,17 @@ export class RAGService {
293293
throw error;
294294
}
295295
}
296+
297+
public async getAllDocumentIds(): Promise<string[]> {
298+
try {
299+
const ids = await mongoService.getAllDocumentIds();
300+
Logger.info(`Retrieved ${ids.length} document IDs from mongoService.`);
301+
return ids;
302+
} catch (error) {
303+
Logger.error('Error retrieving all document IDs via mongoService:', error);
304+
throw error; // Re-throw to allow controller to handle
305+
}
306+
}
296307
}
297308

298309
// Export a promise that resolves when the service is initialized

src/api/swagger/ragManagement.ts

Lines changed: 76 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,127 +5,109 @@ const API_VERSION = process.env.API_VERSION || 'v1';
55
const ragManagementPaths = {
66
[`/api/${API_VERSION}/rag/manage/documents`]: {
77
post: {
8+
summary: 'Create a new document in the RAG system.',
9+
tags: ['RAG Management'],
10+
security: [{ ApiKeyAuth: [] }],
811
requestBody: {
12+
required: true,
913
content: {
1014
'multipart/form-data': {
1115
schema: {
16+
type: 'object',
1217
properties: {
1318
documentId: {
19+
type: 'string',
1420
description: 'Unique identifier for the document (e.g., filename or a unique ID).',
1521
example: 'my-document-123',
16-
type: 'string',
1722
},
1823
markdownFile: {
19-
description: 'The Markdown file to upload.',
20-
format: 'binary',
2124
type: 'string',
25+
format: 'binary',
26+
description: 'The Markdown file to upload.',
2227
},
2328
},
2429
required: ['documentId', 'markdownFile'],
25-
type: 'object',
2630
},
2731
},
2832
},
29-
required: true,
3033
},
3134
responses: {
3235
'201': { description: 'Document added successfully.' },
3336
'400': { description: 'Bad Request: Invalid input.' },
3437
'401': { description: 'Unauthorized: API key is missing or invalid.' },
3538
'500': { description: 'Internal Server Error.' },
3639
},
37-
security: [{ ApiKeyAuth: [] }],
38-
summary: 'Create a new document in the RAG system.',
39-
tags: ['RAG Management'],
4040
},
4141
},
4242
[`/api/${API_VERSION}/rag/manage/documents/{documentId}`]: {
43-
delete: {
44-
parameters: [
45-
{
46-
description: 'Identifier of the document to delete.',
47-
in: 'path',
48-
name: 'documentId',
49-
required: true,
50-
schema: { type: 'string' },
51-
},
52-
],
53-
responses: {
54-
'200': { description: 'Document deleted successfully.' },
55-
// "204": { description: "Document deleted successfully (No Content)." }, // Alternative
56-
'401': { description: 'Unauthorized: API key is missing or invalid.' },
57-
'404': { description: 'Not Found: Document not found (or already deleted).' },
58-
'500': { description: 'Internal Server Error.' },
59-
},
60-
security: [{ ApiKeyAuth: [] }],
61-
summary: 'Delete a document by its ID.',
62-
tags: ['RAG Management'],
63-
},
6443
get: {
44+
summary: 'Retrieve a document by its ID.',
45+
tags: ['RAG Management'],
46+
security: [{ ApiKeyAuth: [] }],
6547
parameters: [
6648
{
67-
description: 'Identifier of the document to retrieve.',
68-
in: 'path',
6949
name: 'documentId',
50+
in: 'path',
7051
required: true,
52+
description: 'Identifier of the document to retrieve.',
7153
schema: { type: 'string' },
7254
},
7355
],
7456
responses: {
7557
'200': {
58+
description: 'Successful retrieval of document content.',
7659
content: {
7760
'application/json': {
7861
schema: {
62+
type: 'object',
7963
properties: {
64+
documentId: { type: 'string' },
8065
content: {
81-
description: 'The full markdown content of the document.',
8266
type: 'string',
67+
description: 'The full markdown content of the document.',
8368
},
84-
documentId: { type: 'string' },
8569
},
86-
type: 'object',
8770
},
8871
},
8972
},
90-
description: 'Successful retrieval of document content.',
9173
},
9274
'401': { description: 'Unauthorized: API key is missing or invalid.' },
9375
'404': { description: 'Not Found: Document not found.' },
9476
'500': { description: 'Internal Server Error.' },
9577
},
96-
security: [{ ApiKeyAuth: [] }],
97-
summary: 'Retrieve a document by its ID.',
98-
tags: ['RAG Management'],
9978
},
10079
put: {
80+
summary: 'Update an existing document.',
81+
tags: ['RAG Management'],
82+
security: [{ ApiKeyAuth: [] }],
10183
parameters: [
10284
{
103-
description: 'Identifier of the document to update.',
104-
in: 'path',
10585
name: 'documentId',
86+
in: 'path',
10687
required: true,
88+
description: 'Identifier of the document to update.',
10789
schema: { type: 'string' },
10890
},
10991
],
11092
requestBody: {
93+
required: true,
11194
content: {
11295
'multipart/form-data': {
11396
// Consistent with POST
11497
schema: {
98+
type: 'object',
11599
properties: {
116100
// documentId is in path, so not needed here again unless we want to allow changing it (unusual for PUT)
117101
markdownFile: {
118-
description: 'The new Markdown file to replace the existing document.',
119-
format: 'binary',
120102
type: 'string',
103+
format: 'binary',
104+
description: 'The new Markdown file to replace the existing document.',
121105
},
122106
},
123107
required: ['markdownFile'],
124-
type: 'object',
125108
},
126109
},
127110
},
128-
required: true,
129111
},
130112
responses: {
131113
'200': { description: 'Document updated successfully.' },
@@ -134,10 +116,59 @@ const ragManagementPaths = {
134116
'404': { description: 'Not Found: Document not found.' },
135117
'500': { description: 'Internal Server Error.' },
136118
},
119+
},
120+
delete: {
121+
summary: 'Delete a document by its ID.',
122+
tags: ['RAG Management'],
137123
security: [{ ApiKeyAuth: [] }],
138-
summary: 'Update an existing document.',
124+
parameters: [
125+
{
126+
name: 'documentId',
127+
in: 'path',
128+
required: true,
129+
description: 'Identifier of the document to delete.',
130+
schema: { type: 'string' },
131+
},
132+
],
133+
responses: {
134+
'200': { description: 'Document deleted successfully.' },
135+
// "204": { description: "Document deleted successfully (No Content)." }, // Alternative
136+
'401': { description: 'Unauthorized: API key is missing or invalid.' },
137+
'404': { description: 'Not Found: Document not found (or already deleted).' },
138+
'500': { description: 'Internal Server Error.' },
139+
},
140+
},
141+
},
142+
[`/api/${API_VERSION}/rag/manage/documents:all`]: {
143+
get: {
144+
summary: 'Get all document IDs',
139145
tags: ['RAG Management'],
146+
security: [{ ApiKeyAuth: [] }],
147+
responses: {
148+
'200': {
149+
description: 'Successful retrieval of all document IDs.',
150+
content: {
151+
'application/json': {
152+
schema: {
153+
type: 'object',
154+
properties: {
155+
documentIds: {
156+
type: 'array',
157+
items: { type: 'string' },
158+
example: ['doc-abc-123', 'doc-def-456'],
159+
},
160+
},
161+
},
162+
},
163+
},
164+
},
165+
'401': { description: 'Unauthorized: API key is missing or invalid.' },
166+
'503': { description: 'Service Unavailable: RAG service is not ready.' },
167+
'500': { description: 'Internal Server Error.' },
168+
},
140169
},
170+
// Note: Based on the router, a POST operation to this path also exists.
171+
// Its definition should be added here if it's different from POST /rag/manage/documents.
141172
},
142173
};
143174

src/jobs/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ void runSyncDocuments();
55

66
// todo: ideally a cronjob should be used to only spin up job pod when required
77
// but nvm this is good enough for now
8+
// todo: consider caching locally in mongodb to reduce requests to github
89
setInterval(() => void runSyncDocuments(), 86400000);

0 commit comments

Comments
 (0)