feat(tester): migrate from gRPC to REST

This commit is contained in:
Vyacheslav1557 2025-02-25 18:40:05 +05:00
parent 6613b03b6c
commit a560715ae8
40 changed files with 403 additions and 961 deletions

View file

@ -1,17 +0,0 @@
package contests
import (
"context"
contestv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/contest/v1"
"google.golang.org/protobuf/types/known/emptypb"
)
type ContestHandlers interface {
CreateContest(ctx context.Context, req *contestv1.CreateContestRequest) (*contestv1.CreateContestResponse, error)
ReadContest(ctx context.Context, req *contestv1.ReadContestRequest) (*contestv1.ReadContestResponse, error)
DeleteContest(ctx context.Context, req *contestv1.DeleteContestRequest) (*emptypb.Empty, error)
AddTask(ctx context.Context, req *contestv1.AddTaskRequest) (*contestv1.AddTaskResponse, error)
DeleteTask(ctx context.Context, req *contestv1.DeleteTaskRequest) (*emptypb.Empty, error)
AddParticipant(ctx context.Context, req *contestv1.AddParticipantRequest) (*contestv1.AddParticipantResponse, error)
DeleteParticipant(ctx context.Context, req *contestv1.DeleteParticipantRequest) (*emptypb.Empty, error)
}

View file

@ -1,83 +0,0 @@
package grpc
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/contests"
contestv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/contest/v1"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
)
type ContestHandlers struct {
contestv1.UnimplementedContestServiceServer
contestUC contests.ContestUseCase
}
func NewContestHandlers(gserver *grpc.Server, contestUC contests.ContestUseCase) {
handlers := &ContestHandlers{contestUC: contestUC}
contestv1.RegisterContestServiceServer(gserver, handlers)
}
func (h *ContestHandlers) CreateContest(ctx context.Context, req *contestv1.CreateContestRequest) (*contestv1.CreateContestResponse, error) {
id, err := h.contestUC.CreateContest(ctx, req.GetTitle())
if err != nil {
return nil, err
}
return &contestv1.CreateContestResponse{Id: id}, nil
}
func (h *ContestHandlers) ReadContest(ctx context.Context, req *contestv1.ReadContestRequest) (*contestv1.ReadContestResponse, error) {
contest, err := h.contestUC.ReadContestById(ctx, req.GetId())
if err != nil {
return nil, err
}
return &contestv1.ReadContestResponse{Contest: &contestv1.ReadContestResponse_Contest{
Id: *contest.Id,
Title: *contest.Title,
CreatedAt: utils.TimestampP(contest.CreatedAt),
UpdatedAt: utils.TimestampP(contest.UpdatedAt),
}}, nil
}
func (h *ContestHandlers) DeleteContest(ctx context.Context, req *contestv1.DeleteContestRequest) (*emptypb.Empty, error) {
err := h.contestUC.DeleteContest(ctx, req.GetId())
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
func (h *ContestHandlers) AddTask(ctx context.Context, req *contestv1.AddTaskRequest) (*contestv1.AddTaskResponse, error) {
id, err := h.contestUC.AddTask(ctx, req.GetContestId(), req.GetProblemId())
if err != nil {
return nil, err
}
return &contestv1.AddTaskResponse{Id: id}, nil
}
func (h *ContestHandlers) DeleteTask(ctx context.Context, req *contestv1.DeleteTaskRequest) (*emptypb.Empty, error) {
err := h.contestUC.DeleteTask(ctx, req.GetTaskId())
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
func (h *ContestHandlers) AddParticipant(ctx context.Context, req *contestv1.AddParticipantRequest) (*contestv1.AddParticipantResponse, error) {
id, err := h.contestUC.AddParticipant(ctx, req.GetContestId(), req.GetUserId())
if err != nil {
return nil, err
}
return &contestv1.AddParticipantResponse{Id: id}, nil
}
func (h *ContestHandlers) DeleteParticipant(ctx context.Context, req *contestv1.DeleteParticipantRequest) (*emptypb.Empty, error) {
err := h.contestUC.DeleteParticipant(ctx, req.GetParticipantId())
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}

View file

@ -1,16 +0,0 @@
package contests
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type ContestRepository interface {
CreateContest(ctx context.Context, title string) (int32, error)
ReadContestById(ctx context.Context, id int32) (*models.Contest, error)
DeleteContest(ctx context.Context, id int32) error
AddTask(ctx context.Context, contestId int32, taskId int32) (int32, error)
DeleteTask(ctx context.Context, taskId int32) error
AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error)
DeleteParticipant(ctx context.Context, participantId int32) error
}

View file

@ -1,16 +0,0 @@
package contests
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type ContestUseCase interface {
CreateContest(ctx context.Context, title string) (int32, error)
ReadContestById(ctx context.Context, id int32) (*models.Contest, error)
DeleteContest(ctx context.Context, id int32) error
AddTask(ctx context.Context, contestId int32, taskId int32) (int32, error)
DeleteTask(ctx context.Context, taskId int32) error
AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error)
DeleteParticipant(ctx context.Context, participantId int32) error
}

