mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-06 08:01:24 +00:00
116 lines
3.4 KiB
Go
116 lines
3.4 KiB
Go
package team
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
"github.com/warmbly/warmbly/internal/models"
|
|
"github.com/warmbly/warmbly/internal/repository"
|
|
)
|
|
|
|
// TeamService is the org-scoped CRUD surface for teams and their members. Teams
|
|
// are created from existing organization members; membership in the org is
|
|
// validated on AddMember.
|
|
type TeamService interface {
|
|
ListTeams(ctx context.Context, orgID uuid.UUID) ([]models.Team, *errx.Error)
|
|
GetTeam(ctx context.Context, orgID, teamID uuid.UUID) (*models.Team, *errx.Error)
|
|
CreateTeam(ctx context.Context, orgID uuid.UUID, data *models.CreateTeam) (*models.Team, *errx.Error)
|
|
UpdateTeam(ctx context.Context, orgID, teamID uuid.UUID, data *models.UpdateTeam) (*models.Team, *errx.Error)
|
|
DeleteTeam(ctx context.Context, orgID, teamID uuid.UUID) *errx.Error
|
|
|
|
AddMember(ctx context.Context, orgID, teamID, userID uuid.UUID) *errx.Error
|
|
RemoveMember(ctx context.Context, orgID, teamID, userID uuid.UUID) *errx.Error
|
|
}
|
|
|
|
type teamService struct {
|
|
repo repository.TeamRepository
|
|
}
|
|
|
|
// NewService builds the team service over its repository.
|
|
func NewService(repo repository.TeamRepository) TeamService {
|
|
return &teamService{repo: repo}
|
|
}
|
|
|
|
func toErrx(err error) *errx.Error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
var bizErr *errx.Error
|
|
if errors.As(err, &bizErr) {
|
|
return bizErr
|
|
}
|
|
return errx.InternalError()
|
|
}
|
|
|
|
func (s *teamService) ListTeams(ctx context.Context, orgID uuid.UUID) ([]models.Team, *errx.Error) {
|
|
teams, err := s.repo.ListTeams(ctx, orgID)
|
|
if err != nil {
|
|
return nil, toErrx(err)
|
|
}
|
|
return teams, nil
|
|
}
|
|
|
|
func (s *teamService) GetTeam(ctx context.Context, orgID, teamID uuid.UUID) (*models.Team, *errx.Error) {
|
|
team, err := s.repo.GetTeam(ctx, orgID, teamID)
|
|
if err != nil {
|
|
return nil, toErrx(err)
|
|
}
|
|
return team, nil
|
|
}
|
|
|
|
func (s *teamService) CreateTeam(ctx context.Context, orgID uuid.UUID, data *models.CreateTeam) (*models.Team, *errx.Error) {
|
|
name := strings.TrimSpace(data.Name)
|
|
if name == "" || len(name) > 255 {
|
|
return nil, errx.New(errx.BadRequest, "team name must be between 1 and 255 characters")
|
|
}
|
|
data.Name = name
|
|
|
|
team, err := s.repo.CreateTeam(ctx, orgID, data)
|
|
if err != nil {
|
|
return nil, toErrx(err)
|
|
}
|
|
return team, nil
|
|
}
|
|
|
|
func (s *teamService) UpdateTeam(ctx context.Context, orgID, teamID uuid.UUID, data *models.UpdateTeam) (*models.Team, *errx.Error) {
|
|
if data.Name != nil {
|
|
name := strings.TrimSpace(*data.Name)
|
|
if name == "" || len(name) > 255 {
|
|
return nil, errx.New(errx.BadRequest, "team name must be between 1 and 255 characters")
|
|
}
|
|
data.Name = &name
|
|
}
|
|
|
|
team, err := s.repo.UpdateTeam(ctx, orgID, teamID, data)
|
|
if err != nil {
|
|
return nil, toErrx(err)
|
|
}
|
|
return team, nil
|
|
}
|
|
|
|
func (s *teamService) DeleteTeam(ctx context.Context, orgID, teamID uuid.UUID) *errx.Error {
|
|
if err := s.repo.DeleteTeam(ctx, orgID, teamID); err != nil {
|
|
return toErrx(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *teamService) AddMember(ctx context.Context, orgID, teamID, userID uuid.UUID) *errx.Error {
|
|
// The repository validates that the user belongs to the org before the
|
|
// junction insert; surface its typed error verbatim.
|
|
if err := s.repo.AddMember(ctx, orgID, teamID, userID); err != nil {
|
|
return toErrx(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *teamService) RemoveMember(ctx context.Context, orgID, teamID, userID uuid.UUID) *errx.Error {
|
|
if err := s.repo.RemoveMember(ctx, orgID, teamID, userID); err != nil {
|
|
return toErrx(err)
|
|
}
|
|
return nil
|
|
}
|