refac: stale test entries, failing web tests, Postgre backup bug, Hardcoded MySQL table count, Stale Postgre connection in test, own-scope visibility filter, GetAll ignoring its error, JSON tags in use-case types, 31 copies of DESKTOP_QUERY, ProfileView endpoint paths
This commit is contained in:
1 parent
efecdee200
commit
5d6f378fce
87 files changed
+696
-373
No files matched your search
@@ -72,7 +72,11 @@ func (a *AcceptInviteUseCase) recordRefusal(ctx context.Context, event audit.Eve
|
||||
}
|
||||
|
||||
func (a *AcceptInviteUseCase) accept(command AcceptInviteCommand, pending invite.Invite, assigned role.Role, now time.Time) (user.User, error) {
|
||||
if a.emailHasAccount(pending.Email) {
|
||||
hasAccount, err := a.emailHasAccount(pending.Email)
|
||||
if err != nil {
|
||||
return user.User{}, err
|
||||
}
|
||||
if hasAccount {
|
||||
if _, err := findUsableInvite(a.inviteRepository, a.hasher, command.Code, now); err != nil {
|
||||
return user.User{}, err
|
||||
}
|
||||
@@ -122,13 +126,17 @@ func (a *AcceptInviteUseCase) inviterOf(pending invite.Invite) (uint32, error) {
|
||||
return assigner.ID, nil
|
||||
}
|
||||
|
||||
func (a *AcceptInviteUseCase) emailHasAccount(email string) bool {
|
||||
for _, existingUser := range a.userRepository.GetAll() {
|
||||
func (a *AcceptInviteUseCase) emailHasAccount(email string) (bool, error) {
|
||||
users, err := a.userRepository.GetAll()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, existingUser := range users {
|
||||
if strings.EqualFold(existingUser.Email, email) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (a *AcceptInviteUseCase) ensureUsernameFree(username string) error {
|
||||
|
||||
@@ -98,7 +98,11 @@ func (g *CreateInviteUseCase) create(command CreateInviteCommand) (invite.Invite
|
||||
return invite.Invite{}, role.Role{}, err
|
||||
}
|
||||
|
||||
if g.emailHasAccount(email) {
|
||||
hasAccount, err := g.emailHasAccount(email)
|
||||
if err != nil {
|
||||
return invite.Invite{}, role.Role{}, err
|
||||
}
|
||||
if hasAccount {
|
||||
return invite.Invite{}, role.Role{}, invite.ErrEmailHasAccount
|
||||
}
|
||||
|
||||
@@ -133,17 +137,25 @@ func (g *CreateInviteUseCase) create(command CreateInviteCommand) (invite.Invite
|
||||
return createdInvite, assigned, nil
|
||||
}
|
||||
|
||||
func (g *CreateInviteUseCase) emailHasAccount(email string) bool {
|
||||
for _, existingUser := range g.userRepository.GetAll() {
|
||||
func (g *CreateInviteUseCase) emailHasAccount(email string) (bool, error) {
|
||||
users, err := g.userRepository.GetAll()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, existingUser := range users {
|
||||
if strings.EqualFold(existingUser.Email, email) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (g *CreateInviteUseCase) discardOpenInvite(email string, now time.Time) error {
|
||||
for _, existing := range g.inviteRepository.GetAll() {
|
||||
invites, err := g.inviteRepository.GetAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, existing := range invites {
|
||||
if existing.Email != email {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -53,7 +53,11 @@ func (g *CreateUserUseCase) CreateUser(ctx context.Context, createUser CreateUse
|
||||
}
|
||||
|
||||
func (g *CreateUserUseCase) create(createUser CreateUserCommand) (user.User, error) {
|
||||
if g.registrationClosed() {
|
||||
closed, err := g.registrationClosed()
|
||||
if err != nil {
|
||||
return user.User{}, err
|
||||
}
|
||||
if closed {
|
||||
return user.User{}, user.ErrRegistrationClosed
|
||||
}
|
||||
|
||||
@@ -108,15 +112,20 @@ func (g *CreateUserUseCase) create(createUser CreateUserCommand) (user.User, err
|
||||
return createdUser, nil
|
||||
}
|
||||
|
||||
func (g *CreateUserUseCase) IsRegistrationOpen() bool {
|
||||
return !g.registrationClosed()
|
||||
func (g *CreateUserUseCase) IsRegistrationOpen() (bool, error) {
|
||||
closed, err := g.registrationClosed()
|
||||
return !closed, err
|
||||
}
|
||||
|
||||
func (g *CreateUserUseCase) registrationClosed() bool {
|
||||
for _, existingUser := range g.userRepository.GetAll() {
|
||||
func (g *CreateUserUseCase) registrationClosed() (bool, error) {
|
||||
users, err := g.userRepository.GetAll()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, existingUser := range users {
|
||||
if existingUser.IsVerified {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
@@ -34,7 +34,11 @@ func (e *EnforceRetentionUseCase) Tick(now time.Time) (retention.Result, error)
|
||||
|
||||
func (e *EnforceRetentionUseCase) deleteStaleUnverifiedUsers(now time.Time) (int, error) {
|
||||
removed := 0
|
||||
for _, candidate := range e.users.GetAll() {
|
||||
users, err := e.users.GetAll()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, candidate := range users {
|
||||
if !candidate.IsStaleUnverifiedAt(now) {
|
||||
continue
|
||||
}
|
||||
@@ -48,7 +52,11 @@ func (e *EnforceRetentionUseCase) deleteStaleUnverifiedUsers(now time.Time) (int
|
||||
|
||||
func (e *EnforceRetentionUseCase) deleteStaleInvites(now time.Time) (int, error) {
|
||||
removed := 0
|
||||
for _, candidate := range e.invites.GetAll() {
|
||||
invites, err := e.invites.GetAll()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, candidate := range invites {
|
||||
if !candidate.IsStaleAt(now) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -24,7 +24,11 @@ func findUsableInvite(inviteRepository invite.Repository, hasher hash.Hasher, co
|
||||
return invite.Invite{}, invite.ErrInviteInvalid
|
||||
}
|
||||
|
||||
for _, candidate := range inviteRepository.GetAll() {
|
||||
invites, err := inviteRepository.GetAll()
|
||||
if err != nil {
|
||||
return invite.Invite{}, err
|
||||
}
|
||||
for _, candidate := range invites {
|
||||
if candidate.AcceptedAt != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -42,20 +42,42 @@ type ArticlePage struct {
|
||||
func (u *ListArticlesUseCase) ListArticlesPage(command ListArticlesCommand) (ArticlePage, error) {
|
||||
switch command.Caller.Grants.ScopeFor(role.KeyContentRead, builtintype.KeyArticle) {
|
||||
case role.ScopeAll:
|
||||
return u.listVisibleByRepository(command, false)
|
||||
return u.listVisibleByRepository(command, articleVisibility{})
|
||||
case role.ScopeOwn:
|
||||
return u.listOwnedAndOnline(command)
|
||||
return u.listVisibleByRepository(command, articleVisibility{userID: &command.Caller.UserID})
|
||||
default:
|
||||
return u.listVisibleByRepository(command, true)
|
||||
return u.listVisibleByRepository(command, articleVisibility{onlineOnly: true})
|
||||
}
|
||||
}
|
||||
|
||||
func (u *ListArticlesUseCase) listVisibleByRepository(command ListArticlesCommand, onlineOnly bool) (ArticlePage, error) {
|
||||
counts, err := u.articleRepository.CountByVisibility(onlineOnly)
|
||||
type articleVisibility struct {
|
||||
onlineOnly bool
|
||||
userID *uint32
|
||||
}
|
||||
|
||||
func (v articleVisibility) apply(filters article.Filters) article.Filters {
|
||||
if v.onlineOnly {
|
||||
online := true
|
||||
filters.IsOnline = &online
|
||||
}
|
||||
filters.VisibleTo = v.userID
|
||||
return filters
|
||||
}
|
||||
|
||||
func (u *ListArticlesUseCase) countVisible(visibility articleVisibility) (article.Counts, error) {
|
||||
if visibility.userID != nil {
|
||||
return u.articleRepository.CountVisibleTo(*visibility.userID)
|
||||
}
|
||||
return u.articleRepository.CountByVisibility(visibility.onlineOnly)
|
||||
}
|
||||
|
||||
func (u *ListArticlesUseCase) listVisibleByRepository(command ListArticlesCommand, visibility articleVisibility) (ArticlePage, error) {
|
||||
onlineOnly := visibility.onlineOnly
|
||||
counts, err := u.countVisible(visibility)
|
||||
if err != nil {
|
||||
return ArticlePage{}, err
|
||||
}
|
||||
summaries, err := u.summarizeVisible(onlineOnly)
|
||||
summaries, err := u.summarizeVisible(visibility)
|
||||
if err != nil {
|
||||
return ArticlePage{}, err
|
||||
}
|
||||
@@ -64,7 +86,7 @@ func (u *ListArticlesUseCase) listVisibleByRepository(command ListArticlesComman
|
||||
if onlineOnly && command.Status == article.StatusDraft {
|
||||
return page, nil
|
||||
}
|
||||
filters := pageFilters(command, onlineOnly)
|
||||
filters := visibility.apply(pageFilters(command))
|
||||
if command.SiteKey != nil {
|
||||
siteID, known, err := u.siteIDOf(*command.SiteKey)
|
||||
if err != nil {
|
||||
@@ -98,71 +120,6 @@ func emptyArticlePage(command ListArticlesCommand, counts article.Counts) Articl
|
||||
return ArticlePage{Items: []article.Article{}, Page: command.Page, Size: command.Size, Counts: counts, SEO: map[uint32]article.SEOSummary{}}
|
||||
}
|
||||
|
||||
func (u *ListArticlesUseCase) listOwnedAndOnline(command ListArticlesCommand) (ArticlePage, error) {
|
||||
everything, err := u.articleRepository.FindPage(article.Filters{Unpaged: true})
|
||||
if err != nil {
|
||||
return ArticlePage{}, err
|
||||
}
|
||||
visible := visibleTo(command.Caller, everything.Items)
|
||||
summaries, err := u.summarize(visible)
|
||||
if err != nil {
|
||||
return ArticlePage{}, err
|
||||
}
|
||||
counts := tally(visible)
|
||||
counts.Suggestions = countSuggestions(summaries)
|
||||
page := emptyArticlePage(command, counts)
|
||||
filters := pageFilters(command, false)
|
||||
filters.Unpaged = true
|
||||
if command.SiteKey != nil {
|
||||
siteID, known, err := u.siteIDOf(*command.SiteKey)
|
||||
if err != nil {
|
||||
return ArticlePage{}, err
|
||||
}
|
||||
if !known {
|
||||
return page, nil
|
||||
}
|
||||
filters.SiteID = &siteID
|
||||
}
|
||||
found, err := u.articleRepository.FindPage(filters)
|
||||
if err != nil {
|
||||
return ArticlePage{}, err
|
||||
}
|
||||
matching := visibleTo(command.Caller, found.Items)
|
||||
if command.SEO != "" {
|
||||
matching = withSEOStatus(matching, summaries, command.SEO)
|
||||
}
|
||||
rendered, err := renderArticles(u.bodyRenderer, pageSlice(matching, command.Page, command.Size), command.Format)
|
||||
if err != nil {
|
||||
return ArticlePage{}, err
|
||||
}
|
||||
page.Items = rendered
|
||||
page.Total = len(matching)
|
||||
page.SEO = summaries
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func visibleTo(caller ContentCaller, items []article.Article) []article.Article {
|
||||
visible := make([]article.Article, 0, len(items))
|
||||
for _, item := range items {
|
||||
if caller.seesArticle(item) {
|
||||
visible = append(visible, item)
|
||||
}
|
||||
}
|
||||
return visible
|
||||
}
|
||||
|
||||
func tally(visible []article.Article) article.Counts {
|
||||
counts := article.Counts{All: len(visible)}
|
||||
for _, item := range visible {
|
||||
if item.IsOnline {
|
||||
counts.Online++
|
||||
} else {
|
||||
counts.Draft++
|
||||
}
|
||||
}
|
||||
return counts
|
||||
}
|
||||
|
||||
func withSEOStatus(items []article.Article, summaries map[uint32]article.SEOSummary, status string) []article.Article {
|
||||
matching := make([]article.Article, 0, len(items))
|
||||
for _, item := range items {
|
||||
@@ -187,13 +144,8 @@ func pageSlice[T any](items []T, page int, size int) []T {
|
||||
return items[start:min(start+size, len(items))]
|
||||
}
|
||||
|
||||
func (u *ListArticlesUseCase) summarizeVisible(onlineOnly bool) (map[uint32]article.SEOSummary, error) {
|
||||
filters := article.Filters{Unpaged: true}
|
||||
if onlineOnly {
|
||||
online := true
|
||||
filters.IsOnline = &online
|
||||
}
|
||||
found, err := u.articleRepository.FindPage(filters)
|
||||
func (u *ListArticlesUseCase) summarizeVisible(visibility articleVisibility) (map[uint32]article.SEOSummary, error) {
|
||||
found, err := u.articleRepository.FindPage(visibility.apply(article.Filters{Unpaged: true}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -245,11 +197,6 @@ func (u *ListArticlesUseCase) siteIDOf(key string) (uint32, bool, error) {
|
||||
return found.ID, true, nil
|
||||
}
|
||||
|
||||
func pageFilters(command ListArticlesCommand, onlineOnly bool) article.Filters {
|
||||
filters := article.Filters{Page: command.Page, Size: command.Size, Q: command.Q, Status: command.Status, Sort: command.Sort}
|
||||
if onlineOnly {
|
||||
online := true
|
||||
filters.IsOnline = &online
|
||||
}
|
||||
return filters
|
||||
func pageFilters(command ListArticlesCommand) article.Filters {
|
||||
return article.Filters{Page: command.Page, Size: command.Size, Q: command.Q, Status: command.Status, Sort: command.Sort}
|
||||
}
|
||||
@@ -70,7 +70,14 @@ func (u *ListEntriesUseCase) page(owner contenttype.ContentType, command ListEnt
|
||||
if query == "" && readScope == role.ScopeAll {
|
||||
return u.entries.ListPage(entry.Filters{ContentTypeID: owner.ID, Page: command.Page, Size: command.Size, Sort: command.Sort})
|
||||
}
|
||||
all, err := u.entries.ListPage(entry.Filters{ContentTypeID: owner.ID, Unpaged: true, Sort: command.Sort})
|
||||
if query == "" && readScope == role.ScopeOwn {
|
||||
return u.entries.ListPage(entry.Filters{ContentTypeID: owner.ID, CreatedBy: &command.Caller.UserID, Page: command.Page, Size: command.Size, Sort: command.Sort})
|
||||
}
|
||||
filters := entry.Filters{ContentTypeID: owner.ID, Unpaged: true, Sort: command.Sort}
|
||||
if readScope == role.ScopeOwn {
|
||||
filters.CreatedBy = &command.Caller.UserID
|
||||
}
|
||||
all, err := u.entries.ListPage(filters)
|
||||
if err != nil {
|
||||
return entry.PageResult{}, err
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ func NewListUsersUseCase(userRepository user.Repository) *ListUsersUseCase {
|
||||
return &ListUsersUseCase{userRepository}
|
||||
}
|
||||
|
||||
func (g *ListUsersUseCase) ListUsers() []user.User {
|
||||
func (g *ListUsersUseCase) ListUsers() ([]user.User, error) {
|
||||
return g.userRepository.GetAll()
|
||||
}
|
||||
@@ -21,8 +21,8 @@ type ListUsersAndInvitesUseCase struct {
|
||||
}
|
||||
|
||||
type RoleAssigner struct {
|
||||
ID uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ID uint32
|
||||
Name string
|
||||
}
|
||||
|
||||
func newRoleAssigner(account user.User) *RoleAssigner {
|
||||
@@ -89,7 +89,10 @@ func (g *ListUsersAndInvitesUseCase) ListUsersAndInvites() ([]DirectoryEntry, er
|
||||
now := time.Now()
|
||||
entries := []DirectoryEntry{}
|
||||
|
||||
users := g.userRepository.GetAll()
|
||||
users, err := g.userRepository.GetAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loaded := make(map[uint32]user.User, len(users))
|
||||
for _, existingUser := range users {
|
||||
loaded[existingUser.ID] = existingUser
|
||||
@@ -98,7 +101,11 @@ func (g *ListUsersAndInvitesUseCase) ListUsersAndInvites() ([]DirectoryEntry, er
|
||||
entries = append(entries, userEntry(existingUser, lookupRoleRef(refs, existingUser.RoleID), assignerAmong(loaded, existingUser.RoleAssignedByID)))
|
||||
}
|
||||
|
||||
for _, existingInvite := range g.inviteRepository.GetAll() {
|
||||
invites, err := g.inviteRepository.GetAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, existingInvite := range invites {
|
||||
status := existingInvite.StatusAt(now)
|
||||
if status == invite.StatusAccepted {
|
||||
continue
|
||||
|
||||
@@ -21,19 +21,19 @@ type TransferOwnershipCommand struct {
|
||||
}
|
||||
|
||||
type OwnershipSuccessor struct {
|
||||
ID uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ID uint32
|
||||
Name string
|
||||
}
|
||||
|
||||
type OwnershipPredecessor struct {
|
||||
ID uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Role RoleRef `json:"role"`
|
||||
ID uint32
|
||||
Name string
|
||||
Role RoleRef
|
||||
}
|
||||
|
||||
type OwnershipTransfer struct {
|
||||
Owner OwnershipSuccessor `json:"owner"`
|
||||
FormerOwner OwnershipPredecessor `json:"former_owner"`
|
||||
Owner OwnershipSuccessor
|
||||
FormerOwner OwnershipPredecessor
|
||||
}
|
||||
|
||||
func NewTransferOwnershipUseCase(users user.Repository, roles role.Repository, recorder audit.Recorder) *TransferOwnershipUseCase {
|
||||
|
||||
@@ -3,7 +3,11 @@ package useCases
|
||||
import "RenewCMS/internal/domain/invite"
|
||||
|
||||
func findManageableInvite(inviteRepository invite.Repository, id uint32) (invite.Invite, error) {
|
||||
for _, existing := range inviteRepository.GetAll() {
|
||||
invites, err := inviteRepository.GetAll()
|
||||
if err != nil {
|
||||
return invite.Invite{}, err
|
||||
}
|
||||
for _, existing := range invites {
|
||||
if existing.ID != id {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ type InviteView struct {
|
||||
}
|
||||
|
||||
type RoleRef struct {
|
||||
ID uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
BuiltIn bool `json:"built_in"`
|
||||
Locked bool `json:"locked"`
|
||||
ID uint32
|
||||
Name string
|
||||
BuiltIn bool
|
||||
Locked bool
|
||||
}
|
||||
|
||||
func NewRoleRef(found role.Role) RoleRef {
|
||||
|
||||
@@ -9,15 +9,15 @@ import (
|
||||
)
|
||||
|
||||
type RoleEditor struct {
|
||||
ID uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ID uint32
|
||||
Name string
|
||||
}
|
||||
|
||||
type RoleView struct {
|
||||
role.Role
|
||||
Members int `json:"members"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
UpdatedBy *RoleEditor `json:"updated_by"`
|
||||
Members int
|
||||
UpdatedAt *time.Time
|
||||
UpdatedBy *RoleEditor
|
||||
}
|
||||
|
||||
type roleViewer struct {
|
||||
|
||||
Reference in new issue
Block a user