View file

@ -1,13 +1,13 @@
package models
type Language struct {
Name string
CompileCmd []string //source: src;result:executable
RunCmd []string //source: executable
}
var Languages = [...]Language{
{Name: "gcc std=c90",
CompileCmd: []string{"gcc", "src", "-std=c90", "-o", "executable"},
RunCmd: []string{"executable"}},
}
//type Language struct {
// Name string
// CompileCmd []string //source: src;result:executable
// RunCmd []string //source: executable
//}
//
//var Languages = []Language{
// {Name: "gcc std=c90",
// CompileCmd: []string{"gcc", "src", "-std=c90", "-o", "executable"},
// RunCmd: []string{"executable"}},
//}

View file

@ -1,12 +1,10 @@
package models
import "time"
type Participant struct {
Id *int32 `db:"id"`
UserId *int32 `db:"user_id"`
ContestId *int32 `db:"contest_id"`
Name *string `db:"name"`
CreatedAt *time.Time `db:"created_at"`
UpdatedAt *time.Time `db:"updated_at"`
}
//type Participant struct {
// Id *int32 `db:"id"`
// UserId *int32 `db:"user_id"`
// ContestId *int32 `db:"contest_id"`
// Name *string `db:"name"`
// CreatedAt *time.Time `db:"created_at"`
// UpdatedAt *time.Time `db:"updated_at"`
//}

View file

@ -1,30 +1,26 @@
package models
import (
"errors"
)
type Result int32
const (
NotTested Result = 1 // change only with schema change
Accepted Result = 2
WrongAnswer Result = 3
PresentationError Result = 4
CompilationError Result = 5
MemoryLimitExceeded Result = 6
TimeLimitExceeded Result = 7
RuntimeError Result = 8
SystemFailDuringTesting Result = 9
Testing Result = 10
)
var ErrBadResult = errors.New("bad result")
func (result Result) Valid() error {
switch result {
case NotTested, Accepted, TimeLimitExceeded, MemoryLimitExceeded, CompilationError, SystemFailDuringTesting:
return nil
}
return ErrBadResult
}
//type Result int32
//
//const (
// NotTested Result = 1 // change only with schema change
// Accepted Result = 2
// WrongAnswer Result = 3
// PresentationError Result = 4
// CompilationError Result = 5
// MemoryLimitExceeded Result = 6
// TimeLimitExceeded Result = 7
// RuntimeError Result = 8
// SystemFailDuringTesting Result = 9
// Testing Result = 10
//)
//
//var ErrBadResult = errors.New("bad result")
//
//func (result Result) Valid() error {
// switch result {
// case NotTested, Accepted, TimeLimitExceeded, MemoryLimitExceeded, CompilationError, SystemFailDuringTesting:
// return nil
// }
// return ErrBadResult
//}

View file

@ -1,52 +0,0 @@
package models
import (
"errors"
)
type Role int32
const (
RoleSpectator Role = 0
RoleParticipant Role = 1
RoleModerator Role = 2
RoleAdmin Role = 3
)
func (role Role) IsAdmin() bool {
return role == RoleAdmin
}
func (role Role) IsModerator() bool {
return role == RoleModerator
}
func (role Role) IsParticipant() bool {
return role == RoleParticipant
}
func (role Role) IsSpectator() bool {
return role == RoleSpectator
}
func (role Role) AtLeast(other Role) bool {
return role >= other
}
func (role Role) AtMost(other Role) bool {
return role <= other
}
var ErrBadRole = errors.New("bad role")
func (role Role) Valid() error {
switch role {
case RoleSpectator, RoleParticipant, RoleModerator, RoleAdmin:
return nil
}
return ErrBadRole
}
func (role Role) AsPointer() *Role {
return &role
}

