This commit is contained in:
Vyacheslav1557 2024-10-13 19:01:36 +05:00
parent 4cdd751b16
commit be25404852
51 changed files with 606 additions and 1194 deletions

View file

@ -1,4 +1,13 @@
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)
}

View file

@ -1 +1,51 @@
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
}

View file

@ -2,12 +2,11 @@ package contests
import (
"context"
"git.sch9.ru/new_gate/models"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type ContestRepository interface {
CreateContest(ctx context.Context, contest *models.Contest) (int32, error)
CreateContest(ctx context.Context, title string) (int32, error)
ReadContestById(ctx context.Context, id int32) (*models.Contest, error)
UpdateContest(ctx context.Context, contest *models.Contest) error
DeleteContest(ctx context.Context, id int32) error
}

View file

@ -1,79 +0,0 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/models"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
type ParticipantStorage struct {
db *sqlx.DB
logger *zap.Logger
}
func NewParticipantStorage(db *sqlx.DB, logger *zap.Logger) *ParticipantStorage {
return &ParticipantStorage{
db: db,
logger: logger,
}
}
func (storage *ParticipantStorage) CreateParticipant(ctx context.Context, participant *models.Participant) (int32, error) {
query := storage.db.Rebind(`
INSERT INTO participants
(user_id,contest_id,name)
VALUES (?,?,?)
RETURNING id
`)
rows, err := storage.db.QueryxContext(
ctx,
query,
participant.UserId,
participant.ContestId,
participant.Name,
)
if err != nil {
return 0, handlePgErr(err)
}
defer rows.Close()
var id int32
err = rows.StructScan(&id)
if err != nil {
return 0, handlePgErr(err)
}
return id, nil
}
func (storage *ParticipantStorage) ReadParticipantById(ctx context.Context, id int32) (*models.Participant, error) {
var participant models.Participant
query := storage.db.Rebind("SELECT * from participants WHERE id=? LIMIT 1")
err := storage.db.GetContext(ctx, &participant, query, id)
if err != nil {
return nil, handlePgErr(err)
}
return &participant, nil
}
func (storage *ParticipantStorage) UpdateParticipant(ctx context.Context, id int32, participant models.Participant) error {
query := storage.db.Rebind("UPDATE participants SET name=?")
_, err := storage.db.ExecContext(ctx, query, participant.Name)
if err != nil {
return handlePgErr(err)
}
return nil
}
func (storage *ParticipantStorage) DeleteParticipant(ctx context.Context, id int32) error {
query := storage.db.Rebind("DELETE FROM participants WHERE id=?")
_, err := storage.db.ExecContext(ctx, query, id)
if err != nil {
return handlePgErr(err)
}
return nil
}

View file

@ -3,7 +3,7 @@ package repository
import (
"context"
"errors"
"git.sch9.ru/new_gate/models"
"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"
@ -23,37 +23,32 @@ func NewContestRepository(db *sqlx.DB, logger *zap.Logger) *ContestRepository {
}
}
func (r *ContestRepository) CreateContest(ctx context.Context, contest *models.Contest) (int32, error) {
query := r.db.Rebind(`
INSERT INTO contests
(name)
VALUES (?)
RETURNING id
`)
const createContestQuery = "INSERT INTO contest (title) VALUES (?) RETURNING id"
rows, err := r.db.QueryxContext(
ctx,
query,
contest.Name,
)
func (r *ContestRepository) CreateContest(ctx context.Context, title string) (int32, error) {
query := r.db.Rebind(createContestQuery)
rows, err := r.db.QueryxContext(ctx, query, title)
if err != nil {
return 0, handlePgErr(err)
}
defer rows.Close()
var id int32
err = rows.StructScan(&id)
rows.Next()
err = rows.Scan(&id)
if err != nil {
return 0, handlePgErr(err)
}
return id, nil
}
const readContestByIdQuery = "SELECT * from contest WHERE id=? LIMIT 1"
func (r *ContestRepository) ReadContestById(ctx context.Context, id int32) (*models.Contest, error) {
var contest models.Contest
query := r.db.Rebind("SELECT * from contests WHERE id=? LIMIT 1")
query := r.db.Rebind(readContestByIdQuery)
err := r.db.GetContext(ctx, &contest, query, id)
if err != nil {
return nil, handlePgErr(err)
@ -61,18 +56,10 @@ func (r *ContestRepository) ReadContestById(ctx context.Context, id int32) (*mod
return &contest, nil
}
func (r *ContestRepository) UpdateContest(ctx context.Context, contest *models.Contest) error {
query := r.db.Rebind("UPDATE contests SET name=? WHERE id=?")
_, err := r.db.ExecContext(ctx, query, contest.Name, contest.Id)
if err != nil {
return handlePgErr(err)
}
return nil
}
const deleteContestQuery = "DELETE FROM contest WHERE id=?"
func (r *ContestRepository) DeleteContest(ctx context.Context, id int32) error {
query := r.db.Rebind("DELETE FROM contests WHERE id=?")
query := r.db.Rebind(deleteContestQuery)
_, err := r.db.ExecContext(ctx, query, id)
if err != nil {
return handlePgErr(err)

View file

@ -0,0 +1,58 @@
package repository
import (
"context"
"github.com/DATA-DOG/go-sqlmock"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"testing"
)
func TestContestRepository_CreateContest(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
contestRepo := NewContestRepository(sqlxDB, zap.NewNop())
t.Run("valid contest creation", func(t *testing.T) {
title := "Contest title"
rows := sqlmock.NewRows([]string{"id"}).AddRow(1)
mock.ExpectQuery(sqlxDB.Rebind(createContestQuery)).WithArgs(title).WillReturnRows(rows)
id, err := contestRepo.CreateContest(context.Background(), title)
require.NoError(t, err)
require.Equal(t, int32(1), id)
})
}
func TestContestRepository_DeleteContest(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
contestRepo := NewContestRepository(sqlxDB, zap.NewNop())
t.Run("valid contest deletion", func(t *testing.T) {
id := int32(1)
rows := sqlmock.NewResult(1, 1)
mock.ExpectExec(sqlxDB.Rebind(deleteContestQuery)).WithArgs(id).WillReturnResult(rows)
err = contestRepo.DeleteContest(context.Background(), id)
require.NoError(t, err)
})
}

View file

@ -1,114 +0,0 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/models"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
type SolutionStorage struct {
db *sqlx.DB
logger *zap.Logger
}
func NewSolutionStorage(db *sqlx.DB, logger *zap.Logger) *SolutionStorage {
return &SolutionStorage{
db: db,
logger: logger,
}
}
// TODO: testing graph
func (storage *SolutionStorage) updateResult(ctx context.Context, participantId int32, taskId int32) error {
tx, err := storage.db.Beginx()
if err != nil {
return handlePgErr(err)
}
query := tx.Rebind("UPDATE participant_subtask AS ps SET best_score = (SELECT COALESCE(max(score),0) FROM subtaskruns WHERE subtask_id = ps.subtask_id AND solution_id IN (SELECT id FROM solutions WHERE participant_id=ps.participant_id)) WHERE participant_id = 2 AND subtask_id IN (SELECT id FROM subtasks WHERE task_id = 2)")
tx.QueryxContext(ctx, query, participantId, taskId)
query = tx.Rebind("UPDATE participant_task SET best_score = (select max(best_score) from participant_subtask WHERE participant_id = ? AND subtask_id IN (SELECT id FROM subtask WHERE task_id = ?)) WHERE participant_id = ? AND task_id = ?")
tx.QueryxContext(ctx, query, participantId, taskId, participantId, taskId)
err = tx.Commit()
if err != nil {
return handlePgErr(err)
}
return nil
}
func (storage *SolutionStorage) CreateSolution(ctx context.Context, solution *models.Solution) (int32, error) {
query := storage.db.Rebind(`
INSERT INTO solutions
(participant_id,task_id,language_id,solution_hash,result)
VALUES (?, ?, ?, ?, ?)
RETURNING id
`)
rows, err := storage.db.QueryxContext(
ctx,
query,
solution.ParticipantId,
solution.TaskId,
solution.LanguageId,
"", //FIXME
models.NotTested,
)
//TODO: add testing tree
if err != nil {
return 0, handlePgErr(err)
}
defer rows.Close()
var id int32
err = rows.StructScan(&id)
if err != nil {
return 0, handlePgErr(err)
}
return id, nil
}
func (storage *SolutionStorage) ReadSolutionById(ctx context.Context, id int32) (*models.Solution, error) {
var solution models.Solution
query := storage.db.Rebind("SELECT * from solutions WHERE id=? LIMIT 1")
err := storage.db.GetContext(ctx, &solution, query, id)
if err != nil {
return nil, handlePgErr(err)
}
return &solution, nil
}
func (storage *SolutionStorage) RejudgeSolution(ctx context.Context, id int32) error {
tx, err := storage.db.Beginx()
if err != nil {
return handlePgErr(err)
}
query := tx.Rebind("UPDATE solutions SET result = ? WHERE id = ?")
tx.QueryxContext(ctx, query, models.NotTested, id) // FIXME
query = tx.Rebind("UPDATE subtaskruns SET result = ?,score = 0 WHERE solution_id = ?")
tx.QueryxContext(ctx, query, models.NotTested, id) // FIXME
query = tx.Rebind("UPDATE testruns SET result = ?, score = 0 WHERE testgrouprun_id IN (SELECT id FROM tesgrouprun WHERE solution_id = ?)")
tx.QueryxContext(ctx, query, models.NotTested, id) // FIXME
err = tx.Commit()
var solution models.Solution
query = storage.db.Rebind("SELECT * from solutions WHERE id=? LIMIT 1")
err = storage.db.GetContext(ctx, &solution, query, id)
if err != nil {
return handlePgErr(err)
}
storage.updateResult(ctx, *solution.ParticipantId, *solution.TaskId) // FIXME
return nil
}
func (storage *SolutionStorage) DeleteSolution(ctx context.Context, id int32) error {
query := storage.db.Rebind("DELETE FROM solutions WHERE id=?")
_, err := storage.db.ExecContext(ctx, query, id)
if err != nil {
return handlePgErr(err)
}
return nil
}

View file

@ -1,80 +0,0 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/models"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
type TaskStorage struct {
db *sqlx.DB
logger *zap.Logger
}
func NewTaskStorage(db *sqlx.DB, logger *zap.Logger) *TaskStorage {
return &TaskStorage{
db: db,
logger: logger,
}
}
func (storage *TaskStorage) CreateTask(ctx context.Context, task *models.Task) (int32, error) {
query := storage.db.Rebind(`
INSERT INTO tasks
(contest_id,problem_id,position,position_name)
VALUES (?, ?, ?, ?)
RETURNING id
`)
rows, err := storage.db.QueryxContext(
ctx,
query,
task.ContestId,
task.ProblemId,
task.Position,
task.PositionName,
)
if err != nil {
return 0, handlePgErr(err)
}
defer rows.Close()
var id int32
err = rows.StructScan(&id)
if err != nil {
return 0, handlePgErr(err)
}
return id, nil
}
func (storage *TaskStorage) ReadTaskById(ctx context.Context, id int32) (*models.Task, error) {
var task models.Task
query := storage.db.Rebind("SELECT * from tasks WHERE id=? LIMIT 1")
err := storage.db.GetContext(ctx, &task, query, id)
if err != nil {
return nil, handlePgErr(err)
}
return &task, nil
}
func (storage *TaskStorage) UpdateTask(ctx context.Context, id int32, task models.Task) error {
query := storage.db.Rebind("UPDATE tasks SET position=?,position_name=?")
_, err := storage.db.ExecContext(ctx, query, task.Position, task.PositionName)
if err != nil {
return handlePgErr(err)
}
return nil
}
func (storage *TaskStorage) DeleteTask(ctx context.Context, id int32) error {
query := storage.db.Rebind("DELETE FROM tasks WHERE id=?")
_, err := storage.db.ExecContext(ctx, query, id)
if err != nil {
return handlePgErr(err)
}
return nil
}

View file

@ -2,12 +2,11 @@ package contests
import (
"context"
"git.sch9.ru/new_gate/models"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type ContestUseCase interface {
CreateContest(ctx context.Context, contest *models.Contest) (int32, error)
CreateContest(ctx context.Context, title string) (int32, error)
ReadContestById(ctx context.Context, id int32) (*models.Contest, error)
UpdateContest(ctx context.Context, contest *models.Contest) error
DeleteContest(ctx context.Context, id int32) error
}

View file

@ -1,57 +0,0 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/models"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
)
type ParticipantStorage interface {
CreateParticipant(ctx context.Context, participant *models.Participant) (int32, error)
ReadParticipantById(ctx context.Context, id int32) (*models.Participant, error)
UpdateParticipant(ctx context.Context, participant *models.Participant) error
DeleteParticipant(ctx context.Context, id int32) error
}
type ParticipantService struct {
participantStorage ParticipantStorage
permissionService IPermissionService
}
func NewParticipantService(
participantStorage ParticipantStorage,
permissionService IPermissionService,
) *ParticipantService {
return &ParticipantService{
participantStorage: participantStorage,
permissionService: permissionService,
}
}
func (service *ParticipantService) CreateParticipant(ctx context.Context, participant *models.Participant) (int32, error) {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "create") {
return 0, utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.participantStorage.CreateParticipant(ctx, participant)
}
func (service *ParticipantService) ReadParticipantById(ctx context.Context, id int32) (*models.Participant, error) {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "read") {
return nil, utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.participantStorage.ReadParticipantById(ctx, id)
}
func (service *ParticipantService) UpdateParticipant(ctx context.Context, participant *models.Participant) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "update") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.participantStorage.UpdateParticipant(ctx, participant)
}
func (service *ParticipantService) DeleteParticipant(ctx context.Context, id int32) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "delete") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.participantStorage.DeleteParticipant(ctx, id)
}

View file

@ -1,39 +0,0 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/models"
"github.com/open-policy-agent/opa/rego"
)
type PermissionService struct {
query *rego.PreparedEvalQuery
}
func NewPermissionService() *PermissionService {
query, err := rego.New(
rego.Query("allow = data.problem.rbac.allow"),
rego.Load([]string{"./opa/all.rego"}, nil),
).PrepareForEval(context.TODO())
if err != nil {
panic(err)
}
return &PermissionService{
query: &query,
}
}
func (s *PermissionService) Allowed(ctx context.Context, user *models.User, action string) bool {
input := map[string]interface{}{
"user": user,
"action": action,
}
result, err := s.query.Eval(ctx, rego.EvalInput(input))
if err != nil {
panic(err)
}
return result[0].Bindings["allow"].(bool)
}

View file

@ -1,57 +0,0 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/models"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
)
type SolutionStorage interface {
CreateSolution(ctx context.Context, solution models.Solution) (int32, error)
ReadSolutionById(ctx context.Context, id int32) (models.Solution, error)
RejudgeSolution(ctx context.Context, id int32) error
DeleteSolution(ctx context.Context, id int32) error
}
type SolutionService struct {
solutionStorage SolutionStorage
permissionService IPermissionService
}
func NewSolutionService(
solutionStorage SolutionStorage,
permissionService IPermissionService,
) *SolutionService {
return &SolutionService{
solutionStorage: solutionStorage,
permissionService: permissionService,
}
}
func (service *SolutionService) CreateSolution(ctx context.Context, solution models.Solution) (int32, error) {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "create") {
return 0, utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.solutionStorage.CreateSolution(ctx, solution)
}
func (service *SolutionService) ReadSolutionById(ctx context.Context, id int32) (models.Solution, error) {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "read") {
return models.Solution{}, utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.solutionStorage.ReadSolutionById(ctx, id)
}
func (service *SolutionService) RejudgeSolution(ctx context.Context, id int32) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "update") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.solutionStorage.RejudgeSolution(ctx, id)
}
func (service *SolutionService) DeleteSolution(ctx context.Context, id int32) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "delete") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.solutionStorage.DeleteSolution(ctx, id)
}

