Skip to content
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(OptimizelyConfig): Add new fields to OptimizelyConfig #266

Merged
merged 26 commits into from
Aug 2, 2021

Conversation

mnoman09
Copy link
Contributor

@mnoman09 mnoman09 commented Jun 30, 2021

Summary

The following new public properties are added to OptimizelyConfig:

  • sdkKey
  • environmentKey
  • attributes
  • audiences
  • events
  • experimentRules and deliveryRules to OptimizelyFeature
  • audiences to OptimizelyExperiment

Test plan

All FSC tests OPTIMIZELY_CONFIG_V2 tests should pass.
FSC OptimizelyConfig link

@mnoman09 mnoman09 changed the title WIP: Added new Optimizely config fields (DO NOT REVIEW) feat(OptimizelyConfig): Add new fields to OptimizelyConfig Jul 8, 2021
@msohailhussain msohailhussain marked this pull request as ready for review July 12, 2021 19:44
@msohailhussain msohailhussain requested a review from a team as a code owner July 12, 2021 19:44
var experimentMap = GetExperimentsMap(projectConfig);
var featureMap = GetFeaturesMap(projectConfig, experimentMap);
OptimizelyConfig = new OptimizelyConfig(projectConfig.Revision,
projectConfig.SDKKey,
projectConfig.EnvironmentKey,
attributes,
Copy link
Contributor

Choose a reason for hiding this comment

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

won't this break existing constructor?

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 actually attributes sdkKey and env key are all in the new constructor.


private Audience[] GetAudiences(ProjectConfig projectConfig)
{
var audiencesArr = Array.FindAll(projectConfig.Audiences, aud => !aud.Id.Equals("$opt_dummy_audience"));
Copy link
Contributor

Choose a reason for hiding this comment

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

am just thinking, can't we avoid this condition? may be we should remove somewhere else.

var featureId = projectConfig.GetExperimentFeatureList(experimentId)?.FirstOrDefault();
var featureIdVariablesMap = GetFeatureIdVariablesMap(projectConfig);
var variablesMap = new Dictionary<string, OptimizelyVariable>();
string featureIdRollout = null;
if (rolloutId != null)
Copy link
Contributor

Choose a reason for hiding this comment

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

lets discuss offline, need to discuss this logic.

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 you add rolloutId checking since "GetExperimentFeatureList" returns a map for experiment-to-feature map not for delivery-rule-to-feature map. What about extend "GetExperimentFeatureList" or similar to cover delivery-rules as well and removing "rolloutId" passing around.

if (item is JArray)
{
subAudience = GetSerializedAudiences(((JArray)item).ToObject<List<object>>(), audienceIdMap);
subAudience = "(" + subAudience + ")";
Copy link
Contributor

Choose a reason for hiding this comment

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

use StringBuilder.

Copy link
Contributor

Choose a reason for hiding this comment

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

use append where you are concatenating strings.

@mnoman09 mnoman09 requested a review from msohailhussain July 13, 2021 14:52
@mnoman09 mnoman09 removed their assignment Jul 13, 2021
public IDictionary<string, OptimizelyExperiment> ExperimentsMap { get; private set; }
public IDictionary<string, OptimizelyFeature> FeaturesMap { get; private set; }

private string _datafile;

Copy link

Choose a reason for hiding this comment

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

If we have auto formatted code, we won't have to make these kinds of fixes.

@msohailhussain msohailhussain requested a review from jaeopt July 21, 2021 23:36
Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

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

A few changes and more tests suggested

Comment on lines 30 to 32
public Entity.Event[] Events { get; private set; }
public OptimizelyAudience[] Audiences { get; private set; }
public Attribute[] Attributes { get; private set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't we add new public types OptimzelyEvent and OptimizelyAttribute? I understand content-wise they currently look identical, but those are types that we'll open to our customers.

{
Revision = revision;
SDKKey = sdkKey;
Attributes = attributes;
Audiences = audiences;
Events = events;
EnvironmentKey = environmentKey;
ExperimentsMap = experimentsMap;
FeaturesMap = featuresMap;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to have these multiple constructors? This is for creating OptimizelyConfig internally.

Copy link
Contributor

Choose a reason for hiding this comment

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

These are publicly exposed classes, that's why we added another constructor.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's expected that clients use OptimizelyConfig object, not building it. Consider not exposing constructor to public if can be controlled.

var featureId = projectConfig.GetExperimentFeatureList(experimentId)?.FirstOrDefault();
var featureIdVariablesMap = GetFeatureIdVariablesMap(projectConfig);
var variablesMap = new Dictionary<string, OptimizelyVariable>();
string featureIdRollout = null;
if (rolloutId != null)
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 you add rolloutId checking since "GetExperimentFeatureList" returns a map for experiment-to-feature map not for delivery-rule-to-feature map. What about extend "GetExperimentFeatureList" or similar to cover delivery-rules as well and removing "rolloutId" passing around.


featureId = featureId ?? featureIdRollout;
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 this is to cover features which are mapped to delivery-rules. See my comments above.

@@ -189,6 +125,7 @@ public void TestGetOptimizelyConfigService()
"audience_combinations_experiment", new OptimizelyExperiment (
id: "1323241598",
key:"audience_combinations_experiment",
audiences: "(\"exactString\" OR \"substringString\") AND (\"exists\" OR \"exactNumber\" OR \"gtNumber\" OR \"ltNumber\" OR \"exactBoolean\")",
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add separate test cases to cover 13 cases for audience serialization in TDD?

new OptimizelyAudience("3468206647", "gtNumber", new object[] { "and", new object[] { "or", new object[] { "or", new Dictionary<string, object>() { { "name", "lasers" }, { "type", "custom_attribute" }, { "match", "gt" }, { "value", 70 } } } } }),
new OptimizelyAudience("3468206644", "ltNumber", new object[] { "and", new object[] { "or", new object[] { "or", new Dictionary<string, object>() { { "name", "lasers" }, { "type", "custom_attribute" }, { "match", "lt" }, { "value", 1.0 } } } } }),
new OptimizelyAudience("3468206643", "exactBoolean", new object[] { "and", new object[] { "or", new object[] { "or", new Dictionary<string, object>() { { "name", "should_do_it" }, { "type", "custom_attribute" }, { "match", "exact" }, { "value", true } } } } }),
new OptimizelyAudience("3468206648", "notExist", new object[] { "not", new Dictionary<string, object>() { { "name", "input_value" }, { "type", "custom_attribute" }, { "match", "exists" } } }),
Copy link
Contributor

Choose a reason for hiding this comment

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

Also need validate "$opt_dummy_audience" audience is filtered out. See TDD.

new OptimizelyAudience("3468206644", "$$dummyLtNumber", "{\"type\": \"custom_attribute\", \"name\": \"$opt_dummy_attribute\", \"value\": \"impossible_value\"}"),
new OptimizelyAudience("3468206643", "$$dummyExactBoolean", "{\"type\": \"custom_attribute\", \"name\": \"$opt_dummy_attribute\", \"value\": \"impossible_value\"}"),
new OptimizelyAudience("0", "$$dummy", "{\"type\": \"custom_attribute\", \"name\": \"$opt_dummy_attribute\", \"value\": \"impossible_value\"}"),
new OptimizelyAudience("3988293898", "substringString", new object[] { "and", new object[] { "or", new object[] { "or", new Dictionary<string, string>() { { "name", "house" }, { "type", "custom_attribute" }, { "match", "substring" }, { "value", "Slytherin" } } } } }),
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we modify tests to validate that typedAudiences and audiences filter out same "names"?

{
cond = string.IsNullOrEmpty(cond) ? "OR" : cond;
sAudience = string.IsNullOrEmpty(sAudience.ToString()) ? new StringBuilder(cond + " \"" + audienceIdMap[itemStr]?.Name + "\"") :
sAudience.Append(" " + cond + " \"" + audienceIdMap[itemStr]?.Name + "\"");
Copy link
Contributor

Choose a reason for hiding this comment

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

append

}
else
{
sAudience = new StringBuilder("\"" + audienceIdMap[itemStr]?.Name + "\"");
Copy link
Contributor

Choose a reason for hiding this comment

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

append

Copy link
Contributor

Choose a reason for hiding this comment

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

or use .format or use interpolation string.

Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

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

Looks good. Suggested to add 2 more test cases for audience serialization.

new List<object>() { "and", new JArray() { "or", "3468206642", "3988293898" }, "3468206646" },
new List<object>() { "and", new JArray() { "or", "3468206642", new JArray() { "and", "3988293898", "3468206646" } }, new JArray() { "and", "3988293899", new JArray() { "or", "3468206647", "3468206643" } } },
new List<object>() { "and", "and" },
new List<object>() { "not", new JArray() { "and", "3468206642", "3988293898" } }
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add 2 more cases from TDD:

  • empty audiences []
  • not match id: ["or", "1", "999999999"]

mnoman09 and others added 2 commits July 30, 2021 14:13
Serialized condition of typedAudience for Optimizelyconfig
if key of audience does not exist in map then use the given id as it is.
Added unit test of audience as empty and audienceId does not exist
@msohailhussain
Copy link
Contributor

Copy link
Contributor

@msohailhussain msohailhussain left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

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

LGTM

@jaeopt jaeopt merged commit db932a5 into master Aug 2, 2021
@jaeopt jaeopt deleted the mnoman/optimizelyConfigNewFields branch August 2, 2021 22:55
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.

5 participants