View file

@ -1,15 +1,13 @@
package models
import "time"
type Solution struct {
Id *int32 `db:"id"`
TaskId *int32 `db:"task_id"`
ParticipantId *int32 `db:"participant_id"`
State *int32 `db:"state"`
Score *int32 `db:"score"`
Penalty *int32 `db:"penalty"`
TotalScore *int32 `db:"total_score"`
Language *int32 `db:"language"`
CreatedAt *time.Time `db:"created_at"`
}
//type Solution struct {
// Id *int32 `db:"id"`
// TaskId *int32 `db:"task_id"`
// ParticipantId *int32 `db:"participant_id"`
// State *int32 `db:"state"`
// Score *int32 `db:"score"`
// Penalty *int32 `db:"penalty"`
// TotalScore *int32 `db:"total_score"`
// Language *int32 `db:"language"`
// CreatedAt *time.Time `db:"created_at"`
//}

View file

@ -1,12 +1,10 @@
package models
import "time"
type Task struct {
Id *int32 `db:"id"`
ProblemId *int32 `db:"problem_id"`
ContestId *int32 `db:"contest_id"`
Position *int32 `db:"position"`
CreatedAt *time.Time `db:"created_at"`
UpdatedAt *time.Time `db:"updated_at"`
}
//type Task struct {
// Id *int32 `db:"id"`
// ProblemId *int32 `db:"problem_id"`
// ContestId *int32 `db:"contest_id"`
// Position *int32 `db:"position"`
// CreatedAt *time.Time `db:"created_at"`
// UpdatedAt *time.Time `db:"updated_at"`
//}

View file

@ -1,13 +0,0 @@
package problems
import (
"context"
problemv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/problem/v1"
"google.golang.org/protobuf/types/known/emptypb"
)
type Handlers interface {
CreateProblem(ctx context.Context, req *problemv1.CreateProblemRequest) (*problemv1.CreateProblemResponse, error)
ReadProblem(ctx context.Context, req *problemv1.ReadProblemRequest) (*problemv1.ReadProblemResponse, error)
DeleteProblem(ctx context.Context, req *problemv1.DeleteProblemRequest) (*emptypb.Empty, error)
}

View file

@ -1,61 +0,0 @@
package grpc
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/problems"
problemv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/problem/v1"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
)
type problemHandlers struct {
problemv1.UnimplementedProblemServiceServer
problemUC problems.ProblemUseCase
}
func NewProblemHandlers(gserver *grpc.Server, problemUC problems.ProblemUseCase) {
handlers := &problemHandlers{problemUC: problemUC}
problemv1.RegisterProblemServiceServer(gserver, handlers)
}
func (h *problemHandlers) CreateProblem(ctx context.Context, req *problemv1.CreateProblemRequest) (*problemv1.CreateProblemResponse, error) {
id, err := h.problemUC.CreateProblem(ctx, req.GetTitle())
if err != nil {
return nil, err
}
return &problemv1.CreateProblemResponse{Id: id}, nil
}
func (h *problemHandlers) ReadProblem(ctx context.Context, req *problemv1.ReadProblemRequest) (*problemv1.ReadProblemResponse, error) {
problem, err := h.problemUC.ReadProblemById(ctx, req.GetId())
if err != nil {
return nil, err
}
return &problemv1.ReadProblemResponse{
Problem: &problemv1.ReadProblemResponse_Problem{
Id: *problem.Id,
Title: *problem.Title,
Legend: *problem.Legend,
InputFormat: *problem.InputFormat,
OutputFormat: *problem.OutputFormat,
Notes: *problem.Notes,
Tutorial: *problem.Tutorial,
LatexSummary: *problem.LatexSummary,
TimeLimit: *problem.TimeLimit,
MemoryLimit: *problem.MemoryLimit,
CreatedAt: utils.TimestampP(problem.CreatedAt),
UpdatedAt: utils.TimestampP(problem.UpdatedAt),
},
}, nil
}
func (h *problemHandlers) DeleteProblem(ctx context.Context, req *problemv1.DeleteProblemRequest) (*emptypb.Empty, error) {
err := h.problemUC.DeleteProblem(ctx, req.GetId())
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}