View file

@ -1,41 +0,0 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/models"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
)
type TaskStorage interface {
CreateTask(ctx context.Context, task models.Task) (int32, error)
DeleteTask(ctx context.Context, id int32) error
}
type TaskService struct {
taskStorage TaskStorage
permissionService IPermissionService
}
func NewTaskService(
taskStorage TaskStorage,
permissionService IPermissionService,
) *TaskService {
return &TaskService{
taskStorage: taskStorage,
permissionService: permissionService,
}
}
func (service *TaskService) CreateTask(ctx context.Context, task models.Task) (int32, error) {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "create") {
return 0, utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.taskStorage.CreateTask(ctx, task)
}
func (service *TaskService) DeleteTask(ctx context.Context, id int32) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "delete") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.taskStorage.DeleteTask(ctx, id)
}

View file

@ -4,48 +4,28 @@ import (
"context"
"git.sch9.ru/new_gate/models"
"git.sch9.ru/new_gate/ms-tester/internal/contests"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
)
type ContestService struct {
contestStorage contests.ContestRepository
permissionService IPermissionService
type ContestUseCase struct {
contestRepo contests.ContestRepository
}
func NewContestService(
contestStorage ContestStorage,
permissionService IPermissionService,
) *ContestService {
return &ContestService{
contestStorage: contestStorage,
permissionService: permissionService,
func NewContestUseCase(
contestRepo contests.ContestRepository,
) *ContestUseCase {
return &ContestUseCase{
contestRepo: contestRepo,
}
}
func (service *ContestService) CreateContest(ctx context.Context, contest *models.Contest) (int32, error) {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "create") {
return 0, utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.contestStorage.CreateContest(ctx, contest)
func (uc *ContestUseCase) CreateContest(ctx context.Context, title string) (int32, error) {
return uc.contestRepo.CreateContest(ctx, title)
}
func (service *ContestService) ReadContestById(ctx context.Context, id int32) (*models.Contest, error) {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "read") {
return nil, utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.contestStorage.ReadContestById(ctx, id)
func (uc *ContestUseCase) ReadContestById(ctx context.Context, id int32) (*models.Contest, error) {
return uc.contestRepo.ReadContestById(ctx, id)
}
func (service *ContestService) UpdateContest(ctx context.Context, contest *models.Contest) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "update") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.contestStorage.UpdateContest(ctx, contest)
}
func (service *ContestService) DeleteContest(ctx context.Context, id int32) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "delete") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return service.contestStorage.DeleteContest(ctx, id)
func (uc *ContestUseCase) DeleteContest(ctx context.Context, id int32) error {
return uc.contestRepo.DeleteContest(ctx, id)
}

View file

@ -1,4 +0,0 @@
package languages
type languageHandlers interface {
}

View file

@ -1 +0,0 @@
package grpc

View file

@ -1,10 +0,0 @@
package languages
import (
"context"
"git.sch9.ru/new_gate/models"
)
type LanguageRepository interface {
ReadLanguageById(ctx context.Context, id int32) (*models.Language, error)
}

View file

@ -1,28 +0,0 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/models"
"git.sch9.ru/new_gate/ms-tester/pkg/utils"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
type LanguageRepository struct {
db *sqlx.DB
logger *zap.Logger
}
func NewLanguageRepository(db *sqlx.DB, logger *zap.Logger) *LanguageRepository {
return &LanguageRepository{
db: db,
logger: logger,
}
}
func (r *LanguageRepository) ReadLanguageById(ctx context.Context, id int32) (*models.Language, error) {
if id <= int32(len(models.Languages)) {
return nil, utils.StorageError(nil, utils.ErrNotFound, "languages not found")
}
return &models.Languages[id], nil
}

View file

@ -1,10 +0,0 @@
package languages
import (
"context"
"git.sch9.ru/new_gate/models"
)
type LanguageUseCase interface {
ReadLanguageById(ctx context.Context, id int32) (*models.Language, error)
}

View file

@ -1,25 +0,0 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/models"
"git.sch9.ru/new_gate/ms-tester/internal/languages"
)
type LanguageUseCase struct {
languageRepo languages.LanguageRepository
}
func NewLanguageUseCase(
languageRepo languages.LanguageRepository,
) *LanguageUseCase {
return &LanguageUseCase{
languageRepo: languageRepo,
}
}
func (uc *LanguageUseCase) ReadLanguageById(ctx context.Context, id int32) (*models.Language, error) {
//userId := ctx.Value("user_id").(int32)
panic("access control is not implemented yet")
return uc.languageRepo.ReadLanguageById(ctx, id)
}

View file

@ -0,0 +1,10 @@
package models
import "time"
type Contest struct {
Id *int32 `db:"id"`
Title *string `db:"title"`
CreatedAt *time.Time `db:"created_at"`
UpdatedAt *time.Time `db:"updated_at"`
}

View file

@ -0,0 +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"}},
}

View file

@ -0,0 +1,8 @@
package models
type Participant struct {
Id *int32 `db:"id"`
UserId *int32 `db:"user_id"`
ContestId *int32 `db:"contest_id"`
Name *string `db:"name"`
}

View file

@ -0,0 +1,15 @@
package models
import "time"
type Problem struct {
Id *int32 `db:"id"`
Title *string `db:"title"`
Content *string `db:"content"`
TimeLimit *int32 `db:"time_limit"`
MemoryLimit *int32 `db:"memory_limit"`
TestingStrategy *int32 `db:"testing_strategy"`
TestingOrder *string `db:"testing_order"`
CreatedAt *time.Time `db:"created_at"`
UpdatedAt *time.Time `db:"updated_at"`
}

30
internal/models/result.go Normal file
View file

@ -0,0 +1,30 @@
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
}

52
internal/models/role.go Normal file
View file

@ -0,0 +1,52 @@
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

@ -0,0 +1,14 @@
package models
import "time"
type Solution struct {
Id *int32 `db:"id"`
ParticipantId *int32 `db:"participant_id"`
TaskId *int32 `db:"task_id"`
LanguageId *int32 `db:"language_id"`
SolutionHash *string `db:"solution_hash"`
Result *int32 `db:"result"`
Score *int32 `db:"score"`
CreatedAt *time.Time `db:"created_at"`
}

