diff --git a/apps/server/README.md b/apps/server/README.md index 3aa8c47c..16e45447 100644 --- a/apps/server/README.md +++ b/apps/server/README.md @@ -80,29 +80,30 @@ When using a Docker container, I had to switch to port 5433 for it to work. #### Working on the database -There isn't a script that sets up the database skeleton with the necessary tables if they don't exist. -Therefore, there is a bit of extra work at the moment to set everything up. You'll have to create two tables in you newly created database; `chat` and `chat_message`. The columns should be as found for the corresponding classes in the file `apps/server/repository/chat-repository.ts`. +It is possible to set up the tables using an endpoint. If the connection to the database is up, you can use the endpoint +`/api/v2/database/setup` + Example tables for PostgresSQL **chat** with columns: -- id: uuid -- user_id: uuid -- created: timestamp with timezone (use function now() as default in Postgres) -- last_updated: timestamp with timezone (use function now() as default in Postgres) -- title: character varying (255) +- id: uuid [PKey] +- user_id: varchar (255) +- created: timestamp +- last_updated: timestamp +- title: varchar (255) **chat_message** with columns: -- id: uuid -- chat_id: uuid -- user_id: character varying (255) +- id: uuid [PKey] +- chat_id: uuid [FKey] +- user_id: varchar (255) - message: text -- role: character varying (255) -- created: timestamp with timezone (use function now() as default in Postgres) +- role: varchar (255) +- created: timestamp ### Testing diff --git a/apps/server/implementations/repositories/postgres-chat-repository.ts b/apps/server/implementations/repositories/postgres-chat-repository.ts index d04955da..aaca9c43 100644 --- a/apps/server/implementations/repositories/postgres-chat-repository.ts +++ b/apps/server/implementations/repositories/postgres-chat-repository.ts @@ -19,6 +19,20 @@ export class PostgresChatRepository implements IChatRepository { }) } + async addChat(userId: string): Promise { + await this.client.connect() + try { + const addChatQuery = { + text: 'INSERT INTO chat (user_id) VALUES ($1) RETURNING *', + values: [userId], + } + const result = await this.client.query(addChatQuery) + return result.rows[0] + } catch (error) { + console.error('Error occurred when creating a new chat.', error) + } + } + async addChatMessage( chatId: string, userId: string, @@ -27,11 +41,11 @@ export class PostgresChatRepository implements IChatRepository { ): Promise { await this.client.connect() try { - const query = { + const addChatMessageQuery = { text: 'INSERT INTO chat_message (chat_id, user_id, message, role) VALUES ($1, $2, $3, $4) RETURNING *', values: [chatId, userId, message, role], } - const result = await this.client.query(query) + const result = await this.client.query(addChatMessageQuery) return result.rows[0] } catch (error) { console.error('Error occurred when adding chat message:', error) @@ -41,25 +55,39 @@ export class PostgresChatRepository implements IChatRepository { async deleteChat(chatId: string): Promise { await this.client.connect() try { - const query = { - text: 'DELETE FROM chat_message WHERE chat_id = $1', + const deleteChatQuery = { + text: 'DELETE FROM chat WHERE chat_id = $1', values: [chatId], } - const result = await this.client.query(query) + const result = await this.client.query(deleteChatQuery) return result.rows.count() > 0 } catch (error) { - console.error('Error occurred when adding chat message:', error) + console.error('Error occurred when adding chat message.', error) + } + } + + async getChat(chatId: string): Promise { + await this.client.connect() + try { + const getChatQuery = { + text: 'SELECT * FROM chat WHERE chat_id = $1', + values: [chatId], + } + const result = await this.client.query(getChatQuery) + return result.rows[0] + } catch (error) { + console.log('Error occurred when fetching chat.', error) } } async getChatMessagesForChat(chatId: string): Promise { await this.client.connect() try { - const query = { + const getChatMessagesForChatQuery = { text: 'SELECT * FROM chat_message WHERE chat_id = $1', values: [chatId], } - const result = await this.client.query(query) + const result = await this.client.query(getChatMessagesForChatQuery) return result.rows } catch (error) { console.error( @@ -76,14 +104,50 @@ export class PostgresChatRepository implements IChatRepository { ): Promise { await this.client.connect() try { - const query = { + const getChatsForUserQuery = { text: 'SELECT * FROM chat WHERE user_id = $1', values: [userId], } - const result = await this.client.query(query) + const result = await this.client.query(getChatsForUserQuery) return result.rows } catch (error) { console.error('Error occurred when fetching chats:', error.message) } } + + /** + * This method is only used to set up database locally. Should be removed when database is created in the cloud. + */ + async setupPostgres(): Promise { + await this.client.connect() + try { + await this.client.query('BEGIN') + await this.client.query( + `CREATE TABLE IF NOT EXISTS chat ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + user_id VARCHAR(255) NOT NULL, + created TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + title VARCHAR(255) NOT NULL + );` + ) + + await this.client.query( + `CREATE TABLE IF NOT EXISTS chat_message ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + chat_id UUID NOT NULL, + user_id VARCHAR(255) NOT NULL, + message TEXT, + role VARCHAR(255) NOT NULL, + created TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + CONSTRAINT fk_chat_message_chat FOREIGN KEY (chat_id) REFERENCES chat (id) ON DELETE CASCADE + );` + ) + + await this.client.query('COMMIT') + console.log('Successfully created tables in database.') + } catch (error) { + console.error('Error creating tables in database.', error) + } + } } diff --git a/apps/server/repository/chat-repository.ts b/apps/server/repository/chat-repository.ts index 7364dfd1..7983a2b3 100644 --- a/apps/server/repository/chat-repository.ts +++ b/apps/server/repository/chat-repository.ts @@ -4,6 +4,14 @@ * with KnowitGPT in the form of chats and chat messages. */ export interface IChatRepository { + /** + * Creates a chat for a user. + * + * @param userId - The identifier of the user that the chat is created for. + * @returns The created chat + */ + addChat(userId: string): Promise + /** * Add a chat message for a chat. * diff --git a/apps/server/routers/databaseRouter.ts b/apps/server/routers/databaseRouter.ts index b176b1ea..55490a61 100644 --- a/apps/server/routers/databaseRouter.ts +++ b/apps/server/routers/databaseRouter.ts @@ -6,20 +6,27 @@ const router: Router = express.Router() const db = new PostgresChatRepository() router.delete('/chats', async (req, res) => { - const chatId = req.body.chatId - const result = await db.deleteChat(chatId) + const result = await db.deleteChat(req.body.chatId) + res.send(result) +}) + +router.get('/chat', async (req, res) => { + const result = await db.getChat(req.body.chatId) res.send(result) }) router.get('/chats', async (req, res) => { - const userId = req.body.userId - const result = await db.getChatsForUser(userId) + const result = await db.getChatsForUser(req.body.userId) res.send(result) }) router.get('/chatMessages', async (req, res) => { - const chatId = req.body.chatId - const result = await db.getChatMessagesForChat(chatId) + const result = await db.getChatMessagesForChat(req.body.chatId) + res.send(result) +}) + +router.post('/chat', async (req, res) => { + const result = await db.addChat(req.body.userId) res.send(result) }) @@ -33,4 +40,9 @@ router.post('/chatMessages', async (req, res) => { res.send(result) }) +router.post('/setup', async (_, res) => { + const result = await db.setupPostgres() + res.send(result) +}) + export { router as databaseRouter }