chore: minor refactorings to shared data middleware logic

This commit is contained in:
Ferdinand Mütsch
2025-09-12 16:33:59 +02:00
parent 861114e55b
commit bff7ed403b
8 changed files with 61 additions and 54 deletions

View File

@@ -38,13 +38,10 @@ const (
KeySubscriptionNotificationSent = "sub_reminder" KeySubscriptionNotificationSent = "sub_reminder"
KeyNewsbox = "newsbox" KeyNewsbox = "newsbox"
KeyInviteCode = "invite" KeyInviteCode = "invite"
KeySharedData = "shared_data"
SessionKeyDefault = "default" SessionKeyDefault = "default"
MiddlewareKeySharedData = "shared_data"
MiddlewareKeyPrincipal = "principal"
MiddlewareKeyPrincipalId = "principal_identity"
SimpleDateFormat = "2006-01-02" SimpleDateFormat = "2006-01-02"
SimpleDateTimeFormat = "2006-01-02 15:04:05" SimpleDateTimeFormat = "2006-01-02 15:04:05"

View File

@@ -113,6 +113,7 @@ func initSentry(config sentryConfig, debug bool, releaseVersion string) {
func getPrincipal(r *http.Request) string { func getPrincipal(r *http.Request) string {
sharedData := r.Context().Value(MiddlewareKeySharedData) sharedData := r.Context().Value(MiddlewareKeySharedData)
if sharedData == nil { if sharedData == nil {
Log().Request(r).Error("request shared data not set while retrieving principal for sentry logging")
return "" return ""
} }
val := sharedData.(*SharedData).MustGet(MiddlewareKeyPrincipalId) val := sharedData.(*SharedData).MustGet(MiddlewareKeyPrincipalId)

View File

@@ -1,11 +1,20 @@
package config package config
import "github.com/muety/wakapi/utils" import (
"github.com/muety/wakapi/lib"
)
type SharedDataKey string
const (
MiddlewareKeyPrincipal = SharedDataKey("principal")
MiddlewareKeyPrincipalId = SharedDataKey("principal_identity")
)
type SharedData struct { type SharedData struct {
*utils.ConcurrentMap[string, interface{}] *lib.ConcurrentMap[SharedDataKey, interface{}]
} }
func NewSharedData() *SharedData { func NewSharedData() *SharedData {
return &SharedData{utils.NewConcurrentMap[string, interface{}]()} return &SharedData{lib.NewConcurrentMap[SharedDataKey, interface{}]()}
} }

View File

@@ -78,7 +78,7 @@ func ParseSummaryFilters(r *http.Request) *models.Filters {
} }
func extractUser(r *http.Request) *models.User { func extractUser(r *http.Request) *models.User {
val := r.Context().Value(config.MiddlewareKeySharedData).(*config.SharedData).MustGet(config.MiddlewareKeyPrincipal) val := r.Context().Value(config.KeySharedData).(*config.SharedData).MustGet(config.MiddlewareKeyPrincipal)
if val == nil { if val == nil {
return nil return nil
} }

44
lib/concurrent_map.go Normal file
View File

@@ -0,0 +1,44 @@
package lib
import "sync"
type ConcurrentMap[K comparable, V any] struct {
mu sync.RWMutex
items map[K]V
}
func NewConcurrentMap[K comparable, V any]() *ConcurrentMap[K, V] {
return &ConcurrentMap[K, V]{
items: make(map[K]V),
}
}
func (m *ConcurrentMap[K, V]) Set(key K, value V) {
m.mu.Lock()
defer m.mu.Unlock()
m.items[key] = value
}
func (m *ConcurrentMap[K, V]) Get(key K) (V, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
value, ok := m.items[key]
return value, ok
}
func (m *ConcurrentMap[K, V]) MustGet(key K) V {
val, _ := m.Get(key)
return val
}
func (m *ConcurrentMap[K, V]) Delete(key K) {
m.mu.Lock()
defer m.mu.Unlock()
delete(m.items, key)
}
func (m *ConcurrentMap[K, V]) Len() int {
m.mu.RLock()
defer m.mu.RUnlock()
return len(m.items)
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/muety/wakapi/config" "github.com/muety/wakapi/config"
) )
const key = config.MiddlewareKeySharedData const key = config.KeySharedData
// This middleware is a bit of a dirty workaround to the fact that a http.Request's context // This middleware is a bit of a dirty workaround to the fact that a http.Request's context
// does not allow to pass values from an inner to an outer middleware. Calling WithContext() on a // does not allow to pass values from an inner to an outer middleware. Calling WithContext() on a

View File

@@ -73,7 +73,7 @@ func mockUserAwareRequest(requestedUser, authorizedUser string) (*http.Request,
r := httptest.NewRequest("GET", "http://localhost:3000/api/{user}/data", nil) r := httptest.NewRequest("GET", "http://localhost:3000/api/{user}/data", nil)
r = withUrlParam(r, "user", requestedUser) r = withUrlParam(r, "user", requestedUser)
r = r.WithContext(context.WithValue(r.Context(), config.MiddlewareKeySharedData, sharedData)) r = r.WithContext(context.WithValue(r.Context(), config.KeySharedData, sharedData))
userServiceMock := new(mocks.UserServiceMock) userServiceMock := new(mocks.UserServiceMock)
userServiceMock.On("GetUserById", "user1").Return(&models.User{ID: "user1"}, nil) userServiceMock.On("GetUserById", "user1").Return(&models.User{ID: "user1"}, nil)

View File

@@ -2,7 +2,6 @@ package utils
import ( import (
"strings" "strings"
"sync"
) )
func SubSlice[T any](slice []T, from, to uint) []T { func SubSlice[T any](slice []T, from, to uint) []T {
@@ -25,46 +24,3 @@ func CloneStringMap(m map[string]string, keysToLower bool) map[string]string {
} }
return m2 return m2
} }
// Concurrent hash map implementation
type ConcurrentMap[K comparable, V any] struct {
mu sync.RWMutex
items map[K]V
}
func NewConcurrentMap[K comparable, V any]() *ConcurrentMap[K, V] {
return &ConcurrentMap[K, V]{
items: make(map[K]V),
}
}
func (m *ConcurrentMap[K, V]) Set(key K, value V) {
m.mu.Lock()
defer m.mu.Unlock()
m.items[key] = value
}
func (m *ConcurrentMap[K, V]) Get(key K) (V, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
value, ok := m.items[key]
return value, ok
}
func (m *ConcurrentMap[K, V]) MustGet(key K) V {
val, _ := m.Get(key)
return val
}
func (m *ConcurrentMap[K, V]) Delete(key K) {
m.mu.Lock()
defer m.mu.Unlock()
delete(m.items, key)
}
func (m *ConcurrentMap[K, V]) Len() int {
m.mu.RLock()
defer m.mu.RUnlock()
return len(m.items)
}