ms-tester/internal/tester/delivery/rest/handlers.go
Vyacheslav1557 b960a923d2 feat(tester): add endpoints
add GetMonitor&GetTask endpoints
2025-03-28 01:17:53 +05:00

602 lines
16 KiB
Go

package rest
import (
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/internal/tester"
"git.sch9.ru/new_gate/ms-tester/pkg"
testerv1 "git.sch9.ru/new_gate/ms-tester/proto/tester/v1"
"github.com/gofiber/fiber/v2"
"io"
)
type TesterHandlers struct {
problemsUC tester.ProblemUseCase
contestsUC tester.ContestUseCase
}
func NewTesterHandlers(problemsUC tester.ProblemUseCase, contestsUC tester.ContestUseCase) *TesterHandlers {
return &TesterHandlers{
problemsUC: problemsUC,
contestsUC: contestsUC,
}
}
func (h *TesterHandlers) ListContests(c *fiber.Ctx, params testerv1.ListContestsParams) error {
contests, count, err := h.contestsUC.ListContests(c.Context(), params.Page, params.PageSize)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
resp := testerv1.ListContestsResponse{
Contests: make([]testerv1.ContestsListItem, len(contests)),
Page: params.Page,
MaxPage: func() int32 {
if count%params.PageSize == 0 {
return count / params.PageSize
}
return count/params.PageSize + 1
}(),
}
for i, contest := range contests {
resp.Contests[i] = testerv1.ContestsListItem{
Id: contest.Id,
Title: contest.Title,
CreatedAt: contest.CreatedAt,
UpdatedAt: contest.UpdatedAt,
}
}
return c.JSON(resp)
}
func (h *TesterHandlers) ListProblems(c *fiber.Ctx, params testerv1.ListProblemsParams) error {
problems, count, err := h.problemsUC.ListProblems(c.Context(), params.Page, params.PageSize)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
resp := testerv1.ListProblemsResponse{
Problems: make([]testerv1.ProblemListItem, len(problems)),
Page: params.Page,
MaxPage: func() int32 {
if count%params.PageSize == 0 {
return count / params.PageSize
}
return count/params.PageSize + 1
}(),
}
for i, problem := range problems {
resp.Problems[i] = testerv1.ProblemListItem{
Id: problem.Id,
Title: problem.Title,
MemoryLimit: problem.MemoryLimit,
TimeLimit: problem.TimeLimit,
CreatedAt: problem.CreatedAt,
UpdatedAt: problem.UpdatedAt,
}
}
return c.JSON(resp)
}
func (h *TesterHandlers) CreateContest(c *fiber.Ctx) error {
id, err := h.contestsUC.CreateContest(c.Context(), "Название контеста")
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.JSON(testerv1.CreateContestResponse{
Id: id,
})
}
func (h *TesterHandlers) DeleteContest(c *fiber.Ctx, id int32) error {
err := h.contestsUC.DeleteContest(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func (h *TesterHandlers) GetContest(c *fiber.Ctx, id int32) error {
contest, err := h.contestsUC.ReadContestById(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
//token, ok := c.Locals(TokenKey).(*models.JWT)
//if !ok {
// return c.SendStatus(fiber.StatusUnauthorized)
//}
tasks, err := h.contestsUC.ReadRichTasks(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
resp := testerv1.GetContestResponse{
Contest: testerv1.Contest{
Id: id,
Title: contest.Title,
CreatedAt: contest.CreatedAt,
UpdatedAt: contest.UpdatedAt,
},
Tasks: make([]struct {
BestSolution testerv1.BestSolution `json:"best_solution"`
Task testerv1.RichTask `json:"task"`
}, len(tasks)),
}
for i, task := range tasks {
resp.Tasks[i] = struct {
BestSolution testerv1.BestSolution `json:"best_solution"`
Task testerv1.RichTask `json:"task"`
}{
BestSolution: testerv1.BestSolution{},
Task: testerv1.RichTask{
Id: task.Id,
ProblemId: task.ProblemId,
Position: task.Position,
Title: task.Title,
MemoryLimit: task.MemoryLimit,
TimeLimit: task.TimeLimit,
CreatedAt: task.CreatedAt,
UpdatedAt: task.UpdatedAt,
},
}
}
return c.JSON(resp)
}
func (h *TesterHandlers) DeleteParticipant(c *fiber.Ctx, params testerv1.DeleteParticipantParams) error {
err := h.contestsUC.DeleteParticipant(c.Context(), params.ParticipantId)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func (h *TesterHandlers) AddParticipant(c *fiber.Ctx, params testerv1.AddParticipantParams) error {
id, err := h.contestsUC.AddParticipant(c.Context(), params.ContestId, params.UserId)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.JSON(testerv1.AddParticipantResponse{
Id: id,
})
}
func (h *TesterHandlers) DeleteTask(c *fiber.Ctx, id int32) error {
err := h.contestsUC.DeleteTask(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func (h *TesterHandlers) AddTask(c *fiber.Ctx, params testerv1.AddTaskParams) error {
id, err := h.contestsUC.AddTask(c.Context(), params.ContestId, params.ProblemId)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.JSON(testerv1.AddTaskResponse{
Id: id,
})
}
func (h *TesterHandlers) CreateProblem(c *fiber.Ctx) error {
id, err := h.problemsUC.CreateProblem(c.Context(), "Название задачи")
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.JSON(testerv1.CreateProblemResponse{
Id: id,
})
}
func (h *TesterHandlers) DeleteProblem(c *fiber.Ctx, id int32) error {
err := h.problemsUC.DeleteProblem(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func (h *TesterHandlers) GetProblem(c *fiber.Ctx, id int32) error {
problem, err := h.problemsUC.ReadProblemById(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.JSON(
testerv1.GetProblemResponse{Problem: testerv1.Problem{
Id: problem.Id,
Title: problem.Title,
TimeLimit: problem.TimeLimit,
MemoryLimit: problem.MemoryLimit,
Legend: problem.Legend,
InputFormat: problem.InputFormat,
OutputFormat: problem.OutputFormat,
Notes: problem.Notes,
Scoring: problem.Scoring,
LegendHtml: problem.LegendHtml,
InputFormatHtml: problem.InputFormatHtml,
OutputFormatHtml: problem.OutputFormatHtml,
NotesHtml: problem.NotesHtml,
ScoringHtml: problem.ScoringHtml,
CreatedAt: problem.CreatedAt,
UpdatedAt: problem.UpdatedAt,
}},
)
}
func (h *TesterHandlers) ListParticipants(c *fiber.Ctx, params testerv1.ListParticipantsParams) error {
participants, count, err := h.contestsUC.ListParticipants(c.Context(), params.ContestId, params.Page, params.PageSize)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
resp := testerv1.ListParticipantsResponse{
Participants: make([]testerv1.ParticipantsListItem, len(participants)),
Page: params.Page,
MaxPage: func() int32 {
if count%params.PageSize == 0 {
return count / params.PageSize
}
return count/params.PageSize + 1
}(),
}
for i, participant := range participants {
resp.Participants[i] = testerv1.ParticipantsListItem{
Id: participant.Id,
UserId: participant.UserId,
Name: participant.Name,
CreatedAt: participant.CreatedAt,
UpdatedAt: participant.UpdatedAt,
}
}
return c.JSON(resp)
}
func (h *TesterHandlers) UpdateProblem(c *fiber.Ctx, id int32) error {
var req testerv1.UpdateProblemRequest
err := c.BodyParser(&req)
if err != nil {
return err
}
err = h.problemsUC.UpdateProblem(c.Context(), id, models.ProblemUpdate{
Title: req.Title,
MemoryLimit: req.MemoryLimit,
TimeLimit: req.TimeLimit,
Legend: req.Legend,
InputFormat: req.InputFormat,
OutputFormat: req.OutputFormat,
Notes: req.Notes,
Scoring: req.Scoring,
})
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func (h *TesterHandlers) UpdateContest(c *fiber.Ctx, id int32) error {
var req testerv1.UpdateContestRequest
err := c.BodyParser(&req)
if err != nil {
return err
}
err = h.contestsUC.UpdateContest(c.Context(), id, models.ContestUpdate{
Title: req.Title,
})
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func (h *TesterHandlers) UpdateParticipant(c *fiber.Ctx, params testerv1.UpdateParticipantParams) error {
var req testerv1.UpdateParticipantRequest
err := c.BodyParser(&req)
if err != nil {
return err
}
err = h.contestsUC.UpdateParticipant(c.Context(), params.ParticipantId, models.ParticipantUpdate{
Name: req.Name,
})
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func (h *TesterHandlers) ListSolutions(c *fiber.Ctx, params testerv1.ListSolutionsParams) error {
list, total, err := h.contestsUC.ListSolutions(c.Context(), models.SolutionsFilter{
ContestId: params.ContestId,
Page: params.Page,
PageSize: params.PageSize,
ParticipantId: params.ParticipantId,
TaskId: params.TaskId,
Language: params.Language,
Order: params.Order,
State: params.State,
})
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
resp := testerv1.ListSolutionsResponse{
Solutions: make([]testerv1.SolutionListItem, len(list)),
Page: params.Page,
MaxPage: func() int32 {
if total%params.PageSize == 0 {
return total / params.PageSize
}
return total/params.PageSize + 1
}(),
}
for i, solution := range list {
resp.Solutions[i] = testerv1.SolutionListItem{
Id: solution.Id,
TaskId: solution.TaskId,
ContestId: solution.ContestId,
ParticipantId: solution.ParticipantId,
Language: solution.Language,
Penalty: solution.Penalty,
Score: solution.Score,
State: solution.State,
TotalScore: solution.TotalScore,
CreatedAt: solution.CreatedAt,
UpdatedAt: solution.UpdatedAt,
}
}
return c.JSON(resp)
}
const (
maxSolutionSize int64 = 10 * 1024 * 1024
)
func (h *TesterHandlers) CreateSolution(c *fiber.Ctx, params testerv1.CreateSolutionParams) error {
s, err := c.FormFile("solution")
if err != nil {
return err
}
if s.Size == 0 || s.Size > maxSolutionSize {
return c.SendStatus(fiber.StatusBadRequest)
}
f, err := s.Open()
if err != nil {
return err
}
defer f.Close()
b, err := io.ReadAll(f)
if err != nil {
return err
}
id, err := h.contestsUC.CreateSolution(c.Context(), &models.SolutionCreation{
TaskId: params.TaskId,
ParticipantId: 1,
Language: params.Language,
Penalty: 0,
Solution: string(b),
})
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.JSON(testerv1.CreateSolutionResponse{
Id: id,
})
}
func (h *TesterHandlers) GetSolution(c *fiber.Ctx, id int32) error {
solution, err := h.contestsUC.ReadSolution(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.JSON(
testerv1.GetSolutionResponse{Solution: testerv1.Solution{
Id: solution.Id,
TaskId: solution.TaskId,
ParticipantId: solution.ParticipantId,
Solution: solution.Solution,
State: solution.State,
Score: solution.Score,
Penalty: solution.Penalty,
TotalScore: solution.TotalScore,
Language: solution.Language,
CreatedAt: solution.CreatedAt,
UpdatedAt: solution.UpdatedAt,
}},
)
}
func (h *TesterHandlers) GetTask(c *fiber.Ctx, id int32) error {
contest, err := h.contestsUC.ReadContestById(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
tasks, err := h.contestsUC.ReadRichTasks(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
t, err := h.contestsUC.ReadTask(c.Context(), id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
resp := testerv1.GetTaskResponse{
Contest: struct {
Id int32 `json:"id"`
Tasks []testerv1.RichTask `json:"tasks"`
Title string `json:"title"`
}{
Id: contest.Id,
Title: contest.Title,
Tasks: make([]testerv1.RichTask, len(tasks)),
},
Task: testerv1.Task{
Id: t.Id,
Title: t.Title,
MemoryLimit: t.MemoryLimit,
TimeLimit: t.TimeLimit,
InputFormatHtml: t.InputFormatHtml,
LegendHtml: t.LegendHtml,
NotesHtml: t.NotesHtml,
OutputFormatHtml: t.OutputFormatHtml,
Position: t.Position,
ScoringHtml: t.ScoringHtml,
CreatedAt: t.CreatedAt,
UpdatedAt: t.UpdatedAt,
},
}
for i, task := range tasks {
resp.Contest.Tasks[i] = testerv1.RichTask{
Id: task.Id,
Position: task.Position,
Title: task.Title,
MemoryLimit: task.MemoryLimit,
ProblemId: task.ProblemId,
TimeLimit: task.TimeLimit,
CreatedAt: task.CreatedAt,
UpdatedAt: task.UpdatedAt}
}
return c.JSON(resp)
}
func (h *TesterHandlers) GetMonitor(c *fiber.Ctx, params testerv1.GetMonitorParams) error {
contest, err := h.contestsUC.ReadContestById(c.Context(), params.ContestId)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
monitor, err := h.contestsUC.ReadMonitor(c.Context(), params.ContestId)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
tasks, err := h.contestsUC.ReadRichTasks(c.Context(), params.ContestId)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
resp := testerv1.GetMonitorResponse{
Contest: struct {
Id int32 `json:"id"`
Tasks []testerv1.RichTask `json:"tasks"`
Title string `json:"title"`
}{
Id: contest.Id,
Title: contest.Title,
Tasks: make([]testerv1.RichTask, len(tasks)),
},
Participants: make([]struct {
Id int32 `json:"id"`
Name string `json:"name"`
PenaltyInTotal int32 `json:"penalty_in_total"`
Solutions []testerv1.SolutionListItem `json:"solutions"`
SolvedInTotal int32 `json:"solved_in_total"`
}, len(monitor.Participants)),
SummaryPerProblem: make([]struct {
Id int32 `json:"id"`
Success int32 `json:"success"`
Total int32 `json:"total"`
}, len(monitor.Summary)),
}
for i, participant := range monitor.Participants {
resp.Participants[i] = struct {
Id int32 `json:"id"`
Name string `json:"name"`
PenaltyInTotal int32 `json:"penalty_in_total"`
Solutions []testerv1.SolutionListItem `json:"solutions"`
SolvedInTotal int32 `json:"solved_in_total"`
}{
Id: participant.Id,
Name: participant.Name,
PenaltyInTotal: participant.PenaltyInTotal,
Solutions: make([]testerv1.SolutionListItem, len(participant.Solutions)),
SolvedInTotal: participant.SolvedInTotal,
}
for j, solution := range participant.Solutions {
resp.Participants[i].Solutions[j] = testerv1.SolutionListItem{
ContestId: solution.ContestId,
CreatedAt: solution.CreatedAt,
Id: solution.Id,
Language: solution.Language,
ParticipantId: solution.ParticipantId,
Penalty: solution.Penalty,
Score: solution.Score,
State: solution.State,
TaskId: solution.TaskId,
TotalScore: solution.TotalScore,
UpdatedAt: solution.UpdatedAt,
}
}
}
for i, problem := range monitor.Summary {
resp.SummaryPerProblem[i] = struct {
Id int32 `json:"id"`
Success int32 `json:"success"`
Total int32 `json:"total"`
}{
Id: problem.Id,
Success: problem.Success,
Total: problem.Total,
}
}
for i, task := range tasks {
resp.Contest.Tasks[i] = testerv1.RichTask{
Id: task.Id,
Position: task.Position,
Title: task.Title,
MemoryLimit: task.MemoryLimit,
ProblemId: task.ProblemId,
TimeLimit: task.TimeLimit,
CreatedAt: task.CreatedAt,
UpdatedAt: task.UpdatedAt}
}
return c.JSON(resp)
}