diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-09-05 21:12:03 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-09-05 21:12:03 +0900 |
| commit | 2fb0b6516b9731ca832a31f6b31515f4eb056cb1 (patch) | |
| tree | e2f4f82d783799f84a111179075e3da11cdb9e80 /backend | |
| parent | 82d3cf35c3c6b85b48c94dd6301c8bf718669b8d (diff) | |
| download | iosdc-japan-2025-albatross-2fb0b6516b9731ca832a31f6b31515f4eb056cb1.tar.gz iosdc-japan-2025-albatross-2fb0b6516b9731ca832a31f6b31515f4eb056cb1.tar.zst iosdc-japan-2025-albatross-2fb0b6516b9731ca832a31f6b31515f4eb056cb1.zip | |
feat(backend): support swift language
Diffstat (limited to 'backend')
| -rw-r--r-- | backend/api/generated.go | 67 | ||||
| -rw-r--r-- | backend/api/handler.go | 22 | ||||
| -rw-r--r-- | backend/db/models.go | 1 | ||||
| -rw-r--r-- | backend/db/query.sql.go | 8 | ||||
| -rw-r--r-- | backend/fixtures/dev.sql | 16 | ||||
| -rw-r--r-- | backend/game/hub.go | 12 | ||||
| -rw-r--r-- | backend/schema.sql | 1 | ||||
| -rw-r--r-- | backend/taskqueue/processor.go | 2 | ||||
| -rw-r--r-- | backend/taskqueue/queue.go | 2 | ||||
| -rw-r--r-- | backend/taskqueue/tasks.go | 3 |
10 files changed, 87 insertions, 47 deletions
diff --git a/backend/api/generated.go b/backend/api/generated.go index 8a74b01..15f1c17 100644 --- a/backend/api/generated.go +++ b/backend/api/generated.go @@ -39,6 +39,12 @@ const ( N1V1 GameGameType = "1v1" ) +// Defines values for ProblemLanguage. +const ( + Php ProblemLanguage = "php" + Swift ProblemLanguage = "swift" +) + // Error defines model for Error. type Error struct { Message string `json:"message"` @@ -72,12 +78,16 @@ type LatestGameState struct { // Problem defines model for Problem. type Problem struct { - Description string `json:"description"` - ProblemID int `json:"problem_id"` - SampleCode string `json:"sample_code"` - Title string `json:"title"` + Description string `json:"description"` + Language ProblemLanguage `json:"language"` + ProblemID int `json:"problem_id"` + SampleCode string `json:"sample_code"` + Title string `json:"title"` } +// ProblemLanguage defines model for Problem.Language. +type ProblemLanguage string + // RankingEntry defines model for RankingEntry. type RankingEntry struct { Code nullable.Nullable[string] `json:"code"` @@ -1130,30 +1140,31 @@ func (sh *strictHandler) PostLogin(ctx echo.Context) error { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "KoN2UB8AAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/backend/api/handler.go b/backend/api/handler.go index 67f859c..4321d15 100644 --- a/backend/api/handler.go +++ b/backend/api/handler.go @@ -21,8 +21,8 @@ type Handler struct { } type GameHubInterface interface { - CalcCodeSize(code string) int - EnqueueTestTasks(ctx context.Context, submissionID, gameID, userID int, code string) error + CalcCodeSize(code string, language string) int + EnqueueTestTasks(ctx context.Context, submissionID, gameID, userID int, language, code string) error } func (h *Handler) PostLogin(ctx context.Context, request PostLoginRequestObject) (PostLoginResponseObject, error) { @@ -88,6 +88,7 @@ func (h *Handler) GetGames(ctx context.Context, _ GetGamesRequestObject, _ *auth ProblemID: int(row.ProblemID), Title: row.Title, Description: row.Description, + Language: ProblemLanguage(*row.Language), SampleCode: row.SampleCode, }, } @@ -166,6 +167,7 @@ func (h *Handler) GetGame(ctx context.Context, request GetGameRequestObject, _ * ProblemID: int(row.ProblemID), Title: row.Title, Description: row.Description, + Language: ProblemLanguage(*row.Language), SampleCode: row.SampleCode, }, MainPlayers: mainPlayers, @@ -294,10 +296,20 @@ func (h *Handler) PostGamePlaySubmit(ctx context.Context, request PostGamePlaySu gameID := request.GameID userID := user.UserID code := request.Body.Code - codeSize := h.hub.CalcCodeSize(code) + + gameRow, err := h.q.GetGameByID(ctx, int32(gameID)) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return PostGamePlaySubmit404JSONResponse{}, nil + } + return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + language := *gameRow.Language + codeSize := h.hub.CalcCodeSize(code, language) // TODO: check if the game is running // TODO: transaction - err := h.q.UpdateCodeAndStatus(ctx, db.UpdateCodeAndStatusParams{ + err = h.q.UpdateCodeAndStatus(ctx, db.UpdateCodeAndStatusParams{ GameID: int32(gameID), UserID: int32(userID), Code: code, @@ -315,7 +327,7 @@ func (h *Handler) PostGamePlaySubmit(ctx context.Context, request PostGamePlaySu if err != nil { return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } - err = h.hub.EnqueueTestTasks(ctx, int(submissionID), gameID, userID, code) + err = h.hub.EnqueueTestTasks(ctx, int(submissionID), gameID, userID, language, code) if err != nil { return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } diff --git a/backend/db/models.go b/backend/db/models.go index 9bb8ccb..c7d649c 100644 --- a/backend/db/models.go +++ b/backend/db/models.go @@ -36,6 +36,7 @@ type Problem struct { ProblemID int32 Title string Description string + Language *string SampleCode string } diff --git a/backend/db/query.sql.go b/backend/db/query.sql.go index d4e134f..f70cd29 100644 --- a/backend/db/query.sql.go +++ b/backend/db/query.sql.go @@ -128,7 +128,7 @@ func (q *Queries) CreateUserAuth(ctx context.Context, arg CreateUserAuthParams) } const getGameByID = `-- name: GetGameByID :one -SELECT game_id, game_type, is_public, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description, sample_code FROM games +SELECT game_id, game_type, is_public, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description, language, sample_code FROM games JOIN problems ON games.problem_id = problems.problem_id WHERE games.game_id = $1 LIMIT 1 @@ -146,6 +146,7 @@ type GetGameByIDRow struct { ProblemID_2 int32 Title string Description string + Language *string SampleCode string } @@ -164,6 +165,7 @@ func (q *Queries) GetGameByID(ctx context.Context, gameID int32) (GetGameByIDRow &i.ProblemID_2, &i.Title, &i.Description, + &i.Language, &i.SampleCode, ) return i, err @@ -575,7 +577,7 @@ func (q *Queries) ListMainPlayers(ctx context.Context, dollar_1 []int32) ([]List } const listPublicGames = `-- name: ListPublicGames :many -SELECT game_id, game_type, is_public, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description, sample_code FROM games +SELECT game_id, game_type, is_public, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description, language, sample_code FROM games JOIN problems ON games.problem_id = problems.problem_id WHERE is_public = true ORDER BY games.game_id @@ -593,6 +595,7 @@ type ListPublicGamesRow struct { ProblemID_2 int32 Title string Description string + Language *string SampleCode string } @@ -617,6 +620,7 @@ func (q *Queries) ListPublicGames(ctx context.Context) ([]ListPublicGamesRow, er &i.ProblemID_2, &i.Title, &i.Description, + &i.Language, &i.SampleCode, ); err != nil { return nil, err diff --git a/backend/fixtures/dev.sql b/backend/fixtures/dev.sql index 1df6353..bc25011 100644 --- a/backend/fixtures/dev.sql +++ b/backend/fixtures/dev.sql @@ -13,15 +13,15 @@ VALUES (3, 'password', '$2a$10$F/TePpu1pyJRWgn0e6A14.VL9D/17sRxT/2DyZ2Oi4Eg/lR6n7JcK'); INSERT INTO problems -(title, description, sample_code) +(title, description, language, sample_code) VALUES - ('TEST problem 1', 'This is TEST problem 1', 'sample code'), - ('TEST problem 2', 'This is TEST problem 2', 'sample code'), - ('TEST problem 3', 'This is TEST problem 3', 'sample code'), - ('TEST problem 4', 'This is TEST problem 4', 'sample code'), - ('TEST problem 5', 'This is TEST problem 5', 'sample code'), - ('TEST problem 6', 'This is TEST problem 6', 'sample code'), - ('TEST problem 7', 'This is TEST problem 7', 'sample code'); + ('TEST problem 1', 'This is TEST problem 1', 'php', 'sample code'), + ('TEST problem 2', 'This is TEST problem 2', 'php', 'sample code'), + ('TEST problem 3', 'This is TEST problem 3', 'php', 'sample code'), + ('TEST problem 4', 'This is TEST problem 4', 'php', 'sample code'), + ('TEST problem 5', 'This is TEST problem 5', 'php', 'sample code'), + ('TEST problem 6', 'This is TEST problem 6', 'php', 'sample code'), + ('TEST problem 7', 'This is TEST problem 7', 'php', 'sample code'); INSERT INTO games (game_type, is_public, display_name, duration_seconds, problem_id) diff --git a/backend/game/hub.go b/backend/game/hub.go index 3697b4a..8f27466 100644 --- a/backend/game/hub.go +++ b/backend/game/hub.go @@ -36,12 +36,17 @@ func (hub *Hub) Run() { go hub.processTaskResults() } -func (hub *Hub) CalcCodeSize(code string) int { +func (hub *Hub) CalcCodeSize(code string, language string) int { re := regexp.MustCompile(`\s+`) - return len(strings.TrimSuffix(strings.TrimPrefix(strings.TrimPrefix(re.ReplaceAllString(code, ""), "<?php"), "<?"), "?>")) + trimmed := re.ReplaceAllString(code, "") + if language == "php" { + return len(strings.TrimSuffix(strings.TrimPrefix(strings.TrimPrefix(trimmed, "<?php"), "<?"), "?>")) + } else { + return len(trimmed) + } } -func (hub *Hub) EnqueueTestTasks(ctx context.Context, submissionID, gameID, userID int, code string) error { +func (hub *Hub) EnqueueTestTasks(ctx context.Context, submissionID, gameID, userID int, language, code string) error { rows, err := hub.q.ListTestcasesByGameID(ctx, int32(gameID)) if err != nil { return err @@ -52,6 +57,7 @@ func (hub *Hub) EnqueueTestTasks(ctx context.Context, submissionID, gameID, user userID, submissionID, int(row.TestcaseID), + language, code, row.Stdin, row.Stdout, diff --git a/backend/schema.sql b/backend/schema.sql index 6408cbc..cd4ed4c 100644 --- a/backend/schema.sql +++ b/backend/schema.sql @@ -22,6 +22,7 @@ CREATE TABLE problems ( problem_id SERIAL PRIMARY KEY, title VARCHAR(255) NOT NULL, description TEXT NOT NULL, + language VARCHAR(8) /* NOT NULL */, sample_code TEXT NOT NULL ); diff --git a/backend/taskqueue/processor.go b/backend/taskqueue/processor.go index 42714f5..8dadfdf 100644 --- a/backend/taskqueue/processor.go +++ b/backend/taskqueue/processor.go @@ -39,7 +39,7 @@ func (p *processor) doProcessTaskRunTestcase( if err != nil { return nil, fmt.Errorf("json.Marshal failed: %v", err) } - req, err := http.NewRequest("POST", "http://worker-php:80/exec", bytes.NewBuffer(reqJSON)) + req, err := http.NewRequest("POST", "http://worker-"+payload.Language+":80/exec", bytes.NewBuffer(reqJSON)) if err != nil { return nil, fmt.Errorf("http.NewRequest failed: %v", err) } diff --git a/backend/taskqueue/queue.go b/backend/taskqueue/queue.go index b348fca..60844d2 100644 --- a/backend/taskqueue/queue.go +++ b/backend/taskqueue/queue.go @@ -25,6 +25,7 @@ func (q *Queue) EnqueueTaskRunTestcase( userID int, submissionID int, testcaseID int, + language string, code string, stdin string, stdout string, @@ -34,6 +35,7 @@ func (q *Queue) EnqueueTaskRunTestcase( userID, submissionID, testcaseID, + language, code, stdin, stdout, diff --git a/backend/taskqueue/tasks.go b/backend/taskqueue/tasks.go index f4cba20..943943f 100644 --- a/backend/taskqueue/tasks.go +++ b/backend/taskqueue/tasks.go @@ -17,6 +17,7 @@ type TaskPayloadRunTestcase struct { UserID int SubmissionID int TestcaseID int + Language string Code string Stdin string Stdout string @@ -27,6 +28,7 @@ func newTaskRunTestcase( userID int, submissionID int, testcaseID int, + language string, code string, stdin string, stdout string, @@ -36,6 +38,7 @@ func newTaskRunTestcase( UserID: userID, SubmissionID: submissionID, TestcaseID: testcaseID, + Language: language, Code: code, Stdin: stdin, Stdout: stdout, |
