Skip to content

Commit

Permalink
Layer Group Name (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
sroyal-statsig authored Mar 13, 2024
1 parent d152dd8 commit 21b0eaf
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 30 deletions.
14 changes: 7 additions & 7 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ func (c *Client) ManuallyLogLayerParameterExposure(user User, layer string, para
}
user = normalizeUser(user, *c.options)
res := c.evaluator.evalLayer(user, layer)
config := NewLayer(layer, res.JsonValue, res.RuleID, res.GroupName, nil).configBase
config := NewLayer(layer, res.JsonValue, res.RuleID, res.GroupName, nil, res.ConfigDelegate)
context := &logContext{isManualExposure: true}
c.logger.logLayerExposure(user, config, parameter, *res, res.EvaluationDetails, context)
c.logger.logLayerExposure(user, *config, parameter, *res, res.EvaluationDetails, context)
})
}

Expand Down Expand Up @@ -367,7 +367,7 @@ func (c *Client) getConfigImpl(user User, name string, context getConfigImplCont
func (c *Client) getLayerImpl(user User, name string, options getLayerOptions) Layer {
return c.errorBoundary.captureGetLayer(func() Layer {
if !c.verifyUser(user) {
return *NewLayer(name, nil, "", "", nil)
return *NewLayer(name, nil, "", "", nil, "")
}

user = normalizeUser(user, *c.options)
Expand All @@ -377,18 +377,18 @@ func (c *Client) getLayerImpl(user User, name string, options getLayerOptions) L
res = c.fetchConfigFromServer(user, name)
}

logFunc := func(config configBase, parameterName string) {
logFunc := func(layer Layer, parameterName string) {
var exposure *ExposureEvent = nil
if !options.disableLogExposures {
context := &logContext{isManualExposure: false}
exposure = c.logger.logLayerExposure(user, config, parameterName, *res, res.EvaluationDetails, context)
exposure = c.logger.logLayerExposure(user, layer, parameterName, *res, res.EvaluationDetails, context)
}
if c.options.EvaluationCallbacks.LayerEvaluationCallback != nil {
c.options.EvaluationCallbacks.LayerEvaluationCallback(name, parameterName, DynamicConfig{configBase: config}, exposure)
c.options.EvaluationCallbacks.LayerEvaluationCallback(name, parameterName, DynamicConfig{layer.configBase}, exposure)
}
}

return *NewLayer(name, res.JsonValue, res.RuleID, res.GroupName, &logFunc)
return *NewLayer(name, res.JsonValue, res.RuleID, res.GroupName, &logFunc, res.ConfigDelegate)
})
}

Expand Down
2 changes: 1 addition & 1 deletion logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ func (l *logger) logConfigExposure(

func (l *logger) logLayerExposure(
user User,
config configBase,
config Layer,
parameterName string,
evalResult evalResult,
evalDetails *evaluationDetails,
Expand Down
100 changes: 80 additions & 20 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,18 @@ type Event struct {
}

type configBase struct {
Name string `json:"name"`
Value map[string]interface{} `json:"value"`
RuleID string `json:"rule_id"`
GroupName string `json:"group_name"`
EvaluationDetails *evaluationDetails `json:"evaluation_details"`
LogExposure *func(configBase, string) `json:"log_exposure"`
Name string `json:"name"`
Value map[string]interface{} `json:"value"`
RuleID string `json:"rule_id"`
GroupName string `json:"group_name"`
EvaluationDetails *evaluationDetails `json:"evaluation_details"`
}

type FeatureGate struct {
Name string `json:"name"`
Value bool `json:"value"`
RuleID string `json:"rule_id"`
GroupName string `json:"group_name"`
LogExposure *func(configBase, string)
Name string `json:"name"`
Value bool `json:"value"`
RuleID string `json:"rule_id"`
GroupName string `json:"group_name"`
}

// A json blob configured in the Statsig Console
Expand All @@ -52,6 +50,8 @@ type DynamicConfig struct {

type Layer struct {
configBase
LogExposure *func(Layer, string) `json:"log_exposure"`
AllocatedExperimentName string `json:"allocated_experiment_name"`
}

func NewGate(name string, value bool, ruleID string, groupName string) *FeatureGate {
Expand All @@ -68,7 +68,7 @@ func NewConfig(name string, value map[string]interface{}, ruleID string, groupNa
value = make(map[string]interface{})
}
return &DynamicConfig{
configBase{
configBase: configBase{
Name: name,
Value: value,
RuleID: ruleID,
Expand All @@ -78,24 +78,38 @@ func NewConfig(name string, value map[string]interface{}, ruleID string, groupNa
}
}

func NewLayer(name string, value map[string]interface{}, ruleID string, groupName string, logExposure *func(configBase, string)) *Layer {
func NewLayer(name string, value map[string]interface{}, ruleID string, groupName string, logExposure *func(Layer, string), allocatedExperimentName string) *Layer {
if value == nil {
value = make(map[string]interface{})
}
return &Layer{
configBase{
Name: name,
Value: value,
RuleID: ruleID,
GroupName: groupName,
LogExposure: logExposure,
configBase: configBase{
Name: name,
Value: value,
RuleID: ruleID,
GroupName: groupName,
},
AllocatedExperimentName: allocatedExperimentName,
LogExposure: logExposure,
}
}

// Gets the string value at the given key in the DynamicConfig
// Returns the fallback string if the item at the given key is not found or not of type string
func (d *configBase) GetString(key string, fallback string) string {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case string:
return val
}
}

return fallback
}

// Gets the string value at the given key in the DynamicConfig
// Returns the fallback string if the item at the given key is not found or not of type string
func (d *Layer) GetString(key string, fallback string) string {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case string:
Expand All @@ -110,6 +124,18 @@ func (d *configBase) GetString(key string, fallback string) string {
// Gets the float64 value at the given key in the DynamicConfig
// Returns the fallback float64 if the item at the given key is not found or not of type float64
func (d *configBase) GetNumber(key string, fallback float64) float64 {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case float64:
return val
}
}
return fallback
}

// Gets the float64 value at the given key in the DynamicConfig
// Returns the fallback float64 if the item at the given key is not found or not of type float64
func (d *Layer) GetNumber(key string, fallback float64) float64 {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case float64:
Expand All @@ -123,6 +149,18 @@ func (d *configBase) GetNumber(key string, fallback float64) float64 {
// Gets the boolean value at the given key in the DynamicConfig
// Returns the fallback boolean if the item at the given key is not found or not of type boolean
func (d *configBase) GetBool(key string, fallback bool) bool {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case bool:
return val
}
}
return fallback
}

// Gets the boolean value at the given key in the DynamicConfig
// Returns the fallback boolean if the item at the given key is not found or not of type boolean
func (d *Layer) GetBool(key string, fallback bool) bool {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case bool:
Expand All @@ -136,6 +174,18 @@ func (d *configBase) GetBool(key string, fallback bool) bool {
// Gets the slice value at the given key in the DynamicConfig
// Returns the fallback slice if the item at the given key is not found or not of type slice
func (d *configBase) GetSlice(key string, fallback []interface{}) []interface{} {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case []interface{}:
return val
}
}
return fallback
}

// Gets the slice value at the given key in the DynamicConfig
// Returns the fallback slice if the item at the given key is not found or not of type slice
func (d *Layer) GetSlice(key string, fallback []interface{}) []interface{} {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case []interface{}:
Expand All @@ -147,6 +197,16 @@ func (d *configBase) GetSlice(key string, fallback []interface{}) []interface{}
}

func (d *configBase) GetMap(key string, fallback map[string]interface{}) map[string]interface{} {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case map[string]interface{}:
return val
}
}
return fallback
}

func (d *Layer) GetMap(key string, fallback map[string]interface{}) map[string]interface{} {
if v, ok := d.Value[key]; ok {
switch val := v.(type) {
case map[string]interface{}:
Expand All @@ -157,7 +217,7 @@ func (d *configBase) GetMap(key string, fallback map[string]interface{}) map[str
return fallback
}

func logExposure(c *configBase, parameterName string) {
func logExposure(c *Layer, parameterName string) {
if c == nil || c.LogExposure == nil {
return
}
Expand Down
46 changes: 44 additions & 2 deletions types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,48 @@ func doValidation(t *testing.T, c *configBase) {
}
}

func doValidationLayer(t *testing.T, c *Layer) {
if c.Name != "test" {
t.Errorf("Failed to set name")
}
if c.RuleID != "rule_id" {
t.Errorf("Failed to set rule_id")
}
if c.GroupName != "group_name" {
t.Errorf("Failed to set group_name")
}

if c.GetString("String", "abc") != "str" {
t.Errorf("Failed to get string")
}
if c.GetString("Number", "abc") != "abc" {
t.Errorf("Failed to use fallback string")
}
if c.GetString("Object", "def") != "def" {
t.Errorf("Failed to use fallback string")
}

if c.GetNumber("String", 0.07) != 0.07 {
t.Errorf("Failed to use fallback number")
}
if c.GetNumber("Number", 0.07) != 143.7 {
t.Errorf("Failed to get number")
}
if c.GetNumber("Object", 4) != 4 {
t.Errorf("Failed to use fallback number")
}

if !c.GetBool("String", true) {
t.Errorf("Failed to use fallback boolean")
}
if !c.GetBool("Boolean", false) {
t.Errorf("Failed to get boolean")
}
if c.GetBool("Object", false) {
t.Errorf("Failed to use fallback boolean")
}
}

func TestBasic(t *testing.T) {
jsonMap := make(map[string]interface{})
_ = json.Unmarshal(
Expand Down Expand Up @@ -75,8 +117,8 @@ func TestBasic(t *testing.T) {
)
doValidation(t, &c.configBase)

l := NewLayer("test", jsonMap, "rule_id", "group_name", nil)
doValidation(t, &l.configBase)
l := NewLayer("test", jsonMap, "rule_id", "group_name", nil, "allocated_experiment_name")
doValidationLayer(t, l)

fallbackValues := make([]interface{}, 0)
fallbackValues = append(fallbackValues, 4, 5, 6)
Expand Down

0 comments on commit 21b0eaf

Please sign in to comment.