rewrite competition service: remove http related things
This commit is contained in:
@@ -6,7 +6,6 @@ toolchain go1.24.9
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/squirrel v1.5.4
|
github.com/Masterminds/squirrel v1.5.4
|
||||||
github.com/gin-gonic/gin v1.10.0
|
|
||||||
github.com/go-playground/validator/v10 v10.28.0
|
github.com/go-playground/validator/v10 v10.28.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||||
github.com/golang-migrate/migrate/v4 v4.19.0
|
github.com/golang-migrate/migrate/v4 v4.19.0
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
|||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
|
||||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
@@ -52,7 +51,6 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
|
|||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
github.com/golang-migrate/migrate/v4 v4.19.0 h1:RcjOnCGz3Or6HQYEJ/EEVLfWnmw9KnoigPSjzhCuaSE=
|
github.com/golang-migrate/migrate/v4 v4.19.0 h1:RcjOnCGz3Or6HQYEJ/EEVLfWnmw9KnoigPSjzhCuaSE=
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type Config struct {
|
|||||||
DBUser string
|
DBUser string
|
||||||
DBPassword string
|
DBPassword string
|
||||||
DBName string
|
DBName string
|
||||||
|
JWTSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Load() (*Config, error) {
|
func Load() (*Config, error) {
|
||||||
@@ -35,6 +36,7 @@ func Load() (*Config, error) {
|
|||||||
DBUser: getEnv("POSTGRES_USERNAME", "postgres"),
|
DBUser: getEnv("POSTGRES_USERNAME", "postgres"),
|
||||||
DBPassword: getEnv("POSTGRES_PASSWORD", "postgres"),
|
DBPassword: getEnv("POSTGRES_PASSWORD", "postgres"),
|
||||||
DBName: getEnv("POSTGRES_DATABASE", "postgres"),
|
DBName: getEnv("POSTGRES_DATABASE", "postgres"),
|
||||||
|
JWTSecret: getEnv("JWT_SECRET", "your-secret-key-change-in-production"),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
package domain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CompetitionState string
|
|
||||||
|
|
||||||
const (
|
|
||||||
CompetitionStateUnspecified CompetitionState = "UNSPECIFIED"
|
|
||||||
CompetitionStateDraft CompetitionState = "DRAFT"
|
|
||||||
CompetitionStateNotStarted CompetitionState = "NOT_STARTED"
|
|
||||||
CompetitionStateStarted CompetitionState = "STARTED"
|
|
||||||
CompetitionStateFinished CompetitionState = "FINISHED"
|
|
||||||
CompetitionStateArchived CompetitionState = "ARCHIVED"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ParticipationType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
ParticipationTypeUnspecified ParticipationType = "UNSPECIFIED"
|
|
||||||
ParticipationTypeIndividual ParticipationType = "INDIVIDUAL"
|
|
||||||
ParticipationTypeTeam ParticipationType = "TEAM"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CompetitionType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
CompetitionTypeUnspecified CompetitionType = "UNSPECIFIED"
|
|
||||||
CompetitionTypeEducative CompetitionType = "EDUCATIVE"
|
|
||||||
CompetitionTypeCompetitive CompetitionType = "COMPETITIVE"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Competition struct {
|
|
||||||
ID uuid.UUID
|
|
||||||
State CompetitionState
|
|
||||||
Title string
|
|
||||||
Description string
|
|
||||||
ImageURL *string
|
|
||||||
StartTime time.Time
|
|
||||||
EndTime time.Time
|
|
||||||
Type CompetitionType
|
|
||||||
ParticipationType ParticipationType
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func NewCompetition(id uuid.UUID, state CompetitionState, title string, description string, imageURL *string, startTime time.Time, endTime time.Time, tpe CompetitionType, participationType ParticipationType, createdAt time.Time, updatedAt time.Time) *Competition {
|
|
||||||
return &Competition{
|
|
||||||
ID: id,
|
|
||||||
State: state,
|
|
||||||
Title: title,
|
|
||||||
Description: description,
|
|
||||||
ImageURL: imageURL,
|
|
||||||
StartTime: startTime,
|
|
||||||
EndTime: endTime,
|
|
||||||
Type: tpe,
|
|
||||||
ParticipationType: participationType,
|
|
||||||
CreatedAt: createdAt,
|
|
||||||
UpdatedAt: updatedAt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Competition) Validate() error {
|
|
||||||
validate := validator.New()
|
|
||||||
|
|
||||||
return fmt.Errorf("%w: %w", ErrInvalidCompetitionData, validate.Struct(c))
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package domain
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrCompetitionNotFound = errors.New("competition not found")
|
|
||||||
ErrInvalidCompetitionData = errors.New("invalid competition data")
|
|
||||||
)
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"datarush/internal/competition/domain"
|
|
||||||
pb "datarush/pkg/api/competition"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
"google.golang.org/protobuf/types/known/emptypb"
|
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ICompetitionService defines the interface for business logic.
|
|
||||||
// It works with domain models.
|
|
||||||
type ICompetitionService interface {
|
|
||||||
CreateCompetition(ctx context.Context, comp *domain.Competition) (*domain.Competition, error)
|
|
||||||
GetCompetition(ctx context.Context, id uuid.UUID) (*domain.Competition, error)
|
|
||||||
EditCompetition(ctx context.Context, comp *domain.Competition) (*domain.Competition, error)
|
|
||||||
DeleteCompetition(ctx context.Context, id uuid.UUID) error
|
|
||||||
ListCompetitions(ctx context.Context, pageSize int32, pageToken int32, state *domain.CompetitionState, isParticipating *bool, searchQuery *string) (competitions []domain.Competition, totalCount int32, nextPageToken int32, err error)
|
|
||||||
ChangeCompetitionState(ctx context.Context, id uuid.UUID, state domain.CompetitionState) (*domain.Competition, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type CompetitionHandler struct {
|
|
||||||
pb.UnimplementedCompetitionServiceServer
|
|
||||||
service ICompetitionService
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCompetitionHandler(s ICompetitionService) *CompetitionHandler {
|
|
||||||
return &CompetitionHandler{service: s}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *CompetitionHandler) CreateCompetition(ctx context.Context, req *pb.Competition) (*pb.Competition, error) {
|
|
||||||
domainComp, err := toDomainCompetition(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "failed to map competition: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
createdComp, err := h.service.CreateCompetition(ctx, domainComp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Internal, "failed to create competition: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fromDomainCompetition(createdComp), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *CompetitionHandler) GetCompetition(ctx context.Context, req *pb.GetCompetitionRequest) (*pb.Competition, error) {
|
|
||||||
id, err := uuid.Parse(req.GetCompetitionId())
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "invalid competition id format")
|
|
||||||
}
|
|
||||||
comp, err := h.service.GetCompetition(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.NotFound, "failed to get competition: %v", err)
|
|
||||||
}
|
|
||||||
return fromDomainCompetition(comp), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *CompetitionHandler) EditCompetition(ctx context.Context, req *pb.Competition) (*pb.Competition, error) {
|
|
||||||
domainComp, err := toDomainCompetition(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "failed to map competition: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedComp, err := h.service.EditCompetition(ctx, domainComp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Internal, "failed to edit competition: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fromDomainCompetition(updatedComp), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *CompetitionHandler) DeleteCompetition(ctx context.Context, req *pb.DeleteCompetitionRequest) (*emptypb.Empty, error) {
|
|
||||||
id, err := uuid.Parse(req.GetCompetitionId())
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "invalid competition id format")
|
|
||||||
}
|
|
||||||
if err := h.service.DeleteCompetition(ctx, id); err != nil {
|
|
||||||
return nil, status.Errorf(codes.Internal, "failed to delete competition: %v", err)
|
|
||||||
}
|
|
||||||
return &emptypb.Empty{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *CompetitionHandler) ListCompetitions(ctx context.Context, req *pb.ListCompetitionsRequest) (*pb.ListCompetitionsResponse, error) {
|
|
||||||
var state *domain.CompetitionState
|
|
||||||
if req.State != nil {
|
|
||||||
s := toDomainCompetitionState(*req.State)
|
|
||||||
state = &s
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchQuery *string
|
|
||||||
if req.SearchQuery != nil {
|
|
||||||
searchQuery = req.SearchQuery
|
|
||||||
}
|
|
||||||
|
|
||||||
competitions, totalCount, nextPageToken, err := h.service.ListCompetitions(ctx, req.PageSize, req.PageToken, state, req.IsParticipating, searchQuery)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Internal, "failed to list competitions: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pbCompetitions := make([]*pb.Competition, len(competitions))
|
|
||||||
for i, c := range competitions {
|
|
||||||
pbCompetitions[i] = fromDomainCompetition(&c)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &pb.ListCompetitionsResponse{
|
|
||||||
Competitions: pbCompetitions,
|
|
||||||
TotalCount: totalCount,
|
|
||||||
NextPageToken: nextPageToken,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *CompetitionHandler) ChangeCompetitionState(ctx context.Context, req *pb.ChangeCompetitionStateRequest) (*pb.Competition, error) {
|
|
||||||
id, err := uuid.Parse(req.GetCompetitionId())
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "invalid competition id format")
|
|
||||||
}
|
|
||||||
|
|
||||||
comp, err := h.service.ChangeCompetitionState(ctx, id, toDomainCompetitionState(req.GetState()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Internal, "failed to change competition state: %v", err)
|
|
||||||
}
|
|
||||||
return fromDomainCompetition(comp), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func toDomainCompetition(c *pb.Competition) (*domain.Competition, error) {
|
|
||||||
if c == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var id uuid.UUID
|
|
||||||
var err error
|
|
||||||
if c.Id != "" {
|
|
||||||
id, err = uuid.Parse(c.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "invalid id format")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &domain.Competition{
|
|
||||||
ID: id,
|
|
||||||
State: toDomainCompetitionState(c.State),
|
|
||||||
Title: c.Title,
|
|
||||||
Description: c.Description,
|
|
||||||
ImageURL: c.ImageUrl,
|
|
||||||
StartTime: c.StartTime.AsTime(),
|
|
||||||
EndTime: c.EndTime.AsTime(),
|
|
||||||
Type: toDomainCompetitionType(c.Type),
|
|
||||||
ParticipationType: toDomainParticipationType(c.ParticipationType),
|
|
||||||
CreatedAt: c.CreatedAt.AsTime(),
|
|
||||||
UpdatedAt: c.UpdatedAt.AsTime(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromDomainCompetition(c *domain.Competition) *pb.Competition {
|
|
||||||
if c == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &pb.Competition{
|
|
||||||
Id: c.ID.String(),
|
|
||||||
State: fromDomainCompetitionState(c.State),
|
|
||||||
Title: c.Title,
|
|
||||||
Description: c.Description,
|
|
||||||
ImageUrl: c.ImageURL,
|
|
||||||
StartTime: timestamppb.New(c.StartTime),
|
|
||||||
EndTime: timestamppb.New(c.EndTime),
|
|
||||||
Type: fromDomainCompetitionType(c.Type),
|
|
||||||
ParticipationType: fromDomainParticipationType(c.ParticipationType),
|
|
||||||
CreatedAt: timestamppb.New(c.CreatedAt),
|
|
||||||
UpdatedAt: timestamppb.New(c.UpdatedAt),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func toDomainCompetitionState(s pb.CompetitionState) domain.CompetitionState {
|
|
||||||
return domain.CompetitionState(strings.TrimPrefix(s.String(), "COMPETITION_STATE_"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromDomainCompetitionState(s domain.CompetitionState) pb.CompetitionState {
|
|
||||||
return pb.CompetitionState(pb.CompetitionState_value["COMPETITION_STATE_"+string(s)])
|
|
||||||
}
|
|
||||||
|
|
||||||
func toDomainParticipationType(pt pb.ParticipationType) domain.ParticipationType {
|
|
||||||
return domain.ParticipationType(strings.TrimPrefix(pt.String(), "PARTICIPATION_TYPE_"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromDomainParticipationType(pt domain.ParticipationType) pb.ParticipationType {
|
|
||||||
return pb.ParticipationType(pb.ParticipationType_value["PARTICIPATION_TYPE_"+string(pt)])
|
|
||||||
}
|
|
||||||
|
|
||||||
func toDomainCompetitionType(ct pb.CompetitionType) domain.CompetitionType {
|
|
||||||
return domain.CompetitionType(strings.TrimPrefix(ct.String(), "COMPETITION_TYPE_"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromDomainCompetitionType(ct domain.CompetitionType) pb.CompetitionType {
|
|
||||||
return pb.CompetitionType(pb.CompetitionType_value["COMPETITION_TYPE_"+string(ct)])
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
pb "datarush/pkg/api/competition"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CompetitionService interface {
|
||||||
|
CreateCompetition(ctx context.Context, req *pb.Competition) (*pb.Competition, error)
|
||||||
|
GetCompetition(ctx context.Context, req *pb.GetCompetitionRequest) (*pb.Competition, error)
|
||||||
|
EditCompetition(ctx context.Context, req *pb.Competition) (*pb.Competition, error)
|
||||||
|
DeleteCompetition(ctx context.Context, req *pb.DeleteCompetitionRequest) (*emptypb.Empty, error)
|
||||||
|
ListCompetitions(ctx context.Context, req *pb.ListCompetitionsRequest) (*pb.ListCompetitionsResponse, error)
|
||||||
|
ChangeCompetitionState(ctx context.Context, req *pb.ChangeCompetitionStateRequest) (*pb.Competition, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompetitionHandler struct {
|
||||||
|
pb.UnimplementedCompetitionServiceServer
|
||||||
|
service CompetitionService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCompetitionHandler(service CompetitionService) *CompetitionHandler {
|
||||||
|
return &CompetitionHandler{service: service}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CompetitionHandler) CreateCompetition(ctx context.Context, req *pb.Competition) (*pb.Competition, error) {
|
||||||
|
return h.service.CreateCompetition(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CompetitionHandler) GetCompetition(ctx context.Context, req *pb.GetCompetitionRequest) (*pb.Competition, error) {
|
||||||
|
return h.service.GetCompetition(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CompetitionHandler) EditCompetition(ctx context.Context, req *pb.Competition) (*pb.Competition, error) {
|
||||||
|
return h.service.EditCompetition(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CompetitionHandler) DeleteCompetition(ctx context.Context, req *pb.DeleteCompetitionRequest) (*emptypb.Empty, error) {
|
||||||
|
return h.service.DeleteCompetition(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CompetitionHandler) ListCompetitions(ctx context.Context, req *pb.ListCompetitionsRequest) (*pb.ListCompetitionsResponse, error) {
|
||||||
|
return h.service.ListCompetitions(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CompetitionHandler) ChangeCompetitionState(ctx context.Context, req *pb.ChangeCompetitionStateRequest) (*pb.Competition, error) {
|
||||||
|
return h.service.ChangeCompetitionState(ctx, req)
|
||||||
|
}
|
||||||
@@ -2,25 +2,25 @@ package repository
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"datarush/internal/competition/domain"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
|
||||||
|
|
||||||
|
pb "datarush/pkg/api/competition"
|
||||||
|
)
|
||||||
|
|
||||||
type ListCompetitionsOptions struct {
|
type ListCompetitionsOptions struct {
|
||||||
Page int
|
Page int
|
||||||
PageSize int
|
PageSize int
|
||||||
State *domain.CompetitionState
|
State *pb.CompetitionState
|
||||||
IsParticipating *bool
|
IsParticipating *bool
|
||||||
SearchQuery *string
|
SearchQuery *string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompetitionRepository interface {
|
type CompetitionRepository interface {
|
||||||
Create(ctx context.Context, competition *domain.Competition) error
|
Create(ctx context.Context, competition *pb.Competition) (*pb.Competition, error)
|
||||||
Get(ctx context.Context, id uuid.UUID) (*domain.Competition, error)
|
Get(ctx context.Context, id uuid.UUID) (*pb.Competition, error)
|
||||||
Update(ctx context.Context, competition *domain.Competition) error
|
Update(ctx context.Context, competition *pb.Competition) (*pb.Competition, error)
|
||||||
Delete(ctx context.Context, id uuid.UUID) error
|
Delete(ctx context.Context, id uuid.UUID) error
|
||||||
List(ctx context.Context, opts ListCompetitionsOptions) ([]domain.Competition, int, error)
|
List(ctx context.Context, opts ListCompetitionsOptions) ([]*pb.Competition, int, error)
|
||||||
ChangeState(ctx context.Context, id uuid.UUID, state domain.CompetitionState) (*domain.Competition, error)
|
ChangeState(ctx context.Context, id uuid.UUID, state pb.CompetitionState) (*pb.Competition, error)
|
||||||
}
|
}
|
||||||
@@ -2,152 +2,91 @@ package postgres
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
"datarush/internal/competition/domain"
|
|
||||||
"datarush/internal/competition/repository"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"datarush/internal/competition/repository"
|
||||||
"github.com/redis/go-redis/v9"
|
pb "datarush/pkg/api/competition"
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
"github.com/google/uuid"
|
||||||
competitionCachePrefix = "competition:"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CompetitionRepository struct {
|
type CompetitionRepository struct {
|
||||||
db *sql.DB
|
db *sqlx.DB
|
||||||
redisClient *redis.Client
|
|
||||||
cacheEnabled bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCompetitionRepository(db *sql.DB, redisClient *redis.Client, cacheEnabled bool) repository.CompetitionRepository {
|
func NewCompetitionRepository(db *sqlx.DB) repository.CompetitionRepository {
|
||||||
return &CompetitionRepository{
|
return &CompetitionRepository{
|
||||||
db: db,
|
db: db,
|
||||||
redisClient: redisClient,
|
|
||||||
cacheEnabled: cacheEnabled,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CompetitionRepository) cacheKey(id string) string {
|
func (r *CompetitionRepository) Create(ctx context.Context, c *pb.Competition) (*pb.Competition, error) {
|
||||||
return competitionCachePrefix + id
|
query := `INSERT INTO competitions (state, title, description, image_url, start_time, end_time, type, participation_type)
|
||||||
}
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, created_at, updated_at`
|
||||||
|
|
||||||
func (r *CompetitionRepository) Create(ctx context.Context, competition *domain.Competition) error {
|
var createdCompetition pb.Competition
|
||||||
query := `INSERT INTO competitions (id, state, title, description, image_url, start_time, end_time, type, participation_type, created_at, updated_at)
|
err := r.db.QueryRowxContext(ctx, query,
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`
|
c.State,
|
||||||
_, err := r.db.ExecContext(ctx, query,
|
c.Title,
|
||||||
competition.ID,
|
c.Description,
|
||||||
competition.State,
|
c.ImageUrl,
|
||||||
competition.Title,
|
c.StartTime.AsTime(),
|
||||||
competition.Description,
|
c.EndTime.AsTime(),
|
||||||
competition.ImageURL,
|
c.Type,
|
||||||
competition.StartTime,
|
c.ParticipationType,
|
||||||
competition.EndTime,
|
).StructScan(&createdCompetition)
|
||||||
competition.Type,
|
|
||||||
competition.ParticipationType,
|
|
||||||
competition.CreatedAt,
|
|
||||||
competition.UpdatedAt,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.cacheEnabled {
|
|
||||||
data, err := json.Marshal(competition)
|
|
||||||
if err == nil {
|
|
||||||
r.redisClient.Set(ctx, r.cacheKey(competition.ID.String()), data, 10*time.Minute).Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *CompetitionRepository) Get(ctx context.Context, id uuid.UUID) (*domain.Competition, error) {
|
|
||||||
if r.cacheEnabled {
|
|
||||||
val, err := r.redisClient.Get(ctx, r.cacheKey(id.String())).Result()
|
|
||||||
if err == nil {
|
|
||||||
var competition domain.Competition
|
|
||||||
if json.Unmarshal([]byte(val), &competition) == nil {
|
|
||||||
return &competition, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `SELECT id, state, title, description, image_url, start_time, end_time, type, participation_type, created_at, updated_at FROM competitions WHERE id = $1`
|
|
||||||
row := r.db.QueryRowContext(ctx, query, id)
|
|
||||||
|
|
||||||
var competition domain.Competition
|
|
||||||
err := row.Scan(
|
|
||||||
&competition.ID,
|
|
||||||
&competition.State,
|
|
||||||
&competition.Title,
|
|
||||||
&competition.Description,
|
|
||||||
&competition.ImageURL,
|
|
||||||
&competition.StartTime,
|
|
||||||
&competition.EndTime,
|
|
||||||
&competition.Type,
|
|
||||||
&competition.ParticipationType,
|
|
||||||
&competition.CreatedAt,
|
|
||||||
&competition.UpdatedAt,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
c.Id = createdCompetition.Id
|
||||||
|
c.CreatedAt = createdCompetition.CreatedAt
|
||||||
|
c.UpdatedAt = createdCompetition.UpdatedAt
|
||||||
|
|
||||||
if r.cacheEnabled {
|
return c, nil
|
||||||
data, err := json.Marshal(&competition)
|
|
||||||
if err == nil {
|
|
||||||
r.redisClient.Set(ctx, r.cacheKey(id.String()), data, 10*time.Minute).Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &competition, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CompetitionRepository) Update(ctx context.Context, competition *domain.Competition) error {
|
func (r *CompetitionRepository) Get(ctx context.Context, id uuid.UUID) (*pb.Competition, error) {
|
||||||
|
query := `SELECT id, state, title, description, image_url, start_time, end_time, type, participation_type, created_at, updated_at FROM competitions WHERE id = $1`
|
||||||
|
var competition pb.Competition
|
||||||
|
err := r.db.GetContext(ctx, &competition, query, id)
|
||||||
|
return &competition, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CompetitionRepository) Update(ctx context.Context, c *pb.Competition) (*pb.Competition, error) {
|
||||||
query := `UPDATE competitions SET
|
query := `UPDATE competitions SET
|
||||||
state = $2, title = $3, description = $4, image_url = $5, start_time = $6, end_time = $7, type = $8, participation_type = $9, updated_at = $10
|
state = $2, title = $3, description = $4, image_url = $5, start_time = $6, end_time = $7, type = $8, participation_type = $9, updated_at = now()
|
||||||
WHERE id = $1`
|
WHERE id = $1 RETURNING updated_at`
|
||||||
_, err := r.db.ExecContext(ctx, query,
|
|
||||||
competition.ID,
|
var updatedCompetition pb.Competition
|
||||||
competition.State,
|
err := r.db.QueryRowxContext(ctx, query,
|
||||||
competition.Title,
|
c.Id,
|
||||||
competition.Description,
|
c.State,
|
||||||
competition.ImageURL,
|
c.Title,
|
||||||
competition.StartTime,
|
c.Description,
|
||||||
competition.EndTime,
|
c.ImageUrl,
|
||||||
competition.Type,
|
c.StartTime.AsTime(),
|
||||||
competition.ParticipationType,
|
c.EndTime.AsTime(),
|
||||||
competition.UpdatedAt,
|
c.Type,
|
||||||
)
|
c.ParticipationType,
|
||||||
|
).StructScan(&updatedCompetition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
c.UpdatedAt = updatedCompetition.UpdatedAt
|
||||||
|
|
||||||
if r.cacheEnabled {
|
return c, nil
|
||||||
r.redisClient.Del(ctx, r.cacheKey(competition.ID.String())).Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CompetitionRepository) Delete(ctx context.Context, id uuid.UUID) error {
|
func (r *CompetitionRepository) Delete(ctx context.Context, id uuid.UUID) error {
|
||||||
query := `DELETE FROM competitions WHERE id = $1`
|
query := `DELETE FROM competitions WHERE id = $1`
|
||||||
_, err := r.db.ExecContext(ctx, query, id)
|
_, err := r.db.ExecContext(ctx, query, id)
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.cacheEnabled {
|
|
||||||
r.redisClient.Del(ctx, r.cacheKey(id.String())).Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
func (r *CompetitionRepository) List(ctx context.Context, opts repository.ListCompetitionsOptions) ([]domain.Competition, int, error) {
|
|
||||||
|
func (r *CompetitionRepository) List(ctx context.Context, opts repository.ListCompetitionsOptions) ([]*pb.Competition, int, error) {
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
var whereClauses []string
|
var whereClauses []string
|
||||||
argId := 1
|
argId := 1
|
||||||
@@ -164,15 +103,15 @@ func (r *CompetitionRepository) List(ctx context.Context, opts repository.ListCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.IsParticipating != nil {
|
if opts.IsParticipating != nil {
|
||||||
userID, ok := ctx.Value("user_id").(uuid.UUID)
|
userID, ok := ctx.Value("user_id").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, 0, fmt.Errorf("user not authenticated or user_id not in context")
|
return nil, 0, fmt.Errorf("user not authenticated or user_id not in context")
|
||||||
}
|
}
|
||||||
|
|
||||||
if *opts.IsParticipating {
|
if *opts.IsParticipating {
|
||||||
whereClauses = append(whereClauses, fmt.Sprintf("id IN (SELECT competition_id FROM competition_participants WHERE user_id = $%d)", argId))
|
whereClauses = append(whereClauses, fmt.Sprintf("id IN (SELECT competition_id FROM user_competitions WHERE user_id = $%d)", argId))
|
||||||
} else {
|
} else {
|
||||||
whereClauses = append(whereClauses, fmt.Sprintf("id NOT IN (SELECT competition_id FROM competition_participants WHERE user_id = $%d)", argId))
|
whereClauses = append(whereClauses, fmt.Sprintf("id NOT IN (SELECT competition_id FROM user_competitions WHERE user_id = $%d)", argId))
|
||||||
}
|
}
|
||||||
args = append(args, userID)
|
args = append(args, userID)
|
||||||
argId++
|
argId++
|
||||||
@@ -185,54 +124,30 @@ func (r *CompetitionRepository) List(ctx context.Context, opts repository.ListCo
|
|||||||
|
|
||||||
countQuery := "SELECT COUNT(*) FROM competitions " + where
|
countQuery := "SELECT COUNT(*) FROM competitions " + where
|
||||||
var total int
|
var total int
|
||||||
if err := r.db.QueryRowContext(ctx, countQuery, args...).Scan(&total); err != nil {
|
if err := r.db.GetContext(ctx, &total, countQuery, args...); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := fmt.Sprintf(`SELECT id, state, title, description, image_url, start_time, end_time, type, participation_type, created_at, updated_at FROM competitions %s ORDER BY created_at DESC LIMIT $%d OFFSET $%d`, where, argId, argId+1)
|
query := fmt.Sprintf(`SELECT id, state, title, description, image_url, start_time, end_time, type, participation_type, created_at, updated_at FROM competitions %s ORDER BY created_at DESC LIMIT $%d OFFSET $%d`, where, argId, argId+1)
|
||||||
args = append(args, opts.PageSize, opts.Page*opts.PageSize)
|
args = append(args, opts.PageSize, (opts.Page-1)*opts.PageSize)
|
||||||
|
|
||||||
rows, err := r.db.QueryContext(ctx, query, args...)
|
var competitions []*pb.Competition
|
||||||
|
err := r.db.SelectContext(ctx, &competitions, query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var competitions []domain.Competition
|
|
||||||
for rows.Next() {
|
|
||||||
var c domain.Competition
|
|
||||||
err := rows.Scan(
|
|
||||||
&c.ID,
|
|
||||||
&c.State,
|
|
||||||
&c.Title,
|
|
||||||
&c.Description,
|
|
||||||
&c.ImageURL,
|
|
||||||
&c.StartTime,
|
|
||||||
&c.EndTime,
|
|
||||||
&c.Type,
|
|
||||||
&c.ParticipationType,
|
|
||||||
&c.CreatedAt,
|
|
||||||
&c.UpdatedAt,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
competitions = append(competitions, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
return competitions, total, nil
|
return competitions, total, nil
|
||||||
}
|
}
|
||||||
func (r *CompetitionRepository) ChangeState(ctx context.Context, id uuid.UUID, state domain.CompetitionState) (*domain.Competition, error) {
|
|
||||||
query := `UPDATE competitions SET state = $1, updated_at = $2 WHERE id = $3`
|
func (r *CompetitionRepository) ChangeState(ctx context.Context, id uuid.UUID, state pb.CompetitionState) (*pb.Competition, error) {
|
||||||
|
query := `UPDATE competitions SET state = $1, updated_at = $2 WHERE id = $3 RETURNING updated_at`
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
_, err := r.db.ExecContext(ctx, query, state, now, id)
|
var competition pb.Competition
|
||||||
|
err := r.db.QueryRowxContext(ctx, query, state, now, id).StructScan(&competition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.cacheEnabled {
|
|
||||||
r.redisClient.Del(ctx, r.cacheKey(id.String())).Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.Get(ctx, id)
|
return r.Get(ctx, id)
|
||||||
}
|
}
|
||||||
@@ -4,11 +4,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"datarush/internal/competition/config"
|
"datarush/internal/competition/config"
|
||||||
grpcHandlers "datarush/internal/competition/handler/grpc"
|
grpcHandlers "datarush/internal/competition/handler/grpc"
|
||||||
|
"datarush/internal/competition/middleware"
|
||||||
"datarush/internal/competition/repository/postgres"
|
"datarush/internal/competition/repository/postgres"
|
||||||
"datarush/internal/competition/service"
|
"datarush/internal/competition/service"
|
||||||
pb "datarush/pkg/api/competition"
|
pb "datarush/pkg/api/competition"
|
||||||
@@ -27,7 +27,6 @@ const (
|
|||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
grpcServer *grpc.Server
|
grpcServer *grpc.Server
|
||||||
httpServer *http.Server
|
|
||||||
config *config.Config
|
config *config.Config
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
}
|
}
|
||||||
@@ -45,32 +44,32 @@ func (s *Server) Start() error {
|
|||||||
}
|
}
|
||||||
s.db = db
|
s.db = db
|
||||||
|
|
||||||
|
if err := s.registerGRPCServices(); err != nil {
|
||||||
|
return fmt.Errorf("failed to register gRPC services: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.startGRPCServer(); err != nil {
|
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", s.config.GRPCPort))
|
||||||
log.Fatalf("failed to start gRPC server: %v", err)
|
if err != nil {
|
||||||
|
log.Fatalf("failed to listen on grpc port: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("starting gRPC server on port %d", s.config.GRPCPort)
|
||||||
|
if err := s.grpcServer.Serve(lis); err != nil {
|
||||||
|
log.Fatalf("failed to serve gRPC: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Println("competition service started")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) startGRPCServer() error {
|
func (s *Server) registerGRPCServices() error {
|
||||||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", s.config.GRPCPort))
|
s.grpcServer = grpc.NewServer(
|
||||||
if err != nil {
|
grpc.UnaryInterceptor(middleware.AuthInterceptor(s.config.JWTSecret)),
|
||||||
return fmt.Errorf("failed to listen on grpc port: %w", err)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
s.grpcServer = grpc.NewServer()
|
compRepo := postgres.NewCompetitionRepository(s.db)
|
||||||
s.registerGRPCServices()
|
compService := service.NewCompetitionService(compRepo)
|
||||||
|
|
||||||
log.Printf("starting gRPC server on port %d", s.config.GRPCPort)
|
|
||||||
return s.grpcServer.Serve(lis)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) registerGRPCServices() {
|
|
||||||
compRepo := postgres.NewCompetitionRepository(s.db.DB, nil, false)
|
|
||||||
compService := service.NewService(compRepo)
|
|
||||||
compHandler := grpcHandlers.NewCompetitionHandler(compService)
|
compHandler := grpcHandlers.NewCompetitionHandler(compService)
|
||||||
|
|
||||||
pb.RegisterCompetitionServiceServer(s.grpcServer, compHandler)
|
pb.RegisterCompetitionServiceServer(s.grpcServer, compHandler)
|
||||||
@@ -78,6 +77,8 @@ func (s *Server) registerGRPCServices() {
|
|||||||
if s.config.GRPCEnableReflection {
|
if s.config.GRPCEnableReflection {
|
||||||
reflection.Register(s.grpcServer)
|
reflection.Register(s.grpcServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Stop() {
|
func (s *Server) Stop() {
|
||||||
@@ -89,9 +90,9 @@ func (s *Server) Stop() {
|
|||||||
|
|
||||||
if s.db != nil {
|
if s.db != nil {
|
||||||
if err := s.db.Close(); err != nil {
|
if err := s.db.Close(); err != nil {
|
||||||
log.Printf("failed to close database connection: %v", err)
|
log.Printf("failed to close database: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("competition server stopped")
|
log.Println("competition server stopped")
|
||||||
}
|
}
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"datarush/internal/competition/domain"
|
|
||||||
"datarush/internal/competition/repository"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
repo repository.CompetitionRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewService(repo repository.CompetitionRepository) *Service {
|
|
||||||
return &Service{repo: repo}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) CreateCompetition(ctx context.Context, comp *domain.Competition) (*domain.Competition, error) {
|
|
||||||
now := time.Now()
|
|
||||||
if comp.ID == uuid.Nil {
|
|
||||||
comp.ID = uuid.New()
|
|
||||||
}
|
|
||||||
comp.CreatedAt = now
|
|
||||||
comp.UpdatedAt = now
|
|
||||||
|
|
||||||
if err := s.repo.Create(ctx, comp); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return comp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetCompetition(ctx context.Context, id uuid.UUID) (*domain.Competition, error) {
|
|
||||||
return s.repo.Get(ctx, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) EditCompetition(ctx context.Context, comp *domain.Competition) (*domain.Competition, error) {
|
|
||||||
comp.UpdatedAt = time.Now()
|
|
||||||
if err := s.repo.Update(ctx, comp); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s.repo.Get(ctx, comp.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) DeleteCompetition(ctx context.Context, id uuid.UUID) error {
|
|
||||||
return s.repo.Delete(ctx, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) ListCompetitions(ctx context.Context, pageSize int32, pageToken int32, state *domain.CompetitionState, isParticipating *bool, searchQuery *string) ([]domain.Competition, int32, int32, error) {
|
|
||||||
opts := repository.ListCompetitionsOptions{
|
|
||||||
Page: int(pageToken),
|
|
||||||
PageSize: int(pageSize),
|
|
||||||
State: state,
|
|
||||||
IsParticipating: isParticipating,
|
|
||||||
SearchQuery: searchQuery,
|
|
||||||
}
|
|
||||||
|
|
||||||
competitions, total, err := s.repo.List(ctx, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var nextPageToken int32
|
|
||||||
if (opts.Page+1)*opts.PageSize < total {
|
|
||||||
nextPageToken = int32(opts.Page + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return competitions, int32(total), nextPageToken, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) ChangeCompetitionState(ctx context.Context, id uuid.UUID, state domain.CompetitionState) (*domain.Competition, error) {
|
|
||||||
return s.repo.ChangeState(ctx, id, state)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"datarush/internal/competition/repository"
|
||||||
|
pb "datarush/pkg/api/competition"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CompetitionService struct {
|
||||||
|
repo repository.CompetitionRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCompetitionService(repo repository.CompetitionRepository) *CompetitionService {
|
||||||
|
return &CompetitionService{repo: repo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CompetitionService) CreateCompetition(ctx context.Context, req *pb.Competition) (*pb.Competition, error) {
|
||||||
|
return s.repo.Create(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CompetitionService) GetCompetition(ctx context.Context, req *pb.GetCompetitionRequest) (*pb.Competition, error) {
|
||||||
|
id, err := uuid.Parse(req.CompetitionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s.repo.Get(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CompetitionService) EditCompetition(ctx context.Context, req *pb.Competition) (*pb.Competition, error) {
|
||||||
|
return s.repo.Update(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CompetitionService) DeleteCompetition(ctx context.Context, req *pb.DeleteCompetitionRequest) (*emptypb.Empty, error) {
|
||||||
|
id, err := uuid.Parse(req.CompetitionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = s.repo.Delete(ctx, id)
|
||||||
|
return &emptypb.Empty{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CompetitionService) ListCompetitions(ctx context.Context, req *pb.ListCompetitionsRequest) (*pb.ListCompetitionsResponse, error) {
|
||||||
|
opts := repository.ListCompetitionsOptions{
|
||||||
|
Page: int(req.PageToken),
|
||||||
|
PageSize: int(req.PageSize),
|
||||||
|
State: req.State,
|
||||||
|
IsParticipating: req.IsParticipating,
|
||||||
|
SearchQuery: req.SearchQuery,
|
||||||
|
}
|
||||||
|
|
||||||
|
competitions, total, err := s.repo.List(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextPageToken int32
|
||||||
|
if (opts.Page+1)*opts.PageSize < total {
|
||||||
|
nextPageToken = int32(opts.Page + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.ListCompetitionsResponse{
|
||||||
|
Competitions: competitions,
|
||||||
|
TotalCount: int32(total),
|
||||||
|
NextPageToken: nextPageToken,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CompetitionService) ChangeCompetitionState(ctx context.Context, req *pb.ChangeCompetitionStateRequest) (*pb.Competition, error) {
|
||||||
|
id, err := uuid.Parse(req.CompetitionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s.repo.ChangeState(ctx, id, req.State)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user