Newer
Older
package sessionmanagermdl
import (
"coresls/servers/coresls/app/models"
"errors"
"time"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/cachemdl"
)
type Entry struct {
Data gjson.Result `json:"data,omitempty"`
Expiration int64 `json:"expiration,omitempty"`
ExpiredAT int64 `json:"expiredAt,omitempty"`
}
KEY_DATA = "data"
KEY_EXPIREDAT = "expiredAt"
KEY_EXPIRATION = "expiration"
)
var store cachemdl.Cacher
var ErrSessionNotFound = errors.New("SESSION_NOT_FOUND")
var ErrInvalidDataType = errors.New("INVALID_DATA_Type")
// InitSessionManagerCache initializes the cache with provided configuration. Need to provide a cache type to use.
func InitSessionManagerCache(chacheType int) {
cacheConfig := cachemdl.CacheConfig{
Type: chacheType,
RedisCache: &cachemdl.RedisCache{
DB: models.RedisDBSessionManager,
Prefix: models.ProjectID,
},
FastCache: &cachemdl.FastCacheHelper{
CleanupTime: 60 * time.Minute,
MaxEntries: 1000,
Expiration: -1,
},
}
store = cachemdl.GetCacheInstance(&cacheConfig)
}
// Init initializes session manager with provided cache. Subsequent calls will not have any effect after fist initialization.
func Init(cache cachemdl.Cacher) {
if store != nil {
return
}
store = cache
}
// NewEntry prepares the object required to store data in session.
//
// The `exp` field interprets time in seconds. Ex. For 5 seconds, set `5`
func NewEntry(val gjson.Result, exp int64) Entry {
duration := time.Duration(exp) * time.Second
deadLine := time.Now().Add(duration).Unix()
return Entry{
Data: val,
Expiration: exp,
ExpiredAT: deadLine,
}
}
// NewRedisEntry prepares the entry for redis cache. This is required because redis accepts a byte array.
func NewRedisEntry(entry Entry) string {
var data string
// the error can be ignored here as we have valid keys and data values
data, _ = sjson.Set(data, KEY_DATA, entry.Data.Value())
data, _ = sjson.Set(data, KEY_EXPIRATION, entry.Expiration)
data, _ = sjson.Set(data, KEY_EXPIREDAT, entry.ExpiredAT)
return data
}
// ToObject returns an cache entry as an object. It is better than sjson.Set() as we need to perform gjson.Parse().
func ToObject(entry Entry) map[string]interface{} {
return map[string]interface{}{
KEY_DATA: entry.Data.Value(),
KEY_EXPIRATION: entry.Expiration,
KEY_EXPIREDAT: entry.ExpiredAT,
}
}
// Store adds/ updates the entry against the provided key.
func Store(key string, entry Entry) {
duration := time.Duration(entry.Expiration) * time.Second
// if session manager uses redis cache, the data field (gjson.Result) is saved as is.
// This adds irrelevant fields in redis cache and we get them on retrieve operation.
// The following operation needs to be performed so that the data is marshaled correctly. Redis only accepts []byte{}.
if store.Type() == cachemdl.TypeRedisCache {
store.SetWithExpiration(key, NewRedisEntry(entry), duration)
return
}
store.SetWithExpiration(key, entry, duration)
}
// Retrieve returns the entry present against the provided key. If a key is not available or data stored is not of type gjson.Result, a non nil error will be returned
func Retrieve(key string) (Entry, error) {
data, ok := store.Get(key)
if !ok {
return Entry{}, ErrSessionNotFound
}
switch v := data.(type) {
case string: // for result from redis cache
res := gjson.Parse(v)
return Entry{
Data: res.Get(KEY_DATA),
Expiration: res.Get(KEY_EXPIRATION).Int(),
ExpiredAT: res.Get(KEY_EXPIREDAT).Int(),
}, nil
case Entry: // for result from fastcache
return v, nil
return Entry{}, ErrInvalidDataType
}
}
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// RetrieveAndExtend returns the entry and extends the entry expiration by provided `SECONDS`, only if remaining time < extendBy.
// If extendBy < 0, it is same as Retrieve function.
func RetrieveAndExtend(key string, extendBy int64) (Entry, error) {
entry, err := Retrieve(key)
if err != nil {
return Entry{}, err
}
if extendBy > 0 {
timeRemaining := entry.ExpiredAT - time.Now().Unix()
if timeRemaining < extendBy {
// update with new expiratin
entry.ExpiredAT = time.Now().Add(time.Second * time.Duration(extendBy)).Unix()
Store(key, entry)
}
}
return entry, nil
}
// RetrieveAndDelete deletes the entry after first retrieval
func RetrieveAndDelete(key string) (Entry, error) {
entry, err := Retrieve(key)
if err != nil {
return Entry{}, err
}
store.Delete(key)
return entry, nil
}
// Delete removes the entry from session manager. If the key is not present, error `ErrSessionNotFound` will be thrown. Caller can ignore error if this is acceptable.