fix: missing/nil custom variables in fractional operator (#1295)

## This PR

Fixes https://github.com/open-feature/flagd/issues/1285

Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com>
This commit is contained in:
Kavindu Dodanduwa 2024-04-19 12:48:55 -07:00 committed by GitHub
parent 281c8094ef
commit 418c5cd7c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 45 additions and 10 deletions

View File

@ -56,6 +56,11 @@ func parseFractionalEvaluationData(values, data any) (string, []fractionalEvalua
if ok { if ok {
valuesArray = valuesArray[1:] valuesArray = valuesArray[1:]
} else { } else {
// check for nil here as custom property could be nil/missing
if valuesArray[0] == nil {
valuesArray = valuesArray[1:]
}
targetingKey, ok := dataMap[targetingKeyKey].(string) targetingKey, ok := dataMap[targetingKeyKey].(string)
if !ok { if !ok {
return "", nil, errors.New("bucketing value not supplied and no targetingKey in context") return "", nil, errors.New("bucketing value not supplied and no targetingKey in context")
@ -78,7 +83,8 @@ func parseFractionalEvaluationDistributions(values []any) ([]fractionalEvaluatio
for i := 0; i < len(values); i++ { for i := 0; i < len(values); i++ {
distributionArray, ok := values[i].([]any) distributionArray, ok := values[i].([]any)
if !ok { if !ok {
return nil, errors.New("distribution elements aren't of type []any") return nil, errors.New("distribution elements aren't of type []any. " +
"please check your rule in flag definition")
} }
if len(distributionArray) != 2 { if len(distributionArray) != 2 {

View File

@ -12,7 +12,7 @@ import (
func TestFractionalEvaluation(t *testing.T) { func TestFractionalEvaluation(t *testing.T) {
ctx := context.Background() ctx := context.Background()
flags := Flags{ commonFlags := Flags{
Flags: map[string]model.Flag{ Flags: map[string]model.Flag{
"headerColor": { "headerColor": {
State: "ENABLED", State: "ENABLED",
@ -95,7 +95,7 @@ func TestFractionalEvaluation(t *testing.T) {
expectedErrorCode string expectedErrorCode string
}{ }{
"rachel@faas.com": { "rachel@faas.com": {
flags: flags, flags: commonFlags,
flagKey: "headerColor", flagKey: "headerColor",
context: map[string]any{ context: map[string]any{
"email": "rachel@faas.com", "email": "rachel@faas.com",
@ -105,7 +105,7 @@ func TestFractionalEvaluation(t *testing.T) {
expectedReason: model.TargetingMatchReason, expectedReason: model.TargetingMatchReason,
}, },
"monica@faas.com": { "monica@faas.com": {
flags: flags, flags: commonFlags,
flagKey: "headerColor", flagKey: "headerColor",
context: map[string]any{ context: map[string]any{
"email": "monica@faas.com", "email": "monica@faas.com",
@ -115,7 +115,7 @@ func TestFractionalEvaluation(t *testing.T) {
expectedReason: model.TargetingMatchReason, expectedReason: model.TargetingMatchReason,
}, },
"joey@faas.com": { "joey@faas.com": {
flags: flags, flags: commonFlags,
flagKey: "headerColor", flagKey: "headerColor",
context: map[string]any{ context: map[string]any{
"email": "joey@faas.com", "email": "joey@faas.com",
@ -125,7 +125,7 @@ func TestFractionalEvaluation(t *testing.T) {
expectedReason: model.TargetingMatchReason, expectedReason: model.TargetingMatchReason,
}, },
"ross@faas.com": { "ross@faas.com": {
flags: flags, flags: commonFlags,
flagKey: "headerColor", flagKey: "headerColor",
context: map[string]any{ context: map[string]any{
"email": "ross@faas.com", "email": "ross@faas.com",
@ -135,7 +135,7 @@ func TestFractionalEvaluation(t *testing.T) {
expectedReason: model.TargetingMatchReason, expectedReason: model.TargetingMatchReason,
}, },
"rachel@faas.com with custom seed": { "rachel@faas.com with custom seed": {
flags: flags, flags: commonFlags,
flagKey: "customSeededHeaderColor", flagKey: "customSeededHeaderColor",
context: map[string]any{ context: map[string]any{
"email": "rachel@faas.com", "email": "rachel@faas.com",
@ -145,7 +145,7 @@ func TestFractionalEvaluation(t *testing.T) {
expectedReason: model.TargetingMatchReason, expectedReason: model.TargetingMatchReason,
}, },
"monica@faas.com with custom seed": { "monica@faas.com with custom seed": {
flags: flags, flags: commonFlags,
flagKey: "customSeededHeaderColor", flagKey: "customSeededHeaderColor",
context: map[string]any{ context: map[string]any{
"email": "monica@faas.com", "email": "monica@faas.com",
@ -155,7 +155,7 @@ func TestFractionalEvaluation(t *testing.T) {
expectedReason: model.TargetingMatchReason, expectedReason: model.TargetingMatchReason,
}, },
"joey@faas.com with custom seed": { "joey@faas.com with custom seed": {
flags: flags, flags: commonFlags,
flagKey: "customSeededHeaderColor", flagKey: "customSeededHeaderColor",
context: map[string]any{ context: map[string]any{
"email": "joey@faas.com", "email": "joey@faas.com",
@ -165,7 +165,7 @@ func TestFractionalEvaluation(t *testing.T) {
expectedReason: model.TargetingMatchReason, expectedReason: model.TargetingMatchReason,
}, },
"ross@faas.com with custom seed": { "ross@faas.com with custom seed": {
flags: flags, flags: commonFlags,
flagKey: "customSeededHeaderColor", flagKey: "customSeededHeaderColor",
context: map[string]any{ context: map[string]any{
"email": "ross@faas.com", "email": "ross@faas.com",
@ -389,6 +389,35 @@ func TestFractionalEvaluation(t *testing.T) {
expectedValue: "#0000FF", expectedValue: "#0000FF",
expectedReason: model.TargetingMatchReason, expectedReason: model.TargetingMatchReason,
}, },
"missing email - parser should ignore nil/missing custom variables and continue": {
flags: Flags{
Flags: map[string]model.Flag{
"headerColor": {
State: "ENABLED",
DefaultVariant: "red",
Variants: map[string]any{
"red": "#FF0000",
"blue": "#0000FF",
},
Targeting: []byte(
`{
"fractional": [
{"var": "email"},
["red",50],
["blue",50]
]
}`),
},
},
},
flagKey: "headerColor",
context: map[string]any{
"targetingKey": "foo@foo.com",
},
expectedVariant: "red",
expectedValue: "#FF0000",
expectedReason: model.TargetingMatchReason,
},
} }
const reqID = "default" const reqID = "default"
for name, tt := range tests { for name, tt := range tests {