Refactor getting all namespaces to use a map for easier handling of pseudo namespaces
continuous-integration/drone/pr Build is failing Details

This commit is contained in:
kolaente 2020-09-07 17:14:59 +02:00
parent 9895f8ad66
commit 9469f38d79
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
3 changed files with 78 additions and 48 deletions

View File

@ -21,6 +21,7 @@ import (
"code.vikunja.io/api/pkg/user" "code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
"github.com/imdario/mergo" "github.com/imdario/mergo"
"sort"
"time" "time"
"xorm.io/builder" "xorm.io/builder"
) )
@ -53,8 +54,8 @@ type Namespace struct {
web.Rights `xorm:"-" json:"-"` web.Rights `xorm:"-" json:"-"`
} }
// PseudoNamespace is a pseudo namespace used to hold shared lists // SharedListsPseudoNamespace is a pseudo namespace used to hold shared lists
var PseudoNamespace = Namespace{ var SharedListsPseudoNamespace = Namespace{
ID: -1, ID: -1,
Title: "Shared Lists", Title: "Shared Lists",
Description: "Lists of other users shared with you via teams or directly.", Description: "Lists of other users shared with you via teams or directly.",
@ -71,6 +72,15 @@ var FavoritesPseudoNamespace = Namespace{
Updated: time.Now(), Updated: time.Now(),
} }
// SavedFiltersPseudoNamespace is a pseudo namespace used to hold saved filters
var SavedFiltersPseudoNamespace = Namespace{
ID: -3,
Title: "Filters",
Description: "Saved filters.",
Created: time.Now(),
Updated: time.Now(),
}
// TableName makes beautiful table names // TableName makes beautiful table names
func (Namespace) TableName() string { func (Namespace) TableName() string {
return "namespaces" return "namespaces"
@ -84,7 +94,7 @@ func (n *Namespace) GetSimpleByID() (err error) {
// Get the namesapce with shared lists // Get the namesapce with shared lists
if n.ID == -1 { if n.ID == -1 {
*n = PseudoNamespace *n = SharedListsPseudoNamespace
return return
} }
@ -179,13 +189,18 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
return nil, 0, 0, ErrGenericForbidden{} return nil, 0, 0, ErrGenericForbidden{}
} }
// This map will hold all namespaces and their lists. The key is usually the id of the namespace.
// We're using a map here because it makes a few things like adding lists or removing pseudo namespaces easier.
namespaces := make(map[int64]*NamespaceWithLists)
//////////////////////////////
// Lists with their namespaces
doer, err := user.GetFromAuth(a) doer, err := user.GetFromAuth(a)
if err != nil { if err != nil {
return nil, 0, 0, err return nil, 0, 0, err
} }
all := []*NamespaceWithLists{}
// Adding a 1=1 condition by default here because xorm always needs a condition and cannot handle nil conditions // Adding a 1=1 condition by default here because xorm always needs a condition and cannot handle nil conditions
var isArchivedCond builder.Cond = builder.Eq{"1": 1} var isArchivedCond builder.Cond = builder.Eq{"1": 1}
if !n.IsArchived { if !n.IsArchived {
@ -194,26 +209,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
) )
} }
// Create our pseudo namespace with favorite lists
// We want this one at the beginning, which is why we create it here
pseudoFavoriteNamespace := FavoritesPseudoNamespace
pseudoFavoriteNamespace.Owner = doer
all = append(all, &NamespaceWithLists{
Namespace: pseudoFavoriteNamespace,
Lists: []*List{{}},
})
*all[0].Lists[0] = FavoritesPseudoList // Copying the list to be able to modify it later
// Create our pseudo namespace to hold the shared lists
pseudonamespace := PseudoNamespace
pseudonamespace.Owner = doer
all = append(all, &NamespaceWithLists{
pseudonamespace,
[]*List{},
})
limit, start := getLimitFromPageIndex(page, perPage) limit, start := getLimitFromPageIndex(page, perPage)
query := x.Select("namespaces.*"). query := x.Select("namespaces.*").
Table("namespaces"). Table("namespaces").
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id"). Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
@ -228,15 +224,15 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
if limit > 0 { if limit > 0 {
query = query.Limit(limit, start) query = query.Limit(limit, start)
} }
err = query.Find(&all) err = query.Find(&namespaces)
if err != nil { if err != nil {
return all, 0, 0, err return nil, 0, 0, err
} }
// Make a list of namespace ids // Make a list of namespace ids
var namespaceids []int64 var namespaceids []int64
var userIDs []int64 var userIDs []int64
for _, nsp := range all { for _, nsp := range namespaces {
namespaceids = append(namespaceids, nsp.ID) namespaceids = append(namespaceids, nsp.ID)
userIDs = append(userIDs, nsp.OwnerID) userIDs = append(userIDs, nsp.OwnerID)
} }
@ -245,7 +241,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
userMap := make(map[int64]*user.User) userMap := make(map[int64]*user.User)
err = x.In("id", userIDs).Find(&userMap) err = x.In("id", userIDs).Find(&userMap)
if err != nil { if err != nil {
return all, 0, 0, err return nil, 0, 0, err
} }
// Get all lists // Get all lists
@ -258,7 +254,18 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
} }
err = listQuery.Find(&lists) err = listQuery.Find(&lists)
if err != nil { if err != nil {
return all, 0, 0, err return nil, 0, 0, err
}
///////////////
// Shared Lists
// Create our pseudo namespace to hold the shared lists
sharedListsPseudonamespace := SharedListsPseudoNamespace
sharedListsPseudonamespace.Owner = doer
namespaces[sharedListsPseudonamespace.ID] = &NamespaceWithLists{
sharedListsPseudonamespace,
[]*List{},
} }
// Get all lists individually shared with our user (not via a namespace) // Get all lists individually shared with our user (not via a namespace)
@ -287,9 +294,9 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
lists = append(lists, l) lists = append(lists, l)
} }
// Remove the pseudonamespace if we don't have any shared lists // Remove the sharedListsPseudonamespace if we don't have any shared lists
if len(individualLists) == 0 { if len(individualLists) == 0 {
all = append(all[:1], all[2:]...) delete(namespaces, sharedListsPseudonamespace.ID)
} }
// More details for the lists // More details for the lists
@ -298,22 +305,23 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
return nil, 0, 0, err return nil, 0, 0, err
} }
nMap := make(map[int64]*NamespaceWithLists, len(all)) /////////////////
// Favorite lists
// Put objects in our namespace list // Create our pseudo namespace with favorite lists
for _, n := range all { pseudoFavoriteNamespace := FavoritesPseudoNamespace
pseudoFavoriteNamespace.Owner = doer
// Users namespaces[pseudoFavoriteNamespace.ID] = &NamespaceWithLists{
n.Owner = userMap[n.OwnerID] Namespace: pseudoFavoriteNamespace,
Lists: []*List{{}},
nMap[n.ID] = n
} }
*namespaces[pseudoFavoriteNamespace.ID].Lists[0] = FavoritesPseudoList // Copying the list to be able to modify it later
for _, list := range lists { for _, list := range lists {
if list.IsFavorite { if list.IsFavorite {
nMap[pseudoFavoriteNamespace.ID].Lists = append(nMap[pseudoFavoriteNamespace.ID].Lists, list) namespaces[pseudoFavoriteNamespace.ID].Lists = append(namespaces[pseudoFavoriteNamespace.ID].Lists, list)
} }
nMap[list.NamespaceID].Lists = append(nMap[list.NamespaceID].Lists, list) namespaces[list.NamespaceID].Lists = append(namespaces[list.NamespaceID].Lists, list)
} }
// Check if we have any favorites or favorited lists and remove the favorites namespace from the list if not // Check if we have any favorites or favorited lists and remove the favorites namespace from the list if not
@ -329,19 +337,24 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
// If we don't have any favorites in the favorites pseudo list, remove that pseudo list from the namespace // If we don't have any favorites in the favorites pseudo list, remove that pseudo list from the namespace
if favoriteCount == 0 { if favoriteCount == 0 {
for in, l := range nMap[pseudoFavoriteNamespace.ID].Lists { for in, l := range namespaces[pseudoFavoriteNamespace.ID].Lists {
if l.ID == FavoritesPseudoList.ID { if l.ID == FavoritesPseudoList.ID {
nMap[pseudoFavoriteNamespace.ID].Lists = append(nMap[pseudoFavoriteNamespace.ID].Lists[:in], nMap[pseudoFavoriteNamespace.ID].Lists[in+1:]...) namespaces[pseudoFavoriteNamespace.ID].Lists = append(namespaces[pseudoFavoriteNamespace.ID].Lists[:in], namespaces[pseudoFavoriteNamespace.ID].Lists[in+1:]...)
break break
} }
} }
} }
// If we don't have any favorites in the namespace, remove it // If we don't have any favorites in the namespace, remove it
if len(nMap[pseudoFavoriteNamespace.ID].Lists) == 0 { if len(namespaces[pseudoFavoriteNamespace.ID].Lists) == 0 {
all = append(all[:0], all[1:]...) delete(namespaces, pseudoFavoriteNamespace.ID)
} }
/////////////////
// Saved Filters
// TODO
numberOfTotalItems, err = x. numberOfTotalItems, err = x.
Table("namespaces"). Table("namespaces").
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id"). Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
@ -355,9 +368,20 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
Where("namespaces.title LIKE ?", "%"+search+"%"). Where("namespaces.title LIKE ?", "%"+search+"%").
Count(&NamespaceWithLists{}) Count(&NamespaceWithLists{})
if err != nil { if err != nil {
return all, 0, 0, err return nil, 0, 0, err
} }
//////////////////////
// Put it all together
all := make([]*NamespaceWithLists, 0, len(namespaces))
for _, n := range namespaces {
n.Owner = userMap[n.OwnerID]
all = append(all, n)
}
sort.Slice(all, func(i, j int) bool {
return all[i].ID < all[j].ID
})
return all, len(all), numberOfTotalItems, nil return all, len(all), numberOfTotalItems, nil
} }

View File

@ -86,7 +86,13 @@ func (s *SavedFilter) Create(auth web.Auth) error {
} }
func getSavedFilterSimpleByID(id int64) (s *SavedFilter, err error) { func getSavedFilterSimpleByID(id int64) (s *SavedFilter, err error) {
_, err = x.Where("id = ?", id).Get(s) exists, err := x.Where("id = ?", id).Get(s)
if err != nil {
return nil, err
}
if !exists {
return nil, ErrSavedFilterDoesNotExist{SavedFilterID: id}
}
return return
} }

View File

@ -69,7 +69,7 @@ func getNamespace(c echo.Context) (namespace *models.Namespace, err error) {
} }
if namespaceID == -1 { if namespaceID == -1 {
namespace = &models.PseudoNamespace namespace = &models.SharedListsPseudoNamespace
return return
} }