ikurotime / gitgud

public
main / internal/app/issue_service.go
2.8 KB · Go Raw
  1package app
  2
  3import (
  4	"context"
  5	"fmt"
  6	"strings"
  7
  8	"gitgud/internal/domain"
  9)
 10
 11type IssueService struct {
 12	issues domain.IssueRepository
 13}
 14
 15func NewIssueService(issues domain.IssueRepository) *IssueService {
 16	return &IssueService{issues: issues}
 17}
 18
 19func (s *IssueService) Open(ctx context.Context, repo *domain.Repository, author *domain.User, title, body string) (*domain.Issue, error) {
 20	if author == nil {
 21		return nil, domain.ErrUnauthorized
 22	}
 23	title = strings.TrimSpace(title)
 24	if title == "" {
 25		return nil, fmt.Errorf("title is required: %w", domain.ErrValidation)
 26	}
 27
 28	issue := &domain.Issue{
 29		RepoID:   repo.ID,
 30		AuthorID: author.ID,
 31		Title:    title,
 32		Body:     strings.TrimSpace(body),
 33		State:    domain.IssueOpen,
 34	}
 35	if err := s.issues.Create(ctx, issue); err != nil {
 36		return nil, err
 37	}
 38	return issue, nil
 39}
 40
 41func (s *IssueService) Comment(ctx context.Context, issue *domain.Issue, author *domain.User, body string) error {
 42	if author == nil {
 43		return domain.ErrUnauthorized
 44	}
 45	body = strings.TrimSpace(body)
 46	if body == "" {
 47		return fmt.Errorf("comment is required: %w", domain.ErrValidation)
 48	}
 49	return s.issues.AddComment(ctx, &domain.IssueComment{
 50		IssueID:  issue.ID,
 51		AuthorID: author.ID,
 52		Body:     body,
 53	})
 54}
 55
 56func (s *IssueService) Close(ctx context.Context, repo *domain.Repository, issue *domain.Issue, actor *domain.User) error {
 57	if !canModifyIssue(repo, issue, actor) {
 58		return domain.ErrPermission
 59	}
 60	return s.issues.SetState(ctx, issue.ID, domain.IssueClosed)
 61}
 62
 63func (s *IssueService) Reopen(ctx context.Context, repo *domain.Repository, issue *domain.Issue, actor *domain.User) error {
 64	if !canModifyIssue(repo, issue, actor) {
 65		return domain.ErrPermission
 66	}
 67	return s.issues.SetState(ctx, issue.ID, domain.IssueOpen)
 68}
 69
 70func (s *IssueService) List(ctx context.Context, repoID int64, state domain.IssueState) ([]*domain.Issue, error) {
 71	return s.issues.List(ctx, repoID, state)
 72}
 73
 74func (s *IssueService) Get(ctx context.Context, repoID int64, number int) (*domain.Issue, []*domain.IssueComment, error) {
 75	issue, err := s.issues.ByNumber(ctx, repoID, number)
 76	if err != nil {
 77		return nil, nil, err
 78	}
 79	comments, err := s.issues.Comments(ctx, issue.ID)
 80	if err != nil {
 81		return nil, nil, err
 82	}
 83	return issue, comments, nil
 84}
 85
 86func (s *IssueService) Comments(ctx context.Context, issueID int64) ([]*domain.IssueComment, error) {
 87	return s.issues.Comments(ctx, issueID)
 88}
 89
 90func (s *IssueService) Counts(ctx context.Context, repoID int64) (open, closed int, err error) {
 91	return s.issues.CountByState(ctx, repoID)
 92}
 93
 94func CanModifyIssue(repo *domain.Repository, issue *domain.Issue, actor *domain.User) bool {
 95	return canModifyIssue(repo, issue, actor)
 96}
 97
 98func canModifyIssue(repo *domain.Repository, issue *domain.Issue, actor *domain.User) bool {
 99	return actor != nil && (actor.ID == issue.AuthorID || actor.ID == repo.OwnerID)
100}