View file

@ -1,57 +0,0 @@
package rabbitmq
import (
"fmt"
"git.sch9.ru/new_gate/ms-tester/internal/problems"
"github.com/golang/protobuf/proto"
amqp "github.com/rabbitmq/amqp091-go"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
)
func NewNotificationSubscriber(ch *amqp.Channel, queueName string, instanceName string, problemUC problems.ProblemUseCase) {
_, err := ch.QueueDeclare(
queueName,
true,
false,
false,
false,
nil,
)
if err != nil {
panic(err)
}
msgs, err := ch.Consume(
queueName,
instanceName,
false,
false,
false,
false,
nil,
)
if err != nil {
panic(err) // FIXME
}
go func() {
for d := range msgs {
err = d.Ack(false)
if err != nil {
panic(err) // FIXME
}
msg := filer_pb.EventNotification{}
err = proto.Unmarshal(d.Body, &msg)
if err != nil {
panic(err) // FIXME
}
fmt.Println(msg.String()) // TODO: instead, call appropriate problemUC handler
}
}()
return
}

View file

@ -1,12 +0,0 @@
package problems
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type ProblemPostgresRepository interface {
CreateProblem(ctx context.Context, title string) (int32, error)
ReadProblemById(ctx context.Context, id int32) (*models.Problem, error)
DeleteProblem(ctx context.Context, id int32) error
}

View file

@ -1,18 +0,0 @@
package problems
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type ProblemPolicyAgent interface {
CanCreateProblem(ctx context.Context) error
CanReadProblem(ctx context.Context) error
CanDeleteProblem(ctx context.Context) error
}
type ProblemUseCase interface {
CreateProblem(ctx context.Context, title string) (int32, error)
ReadProblemById(ctx context.Context, id int32) (*models.Problem, error)
DeleteProblem(ctx context.Context, id int32) error
}

View file

@ -1,10 +1,21 @@
package tester
import (
testerv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/tester/v1"
"google.golang.org/grpc"
testerv1 "git.sch9.ru/new_gate/ms-tester/proto/tester/v1"
"github.com/gofiber/fiber/v2"
)
type Handlers interface {
CreateSolution(*testerv1.CreateSolutionRequest, grpc.ServerStreamingServer[testerv1.TestingState]) error
ListContests(c *fiber.Ctx) error
CreateContest(c *fiber.Ctx) error
DeleteContest(c *fiber.Ctx, id int32) error
GetContest(c *fiber.Ctx, id int32) error
DeleteParticipant(c *fiber.Ctx, id int32, params testerv1.DeleteParticipantParams) error
AddParticipant(c *fiber.Ctx, id int32, params testerv1.AddParticipantParams) error
DeleteTask(c *fiber.Ctx, id int32, params testerv1.DeleteTaskParams) error
AddTask(c *fiber.Ctx, id int32, params testerv1.AddTaskParams) error
ListProblems(c *fiber.Ctx) error
CreateProblem(c *fiber.Ctx) error
DeleteProblem(c *fiber.Ctx, id int32) error
GetProblem(c *fiber.Ctx, id int32) error
}

View file

@ -1,41 +0,0 @@
package grpc
import (
"git.sch9.ru/new_gate/ms-tester/internal/tester"
testerv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/tester/v1"
"google.golang.org/grpc"
)
func NewTesterHandlers(gserver *grpc.Server, testerUC tester.TesterUseCase) {
handlers := &testerHandlers{
testerUC: testerUC,
}
testerv1.RegisterTesterServiceServer(gserver, handlers)
}
type testerHandlers struct {
testerv1.UnimplementedTesterServiceServer
testerUC tester.TesterUseCase
}
func (h *testerHandlers) CreateSolution(req *testerv1.CreateSolutionRequest, stream testerv1.TesterService_CreateSolutionServer) error {
id, err := h.testerUC.CreateSolution(stream.Context(), req.GetTaskId(), req.GetSolution(), req.GetLanguage())
if err != nil {
return err
}
ch, err := h.testerUC.ProcessTesting(stream.Context(), id)
if err != nil {
return err
}
for state := range ch {
err = stream.Send(&testerv1.TestingState{Msg: state})
if err != nil {
return err
}
}
return nil
}

