feat: add pandoc
This commit is contained in:
parent
5832e83460
commit
b35fd29049
5 changed files with 227 additions and 93 deletions
61
internal/lib/pandoc.go
Normal file
61
internal/lib/pandoc.go
Normal file
|
@ -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")
|
||||||
|
}
|
|
@ -12,39 +12,49 @@ type ProblemStorage interface {
|
||||||
DeleteProblem(ctx context.Context, id int32) error
|
DeleteProblem(ctx context.Context, id int32) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PandocClient interface {
|
||||||
|
ConvertLatexToHtml5(text string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
type ProblemService struct {
|
type ProblemService struct {
|
||||||
problemStorage ProblemStorage
|
problemStorage ProblemStorage
|
||||||
|
pandocClient PandocClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProblemService(
|
func NewProblemService(
|
||||||
problemStorage ProblemStorage,
|
problemStorage ProblemStorage,
|
||||||
|
pandocClient PandocClient,
|
||||||
) *ProblemService {
|
) *ProblemService {
|
||||||
return &ProblemService{
|
return &ProblemService{
|
||||||
problemStorage: problemStorage,
|
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)
|
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)
|
return service.problemStorage.CreateProblem(ctx, problem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (service *ProblemService) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) {
|
func (service *ProblemService) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) {
|
||||||
userId := ctx.Value("user_id").(int32)
|
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)
|
return service.problemStorage.ReadProblemById(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (service *ProblemService) UpdateProblem(ctx context.Context, problem *models.Problem) error {
|
func (service *ProblemService) UpdateProblem(ctx context.Context, problem *models.Problem) error {
|
||||||
userId := ctx.Value("user_id").(int32)
|
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)
|
return service.problemStorage.UpdateProblem(ctx, problem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (service *ProblemService) DeleteProblem(ctx context.Context, id int32) error {
|
func (service *ProblemService) DeleteProblem(ctx context.Context, id int32) error {
|
||||||
userId := ctx.Value("user_id").(int32)
|
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)
|
return service.problemStorage.DeleteProblem(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
145
internal/transport/problem.go
Normal file
145
internal/transport/problem.go
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -3,8 +3,8 @@ package transport
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"git.sch9.ru/new_gate/ms-tester/internal/models"
|
"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"
|
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"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
"time"
|
"time"
|
||||||
|
@ -13,8 +13,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProblemService interface {
|
type ProblemService interface {
|
||||||
CreateProblem(ctx context.Context, problem *models.Problem) (int32, error)
|
CreateProblem(ctx context.Context, problem *models.Problem, ch <-chan []byte) (int32, error) // FIXME: specify chan type
|
||||||
ReadProblem(ctx context.Context, id int32) (*models.Problem, error)
|
ReadProblemById(ctx context.Context, id int32) (*models.Problem, error)
|
||||||
UpdateProblem(ctx context.Context, problem *models.Problem) error
|
UpdateProblem(ctx context.Context, problem *models.Problem) error
|
||||||
DeleteProblem(ctx context.Context, id int32) error
|
DeleteProblem(ctx context.Context, id int32) error
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ type SessionClient interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type TesterServer struct {
|
type TesterServer struct {
|
||||||
testerv1.UnimplementedTesterServiceServer
|
problemv1.UnimplementedProblemServiceServer
|
||||||
problemService ProblemService
|
problemService ProblemService
|
||||||
|
|
||||||
sessionClient SessionClient
|
sessionClient SessionClient
|
||||||
|
@ -51,7 +51,7 @@ func NewTesterServer(
|
||||||
grpc.UnaryInterceptor(server.AuthInterceptor()),
|
grpc.UnaryInterceptor(server.AuthInterceptor()),
|
||||||
)
|
)
|
||||||
|
|
||||||
testerv1.RegisterTesterServiceServer(grpcServer, server)
|
problemv1.RegisterProblemServiceServer(grpcServer, server)
|
||||||
|
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
Loading…
Reference in a new issue