aboutsummaryrefslogtreecommitdiffhomepage
path: root/backend/api/handler.go
diff options
context:
space:
mode:
Diffstat (limited to 'backend/api/handler.go')
-rw-r--r--backend/api/handler.go233
1 files changed, 190 insertions, 43 deletions
diff --git a/backend/api/handler.go b/backend/api/handler.go
index d16b731..26031f6 100644
--- a/backend/api/handler.go
+++ b/backend/api/handler.go
@@ -3,7 +3,6 @@ package api
import (
"context"
"errors"
- "fmt"
"log"
"net/http"
@@ -16,12 +15,13 @@ import (
)
type Handler struct {
- q *db.Queries
- hubs GameHubsInterface
+ q *db.Queries
+ hub GameHubInterface
}
-type GameHubsInterface interface {
- StartGame(gameID int) error
+type GameHubInterface interface {
+ CalcCodeSize(code string) int
+ EnqueueTestTasks(ctx context.Context, submissionID, gameID, userID int, code string) error
}
func (h *Handler) PostLogin(ctx context.Context, request PostLoginRequestObject) (PostLoginResponseObject, error) {
@@ -62,22 +62,14 @@ func (h *Handler) PostLogin(ctx context.Context, request PostLoginRequestObject)
}, nil
}
-func (h *Handler) GetToken(_ context.Context, _ GetTokenRequestObject, user *auth.JWTClaims) (GetTokenResponseObject, error) {
- newToken, err := auth.NewShortLivedJWT(user)
- if err != nil {
- return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
- }
- return GetToken200JSONResponse{
- Token: newToken,
- }, nil
-}
-
-func (h *Handler) GetGames(ctx context.Context, _ GetGamesRequestObject, user *auth.JWTClaims) (GetGamesResponseObject, error) {
- gameRows, err := h.q.ListGamesForPlayer(ctx, int32(user.UserID))
+func (h *Handler) GetGames(ctx context.Context, _ GetGamesRequestObject, _ *auth.JWTClaims) (GetGamesResponseObject, error) {
+ gameRows, err := h.q.ListPublicGames(ctx)
if err != nil {
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
games := make([]Game, len(gameRows))
+ gameIDs := make([]int32, len(gameRows))
+ gameID2Index := make(map[int32]int, len(gameRows))
for i, row := range gameRows {
var startedAt *int64
if row.StartedAt.Valid {
@@ -87,7 +79,7 @@ func (h *Handler) GetGames(ctx context.Context, _ GetGamesRequestObject, user *a
games[i] = Game{
GameID: int(row.GameID),
GameType: GameGameType(row.GameType),
- State: GameState(row.State),
+ IsPublic: row.IsPublic,
DisplayName: row.DisplayName,
DurationSeconds: int(row.DurationSeconds),
StartedAt: startedAt,
@@ -97,15 +89,30 @@ func (h *Handler) GetGames(ctx context.Context, _ GetGamesRequestObject, user *a
Description: row.Description,
},
}
+ gameIDs[i] = row.GameID
+ gameID2Index[row.GameID] = i
+ }
+ mainPlayerRows, err := h.q.ListMainPlayers(ctx, gameIDs)
+ if err != nil {
+ return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ }
+ for _, row := range mainPlayerRows {
+ idx := gameID2Index[row.GameID]
+ game := &games[idx]
+ game.MainPlayers = append(game.MainPlayers, User{
+ UserID: int(row.UserID),
+ Username: row.Username,
+ DisplayName: row.DisplayName,
+ IconPath: row.IconPath,
+ IsAdmin: row.IsAdmin,
+ })
}
return GetGames200JSONResponse{
Games: games,
}, nil
}
-func (h *Handler) GetGame(ctx context.Context, request GetGameRequestObject, user *auth.JWTClaims) (GetGameResponseObject, error) {
- // TODO: check user permission
- _ = user
+func (h *Handler) GetGame(ctx context.Context, request GetGameRequestObject, _ *auth.JWTClaims) (GetGameResponseObject, error) {
gameID := request.GameID
row, err := h.q.GetGameByID(ctx, int32(gameID))
if err != nil {
@@ -118,18 +125,25 @@ func (h *Handler) GetGame(ctx context.Context, request GetGameRequestObject, use
}
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
+ if !row.IsPublic {
+ return GetGame404JSONResponse{
+ NotFoundJSONResponse: NotFoundJSONResponse{
+ Message: "Game not found",
+ },
+ }, nil
+ }
var startedAt *int64
if row.StartedAt.Valid {
startedAtTimestamp := row.StartedAt.Time.Unix()
startedAt = &startedAtTimestamp
}
- playerRows, err := h.q.ListGamePlayers(ctx, int32(gameID))
+ mainPlayerRows, err := h.q.ListMainPlayers(ctx, []int32{int32(gameID)})
if err != nil {
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
- players := make([]User, len(playerRows))
- for i, playerRow := range playerRows {
- players[i] = User{
+ mainPlayers := make([]User, len(mainPlayerRows))
+ for i, playerRow := range mainPlayerRows {
+ mainPlayers[i] = User{
UserID: int(playerRow.UserID),
Username: playerRow.Username,
DisplayName: playerRow.DisplayName,
@@ -137,25 +151,10 @@ func (h *Handler) GetGame(ctx context.Context, request GetGameRequestObject, use
IsAdmin: playerRow.IsAdmin,
}
}
- testcaseIDs, err := h.q.ListTestcaseIDsByGameID(ctx, int32(gameID))
- if err != nil {
- return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
- }
- execSteps := make([]ExecStep, len(testcaseIDs)+1)
- execSteps[0] = ExecStep{
- TestcaseID: nullable.NewNullNullable[int](),
- Label: "Compile",
- }
- for i, testcaseID := range testcaseIDs {
- execSteps[i+1] = ExecStep{
- TestcaseID: nullable.NewNullableWithValue(int(testcaseID)),
- Label: fmt.Sprintf("Testcase %d", i+1),
- }
- }
game := Game{
GameID: int(row.GameID),
GameType: GameGameType(row.GameType),
- State: GameState(row.State),
+ IsPublic: row.IsPublic,
DisplayName: row.DisplayName,
DurationSeconds: int(row.DurationSeconds),
StartedAt: startedAt,
@@ -164,10 +163,158 @@ func (h *Handler) GetGame(ctx context.Context, request GetGameRequestObject, use
Title: row.Title,
Description: row.Description,
},
- Players: players,
- ExecSteps: execSteps,
+ MainPlayers: mainPlayers,
}
return GetGame200JSONResponse{
Game: game,
}, nil
}
+
+func (h *Handler) GetGamePlayLatestState(ctx context.Context, request GetGamePlayLatestStateRequestObject, user *auth.JWTClaims) (GetGamePlayLatestStateResponseObject, error) {
+ gameID := request.GameID
+ userID := user.UserID
+ row, err := h.q.GetLatestState(ctx, db.GetLatestStateParams{
+ GameID: int32(gameID),
+ UserID: int32(userID),
+ })
+ if err != nil {
+ if errors.Is(err, pgx.ErrNoRows) {
+ return GetGamePlayLatestState200JSONResponse{
+ State: LatestGameState{
+ Code: "",
+ Score: nullable.NewNullNullable[int](),
+ Status: None,
+ },
+ }, nil
+ }
+ return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ }
+ var score nullable.Nullable[int]
+ if row.CodeSize != nil {
+ score = nullable.NewNullableWithValue(int(*row.CodeSize))
+ } else {
+ score = nullable.NewNullNullable[int]()
+ }
+ return GetGamePlayLatestState200JSONResponse{
+ State: LatestGameState{
+ Code: row.Code,
+ Score: score,
+ Status: ExecutionStatus(row.Status),
+ },
+ }, nil
+}
+
+func (h *Handler) GetGameWatchLatestStates(ctx context.Context, request GetGameWatchLatestStatesRequestObject, user *auth.JWTClaims) (GetGameWatchLatestStatesResponseObject, error) {
+ gameID := request.GameID
+ rows, err := h.q.GetLatestStatesOfMainPlayers(ctx, int32(gameID))
+ if err != nil {
+ return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ }
+ states := make(map[string]LatestGameState, len(rows))
+ for _, row := range rows {
+ var code string
+ if row.Code != nil {
+ code = *row.Code
+ }
+ var score nullable.Nullable[int]
+ if row.CodeSize != nil {
+ score = nullable.NewNullableWithValue(int(*row.CodeSize))
+ } else {
+ score = nullable.NewNullNullable[int]()
+ }
+ var status ExecutionStatus
+ if row.Status != nil {
+ status = ExecutionStatus(*row.Status)
+ } else {
+ status = None
+ }
+ states[string(row.UserID)] = LatestGameState{
+ Code: code,
+ Score: score,
+ Status: status,
+ }
+
+ if int(row.UserID) == user.UserID {
+ return GetGameWatchLatestStates403JSONResponse{
+ ForbiddenJSONResponse: ForbiddenJSONResponse{
+ Message: "You are one of the main players of this game",
+ },
+ }, nil
+ }
+ }
+ return GetGameWatchLatestStates200JSONResponse{
+ States: states,
+ }, nil
+}
+
+func (h *Handler) GetGameWatchRanking(ctx context.Context, request GetGameWatchRankingRequestObject, _ *auth.JWTClaims) (GetGameWatchRankingResponseObject, error) {
+ gameID := request.GameID
+ rows, err := h.q.GetRanking(ctx, int32(gameID))
+ if err != nil {
+ if errors.Is(err, pgx.ErrNoRows) {
+ return GetGameWatchRanking200JSONResponse{}, nil
+ }
+ }
+ ranking := make([]RankingEntry, len(rows))
+ for i, row := range rows {
+ ranking[i] = RankingEntry{
+ Player: User{
+ UserID: int(row.UserID),
+ Username: row.Username,
+ DisplayName: row.DisplayName,
+ IconPath: row.IconPath,
+ IsAdmin: row.IsAdmin,
+ },
+ Score: int(row.CodeSize),
+ }
+ }
+ return GetGameWatchRanking200JSONResponse{
+ Ranking: ranking,
+ }, nil
+}
+
+func (h *Handler) PostGamePlayCode(ctx context.Context, request PostGamePlayCodeRequestObject, user *auth.JWTClaims) (PostGamePlayCodeResponseObject, error) {
+ gameID := request.GameID
+ userID := user.UserID
+ err := h.q.UpdateCode(ctx, db.UpdateCodeParams{
+ GameID: int32(gameID),
+ UserID: int32(userID),
+ Code: request.Body.Code,
+ Status: "none",
+ })
+ if err != nil {
+ return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ }
+ return PostGamePlayCode200Response{}, nil
+}
+
+func (h *Handler) PostGamePlaySubmit(ctx context.Context, request PostGamePlaySubmitRequestObject, user *auth.JWTClaims) (PostGamePlaySubmitResponseObject, error) {
+ gameID := request.GameID
+ userID := user.UserID
+ code := request.Body.Code
+ codeSize := h.hub.CalcCodeSize(code)
+ // TODO: transaction
+ err := h.q.UpdateCode(ctx, db.UpdateCodeParams{
+ GameID: int32(gameID),
+ UserID: int32(userID),
+ Code: code,
+ Status: "running",
+ })
+ if err != nil {
+ return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ }
+ submissionID, err := h.q.CreateSubmission(ctx, db.CreateSubmissionParams{
+ GameID: int32(gameID),
+ UserID: int32(userID),
+ Code: code,
+ CodeSize: int32(codeSize),
+ })
+ if err != nil {
+ return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ }
+ err = h.hub.EnqueueTestTasks(ctx, int(submissionID), gameID, userID, code)
+ if err != nil {
+ return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ }
+ return PostGamePlaySubmit200Response{}, nil
+}