Skip to content

Commit 7b39e5f

Browse files
authored
Merge pull request #30 from CS3219-AY2425S1/feature/questions-integration
[Milestone 2] Add Questions Integration w backend APIs and remove code smells
2 parents 652012c + 14e8f49 commit 7b39e5f

File tree

22 files changed

+202
-465
lines changed

22 files changed

+202
-465
lines changed

backend/question/.env.local

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
EXPRESS_ENV="local"
2+
PEERPREP_UI_HOST="http://localhost:5173"
3+
4+
EXPRESS_PORT=9002
25
EXPRESS_DB_HOST="localhost"
36
EXPRESS_DB_PORT=5433
47
POSTGRES_DB="question"
58
POSTGRES_USER="peerprep-qn-express"
69
POSTGRES_PASSWORD="Xk8qEcEI2sizjfEn/lF6mLqiyBECjIHY3q6sdXf9poQ="
7-
PGDATA="/data/qn-db"
10+
PGDATA="/data/qn-db"

backend/question/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,26 @@
2020
"license": "ISC",
2121
"description": "",
2222
"dependencies": {
23+
"cors": "^2.8.5",
2324
"drizzle-orm": "^0.33.0",
2425
"env-cmd": "^10.1.0",
2526
"express": "^4.21.0",
27+
"helmet": "^8.0.0",
2628
"http-status-codes": "^2.3.0",
2729
"pino": "^9.4.0",
2830
"pino-http": "^10.3.0",
2931
"postgres": "^3.4.4"
3032
},
3133
"devDependencies": {
34+
"@swc/core": "^1.7.26",
35+
"@swc/helpers": "^0.5.13",
3236
"@types/express": "^4.17.21",
3337
"@types/node": "^22.5.5",
3438
"drizzle-kit": "^0.24.2",
3539
"nodemon": "^3.1.4",
3640
"pino-pretty": "^11.2.2",
3741
"ts-node": "^10.9.2",
38-
"tsc-alias": "^1.8.10"
42+
"tsc-alias": "^1.8.10",
43+
"tsconfig-paths": "^4.2.0"
3944
}
4045
}

backend/question/src/controller/question-controller.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { Request, Response } from 'express';
1+
import { StatusCodes } from 'http-status-codes';
2+
import type { Request, Response } from 'express';
3+
24
import {
35
getQuestionsService,
46
getQuestionDetailsService,
@@ -22,9 +24,16 @@ export const getQuestions = async (req: Request, res: Response): Promise<Respons
2224

2325
try {
2426
const result = await getQuestionsService(payload);
25-
return res.status(result.code).json(result);
27+
if (!result.data || result.code >= 400) {
28+
return res.status(result.code).json({
29+
message: result.error?.message ?? 'An error occurred',
30+
});
31+
}
32+
return res.status(result.code).json(result.data);
2633
} catch (error) {
27-
return res.status(500).json({ success: false, message: 'An error occurred', error });
34+
return res
35+
.status(StatusCodes.INTERNAL_SERVER_ERROR)
36+
.json({ success: false, message: 'An error occurred', error });
2837
}
2938
};
3039

@@ -35,9 +44,16 @@ export const getQuestionDetails = async (req: Request, res: Response): Promise<R
3544

3645
try {
3746
const result = await getQuestionDetailsService(payload);
38-
return res.status(result.code).json(result);
47+
if (!result.data || result.code >= 400) {
48+
return res.status(result.code).json({
49+
message: result.error?.message ?? 'An error occurred',
50+
});
51+
}
52+
return res.status(result.code).json(result.data);
3953
} catch (error) {
40-
return res.status(500).json({ success: false, message: 'An error occurred', error });
54+
return res
55+
.status(StatusCodes.INTERNAL_SERVER_ERROR)
56+
.json({ success: false, message: 'An error occurred', error });
4157
}
4258
};
4359

@@ -51,7 +67,9 @@ export const getRandomQuestion = async (req: Request, res: Response): Promise<Re
5167
const result = await getRandomQuestionService(payload);
5268
return res.status(result.code).json(result);
5369
} catch (error) {
54-
return res.status(500).json({ success: false, message: 'An error occurred', error });
70+
return res
71+
.status(StatusCodes.INTERNAL_SERVER_ERROR)
72+
.json({ success: false, message: 'An error occurred', error });
5573
}
5674
};
5775

@@ -61,13 +79,17 @@ export const searchQuestionsByTitle = async (req: Request, res: Response): Promi
6179
const limit = parseInt(req.query.limit as string) || 10;
6280

6381
if (!title) {
64-
return res.status(400).json({ success: false, message: 'Title is required' });
82+
return res
83+
.status(StatusCodes.UNPROCESSABLE_ENTITY)
84+
.json({ success: false, message: 'Title is required' });
6585
}
6686

6787
try {
6888
const result = await searchQuestionsByTitleService(title.toString(), page, limit);
6989
return res.status(result.code).json(result);
7090
} catch (error) {
71-
return res.status(500).json({ success: false, message: 'An error occurred', error });
91+
return res
92+
.status(StatusCodes.INTERNAL_SERVER_ERROR)
93+
.json({ success: false, message: 'An error occurred', error });
7294
}
7395
};

