-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(flags): fall back to /decide
endpoint for GetFeatureFlag
and GetAllFlags
so that users can use this library without needing a personal API Key
#46
Conversation
@@ -325,7 +325,7 @@ func TestFlagGroup(t *testing.T) { | |||
t.Errorf("Expected personProperties to be map[region:Canada], got %s", reqBody.PersonProperties) | |||
} | |||
|
|||
groupPropertiesEquality := reflect.DeepEqual(reqBody.GroupProperties, map[string]Properties{"company": Properties{"name": "Project Name 1"}}) | |||
groupPropertiesEquality := reflect.DeepEqual(reqBody.GroupProperties, map[string]Properties{"company": {"name": "Project Name 1"}}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compiler told me that Properties
field was unused.
@@ -311,7 +311,7 @@ func (poller *FeatureFlagsPoller) computeFlagLocally( | |||
groupName, exists := poller.groups[fmt.Sprintf("%d", *flag.Filters.AggregationGroupTypeIndex)] | |||
|
|||
if !exists { | |||
errMessage := "Flag has unknown group type index" | |||
errMessage := "flag has unknown group type index" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compiler told me to lowercase error messages
@@ -467,7 +467,7 @@ func matchCohort(property FlagProperty, properties Properties, cohorts map[strin | |||
cohortId := fmt.Sprint(property.Value) | |||
propertyGroup, ok := cohorts[cohortId] | |||
if !ok { | |||
return false, fmt.Errorf("Can't match cohort: cohort %s not found", cohortId) | |||
return false, fmt.Errorf("can't match cohort: cohort %s not found", cohortId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compiler told me to lowercase error messages
@@ -578,7 +578,7 @@ func matchProperty(property FlagProperty, properties Properties) (bool, error) { | |||
return false, &InconclusiveMatchError{"Can't match properties with operator is_not_set"} | |||
} | |||
|
|||
override_value, _ := properties[key] | |||
override_value := properties[key] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compiler told me this was unused.
@@ -637,7 +637,7 @@ func matchProperty(property FlagProperty, properties Properties) (bool, error) { | |||
valueString = strconv.Itoa(valueInt) | |||
r, err = regexp.Compile(valueString) | |||
} else { | |||
errMessage := "Regex expression not allowed" | |||
errMessage := "regex expression not allowed" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compiler told me to lowercase error messages
@@ -705,7 +706,7 @@ func TestClientMaxConcurrentRequests(t *testing.T) { | |||
func(m APIMessage, e error) { errchan <- e }, | |||
}, | |||
Transport: testTransportDelayed, | |||
// Only one concurreny request can be submitted, because the transport | |||
// Only one concurrency request can be submitted, because the transport |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drive-by spellcheck
receivedErrors := [2]error{} | ||
receivedErrors[0] = client.ReloadFeatureFlags() | ||
_, receivedErrors[1] = client.IsFeatureEnabled( | ||
FeatureFlagPayload{ | ||
Key: "some key", | ||
DistinctId: "some id", | ||
}, | ||
) | ||
_, receivedErrors[2] = client.GetFeatureFlag( | ||
FeatureFlagPayload{ | ||
Key: "some key", | ||
DistinctId: "some id", | ||
}, | ||
) | ||
_, receivedErrors[3] = client.GetFeatureFlags() | ||
_, receivedErrors[1] = client.GetFeatureFlags() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we only have 2 methods that error on a missing PersonalApiKey, and neither of them are hot paths (i.e. users don't need them to use our feature flags).
@@ -766,6 +755,442 @@ func TestFeatureFlagsWithNoPersonalApiKey(t *testing.T) { | |||
|
|||
} | |||
|
|||
func TestIsFeatureEnabled(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method didn't have tests, so now it does. Boy scout principle!
} | ||
} | ||
|
||
func TestGetFeatureFlagWithNoPersonalApiKey(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test the fallbacks
|
||
// flagValue, valueError := client.GetFeatureFlag("simpleFlag", "hey", false, Groups{}, NewProperties(), map[string]Properties{}) | ||
// if valueError != nil || flagValue != true { | ||
// t.Errorf("simple flag with null rollout percentage should have value 'true'") | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was dead code
if c.featureFlagsPoller != nil { | ||
// get feature flags from the poller, which uses the personal api key | ||
flagValue, err = c.featureFlagsPoller.GetFeatureFlag(flagConfig) | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should I log that we're falling back here? Does it matter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe, but only at a trace/info/debug level. Might be good for investigations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func (c *client) setLastCapturedEvent(event Capture) { | ||
c.lastEventMutex.Lock() | ||
defer c.lastEventMutex.Unlock() | ||
c.lastCapturedEvent = &event | ||
} | ||
|
||
func (c *client) GetLastCapturedEvent() *Capture { | ||
c.lastEventMutex.RLock() | ||
defer c.lastEventMutex.RUnlock() | ||
if c.lastCapturedEvent == nil { | ||
return nil | ||
} | ||
// Return a copy to avoid data races | ||
eventCopy := *c.lastCapturedEvent | ||
return &eventCopy | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eh, should be fine. Doesn't seem too unsafe to expose
if c.featureFlagsPoller != nil { | ||
// get feature flags from the poller, which uses the personal api key | ||
flagValue, err = c.featureFlagsPoller.GetFeatureFlag(flagConfig) | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe, but only at a trace/info/debug level. Might be good for investigations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ship it!
Does what it says on the tin. I made the following changes:
GetFeatureFlag
andGetAllFlags
that falls back to using the/decide
endpoint for fetching feature flags if there's no PersonalApiKey providedPotential TODOs:
Other things: