Newer
Older
package sessionmanagermdl
import (
"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")
// Init initializes session manager with provided cache. Subsequent calls will not have any effect after first 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
}
}
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// 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.