Skip to content

Commit 4e07e25

Browse files
AnphsLook-Its-Sky1solomonwakhunguDedsecKnight
authored
Leaderboard and Room/Round Circular Dependency (#40)
* Support For Joining and Leaving Rooms (RTC Side) * remove leaderboardKey * add leaderboardKey * GetLeaderboard() * refactored JoinRoom * fixed FindRoomEndpoint * refactored LeaveRoom * compressScoreAndTimeStamp * inverted time * deleteLeaderboard * Organized functions * Added comments * Fixed rdb dependency in round_test * typo * Fix Some Room Edge Cases That Crash Server * Moved CreateNewRound and StartRound to RoomController * removed RoundController * room cascade delete rounds * append round to room on round create * CreateNewRound by roomID path param * GetLeaderboardEndpoint * Deleted round-accessor * Fixed InitiateRoundStart * on room delete remove leaderboards * Move request types to separate file. (#37) * GetLeaderboard room.Rounds null check * feat: added database relation * feat: added implementation for round submission * refactor: refactored auto migrate logic * test: added unit test * refactor: changed evaluation order * feat: resolved remaining TODOs Added check to prevent double counting AC submissions in a round. Added check to prevent mismatch between problem and round's problemset. * refactor: fixed unit test * fix: added timestamp to submission * GetLeaderboardByRoomID and GetLeaderboardByRoundID * Remove test syntax errors * Create Round Participant if Round is already started * Leaderboard add leaderboardKey GetLeaderboard() refactored JoinRoom fixed FindRoomEndpoint refactored LeaveRoom compressScoreAndTimeStamp inverted time deleteLeaderboard Organized functions Added comments Fixed rdb dependency in round_test typo * Moved CreateNewRound and StartRound to RoomController removed RoundController room cascade delete rounds append round to room on round create CreateNewRound by roomID path param GetLeaderboardEndpoint Deleted round-accessor Fixed InitiateRoundStart on room delete remove leaderboards GetLeaderboard room.Rounds null check GetLeaderboardByRoomID and GetLeaderboardByRoundID Remove test syntax errors Create Round Participant if Round is already started * Merged RoundSubmissionService into RoundService * fix: fixed test case * Fixed RoundStart * Round Start once check * fix: fixed test case * Mike Feedback --------- Co-authored-by: Jude Joubert <[email protected]> Co-authored-by: Jude Joubert <[email protected]> Co-authored-by: Solomon Wakhungu <[email protected]> Co-authored-by: Mike Nguyen <[email protected]> Co-authored-by: Mike Nguyen <[email protected]>
1 parent f5567ba commit 4e07e25

File tree

13 files changed

+1940
-1972
lines changed

13 files changed

+1940
-1972
lines changed

central-service/controllers/room.go

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ func (controller *RoomController) CreateNewRoomEndpoint(c echo.Context) error {
2828
newRoom, err := controller.roomService.CreateRoom(&roomDTO, userAuthID)
2929
if err != nil {
3030
log.Printf("User id: %s failed to create room object: %v\n", userAuthID, err)
31-
if _, ok := err.(services.RoomServiceError); ok {
32-
return echo.NewHTTPError(http.StatusBadRequest, "Failed to create room. " + err.Error())
31+
if err, ok := err.(services.BSGError); ok {
32+
return echo.NewHTTPError(err.StatusCode, "Failed to create room. "+err.Error())
3333
}
3434
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create room. Please try again later")
3535
}
@@ -44,10 +44,10 @@ func (controller *RoomController) FindRoomEndpoint(c echo.Context) error {
4444
room, err := controller.roomService.FindRoomByID(targetRoomID)
4545
if err != nil {
4646
log.Printf("Failed to search for room with id %s: %v\n", targetRoomID, err)
47-
return echo.NewHTTPError(http.StatusInternalServerError)
48-
}
49-
if room == nil {
50-
return echo.NewHTTPError(http.StatusNotFound, "Room not found")
47+
if err, ok := err.(services.BSGError); ok {
48+
return echo.NewHTTPError(err.StatusCode, "Room not found")
49+
}
50+
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find room. Please try again later")
5151
}
5252
return c.JSON(http.StatusOK, map[string]models.Room{
5353
"data": *room,
@@ -61,10 +61,10 @@ func (controller *RoomController) JoinRoomEndpoint(c echo.Context) error {
6161
room, err := controller.roomService.JoinRoom(roomID, userAuthID)
6262
if err != nil {
6363
log.Printf("User id: %s failed to join room with id %s: %v\n", userAuthID, roomID, err)
64-
if _, ok := err.(services.RoomServiceError); ok {
65-
return echo.NewHTTPError(http.StatusBadRequest, "Failed to join room. " + err.Error())
64+
if err, ok := err.(services.BSGError); ok {
65+
return echo.NewHTTPError(err.StatusCode, "Failed to join room. "+err.Error())
6666
}
67-
return echo.NewHTTPError(http.StatusInternalServerError)
67+
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to join room. Please try again later")
6868
}
6969
return c.JSON(http.StatusOK, map[string]models.Room{
7070
"data": *room,
@@ -78,19 +78,73 @@ func (controller *RoomController) LeaveRoomEndpoint(c echo.Context) error {
7878
err := controller.roomService.LeaveRoom(roomID, userAuthID)
7979
if err != nil {
8080
log.Printf("User id: %s failed to leave room with id %s: %v\n", userAuthID, roomID, err)
81-
if _, ok := err.(services.RoomServiceError); ok {
82-
return echo.NewHTTPError(http.StatusBadRequest, "Failed to leave room. " + err.Error())
81+
if err, ok := err.(services.BSGError); ok {
82+
return echo.NewHTTPError(err.StatusCode, "Failed to leave room. "+err.Error())
8383
}
84-
return echo.NewHTTPError(http.StatusInternalServerError)
84+
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to leave room. Please try again later")
8585
}
8686
return c.JSON(http.StatusOK, map[string]string{
8787
"message": "Successfully Left Room",
8888
})
8989
}
9090

91+
func (controller *RoomController) CreateNewRoundEndpoint(c echo.Context) error {
92+
var roundCreationParams services.RoundCreationParameters
93+
if err := c.Bind(&roundCreationParams); err != nil {
94+
return echo.NewHTTPError(http.StatusBadRequest, "Invalid data. Please try again")
95+
}
96+
roomID := c.Param("roomID")
97+
newRound, err := controller.roomService.CreateRound(&roundCreationParams, roomID)
98+
if err != nil {
99+
log.Printf("Failed to create new round: %v\n", err)
100+
if err, ok := err.(*services.BSGError); ok {
101+
return echo.NewHTTPError(err.StatusCode, "Failed to create round. "+err.Message)
102+
}
103+
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create round. Please try again later")
104+
}
105+
return c.JSON(http.StatusCreated, map[string]models.Round{
106+
"data": *newRound,
107+
})
108+
}
109+
110+
func (controller *RoomController) StartRoundEndpoint(c echo.Context) error {
111+
targetRoomID := c.Param("roomID")
112+
userAuthID := c.Get("authToken").(*auth.Token).UID
113+
roundStartTime, err := controller.roomService.StartRoundByRoomID(targetRoomID, userAuthID)
114+
if err != nil {
115+
log.Printf("Failed to start round for room with id %s: %v\n", targetRoomID, err)
116+
if err, ok := err.(*services.BSGError); ok {
117+
return echo.NewHTTPError(err.StatusCode, "Failed to start round. "+err.Message)
118+
}
119+
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to start round. Please try again later")
120+
}
121+
return c.JSON(http.StatusOK, map[string]interface{}{
122+
"startTime": roundStartTime.Unix(),
123+
})
124+
}
125+
126+
func (controller *RoomController) GetLeaderboardEndpoint(c echo.Context) error {
127+
roomID := c.Param("roomID")
128+
leaderboard, err := controller.roomService.GetLeaderboard(roomID)
129+
if err != nil {
130+
log.Printf("Failed to get leaderboard for room with id %s: %v\n", roomID, err)
131+
if err, ok := err.(services.BSGError); ok {
132+
return echo.NewHTTPError(err.StatusCode, "Failed to get leaderboard. "+err.Message)
133+
}
134+
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get leaderboard. Please try again later")
135+
}
136+
return c.JSON(http.StatusOK, map[string]interface{}{
137+
"leaderboard": leaderboard,
138+
})
139+
}
140+
91141
func (controller *RoomController) InitializeRoutes(g *echo.Group) {
92142
g.POST("/", controller.CreateNewRoomEndpoint)
93-
g.GET("/:roomID", controller.FindRoomEndpoint)
94143
g.POST("/:roomID/join", controller.JoinRoomEndpoint)
95144
g.POST("/:roomID/leave", controller.LeaveRoomEndpoint)
96-
}
145+
g.POST("/:roomID/rounds/create", controller.CreateNewRoundEndpoint)
146+
g.POST("/:roomID/start", controller.StartRoundEndpoint)
147+
g.GET("/:roomID", controller.FindRoomEndpoint)
148+
g.GET("/:roomID/leaderboard", controller.GetLeaderboardEndpoint)
149+
// TODO: add routes to submit problem to room
150+
}

central-service/controllers/round.go

Lines changed: 0 additions & 116 deletions
This file was deleted.

central-service/main.go

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func main() {
4949
fmt.Printf("Error migrating Submission schema: %v\n", err)
5050
}
5151
if err := db.AutoMigrate(&models.RoundSubmission{}); err != nil {
52-
fmt.Printf("Error migrating RoundSubmission schema: %v\n",err)
52+
fmt.Printf("Error migrating RoundSubmission schema: %v\n", err)
5353
}
5454
e := echo.New()
5555

@@ -59,27 +59,20 @@ func main() {
5959
problemService := services.InitializeProblemService(db)
6060
problemController := controllers.InitializeProblemController(&problemService)
6161

62-
roomService := services.InitializeRoomService(db, rdb, maxNumRoundsPerRoom)
63-
roomController := controllers.InitializeRoomController(&roomService)
64-
roomAccessor := services.NewRoomAccessor(&roomService)
6562
problemAccessor := services.NewProblemAccessor(&problemService)
6663
roundScheduler := tasks.New()
6764
defer roundScheduler.Stop()
68-
roundService := services.InitializeRoundService(db, rdb, &roomAccessor, roundScheduler, &problemAccessor)
69-
roundAccessor := services.NewRoundAccessor(&roundService)
70-
roundSubmissionService := services.InitializeRoundSubmissionService(db, &problemAccessor, &roundAccessor)
71-
roundController := controllers.InitializeRoundController(&roundService, &userService, &roomService, &roundSubmissionService)
65+
roundService := services.InitializeRoundService(db, rdb, roundScheduler, &problemAccessor)
7266

73-
// TODO: Initialize Kafka-related components
74-
// TODO: Create a co-routine to listen for messages coming from Kafka and update database
67+
roomService := services.InitializeRoomService(db, rdb, &roundService, maxNumRoundsPerRoom)
68+
roomController := controllers.InitializeRoomController(&roomService)
7569

7670
e.Use(middleware.CORS())
7771
e.Use(userController.ValidateUserRequest)
7872

7973
userController.InitializeRoutes(e.Group("/api/users"))
8074
problemController.InitializeRoutes(e.Group("/api/problems"))
8175
roomController.InitializeRoutes(e.Group("/api/rooms"))
82-
roundController.InitializeRoutes(e.Group("/api/rounds"))
8376

8477
e.Logger.Fatal(e.Start(":5000"))
8578
}

central-service/models/room.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ type Room struct {
66
ID uuid.UUID `gorm:"primaryKey" json:"id"`
77
Admin string `gorm:"not null" json:"adminId"`
88
Name string `json:"roomName"`
9-
Rounds []Round `gorm:"foreignKey:RoomID"`
9+
Rounds []Round `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"rounds"`
1010
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package services
2+
3+
type BSGError struct {
4+
StatusCode int
5+
Message string
6+
}
7+
8+
func (e BSGError) Error() string {
9+
return e.Message
10+
}

central-service/services/problem.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ type ProblemService struct {
1212
}
1313

1414
type DifficultyParameter struct {
15-
NumEasyProblems int
15+
NumEasyProblems int
1616
NumMediumProblems int
17-
NumHardProblems int
17+
NumHardProblems int
1818
}
1919

2020
func InitializeProblemService(db *gorm.DB) ProblemService {
@@ -78,24 +78,24 @@ func (service *ProblemService) GenerateProblemsetByDifficultyParameters(params D
7878
err := service.db.Transaction(func(tx *gorm.DB) error {
7979
if err := tx.Clauses(clause.OrderBy{
8080
Expression: clause.Expr{
81-
SQL: "RAND()",
81+
SQL: "RANDOM()",
8282
},
8383
}).Where("difficulty = ?", constants.DIFFICULTY_EASY).Limit(params.NumEasyProblems).Find(&easyProblems).Error; err != nil {
8484
return err
8585
}
8686
if err := tx.Clauses(clause.OrderBy{
8787
Expression: clause.Expr{
88-
SQL: "RAND()",
88+
SQL: "RANDOM()",
8989
},
9090
}).Where("difficulty = ?", constants.DIFFICULTY_MEDIUM).Limit(params.NumMediumProblems).Find(&mediumProblems).Error; err != nil {
9191
return err
9292
}
9393
if err := tx.Clauses(clause.OrderBy{
9494
Expression: clause.Expr{
95-
SQL: "RAND()",
95+
SQL: "RANDOM()",
9696
},
9797
}).Where("difficulty = ?", constants.DIFFICULTY_HARD).Limit(params.NumHardProblems).Order(clause.Expr{
98-
SQL: "RAND()",
98+
SQL: "RANDOM()",
9999
}).Find(&hardProblems).Error; err != nil {
100100
return err
101101
}
@@ -119,4 +119,4 @@ func (service *ProblemService) DetermineScoreForProblem(problem *models.Problem)
119119
}
120120

121121
return 5
122-
}
122+
}

central-service/services/room-accessor.go

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)