diff --git a/mutations_create_post.go b/mutations_create_post.go new file mode 100644 index 0000000..89c5c73 --- /dev/null +++ b/mutations_create_post.go @@ -0,0 +1,68 @@ +package main + +import ( + "fmt" + "strings" +) + +// CreatePost — the canonical "make a new post" mutation. The bump_at is set +// to the current time; the post is added to the postsByBumpedAt index; the +// channels (if any) are added to the post_channels join map. +type CreatePost struct { + AuthorID string + Title string + Body string + Channels []string + + // Set by Validate, used by Apply and AffectedAuthors. + resolvedPostID string +} + +func (m *CreatePost) Kind() string { return "create_post" } + +func (m *CreatePost) AffectedAuthors() []string { + return []string{m.AuthorID} +} + +func (m *CreatePost) 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.Body) == "" { + return &ValidationError{Field: "body", Message: "required"} + } + for _, ch := range m.Channels { + if _, ok := s.GetChannel(ch); !ok { + return &ValidationError{Field: "channels", Message: fmt.Sprintf("unknown channel %q", ch)} + } + } + return nil +} + +func (m *CreatePost) Apply(s *Store, lock LockRegion) error { + now := s.Now() + p := &Post{ + ID: s.NewID(), + AuthorID: m.AuthorID, + Title: m.Title, + Body: m.Body, + BumpedAt: now, + CreatedAt: now, + UpdatedAt: now, + Channels: append([]string(nil), m.Channels...), + } + m.resolvedPostID = p.ID + s.mu.Lock() + defer s.mu.Unlock() + s.posts[p.ID] = p + s.indexInsertPost(p) + if len(m.Channels) > 0 { + s.postChannels[p.ID] = append([]string(nil), m.Channels...) + } + return nil +} + +func (m *CreatePost) ResolvedPostID() string { return m.resolvedPostID }