View file

@ -1,107 +0,0 @@
package rabbitmq
import (
"context"
"fmt"
"git.sch9.ru/new_gate/ms-tester/internal/tester"
runnerv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/runner/v1"
"github.com/golang/protobuf/proto"
amqp "github.com/rabbitmq/amqp091-go"
)
func NewTesterProducer(ch *amqp.Channel, tQueueName string, rQueueName string, testerUC tester.TesterUseCase) {
_, err := ch.QueueDeclare(
tQueueName,
true,
false,
false,
false,
nil,
)
if err != nil {
panic(err)
}
go func() {
for d := range testerUC.TestingChannel() {
for i := 0; i < 15; i++ {
msg := runnerv1.Instruction{
Instruction: &runnerv1.Instruction_Run{
Run: &runnerv1.Run{
SolutionId: d,
TestId: 0,
BindingKey: rQueueName,
},
}}
body, err := proto.Marshal(&msg)
if err != nil {
panic(err)
}
err = ch.Publish(
"",
tQueueName,
false,
false,
amqp.Publishing{
ContentType: "text/plain",
Body: body,
},
)
if err != nil {
panic(err)
}
}
}
}()
return
}
func NewTesterConsumer(ch *amqp.Channel, rQueueName string, instanceName string, testerUC tester.TesterUseCase) {
_, err := ch.QueueDeclare(
rQueueName,
true,
false,
false,
false,
nil,
)
if err != nil {
panic(err)
}
msgs, err := ch.Consume(
rQueueName,
"",
false,
true, // each tester must have exclusive results queue
false,
false,
nil,
)
if err != nil {
panic(err)
}
go func() {
for d := range msgs {
err = d.Ack(false)
if err != nil {
panic(err)
}
msg := runnerv1.Result{}
err = proto.Unmarshal(d.Body, &msg)
if err != nil {
panic(err)
}
fmt.Println(msg.String())
err = testerUC.ProcessResult(context.Background(), msg.GetRun().SolutionId, msg.GetRun().String())
if err != nil {
fmt.Println(err)
}
}
}()
}

View file

@ -0,0 +1,145 @@
package rest
import (
"git.sch9.ru/new_gate/ms-tester/internal/tester"
testerv1 "git.sch9.ru/new_gate/ms-tester/proto/tester/v1"
"github.com/gofiber/fiber/v2"
)
type TesterHandlers struct {
problemsUC tester.ProblemUseCase
contestsUC tester.ContestUseCase
}
func NewTesterHandlers(problemsUC tester.ProblemUseCase, contestsUC tester.ContestRepository) *TesterHandlers {
return &TesterHandlers{
problemsUC: problemsUC,
contestsUC: contestsUC,
}
}
func (h *TesterHandlers) ListContests(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNotImplemented)
}
func (h *TesterHandlers) ListProblems(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNotImplemented)
}
func (h *TesterHandlers) CreateContest(c *fiber.Ctx) error {
id, err := h.contestsUC.CreateContest(c.Context(), "Название контеста")
if err != nil {
return 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 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 err
}
return c.JSON(testerv1.GetContestResponse{
Contest: testerv1.Contest{
Id: *contest.Id,
Title: *contest.Title,
CreatedAt: *contest.CreatedAt,
UpdatedAt: *contest.UpdatedAt,
},
})
}
func (h *TesterHandlers) DeleteParticipant(c *fiber.Ctx, id int32, params testerv1.DeleteParticipantParams) error {
err := h.contestsUC.DeleteParticipant(c.Context(), params.ParticipantId)
if err != nil {
return err
}
return c.SendStatus(fiber.StatusOK)
}
func (h *TesterHandlers) AddParticipant(c *fiber.Ctx, id int32, params testerv1.AddParticipantParams) error {
id, err := h.contestsUC.AddParticipant(c.Context(), id, params.UserId)
if err != nil {
return err
}
return c.JSON(testerv1.AddParticipantResponse{
Id: id,
})
}
func (h *TesterHandlers) DeleteTask(c *fiber.Ctx, id int32, params testerv1.DeleteTaskParams) error {
err := h.contestsUC.DeleteTask(c.Context(), params.TaskId)
if err != nil {
return err
}
return c.SendStatus(fiber.StatusOK)
}
func (h *TesterHandlers) AddTask(c *fiber.Ctx, id int32, params testerv1.AddTaskParams) error {
id, err := h.contestsUC.AddTask(c.Context(), id, params.ProblemId)
if err != nil {
return err
}
return c.SendStatus(fiber.StatusNotImplemented)
}
func (h *TesterHandlers) CreateProblem(c *fiber.Ctx) error {
id, err := h.problemsUC.CreateProblem(c.Context(), "Название задачи")
if err != nil {
return 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 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 err
}
return c.JSON(
testerv1.GetProblemResponse{Problem: testerv1.Problem{
Id: *problem.Id,
Legend: *problem.Legend,
InputFormat: *problem.InputFormat,
OutputFormat: *problem.OutputFormat,
Notes: *problem.Notes,
Tutorial: *problem.Tutorial,
LatexSummary: *problem.LatexSummary,
TimeLimit: *problem.TimeLimit,
MemoryLimit: *problem.MemoryLimit,
CreatedAt: *problem.CreatedAt,
UpdatedAt: *problem.UpdatedAt,
}},
)
}

