Skip to content

Commit 8e2f51e

Browse files
committed
Add confirmMatch event
1 parent a33007b commit 8e2f51e

File tree

4 files changed

+147
-40
lines changed

4 files changed

+147
-40
lines changed

backend/gateway-service/package-lock.json

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/gateway-service/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"ioredis": "^5.4.1",
3535
"passport-jwt": "^4.0.1",
3636
"reflect-metadata": "^0.1.13",
37-
"rxjs": "^7.8.1"
37+
"rxjs": "^7.8.1",
38+
"uuid": "^10.0.0"
3839
},
3940
"devDependencies": {
4041
"@nestjs/cli": "^10.0.0",
@@ -44,6 +45,7 @@
4445
"@types/jest": "^29.5.2",
4546
"@types/node": "^20.3.1",
4647
"@types/supertest": "^2.0.12",
48+
"@types/uuid": "^10.0.0",
4749
"@typescript-eslint/eslint-plugin": "^6.0.0",
4850
"@typescript-eslint/parser": "^6.0.0",
4951
"eslint": "^8.42.0",

backend/gateway-service/src/modules/match/match.controller.ts

Lines changed: 120 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ import {
1818
MATCH_CONFIRMED,
1919
MATCH_TIMEOUT,
2020
MATCH_REQUESTED,
21-
MATCH_ERROR,
2221
EXCEPTION,
2322
} from './match.event';
24-
import { CANCEL_MATCH, FIND_MATCH } from './match.message';
23+
import { CANCEL_MATCH, CONFIRM_MATCH, FIND_MATCH } from './match.message';
24+
import { v4 as uuidv4 } from 'uuid';
2525

2626
@WebSocketGateway({
2727
namespace: '/match',
@@ -35,6 +35,15 @@ import { CANCEL_MATCH, FIND_MATCH } from './match.message';
3535
export class MatchGateway implements OnGatewayInit {
3636
@WebSocketServer() server: Server;
3737
private userSockets: Map<string, string> = new Map();
38+
private matchConfirmations: Map<
39+
string,
40+
{
41+
user1: string;
42+
user2: string;
43+
user1Confirmed: boolean;
44+
user2Confirmed: boolean;
45+
}
46+
> = new Map();
3847

3948
constructor(
4049
@Inject('MATCHING_SERVICE') private matchingClient: ClientProxy,
@@ -45,7 +54,9 @@ export class MatchGateway implements OnGatewayInit {
4554
afterInit() {
4655
// Subscribe to Redis Pub/Sub for match notifications
4756
this.redisService.subscribeToMatchEvents((matchedUsers) => {
48-
this.notifyUsersWithMatch(matchedUsers);
57+
const matchId = this.generateMatchId(); // Generate matchId
58+
this.notifyUsersMatchFound(matchId, matchedUsers); // Notify users of match with matchId
59+
this.trackMatchConfirmation(matchId, matchedUsers); // Track match confirmation
4960
});
5061

5162
this.redisService.subscribeToTimeoutEvents((timedOutUsers) => {
@@ -63,7 +74,7 @@ export class MatchGateway implements OnGatewayInit {
6374
!payload.selectedTopic ||
6475
!payload.selectedDifficulty
6576
) {
66-
client.emit(EXCEPTION, 'Invalid match request payload.');
77+
client.emit(EXCEPTION, 'Invalid payload for match request.');
6778
return;
6879
}
6980

@@ -95,10 +106,7 @@ export class MatchGateway implements OnGatewayInit {
95106
@MessageBody() payload: { userId: string },
96107
) {
97108
if (!payload.userId) {
98-
client.emit(
99-
EXCEPTION,
100-
'Invalid userId. Please check your payload and try again.',
101-
);
109+
client.emit(EXCEPTION, 'Invalid userId.');
102110
return;
103111
}
104112

@@ -125,36 +133,45 @@ export class MatchGateway implements OnGatewayInit {
125133
}
126134
}
127135

128-
// Notify both matched users via WebSocket
129-
notifyUsersWithMatch(matchedUsers: string[]) {
130-
const [user1, user2] = matchedUsers;
131-
const user1SocketId = this.getUserSocketId(user1);
132-
const user2SocketId = this.getUserSocketId(user2);
133-
if (user1SocketId && user2SocketId) {
134-
this.server.to(user1SocketId).emit(MATCH_FOUND, {
135-
message: `You have been matched with user ${user2}`,
136-
matchedUserId: user2,
137-
});
138-
this.server.to(user2SocketId).emit(MATCH_FOUND, {
139-
message: `You have been matched with user ${user1}`,
140-
matchedUserId: user1,
141-
});
136+
@SubscribeMessage(CONFIRM_MATCH)
137+
async handleConfirmMatch(
138+
@ConnectedSocket() client: Socket,
139+
@MessageBody() payload: { userId: string; matchId: string },
140+
) {
141+
if (!payload.userId || !payload.matchId) {
142+
client.emit(EXCEPTION, 'Invalid message payload for match confirmation.');
143+
return;
142144
}
143-
}
144145

145-
notifyUsersWithTimeout(timedOutUsers: string[]) {
146-
timedOutUsers.forEach((user) => {
147-
const socketId = this.getUserSocketId(user);
148-
if (socketId) {
149-
this.server.to(socketId).emit(MATCH_TIMEOUT, {
150-
message: `You have been timed out.`,
151-
timedOutUserId: user,
152-
});
153-
}
154-
});
146+
const { userId, matchId } = payload;
147+
if (!this.validateUserId(client, userId)) {
148+
return;
149+
}
150+
151+
// Get the confirmation state of the users
152+
const confirmationState = this.matchConfirmations.get(matchId);
153+
if (!confirmationState) {
154+
client.emit(EXCEPTION, 'Invalid Match Id.');
155+
return;
156+
}
157+
158+
// Checks which user is confirming the match
159+
if (confirmationState.user1 === payload.userId) {
160+
confirmationState.user1Confirmed = true;
161+
} else if (confirmationState.user2 === payload.userId) {
162+
confirmationState.user2Confirmed = true;
163+
} else {
164+
client.emit(EXCEPTION, 'Invalid userId for this match.');
165+
return;
166+
}
167+
168+
// Check if both users have confirmed
169+
if (confirmationState.user1Confirmed && confirmationState.user2Confirmed) {
170+
this.notifyUsersMatchConfirmed(payload.matchId, confirmationState);
171+
}
155172
}
156173

157-
async handleConnection(@ConnectedSocket() client: Socket) {
174+
async handleConnect(@ConnectedSocket() client: Socket) {
158175
const id = client.handshake.query.userId as string;
159176

160177
if (!id) {
@@ -183,10 +200,8 @@ export class MatchGateway implements OnGatewayInit {
183200
return;
184201
}
185202

186-
if (id) {
187-
this.userSockets.set(id as string, client.id);
188-
console.log(`User ${id} connected with socket ID ${client.id}`);
189-
}
203+
this.userSockets.set(id as string, client.id);
204+
console.log(`User ${id} connected with socket ID ${client.id}`);
190205
} catch (error) {
191206
this.emitExceptionAndDisconnect(client, error.message);
192207
return;
@@ -230,6 +245,73 @@ export class MatchGateway implements OnGatewayInit {
230245
}
231246
}
232247

248+
trackMatchConfirmation(matchId: string, matchedUsers: string[]) {
249+
const confirmationState = {
250+
user1: matchedUsers[0],
251+
user2: matchedUsers[1],
252+
user1Confirmed: false,
253+
user2Confirmed: false,
254+
};
255+
256+
this.matchConfirmations.set(matchId, confirmationState);
257+
}
258+
259+
notifyUsersMatchFound(matchId: string, matchedUsers: string[]) {
260+
const [user1, user2] = matchedUsers;
261+
const user1SocketId = this.getUserSocketId(user1);
262+
const user2SocketId = this.getUserSocketId(user2);
263+
if (user1SocketId && user2SocketId) {
264+
this.server.to(user1SocketId).emit(MATCH_FOUND, {
265+
message: `You have been matched with user ${user2}`,
266+
matchId,
267+
});
268+
this.server.to(user2SocketId).emit(MATCH_FOUND, {
269+
message: `You have been matched with user ${user1}`,
270+
matchId,
271+
});
272+
}
273+
}
274+
275+
notifyUsersWithTimeout(timedOutUsers: string[]) {
276+
timedOutUsers.forEach((user) => {
277+
const socketId = this.getUserSocketId(user);
278+
if (socketId) {
279+
this.server.to(socketId).emit(MATCH_TIMEOUT, {
280+
message: `You have been timed out.`,
281+
timedOutUserId: user,
282+
});
283+
}
284+
});
285+
}
286+
287+
notifyUsersMatchConfirmed(matchId: string, confirmationState: any) {
288+
const user1SocketId = this.getUserSocketId(confirmationState.user1);
289+
const user2SocketId = this.getUserSocketId(confirmationState.user2);
290+
291+
const sessionId = this.generateSessionId(); // TODO - To be substituted with collab-service method in next MS
292+
293+
if (user1SocketId && user2SocketId) {
294+
this.server.to(user1SocketId).emit(MATCH_CONFIRMED, {
295+
message: 'Both users have confirmed the match.',
296+
sessionId,
297+
});
298+
this.server.to(user2SocketId).emit(MATCH_CONFIRMED, {
299+
message: 'Both users have confirmed the match.',
300+
sessionId,
301+
});
302+
}
303+
304+
this.matchConfirmations.delete(matchId);
305+
}
306+
307+
private generateMatchId(): string {
308+
return uuidv4();
309+
}
310+
311+
private generateSessionId(): string {
312+
return uuidv4();
313+
}
314+
233315
private getUserSocketId(userId: string): string | undefined {
234316
return this.userSockets.get(userId);
235317
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export const FIND_MATCH = 'findMatch';
22
export const CANCEL_MATCH = 'cancelMatch';
3+
export const CONFIRM_MATCH = 'confirmMatch';

0 commit comments

Comments
 (0)