package redis import ( "context" "encoding/json" "os" "reflect" "time" "github.com/go-redis/redis/v8" "gitlab.com/uafrica/go-utils/errors" "gitlab.com/uafrica/go-utils/logger" ) type IRedis interface { Del(key string) error SetJSON(key string, value interface{}) error SetJSONIndefinitely(key string, value interface{}) error SetJSONForDur(key string, value interface{}, dur time.Duration) error GetJSON(key string, valueType reflect.Type) (value interface{}, ok bool) SetString(key string, value string) error SetStringIndefinitely(key string, value string) error SetStringForDur(key string, value string, dur time.Duration) error GetString(key string) (value string, ok bool) } type redisWithContext struct { context.Context client *redis.Client } func New(ctx context.Context) (IRedis, error) { if globalClient == nil { var err error if globalClient, err = connect(); err != nil { return redisWithContext{Context: ctx}, errors.Wrapf(err, "cannot connect to REDIS") } } return redisWithContext{ Context: ctx, client: globalClient, }, nil } func (r redisWithContext) Del(key string) error { if r.client == nil { return errors.Errorf("REDIS disabled: cannot del key(%s)", key) } _, err := r.client.Del(r.Context, key).Result() if err != nil { return errors.Wrapf(err, "failed to del key(%s)", key) } logger.Debugf("REDIS.Del(%s)", key) return nil } //set JSON value for 24h func (r redisWithContext) SetJSON(key string, value interface{}) error { return r.SetJSONForDur(key, value, 24*time.Hour) } func (r redisWithContext) SetJSONIndefinitely(key string, value interface{}) error { return r.SetJSONForDur(key, value, 0) } func (r redisWithContext) SetJSONForDur(key string, value interface{}, dur time.Duration) error { if r.client == nil { return errors.Errorf("REDIS disabled: cannot set JSON key(%s) = (%T)%v", key, value, value) } jsonBytes, err := json.Marshal(value) if err != nil { return errors.Wrapf(err, "failed to JSON encode key(%s) = (%T)", key, value) } if _, err = r.client.Set(r.Context, key, string(jsonBytes), dur).Result(); err != nil { return errors.Wrapf(err, "failed to set JSON key(%s)", key) } logger.Debugf("REDIS.SetJSON(%s)=%s (%T) (exp: %v)", key, string(jsonBytes), value, dur) return nil } //return: // nil,nil if key is not defined // nil,err if failed to get/determine if it exists, or failed to decode // <value>,nil if found and decoded func (r redisWithContext) GetJSON(key string, valueType reflect.Type) (value interface{}, ok bool) { if r.client == nil { return nil, false } jsonValue, err := r.client.Get(r.Context, key).Result() if err != nil { return nil, false } newValuePtr := reflect.New(valueType) if err := json.Unmarshal([]byte(jsonValue), newValuePtr.Interface()); err != nil { return nil, false } return newValuePtr.Elem().Interface(), true } func (r redisWithContext) SetString(key string, value string) error { return r.SetStringForDur(key, value, 24*time.Hour) } func (r redisWithContext) SetStringIndefinitely(key string, value string) error { return r.SetStringForDur(key, value, 0) } func (r redisWithContext) SetStringForDur(key string, value string, dur time.Duration) error { if r.client == nil { return errors.Errorf("REDIS disabled: cannot set key(%s) = (%T)%v", key, value, value) } if _, err := r.client.Set(r.Context, key, value, dur).Result(); err != nil { return errors.Wrapf(err, "failed to set key(%s)", key) } logger.Debugf("REDIS.SetString(%s)=%s (exp: %v)", key, value, dur) return nil } func (r redisWithContext) GetString(key string) (string, bool) { if r.client == nil { return "", false } value, err := r.client.Get(r.Context, key).Result() if err != nil { /* Actual error */ if err != redis.Nil { /* other than Key does not exist */ logger.Errorf("Error fetching redis key(%s): %+v", key, err) } return "", false } return value, true } //global connection to REDIS used in all context var globalClient *redis.Client func connect() (*redis.Client, error) { host := os.Getenv("REDIS_HOST") if host == "false" { return nil, errors.Errorf("REDIS_HOST=false") } port := os.Getenv("REDIS_PORT") if os.Getenv("DEBUGGING") == "true" { host = "host.docker.internal" if os.Getenv("LOCAL") == "true" { host = "localhost" } env := os.Getenv("ENVIRONMENT") switch env { case "dev": port = "6380" case "stage": port = "6381" case "prod": port = "6383" } } logger.Debugf("Using REDIS(%s:%s)", host, port) globalClient = redis.NewClient(&redis.Options{ Addr: host + ":" + port, Password: "", // no password set DB: 0, // use default DB }) return globalClient, nil } //connect()