diff --git a/OptimizelySDK.Net35/OptimizelySDK.Net35.csproj b/OptimizelySDK.Net35/OptimizelySDK.Net35.csproj
index 8dc64e4e..51db7eb4 100644
--- a/OptimizelySDK.Net35/OptimizelySDK.Net35.csproj
+++ b/OptimizelySDK.Net35/OptimizelySDK.Net35.csproj
@@ -148,6 +148,9 @@
IOptimizely.cs
+
+ IOptimizelyUserContext.cs
+
Logger\DefaultLogger.cs
diff --git a/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj b/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
index e5c64c6c..d451c076 100644
--- a/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
+++ b/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj
@@ -48,6 +48,9 @@
+
+ IOptimizelyUserContext.cs
+
AudienceConditions\AndCondition.cs
diff --git a/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj b/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
index 7d5e7c81..5824a1c5 100644
--- a/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
+++ b/OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj
@@ -100,7 +100,10 @@
AtomicProjectConfigManager.cs
- OptimizelyFactory.cs
+ OptimizelyFactory.cs
+
+
+ IOptimizelyUserContext.cs
ConversionEvent.cs
diff --git a/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj b/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj
index b43a21cb..63997423 100644
--- a/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj
+++ b/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj
@@ -10,6 +10,9 @@
IOptimizely.cs
+
+ IOptimizelyUserContext.cs
+
Optimizely.cs
diff --git a/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj b/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj
index 1a4e7df1..d5231738 100644
--- a/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj
+++ b/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj
@@ -97,6 +97,7 @@
+
diff --git a/OptimizelySDK.Tests/OptimizelyUserContextTest.cs b/OptimizelySDK.Tests/OptimizelyUserContextTest.cs
index 6d9630ca..12baae0f 100644
--- a/OptimizelySDK.Tests/OptimizelyUserContextTest.cs
+++ b/OptimizelySDK.Tests/OptimizelyUserContextTest.cs
@@ -63,7 +63,7 @@ public void SetUp()
public void OptimizelyUserContextWithAttributes()
{
var attributes = new UserAttributes() { { "house", "GRYFFINDOR" } };
- OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);
+ IOptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);
Assert.AreEqual(user.GetOptimizely(), Optimizely);
Assert.AreEqual(user.GetUserId(), UserID);
@@ -73,7 +73,7 @@ public void OptimizelyUserContextWithAttributes()
[Test]
public void OptimizelyUserContextNoAttributes()
{
- OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, null, ErrorHandlerMock.Object, LoggerMock.Object);
+ IOptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, null, ErrorHandlerMock.Object, LoggerMock.Object);
Assert.AreEqual(user.GetOptimizely(), Optimizely);
Assert.AreEqual(user.GetUserId(), UserID);
@@ -84,7 +84,7 @@ public void OptimizelyUserContextNoAttributes()
public void SetAttribute()
{
var attributes = new UserAttributes() { { "house", "GRYFFINDOR" } };
- OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);
+ IOptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);
user.SetAttribute("k1", "v1");
user.SetAttribute("k2", true);
@@ -104,7 +104,7 @@ public void SetAttribute()
[Test]
public void SetAttributeNoAttribute()
{
- OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, null, ErrorHandlerMock.Object, LoggerMock.Object);
+ IOptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, null, ErrorHandlerMock.Object, LoggerMock.Object);
user.SetAttribute("k1", "v1");
user.SetAttribute("k2", true);
@@ -120,7 +120,7 @@ public void SetAttributeNoAttribute()
public void SetAttributeOverride()
{
var attributes = new UserAttributes() { { "house", "GRYFFINDOR" } };
- OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);
+ IOptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);
user.SetAttribute("k1", "v1");
user.SetAttribute("house", "v2");
@@ -134,7 +134,7 @@ public void SetAttributeOverride()
public void SetAttributeNullValue()
{
var attributes = new UserAttributes() { { "k1", null } };
- OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);
+ IOptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);
var newAttributes = user.GetAttributes();
Assert.AreEqual(newAttributes["k1"], null);
@@ -151,7 +151,7 @@ public void SetAttributeNullValue()
[Test]
public void SetAttributeToOverrideAttribute()
{
- OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, null, ErrorHandlerMock.Object, LoggerMock.Object);
+ IOptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, null, ErrorHandlerMock.Object, LoggerMock.Object);
Assert.AreEqual(user.GetOptimizely(), Optimizely);
diff --git a/OptimizelySDK.Tests/TestSetup.cs b/OptimizelySDK.Tests/TestSetup.cs
new file mode 100644
index 00000000..7049f1c5
--- /dev/null
+++ b/OptimizelySDK.Tests/TestSetup.cs
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021, Optimizely
+ *
+ * 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 NUnit.Framework;
+using System.Globalization;
+using System.Threading;
+
+namespace OptimizelySDK.Tests
+{
+ [SetUpFixture]
+ public class TestSetup
+ {
+ [SetUp]
+ public void Init()
+ {
+ /* There are some issues doing assertions on tests with floating point numbers using the .ToString()
+ * method, as it's culture dependent. EG: TestGetFeatureVariableValueForTypeGivenFeatureFlagIsNotEnabledForUser,
+ * assigning the culture to English will make this kind of tests to work on others culture based systems. */
+ Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
+ }
+
+ [TearDown]
+ public void Cleanup()
+ {
+ // Empty, but required: https://nunit.org/nunitv2/docs/2.6.4/setupFixture.html
+ }
+ }
+}
diff --git a/OptimizelySDK/IOptimizely.cs b/OptimizelySDK/IOptimizely.cs
index 836cdb92..77643bf1 100644
--- a/OptimizelySDK/IOptimizely.cs
+++ b/OptimizelySDK/IOptimizely.cs
@@ -41,7 +41,7 @@ public interface IOptimizely
/// The user ID to be used for bucketing.
/// The user's attributes
/// OptimizelyUserContext | An OptimizelyUserContext associated with this OptimizelyClient.
- OptimizelyUserContext CreateUserContext(string userId, UserAttributes userAttributes = null);
+ IOptimizelyUserContext CreateUserContext(string userId, UserAttributes userAttributes = null);
///
/// Sends conversion event to Optimizely.
diff --git a/OptimizelySDK/IOptimizelyUserContext.cs b/OptimizelySDK/IOptimizelyUserContext.cs
new file mode 100644
index 00000000..1111a904
--- /dev/null
+++ b/OptimizelySDK/IOptimizelyUserContext.cs
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2020-2021, Optimizely
+ *
+ * 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 OptimizelySDK.Entity;
+using OptimizelySDK.OptimizelyDecisions;
+using System.Collections.Generic;
+
+namespace OptimizelySDK
+{
+ public interface IOptimizelyUserContext
+ {
+ ///
+ /// Returns a decision result ({@link OptimizelyDecision}) for a given flag key and a user context, which contains all data required to deliver the flag.
+ ///
+ ///
If the SDK finds an error, it’ll return a decision with null for variationKey. The decision will include an error message in reasons.
+ ///
+ ///
+ /// A flag key for which a decision will be made.
+ /// A decision result.
+ OptimizelyDecision Decide(string key);
+
+ ///
+ /// Returns a decision result ({@link OptimizelyDecision}) for a given flag key and a user context, which contains all data required to deliver the flag.
+ ///
+ ///
If the SDK finds an error, it’ll return a decision with null for variationKey. The decision will include an error message in reasons.
+ ///
+ ///
+ /// A flag key for which a decision will be made.
+ /// A list of options for decision-making.
+ /// A decision result.
+ OptimizelyDecision Decide(string key, OptimizelyDecideOption[] options);
+
+ ///
+ /// Returns a key-map of decision results ({@link OptimizelyDecision}) for all active flag keys.
+ ///
+ /// A dictionary of all decision results, mapped by flag keys.
+ Dictionary DecideAll();
+
+ ///
+ /// Returns a key-map of decision results ({@link OptimizelyDecision}) for all active flag keys.
+ ///
+ /// A list of options for decision-making.
+ /// All decision results mapped by flag keys.
+ Dictionary DecideAll(OptimizelyDecideOption[] options);
+
+ ///
+ /// Returns a key-map of decision results for multiple flag keys and a user context.
+ ///
+ /// list of flag keys for which a decision will be made.
+ /// A dictionary of all decision results, mapped by flag keys.
+ Dictionary DecideForKeys(string[] keys);
+
+ ///
+ /// Returns a key-map of decision results for multiple flag keys and a user context.
+ ///
+ /// list of flag keys for which a decision will be made.
+ /// An array of decision options.
+ ///
+ Dictionary DecideForKeys(string[] keys, OptimizelyDecideOption[] options);
+
+ ///
+ /// Returns copy of UserAttributes associated with UserContext.
+ ///
+ /// copy of UserAttributes.
+ UserAttributes GetAttributes();
+
+ ///
+ /// Returns Optimizely instance associated with the UserContext.
+ ///
+ /// Optimizely instance.
+ Optimizely GetOptimizely();
+
+ ///
+ /// Returns UserId associated with the UserContext
+ ///
+ /// UserId of this instance.
+ string GetUserId();
+
+ ///
+ /// Set an attribute for a given key.
+ ///
+ /// An attribute key
+ /// value An attribute value
+ void SetAttribute(string key, object value);
+
+ ///
+ /// Track an event.
+ ///
+ /// The event name.
+ void TrackEvent(string eventName);
+
+ ///
+ /// Track an event.
+ ///
+ /// The event name.
+ /// A map of event tag names to event tag values.
+ void TrackEvent(string eventName, EventTags eventTags);
+ }
+}
diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index 5aae0da4..e4c362bb 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -700,7 +700,7 @@ public OptimizelyJSON GetFeatureVariableJSON(string featureKey, string variableK
/// The user ID to be used for bucketing.
/// The user's attributes
/// OptimizelyUserContext | An OptimizelyUserContext associated with this OptimizelyClient.
- public OptimizelyUserContext CreateUserContext(string userId,
+ public IOptimizelyUserContext CreateUserContext(string userId,
UserAttributes userAttributes = null)
{
var inputValues = new Dictionary
@@ -724,7 +724,7 @@ public OptimizelyUserContext CreateUserContext(string userId,
/// A flag key for which a decision will be made.
/// A list of options for decision-making.
/// A decision result.
- internal OptimizelyDecision Decide(OptimizelyUserContext user,
+ internal OptimizelyDecision Decide(IOptimizelyUserContext user,
string key,
OptimizelyDecideOption[] options)
{
@@ -844,7 +844,7 @@ internal OptimizelyDecision Decide(OptimizelyUserContext user,
reasonsToReport);
}
- internal Dictionary DecideAll(OptimizelyUserContext user,
+ internal Dictionary DecideAll(IOptimizelyUserContext user,
OptimizelyDecideOption[] options)
{
var decisionMap = new Dictionary();
@@ -862,7 +862,7 @@ internal Dictionary DecideAll(OptimizelyUserContext
return DecideForKeys(user, allFlagKeys, options);
}
- internal Dictionary DecideForKeys(OptimizelyUserContext user,
+ internal Dictionary DecideForKeys(IOptimizelyUserContext user,
string[] keys,
OptimizelyDecideOption[] options)
{
diff --git a/OptimizelySDK/OptimizelyDecisions/OptimizelyDecision.cs b/OptimizelySDK/OptimizelyDecisions/OptimizelyDecision.cs
index dfc571ac..8afeeec9 100644
--- a/OptimizelySDK/OptimizelyDecisions/OptimizelyDecision.cs
+++ b/OptimizelySDK/OptimizelyDecisions/OptimizelyDecision.cs
@@ -53,7 +53,7 @@ public class OptimizelyDecision
///
/// user context for which the decision was made.
///
- public OptimizelyUserContext UserContext { get; private set; }
+ public IOptimizelyUserContext UserContext { get; private set; }
///
/// an array of error/info/debug messages describing why the decision has been made.
@@ -65,7 +65,7 @@ public OptimizelyDecision(string variationKey,
OptimizelyJSON variables,
string ruleKey,
string flagKey,
- OptimizelyUserContext userContext,
+ IOptimizelyUserContext userContext,
string[] reasons)
{
VariationKey = variationKey;
@@ -84,7 +84,7 @@ public OptimizelyDecision(string variationKey,
/// and error reason array
///
public static OptimizelyDecision NewErrorDecision(string key,
- OptimizelyUserContext optimizelyUserContext,
+ IOptimizelyUserContext optimizelyUserContext,
string error,
IErrorHandler errorHandler,
ILogger logger)
diff --git a/OptimizelySDK/OptimizelySDK.csproj b/OptimizelySDK/OptimizelySDK.csproj
index 50f8b137..d3ac9047 100644
--- a/OptimizelySDK/OptimizelySDK.csproj
+++ b/OptimizelySDK/OptimizelySDK.csproj
@@ -87,6 +87,7 @@
+
diff --git a/OptimizelySDK/OptimizelyUserContext.cs b/OptimizelySDK/OptimizelyUserContext.cs
index aca35788..35aca732 100644
--- a/OptimizelySDK/OptimizelyUserContext.cs
+++ b/OptimizelySDK/OptimizelyUserContext.cs
@@ -25,7 +25,7 @@ namespace OptimizelySDK
///
/// OptimizelyUserContext defines user contexts that the SDK will use to make decisions for
///
- public class OptimizelyUserContext
+ public class OptimizelyUserContext : IOptimizelyUserContext
{
private ILogger Logger;
private IErrorHandler ErrorHandler;
@@ -46,7 +46,7 @@ public OptimizelyUserContext(Optimizely optimizely, string userId, UserAttribute
UserId = userId;
}
- private OptimizelyUserContext Copy() => new OptimizelyUserContext(Optimizely, UserId, GetAttributes(), ErrorHandler, Logger);
+ private IOptimizelyUserContext Copy() => new OptimizelyUserContext(Optimizely, UserId, GetAttributes(), ErrorHandler, Logger);
///
/// Returns Optimizely instance associated with the UserContext.
@@ -133,6 +133,7 @@ public OptimizelyDecision Decide(string key,
/// Returns a key-map of decision results for multiple flag keys and a user context.
///
/// list of flag keys for which a decision will be made.
+ /// An array of decision options.
/// A dictionary of all decision results, mapped by flag keys.
public Dictionary DecideForKeys(string[] keys, OptimizelyDecideOption[] options)
{