From 36b86083a1aa19878caa0cc52be6386dc13b7640 Mon Sep 17 00:00:00 2001 From: stephenmcconkey Date: Thu, 7 Jul 2022 13:50:50 +0100 Subject: [PATCH 1/3] Change 'number' to 'int' input on number variations --- evaluation/evaluator.go | 4 ++-- evaluation/evaluator_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/evaluation/evaluator.go b/evaluation/evaluator.go index d1f53819..a705ad7c 100644 --- a/evaluation/evaluator.go +++ b/evaluation/evaluator.go @@ -386,8 +386,8 @@ func (e Evaluator) IntVariation(identifier string, target *Target, defaultValue // NumberVariation returns number evaluation for target func (e Evaluator) NumberVariation(identifier string, target *Target, defaultValue float64) float64 { - - variation, err := e.evaluate(identifier, target, "number") + //all numbers are stored as ints in the database + variation, err := e.evaluate(identifier, target, "int") if err != nil { e.logger.Errorf("Error while evaluating number flag '%s', err: %v", identifier, err) return defaultValue diff --git a/evaluation/evaluator_test.go b/evaluation/evaluator_test.go index ce20d945..f41aa192 100644 --- a/evaluation/evaluator_test.go +++ b/evaluation/evaluator_test.go @@ -136,7 +136,7 @@ var ( Variation: &heavyWeight, }, Variations: numberVariations, - Kind: "number", + Kind: "int", }, org: { Feature: org, @@ -173,7 +173,7 @@ var ( Value: invalidNumberValue, }, }, - Kind: "number", + Kind: "int", }, invalidJSON: { Feature: invalidJSON, From 60ec42aaed703eb872131bc4dc20068250e2f75a Mon Sep 17 00:00:00 2001 From: stephenmcconkey Date: Tue, 26 Jul 2022 10:53:49 +0100 Subject: [PATCH 2/3] Check prereq flags for nested prereqs, update example app --- evaluation/feature.go | 33 ++++++ evaluation/feature_test.go | 200 ++++++++++++++++++++++++++++++++++++ examples/getting_started.go | 10 +- 3 files changed, 238 insertions(+), 5 deletions(-) diff --git a/evaluation/feature.go b/evaluation/feature.go index bda31240..48955178 100644 --- a/evaluation/feature.go +++ b/evaluation/feature.go @@ -304,6 +304,13 @@ func prereqsSatisfied(fc FeatureConfig, target *Target, flags map[string]Feature continue } + if prereqFlag.Prerequisites != nil { + res := checkPreReqsForPreReqs(prereqFlag.Prerequisites, flags, target) + if res == false { + return false + } + } + variationToMatch := prereqFlag.Variations.FindByIdentifier(prereqFlag.GetVariationName(target)) if pre.Feature == prereqFlag.Feature { @@ -540,3 +547,29 @@ type WeightedVariation struct { Variation string Weight int } + +func checkPreReqsForPreReqs(preReqFlagPreReqs []Prerequisite, flags map[string]FeatureConfig, target *Target) bool { + for _, preReq := range preReqFlagPreReqs { + nestedPreReq, ok := flags[preReq.Feature] + if !ok { + continue + } + + if len(nestedPreReq.Prerequisites) > 0 { + nested := checkPreReqsForPreReqs(nestedPreReq.Prerequisites, flags, target) + if nested == false { + return false + } + } + + preReqVariationToMatch := nestedPreReq.Variations.FindByIdentifier(nestedPreReq.GetVariationName(target)) + if preReq.Feature == nestedPreReq.Feature { + for _, variation := range preReq.Variations { + if variation != preReqVariationToMatch.Value { + return false + } + } + } + } + return true +} diff --git a/evaluation/feature_test.go b/evaluation/feature_test.go index fa3bef11..286c3525 100644 --- a/evaluation/feature_test.go +++ b/evaluation/feature_test.go @@ -1546,3 +1546,203 @@ func TestFeatureConfig_prereqsSatisfied(t *testing.T) { }) } } + +func TestCheckPreReqsForPreReqs(t *testing.T) { + trueVariation := Variation{ + Name: stringPtr("True"), + Value: "true", + Identifier: "true", + } + + falseVariation := Variation{ + Name: stringPtr("False"), + Value: "false", + Identifier: "false", + } + + blueVariation := Variation{ + Name: stringPtr("blue"), + Value: "blue", + Identifier: "blue", + } + + redVariation := Variation{ + Name: stringPtr("red"), + Value: "red", + Identifier: "red", + } + + flags := map[string]FeatureConfig{ + "flag1": { + DefaultServe: Serve{ + Variation: stringPtr("true"), + }, + Environment: "dev", + Feature: "flag1", + Kind: "boolean", + OffVariation: "false", + Prerequisites: nil, + Project: "default", + Rules: nil, + State: "on", + VariationToTargetMap: nil, + Variations: Variations{ + trueVariation, falseVariation, + }, + Segments: nil, + }, + "flag2": { + DefaultServe: Serve{ + Variation: stringPtr("true"), + }, + Environment: "dev", + Feature: "flag2", + Kind: "boolean", + OffVariation: "false", + Prerequisites: []Prerequisite{ + {Feature: "flag1", + Variations: []string{"true"}, + }, + }, + Project: "default", + Rules: nil, + State: "on", + VariationToTargetMap: nil, + Variations: Variations{ + trueVariation, falseVariation, + }, + Segments: nil, + }, + "flag3": { + DefaultServe: Serve{ + Variation: stringPtr("true"), + }, + Environment: "dev", + Feature: "flag3", + Kind: "boolean", + OffVariation: "false", + Prerequisites: []Prerequisite{ + {Feature: "flag2", + Variations: []string{"true"}, + }, + }, + Project: "default", + Rules: nil, + State: "on", + VariationToTargetMap: nil, + Variations: Variations{ + trueVariation, falseVariation, + }, + Segments: nil, + }, + "mv1": { + DefaultServe: Serve{ + Variation: stringPtr("red"), + }, + Environment: "dev", + Feature: "mv1", + Kind: "string", + OffVariation: "blue", + Prerequisites: []Prerequisite{ + {Feature: "flag1", + Variations: []string{"true"}, + }, + }, + Project: "default", + Rules: nil, + State: "on", + VariationToTargetMap: nil, + Variations: Variations{ + redVariation, blueVariation, + }, + Segments: nil, + }, + } + tests := []struct { + name string + expected bool + preReqFlagPreReqs []Prerequisite + flags map[string]FeatureConfig + target *Target + }{ + { + name: "Given I have no preReqFlagPreReqs", + expected: true, + flags: nil, + preReqFlagPreReqs: nil, + target: nil, + }, + { + name: "Given I have a preReqFlagPreReqs that has no nested preregs that will match", + expected: true, + flags: flags, + preReqFlagPreReqs: []Prerequisite{ + {Feature: "flag1", + Variations: []string{"true"}, + }, + }, + target: nil, + }, + { + name: "Given I have a preReqFlagPreReqs that has no nested prereqs that will not match", + expected: false, + flags: flags, + preReqFlagPreReqs: []Prerequisite{ + {Feature: "flag1", + Variations: []string{"false"}, + }, + }, + target: nil, + }, + { + name: "Given I have a preReqFlagPreReqs that has nested prereqs that will match i.e. flag 3 depends on flag 2, which depends on flag 1", + expected: true, + flags: flags, + preReqFlagPreReqs: []Prerequisite{ + {Feature: "flag3", + Variations: []string{"true"}, + }, + }, + target: nil, + }, + { + name: "Given I have a preReqFlagPreReqs that has nested prereqs that will not match", + expected: false, + flags: flags, + preReqFlagPreReqs: []Prerequisite{ + {Feature: "flag3", + Variations: []string{"false"}, + }, + }, + target: nil, + }, + { + name: "Given I have a preReqFlagPreReqs of a mv flag that has nested prereqs that will not match", + expected: false, + flags: flags, + preReqFlagPreReqs: []Prerequisite{ + {Feature: "mv1", + Variations: []string{"blue"}, + }, + }, + target: nil, + }, + { + name: "Given I have a preReqFlagPreReqs of a mv flag that has nested prereqs that will match", + expected: true, + flags: flags, + preReqFlagPreReqs: []Prerequisite{ + {Feature: "mv1", + Variations: []string{"red"}, + }, + }, + target: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, checkPreReqsForPreReqs(tt.preReqFlagPreReqs, tt.flags, tt.target)) + }) + } +} diff --git a/examples/getting_started.go b/examples/getting_started.go index d99b39ef..422b80d3 100644 --- a/examples/getting_started.go +++ b/examples/getting_started.go @@ -11,14 +11,14 @@ import ( var ( flagName string = getEnvOrDefault("FF_FLAG_NAME", "harnessappdemodarkmode") - apiKey string = getEnvOrDefault("FF_API_KEY", "changeme") + sdkKey string = getEnvOrDefault("FF_API_KEY", "changeme") ) func main() { log.Println("Harness SDK Getting Started") // Create a feature flag client - client, err := harness.NewCfClient(apiKey) + client, err := harness.NewCfClient(sdkKey) if err != nil { log.Fatalf("could not connect to CF servers %s\n", err) } @@ -26,9 +26,9 @@ func main() { // Create a target (different targets can get different results based on rules) target := evaluation.Target{ - Identifier: "golangsdk", - Name: "GolangSDK", - Attributes: &map[string]interface{}{"location": "emea"}, + Identifier: "HT_1", + Name: "Harness_Target_1", + Attributes: &map[string]interface{}{"email": "demo@harness.io"}, } // Loop forever reporting the state of the flag From 0e82c4fc9175742163376eb53d3febead941dc26 Mon Sep 17 00:00:00 2001 From: stephenmcconkey Date: Tue, 26 Jul 2022 11:00:38 +0100 Subject: [PATCH 3/3] simple refactor --- evaluation/feature.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evaluation/feature.go b/evaluation/feature.go index 48955178..7b6b4ac0 100644 --- a/evaluation/feature.go +++ b/evaluation/feature.go @@ -306,7 +306,7 @@ func prereqsSatisfied(fc FeatureConfig, target *Target, flags map[string]Feature if prereqFlag.Prerequisites != nil { res := checkPreReqsForPreReqs(prereqFlag.Prerequisites, flags, target) - if res == false { + if !res { return false } } @@ -557,7 +557,7 @@ func checkPreReqsForPreReqs(preReqFlagPreReqs []Prerequisite, flags map[string]F if len(nestedPreReq.Prerequisites) > 0 { nested := checkPreReqsForPreReqs(nestedPreReq.Prerequisites, flags, target) - if nested == false { + if !nested { return false } }