feat(mutation): AddComment with path enumeration, bumps post
This commit is contained in:
parent
425f104bc0
commit
b3b0aba5e5
1 changed files with 116 additions and 0 deletions
116
mutations_add_comment.go
Normal file
116
mutations_add_comment.go
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddComment — appends a comment to a post. Bumps the post. Computes the
|
||||||
|
// comment path from the parent comment's path. Pre-validated; the lock is
|
||||||
|
// held during apply so the path computation + index update is atomic.
|
||||||
|
type AddComment struct {
|
||||||
|
PostID string
|
||||||
|
ParentID string // empty for top-level
|
||||||
|
AuthorID string
|
||||||
|
Body string
|
||||||
|
|
||||||
|
// Set by Apply.
|
||||||
|
resolvedCommentID string
|
||||||
|
resolvedPath string
|
||||||
|
resolvedDepth int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AddComment) Kind() string { return "add_comment" }
|
||||||
|
|
||||||
|
func (m *AddComment) AffectedAuthors() []string {
|
||||||
|
return []string{m.AuthorID}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AddComment) Validate(s *Store) error {
|
||||||
|
if strings.TrimSpace(m.AuthorID) == "" {
|
||||||
|
return &ValidationError{Field: "author_id", Message: "required"}
|
||||||
|
}
|
||||||
|
if _, ok := s.GetAuthor(m.AuthorID); !ok {
|
||||||
|
return &ValidationError{Field: "author_id", Message: fmt.Sprintf("unknown author %q", m.AuthorID)}
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(m.PostID) == "" {
|
||||||
|
return &ValidationError{Field: "post_id", Message: "required"}
|
||||||
|
}
|
||||||
|
post, ok := s.GetPost(m.PostID)
|
||||||
|
if !ok {
|
||||||
|
return &ValidationError{Field: "post_id", Message: fmt.Sprintf("unknown post %q", m.PostID)}
|
||||||
|
}
|
||||||
|
if post.IsDeleted {
|
||||||
|
return &ValidationError{Field: "post_id", Message: "post is deleted"}
|
||||||
|
}
|
||||||
|
if m.ParentID != "" {
|
||||||
|
comments := s.ListComments(m.PostID)
|
||||||
|
found := false
|
||||||
|
for _, c := range comments {
|
||||||
|
if c.ID == m.ParentID && !c.IsDeleted {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return &ValidationError{Field: "parent_id", Message: fmt.Sprintf("unknown parent %q on post %q", m.ParentID, m.PostID)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(m.Body) == "" {
|
||||||
|
return &ValidationError{Field: "body", Message: "required"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AddComment) Apply(s *Store, lock LockRegion) error {
|
||||||
|
now := s.Now()
|
||||||
|
id := s.NewID()
|
||||||
|
|
||||||
|
// Compute path under the lock. Look up parent's path if any.
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
var path string
|
||||||
|
var depth int
|
||||||
|
if m.ParentID != "" {
|
||||||
|
parent, ok := s.comments[m.ParentID]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("parent comment disappeared mid-apply: %s", m.ParentID)
|
||||||
|
}
|
||||||
|
path = parent.Path + "." + id
|
||||||
|
depth = parent.Depth + 1
|
||||||
|
} else {
|
||||||
|
path = id
|
||||||
|
depth = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Comment{
|
||||||
|
ID: id,
|
||||||
|
PostID: m.PostID,
|
||||||
|
ParentID: m.ParentID,
|
||||||
|
Path: path,
|
||||||
|
Depth: depth,
|
||||||
|
AuthorID: m.AuthorID,
|
||||||
|
Body: m.Body,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
}
|
||||||
|
m.resolvedCommentID = id
|
||||||
|
m.resolvedPath = path
|
||||||
|
m.resolvedDepth = depth
|
||||||
|
s.comments[id] = c
|
||||||
|
s.indexInsertComment(c)
|
||||||
|
|
||||||
|
// Bump the post.
|
||||||
|
post, ok := s.posts[m.PostID]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("post disappeared mid-apply: %s", m.PostID)
|
||||||
|
}
|
||||||
|
post.BumpedAt = now
|
||||||
|
post.UpdatedAt = now
|
||||||
|
s.indexUpdatePost(post)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AddComment) ResolvedCommentID() string { return m.resolvedCommentID }
|
||||||
|
func (m *AddComment) ResolvedPath() string { return m.resolvedPath }
|
||||||
|
func (m *AddComment) ResolvedDepth() int { return m.resolvedDepth }
|
||||||
Loading…
Add table
Add a link
Reference in a new issue