diff --git a/internal/lib/pandoc.go b/internal/lib/pandoc.go new file mode 100644 index 0000000..df47e14 --- /dev/null +++ b/internal/lib/pandoc.go @@ -0,0 +1,61 @@ +package lib + +import ( + "bytes" + "encoding/json" + "io" + "net/http" +) + +type PandocClient struct { + client *http.Client + address string +} + +func NewPandocClient(client *http.Client, address string) *PandocClient { + return &PandocClient{ + client: client, + address: address, + } +} + +type convertRequest struct { + Text string `json:"text"` + From string `json:"from"` + To string `json:"to"` +} + +func (client *PandocClient) convert(text, from, to string) (string, error) { + body, err := json.Marshal(convertRequest{ + Text: text, + From: from, + To: to, + }) + if err != nil { + return "", err + } + + buf := bytes.NewBuffer(body) + + resp, err := client.client.Post(client.address, "application/json", buf) + if err != nil { + return "", err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", err + } + + body, err = io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + return string(body), nil +} + +func (client *PandocClient) ConvertLatexToHtml5(text string) (string, error) { + return client.convert(text, "latex", "html5") +} diff --git a/internal/services/problem.go b/internal/services/problem.go index 96fc857..4747f5e 100644 --- a/internal/services/problem.go +++ b/internal/services/problem.go @@ -12,39 +12,49 @@ type ProblemStorage interface { DeleteProblem(ctx context.Context, id int32) error } +type PandocClient interface { + ConvertLatexToHtml5(text string) (string, error) +} + type ProblemService struct { problemStorage ProblemStorage + pandocClient PandocClient } func NewProblemService( problemStorage ProblemStorage, + pandocClient PandocClient, ) *ProblemService { return &ProblemService{ problemStorage: problemStorage, + pandocClient: pandocClient, } } -func (service *ProblemService) CreateProblem(ctx context.Context, problem *models.Problem) (int32, error) { +func (service *ProblemService) CreateProblem(ctx context.Context, problem *models.Problem, ch <-chan []byte) (int32, error) { userId := ctx.Value("user_id").(int32) - panic("access control is not implemented yet") + html, err := service.pandocClient.ConvertLatexToHtml5(*problem.Description) + if err != nil { + return 0, err + } + panic("access control is not implemented yet") return service.problemStorage.CreateProblem(ctx, problem) } func (service *ProblemService) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) { userId := ctx.Value("user_id").(int32) - panic("access control is not implemented yet") + panic("access control is not implemented yet") return service.problemStorage.ReadProblemById(ctx, id) } func (service *ProblemService) UpdateProblem(ctx context.Context, problem *models.Problem) error { userId := ctx.Value("user_id").(int32) - panic("access control is not implemented yet") + panic("access control is not implemented yet") return service.problemStorage.UpdateProblem(ctx, problem) } func (service *ProblemService) DeleteProblem(ctx context.Context, id int32) error { userId := ctx.Value("user_id").(int32) - panic("access control is not implemented yet") + panic("access control is not implemented yet") return service.problemStorage.DeleteProblem(ctx, id) } - diff --git a/internal/transport/problem.go b/internal/transport/problem.go new file mode 100644 index 0000000..3b84104 --- /dev/null +++ b/internal/transport/problem.go @@ -0,0 +1,145 @@ +package transport + +import ( + "context" + "git.sch9.ru/new_gate/ms-tester/internal/lib" + "git.sch9.ru/new_gate/ms-tester/internal/models" + problemv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/problem/v1" + sessionv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/session/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" + "io" +) + +func (s *TesterServer) CreateProblem(server problemv1.ProblemService_CreateProblemServer) error { + ctx := server.Context() + + req, err := server.Recv() // receive token + if err != nil { + return err // FIXME + } + + token := req.GetToken() + userId, err := s.sessionClient.Read(ctx, &sessionv1.ReadSessionRequest{ + Token: token, + }) + if err != nil { + return err // FIXME + } + + ctx = context.WithValue(ctx, "user_id", userId.GetUserId()) + + req, err = server.Recv() // receive problem + if err != nil { + return err // FIXME + } + problem := req.GetProblem() + if problem == nil { + return status.Errorf(codes.Unknown, "") // FIXME + } + + p := &models.Problem{ + Name: lib.AsStringP(problem.Name), + Description: lib.AsStringP(problem.Description), + TimeLimit: lib.AsInt32P(problem.TimeLimit), + MemoryLimit: lib.AsInt32P(problem.MemoryLimit), + } + + ch := readChunks(ctx, server) + + id, err := s.problemService.CreateProblem(ctx, p, ch) + if err != nil { + return status.Errorf(codes.Unknown, "") // FIXME + } + + err = server.SendAndClose(&problemv1.CreateProblemResponse{ + Id: id, + }) + if err != nil { + return err // FIXME + } + + return nil +} + +func readChunks(ctx context.Context, server problemv1.ProblemService_CreateProblemServer) <-chan []byte { + ch := make(chan []byte) + + go func() { + defer close(ch) + for { + select { + case <-ctx.Done(): + return // FIXME + default: + req, err := server.Recv() + if err != nil { + if err == io.EOF { + return // FIXME + } + if status.Code(err) == codes.Canceled { + return // FIXME + } + continue + } + + test := req.GetTest() + if test == nil { + return // FIXME + } + + ch <- test.Chunk + } + } + }() + return ch +} + +func (s *TesterServer) ReadProblem(ctx context.Context, req *problemv1.ReadProblemRequest) (*problemv1.ReadProblemResponse, error) { + problem, err := s.problemService.ReadProblemById(ctx, req.GetId()) + if err != nil { + return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME + } + return &problemv1.ReadProblemResponse{ + Problem: &problemv1.ReadProblemResponse_Problem{ + Id: *problem.Id, + Name: *problem.Name, + Description: *problem.Description, + TimeLimit: *problem.TimeLimit, + MemoryLimit: *problem.MemoryLimit, + CreatedAt: AsTimestampP(problem.CreatedAt), + UpdatedAt: AsTimestampP(problem.UpdatedAt), + }, + }, nil +} + +//func (s *TesterServer) UpdateProblem(ctx context.Context, req *problemv1.UpdateProblemRequest) (*emptypb.Empty, error) { +// problem := req.GetProblem() +// if problem == nil { +// return nil, status.Errorf(codes.Unknown, "") // FIXME +// } +// err := s.problemService.UpdateProblem( +// ctx, +// &models.Problem{ +// Id: lib.AsInt32P(problem.Id), +// Name: problem.Name, +// Description: problem.Description, +// TimeLimit: problem.TimeLimit, +// MemoryLimit: problem.MemoryLimit, +// }, +// ) +// if err != nil { +// return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME +// } +// +// return &emptypb.Empty{}, nil +//} + +func (s *TesterServer) DeleteProblem(ctx context.Context, req *problemv1.DeleteProblemRequest) (*emptypb.Empty, error) { + err := s.problemService.DeleteProblem(ctx, req.GetId()) + if err != nil { + return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME + } + return &emptypb.Empty{}, nil +} diff --git a/internal/transport/server.go b/internal/transport/server.go index 9993385..809d488 100644 --- a/internal/transport/server.go +++ b/internal/transport/server.go @@ -3,8 +3,8 @@ package transport import ( "context" "git.sch9.ru/new_gate/ms-tester/internal/models" + problemv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/problem/v1" sessionv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/session/v1" - testerv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/tester/v1" "go.uber.org/zap" "google.golang.org/protobuf/types/known/timestamppb" "time" @@ -13,8 +13,8 @@ import ( ) type ProblemService interface { - CreateProblem(ctx context.Context, problem *models.Problem) (int32, error) - ReadProblem(ctx context.Context, id int32) (*models.Problem, error) + CreateProblem(ctx context.Context, problem *models.Problem, ch <-chan []byte) (int32, error) // FIXME: specify chan type + ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) UpdateProblem(ctx context.Context, problem *models.Problem) error DeleteProblem(ctx context.Context, id int32) error } @@ -27,7 +27,7 @@ type SessionClient interface { } type TesterServer struct { - testerv1.UnimplementedTesterServiceServer + problemv1.UnimplementedProblemServiceServer problemService ProblemService sessionClient SessionClient @@ -51,7 +51,7 @@ func NewTesterServer( grpc.UnaryInterceptor(server.AuthInterceptor()), ) - testerv1.RegisterTesterServiceServer(grpcServer, server) + problemv1.RegisterProblemServiceServer(grpcServer, server) return server } diff --git a/internal/transport/tester.go b/internal/transport/tester.go deleted file mode 100644 index 07a01bd..0000000 --- a/internal/transport/tester.go +++ /dev/null @@ -1,82 +0,0 @@ -package transport - -import ( - "context" - "git.sch9.ru/new_gate/ms-tester/internal/lib" - "git.sch9.ru/new_gate/ms-tester/internal/models" - testerv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/tester/v1" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/emptypb" -) - -func (s *TesterServer) CreateProblem(ctx context.Context, req *testerv1.CreateProblemRequest) (*testerv1.CreateProblemResponse, error) { - problem := req.GetProblem() - if problem == nil { - return nil, status.Errorf(codes.Unknown, "") // FIXME - } - id, err := s.problemService.CreateProblem( - ctx, - &models.Problem{ - Name: lib.AsStringP(problem.Name), - Description: lib.AsStringP(problem.Description), - TimeLimit: lib.AsInt32P(problem.TimeLimit), - MemoryLimit: lib.AsInt32P(problem.MemoryLimit), - }, - ) - if err != nil { - return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME - } - - return &testerv1.CreateProblemResponse{ - Id: id, - }, nil -} - -func (s *TesterServer) ReadProblem(ctx context.Context, req *testerv1.ReadProblemRequest) (*testerv1.ReadProblemResponse, error) { - problem, err := s.problemService.ReadProblem(ctx, req.GetId()) - if err != nil { - return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME - } - return &testerv1.ReadProblemResponse{ - Problem: &testerv1.ReadProblemResponse_Problem{ - Id: *problem.Id, - Name: *problem.Name, - Description: *problem.Description, - TimeLimit: *problem.TimeLimit, - MemoryLimit: *problem.MemoryLimit, - CreatedAt: AsTimestampP(problem.CreatedAt), - UpdatedAt: AsTimestampP(problem.UpdatedAt), - }, - }, nil -} - -func (s *TesterServer) UpdateProblem(ctx context.Context, req *testerv1.UpdateProblemRequest) (*emptypb.Empty, error) { - problem := req.GetProblem() - if problem == nil { - return nil, status.Errorf(codes.Unknown, "") // FIXME - } - err := s.problemService.UpdateProblem( - ctx, - &models.Problem{ - Id: lib.AsInt32P(problem.Id), - Name: problem.Name, - Description: problem.Description, - TimeLimit: problem.TimeLimit, - MemoryLimit: problem.MemoryLimit, - }, - ) - if err != nil { - return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME - } - - return &emptypb.Empty{}, nil -} - -func (s *TesterServer) DeleteProblem(ctx context.Context, req *testerv1.DeleteProblemRequest) (*emptypb.Empty, error) { - err := s.problemService.DeleteProblem(ctx, req.GetId()) - if err != nil { - return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME - } - return &emptypb.Empty{}, nil -}