Skip to content

Commit

Permalink
feat(flag-decisions): Add support for sending flag decisions along wi…
Browse files Browse the repository at this point in the history
…th decision metadata. (#244)
  • Loading branch information
mnoman09 authored Oct 20, 2020
1 parent bbdb629 commit f22ad21
Show file tree
Hide file tree
Showing 20 changed files with 474 additions and 70 deletions.
3 changes: 3 additions & 0 deletions OptimizelySDK.Net35/OptimizelySDK.Net35.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@
<Compile Include="..\OptimizelySDK\Event\Entity\Visitor.cs">
<Link>Event\Entity\Visitor.cs</Link>
</Compile>
<Compile Include="..\OptimizelySDK\Event\Entity\DecisionMetadata.cs">
<Link>Event\Entity\DecisionMetadata.cs</Link>
</Compile>
<Compile Include="..\OptimizelySDK\Event\Entity\VisitorAttribute.cs">
<Link>Event\Entity\VisitorAttribute.cs</Link>
</Compile>
Expand Down
3 changes: 3 additions & 0 deletions OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@
<Compile Include="..\OptimizelySDK\Event\Entity\VisitorAttribute.cs">
<Link>Event\Entity\VisitorAttribute.cs</Link>
</Compile>
<Compile Include="..\OptimizelySDK\Event\Entity\DecisionMetadata.cs">
<Link>Event\Entity\DecisionMetadata.cs</Link>
</Compile>
<Compile Include="..\OptimizelySDK\Event\EventFactory.cs">
<Link>Event\EventFactory.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@
<Compile Include="..\OptimizelySDK\Event\Entity\VisitorAttribute.cs">
<Link>VisitorAttribute.cs</Link>
</Compile>
<Compile Include="..\OptimizelySDK\Event\Entity\DecisionMetadata.cs">
<Link>DecisionMetadata.cs</Link>
</Compile>
<Compile Include="..\OptimizelySDK\Event\Entity\Decision.cs">
<Link>DecisionEvent.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@
</Compile>
<Compile Include="..\OptimizelySDK\Event\Entity\Decision.cs">
<Link>Event\Entity\Decision.cs</Link>
</Compile>
<Compile Include="..\OptimizelySDK\Event\Entity\DecisionMetadata.cs">
<Link>Event\Entity\DecisionMetadata.cs</Link>
</Compile>
<Compile Include="..\OptimizelySDK\Event\Entity\EventBatch.cs">
<Link>Event\Entity\EventBatch.cs</Link>
Expand Down
22 changes: 20 additions & 2 deletions OptimizelySDK.Tests/EventTests/EventEntitiesTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
using System;
/**
*
* Copyright 2019-2020, Optimizely and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
Expand Down Expand Up @@ -104,7 +122,7 @@ public void TestImpressionEventEqualsSerializedPayload()
.WithTimeStamp(timeStamp)
.WithEventTags(null)
.Build();

var metadata = new DecisionMetadata("experiment", "experiment_key", "7716830082");
var decision = new Decision("7719770039", "7716830082", "77210100090");
var snapshot = new Snapshot(events: new SnapshotEvent[] { snapshotEvent }, decisions: new Decision[] { decision });

Expand Down
253 changes: 231 additions & 22 deletions OptimizelySDK.Tests/EventTests/EventFactoryTest.cs

Large diffs are not rendered by default.

23 changes: 20 additions & 3 deletions OptimizelySDK.Tests/EventTests/UserEventFactoryTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
using Moq;
/**
*
* Copyright 2019-2020, Optimizely and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using Moq;
using NUnit.Framework;
using OptimizelySDK.Config;
using OptimizelySDK.Entity;
Expand Down Expand Up @@ -32,7 +49,7 @@ public void ImpressionEventTest()
var variation = Config.GetVariationFromId(experiment.Key, "77210100090");
var userId = TestUserId;

var impressionEvent = UserEventFactory.CreateImpressionEvent(projectConfig, experiment, variation, userId, null);
var impressionEvent = UserEventFactory.CreateImpressionEvent(projectConfig, experiment, variation, userId, null, "test_experiment", "experiment");

Assert.AreEqual(Config.ProjectId, impressionEvent.Context.ProjectId);
Assert.AreEqual(Config.Revision, impressionEvent.Context.Revision);
Expand All @@ -58,7 +75,7 @@ public void ImpressionEventTestWithAttributes()
{ "company", "Optimizely" }
};

var impressionEvent = UserEventFactory.CreateImpressionEvent(projectConfig, experiment, variation, userId, userAttributes);
var impressionEvent = UserEventFactory.CreateImpressionEvent(projectConfig, experiment, variation, userId, userAttributes, "test_experiment", "experiment");

Assert.AreEqual(Config.ProjectId, impressionEvent.Context.ProjectId);
Assert.AreEqual(Config.Revision, impressionEvent.Context.Revision);
Expand Down
10 changes: 5 additions & 5 deletions OptimizelySDK.Tests/OptimizelyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ public void TestActivateNoAudienceNoAttributes()
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [user_1] is in experiment [group_experiment_1] of group [7722400015]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [9525] to user [user_1] with bucketing ID [user_1]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [user_1] is in variation [group_exp_1_var_2] of experiment [group_experiment_1]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user user_1 in experiment group_experiment_1."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user user_1 in experiment group_experiment_1."), Times.Once);

Assert.IsTrue(TestData.CompareObjects(GroupVariation, variation));
}
Expand Down Expand Up @@ -430,7 +430,7 @@ public void TestActivateWithAttributes()
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "This decision will not be saved since the UserProfileService is null."));
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);

Assert.IsTrue(TestData.CompareObjects(VariationWithKeyControl, variation));
}
Expand Down Expand Up @@ -490,7 +490,7 @@ public void TestActivateWithTypedAttributes()

LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);

Assert.IsTrue(TestData.CompareObjects(VariationWithKeyControl, variation));
}
Expand Down Expand Up @@ -683,8 +683,8 @@ public void TestInvalidDispatchImpressionEvent()

LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [3037] to user [test_user] with bucketing ID [test_user]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [test_user] is in variation [control] of experiment [test_experiment]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);

LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user test_user in experiment test_experiment."), Times.Once);
// Need to see how error handler can be verified.
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, It.IsAny<string>()), Times.Once);

Expand Down Expand Up @@ -1079,7 +1079,7 @@ public void TestActivateNoAudienceNoAttributesAfterSetForcedVariation()
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, "Assigned bucket [9525] to user [user_1] with bucketing ID [user_1]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "User [user_1] is in variation [group_exp_1_var_2] of experiment [group_experiment_1]."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "This decision will not be saved since the UserProfileService is null."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user user_1 in experiment group_experiment_1."), Times.Once);
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "Activating user user_1 in experiment group_experiment_1."), Times.Once);

Assert.IsTrue(TestData.CompareObjects(GroupVariation, variation));
}
Expand Down
10 changes: 10 additions & 0 deletions OptimizelySDK.Tests/ProjectConfigTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public void TestInit()
Assert.AreEqual("7720880029", Config.ProjectId);
// Check Revision
Assert.AreEqual("15", Config.Revision);
// Check SendFlagDecision
Assert.IsTrue(Config.SendFlagDecisions);

// Check Group ID Map
var expectedGroupId = CreateDictionary("7722400015", Config.GetGroup("7722400015"));
Expand Down Expand Up @@ -415,6 +417,14 @@ public void TestInit()
Assert.IsTrue(TestData.CompareObjects(expectedRolloutIdMap, Config.RolloutIdMap));
}


[Test]
public void TestIfSendFlagDecisionKeyIsMissingItShouldReturnFalse()
{
var tempConfig = DatafileProjectConfig.Create(TestData.SimpleABExperimentsDatafile, LoggerMock.Object, ErrorHandlerMock.Object);
Assert.IsFalse(tempConfig.SendFlagDecisions);
}

[Test]
public void TestGetAccountId()
{
Expand Down
3 changes: 2 additions & 1 deletion OptimizelySDK.Tests/TestData.json
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@
"key": "integer_variable",
"type": "integer",
"defaultValue": "7"
}
}
]
},
{
Expand Down Expand Up @@ -940,5 +940,6 @@
],
"revision": "15",
"anonymizeIP": false,
"sendFlagDecisions": true,
"botFiltering": true
}
5 changes: 5 additions & 0 deletions OptimizelySDK/Config/DatafileProjectConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ public enum OPTLYSDKVersion
/// </summary>
public string Revision { get; set; }

/// <summary>
/// SendFlagDecisions determines whether impressions events are sent for ALL decision types.
/// </summary>
public bool SendFlagDecisions { get; set; }

/// <summary>
/// Allow Anonymize IP by truncating the last block of visitors' IP address.
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion OptimizelySDK/Event/Builder/Params.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017, 2019, Optimizely
* Copyright 2017, 2019-2020, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using OptimizelySDK.Event.Entity;
using System;

namespace OptimizelySDK.Event.Builder
Expand All @@ -30,6 +31,7 @@ public static class Params
public const string ENTITY_ID = "entity_id";
public const string EVENTS = "events";
public const string EXPERIMENT_ID = "experiment_id";
public const string METADATA = "metadata";
public const string PROJECT_ID = "project_id";
public const string REVISION = "revision";
public const string TIME = "timestamp";
Expand Down
8 changes: 5 additions & 3 deletions OptimizelySDK/Event/Entity/Decision.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019, Optimizely
* Copyright 2019-2020, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,15 +23,17 @@ public class Decision
public string CampaignId { get; private set; }
[JsonProperty("experiment_id")]
public string ExperimentId { get; private set; }
[JsonProperty("metadata")]
public DecisionMetadata Metadata { get; private set; }
[JsonProperty("variation_id")]
public string VariationId { get; private set; }

public Decision() {}

public Decision(string campaignId, string experimentId, string variationId)
public Decision(string campaignId, string experimentId, string variationId, DecisionMetadata metadata = null)
{
CampaignId = campaignId;
ExperimentId = experimentId;
Metadata = metadata;
VariationId = variationId;
}
}
Expand Down
44 changes: 44 additions & 0 deletions OptimizelySDK/Event/Entity/DecisionMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
*
* Copyright 2020, Optimizely and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using Newtonsoft.Json;

namespace OptimizelySDK.Event.Entity
{
/// <summary>
/// DecisionMetadata captures additional information regarding the decision
/// </summary>
public class DecisionMetadata
{
[JsonProperty("flag_key")]
public string FlagKey { get; private set; }
[JsonProperty("rule_key")]
public string RuleKey { get; private set; }
[JsonProperty("rule_type")]
public string RuleType { get; private set; }
[JsonProperty("variation_key")]
public string VariationKey { get; private set; }

public DecisionMetadata(string flagKey, string ruleKey, string ruleType, string variationKey = "")
{
FlagKey = flagKey;
RuleKey = ruleKey;
RuleType = ruleType;
VariationKey = variationKey;
}
}
}
12 changes: 11 additions & 1 deletion OptimizelySDK/Event/Entity/ImpressionEvent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019, Optimizely
* Copyright 2019-2020, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,7 @@ public class ImpressionEvent : UserEvent
public VisitorAttribute[] VisitorAttributes { get; private set; }

public Experiment Experiment { get; set; }
public DecisionMetadata Metadata { get; set; }
public Variation Variation { get; set; }
public bool? BotFiltering { get; set; }

Expand All @@ -42,6 +43,7 @@ public class Builder
public VisitorAttribute[] VisitorAttributes;
private Experiment Experiment;
private Variation Variation;
private DecisionMetadata Metadata;
private bool? BotFiltering;

public Builder WithUserId(string userId)
Expand All @@ -65,6 +67,13 @@ public Builder WithExperiment(Experiment experiment)
return this;
}

public Builder WithMetadata(DecisionMetadata metadata)
{
Metadata = metadata;

return this;
}

public Builder WithVisitorAttributes(VisitorAttribute[] visitorAttributes)
{
VisitorAttributes = visitorAttributes;
Expand Down Expand Up @@ -102,6 +111,7 @@ public ImpressionEvent Build()
impressionEvent.VisitorAttributes = VisitorAttributes;
impressionEvent.UserId = UserId;
impressionEvent.Variation = Variation;
impressionEvent.Metadata = Metadata;
impressionEvent.BotFiltering = BotFiltering;

return impressionEvent;
Expand Down
7 changes: 4 additions & 3 deletions OptimizelySDK/Event/EventFactory.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019, Optimizely
* Copyright 2019-2020, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -115,11 +115,12 @@ private static Visitor CreateVisitor(ImpressionEvent impressionEvent) {

Decision decision = new Decision(impressionEvent.Experiment?.LayerId,
impressionEvent.Experiment?.Id,
impressionEvent.Variation?.Id);
impressionEvent.Variation?.Id,
impressionEvent.Metadata);

SnapshotEvent snapshotEvent = new SnapshotEvent.Builder()
.WithUUID(impressionEvent.UUID)
.WithEntityId(impressionEvent.Experiment.LayerId)
.WithEntityId(impressionEvent.Experiment?.LayerId)
.WithKey(ACTIVATE_EVENT_KEY)
.WithTimeStamp(impressionEvent.Timestamp)
.Build();
Expand Down
Loading

0 comments on commit f22ad21

Please sign in to comment.