Initial cloud-services repo - gateway service + pkg modules

This commit is contained in:
Chris Rai
2026-01-30 23:14:52 -05:00
commit fbb820d7b3
1037 changed files with 171318 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
package redisutils
import (
"time"
"fiskerinc.com/modules/redis"
"github.com/pkg/errors"
)
var (
ErrCacheDoesntExist = errors.New("key doesn't exist")
ErrWrongResponseFormat = errors.New("wrong response format")
)
type CachedSet struct {
redis redis.Client
}
func (s *CachedSet) SetConnection(client redis.Client) {
s.redis = client
}
func (s *CachedSet) GetCachedSet(key string) (map[string]struct{}, error) {
batch := redis.NewRedisBatchCommands()
batch.Add("EXISTS", key)
batch.Add("SMEMBERS", key)
resultsI, err := s.redis.ExecuteBatch(batch)
if err != nil {
return nil, err
}
results, ok := resultsI.([]interface{})
if !ok || len(results) != 2 {
return nil, errResponseFormat("[2]interface{}", resultsI)
}
keyExists, ok := results[0].(int64)
if !ok {
return nil, errResponseFormat("int64", results[0])
}
if keyExists == 0 {
return nil, ErrCacheDoesntExist
}
cacheRes, ok := results[1].([]interface{})
if !ok {
return nil, errResponseFormat("[]interface{}", results[1])
}
cacheSet := make(map[string]struct{}, len(cacheRes))
for _, signal := range cacheRes {
s, ok := signal.([]byte)
if !ok {
return nil, errResponseFormat("[]byte", signal)
}
cacheSet[string(s)] = struct{}{}
}
return cacheSet, nil
}
func (s *CachedSet) UpdateSetCache(key string, cacheValues []interface{}) error {
saddCommand := append([]interface{}{"SADD", key}, cacheValues...)
_, err := s.redis.Execute(saddCommand...)
return err
}
func (s *CachedSet) CreateSetCache(key string, cacheValues []interface{}, expireTime time.Time) error {
batch := redis.NewRedisBatchCommands()
saddCommand := append([]interface{}{"SADD", key}, cacheValues...)
batch.Add(saddCommand...)
batch.Add("EXPIREAT", key, expireTime.Unix())
_, err := s.redis.ExecuteBatch(batch)
return err
}
func errResponseFormat(expectedType string, got interface{}) error {
return errors.WithStack(
errors.WithMessagef(ErrWrongResponseFormat, "expected type: %s, got: %v", expectedType, got),
)
}

View File

@@ -0,0 +1,28 @@
package redisutils
import (
"fiskerinc.com/modules/redis"
"time"
)
type GetCachedSetMock func(key string) (map[string]struct{}, error)
type CacheSetMock struct {
GetCachedSetMock func(key string) (map[string]struct{}, error)
UpdateSetCacheMock func(key string, cacheValues []interface{}) error
CreateSetCacheMock func(key string, cacheValues []interface{}, expireTime time.Time) error
}
func (c CacheSetMock) SetConnection(client redis.Client) {}
func (c CacheSetMock) GetCachedSet(key string) (map[string]struct{}, error) {
return c.GetCachedSetMock(key)
}
func (c CacheSetMock) UpdateSetCache(key string, cacheValues []interface{}) error {
return c.UpdateSetCacheMock(key, cacheValues)
}
func (c CacheSetMock) CreateSetCache(key string, cacheValues []interface{}, expireTime time.Time) error {
return c.CreateSetCacheMock(key, cacheValues, expireTime)
}

View File

