Module project, commit, submission
All checks were successful
Build and Release / release (push) Successful in 1m15s
All checks were successful
Build and Release / release (push) Successful in 1m15s
This commit is contained in:
282
internal/services/submissionService.go
Normal file
282
internal/services/submissionService.go
Normal file
@@ -0,0 +1,282 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/internal/repositories"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
"slices"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type SubmissionService interface {
|
||||
CreateSubmission(ctx context.Context, userID string, dto *request.CreateSubmissionDto) (*response.SubmissionResponse, error)
|
||||
UpdateSubmissionStatus(ctx context.Context, reviewerID string, submissionID string, dto *request.UpdateSubmissionStatusDto) (*response.SubmissionResponse, error)
|
||||
GetSubmissionByID(ctx context.Context, id string) (*response.SubmissionResponse, error)
|
||||
SearchSubmissions(ctx context.Context, dto *request.SearchSubmissionDto) (*response.PaginatedResponse, error)
|
||||
DeleteSubmission(ctx context.Context, userID string, id string, claims *response.JWTClaims) error
|
||||
}
|
||||
|
||||
type submissionService struct {
|
||||
submissionRepo repositories.SubmissionRepository
|
||||
projectRepo repositories.ProjectRepository
|
||||
commitRepo repositories.CommitRepository
|
||||
userRepo repositories.UserRepository
|
||||
db *pgxpool.Pool
|
||||
c cache.Cache
|
||||
}
|
||||
|
||||
func NewSubmissionService(
|
||||
submissionRepo repositories.SubmissionRepository,
|
||||
projectRepo repositories.ProjectRepository,
|
||||
commitRepo repositories.CommitRepository,
|
||||
userRepo repositories.UserRepository,
|
||||
db *pgxpool.Pool,
|
||||
c cache.Cache,
|
||||
) SubmissionService {
|
||||
return &submissionService{
|
||||
submissionRepo: submissionRepo,
|
||||
projectRepo: projectRepo,
|
||||
commitRepo: commitRepo,
|
||||
userRepo: userRepo,
|
||||
db: db,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *submissionService) CreateSubmission(ctx context.Context, userID string, dto *request.CreateSubmissionDto) (*response.SubmissionResponse, error) {
|
||||
projectUUID, err := convert.StringToUUID(dto.ProjectID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID")
|
||||
}
|
||||
|
||||
commitUUID, err := convert.StringToUUID(dto.CommitID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid commit ID")
|
||||
}
|
||||
|
||||
userUUID, err := convert.StringToUUID(userID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID")
|
||||
}
|
||||
|
||||
commit, err := s.commitRepo.GetByID(ctx, commitUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Commit not found")
|
||||
}
|
||||
if commit.ProjectID != dto.ProjectID {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Commit does not belong to project")
|
||||
}
|
||||
|
||||
arg := sqlc.CreateSubmissionParams{
|
||||
ProjectID: projectUUID,
|
||||
CommitID: commitUUID,
|
||||
UserID: userUUID,
|
||||
Status: constants.StatusTypePending.Int16(),
|
||||
Content: convert.StringToText(dto.Content),
|
||||
}
|
||||
|
||||
submission, err := s.submissionRepo.Create(ctx, arg)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create submission")
|
||||
}
|
||||
|
||||
return submission.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewerID string, submissionID string, dto *request.UpdateSubmissionStatusDto) (*response.SubmissionResponse, error) {
|
||||
submissionUUID, err := convert.StringToUUID(submissionID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid submission ID")
|
||||
}
|
||||
|
||||
reviewerUUID, err := convert.StringToUUID(reviewerID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid reviewer ID")
|
||||
}
|
||||
|
||||
status := constants.ParseStatusTypeText(dto.Status)
|
||||
if status == constants.StatusTypeUnknown {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid status")
|
||||
}
|
||||
|
||||
|
||||
submission, err := s.submissionRepo.GetByID(ctx, submissionUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Submission not found")
|
||||
}
|
||||
|
||||
if submission.Status != constants.StatusTypePending {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Submission already processed")
|
||||
}
|
||||
|
||||
arg := sqlc.UpdateSubmissionParams{
|
||||
ID: submissionUUID,
|
||||
Status: pgtype.Int2{Int16: status.Int16(), Valid: true},
|
||||
ReviewedBy: reviewerUUID,
|
||||
ReviewNote: convert.StringToText(dto.ReviewNote),
|
||||
}
|
||||
|
||||
updatedSubmission, err := s.submissionRepo.Update(ctx, arg)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update submission status")
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("proejct:id:%s", submission.ProjectID))
|
||||
|
||||
|
||||
return updatedSubmission.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (m *submissionService) fillSearchArgs(arg *sqlc.SearchSubmissionsParams, dto *request.SearchSubmissionDto) {
|
||||
if dto.Sort != "" {
|
||||
arg.Sort = pgtype.Text{String: dto.Sort, Valid: true}
|
||||
} else {
|
||||
arg.Sort = pgtype.Text{String: "id", Valid: true}
|
||||
}
|
||||
|
||||
arg.Order = pgtype.Text{String: "asc", Valid: true}
|
||||
if dto.Order == "desc" {
|
||||
arg.Order = pgtype.Text{String: "desc", Valid: true}
|
||||
}
|
||||
|
||||
if len(dto.Statuses) > 0 {
|
||||
for _, id := range dto.Statuses {
|
||||
if u := constants.ParseStatusTypeText(id); u != constants.StatusTypeUnknown {
|
||||
arg.Statuses = append(arg.Statuses, u.Int16())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(dto.UserIDs) > 0 {
|
||||
for _, id := range dto.UserIDs {
|
||||
if u, err := convert.StringToUUID(id); err == nil {
|
||||
arg.UserIds = append(arg.UserIds, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dto.ReviewedBy != nil {
|
||||
if rvID, err := convert.StringToUUID(*dto.ReviewedBy); err == nil {
|
||||
arg.ReviewedBy = rvID
|
||||
}
|
||||
}
|
||||
|
||||
if dto.CreatedFrom != nil {
|
||||
arg.CreatedFrom = pgtype.Timestamptz{Time: *dto.CreatedFrom, Valid: true}
|
||||
}
|
||||
|
||||
if dto.CreatedTo != nil {
|
||||
arg.CreatedTo = pgtype.Timestamptz{Time: *dto.CreatedTo, Valid: true}
|
||||
}
|
||||
|
||||
if dto.Search != "" {
|
||||
arg.SearchText = pgtype.Text{String: dto.Search, Valid: true}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *submissionService) GetSubmissionByID(ctx context.Context, id string) (*response.SubmissionResponse, error) {
|
||||
submissionUUID, err := convert.StringToUUID(id)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid submission ID")
|
||||
}
|
||||
|
||||
submission, err := s.submissionRepo.GetByID(ctx, submissionUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Submission not found")
|
||||
}
|
||||
|
||||
return submission.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *submissionService) SearchSubmissions(ctx context.Context, dto *request.SearchSubmissionDto) (*response.PaginatedResponse, error) {
|
||||
if dto.Page < 1 {
|
||||
dto.Page = 1
|
||||
}
|
||||
if dto.Limit == 0 {
|
||||
dto.Limit = 20
|
||||
}
|
||||
offset := (dto.Page - 1) * dto.Limit
|
||||
|
||||
arg := sqlc.SearchSubmissionsParams{
|
||||
Limit: int32(dto.Limit),
|
||||
Offset: int32(offset),
|
||||
}
|
||||
|
||||
s.fillSearchArgs(&arg, dto)
|
||||
|
||||
var rows []*models.SubmissionEntity
|
||||
var totalRecords int64
|
||||
|
||||
g, gCtx := errgroup.WithContext(ctx)
|
||||
|
||||
g.Go(func() error {
|
||||
var err error
|
||||
rows, err = s.submissionRepo.Search(gCtx, arg)
|
||||
return err
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
countArg := sqlc.CountSubmissionsParams{
|
||||
UserIds: arg.UserIds,
|
||||
Statuses: arg.Statuses,
|
||||
ReviewedBy: arg.ReviewedBy,
|
||||
CreatedFrom: arg.CreatedFrom,
|
||||
CreatedTo: arg.CreatedTo,
|
||||
SearchText: arg.SearchText,
|
||||
}
|
||||
var err error
|
||||
totalRecords, err = s.submissionRepo.Count(gCtx, countArg)
|
||||
return err
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
submissions := models.SubmissionsEntityToResponse(rows)
|
||||
|
||||
return response.BuildPaginatedResponse(submissions, totalRecords, dto.Page, dto.Limit), nil
|
||||
}
|
||||
|
||||
func (s *submissionService) DeleteSubmission(ctx context.Context, userID string, id string, claims *response.JWTClaims) error {
|
||||
submissionUUID, err := convert.StringToUUID(id)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid submission ID")
|
||||
}
|
||||
|
||||
submission, err := s.submissionRepo.GetByID(ctx, submissionUUID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Submission not found")
|
||||
}
|
||||
|
||||
shoudDelete := false
|
||||
if slices.Contains(claims.Roles, constants.RoleTypeAdmin) || slices.Contains(claims.Roles, constants.RoleTypeMod) {
|
||||
shoudDelete = true
|
||||
}
|
||||
|
||||
if submission.UserID == claims.UId && submission.Status == constants.StatusTypePending {
|
||||
shoudDelete = true
|
||||
}
|
||||
|
||||
if !shoudDelete {
|
||||
return fiber.NewError(fiber.StatusForbidden, "You don't have permission to delete this submission")
|
||||
}
|
||||
|
||||
err = s.submissionRepo.Delete(ctx, submissionUUID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete submission")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user