Skip to content

Commit

Permalink
Specify error types
Browse files Browse the repository at this point in the history
  • Loading branch information
z4kn4fein committed Apr 11, 2024
1 parent 8ddeb7e commit 4158200
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 24 deletions.
72 changes: 72 additions & 0 deletions configcat_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,18 @@ func TestClient_GetWithInvalidConfig(t *testing.T) {
c.Assert(result, qt.Equals, "default")
}

func TestClient_GetDetails_WithInvalidConfig(t *testing.T) {
c := qt.New(t)
srv, client := getTestClients(t)
srv.setResponse(configResponse{body: "invalid-json"})
client.Refresh(context.Background())
result := client.GetStringValueDetails("key", "default", nil)
_, ok := result.Data.Error.(ErrConfigJsonMissing)
c.Assert(ok, qt.IsTrue)
c.Assert(result.Data.IsDefaultValue, qt.IsTrue)
c.Assert(result.Value, qt.Equals, "default")
}

func TestClient_GetInt(t *testing.T) {
c := qt.New(t)
srv, client := getTestClients(t)
Expand Down Expand Up @@ -751,6 +763,66 @@ func TestClient_GetFloatDetails_NotExist(t *testing.T) {
c.Assert(details.Value, qt.Equals, float64(0))
}

func TestClient_GetBoolDetails_TypeMismatch(t *testing.T) {
c := qt.New(t)
srv := newConfigServer(t)
srv.setResponse(configResponse{
body: contentForIntegrationTestKey("PKDVCLf-Hq-h-kCzMp-L7Q/psuH7BGHoUmdONrzzUOY7A"),
})
client := NewCustomClient(srv.config())
client.Refresh(context.Background())

details := client.GetBoolValueDetails("integerDefaultOne", false, nil)
_, ok := details.Data.Error.(ErrSettingTypeMismatch)
c.Assert(ok, qt.IsTrue)
c.Assert(details.Value, qt.IsFalse)
}

func TestClient_GetStringDetails_TypeMismatch(t *testing.T) {
c := qt.New(t)
srv := newConfigServer(t)
srv.setResponse(configResponse{
body: contentForIntegrationTestKey("PKDVCLf-Hq-h-kCzMp-L7Q/psuH7BGHoUmdONrzzUOY7A"),
})
client := NewCustomClient(srv.config())
client.Refresh(context.Background())

details := client.GetStringValueDetails("integerDefaultOne", "", nil)
_, ok := details.Data.Error.(ErrSettingTypeMismatch)
c.Assert(ok, qt.IsTrue)
c.Assert(details.Value, qt.Equals, "")
}

func TestClient_GetIntDetails_TypeMismatch(t *testing.T) {
c := qt.New(t)
srv := newConfigServer(t)
srv.setResponse(configResponse{
body: contentForIntegrationTestKey("PKDVCLf-Hq-h-kCzMp-L7Q/psuH7BGHoUmdONrzzUOY7A"),
})
client := NewCustomClient(srv.config())
client.Refresh(context.Background())

details := client.GetIntValueDetails("boolDefaultTrue", 0, nil)
_, ok := details.Data.Error.(ErrSettingTypeMismatch)
c.Assert(ok, qt.IsTrue)
c.Assert(details.Value, qt.Equals, 0)
}

func TestClient_GetFloatDetails_TypeMismatch(t *testing.T) {
c := qt.New(t)
srv := newConfigServer(t)
srv.setResponse(configResponse{
body: contentForIntegrationTestKey("PKDVCLf-Hq-h-kCzMp-L7Q/psuH7BGHoUmdONrzzUOY7A"),
})
client := NewCustomClient(srv.config())
client.Refresh(context.Background())

details := client.GetFloatValueDetails("boolDefaultTrue", 0, nil)
_, ok := details.Data.Error.(ErrSettingTypeMismatch)
c.Assert(ok, qt.IsTrue)
c.Assert(details.Value, qt.Equals, float64(0))
}

func TestClient_GetDetails_Reflected_User(t *testing.T) {
c := qt.New(t)
srv := newConfigServer(t)
Expand Down
52 changes: 52 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package configcat

import (
"fmt"
"strings"
)

// ErrKeyNotFound is returned when a key is not found in the configuration.
type ErrKeyNotFound struct {
Key string
AvailableKeys []string
}

func (e ErrKeyNotFound) Error() string {
var availableKeys = ""
if len(e.AvailableKeys) > 0 {
availableKeys = "'" + strings.Join(e.AvailableKeys, "', '") + "'"
}
return fmt.Sprintf(
"failed to evaluate setting '%s' (the key was not found in config JSON); available keys: [%s]",
e.Key,
availableKeys,
)
}

// ErrSettingTypeMismatch is returned when a requested setting type doesn't match with the expected type.
type ErrSettingTypeMismatch struct {
Key string
Value interface{}
ExpectedType string
}

func (e ErrSettingTypeMismatch) Error() string {
return fmt.Sprintf(
"the type of the setting '%s' doesn't match with the expected type; setting's type was '%t' but the expected type was '%s'",
e.Key,
e.Value,
e.ExpectedType,
)
}

// ErrConfigJsonMissing is returned when the config JSON is empty or missing.
type ErrConfigJsonMissing struct {
Key string
}

func (e ErrConfigJsonMissing) Error() string {
return fmt.Sprintf(
"config JSON is not present when evaluating setting '%s'; returning the `defaultValue` parameter that you specified in your application",
e.Key,
)
}
9 changes: 4 additions & 5 deletions flag.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package configcat

import (
"fmt"
"sync"
)

Expand Down Expand Up @@ -84,7 +83,7 @@ func (f BoolFlag) GetValueDetails(snap *Snapshot) EvaluationDetails {
}
boolVal, ok := details.Value.(bool)
if !ok {
return produceDetailsWithError(f.key, f.defaultValue, snap, fmt.Errorf("could not convert %s to bool", details.Value))
return produceDetailsWithError(f.key, f.defaultValue, snap, ErrSettingTypeMismatch{Key: f.key, ExpectedType: "bool", Value: details.Value})
}
details.Value = boolVal
return details
Expand Down Expand Up @@ -144,7 +143,7 @@ func (f IntFlag) GetValueDetails(snap *Snapshot) EvaluationDetails {
}
intVal, ok := convertInt(details.Value)
if !ok {
return produceDetailsWithError(f.key, f.defaultValue, snap, fmt.Errorf("could not convert %s to int", details.Value))
return produceDetailsWithError(f.key, f.defaultValue, snap, ErrSettingTypeMismatch{Key: f.key, ExpectedType: "int", Value: details.Value})
}
details.Value = intVal
return details
Expand Down Expand Up @@ -204,7 +203,7 @@ func (f StringFlag) GetValueDetails(snap *Snapshot) EvaluationDetails {
}
stringVal, ok := details.Value.(string)
if !ok {
return produceDetailsWithError(f.key, f.defaultValue, snap, fmt.Errorf("could not convert %s to string", details.Value))
return produceDetailsWithError(f.key, f.defaultValue, snap, ErrSettingTypeMismatch{Key: f.key, ExpectedType: "string", Value: details.Value})
}
details.Value = stringVal
return details
Expand Down Expand Up @@ -264,7 +263,7 @@ func (f FloatFlag) GetValueDetails(snap *Snapshot) EvaluationDetails {
}
floatVal, ok := details.Value.(float64)
if !ok {
return produceDetailsWithError(f.key, f.defaultValue, snap, fmt.Errorf("could not convert %s to float64", details.Value))
return produceDetailsWithError(f.key, f.defaultValue, snap, ErrSettingTypeMismatch{Key: f.key, ExpectedType: "float", Value: details.Value})
}
details.Value = floatVal
return details
Expand Down
24 changes: 5 additions & 19 deletions snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,11 @@ import (
"errors"
"fmt"
"reflect"
"strings"
"sync"
"sync/atomic"
"time"
)

// ErrKeyNotFound is returned when a key is not found in the configuration.
type ErrKeyNotFound struct {
Key string
AvailableKeys []string
}

func (e ErrKeyNotFound) Error() string {
var availableKeys = ""
if len(e.AvailableKeys) > 0 {
availableKeys = "'" + strings.Join(e.AvailableKeys, "', '") + "'"
}
return fmt.Sprintf(
"failed to evaluate setting '%s' (the key was not found in config JSON); available keys: [%s]",
e.Key,
availableKeys,
)
}

// Snapshot holds a snapshot of the ConfigCat configuration.
// A snapshot is immutable once taken.
//
Expand Down Expand Up @@ -224,6 +205,11 @@ func (snap *Snapshot) details(id keyID, key string) (interface{}, string, *Targe
if snap == nil {
return nil, "", nil, nil, errors.New("snapshot is nil")
}
if snap.evaluators == nil {
err := ErrConfigJsonMissing{Key: key}
snap.logger.Errorf(1000, err.Error())
return nil, "", nil, nil, err
}
var eval settingEvalFunc
if int(id) < len(snap.evaluators) {
eval = snap.evaluators[id]
Expand Down

0 comments on commit 4158200

Please sign in to comment.