@@ -0,0 +1,59 @@
package redisutils
import (
"errors"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
var ErrFailedFunc = errors.New("some error")
// GetCachedSetMock funcs.
func SuccessGetCachedSetMock(t *testing.T, expKey string, response map[string]struct{}) func(key string) (map[string]struct{}, error) {
return func(key string) (map[string]struct{}, error) {
assert.Equal(t, expKey, key)
return response, nil
}
}
func NoKeyGetCachedSetMock(key string) (map[string]struct{}, error) {
return nil, ErrCacheDoesntExist
}
func FailedGetCachedSetMock(key string) (map[string]struct{}, error) {
return nil, ErrFailedFunc
}
// UpdateSetCacheMock funcs.
func SuccessUpdateSetCacheMock(t *testing.T, expKey string, expCacheValues []interface{}) func(key string, cacheValues []interface{}) error {
return func(key string, cacheValues []interface{}) error {
assert.Equal(t, expKey, key)
assert.Equal(t, expCacheValues, cacheValues)
return nil
}
}
func FailUpdateSetCacheMock(key string, cacheValues []interface{}) error {
return ErrFailedFunc
}
// CreateSetCacheMock funcs.
func SuccessCreateSetCacheMock(t *testing.T, expKey string, expCacheValues []interface{}, expExpireTime time.Time) func(key string, cacheValues []interface{}, expireTime time.Time) error {
return func(key string, cacheValues []interface{}, expireTime time.Time) error {
assert.Equal(t, expKey, key)
assert.Equal(t, expCacheValues, cacheValues)
assert.Equal(t, expExpireTime, expireTime)
return nil
}
}
func FailCreateSetCacheMock(key string, cacheValues []interface{}, expireTime time.Time) error {
return ErrFailedFunc
}

View File

@@ -0,0 +1,211 @@
package redisutils_test
import (
"errors"
"testing"
"time"
"fiskerinc.com/modules/redis/redisutils"
"fiskerinc.com/modules/redis/tester"
"github.com/stretchr/testify/assert"
)
func TestGetCacheSet(t *testing.T) {
key := "someKey"
redisMock := tester.NewRedisMock()
tests := map[string]struct {
getResults map[string]map[string]interface{}
batchError error
expRes map[string]struct{}
expError error
}{
"correct": {
getResults: map[string]map[string]interface{}{
"EXISTS": {
key: int64(1),
},
"SMEMBERS": {
key: []interface{}{
[]byte("609:ESP_ActvSig_DTC"),
[]byte("832:ADAS_FltIndcr"),
[]byte("832:ADAS_IntegtCrsFltTxt"),
},
},
},
expRes: map[string]struct{}{
"609:ESP_ActvSig_DTC": {},
"832:ADAS_FltIndcr": {},
"832:ADAS_IntegtCrsFltTxt": {},
},
},
"key_doesnt_exist": {
getResults: map[string]map[string]interface{}{
"EXISTS": {
key: int64(0),
},
},
expError: redisutils.ErrCacheDoesntExist,
},
"batch_error": {
batchError: errors.New("some error"),
expError: errors.New("some error"),
},
"wrong_batch_first_elem": {
getResults: map[string]map[string]interface{}{
"EXISTS": {
key: 1,
},
"SMEMBERS": {
key: []interface{}{},
},
},
expError: errors.New("expected type: int64, got: 1: wrong response format"),
},
"wrong_batch_second_elem": {
getResults: map[string]map[string]interface{}{
"EXISTS": {
key: int64(1),
},
"SMEMBERS": {
key: 1,
},
},
expError: errors.New("expected type: []interface{}, got: 1: wrong response format"),
},
"wrong_batch_second_sub_elem": {
getResults: map[string]map[string]interface{}{
"EXISTS": {
key: int64(1),
},
"SMEMBERS": {
key: []interface{}{1},
},
},
expError: errors.New("expected type: []byte, got: 1: wrong response format"),
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
redisMock.GetCommandResult = tt.getResults
redisMock.Error = tt.batchError
cSet := redisutils.CachedSet{}
cSet.SetConnection(redisMock)
res, err := cSet.GetCachedSet(key)
if err != nil && tt.expError != nil {
assert.Equal(t, tt.expError.Error(), err.Error())
return
}
assert.Equal(t, tt.expError, err)
assert.Equal(t, tt.expRes, res)
})
}
}
func TestCreateSetCache(t *testing.T) {
redisMock := tester.NewRedisMock()
mockTime := time.Date(2022, 8, 11, 15, 53, 0, 0, time.UTC)
tests := map[string]struct {
cacheValues []interface{}
batchError error
expSetValues map[string]tester.ExpiringCache
expError error
}{
"correct": {
cacheValues: []interface{}{
"609:ESP_ActvSig_DTC",
"832:ADAS_FltIndcr",
"832:ADAS_IntegtCrsFltTxt",
},
expSetValues: map[string]tester.ExpiringCache{
"someKey": {
Value: []interface{}{
"609:ESP_ActvSig_DTC",
"832:ADAS_FltIndcr",
"832:ADAS_IntegtCrsFltTxt",
},
Expires: 1660233180,
},
},
},
"fail": {
batchError: errors.New("some error"),
expError: errors.New("some error"),
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
redisMock.Reset()
redisMock.Error = tt.batchError
cSet := redisutils.CachedSet{}
cSet.SetConnection(redisMock)
err := cSet.CreateSetCache("someKey", tt.cacheValues, mockTime)
if err != nil && tt.expError != nil {
assert.Equal(t, tt.expError.Error(), err.Error())
return
}
assert.Equal(t, tt.expError, err)
assert.Equal(t, tt.expSetValues, redisMock.SetValues)
})
}
}
func TestUpdateSetCache(t *testing.T) {
redisMock := tester.NewRedisMock()
tests := map[string]struct {
cacheValues []interface{}
execError error
expSetValues map[string]tester.ExpiringCache
expError error
}{
"correct": {
cacheValues: []interface{}{
"609:ESP_ActvSig_DTC",
"832:ADAS_FltIndcr",
"832:ADAS_IntegtCrsFltTxt",
},
expSetValues: map[string]tester.ExpiringCache{
"someKey": {
Value: []interface{}{
"609:ESP_ActvSig_DTC",
"832:ADAS_FltIndcr",
"832:ADAS_IntegtCrsFltTxt",
},
},
},
},
"fail": {
execError: errors.New("some error"),
expError: errors.New("some error"),
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
redisMock.Reset()
redisMock.Error = tt.execError
cSet := redisutils.CachedSet{}
cSet.SetConnection(redisMock)
err := cSet.UpdateSetCache("someKey", tt.cacheValues)
if err != nil && tt.expError != nil {
assert.Equal(t, tt.expError.Error(), err.Error())
return
}
assert.Equal(t, tt.expError, err)
assert.Equal(t, tt.expSetValues, redisMock.SetValues)
})
}
}

View File

@@ -0,0 +1,32 @@
package redisutils
import (
re "fiskerinc.com/modules/redis"
"github.com/gomodule/redigo/redis"
"github.com/pkg/errors"
)
func CheckSet(conn re.Client, id string, keys interface{}) ([]bool, error) {
ckeys, ok := keys.([]string)
if !ok {
return nil, errors.New("keys is not []string")
}
results := make([]bool, len(ckeys))
batch := re.NewRedisBatchCommands()
for _, key := range ckeys {
batch.Add("SISMEMBER", id, key)
}
values, err := conn.ExecuteBatch(batch)
if err != nil {
return results, err
}
err = redis.ScanSlice(values.([]interface{}), &results)
if err != nil {
return results, errors.WithStack(err)
}
return results, nil
}