Skip to content

Commit 4d28d5b

Browse files
committed
PEER-219: Streamline query logic and integrate with matching service
Signed-off-by: SeeuSim <[email protected]>
1 parent ec62104 commit 4d28d5b

37 files changed

+348
-430
lines changed

.eslintrc.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@
2121
"simple-import-sort"
2222
],
2323
"rules": {
24+
"@typescript-eslint/array-type": [
25+
"error",
26+
{
27+
"default": "generic",
28+
"readonly": "generic"
29+
}
30+
],
2431
"@typescript-eslint/no-explicit-any": "warn",
2532
"@typescript-eslint/explicit-module-boundary-types": "off",
2633
"@typescript-eslint/explicit-function-return-type": 0,

backend/matching/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { logger } from '@/lib/utils';
55
import server, { io } from '@/server';
66
import { initWorker } from '@/workers';
77

8-
const workers: ChildProcess[] = [];
8+
const workers: Array<ChildProcess> = [];
99

1010
const port = Number.parseInt(EXPRESS_PORT || '8001');
1111

backend/matching/src/services/get-match-items.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { IMatchItemsResponse, IMatchType } from '@/types';
33

44
import { createRoom } from './collab';
55
import { getRandomQuestion } from './question';
6-
import { fetchAttemptedQuestions } from './user';
76

87
export async function getMatchItems(
98
searchIdentifier: IMatchType,
@@ -17,26 +16,13 @@ export async function getMatchItems(
1716
throw new Error('Both user IDs are required');
1817
}
1918

20-
let allAttemptedQuestions: number[] = [];
21-
22-
try {
23-
const [attemptedQuestions1, attemptedQuestions2] = await Promise.all([
24-
fetchAttemptedQuestions(userId1),
25-
fetchAttemptedQuestions(userId2),
26-
]);
27-
allAttemptedQuestions = [...new Set([...attemptedQuestions1, ...attemptedQuestions2])];
28-
} catch (error) {
29-
logger.error('Error in getMatchItems: Failed to fetch attempted questions', error);
30-
}
31-
3219
const topics = topic?.split('|') ?? [];
3320
const payload = {
34-
attemptedQuestions: allAttemptedQuestions,
21+
userId1,
22+
userId2,
3523
...(searchIdentifier === 'difficulty' && difficulty ? { difficulty } : {}),
36-
...(searchIdentifier === 'topic' && topic ? { topic: topics } : {}),
37-
...(searchIdentifier === 'exact match' && topic && difficulty
38-
? { topic: topics, difficulty }
39-
: {}),
24+
...(searchIdentifier === 'topic' && topic ? { topics } : {}),
25+
...(searchIdentifier === 'exact match' && topic && difficulty ? { topics, difficulty } : {}),
4026
};
4127

4228
// Get a random question
@@ -55,7 +41,8 @@ export async function getMatchItems(
5541
questionId: question.id,
5642
};
5743
} catch (error) {
58-
logger.error('Error in getMatchItems:', error);
44+
const { name, message, stack, cause } = error as Error;
45+
logger.error(`Error in getMatchItems: ${JSON.stringify({ name, message, stack, cause })}`);
5946
return undefined;
6047
}
6148
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import type { IGetRandomQuestionPayload, IQuestion, IServiceResponse } from '@/types';
1+
import type { IGetRandomQuestionPayload, IQuestion } from '@/types';
22

33
import { questionServiceClient, routes } from './_hosts';
44

55
export async function getRandomQuestion(payload: IGetRandomQuestionPayload): Promise<IQuestion> {
6-
const response = await questionServiceClient.post<IServiceResponse<{ question: IQuestion }>>(
6+
const response = await questionServiceClient.post<IQuestion>(
77
routes.QUESTION_SERVICE.GET_RANDOM_QN.path,
88
payload
99
);
1010

11-
if (response.status !== 200 || !response.data.data) {
12-
throw new Error(response.data.error?.message || 'Failed to get a random question');
11+
if (response.status !== 200 || !response.data) {
12+
throw new Error(response.statusText || 'Failed to get a random question');
1313
}
1414

15-
return response?.data?.data?.question ?? undefined;
15+
return response?.data ?? undefined;
1616
}

backend/matching/src/services/user.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { routes, userServiceClient } from './_hosts';
22

3-
export async function fetchAttemptedQuestions(userId: string): Promise<number[]> {
4-
const response = await userServiceClient.post<number[]>(
3+
export async function fetchAttemptedQuestions(userId: string): Promise<Array<number>> {
4+
const response = await userServiceClient.post<Array<number>>(
55
routes.USER_SERVICE.ATTEMPTED_QNS.GET.path,
66
{
77
userId,

backend/matching/src/types/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type IRequestMatchRESTPayload = {
1010
};
1111

1212
export type IRequestMatchWSPayload = {
13-
topic: string | string[];
13+
topic: string | Array<string>;
1414
difficulty: string;
1515
};
1616

@@ -61,9 +61,10 @@ export interface IQuestion {
6161
}
6262

6363
export interface IGetRandomQuestionPayload {
64-
attemptedQuestions: number[];
64+
userId1: string;
65+
userId2: string;
6566
difficulty?: string;
66-
topic?: Array<string>;
67+
topics?: Array<string>;
6768
}
6869

6970
export interface IMatchItemsResponse {

backend/matching/src/workers/matcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ async function processMatch(
7373
]);
7474

7575
// Notify both sockets
76-
const { ...matchItems } = await getMatchItems(
76+
const matchItems = await getMatchItems(
7777
searchIdentifier,
7878
topic,
7979
difficulty,

backend/question/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"http-status-codes": "^2.3.0",
3232
"pino": "^9.4.0",
3333
"pino-http": "^10.3.0",
34-
"postgres": "^3.4.4"
34+
"postgres": "^3.4.4",
35+
"uuid": "^11.0.2"
3536
},
3637
"devDependencies": {
3738
"@swc/core": "^1.7.26",

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

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { Request, Response } from 'express';
2+
import { StatusCodes } from 'http-status-codes';
23

3-
import { addAttempt } from '../services/post/addAttempt';
4+
import { logger } from '@/lib/utils';
5+
import { isValidUUID } from '@/lib/uuid';
6+
import { getQuestionAttempts } from '@/services/get/get-attempts';
7+
import { addAttempt } from '@/services/post/addAttempt';
48

59
// Define the expected request body structure
610
interface AddAttemptRequestBody {
@@ -34,14 +38,40 @@ export const createAttempt = async (
3438
});
3539

3640
// Respond with success
37-
res.status(201).json({ message: 'Attempt added successfully', result });
38-
} catch (error) {
39-
console.error('Error adding attempt:', error);
41+
res.status(StatusCodes.OK).json({ message: 'Attempt added successfully', result });
42+
} catch (err) {
43+
const { name, message, stack, cause } = err as Error;
44+
logger.error({ name, message, stack, cause }, 'Error adding attempt');
4045

4146
// Enhanced error response with error details
4247
res.status(500).json({
4348
error: 'Error adding attempt',
44-
details: error instanceof Error ? error.message : 'Unknown error',
49+
details: err instanceof Error ? err.message : 'Unknown error',
50+
});
51+
}
52+
};
53+
54+
export const getAttempts = async (
55+
req: Request<unknown, unknown, Partial<Parameters<typeof getQuestionAttempts>[0]>, unknown>,
56+
res: Response
57+
) => {
58+
const { questionId, userId, limit, offset } = req.body;
59+
60+
if (!questionId || isNaN(questionId) || !userId || !isValidUUID(userId)) {
61+
return res.status(StatusCodes.UNPROCESSABLE_ENTITY).json('Malformed Request');
62+
}
63+
64+
try {
65+
const result = await getQuestionAttempts({ questionId, userId, limit, offset });
66+
return res.status(StatusCodes.OK).json(result);
67+
} catch (err) {
68+
const { name, message, stack, cause } = err as Error;
69+
logger.error({ name, message, stack, cause }, 'Error retrieving attempts');
70+
71+
// Enhanced error response with error details
72+
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
73+
error: 'Error retrieving attempts',
74+
details: err instanceof Error ? err.message : 'Unknown error',
4575
});
4676
}
4777
};

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

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,10 @@ import {
55
getDifficultiesService,
66
getQuestionDetailsService,
77
getQuestionsService,
8-
getRandomQuestionService,
98
getTopicsService,
109
searchQuestionsByTitleService,
1110
} from '@/services/get/index';
12-
import type {
13-
IGetQuestionPayload,
14-
IGetQuestionsPayload,
15-
IGetRandomQuestionPayload,
16-
} from '@/services/get/types';
11+
import type { IGetQuestionPayload, IGetQuestionsPayload } from '@/services/get/types';
1712
import {
1813
createQuestionService,
1914
deleteQuestionService,
@@ -30,7 +25,7 @@ export const getQuestions = async (req: Request, res: Response): Promise<Respons
3025
const payload: IGetQuestionsPayload = {
3126
questionName: questionName as string,
3227
difficulty: difficulty as string,
33-
topic: topic as string[],
28+
topic: topic as Array<string>,
3429
pageNum: parseInt(pageNum as string) || 0,
3530
recordsPerPage: parseInt(recordsPerPage as string) || 20,
3631
};
@@ -74,25 +69,6 @@ export const getQuestionDetails = async (req: Request, res: Response): Promise<R
7469
}
7570
};
7671

77-
export const getRandomQuestion = async (req: Request, res: Response): Promise<Response> => {
78-
const payload: IGetRandomQuestionPayload = {
79-
attemptedQuestions: req.body.attemptedQuestions,
80-
difficulty: req.body.difficulty,
81-
topic: req.body.topic,
82-
};
83-
84-
try {
85-
const result = await getRandomQuestionService(payload);
86-
return res.status(result.code).json(result);
87-
} catch (error) {
88-
console.log('error', error);
89-
90-
return res
91-
.status(StatusCodes.INTERNAL_SERVER_ERROR)
92-
.json({ success: false, message: 'An error occurred', error });
93-
}
94-
};
95-
9672
export const searchQuestionsByTitle = async (req: Request, res: Response): Promise<Response> => {
9773
const { title } = req.query;
9874
const page = parseInt(req.query.page as string) || 1;

0 commit comments

Comments
 (0)