Skip to content

Commit 7a2df53

Browse files
committed
Create request validation
1 parent e993794 commit 7a2df53

File tree

6 files changed

+67
-72
lines changed

6 files changed

+67
-72
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"scripts": {
33
"pre-commit": "npm run --workspaces pre-commit",
4-
"prepare": "husky"
4+
"prepare": "husky prepare"
55
},
66
"devDependencies": {
77
"husky": "^9.1.6"

question-service/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"express": "^4.21.0",
2323
"mongodb": "^6.9.0",
2424
"mongoose": "^8.6.3",
25-
"prettier": "^3.3.3"
25+
"prettier": "^3.3.3",
26+
"zod": "^3.23.8"
2627
},
2728
"devDependencies": {
2829
"@types/cors": "^2.8.17",

question-service/pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1-
import { UUID } from 'crypto';
1+
import { z } from 'zod';
2+
import { ObjectId } from 'mongodb';
23

3-
export interface Questions {
4-
_question_id: number;
5-
difficulty: number; // 1, 2, 3
6-
description: string;
7-
examples: string[];
8-
constraints: string;
9-
tags: string[];
10-
title_slug: string;
11-
title: string;
12-
pictures?: File[];
13-
}
4+
// object id schema
5+
const objectIdSchema = z.instanceof(ObjectId);
146

15-
export interface UserQuestions {
16-
_user_id: UUID;
17-
_question_id: UUID;
18-
status: string; // 'completed', 'in-progress', 'not-started'
19-
}
7+
export const QuestionsSchema = z.object({
8+
_id: objectIdSchema,
9+
difficulty: z.number(),
10+
description: z.string(),
11+
examples: z.array(z.string()),
12+
constraints: z.string(),
13+
tags: z.array(z.string()),
14+
title_slug: z.string(),
15+
title: z.string(),
16+
pictures: z.array(z.instanceof(File)).optional(),
17+
});
18+
19+
export const UserQuestionsSchema = z.object({
20+
_id: objectIdSchema,
21+
_user_id: objectIdSchema,
22+
_question_id: objectIdSchema,
23+
status: z.enum(['completed', 'in-progress', 'not-started']),
24+
});

question-service/src/routes/questionsController.ts

Lines changed: 30 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
import express, { Request, Response } from 'express';
33
import { Collection, ObjectId } from 'mongodb';
44
import { connectToDB } from '../db/mongoClient';
5-
import { Questions } from '../models/types';
5+
import { QuestionsSchema } from '../models/types';
6+
import { z } from 'zod';
67

78
const router = express.Router();
9+
type Questions = z.infer<typeof QuestionsSchema>;
10+
811
let questionsCollection: Collection<Questions>;
912

1013
// Middleware to connect to MongoDB and get the collection
@@ -19,7 +22,7 @@ router.use(async (_, res, next) => {
1922
});
2023

2124
// GET all items
22-
router.get('/', async (req: Request, res: Response) => {
25+
router.get('/', async (_, res: Response) => {
2326
try {
2427
const items = await questionsCollection.find().toArray();
2528
res.status(200).json(items);
@@ -29,18 +32,19 @@ router.get('/', async (req: Request, res: Response) => {
2932
});
3033

3134
// Update a question
32-
router.put('/:questionId', async (req: Request, res: Response) => {
33-
const { questionId } = req.params;
34-
const updatedQuestion: Questions = req.body;
35+
router.put('/:id', async (req: Request, res: Response) => {
36+
const { id } = req.params;
37+
const parsedResult = QuestionsSchema.safeParse(req.body);
38+
if (!parsedResult.success) {
39+
return res
40+
.status(400)
41+
.json({ error: 'Invalid question data. Please check your input.' });
42+
}
3543

3644
try {
37-
if (!updatedQuestion || typeof updatedQuestion !== 'object') {
38-
return res.status(400).json({ error: 'Invalid question data.' });
39-
}
40-
4145
const result = await questionsCollection.updateOne(
42-
{ _id: new ObjectId(questionId) },
43-
{ $set: updatedQuestion },
46+
{ _id: new ObjectId(id) },
47+
{ $set: parsedResult.data },
4448
);
4549

4650
if (result.matchedCount === 0) {
@@ -49,7 +53,7 @@ router.put('/:questionId', async (req: Request, res: Response) => {
4953

5054
res.status(200).json({
5155
message: 'Question updated successfully',
52-
data: [{ _id: questionId }],
56+
data: [{ _id: id }],
5357
});
5458
} catch (error) {
5559
console.error('Error updating question:', error);
@@ -58,54 +62,29 @@ router.put('/:questionId', async (req: Request, res: Response) => {
5862
});
5963

6064
// POST a new question
61-
router.post('/', async (req: Request, res: Response) => {
62-
try {
63-
const newQuestion: Questions = req.body; // Assume the body contains a question object
64-
65-
const {
66-
difficulty,
67-
description,
68-
examples,
69-
constraints,
70-
tags,
71-
title_slug,
72-
title,
73-
pictures,
74-
} = newQuestion;
65+
router.post('/post', async (req: Request, res: Response) => {
66+
const parseResult = QuestionsSchema.safeParse(req.body);
7567

76-
if (
77-
typeof difficulty !== 'number' ||
78-
typeof description !== 'string' ||
79-
!Array.isArray(examples) ||
80-
typeof constraints !== 'string' ||
81-
!Array.isArray(tags) ||
82-
typeof title_slug !== 'string' ||
83-
typeof title !== 'string'
84-
) {
85-
return res
86-
.status(400)
87-
.json({ error: 'Invalid question data. Please check your input.' });
88-
}
89-
90-
const result = await questionsCollection.insertOne(newQuestion);
91-
92-
res.status(201).json({
93-
message: 'Question inserted successfully',
94-
data: [{ _id: result.insertedId }],
95-
});
68+
if (!parseResult.success) {
69+
return res
70+
.status(400)
71+
.json({ error: 'Invalid question data. Please check your input.' });
72+
}
73+
try {
74+
const result = await questionsCollection.insertOne(parseResult.data);
75+
res.status(201).json(result);
9676
} catch (error) {
97-
console.error('Error inserting question:', error);
9877
res.status(500).json({ error: 'Failed to insert question' });
9978
}
10079
});
10180

10281
// DELETE a question
103-
router.delete('/:questionId', async (req: Request, res: Response) => {
104-
const { questionId } = req.params;
82+
router.delete('/:id', async (req: Request, res: Response) => {
83+
const { id } = req.params;
10584

10685
try {
10786
const result = await questionsCollection.deleteOne({
108-
_id: new ObjectId(questionId),
87+
_id: new ObjectId(id),
10988
});
11089

11190
if (result.deletedCount === 0) {
@@ -116,7 +95,7 @@ router.delete('/:questionId', async (req: Request, res: Response) => {
11695
message: 'Question deleted successfully',
11796
data: [
11897
{
119-
_id: questionId,
98+
_id: id,
12099
},
121100
],
122101
});

question-service/src/routes/userQuestionsController.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
import express, { Request, Response } from 'express';
33
import { Collection } from 'mongodb';
44
import { connectToDB } from '../db/mongoClient';
5-
import { UserQuestions } from '../models/types';
5+
import { UserQuestionsSchema } from '../models/types';
6+
import { z } from 'zod';
67

78
const router = express.Router();
89
let questionsCollection: Collection<UserQuestions>;
10+
type UserQuestions = z.infer<typeof UserQuestionsSchema>;
911

1012
// Middleware to connect to MongoDB and get the collection
1113
router.use(async (_, res, next) => {
@@ -19,7 +21,7 @@ router.use(async (_, res, next) => {
1921
});
2022

2123
// GET all items
22-
router.get('/', async (req: Request, res: Response) => {
24+
router.get('/', async (_, res: Response) => {
2325
try {
2426
const items = await questionsCollection.find().toArray();
2527
res.status(200).json(items);

0 commit comments

Comments
 (0)