refac: migration crash, real-database tests, site deleted during create, ruels out of the repo, lists

This commit is contained in:
Florian Sylvain committed 2026-11-03 05:06:04 +01:00
1 parent 680b9a1308
commit 9d284e65a9
62 files changed
+1858 -987

No files matched your search

@@ -0,0 +1,36 @@
package useCases
import (
"RenewCMS/internal/domain/language"
"RenewCMS/internal/domain/transaction"
"context"
)
type ConfirmDefaultLanguageUseCase struct {
transactor transaction.Transactor
}
func NewConfirmDefaultLanguageUseCase(transactor transaction.Transactor) *ConfirmDefaultLanguageUseCase {
return &ConfirmDefaultLanguageUseCase{transactor}
}
func (u *ConfirmDefaultLanguageUseCase) Confirm(ctx context.Context, siteID uint32) error {
return u.transactor.Run(ctx, func(tx transaction.Repositories) error {
languages, err := tx.Languages.LockSiteLanguages(siteID)
if err != nil {
return err
}
defaultLanguage, found := language.DefaultOf(languages)
if !found {
return language.ErrNoDefaultLanguage
}
if defaultLanguage.Confirmed {
return nil
}
if err := tx.Languages.MarkConfirmed(siteID, defaultLanguage.Tag); err != nil {
return err
}
_, err = tx.Articles.AssignUnassignedLanguage(siteID, defaultLanguage.Tag)
return err
})
}
@@ -124,10 +124,14 @@ func (g *CreateArticleUseCase) createWithFreeSlug(ctx context.Context, requested
func (g *CreateArticleUseCase) createInTransaction(ctx context.Context, requested string, draft article.Article) (article.Article, error) {
var created article.Article
err := g.transactor.Run(ctx, func(tx transaction.Repositories) error {
lang, err := tx.Languages.LockStartingLanguage(draft.SiteID)
languages, err := tx.Languages.LockSiteLanguages(draft.SiteID)
if errors.Is(err, site.ErrSiteNotFound) {
return article.ErrTargetSiteDeleted
}
if err != nil {
return err
}
lang := language.StartingTag(languages)
if requested != language.Unassigned {
lang, err = chooseLanguage(tx.Localization, draft.SiteID, requested)
if err != nil {
@@ -8,6 +8,7 @@ import (
"RenewCMS/internal/domain/localization"
"RenewCMS/internal/domain/role"
"slices"
"strings"
"time"
)
@@ -64,12 +65,8 @@ func (u *ListArticlesUseCase) ListStoriesPage(command ListStoriesCommand) (Story
if err != nil {
return StoryRowsPage{}, err
}
if command.SEO != "" {
allowed, err := u.versionsWithSEOStatus(command, filters, summaries)
if err != nil {
return StoryRowsPage{}, err
}
filters.VersionIDs = &allowed
if filters.VersionIDs, err = u.versionRestriction(command, filters, summaries); err != nil {
return StoryRowsPage{}, err
}
counts, err := u.articleRepository.CountStories(filters)
if err != nil {
@@ -88,7 +85,7 @@ func (u *ListArticlesUseCase) ListStoriesPage(command ListStoriesCommand) (Story
if len(found.Refs) == 0 {
return empty, nil
}
rows, err := u.storyRows(command, found.Refs, creatable, owners)
rows, err := u.storyRows(command, filters, found.Refs, creatable, storyVersionsByID(visibleVersions), summaries)
if err != nil {
return StoryRowsPage{}, err
}
@@ -105,7 +102,6 @@ func storyFilters(command ListStoriesCommand, visibility articleVisibility, skip
Missing: command.Tab == article.TabMissing,
Status: command.Status,
AuthorID: command.Author,
Q: command.Q,
Sort: command.Sort,
Page: command.Page,
Size: command.Size,
@@ -121,23 +117,39 @@ func (u *ListArticlesUseCase) summarizeVisibleVersions(visibility articleVisibil
return summaries, found.Items, err
}
func (u *ListArticlesUseCase) versionsWithSEOStatus(command ListStoriesCommand, filters article.StoryFilters, summaries map[uint32]article.SEOSummary) ([]uint32, error) {
narrowed := article.Filters{VisibleTo: filters.VisibleTo, SiteID: filters.SiteID, AuthorID: filters.AuthorID, Q: filters.Q, Unpaged: true}
if filters.OnlyOnline {
online := true
narrowed.IsOnline = &online
func (u *ListArticlesUseCase) versionRestriction(command ListStoriesCommand, filters article.StoryFilters, summaries map[uint32]article.SEOSummary) (*[]uint32, error) {
var allowed *[]uint32
if query := strings.TrimSpace(command.Q); query != "" {
matching, err := u.articleRepository.FindTitleMatches(query, filters)
if err != nil {
return nil, err
}
allowed = &matching
}
found, err := u.articleRepository.FindPage(narrowed)
if err != nil {
return nil, err
if command.SEO == "" {
return allowed, nil
}
allowed := []uint32{}
for _, version := range found.Items {
if summaries[version.ID].Status == command.SEO {
allowed = append(allowed, version.ID)
matchingTitle := map[uint32]bool{}
if allowed != nil {
for _, id := range *allowed {
matchingTitle[id] = true
}
}
return allowed, nil
withStatus := []uint32{}
for id, summary := range summaries {
if summary.Status == command.SEO && (allowed == nil || matchingTitle[id]) {
withStatus = append(withStatus, id)
}
}
return &withStatus, nil
}
func storyVersionsByID(versions []article.Article) map[uint32]article.Article {
byID := make(map[uint32]article.Article, len(versions))
for _, version := range versions {
byID[version.ID] = version
}
return byID
}
func countSuggestedStories(versions []article.Article, summaries map[uint32]article.SEOSummary) int {
@@ -156,12 +168,12 @@ func countSuggestedStories(versions []article.Article, summaries map[uint32]arti
return len(stories) + standalone
}
func (u *ListArticlesUseCase) storyRows(command ListStoriesCommand, refs []article.StoryRef, creatable bool, owners *siteOwners) ([]StoryRow, error) {
func (u *ListArticlesUseCase) storyRows(command ListStoriesCommand, filters article.StoryFilters, refs []article.StoryRef, creatable bool, visible map[uint32]article.Article, summaries map[uint32]article.SEOSummary) ([]StoryRow, error) {
storyIDs := make([]uint32, 0, len(refs))
for _, ref := range refs {
storyIDs = append(storyIDs, ref.StoryID)
}
loaded, err := u.articleRepository.ListByStories(storyIDs)
loaded, err := u.articleRepository.ListByStories(storyIDs, filters)
if err != nil {
return nil, err
}
@@ -174,8 +186,9 @@ func (u *ListArticlesUseCase) storyRows(command ListStoriesCommand, refs []artic
representatives := make([]article.Article, 0, len(refs))
for _, ref := range refs {
versions := byStory[ref.StoryID]
representative, found := versionByID(versions, ref.ArticleID)
if !found {
_, listed := versionByID(versions, ref.ArticleID)
representative, found := visible[ref.ArticleID]
if !listed || !found {
continue
}
languages, cached := siteRows[representative.SiteID]
@@ -188,10 +201,6 @@ func (u *ListArticlesUseCase) storyRows(command ListStoriesCommand, refs []artic
rows = append(rows, buildStoryRow(command.Caller, representative, versions, languages, creatable))
representatives = append(representatives, representative)
}
summaries, err := u.summarize(representatives, owners)
if err != nil {
return nil, err
}
rendered, err := renderArticles(u.bodyRenderer, representatives, command.Format)
if err != nil {
return nil, err
@@ -37,6 +37,7 @@ var failureReasons = []failureReason{
{article.ErrSourceOtherStory, "The chosen source version belongs to another article."},
{article.ErrStoryMoveRequired, "The article has other versions, so the whole story must be moved."},
{article.ErrSlugConflict, "An address of the article is already used on the destination site."},
{article.ErrTargetSiteDeleted, "The site was deleted while the article was being created."},
{image.ErrImageTypeNotAllowed, "The file must be a PNG, JPEG, WEBP or SVG image."},
{image.ErrImageTooLarge, "The file is too large to upload."},
{image.ErrImageUnreadable, "The image could not be read."},