View file

@ -1,4 +1,22 @@
package tester
type TesterRepository interface {
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type ProblemPostgresRepository interface {
CreateProblem(ctx context.Context, title string) (int32, error)
ReadProblemById(ctx context.Context, id int32) (*models.Problem, error)
DeleteProblem(ctx context.Context, id int32) error
}
type ContestRepository interface {
CreateContest(ctx context.Context, title string) (int32, error)
ReadContestById(ctx context.Context, id int32) (*models.Contest, error)
DeleteContest(ctx context.Context, id int32) error
AddTask(ctx context.Context, contestId int32, taskId int32) (int32, error)
DeleteTask(ctx context.Context, taskId int32) error
AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error)
DeleteParticipant(ctx context.Context, participantId int32) error
}

View file

@ -0,0 +1,23 @@
package repository
import (
"errors"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"
)
func handlePgErr(err error) error {
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
return utils.StorageError(err, utils.ErrUnknown, "unexpected error from postgres")
}
if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) {
// TODO: probably should specify which constraint
return utils.StorageError(err, utils.ErrConflict, pgErr.Message)
}
if pgerrcode.IsNoData(pgErr.Code) {
return utils.StorageError(err, utils.ErrNotFound, pgErr.Message)
}
return utils.StorageError(err, utils.ErrUnimplemented, "unimplemented error")
}

View file

