feat: editor write path

This commit is contained in:
Florian Sylvain committed 2026-11-02 03:24:30 +01:00
1 parent d9158025d4
commit a0b3c58c52
88 files changed
+5738 -263

No files matched your search

+106
View File
@@ -6,6 +6,7 @@ import (
"RenewCMS/internal/domain/language"
"RenewCMS/internal/infrastructure/useCases"
"errors"
"maps"
"slices"
"sort"
"strings"
@@ -30,6 +31,12 @@ type mockArticleRepo struct {
createAlwaysRaces bool
afterFindByBody func()
updateBodyIDs []uint32
moveFailsAt int
beforeCreate func()
beforeUpdate func()
beforeMove func()
onGet func(id uint32) error
reverseStory bool
}
func newMockArticleRepo() *mockArticleRepo {
@@ -210,6 +217,11 @@ func (m *mockArticleRepo) Get(id uint32) (article.Article, error) {
if err := m.fail.trigger("Get"); err != nil {
return article.Article{}, err
}
if m.onGet != nil {
if err := m.onGet(id); err != nil {
return article.Article{}, err
}
}
a, ok := m.articles[id]
if !ok {
return article.Article{}, article.ErrArticleNotFound
@@ -330,6 +342,10 @@ func (m *mockArticleRepo) RepointImage(fromImageIDs []uint32, toImageID uint32)
func (m *mockArticleRepo) Create(a article.Article) (article.Article, error) {
m.createCalled = true
if hook := m.beforeCreate; hook != nil {
m.beforeCreate = nil
hook()
}
if err := m.fail.trigger("Create"); err != nil {
return article.Article{}, err
}
@@ -343,6 +359,16 @@ func (m *mockArticleRepo) Create(a article.Article) (article.Article, error) {
if m.slugUsedOn(a.SiteID, a.Lang, a.Slug, 0) {
return article.Article{}, article.ErrSlugTaken
}
if a.StoryID != 0 {
if !m.storyHasVersions(a.StoryID) {
return article.Article{}, article.ErrArticleNotFound
}
if m.storyHasLanguage(a.StoryID, a.Lang) {
return article.Article{}, article.ErrLanguageTaken
}
}
a.ContentRevision = 1
a.EditRevision = 1
m.nextID++
a.ID = m.nextID
m.articles[a.ID] = a
@@ -373,6 +399,70 @@ func (m *mockArticleRepo) UpdateBody(id uint32, body string) (article.Article, e
return m.withAuthors(a), nil
}
func (m *mockArticleRepo) storyHasVersions(storyID uint32) bool {
for _, stored := range m.articles {
if stored.StoryID == storyID {
return true
}
}
return false
}
func (m *mockArticleRepo) storyHasLanguage(storyID uint32, lang string) bool {
for _, stored := range m.articles {
if stored.StoryID == storyID && stored.Lang == lang {
return true
}
}
return false
}
func (m *mockArticleRepo) ListByStory(storyID uint32) ([]article.Article, error) {
if err := m.fail.trigger("ListByStory"); err != nil {
return nil, err
}
versions := []article.Article{}
for _, id := range m.sortedIds() {
if stored := m.articles[id]; stored.StoryID == storyID {
versions = append(versions, m.withAuthors(stored))
}
}
if m.reverseStory {
slices.Reverse(versions)
}
return versions, nil
}
func (m *mockArticleRepo) MoveToSite(ids []uint32, siteID uint32) error {
if err := m.fail.trigger("MoveToSite"); err != nil {
return err
}
if hook := m.beforeMove; hook != nil {
m.beforeMove = nil
hook()
}
snapshot := maps.Clone(m.articles)
for position, id := range ids {
moving, ok := m.articles[id]
if !ok {
m.articles = snapshot
return article.ErrArticleNotFound
}
if m.moveFailsAt == position+1 {
m.articles = snapshot
return errRepositoryFailure
}
if m.slugUsedOn(siteID, moving.Lang, moving.Slug, id) {
m.articles = snapshot
return article.ErrSlugTaken
}
moving.SiteID = siteID
moving.EditRevision++
m.articles[id] = moving
}
return nil
}
func (m *mockArticleRepo) Update(id uint32, update article.Update) (article.Article, error) {
if err := m.fail.trigger("Update"); err != nil {
return article.Article{}, err
@@ -381,6 +471,18 @@ func (m *mockArticleRepo) Update(id uint32, update article.Update) (article.Arti
if !ok {
return article.Article{}, article.ErrArticleNotFound
}
if hook := m.beforeUpdate; hook != nil {
m.beforeUpdate = nil
hook()
a = m.articles[id]
}
if update.IfRevision != nil && a.EditRevision != *update.IfRevision {
return article.Article{}, &article.RevisionConflictError{Current: a.EditRevision}
}
before := a
if update.Title != nil {
a.Title = *update.Title
}
if update.Body != nil {
if err, failing := m.failUpdateBodyFor[id]; failing {
return article.Article{}, err
@@ -400,9 +502,13 @@ func (m *mockArticleRepo) Update(id uint32, update article.Update) (article.Arti
return article.Article{}, article.ErrSlugTaken
}
applySeoUpdate(&a, update)
authorsChanged := update.AuthorIDs != nil && !slices.Equal(m.credits[id], *update.AuthorIDs)
if update.AuthorIDs != nil {
m.credits[id] = append([]uint32{}, *update.AuthorIDs...)
}
if before.EditRevision > 0 {
a = a.Revised(article.ImpactOf(before, a, authorsChanged))
}
m.articles[id] = a
return m.withAuthors(a), nil
}