Skip to content

Conversation

@yasirfolio3
Copy link
Contributor

@yasirfolio3 yasirfolio3 commented Aug 22, 2019

Summary

  • Add GetFeatureVariableString as a function in client.go
  • Add GetVariableByKey() function in datafileprojectconfig/config.go

Tests

  • Unit tests in client_test.go

Issues

OASIS-5006


var typeName = ""
var valueParsed interface{}
switch valueType.(type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of passing in a variable for which we then get the type of, we can just pass in a string with the name of the type. This is less expensive to do.

func (o *OptimizelyClient) GetFeatureVariableString(featureKey string, variableKey string, userContext entities.UserContext) (value string, err error) {
val, err := o.getFeatureVariable("", featureKey, variableKey, userContext)
if err == nil {
switch val.(type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we can

if returnValue, ok := val.(string); ok {
 value = returnValue
}

Then at the end you return the named variables value, err

}

func (o *OptimizelyClient) getFeatureVariable(valueType interface{}, featureKey string, variableKey string, userContext entities.UserContext) (value interface{}, err error) {
if !o.isValid {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this check, along with the project config check below can likely be collapsed into a isValid method that takes in the method name so it can print it out if not valid

GetEventByKey(string) (entities.Event, error)
GetExperimentByKey(string) (entities.Experiment, error)
GetFeatureByKey(string) (entities.Feature, error)
GetFeatureFlagByKey(string) (datafileEntities.FeatureFlag, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong. Feature flags an d features are the same thing. You are bringing in the data file raw feature flags in here, which is not what we want.

}

// GetVariable returns variable for key from feature flag
func (f *FeatureFlag) GetVariable(key string) (Variable, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding this functionality to the model, let's add this method to the project config itself to get a variable with key from the feature

experimentKeyToIDMap map[string]string
experimentMap map[string]entities.Experiment
featureMap map[string]entities.Feature
featureFlagMap map[string]datafileEntities.FeatureFlag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment at bottom. This is incorrect. We should be using the entities.* which are public facing. The datafileEntities are internal models that we don't expose and are only needed so we can parse into entities.* models

Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly good. I have a few more suggestions

}

for _, variable := range rawVariation.Variables {
variation.Variables = append(variation.Variables, entities.VariationVariable{ID: variable.ID, Value: variable.Value})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can benefit from making Feature.Variables a map of string to variable for easier access. It also helps that we can easily iterate over maps in Go.

DefaultValue: "default",
ID: "1",
Key: "test_feature_flag_key",
Type: "string"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the } to a new line

DefaultValue: "default",
ID: "1",
Key: "test_feature_flag_key",
Type: "boolean"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the } to a new line

DefaultValue: "defaultString",
ID: "1",
Key: "test_feature_flag_key",
Type: "string"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto above

Key string `json:"key"`
FeatureEnabled bool `json:"featureEnabled"`
ID string `json:"id"`
Variables []VariationVariable `json:"variables"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use a map here to make look-ups easier.

Copy link
Contributor Author

@yasirfolio3 yasirfolio3 Aug 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the public facing dataModel of Variation with a map for Variables.
https://github.com/optimizely/go-sdk/pull/71/files#diff-f55225b09df17f6c9183261f39659ee1R22

assert.Equal(t, 1, processor.EventsCount())

time.Sleep(200 * time.Millisecond)
time.Sleep(2000 * time.Millisecond)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this because the test is flaky?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, i had instances where it failed, increasing the timeout made it more stable.

return value, err
}

func (o *OptimizelyClient) getFeatureVariable(valueType string, featureKey string, variableKey string, userContext entities.UserContext) (value interface{}, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think conceptually it will be better if that function returns the string instead of interface and GetFeatureVariableString does the checking. That way we eliminate valueType parameter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually GetFeatureVariableString will not have to do any checking - just pass-through, but GetFeatureVariableBool, GetFeatureVariableFloat, etc will do some checking

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a great point. I think we should do it that way even if it ends up duplicating some of the code at the bottom since strong typing does give us other kinds of guarantees @yasirfolio3

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@yasirfolio3 yasirfolio3 Aug 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikeng13 yes we can move the checking part to each individual api such as GetFeatureVariableString, but we would still need valueType argument to check if variable.Type is the required valueType, any thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are calling it as getFeatureVariable("string", featureKey, variableKey, userContext), that means variable "2" would be treated as a string in GetFeatureVariableString

Copy link
Contributor

@pawels-optimizely pawels-optimizely Aug 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also if you need the type, you can return a string and the type and check that in GetFeatureVariableString. try to avoid returning interface{} unless you really have to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also if you need the type, you can return a string and the type and check that in GetFeatureVariableString

il go ahead with this idea!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, sorry to be a pain - I am not 100% on this code yet, but it's kind of awkward flow when I read the code: 1. getting a string, 2. converting to interface, 3. returning the interface, 4. converting interface to string again - this is your overall flow. Just trying to avoid that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no worries, can you review it now!

}

// GetFeatureVariableString returns string feature variable value
func (o *OptimizelyClient) GetFeatureVariableString(featureKey string, variableKey string, userContext entities.UserContext) (value string, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice !, much cleaner now, no hardcoded parameter, more readable, no unneeded interface. small change but cleaned a lot of things and eliminated confusion. lgtm

Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one more comment on it!

}
}

if featureValue == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed? Empty strings should be OK. I think we should revert the above flow where if err != nil then we return this error message

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done! i'l do that for other getFeatureVariable API's, getFeatureVariableString doesn't require type-casting so i don't think we need it.

Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mikeproeng37
Copy link
Contributor

@yasirfolio3 can you please resolve merge conflicts?

@yasirfolio3
Copy link
Contributor Author

@yasirfolio3 can you please resolve merge conflicts?

yep, done!

Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mikeproeng37 mikeproeng37 merged commit 9d260d6 into go-alpha Aug 26, 2019
@yasirfolio3 yasirfolio3 deleted the yasir/featurevariablestring branch August 27, 2019 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants