Merge branch 'main' of https://gitlab.crja72.ru/9-go/datarush-go
This commit is contained in:
+2
-2
@@ -30,8 +30,8 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
CGO_ENABLED=${CGO_ENABLED} \
|
||||
GOOS=${GOOS} \
|
||||
GOARCH=${GOARCH} \
|
||||
go build -trimpath \
|
||||
-ldflags "-s -w -X main.BuildTime=${BUILD_TIME} -X main.Version=${VERSION}" \
|
||||
go build -a -installsuffix cgo -trimpath \
|
||||
-ldflags '-extldflags "-static" -s -w -X main.BuildTime=${BUILD_TIME} -X main.Version=${VERSION}' \
|
||||
-o /out/${SERVICE} ./cmd/${SERVICE} && \
|
||||
chmod +x /out/${SERVICE}
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ func Load() (*Config, error) {
|
||||
_ = godotenv.Load()
|
||||
|
||||
return &Config{
|
||||
DBHost: getEnv("POSTGRES_HOST", "localhost"),
|
||||
DBPort: mustGetInt("POSTGRES_PORT", 5432), //nolint:mnd // false-positive
|
||||
DBUser: getEnv("POSTGRES_USERNAME", "postgres"),
|
||||
DBPassword: getEnv("POSTGRES_PASSWORD", "postgres"),
|
||||
DBName: getEnv("POSTGRES_DATABASE", "postgres"),
|
||||
DBHost: getEnv("POSTGRES_HOST", "localhost"),
|
||||
DBPort: mustGetInt("POSTGRES_PORT", 5432), //nolint:mnd // false-positive
|
||||
DBUser: getEnv("POSTGRES_USERNAME", "postgres"),
|
||||
DBPassword: getEnv("POSTGRES_PASSWORD", "postgres"),
|
||||
DBName: getEnv("POSTGRES_DATABASE", "postgres"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,18 @@ type Result struct {
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func NewResult(id uuid.UUID, competitionID uuid.UUID, userID uuid.UUID, score float64, rank *int, status ResultStatus, submissionID *uuid.UUID, metadata map[string]interface{}, createdAt time.Time, updatedAt time.Time) *Result {
|
||||
func NewResult(
|
||||
id uuid.UUID,
|
||||
competitionID uuid.UUID,
|
||||
userID uuid.UUID,
|
||||
score float64,
|
||||
rank *int,
|
||||
status ResultStatus,
|
||||
submissionID *uuid.UUID,
|
||||
metadata map[string]interface{},
|
||||
createdAt time.Time,
|
||||
updatedAt time.Time,
|
||||
) *Result {
|
||||
return &Result{
|
||||
ID: id,
|
||||
CompetitionID: competitionID,
|
||||
|
||||
@@ -24,9 +24,23 @@ type IResultService interface {
|
||||
GetResult(ctx context.Context, id uuid.UUID) (*domain.Result, error)
|
||||
UpdateResult(ctx context.Context, result *domain.Result) (*domain.Result, error)
|
||||
DeleteResult(ctx context.Context, id uuid.UUID) error
|
||||
ListResults(ctx context.Context, pageSize int32, pageToken int32, competitionID *uuid.UUID, userID *uuid.UUID, status *domain.ResultStatus, minScore *float64, maxScore *float64) (results []domain.Result, totalCount int32, nextPageToken int32, err error)
|
||||
ListResults(
|
||||
ctx context.Context,
|
||||
pageSize int32,
|
||||
pageToken int32,
|
||||
competitionID *uuid.UUID,
|
||||
userID *uuid.UUID,
|
||||
status *domain.ResultStatus,
|
||||
minScore *float64,
|
||||
maxScore *float64,
|
||||
) (results []domain.Result, totalCount int32, nextPageToken int32, err error)
|
||||
GetByCompetitionAndUser(ctx context.Context, competitionID uuid.UUID, userID uuid.UUID) (*domain.Result, error)
|
||||
GetLeaderboard(ctx context.Context, competitionID uuid.UUID, limit int32, offset int32) ([]domain.Result, int32, error)
|
||||
GetLeaderboard(
|
||||
ctx context.Context,
|
||||
competitionID uuid.UUID,
|
||||
limit int32,
|
||||
offset int32,
|
||||
) ([]domain.Result, int32, error)
|
||||
UpdateStatus(ctx context.Context, id uuid.UUID, status domain.ResultStatus) (*domain.Result, error)
|
||||
RecalculateRanks(ctx context.Context, competitionID uuid.UUID) error
|
||||
}
|
||||
@@ -43,7 +57,10 @@ type ResultsHandler struct {
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewResultsHandler(s IResultService, userServiceAddr, submissionServiceAddr, taskServiceAddr string) *ResultsHandler {
|
||||
func NewResultsHandler(
|
||||
s IResultService,
|
||||
userServiceAddr, submissionServiceAddr, taskServiceAddr string,
|
||||
) *ResultsHandler {
|
||||
return &ResultsHandler{
|
||||
service: s,
|
||||
userServiceAddr: userServiceAddr,
|
||||
@@ -210,7 +227,11 @@ func (h *ResultsHandler) fetchTaskStatuses(ctx context.Context, competitionID, u
|
||||
return taskStatuses
|
||||
}
|
||||
|
||||
func (h *ResultsHandler) enrichUserResult(ctx context.Context, result *domain.Result, competitionID string) *pb.UserResult {
|
||||
func (h *ResultsHandler) enrichUserResult(
|
||||
ctx context.Context,
|
||||
result *domain.Result,
|
||||
competitionID string,
|
||||
) *pb.UserResult {
|
||||
userID := result.UserID.String()
|
||||
username := h.fetchUsername(ctx, userID)
|
||||
taskStatuses := h.fetchTaskStatuses(ctx, competitionID, userID)
|
||||
@@ -230,7 +251,10 @@ func (h *ResultsHandler) enrichUserResult(ctx context.Context, result *domain.Re
|
||||
return pbResult
|
||||
}
|
||||
|
||||
func (h *ResultsHandler) GetCompetitionResults(ctx context.Context, req *pb.GetCompetitionResultsRequest) (*pb.GetCompetitionResultsResponse, error) {
|
||||
func (h *ResultsHandler) GetCompetitionResults(
|
||||
ctx context.Context,
|
||||
req *pb.GetCompetitionResultsRequest,
|
||||
) (*pb.GetCompetitionResultsResponse, error) {
|
||||
competitionID, err := uuid.Parse(req.GetCompetitionId())
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid competition id format: %v", err)
|
||||
@@ -267,7 +291,10 @@ func (h *ResultsHandler) GetCompetitionResults(ctx context.Context, req *pb.GetC
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *ResultsHandler) GetUserCompetitionResults(ctx context.Context, req *pb.GetUserCompetitionResultsRequest) (*pb.GetUserCompetitionResultsResponse, error) {
|
||||
func (h *ResultsHandler) GetUserCompetitionResults(
|
||||
ctx context.Context,
|
||||
req *pb.GetUserCompetitionResultsRequest,
|
||||
) (*pb.GetUserCompetitionResultsResponse, error) {
|
||||
competitionID, err := uuid.Parse(req.GetCompetitionId())
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid competition id format: %v", err)
|
||||
@@ -294,7 +321,10 @@ func (h *ResultsHandler) GetUserCompetitionResults(ctx context.Context, req *pb.
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *ResultsHandler) RecalculateResults(ctx context.Context, req *pb.RecalculateResultsRequest) (*emptypb.Empty, error) {
|
||||
func (h *ResultsHandler) RecalculateResults(
|
||||
ctx context.Context,
|
||||
req *pb.RecalculateResultsRequest,
|
||||
) (*emptypb.Empty, error) {
|
||||
competitionID, err := uuid.Parse(req.GetCompetitionId())
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid competition id format: %v", err)
|
||||
|
||||
@@ -53,28 +53,50 @@ func (m *MockResultService) DeleteResult(ctx context.Context, id uuid.UUID) erro
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (m *MockResultService) ListResults(ctx context.Context, pageSize int32, pageToken int32, competitionID *uuid.UUID, userID *uuid.UUID, status *domain.ResultStatus, minScore *float64, maxScore *float64) (results []domain.Result, totalCount int32, nextPageToken int32, err error) {
|
||||
func (m *MockResultService) ListResults(
|
||||
ctx context.Context,
|
||||
pageSize int32,
|
||||
pageToken int32,
|
||||
competitionID *uuid.UUID,
|
||||
userID *uuid.UUID,
|
||||
status *domain.ResultStatus,
|
||||
minScore *float64,
|
||||
maxScore *float64,
|
||||
) (results []domain.Result, totalCount int32, nextPageToken int32, err error) {
|
||||
if m.ListResultsFunc != nil {
|
||||
return m.ListResultsFunc(ctx, pageSize, pageToken, competitionID, userID, status, minScore, maxScore)
|
||||
}
|
||||
return nil, 0, 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (m *MockResultService) GetByCompetitionAndUser(ctx context.Context, competitionID uuid.UUID, userID uuid.UUID) (*domain.Result, error) {
|
||||
func (m *MockResultService) GetByCompetitionAndUser(
|
||||
ctx context.Context,
|
||||
competitionID uuid.UUID,
|
||||
userID uuid.UUID,
|
||||
) (*domain.Result, error) {
|
||||
if m.GetByCompetitionAndUserFunc != nil {
|
||||
return m.GetByCompetitionAndUserFunc(ctx, competitionID, userID)
|
||||
}
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (m *MockResultService) GetLeaderboard(ctx context.Context, competitionID uuid.UUID, limit int32, offset int32) ([]domain.Result, int32, error) {
|
||||
func (m *MockResultService) GetLeaderboard(
|
||||
ctx context.Context,
|
||||
competitionID uuid.UUID,
|
||||
limit int32,
|
||||
offset int32,
|
||||
) ([]domain.Result, int32, error) {
|
||||
if m.GetLeaderboardFunc != nil {
|
||||
return m.GetLeaderboardFunc(ctx, competitionID, limit, offset)
|
||||
}
|
||||
return nil, 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (m *MockResultService) UpdateStatus(ctx context.Context, id uuid.UUID, status domain.ResultStatus) (*domain.Result, error) {
|
||||
func (m *MockResultService) UpdateStatus(
|
||||
ctx context.Context,
|
||||
id uuid.UUID,
|
||||
status domain.ResultStatus,
|
||||
) (*domain.Result, error) {
|
||||
if m.UpdateStatusFunc != nil {
|
||||
return m.UpdateStatusFunc(ctx, id, status)
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@ package postgres
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"datarush/internal/results/domain"
|
||||
"datarush/internal/results/repository"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"datarush/internal/results/domain"
|
||||
"datarush/internal/results/repository"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/lib/pq"
|
||||
"github.com/redis/go-redis/v9"
|
||||
@@ -257,8 +258,13 @@ func (r *ResultRepository) List(ctx context.Context, opts repository.ListResults
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`SELECT id, competition_id, user_id, score, rank, status, submission_id, metadata, created_at, updated_at
|
||||
FROM results %s ORDER BY score DESC, created_at ASC LIMIT $%d OFFSET $%d`, where, argId, argId+1)
|
||||
query := fmt.Sprintf(
|
||||
`SELECT id, competition_id, user_id, score, rank, status, submission_id, metadata, created_at, updated_at
|
||||
FROM results %s ORDER BY score DESC, created_at ASC LIMIT $%d OFFSET $%d`,
|
||||
where,
|
||||
argId,
|
||||
argId+1,
|
||||
)
|
||||
args = append(args, opts.PageSize, opts.Page*opts.PageSize)
|
||||
|
||||
rows, err := r.db.QueryContext(ctx, query, args...)
|
||||
@@ -299,7 +305,11 @@ func (r *ResultRepository) List(ctx context.Context, opts repository.ListResults
|
||||
return results, total, nil
|
||||
}
|
||||
|
||||
func (r *ResultRepository) GetByCompetitionAndUser(ctx context.Context, competitionID uuid.UUID, userID uuid.UUID) (*domain.Result, error) {
|
||||
func (r *ResultRepository) GetByCompetitionAndUser(
|
||||
ctx context.Context,
|
||||
competitionID uuid.UUID,
|
||||
userID uuid.UUID,
|
||||
) (*domain.Result, error) {
|
||||
query := `SELECT id, competition_id, user_id, score, rank, status, submission_id, metadata, created_at, updated_at
|
||||
FROM results WHERE competition_id = $1 AND user_id = $2`
|
||||
row := r.db.QueryRowContext(ctx, query, competitionID, userID)
|
||||
@@ -334,7 +344,12 @@ func (r *ResultRepository) GetByCompetitionAndUser(ctx context.Context, competit
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (r *ResultRepository) GetLeaderboard(ctx context.Context, competitionID uuid.UUID, limit int, offset int) ([]domain.Result, int, error) {
|
||||
func (r *ResultRepository) GetLeaderboard(
|
||||
ctx context.Context,
|
||||
competitionID uuid.UUID,
|
||||
limit int,
|
||||
offset int,
|
||||
) ([]domain.Result, int, error) {
|
||||
if r.cacheEnabled {
|
||||
key := r.leaderboardCacheKey(competitionID, limit, offset)
|
||||
val, err := r.redisClient.Get(ctx, key).Result()
|
||||
@@ -404,7 +419,11 @@ func (r *ResultRepository) GetLeaderboard(ctx context.Context, competitionID uui
|
||||
return results, total, nil
|
||||
}
|
||||
|
||||
func (r *ResultRepository) UpdateStatus(ctx context.Context, id uuid.UUID, status domain.ResultStatus) (*domain.Result, error) {
|
||||
func (r *ResultRepository) UpdateStatus(
|
||||
ctx context.Context,
|
||||
id uuid.UUID,
|
||||
status domain.ResultStatus,
|
||||
) (*domain.Result, error) {
|
||||
query := `UPDATE results SET status = $1, updated_at = $2 WHERE id = $3`
|
||||
now := time.Now()
|
||||
res, err := r.db.ExecContext(ctx, query, status, now, id)
|
||||
|
||||
@@ -2,6 +2,7 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"datarush/internal/results/domain"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
@@ -52,7 +52,16 @@ func (s *Service) DeleteResult(ctx context.Context, id uuid.UUID) error {
|
||||
return s.repo.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) ListResults(ctx context.Context, pageSize int32, pageToken int32, competitionID *uuid.UUID, userID *uuid.UUID, status *domain.ResultStatus, minScore *float64, maxScore *float64) ([]domain.Result, int32, int32, error) {
|
||||
func (s *Service) ListResults(
|
||||
ctx context.Context,
|
||||
pageSize int32,
|
||||
pageToken int32,
|
||||
competitionID *uuid.UUID,
|
||||
userID *uuid.UUID,
|
||||
status *domain.ResultStatus,
|
||||
minScore *float64,
|
||||
maxScore *float64,
|
||||
) ([]domain.Result, int32, int32, error) {
|
||||
opts := repository.ListResultsOptions{
|
||||
Page: int(pageToken),
|
||||
PageSize: int(pageSize),
|
||||
@@ -76,11 +85,20 @@ func (s *Service) ListResults(ctx context.Context, pageSize int32, pageToken int
|
||||
return results, int32(total), nextPageToken, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetByCompetitionAndUser(ctx context.Context, competitionID uuid.UUID, userID uuid.UUID) (*domain.Result, error) {
|
||||
func (s *Service) GetByCompetitionAndUser(
|
||||
ctx context.Context,
|
||||
competitionID uuid.UUID,
|
||||
userID uuid.UUID,
|
||||
) (*domain.Result, error) {
|
||||
return s.repo.GetByCompetitionAndUser(ctx, competitionID, userID)
|
||||
}
|
||||
|
||||
func (s *Service) GetLeaderboard(ctx context.Context, competitionID uuid.UUID, limit int32, offset int32) ([]domain.Result, int32, error) {
|
||||
func (s *Service) GetLeaderboard(
|
||||
ctx context.Context,
|
||||
competitionID uuid.UUID,
|
||||
limit int32,
|
||||
offset int32,
|
||||
) ([]domain.Result, int32, error) {
|
||||
results, total, err := s.repo.GetLeaderboard(ctx, competitionID, int(limit), int(offset))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
|
||||
@@ -59,21 +59,34 @@ func (m *MockRepository) List(ctx context.Context, opts repository.ListResultsOp
|
||||
return nil, 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (m *MockRepository) GetByCompetitionAndUser(ctx context.Context, competitionID uuid.UUID, userID uuid.UUID) (*domain.Result, error) {
|
||||
func (m *MockRepository) GetByCompetitionAndUser(
|
||||
ctx context.Context,
|
||||
competitionID uuid.UUID,
|
||||
userID uuid.UUID,
|
||||
) (*domain.Result, error) {
|
||||
if m.GetByCompetitionAndUserFunc != nil {
|
||||
return m.GetByCompetitionAndUserFunc(ctx, competitionID, userID)
|
||||
}
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (m *MockRepository) GetLeaderboard(ctx context.Context, competitionID uuid.UUID, limit int, offset int) ([]domain.Result, int, error) {
|
||||
func (m *MockRepository) GetLeaderboard(
|
||||
ctx context.Context,
|
||||
competitionID uuid.UUID,
|
||||
limit int,
|
||||
offset int,
|
||||
) ([]domain.Result, int, error) {
|
||||
if m.GetLeaderboardFunc != nil {
|
||||
return m.GetLeaderboardFunc(ctx, competitionID, limit, offset)
|
||||
}
|
||||
return nil, 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (m *MockRepository) UpdateStatus(ctx context.Context, id uuid.UUID, status domain.ResultStatus) (*domain.Result, error) {
|
||||
func (m *MockRepository) UpdateStatus(
|
||||
ctx context.Context,
|
||||
id uuid.UUID,
|
||||
status domain.ResultStatus,
|
||||
) (*domain.Result, error) {
|
||||
if m.UpdateStatusFunc != nil {
|
||||
return m.UpdateStatusFunc(ctx, id, status)
|
||||
}
|
||||
@@ -423,20 +436,20 @@ func TestListResults(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pageSize int32
|
||||
pageToken int32
|
||||
competitionID *uuid.UUID
|
||||
userID *uuid.UUID
|
||||
status *domain.ResultStatus
|
||||
minScore *float64
|
||||
maxScore *float64
|
||||
repoResults []domain.Result
|
||||
repoTotal int
|
||||
repoError error
|
||||
expectError bool
|
||||
expectedTotal int32
|
||||
expectedNext int32
|
||||
name string
|
||||
pageSize int32
|
||||
pageToken int32
|
||||
competitionID *uuid.UUID
|
||||
userID *uuid.UUID
|
||||
status *domain.ResultStatus
|
||||
minScore *float64
|
||||
maxScore *float64
|
||||
repoResults []domain.Result
|
||||
repoTotal int
|
||||
repoError error
|
||||
expectError bool
|
||||
expectedTotal int32
|
||||
expectedNext int32
|
||||
}{
|
||||
{
|
||||
name: "successful list with filters",
|
||||
|
||||
@@ -3,8 +3,9 @@ package grpc
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "datarush/pkg/api/task"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
pb "datarush/pkg/api/task"
|
||||
)
|
||||
|
||||
type TaskService interface {
|
||||
@@ -12,7 +13,10 @@ type TaskService interface {
|
||||
GetTask(ctx context.Context, req *pb.GetTaskRequest) (*pb.Task, error)
|
||||
EditTask(ctx context.Context, req *pb.Task) (*pb.Task, error)
|
||||
DeleteTask(ctx context.Context, req *pb.DeleteTaskRequest) (*emptypb.Empty, error)
|
||||
ListCompetitionTasks(ctx context.Context, req *pb.ListCompetitionTasksRequest) (*pb.ListCompetitionTasksResponse, error)
|
||||
ListCompetitionTasks(
|
||||
ctx context.Context,
|
||||
req *pb.ListCompetitionTasksRequest,
|
||||
) (*pb.ListCompetitionTasksResponse, error)
|
||||
GetTaskAttachments(ctx context.Context, req *pb.GetTaskAttachmentsRequest) (*pb.GetTaskAttachmentsResponse, error)
|
||||
}
|
||||
|
||||
@@ -41,10 +45,16 @@ func (h *TaskHandler) DeleteTask(ctx context.Context, req *pb.DeleteTaskRequest)
|
||||
return h.service.DeleteTask(ctx, req)
|
||||
}
|
||||
|
||||
func (h *TaskHandler) ListCompetitionTasks(ctx context.Context, req *pb.ListCompetitionTasksRequest) (*pb.ListCompetitionTasksResponse, error) {
|
||||
func (h *TaskHandler) ListCompetitionTasks(
|
||||
ctx context.Context,
|
||||
req *pb.ListCompetitionTasksRequest,
|
||||
) (*pb.ListCompetitionTasksResponse, error) {
|
||||
return h.service.ListCompetitionTasks(ctx, req)
|
||||
}
|
||||
|
||||
func (h *TaskHandler) GetTaskAttachments(ctx context.Context, req *pb.GetTaskAttachmentsRequest) (*pb.GetTaskAttachmentsResponse, error) {
|
||||
func (h *TaskHandler) GetTaskAttachments(
|
||||
ctx context.Context,
|
||||
req *pb.GetTaskAttachmentsRequest,
|
||||
) (*pb.GetTaskAttachmentsResponse, error) {
|
||||
return h.service.GetTaskAttachments(ctx, req)
|
||||
}
|
||||
@@ -21,7 +21,8 @@ func (r *TaskRepository) CreateTask(ctx context.Context, t *task.Task) (*task.Ta
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at, updated_at`
|
||||
|
||||
var createdTask task.Task
|
||||
err := r.db.QueryRowxContext(ctx, query, t.CompetitionId, t.Title, t.Description, t.InCompetitionPosition, t.MaxPoints, t.MaxAttempts, t.Type).StructScan(&createdTask)
|
||||
err := r.db.QueryRowxContext(ctx, query, t.CompetitionId, t.Title, t.Description, t.InCompetitionPosition, t.MaxPoints, t.MaxAttempts, t.Type).
|
||||
StructScan(&createdTask)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -46,7 +47,8 @@ func (r *TaskRepository) EditTask(ctx context.Context, t *task.Task) (*task.Task
|
||||
WHERE id = $7 RETURNING updated_at`
|
||||
|
||||
var updatedTask task.Task
|
||||
err := r.db.QueryRowxContext(ctx, query, t.Title, t.Description, t.InCompetitionPosition, t.MaxPoints, t.MaxAttempts, t.Type, t.Id).StructScan(&updatedTask)
|
||||
err := r.db.QueryRowxContext(ctx, query, t.Title, t.Description, t.InCompetitionPosition, t.MaxPoints, t.MaxAttempts, t.Type, t.Id).
|
||||
StructScan(&updatedTask)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,7 +67,11 @@ func (r *TaskRepository) ListCompetitionTasks(ctx context.Context, competitionID
|
||||
return tasks, err
|
||||
}
|
||||
|
||||
func (r *TaskRepository) GetTaskAttachments(ctx context.Context, taskID string, showPrivate bool) ([]*task.TaskAttachment, error) {
|
||||
func (r *TaskRepository) GetTaskAttachments(
|
||||
ctx context.Context,
|
||||
taskID string,
|
||||
showPrivate bool,
|
||||
) ([]*task.TaskAttachment, error) {
|
||||
var attachments []*task.TaskAttachment
|
||||
query := "SELECT * FROM task_attachments WHERE task_id = $1"
|
||||
args := []interface{}{taskID}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "datarush/pkg/api/task"
|
||||
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
@@ -41,7 +42,10 @@ func (s *TaskService) DeleteTask(ctx context.Context, req *pb.DeleteTaskRequest)
|
||||
return &emptypb.Empty{}, err
|
||||
}
|
||||
|
||||
func (s *TaskService) ListCompetitionTasks(ctx context.Context, req *pb.ListCompetitionTasksRequest) (*pb.ListCompetitionTasksResponse, error) {
|
||||
func (s *TaskService) ListCompetitionTasks(
|
||||
ctx context.Context,
|
||||
req *pb.ListCompetitionTasksRequest,
|
||||
) (*pb.ListCompetitionTasksResponse, error) {
|
||||
tasks, err := s.repo.ListCompetitionTasks(ctx, req.CompetitionId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -49,7 +53,10 @@ func (s *TaskService) ListCompetitionTasks(ctx context.Context, req *pb.ListComp
|
||||
return &pb.ListCompetitionTasksResponse{Tasks: tasks}, nil
|
||||
}
|
||||
|
||||
func (s *TaskService) GetTaskAttachments(ctx context.Context, req *pb.GetTaskAttachmentsRequest) (*pb.GetTaskAttachmentsResponse, error) {
|
||||
func (s *TaskService) GetTaskAttachments(
|
||||
ctx context.Context,
|
||||
req *pb.GetTaskAttachmentsRequest,
|
||||
) (*pb.GetTaskAttachmentsResponse, error) {
|
||||
showPrivate := false
|
||||
if req.ShowPrivate != nil {
|
||||
showPrivate = *req.ShowPrivate
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"datarush/internal/task/service/mocks"
|
||||
pb "datarush/pkg/api/task"
|
||||
|
||||
"go.uber.org/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
@@ -24,13 +24,13 @@ func TestTaskService(t *testing.T) {
|
||||
|
||||
t.Run("CreateTask", func(t *testing.T) {
|
||||
task := &pb.Task{
|
||||
CompetitionId: "comp1",
|
||||
Title: "Test Task",
|
||||
Description: "This is a test task",
|
||||
CompetitionId: "comp1",
|
||||
Title: "Test Task",
|
||||
Description: "This is a test task",
|
||||
InCompetitionPosition: 1,
|
||||
MaxPoints: 100,
|
||||
MaxAttempts: 10,
|
||||
Type: pb.TaskType_TASK_TYPE_INPUT,
|
||||
MaxPoints: 100,
|
||||
MaxAttempts: 10,
|
||||
Type: pb.TaskType_TASK_TYPE_INPUT,
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().CreateTask(ctx, task).Return(task, nil)
|
||||
@@ -44,16 +44,16 @@ func TestTaskService(t *testing.T) {
|
||||
taskID := "task1"
|
||||
req := &pb.GetTaskRequest{TaskId: taskID}
|
||||
expectedTask := &pb.Task{
|
||||
Id: taskID,
|
||||
CompetitionId: "comp1",
|
||||
Title: "Test Task",
|
||||
Description: "This is a test task",
|
||||
Id: taskID,
|
||||
CompetitionId: "comp1",
|
||||
Title: "Test Task",
|
||||
Description: "This is a test task",
|
||||
InCompetitionPosition: 1,
|
||||
MaxPoints: 100,
|
||||
MaxAttempts: 10,
|
||||
Type: pb.TaskType_TASK_TYPE_INPUT,
|
||||
CreatedAt: timestamppb.Now(),
|
||||
UpdatedAt: timestamppb.Now(),
|
||||
MaxPoints: 100,
|
||||
MaxAttempts: 10,
|
||||
Type: pb.TaskType_TASK_TYPE_INPUT,
|
||||
CreatedAt: timestamppb.Now(),
|
||||
UpdatedAt: timestamppb.Now(),
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().GetTask(ctx, taskID).Return(expectedTask, nil)
|
||||
@@ -65,14 +65,14 @@ func TestTaskService(t *testing.T) {
|
||||
|
||||
t.Run("EditTask", func(t *testing.T) {
|
||||
task := &pb.Task{
|
||||
Id: "task1",
|
||||
CompetitionId: "comp1",
|
||||
Title: "Updated Test Task",
|
||||
Description: "This is an updated test task",
|
||||
Id: "task1",
|
||||
CompetitionId: "comp1",
|
||||
Title: "Updated Test Task",
|
||||
Description: "This is an updated test task",
|
||||
InCompetitionPosition: 1,
|
||||
MaxPoints: 150,
|
||||
MaxAttempts: 5,
|
||||
Type: pb.TaskType_TASK_TYPE_CHECKER,
|
||||
MaxPoints: 150,
|
||||
MaxAttempts: 5,
|
||||
Type: pb.TaskType_TASK_TYPE_CHECKER,
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().EditTask(ctx, task).Return(task, nil)
|
||||
|
||||
@@ -3,15 +3,19 @@ package grpc
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "datarush/pkg/api/user"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
pb "datarush/pkg/api/user"
|
||||
)
|
||||
|
||||
type UserService interface {
|
||||
GetProfile(ctx context.Context, req *pb.GetProfileRequest) (*pb.User, error)
|
||||
RegisterForCompetition(ctx context.Context, req *pb.RegisterForCompetitionRequest) (*emptypb.Empty, error)
|
||||
UnregisterFromCompetition(ctx context.Context, req *pb.UnregisterFromCompetitionRequest) (*emptypb.Empty, error)
|
||||
ListUserCompetitions(ctx context.Context, req *pb.ListUserCompetitionsRequest) (*pb.ListUserCompetitionsResponse, error)
|
||||
ListUserCompetitions(
|
||||
ctx context.Context,
|
||||
req *pb.ListUserCompetitionsRequest,
|
||||
) (*pb.ListUserCompetitionsResponse, error)
|
||||
}
|
||||
|
||||
type UserHandler struct {
|
||||
@@ -27,14 +31,23 @@ func (h *UserHandler) GetProfile(ctx context.Context, req *pb.GetProfileRequest)
|
||||
return h.service.GetProfile(ctx, req)
|
||||
}
|
||||
|
||||
func (h *UserHandler) RegisterForCompetition(ctx context.Context, req *pb.RegisterForCompetitionRequest) (*emptypb.Empty, error) {
|
||||
func (h *UserHandler) RegisterForCompetition(
|
||||
ctx context.Context,
|
||||
req *pb.RegisterForCompetitionRequest,
|
||||
) (*emptypb.Empty, error) {
|
||||
return h.service.RegisterForCompetition(ctx, req)
|
||||
}
|
||||
|
||||
func (h *UserHandler) UnregisterFromCompetition(ctx context.Context, req *pb.UnregisterFromCompetitionRequest) (*emptypb.Empty, error) {
|
||||
func (h *UserHandler) UnregisterFromCompetition(
|
||||
ctx context.Context,
|
||||
req *pb.UnregisterFromCompetitionRequest,
|
||||
) (*emptypb.Empty, error) {
|
||||
return h.service.UnregisterFromCompetition(ctx, req)
|
||||
}
|
||||
|
||||
func (h *UserHandler) ListUserCompetitions(ctx context.Context, req *pb.ListUserCompetitionsRequest) (*pb.ListUserCompetitionsResponse, error) {
|
||||
func (h *UserHandler) ListUserCompetitions(
|
||||
ctx context.Context,
|
||||
req *pb.ListUserCompetitionsRequest,
|
||||
) (*pb.ListUserCompetitionsResponse, error) {
|
||||
return h.service.ListUserCompetitions(ctx, req)
|
||||
}
|
||||
@@ -3,8 +3,9 @@ package postgres
|
||||
import (
|
||||
"context"
|
||||
|
||||
"datarush/pkg/api/user"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"datarush/pkg/api/user"
|
||||
)
|
||||
|
||||
type UserRepository struct {
|
||||
@@ -17,22 +18,42 @@ func NewUserRepository(db *sqlx.DB) *UserRepository {
|
||||
|
||||
func (r *UserRepository) GetProfile(ctx context.Context, userID string) (*user.User, error) {
|
||||
var u user.User
|
||||
err := r.db.GetContext(ctx, &u, "SELECT id, username, email, full_name, avatar_url FROM users WHERE id = $1", userID)
|
||||
err := r.db.GetContext(
|
||||
ctx,
|
||||
&u,
|
||||
"SELECT id, username, email, full_name, avatar_url FROM users WHERE id = $1",
|
||||
userID,
|
||||
)
|
||||
return &u, err
|
||||
}
|
||||
|
||||
func (r *UserRepository) RegisterForCompetition(ctx context.Context, userID, competitionID string) error {
|
||||
_, err := r.db.ExecContext(ctx, "INSERT INTO user_competitions (user_id, competition_id) VALUES ($1, $2)", userID, competitionID)
|
||||
_, err := r.db.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO user_competitions (user_id, competition_id) VALUES ($1, $2)",
|
||||
userID,
|
||||
competitionID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *UserRepository) UnregisterFromCompetition(ctx context.Context, userID, competitionID string) error {
|
||||
_, err := r.db.ExecContext(ctx, "DELETE FROM user_competitions WHERE user_id = $1 AND competition_id = $2", userID, competitionID)
|
||||
_, err := r.db.ExecContext(
|
||||
ctx,
|
||||
"DELETE FROM user_competitions WHERE user_id = $1 AND competition_id = $2",
|
||||
userID,
|
||||
competitionID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *UserRepository) ListUserCompetitions(ctx context.Context, userID string) ([]string, error) {
|
||||
var competitionIDs []string
|
||||
err := r.db.SelectContext(ctx, &competitionIDs, "SELECT competition_id FROM user_competitions WHERE user_id = $1", userID)
|
||||
err := r.db.SelectContext(
|
||||
ctx,
|
||||
&competitionIDs,
|
||||
"SELECT competition_id FROM user_competitions WHERE user_id = $1",
|
||||
userID,
|
||||
)
|
||||
return competitionIDs, err
|
||||
}
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
"datarush/internal/user/middleware"
|
||||
pb "datarush/pkg/api/user"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
type UserRepository interface {
|
||||
@@ -28,7 +29,10 @@ func (s *UserService) GetProfile(ctx context.Context, req *pb.GetProfileRequest)
|
||||
return s.repo.GetProfile(ctx, req.UserId)
|
||||
}
|
||||
|
||||
func (s *UserService) RegisterForCompetition(ctx context.Context, req *pb.RegisterForCompetitionRequest) (*emptypb.Empty, error) {
|
||||
func (s *UserService) RegisterForCompetition(
|
||||
ctx context.Context,
|
||||
req *pb.RegisterForCompetitionRequest,
|
||||
) (*emptypb.Empty, error) {
|
||||
userID, ok := ctx.Value(middleware.UserIDKey{}).(string)
|
||||
if !ok {
|
||||
return nil, errors.New("user ID not found in context")
|
||||
@@ -38,7 +42,10 @@ func (s *UserService) RegisterForCompetition(ctx context.Context, req *pb.Regist
|
||||
return &emptypb.Empty{}, err
|
||||
}
|
||||
|
||||
func (s *UserService) UnregisterFromCompetition(ctx context.Context, req *pb.UnregisterFromCompetitionRequest) (*emptypb.Empty, error) {
|
||||
func (s *UserService) UnregisterFromCompetition(
|
||||
ctx context.Context,
|
||||
req *pb.UnregisterFromCompetitionRequest,
|
||||
) (*emptypb.Empty, error) {
|
||||
userID, ok := ctx.Value(middleware.UserIDKey{}).(string)
|
||||
if !ok {
|
||||
return nil, errors.New("user ID not found in context")
|
||||
@@ -48,7 +55,10 @@ func (s *UserService) UnregisterFromCompetition(ctx context.Context, req *pb.Unr
|
||||
return &emptypb.Empty{}, err
|
||||
}
|
||||
|
||||
func (s *UserService) ListUserCompetitions(ctx context.Context, req *pb.ListUserCompetitionsRequest) (*pb.ListUserCompetitionsResponse, error) {
|
||||
func (s *UserService) ListUserCompetitions(
|
||||
ctx context.Context,
|
||||
req *pb.ListUserCompetitionsRequest,
|
||||
) (*pb.ListUserCompetitionsResponse, error) {
|
||||
competitionIDs, err := s.repo.ListUserCompetitions(ctx, req.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"datarush/internal/user/service/mocks"
|
||||
pb "datarush/pkg/api/user"
|
||||
|
||||
"go.uber.org/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestUserService(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user