Files
RenewCMS/internal/infrastructure/useCases/UpdateAuthor.go
T

153 lines
3.9 KiB
Go

package useCases
import (
"RenewCMS/internal/domain/audit"
"RenewCMS/internal/domain/author"
"RenewCMS/internal/domain/image"
"RenewCMS/internal/domain/user"
"context"
"strings"
)
type UpdateAuthorUseCase struct {
authors author.Repository
users user.Repository
images image.Repository
recorder audit.Recorder
}
func NewUpdateAuthorUseCase(authors author.Repository, users user.Repository, images image.Repository, recorder audit.Recorder) *UpdateAuthorUseCase {
return &UpdateAuthorUseCase{authors: authors, users: users, images: images, recorder: recorder}
}
func (u *UpdateAuthorUseCase) Update(ctx context.Context, id uint32, caller AuthorCaller, patch author.Patch) (author.Author, error) {
before, err := u.authors.Get(id)
if err != nil {
return author.Author{}, err
}
event := audit.NewEvent(audit.ActionAuthorEdit, audit.CategoryAuthors)
event.Target = authorTarget(before)
after, err := u.prepare(before, caller, patch)
if err != nil {
recordFailure(ctx, u.recorder, event, err)
return author.Author{}, err
}
changes := authorChanges(u.users, u.images, before, after)
if len(changes) == 0 {
return u.present(before, caller), nil
}
saved, err := u.authors.Update(after)
if err != nil {
recordFailure(ctx, u.recorder, event, err)
return author.Author{}, err
}
event.Target = authorTarget(saved)
event.Changes = changes
u.recorder.Record(ctx, event)
return u.present(saved, caller), nil
}
func (u *UpdateAuthorUseCase) present(a author.Author, caller AuthorCaller) author.Author {
if caller.isAdmin() {
return withUserEmail(u.users, a)
}
a.UserEmail = nil
return a
}
func (u *UpdateAuthorUseCase) prepare(before author.Author, caller AuthorCaller, patch author.Patch) (author.Author, error) {
if err := authorizeEdit(before, caller, patch); err != nil {
return author.Author{}, err
}
after := before
if patch.Name != nil {
after.Name = strings.TrimSpace(*patch.Name)
}
if patch.Bio != nil {
after.Bio = *patch.Bio
}
if patch.Website.Set {
after.Website = patch.Website.Value
}
if patch.SocialLinks != nil {
after.SocialLinks = *patch.SocialLinks
}
website, err := validateAuthorContent(after.Bio, after.Website, after.SocialLinks)
if err != nil {
return author.Author{}, err
}
after.Website = website
if err := u.applySlug(&after, patch); err != nil {
return author.Author{}, err
}
if err := u.applyAvatar(&after, patch); err != nil {
return author.Author{}, err
}
return after, u.applyLink(&after, patch)
}
func authorizeEdit(before author.Author, caller AuthorCaller, patch author.Patch) error {
if caller.isAdmin() {
return nil
}
if before.UserID == nil || *before.UserID != caller.UserID || patch.UserID.Set {
return author.ErrAuthorRights
}
return nil
}
func (u *UpdateAuthorUseCase) applySlug(after *author.Author, patch author.Patch) error {
if patch.Slug == nil || *patch.Slug == after.Slug {
return nil
}
if !author.IsValidSlug(*patch.Slug) {
return author.ErrInvalidSlug
}
taken, err := isSlugTaken(u.authors, *patch.Slug)
if err != nil {
return err
}
if taken {
return author.ErrSlugTaken
}
after.Slug = *patch.Slug
return nil
}
func (u *UpdateAuthorUseCase) applyAvatar(after *author.Author, patch author.Patch) error {
if !patch.AvatarID.Set {
return nil
}
if err := ensureImageExists(u.images, patch.AvatarID.Value); err != nil {
return err
}
after.AvatarID = patch.AvatarID.Value
return nil
}
func (u *UpdateAuthorUseCase) applyLink(after *author.Author, patch author.Patch) error {
if !patch.UserID.Set {
return nil
}
requested := patch.UserID.Value
if requested == nil {
after.UserID = nil
after.IsLinked = false
return nil
}
if after.UserID != nil {
if *after.UserID == *requested {
return nil
}
return author.ErrAuthorAlreadyLinked
}
if err := ensureUserCanBeLinked(u.users, u.authors, *requested); err != nil {
return err
}
after.UserID = requested
after.IsLinked = true
return nil
}