refactor(notifications): refresh users via an explicit type switch
Reflection over reflect.Kind was overkill: only top-level doer/assignee/ member fields are ever rendered, and the walk forced an exhaustive linter exclusion. List the user fields per notification type instead, which drops the reflect dependency and the .golangci.yml carve-out.
This commit is contained in:
parent
7f53be4105
commit
aac4dd845e
|
|
@ -80,9 +80,6 @@ linters:
|
||||||
- linters:
|
- linters:
|
||||||
- exhaustive
|
- exhaustive
|
||||||
path: pkg/models/task_collection_filter\.go
|
path: pkg/models/task_collection_filter\.go
|
||||||
- linters:
|
|
||||||
- exhaustive
|
|
||||||
path: pkg/models/notifications_refresh\.go
|
|
||||||
- linters:
|
- linters:
|
||||||
- gosec
|
- gosec
|
||||||
path: pkg/utils/random_string\.go
|
path: pkg/utils/random_string\.go
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/notifications"
|
"code.vikunja.io/api/pkg/notifications"
|
||||||
|
|
@ -27,16 +26,12 @@ import (
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// maxNotificationUserRefreshDepth bounds the reflection walk so an unexpectedly
|
// refreshNotificationsUsers reloads each notification's embedded users from the
|
||||||
// deep payload cannot recurse without end.
|
// database. Notifications serialized before the acting user was resolved with
|
||||||
const maxNotificationUserRefreshDepth = 8
|
// its full profile (#2720) stored only id+username, so without this they keep
|
||||||
|
// rendering the auto-generated username instead of the display name. It runs at
|
||||||
// refreshNotificationsUsers reloads every embedded user of each notification
|
// read time and is not persisted; one cache is shared across the batch so a
|
||||||
// from the database. Notifications serialized before the acting user was
|
// user recurring across notifications is fetched only once.
|
||||||
// resolved with its full profile (#2720) stored only id+username, so without
|
|
||||||
// this they keep rendering the auto-generated username instead of the display
|
|
||||||
// name. It runs at read time and is not persisted; one cache is shared across
|
|
||||||
// the batch so a user recurring across notifications is fetched only once.
|
|
||||||
func refreshNotificationsUsers(s *xorm.Session, dbNotifications []*notifications.DatabaseNotification) {
|
func refreshNotificationsUsers(s *xorm.Session, dbNotifications []*notifications.DatabaseNotification) {
|
||||||
cache := make(map[int64]*user.User)
|
cache := make(map[int64]*user.User)
|
||||||
for _, dbn := range dbNotifications {
|
for _, dbn := range dbNotifications {
|
||||||
|
|
@ -60,40 +55,30 @@ func refreshNotificationUsers(s *xorm.Session, dbn *notifications.DatabaseNotifi
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshUsersInValue(s, reflect.ValueOf(typed), cache, 0)
|
for _, u := range notificationUsers(typed) {
|
||||||
|
refreshUser(s, u, cache)
|
||||||
|
}
|
||||||
dbn.Notification = typed
|
dbn.Notification = typed
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshUsersInValue(s *xorm.Session, v reflect.Value, cache map[int64]*user.User, depth int) {
|
// notificationUsers returns the user fields a stored notification renders, so
|
||||||
if depth > maxNotificationUserRefreshDepth || !v.IsValid() {
|
// they can be reloaded. New notification types carrying a user belong here.
|
||||||
return
|
func notificationUsers(n notifications.Notification) []*user.User {
|
||||||
}
|
switch n := n.(type) {
|
||||||
|
case *TaskCommentNotification:
|
||||||
switch v.Kind() {
|
return []*user.User{n.Doer}
|
||||||
case reflect.Ptr:
|
case *TaskAssignedNotification:
|
||||||
if v.IsNil() {
|
return []*user.User{n.Doer, n.Assignee}
|
||||||
return
|
case *TaskDeletedNotification:
|
||||||
}
|
return []*user.User{n.Doer}
|
||||||
if u, is := v.Interface().(*user.User); is {
|
case *ProjectCreatedNotification:
|
||||||
refreshUser(s, u, cache)
|
return []*user.User{n.Doer}
|
||||||
return
|
case *TeamMemberAddedNotification:
|
||||||
}
|
return []*user.User{n.Doer, n.Member}
|
||||||
refreshUsersInValue(s, v.Elem(), cache, depth+1)
|
case *UserMentionedInTaskNotification:
|
||||||
case reflect.Struct:
|
return []*user.User{n.Doer}
|
||||||
for i := 0; i < v.NumField(); i++ {
|
default:
|
||||||
if !v.Type().Field(i).IsExported() {
|
return nil
|
||||||
continue
|
|
||||||
}
|
|
||||||
refreshUsersInValue(s, v.Field(i), cache, depth+1)
|
|
||||||
}
|
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
refreshUsersInValue(s, v.Index(i), cache, depth+1)
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
for _, key := range v.MapKeys() {
|
|
||||||
refreshUsersInValue(s, v.MapIndex(key), cache, depth+1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue