Skip to content

Commit

Permalink
Move the SDK's status registration into the SDK initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
z4kn4fein committed Mar 27, 2024
1 parent 08597ae commit 99b83b3
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 51 deletions.
6 changes: 3 additions & 3 deletions grpc/flag_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ func (s *flagService) EvalFlag(_ context.Context, req *proto.EvalRequest) (*prot
if err != nil {
return nil, err
}
value, err := sdkClient.Eval(req.GetKey(), user)
if err != nil {
value := sdkClient.Eval(req.GetKey(), user)
if value.Error != nil {
var errKeyNotFound configcat.ErrKeyNotFound
if errors.As(err, &errKeyNotFound) {
if errors.As(value.Error, &errKeyNotFound) {
return nil, status.Error(codes.NotFound, "feature flag or setting with key '"+req.GetKey()+"' not found")
} else {
return nil, status.Error(codes.Unknown, "the request failed; please check the logs for more details")
Expand Down
1 change: 1 addition & 0 deletions model/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
type EvalData struct {
Value interface{}
VariationId string
Error error
User configcat.User
}

Expand Down
10 changes: 6 additions & 4 deletions sdk/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const (
)

type Client interface {
Eval(key string, user model.UserAttrs) (model.EvalData, error)
Eval(key string, user model.UserAttrs) model.EvalData
EvalAll(user model.UserAttrs) map[string]model.EvalData
Keys() []string
GetCachedJson() *store.EntryWithEtag
Expand Down Expand Up @@ -66,6 +66,8 @@ type client struct {

func NewClient(sdkCtx *Context, log log.Logger) Client {
sdkLog := log.WithLevel(sdkCtx.SDKConf.Log.GetLevel()).WithPrefix("sdk-" + sdkCtx.SdkId)
sdkCtx.StatusReporter.RegisterSdk(sdkCtx.SdkId, sdkCtx.SDKConf)

offline := sdkCtx.SDKConf.Offline.Enabled
key := sdkCtx.SDKConf.Key
var storage configcat.ConfigCache
Expand Down Expand Up @@ -183,18 +185,18 @@ func (c *client) signal() {
}
}

func (c *client) Eval(key string, user model.UserAttrs) (model.EvalData, error) {
func (c *client) Eval(key string, user model.UserAttrs) model.EvalData {
mergedUser := model.MergeUserAttrs(c.defaultAttrs, user)
details := c.configCatClient.Snapshot(mergedUser).GetValueDetails(key)
return model.EvalData{Value: details.Value, VariationId: details.Data.VariationID, User: details.Data.User}, details.Data.Error
return model.EvalData{Value: details.Value, VariationId: details.Data.VariationID, User: details.Data.User, Error: details.Data.Error}
}

func (c *client) EvalAll(user model.UserAttrs) map[string]model.EvalData {
mergedUser := model.MergeUserAttrs(c.defaultAttrs, user)
allDetails := c.configCatClient.Snapshot(mergedUser).GetAllValueDetails()
result := make(map[string]model.EvalData, len(allDetails))
for _, details := range allDetails {
result[details.Data.Key] = model.EvalData{Value: details.Value, VariationId: details.Data.VariationID, User: details.Data.User}
result[details.Data.Key] = model.EvalData{Value: details.Value, VariationId: details.Data.VariationID, User: details.Data.User, Error: details.Data.Error}
}
return result
}
Expand Down
1 change: 0 additions & 1 deletion sdk/sdk_registrar.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ type registrar struct {
func NewRegistrar(conf *config.Config, metricsReporter metrics.Reporter, statusReporter status.Reporter, externalCache configcat.ConfigCache, log log.Logger) Registrar {
sdkClients := make(map[string]Client, len(conf.SDKs))
for key, sdkConf := range conf.SDKs {
statusReporter.RegisterSdk(key, sdkConf)
sdkClients[key] = NewClient(&Context{
SDKConf: sdkConf,
MetricsReporter: metricsReporter,
Expand Down
10 changes: 0 additions & 10 deletions sdk/sdk_registrar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,3 @@ func TestClient_Close(t *testing.T) {
<-c.ctx.Done()
})
}

func TestRegistrar_Reporter(t *testing.T) {
reporter := status.NewEmptyReporter()
reg := NewRegistrar(&config.Config{
SDKs: map[string]*config.SDKConfig{"test": {Key: "key"}},
}, nil, reporter, nil, log.NewNullLogger())
defer reg.Close()

assert.NotEmpty(t, reporter.GetStatus().SDKs)
}
72 changes: 45 additions & 27 deletions sdk/sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ func TestSdk_Signal(t *testing.T) {
client := NewClient(ctx, log.NewNullLogger())
defer client.Close()
sub := client.SubConfigChanged("id")
data, err := client.Eval("flag", nil)
data := client.Eval("flag", nil)
j := client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.True(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":true,"s":null,"i":null,"d":null},"t":0,"r":[],"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, fmt.Sprintf("%x", sha1.Sum(j.ConfigJson)), j.ETag)
Expand All @@ -51,9 +51,9 @@ func TestSdk_Signal(t *testing.T) {
utils.WithTimeout(2*time.Second, func() {
<-sub
})
data, err = client.Eval("flag", nil)
data = client.Eval("flag", nil)
j = client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.False(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":false,"s":null,"i":null,"d":null},"t":0,"r":[],"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, fmt.Sprintf("%x", sha1.Sum(j.ConfigJson)), j.ETag)
Expand Down Expand Up @@ -110,9 +110,9 @@ func TestSdk_Signal_Refresh(t *testing.T) {
client := NewClient(ctx, log.NewNullLogger())
defer client.Close()
sub := client.SubConfigChanged("id")
data, err := client.Eval("flag", nil)
data := client.Eval("flag", nil)
j := client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.True(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":true,"s":null,"i":null,"d":null},"t":0,"r":[],"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, fmt.Sprintf("%x", sha1.Sum(j.ConfigJson)), j.ETag)
Expand All @@ -126,10 +126,11 @@ func TestSdk_Signal_Refresh(t *testing.T) {
utils.WithTimeout(2*time.Second, func() {
<-sub
})
data, err = client.Eval("flag", nil)
data = client.Eval("flag", nil)
j = client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.False(t, data.Value.(bool))
assert.Nil(t, data.Error)
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":false,"s":null,"i":null,"d":null},"t":0,"r":[],"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, fmt.Sprintf("%x", sha1.Sum(j.ConfigJson)), j.ETag)
}
Expand All @@ -143,10 +144,11 @@ func TestSdk_BadConfig(t *testing.T) {
ctx := newTestSdkContext(&config.SDKConfig{BaseUrl: srv.URL, Key: key, Log: config.LogConfig{Level: "debug"}}, nil)
client := NewClient(ctx, log.NewDebugLogger())
defer client.Close()
data, err := client.Eval("flag", nil)
data := client.Eval("flag", nil)
j := client.GetCachedJson()
assert.Error(t, err)
assert.Error(t, data.Error)
assert.Nil(t, data.Value)
assert.NotNil(t, data.Error)
assert.Equal(t, `{"f":null,"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, utils.GenerateEtag(j.ConfigJson), j.ETag)
}
Expand All @@ -161,13 +163,14 @@ func TestSdk_BadConfig_WithCache(t *testing.T) {
cacheKey := configcatcache.ProduceCacheKey(key, configcatcache.ConfigJSONName, configcatcache.ConfigJSONCacheVersion)
cacheEntry := configcatcache.CacheSegmentsToBytes(time.Now(), "etag", []byte(`{"f":{"flag":{"a":"","i":"v_flag","v":{"b":true},"t":0}}}`))
err := s.Set(cacheKey, string(cacheEntry))
assert.NoError(t, err)

ctx := newTestSdkContext(&config.SDKConfig{BaseUrl: srv.URL, Key: key, Log: config.LogConfig{Level: "debug"}}, newRedisCache(s.Addr()))
client := NewClient(ctx, log.NewDebugLogger())
defer client.Close()
data, err := client.Eval("flag", nil)
data := client.Eval("flag", nil)
j := client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.True(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":true},"t":0}}}`, string(j.ConfigJson))
assert.Equal(t, "etag", j.ETag)
Expand All @@ -179,9 +182,9 @@ func TestSdk_Signal_Offline_File_Watch(t *testing.T) {
client := NewClient(ctx, log.NewNullLogger())
defer client.Close()
sub := client.SubConfigChanged("id")
data, err := client.Eval("flag", nil)
data := client.Eval("flag", nil)
j := client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.True(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":true,"s":null,"i":null,"d":null},"t":0,"r":null,"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, fmt.Sprintf("W/\"%s\"", utils.FastHashHex(j.ConfigJson)), j.ETag)
Expand All @@ -190,9 +193,9 @@ func TestSdk_Signal_Offline_File_Watch(t *testing.T) {
utils.WithTimeout(2*time.Second, func() {
<-sub
})
data, err = client.Eval("flag", nil)
data = client.Eval("flag", nil)
j = client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.False(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":false,"s":null,"i":null,"d":null},"t":0,"r":null,"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, utils.GenerateEtag(j.ConfigJson), j.ETag)
Expand All @@ -205,9 +208,9 @@ func TestSdk_Signal_Offline_Poll_Watch(t *testing.T) {
client := NewClient(ctx, log.NewNullLogger())
defer client.Close()
sub := client.SubConfigChanged("id")
data, err := client.Eval("flag", nil)
data := client.Eval("flag", nil)
j := client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.True(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":true,"s":null,"i":null,"d":null},"t":0,"r":null,"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, fmt.Sprintf("W/\"%s\"", utils.FastHashHex(j.ConfigJson)), j.ETag)
Expand All @@ -216,9 +219,9 @@ func TestSdk_Signal_Offline_Poll_Watch(t *testing.T) {
utils.WithTimeout(2*time.Second, func() {
<-sub
})
data, err = client.Eval("flag", nil)
data = client.Eval("flag", nil)
j = client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.False(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":false,"s":null,"i":null,"d":null},"t":0,"r":null,"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, utils.GenerateEtag(j.ConfigJson), j.ETag)
Expand All @@ -239,9 +242,9 @@ func TestSdk_Signal_Offline_Redis_Watch(t *testing.T) {
client := NewClient(ctx, log.NewNullLogger())
defer client.Close()
sub := client.SubConfigChanged("id")
data, err := client.Eval("flag", nil)
data := client.Eval("flag", nil)
j := client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.True(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":true,"s":null,"i":null,"d":null},"t":0,"r":null,"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, "etag", j.ETag)
Expand All @@ -251,9 +254,9 @@ func TestSdk_Signal_Offline_Redis_Watch(t *testing.T) {
utils.WithTimeout(2*time.Second, func() {
<-sub
})
data, err = client.Eval("flag", nil)
data = client.Eval("flag", nil)
j = client.GetCachedJson()
assert.NoError(t, err)
assert.NoError(t, data.Error)
assert.False(t, data.Value.(bool))
assert.Equal(t, `{"f":{"flag":{"a":"","i":"v_flag","v":{"b":false,"s":null,"i":null,"d":null},"t":0,"r":null,"p":null}},"s":null,"p":null}`, string(j.ConfigJson))
assert.Equal(t, "etag2", j.ETag)
Expand Down Expand Up @@ -301,6 +304,8 @@ func TestSdk_EvalAll(t *testing.T) {
assert.Equal(t, 2, len(details))
assert.Equal(t, "v1", details["flag1"].Value)
assert.Equal(t, "v2", details["flag2"].Value)
assert.Nil(t, details["flag1"].Error)
assert.Nil(t, details["flag2"].Error)
}

func TestSdk_Keys(t *testing.T) {
Expand Down Expand Up @@ -344,7 +349,7 @@ func TestSdk_EvalStatsReporter(t *testing.T) {
client := NewClient(ctx, log.NewNullLogger())
defer client.Close()

_, _ = client.Eval("flag1", model.UserAttrs{"e": "h"})
_ = client.Eval("flag1", model.UserAttrs{"e": "h"})

var event *statistics.EvalEvent
utils.WithTimeout(2*time.Second, func() {
Expand All @@ -369,7 +374,7 @@ func TestSdk_DefaultAttrs(t *testing.T) {
client := NewClient(ctx, log.NewNullLogger())
defer client.Close()

evalData, _ := client.Eval("flag1", model.UserAttrs{"e": "h"})
evalData := client.Eval("flag1", model.UserAttrs{"e": "h"})
assert.Equal(t, model.UserAttrs{"a": "g", "c": "d", "e": "h"}, evalData.User.(model.UserAttrs))
}

Expand Down Expand Up @@ -427,11 +432,24 @@ func TestSdk_IsInValidState_EmptyCache_False(t *testing.T) {
assert.False(t, client.IsInValidState())
}

func TestSdk_StatusReporter(t *testing.T) {
reporter := status.NewEmptyReporter()
ctx := newTestSdkContextWithReporter(&config.SDKConfig{BaseUrl: "https://localhost", Key: configcattest.RandomSDKKey()}, nil, reporter)
client := NewClient(ctx, log.NewDebugLogger())
defer client.Close()

assert.NotEmpty(t, reporter.GetStatus().SDKs)
}

func newTestSdkContext(conf *config.SDKConfig, externalCache configcat.ConfigCache) *Context {
return newTestSdkContextWithReporter(conf, externalCache, status.NewEmptyReporter())
}

func newTestSdkContextWithReporter(conf *config.SDKConfig, externalCache configcat.ConfigCache, reporter status.Reporter) *Context {
return &Context{
SDKConf: conf,
ProxyConf: &config.HttpProxyConfig{},
StatusReporter: status.NewEmptyReporter(),
StatusReporter: reporter,
MetricsReporter: nil,
EvalReporter: nil,
SdkId: "test",
Expand Down
6 changes: 3 additions & 3 deletions stream/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func createChannel(established *connEstablished, sdkClient sdk.Client) channel {
}
return &allFlagsChannel{connectionHolder: connectionHolder{user: established.user}, lastPayload: payloads}
} else {
val, _ := sdkClient.Eval(established.key, established.user)
val := sdkClient.Eval(established.key, established.user)
payload := model.PayloadFromEvalData(&val)
return &singleFlagChannel{connectionHolder: connectionHolder{user: established.user}, lastPayload: &payload}
}
Expand All @@ -58,8 +58,8 @@ func (af *allFlagsChannel) LastPayload() interface{} {

func (sf *singleFlagChannel) Notify(sdkClient sdk.Client, key string) int {
sent := 0
val, err := sdkClient.Eval(key, sf.user)
if err != nil {
val := sdkClient.Eval(key, sf.user)
if val.Error != nil {
return 0
}
if sf.lastPayload == nil || val.Value != sf.lastPayload.Value {
Expand Down
6 changes: 3 additions & 3 deletions web/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ func (s *Server) Eval(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), code)
return
}
eval, err := sdkClient.Eval(evalReq.Key, evalReq.User)
if err != nil {
eval := sdkClient.Eval(evalReq.Key, evalReq.User)
if eval.Error != nil {
var errKeyNotFound configcat.ErrKeyNotFound
if errors.As(err, &errKeyNotFound) {
if errors.As(eval.Error, &errKeyNotFound) {
http.Error(w, "feature flag or setting with key '"+evalReq.Key+"' not found", http.StatusBadRequest)
} else {
http.Error(w, "the request failed; please check the logs for more details", http.StatusInternalServerError)
Expand Down

0 comments on commit 99b83b3

Please sign in to comment.