backend/question/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { logger } from '@/lib/utils';
22
import app, { dbHealthCheck } from '@/server';
33

4-
const port = process.env.PORT || 8001;
4+
const port = Number.parseInt(process.env.EXPRESS_PORT ?? '8001');
55

66
const listenMessage = `App listening on port: ${port}`;
77
app.listen(port, () => {
File renamed without changes.

backend/question/src/server.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
import { exit } from 'process';
2-
import questionsRouter from './routes/question-routes';
2+
3+
import cors from 'cors';
34
import express, { json } from 'express';
45
import pino from 'pino-http';
56
import { sql } from 'drizzle-orm';
7+
import helmet from 'helmet';
68

9+
import questionsRouter from '@/routes/question';
710
import { config, db } from '@/lib/db';
811
import { logger } from '@/lib/utils';
912

1013
const app = express();
1114
app.use(pino());
1215
app.use(json());
16+
app.use(helmet());
17+
app.use(
18+
cors({
19+
origin: [process.env.PEERPREP_UI_HOST!],
20+
credentials: true,
21+
})
22+
);
23+
1324
app.use('/questions', questionsRouter);
1425
app.get('/', async (_req, res) => {
1526
res.json({

backend/question/src/services/get/index.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
IGetRandomQuestionPayload,
1212
IGetRandomQuestionResponse,
1313
} from './types';
14+
import { StatusCodes } from 'http-status-codes';
1415

1516
export const getQuestionsService = async (
1617
payload: IGetQuestionsPayload
@@ -35,7 +36,8 @@ export const getQuestionsService = async (
3536
.from(questions)
3637
.where(and(...whereClause))
3738
.limit(recordsPerPage)
38-
.offset(offset);
39+
.offset(offset)
40+
.orderBy(questions.id);
3941

4042
const [results, totalCount] = await Promise.all([
4143
query,
@@ -47,7 +49,7 @@ export const getQuestionsService = async (
4749
]);
4850

4951
return {
50-
code: 200,
52+
code: StatusCodes.OK,
5153
data: {
5254
questions: results.map((q) => ({
5355
id: q.id,
@@ -73,7 +75,7 @@ export const getQuestionDetailsService = async (
7375

7476
if (result.length === 0) {
7577
return {
76-
code: 404,
78+
code: StatusCodes.NOT_FOUND,
7779
data: { question: null },
7880
error: {
7981
message: 'Question not found',
@@ -82,7 +84,7 @@ export const getQuestionDetailsService = async (
8284
}
8385

8486
return {
85-
code: 200,
87+
code: StatusCodes.OK,
8688
data: { question: result[0] },
8789
};
8890
};
@@ -117,7 +119,7 @@ export const getRandomQuestionService = async (
117119

118120
if (result.length === 0) {
119121
return {
120-
code: 404,
122+
code: StatusCodes.NOT_FOUND,
121123
data: { question: null },
122124
error: {
123125
message: 'No matching questions found',
@@ -126,7 +128,7 @@ export const getRandomQuestionService = async (
126128
}
127129

128130
return {
129-
code: 200,
131+
code: StatusCodes.OK,
130132
data: { question: result[0] },
131133
};
132134
};
@@ -156,7 +158,7 @@ export const searchQuestionsByTitleService = async (
156158

157159
// Return the results as per IGetQuestionsResponse format
158160
return {
159-
code: 200,
161+
code: StatusCodes.OK,
160162
data: {
161163
questions: results, // Directly returning the query results
162164
totalQuestions: results.length, // Count of questions returned

backend/user/drizzle/0000_steady_madame_masque.sql renamed to backend/user/drizzle/0000_initial_schema.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
CREATE TABLE IF NOT EXISTS "users" (
2-
"id" serial PRIMARY KEY NOT NULL,
2+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
33
"email" varchar(255) NOT NULL,
44
"username" varchar(255) NOT NULL,
55
"first_name" varchar(255) NOT NULL,

backend/user/drizzle/meta/0000_snapshot.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"id": "d8e4806c-713c-4202-a3f8-4a8be2c8b711",
2+
"id": "d0b8800a-d4b6-49f8-b2cf-57f4a64fc64a",
33
"prevId": "00000000-0000-0000-0000-000000000000",
44
"version": "7",
55
"dialect": "postgresql",
@@ -10,9 +10,10 @@
1010
"columns": {
1111
"id": {
1212
"name": "id",
13-
"type": "serial",
13+
"type": "uuid",
1414
"primaryKey": true,
15-
"notNull": true
15+
"notNull": true,
16+
"default": "gen_random_uuid()"
1617
},
1718
"email": {
1819
"name": "email",

backend/user/drizzle/meta/_journal.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
{
66
"idx": 0,
77
"version": "7",
8-
"when": 1727590732890,
9-
"tag": "0000_steady_madame_masque",
8+
"when": 1727623675374,
9+
"tag": "0000_initial_schema",
1010
"breakpoints": true
1111
}
1212
]

0 commit comments

Comments
 (0)