@ -2,11 +2,7 @@ package repository
import (
"context"
"errors"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
@ -23,7 +19,7 @@ func NewContestRepository(db *sqlx.DB, logger *zap.Logger) *ContestRepository {
}
}
const createContestQuery = "INSERT INTO contest (title) VALUES (?) RETURNING id"
const createContestQuery = "INSERT INTO contests (title) VALUES (?) RETURNING id"
func (r *ContestRepository) CreateContest(ctx context.Context, title string) (int32, error) {
query := r.db.Rebind(createContestQuery)
@ -44,7 +40,7 @@ func (r *ContestRepository) CreateContest(ctx context.Context, title string) (in
return id, nil
}
const readContestByIdQuery = "SELECT * from contest WHERE id=? LIMIT 1"
const readContestByIdQuery = "SELECT * from contests WHERE id=? LIMIT 1"
func (r *ContestRepository) ReadContestById(ctx context.Context, id int32) (*models.Contest, error) {
var contest models.Contest
@ -56,7 +52,7 @@ func (r *ContestRepository) ReadContestById(ctx context.Context, id int32) (*mod
return &contest, nil
}
const deleteContestQuery = "DELETE FROM contest WHERE id=?"
const deleteContestQuery = "DELETE FROM contests WHERE id=?"
func (r *ContestRepository) DeleteContest(ctx context.Context, id int32) error {
query := r.db.Rebind(deleteContestQuery)
@ -68,7 +64,7 @@ func (r *ContestRepository) DeleteContest(ctx context.Context, id int32) error {
return nil
}
const addTaskQuery = "INSERT INTO task (problem_id, contest_id, position) VALUES (?, ?,COALESCE(SELECT MAX(position) FROM task WHERE contest_id = ? ,0) + 1) RETURNING id"
const addTaskQuery = "INSERT INTO tasks (problem_id, contest_id, position) VALUES (?, ?,COALESCE(SELECT MAX(position) FROM task WHERE contest_id = ? ,0) + 1) RETURNING id"
func (r *ContestRepository) AddTask(ctx context.Context, contestId int32, problem_id int32) (int32, error) {
query := r.db.Rebind(addTaskQuery)
@ -86,7 +82,7 @@ func (r *ContestRepository) AddTask(ctx context.Context, contestId int32, proble
return id, nil
}
const deleteTaskQuery = "DELETE FROM task WHERE id=?"
const deleteTaskQuery = "DELETE FROM tasks WHERE id=?"
func (r *ContestRepository) DeleteTask(ctx context.Context, taskId int32) error {
query := r.db.Rebind(deleteTaskQuery)
@ -97,7 +93,7 @@ func (r *ContestRepository) DeleteTask(ctx context.Context, taskId int32) error
return nil
}
const addParticipantQuery = "INSERT INTO participant (user_id ,contest_id, name) VALUES (?, ?, ?) RETURNING id"
const addParticipantQuery = "INSERT INTO participants (user_id ,contest_id, name) VALUES (?, ?, ?) RETURNING id"
func (r *ContestRepository) AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error) {
query := r.db.Rebind(addParticipantQuery)
@ -116,7 +112,7 @@ func (r *ContestRepository) AddParticipant(ctx context.Context, contestId int32,
return id, nil
}
const deleteParticipantQuery = "DELETE FROM participant WHERE id=?"
const deleteParticipantQuery = "DELETE FROM participants WHERE id=?"
func (r *ContestRepository) DeleteParticipant(ctx context.Context, participantId int32) error {
query := r.db.Rebind(deleteParticipantQuery)
@ -126,18 +122,3 @@ func (r *ContestRepository) DeleteParticipant(ctx context.Context, participantId
}
return nil
}
func handlePgErr(err error) error {
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
return utils.StorageError(err, utils.ErrUnknown, "unexpected error from postgres")
}
if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) {
// TODO: probably should specify which constraint
return utils.StorageError(err, utils.ErrConflict, pgErr.Message)
}
if pgerrcode.IsNoData(pgErr.Code) {
return utils.StorageError(err, utils.ErrNotFound, pgErr.Message)
}
return utils.StorageError(err, utils.ErrUnimplemented, "unimplemented error")
}

View file

@ -2,11 +2,7 @@ package repository
import (
"context"
"errors"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
@ -66,18 +62,3 @@ func (r *ProblemRepository) DeleteProblem(ctx context.Context, id int32) error {
return nil
}
func handlePgErr(err error) error {
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
return utils.StorageError(err, utils.ErrUnknown, "unexpected error from postgres")
}
if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) {
// TODO: probably should specify which constraint
return utils.StorageError(err, utils.ErrConflict, pgErr.Message)
}
if pgerrcode.IsNoData(pgErr.Code) {
return utils.StorageError(err, utils.ErrNotFound, pgErr.Message)
}
return utils.StorageError(err, utils.ErrUnimplemented, "unimplemented error")
}

View file

@ -1 +0,0 @@
package repository

View file

@ -2,11 +2,21 @@ package tester
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type TesterUseCase interface {
CreateSolution(ctx context.Context, taskId int32, solution string, language int32) (int32, error)
ProcessTesting(ctx context.Context, solutionId int32) (<-chan string, error)
ProcessResult(ctx context.Context, solutionId int32, result string) error
TestingChannel() chan int32
type ProblemUseCase interface {
CreateProblem(ctx context.Context, title string) (int32, error)
ReadProblemById(ctx context.Context, id int32) (*models.Problem, error)
DeleteProblem(ctx context.Context, id int32) error
}
type ContestUseCase interface {
CreateContest(ctx context.Context, title string) (int32, error)
ReadContestById(ctx context.Context, id int32) (*models.Contest, error)
DeleteContest(ctx context.Context, id int32) error
AddTask(ctx context.Context, contestId int32, taskId int32) (int32, error)
DeleteTask(ctx context.Context, taskId int32) error
AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error)
DeleteParticipant(ctx context.Context, participantId int32) error
}

View file

@ -2,16 +2,16 @@ package usecase
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/contests"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/internal/tester"
)
type ContestUseCase struct {
contestRepo contests.ContestRepository
contestRepo tester.ContestRepository
}
func NewContestUseCase(
contestRepo contests.ContestRepository,
contestRepo tester.ContestRepository,
) *ContestUseCase {
return &ContestUseCase{
contestRepo: contestRepo,

View file

@ -3,22 +3,21 @@ package usecase
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/internal/problems"
"git.sch9.ru/new_gate/ms-tester/pkg/external/pandoc"
"git.sch9.ru/new_gate/ms-tester/internal/tester"
)
type ProblemUseCase struct {
problemRepo problems.ProblemPostgresRepository
pandocClient pandoc.PandocClient
problemRepo tester.ProblemPostgresRepository
//pandocClient pandoc.PandocClient
}
func NewProblemUseCase(
problemRepo problems.ProblemPostgresRepository,
pandocClient pandoc.PandocClient,
problemRepo tester.ProblemPostgresRepository,
// pandocClient pandoc.PandocClient,
) *ProblemUseCase {
return &ProblemUseCase{
problemRepo: problemRepo,
pandocClient: pandocClient,
problemRepo: problemRepo,
//pandocClient: pandocClient,
}
}

View file

@ -1,107 +0,0 @@
package usecase
import (
"context"
"errors"
"fmt"
"math/rand"
"sync"
)
const (
MaxSimultaneousTestingProcesses = 5000
MaxMessagesPerSolution = 500
)
type TesterUseCase struct {
publicChannels sync.Map
privateChannels sync.Map
testingChannel chan int32
}
func NewTesterUseCase() *TesterUseCase {
return &TesterUseCase{
testingChannel: make(chan int32, MaxSimultaneousTestingProcesses),
}
}
func (u *TesterUseCase) CreateSolution(ctx context.Context, taskId int32, solution string, language int32) (int32, error) {
return rand.Int31(), nil
}
func (u *TesterUseCase) ProcessTesting(ctx context.Context, solutionId int32) (<-chan string, error) {
u.testingChannel <- solutionId
publicChannel := u.newPublicChannel(solutionId)
go func() {
privateChannel := u.newPrivateChannel(solutionId)
defer func() {
err := u.closeAndDeletePrivateChannel(solutionId)
if err != nil {
panic(err)
}
err = u.closeAndDeletePublicChannel(solutionId)
if err != nil {
panic(err)
}
}()
c := 0
for res := range privateChannel {
c += 1
publicChannel <- res
if c == 15 {
fmt.Println("finished")
break
}
}
}()
return publicChannel, nil
}
func (u *TesterUseCase) ProcessResult(ctx context.Context, solutionId int32, result string) error {
ch, ok := u.privateChannels.Load(solutionId)
if !ok {
return errors.New("")
}
ch.(chan string) <- result
return nil
}
func (u *TesterUseCase) TestingChannel() chan int32 {
return u.testingChannel
}
func (u *TesterUseCase) newPublicChannel(solutionId int32) chan string {
userCh := make(chan string, MaxMessagesPerSolution)
u.publicChannels.Store(solutionId, userCh)
return userCh
}
func (u *TesterUseCase) newPrivateChannel(solutionId int32) chan string {
userCh := make(chan string, MaxMessagesPerSolution)
u.privateChannels.Store(solutionId, userCh)
return userCh
}
func (u *TesterUseCase) closeAndDeletePublicChannel(solutionId int32) error {
ch, ok := u.publicChannels.Load(solutionId)
if !ok {
return errors.New("")
}
close(ch.(chan string))
u.publicChannels.Delete(solutionId)
return nil
}
func (u *TesterUseCase) closeAndDeletePrivateChannel(solutionId int32) error {
ch, ok := u.privateChannels.Load(solutionId)
if !ok {
return errors.New("")
}
close(ch.(chan string))
u.privateChannels.Delete(solutionId)
return nil
}