9
internal/models/task.go Normal file
View file

@ -0,0 +1,9 @@
package models
type Task struct {
Id *int32 `db:"id"`
ContestId *int32 `db:"contest_id"`
ProblemId *int32 `db:"problem_id"`
Position *int32 `db:"position"`
PositionName *string `db:"position_name"`
}

9
internal/models/user.go Normal file
View file

@ -0,0 +1,9 @@
package models
import "time"
type User struct {
UserId *int32 `json:"user_id" db:"user_id"`
Role *Role `json:"role" db:"role"`
UpdatedAt *time.Time `json:"updated_at" db:"updated_at"`
}

View file

@ -7,7 +7,7 @@ import (
)
type Handlers interface {
CreateProblem(server problemv1.ProblemService_CreateProblemServer) error
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

@ -2,15 +2,11 @@ package grpc
import (
"context"
"git.sch9.ru/new_gate/models"
"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/codes"
"google.golang.org/grpc/status"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
"io"
"os"
)
type problemHandlers struct {
@ -19,105 +15,18 @@ type problemHandlers struct {
problemUC problems.ProblemUseCase
}
func (h *problemHandlers) CreateProblem(server problemv1.ProblemService_CreateProblemServer) error {
ctx := server.Context()
func NewProblemHandlers(gserver *grpc.Server, problemUC problems.ProblemUseCase) {
handlers := &problemHandlers{problemUC: problemUC}
if err := h.problemUC.CanCreateProblem(ctx); err != nil {
return err
}
req, err := server.Recv() // receive problem
if err != nil {
return utils.TransportError(err, utils.ErrBadInput, "can't receive problem")
}
problem := req.GetProblem()
if problem == nil {
return utils.TransportError(nil, utils.ErrBadInput, "empty problem")
}
p := &models.Problem{
Name: utils.AsStringP(problem.Name),
Description: utils.AsStringP(problem.Description),
TimeLimit: utils.AsInt32P(problem.TimeLimit),
MemoryLimit: utils.AsInt32P(problem.MemoryLimit),
}
ch := readChunks(ctx, server)
err = writeChunks(ctx, ch) // temp stub
if err != nil {
return err // FIXME
}
id, err := h.problemUC.CreateProblem(ctx, p) // FIXME
if err != nil {
return err
}
err = server.SendAndClose(&problemv1.CreateProblemResponse{
Id: id,
})
if err != nil {
return utils.TransportError(err, utils.ErrBadInput, "can't send response")
}
return nil
problemv1.RegisterProblemServiceServer(gserver, handlers)
}
func writeChunks(ctx context.Context, chunks <-chan []byte) error {
// use s3
// FIXME: use ctx?
f, err := os.Create("out.txt") // FIXME: uuidv4 as initial temp name?
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 err
return nil, err
}
defer f.Close()
var off int64 = 0
for chunk := range chunks {
_, err = f.WriteAt(chunk, off)
if err != nil {
return err
}
off += int64(len(chunk))
}
// TODO: rename file to its hash
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
return &problemv1.CreateProblemResponse{Id: id}, nil
}
func (h *problemHandlers) ReadProblem(ctx context.Context, req *problemv1.ReadProblemRequest) (*problemv1.ReadProblemResponse, error) {
@ -127,39 +36,19 @@ func (h *problemHandlers) ReadProblem(ctx context.Context, req *problemv1.ReadPr
}
return &problemv1.ReadProblemResponse{
Problem: &problemv1.ReadProblemResponse_Problem{
Id: *problem.Id,
Name: *problem.Name,
Description: *problem.Description,
TimeLimit: *problem.TimeLimit,
MemoryLimit: *problem.MemoryLimit,
CreatedAt: utils.TimestampP(problem.CreatedAt),
UpdatedAt: utils.TimestampP(problem.UpdatedAt),
Id: *problem.Id,
Title: *problem.Title,
Content: *problem.Content,
TimeLimit: *problem.TimeLimit,
MemoryLimit: *problem.MemoryLimit,
TestingStrategy: *problem.TestingStrategy,
TestingOrder: *problem.TestingOrder,
CreatedAt: utils.TimestampP(problem.CreatedAt),
UpdatedAt: utils.TimestampP(problem.UpdatedAt),
},
}, nil
}
//func (h *problemHandlers) 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: utils.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 (h *problemHandlers) DeleteProblem(ctx context.Context, req *problemv1.DeleteProblemRequest) (*emptypb.Empty, error) {
err := h.problemUC.DeleteProblem(ctx, req.GetId())
if err != nil {

View file

@ -2,12 +2,11 @@ package problems
import (
"context"
"git.sch9.ru/new_gate/models"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type ProblemPostgresRepository interface {
CreateProblem(ctx context.Context, problem *models.Problem, testGroupData []models.TestGroupData) (int32, error)
CreateProblem(ctx context.Context, title string) (int32, error)
ReadProblemById(ctx context.Context, id int32) (*models.Problem, error)
UpdateProblem(ctx context.Context, problem *models.Problem) error
DeleteProblem(ctx context.Context, id int32) error
}

View file

@ -23,59 +23,19 @@ func NewProblemRepository(db *sqlx.DB, logger *zap.Logger) *ProblemRepository {
}
}
func (storage *ProblemRepository) CreateProblem(ctx context.Context, problem *models.Problem, testGroupData []models.TestGroupData) (int32, error) {
tx, err := storage.db.Beginx()
const createProblemQuery = "INSERT INTO problems (title) VALUES (?) RETURNING id"
func (r *ProblemRepository) CreateProblem(ctx context.Context, title string) (int32, error) {
query := r.db.Rebind(createProblemQuery)
rows, err := r.db.QueryxContext(ctx, query, title)
if err != nil {
return 0, handlePgErr(err)
}
query := tx.Rebind(`
INSERT INTO problems
(name,description,time_limit,memory_limit)
VALUES (?, ?, ?, ?)
RETURNING id
`)
rows, err := tx.QueryxContext(
ctx,
query,
problem.Name,
problem.Description,
problem.TimeLimit,
problem.MemoryLimit,
)
if err != nil {
return 0, handlePgErr(errors.Join(err, tx.Rollback()))
}
for _, tgd := range testGroupData {
query := tx.Rebind(`
INSERT INTO testgroups
(problem_id,testing_strategy)
VALUES ((select last_value from problems_id_seq),?)
RETURNING id
`)
rows, err = tx.QueryxContext(ctx, query, tgd.Ts)
if err != nil {
return 0, handlePgErr(errors.Join(err, tx.Rollback()))
}
var i int32 = 0
for ; i < tgd.TestAmount; i++ {
query := tx.Rebind(`
INSERT INTO tests
(testgroup_id)
VALUES ((select last_value from testgroups_id_seq))
RETURNING id
`)
rows, err = tx.QueryxContext(ctx, query, tgd.Ts)
if err != nil {
return 0, handlePgErr(errors.Join(err, tx.Rollback()))
}
}
}
err = tx.Commit()
//add test saving
defer rows.Close()
var id int32
err = rows.StructScan(&id)
rows.Next()
err = rows.Scan(&id)
if err != nil {
return 0, handlePgErr(err)
}
@ -83,28 +43,23 @@ RETURNING id
return id, nil
}
func (storage *ProblemRepository) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) {
const readProblemQuery = "SELECT * from problems WHERE id=? LIMIT 1"
func (r *ProblemRepository) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) {
var problem models.Problem
query := storage.db.Rebind("SELECT * from problems WHERE id=? LIMIT 1")
err := storage.db.GetContext(ctx, &problem, query, id)
query := r.db.Rebind(readProblemQuery)
err := r.db.GetContext(ctx, &problem, query, id)
if err != nil {
return nil, handlePgErr(err)
}
return &problem, nil
}
func (storage *ProblemRepository) UpdateProblem(ctx context.Context, problem *models.Problem) error {
query := storage.db.Rebind("UPDATE problems SET name=?,description=?,time_limit=?,memory_limit=? WHERE id=?")
_, err := storage.db.ExecContext(ctx, query, problem.Name, problem.Description, problem.TimeLimit, problem.MemoryLimit, problem.Id)
if err != nil {
return handlePgErr(err)
}
return nil
}
const deleteProblemQuery = "DELETE FROM problems WHERE id=?"
func (storage *ProblemRepository) DeleteProblem(ctx context.Context, id int32) error {
query := storage.db.Rebind("DELETE FROM problems WHERE id=?")
_, err := storage.db.ExecContext(ctx, query, id)
func (r *ProblemRepository) DeleteProblem(ctx context.Context, id int32) error {
query := r.db.Rebind(deleteProblemQuery)
_, err := r.db.ExecContext(ctx, query, id)
if err != nil {
return handlePgErr(err)
}

View file

@ -0,0 +1,58 @@
package repository
import (
"context"
"github.com/DATA-DOG/go-sqlmock"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"testing"
)
func TestProblemRepository_CreateProblem(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
problemRepo := NewProblemRepository(sqlxDB, zap.NewNop())
t.Run("valid problem creation", func(t *testing.T) {
title := "Problem title"
rows := sqlmock.NewRows([]string{"id"}).AddRow(1)
mock.ExpectQuery(sqlxDB.Rebind(createProblemQuery)).WithArgs(title).WillReturnRows(rows)
id, err := problemRepo.CreateProblem(context.Background(), title)
require.NoError(t, err)
require.Equal(t, int32(1), id)
})
}
func TestProblemRepository_DeleteProblem(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
problemRepo := NewProblemRepository(sqlxDB, zap.NewNop())
t.Run("valid problem deletion", func(t *testing.T) {
id := int32(1)
rows := sqlmock.NewResult(1, 1)
mock.ExpectExec(sqlxDB.Rebind(deleteProblemQuery)).WithArgs(id).WillReturnResult(rows)
err = problemRepo.DeleteProblem(context.Background(), id)
require.NoError(t, err)
})
}

View file

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

View file

@ -1,44 +0,0 @@
package problem.rbac
import rego.v1
spectator := 0
participant := 1
moderator := 2
admin := 3
permissions := {
"read": is_spectator,
"participate": is_participant,
"update": is_moderator,
"create": is_moderator,
"delete": is_moderator,
}
default allow := false
allow if is_admin
allow if {
permissions[input.action]
}
default is_admin := false
is_admin if {
input.user.role == admin
}
default is_moderator := false
is_moderator if {
input.user.role >= moderator
}
default is_participant := false
is_participant if {
input.user.role >= participant
}
default is_spectator := true
is_spectator if {
input.user.role >= spectator
}

View file

@ -0,0 +1,67 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/models"
"github.com/open-policy-agent/opa/rego"
)
type PermissionService struct {
query *rego.PreparedEvalQuery
}
func NewPermissionService() *PermissionService {
query, err := rego.New(
rego.Query("allow = data.problem.rbac.allow"),
rego.Load([]string{"./opa/problem.rego"}, nil),
).PrepareForEval(context.TODO())
if err != nil {
panic(err)
}
return &PermissionService{
query: &query,
}
}
func (s *PermissionService) Allowed(ctx context.Context, user *models.User, action string) bool {
input := map[string]interface{}{
"user": user,
"action": action,
}
result, err := s.query.Eval(ctx, rego.EvalInput(input))
if err != nil {
panic(err)
}
return result[0].Bindings["allow"].(bool)
}
//func (service *ProblemUseCase) CanCreateProblem(ctx context.Context) error {
// if !service.permissionService.Allowed(ctx, extractUser(ctx), "create") {
// return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
// }
// return nil
//}
//
//func (service *ProblemUseCase) CanReadProblemById(ctx context.Context) error {
// if !service.permissionService.Allowed(ctx, extractUser(ctx), "read") {
// return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
// }
// return nil
//}
//
//func (service *ProblemUseCase) CanUpdateProblem(ctx context.Context) error {
// if !service.permissionService.Allowed(ctx, extractUser(ctx), "update") {
// return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
// }
// return nil
//}
//
//func (service *ProblemUseCase) CanDeleteProblem(ctx context.Context) error {
// if !service.permissionService.Allowed(ctx, extractUser(ctx), "delete") {
// return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
// }
// return nil
//}

View file

@ -2,100 +2,34 @@ package usecase
import (
"context"
"git.sch9.ru/new_gate/models"
"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/pkg/utils"
)
type ProblemStorage interface {
CreateProblem(ctx context.Context, problem *models.Problem, testGroupData []models.TestGroupData) (int32, error)
ReadProblemById(ctx context.Context, id int32) (*models.Problem, error)
UpdateProblem(ctx context.Context, problem *models.Problem) error
DeleteProblem(ctx context.Context, id int32) error
}
type IPermissionService interface {
Allowed(ctx context.Context, user *models.User, action string) bool
}
type ProblemUseCase struct {
problemStorage ProblemStorage
pandocClient pandoc.PandocClient
permissionService IPermissionService
problemRepo problems.ProblemPostgresRepository
pandocClient pandoc.PandocClient
}
func NewProblemUseCase(
problemStorage ProblemStorage,
problemRepo problems.ProblemPostgresRepository,
pandocClient pandoc.PandocClient,
permissionService IPermissionService,
) *ProblemUseCase {
return &ProblemUseCase{
problemStorage: problemStorage,
pandocClient: pandocClient,
permissionService: permissionService,
problemRepo: problemRepo,
pandocClient: pandocClient,
}
}
func extractUser(ctx context.Context) *models.User {
return ctx.Value("user").(*models.User)
func (u *ProblemUseCase) CreateProblem(ctx context.Context, title string) (int32, error) {
return u.problemRepo.CreateProblem(ctx, title)
}
func (service *ProblemUseCase) CanCreateProblem(ctx context.Context) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "create") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return nil
func (u *ProblemUseCase) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) {
return u.problemRepo.ReadProblemById(ctx, id)
}
func (service *ProblemUseCase) CanReadProblemById(ctx context.Context) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "read") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return nil
}
func (service *ProblemUseCase) CanUpdateProblem(ctx context.Context) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "update") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return nil
}
func (service *ProblemUseCase) CanDeleteProblem(ctx context.Context) error {
if !service.permissionService.Allowed(ctx, extractUser(ctx), "delete") {
return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
}
return nil
}
func (service *ProblemUseCase) CreateProblem(ctx context.Context, problem *models.Problem) (int32, error) {
if err := service.CanCreateProblem(ctx); err != nil {
return 0, err
}
_, err := service.pandocClient.ConvertLatexToHtml5(ctx, *problem.Description)
if err != nil {
return 0, err
}
return service.problemStorage.CreateProblem(ctx, problem, nil)
}
func (service *ProblemUseCase) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) {
if err := service.CanReadProblemById(ctx); err != nil {
return nil, err
}
return service.problemStorage.ReadProblemById(ctx, id)
}
func (service *ProblemUseCase) UpdateProblem(ctx context.Context, problem *models.Problem) error {
if err := service.CanUpdateProblem(ctx); err != nil {
return err
}
return service.problemStorage.UpdateProblem(ctx, problem)
}
func (service *ProblemUseCase) DeleteProblem(ctx context.Context, id int32) error {
if err := service.CanDeleteProblem(ctx); err != nil {
return err
}
return service.problemStorage.DeleteProblem(ctx, id)
func (u *ProblemUseCase) DeleteProblem(ctx context.Context, id int32) error {
return u.problemRepo.DeleteProblem(ctx, id)
}

View file

@ -13,7 +13,7 @@ type PermissionService struct {
func NewPermissionService() *PermissionService {
query, err := rego.New(
rego.Query("allow = data.problem.rbac.allow"),
rego.Load([]string{"./opa/all.rego"}, nil),
rego.Load([]string{"./opa/problem.rego"}, nil),
).PrepareForEval(context.TODO())
if err != nil {