Skip to content

Feat: Add thread-safe UserAttributes in read/write operations. #253

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

Merged
merged 60 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
01bad8f
Added OptimizelyUserContext and basic functionalities
mnoman09 Nov 5, 2020
35c5e76
Added DecisionTests
mnoman09 Nov 6, 2020
f79974c
Added classes of optimizelyDecision in net35 file
mnoman09 Nov 6, 2020
7531f3c
Updated decisionmessage class and added decisionMessage test
mnoman09 Nov 6, 2020
f006b08
Added OptimizelyDecisionTest
mnoman09 Nov 9, 2020
ac0c6a5
Added CreateUserContext in Optimizely
mnoman09 Nov 9, 2020
6c9ac4d
Added UserContext add reason logs
mnoman09 Nov 11, 2020
9e36946
Revert "Added UserContext add reason logs"
mnoman09 Nov 11, 2020
8c78330
Added UserContext add reason logs
mnoman09 Nov 11, 2020
ee465c3
Added TrackEvent, DecideForKeys and DecideAllFunction
mnoman09 Nov 12, 2020
591c770
Replaced list of reasons to array.
mnoman09 Nov 13, 2020
f644b24
Merge branch 'mnoman/user-context' into mnoman/decideapi
mnoman09 Nov 13, 2020
ad77477
Merge remote-tracking branch 'origin/master' into mnoman/user-context
mnoman09 Nov 13, 2020
bab871e
Merge branch 'mnoman/user-context' into mnoman/decideapi
mnoman09 Nov 13, 2020
2c0c3ec
Merge branch 'mnoman/decideapi' into mnoman/decideAllApi
mnoman09 Nov 13, 2020
c3db077
Bug fix
mnoman09 Nov 13, 2020
055bb93
Resolved comments of adding null check before reasons
mnoman09 Nov 13, 2020
5ce1419
removed underscores
mnoman09 Nov 13, 2020
1e388b7
Merge branch 'mnoman/user-context' into mnoman/decideapi
mnoman09 Nov 13, 2020
a7e684b
Merge branch 'mnoman/decideapi' into mnoman/decideAllApi
mnoman09 Nov 13, 2020
a424fef
Added Decide Options unit tests
mnoman09 Nov 16, 2020
7eeb588
Added track event tests and fixes defaultDecideOptions
mnoman09 Nov 16, 2020
8e1750e
Changed UserAttribute to Attributes
mnoman09 Nov 17, 2020
1163cde
Merge branch 'master' into mnoman/user-context
mnoman09 Nov 18, 2020
6330750
Merge branch 'mnoman/user-context' into mnoman/decideapi
mnoman09 Nov 18, 2020
9ec58d3
Merge branch 'mnoman/decideapi' into mnoman/decideAllApi
mnoman09 Nov 18, 2020
980abd0
Added DecisionNotificationType flag
mnoman09 Nov 18, 2020
14769f8
Added check of Key != null in decide
mnoman09 Nov 20, 2020
fafb14c
Added Additional unit tests of optimizelyUserContext
mnoman09 Nov 24, 2020
f9d3939
Merge branch 'mnoman/user-context' into mnoman/decideapi
mnoman09 Nov 24, 2020
671aa29
added notification type flag
mnoman09 Nov 24, 2020
108cc06
Merge branch 'mnoman/decideapi' into mnoman/decideAllApi
mnoman09 Nov 24, 2020
0628e68
Added additional test cases
mnoman09 Nov 25, 2020
a0753a6
Added mutex lock in setting attributes value
mnoman09 Nov 25, 2020
0e9faa1
replaced list from array of decideOptions
mnoman09 Nov 26, 2020
4bd0b3e
Merge branch 'mnoman/user-context' into mnoman/decideapi
mnoman09 Nov 26, 2020
9455e92
Merge branch 'mnoman/decideapi' into mnoman/decideAllApi
mnoman09 Nov 26, 2020
5d0028f
changed decide decideall and decide for keys from public to internal
mnoman09 Nov 26, 2020
320ddc4
Resolved failing scenario
mnoman09 Nov 27, 2020
451e001
Added unit test that byUPS will not call save in userprofile even once
mnoman09 Nov 30, 2020
54e9e74
Removed unnecessary methods from errorDecisionsReasons
mnoman09 Dec 2, 2020
6934f30
Merge branch 'mnoman/user-context' into mnoman/decideapi
mnoman09 Dec 2, 2020
b824b99
Added ignoreUps check first in decision service
mnoman09 Dec 2, 2020
cac22e4
Merge branch 'mnoman/decideapi' into mnoman/decideAllApi
mnoman09 Dec 2, 2020
7eb41c7
Converted a loop to lambda
mnoman09 Dec 2, 2020
eda2743
Nit Fix
mnoman09 Dec 7, 2020
74bd4a5
Merge branch 'mnoman/decideapi' into mnoman/decideAllApi
mnoman09 Dec 7, 2020
f97e302
Changed decide keys to array
mnoman09 Dec 7, 2020
869b655
Added additional test with count of Notfication
mnoman09 Dec 7, 2020
73cd357
Merge branch 'master' into mnoman/decideapi
mnoman09 Dec 8, 2020
fb2ebe4
Merge branch 'mnoman/decideapi' into mnoman/decideAllApi
mnoman09 Dec 8, 2020
d253a13
Sending copy of userattributes to make it thread safe
mnoman09 Dec 8, 2020
deabb0d
user context mutex lock
msohailhussain Dec 8, 2020
fa90e13
replaced copiedOptions = copiedOptions.Concat(options).Concat(Default…
mnoman09 Dec 9, 2020
0f25fb2
Merge remote-tracking branch 'origin/master' into mnoman/decideAllApi
mnoman09 Dec 9, 2020
ecbd2a4
Merge branch 'mnoman/decideAllApi' into mnoman/thread-safe-userattrib…
mnoman09 Dec 9, 2020
24405c3
temporarily added.
msohailhussain Dec 9, 2020
2585e5c
Merge remote-tracking branch 'origin/master' into mnoman/thread-safe-…
mnoman09 Dec 10, 2020
19f109e
Removed unused variable
mnoman09 Dec 10, 2020
97fb7f8
reverted to master
msohailhussain Dec 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 18 additions & 19 deletions OptimizelySDK.Tests/OptimizelyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,18 +198,18 @@ public void TestCreateUserContext()
{ "location", "San Francisco" }
};
var optlyUserContext = Optimizely.CreateUserContext(TestUserId, attribute);
Assert.AreEqual(TestUserId, optlyUserContext.UserId);
Assert.AreEqual(Optimizely, optlyUserContext.Optimizely);
Assert.AreEqual(attribute, optlyUserContext.Attributes);
Assert.AreEqual(TestUserId, optlyUserContext.GetUserId());
Assert.AreEqual(Optimizely, optlyUserContext.GetOptimizely());
Assert.AreEqual(attribute, optlyUserContext.GetAttributes());
}

[Test]
public void TestCreateUserContextWithoutAttributes()
{
var optlyUserContext = Optimizely.CreateUserContext(TestUserId);
Assert.AreEqual(TestUserId, optlyUserContext.UserId);
Assert.AreEqual(Optimizely, optlyUserContext.Optimizely);
Assert.IsTrue(optlyUserContext.Attributes.Count == 0);
Assert.AreEqual(TestUserId, optlyUserContext.GetUserId());
Assert.AreEqual(Optimizely, optlyUserContext.GetOptimizely());
Assert.IsTrue(optlyUserContext.GetAttributes().Count == 0);
}

[Test]
Expand All @@ -230,13 +230,13 @@ public void TestCreateUserContextMultipleAttribute()
};
var optlyUserContext2 = Optimizely.CreateUserContext("userId2", attribute2);

Assert.AreEqual("userId1", optlyUserContext1.UserId);
Assert.AreEqual(Optimizely, optlyUserContext1.Optimizely);
Assert.AreEqual(attribute1, optlyUserContext1.Attributes);
Assert.AreEqual("userId1", optlyUserContext1.GetUserId());
Assert.AreEqual(Optimizely, optlyUserContext1.GetOptimizely());
Assert.AreEqual(attribute1, optlyUserContext1.GetAttributes());

Assert.AreEqual("userId2", optlyUserContext2.UserId);
Assert.AreEqual(Optimizely, optlyUserContext2.Optimizely);
Assert.AreEqual(attribute2, optlyUserContext2.Attributes);
Assert.AreEqual("userId2", optlyUserContext2.GetUserId());
Assert.AreEqual(Optimizely, optlyUserContext2.GetOptimizely());
Assert.AreEqual(attribute2, optlyUserContext2.GetAttributes());
}

[Test]
Expand All @@ -249,20 +249,19 @@ public void TestChangeAttributeDoesNotEffectValues()
{ "location", "San Francisco" }
};
var optlyUserContext = Optimizely.CreateUserContext(userId, attribute);
Assert.AreEqual(TestUserId, optlyUserContext.UserId);
Assert.AreEqual(Optimizely, optlyUserContext.Optimizely);
Assert.AreEqual(attribute, optlyUserContext.Attributes);
Assert.AreEqual(TestUserId, optlyUserContext.GetUserId());
Assert.AreEqual(Optimizely, optlyUserContext.GetOptimizely());
Assert.AreEqual(attribute, optlyUserContext.GetAttributes());

attribute = new UserAttributes
{
{ "device_type", "iPhone" },
{ "level", "low" },
{ "location", "San Francisco" }
};
userId = "InvalidUser";
Assert.AreEqual("testUserId", optlyUserContext.UserId);
Assert.AreEqual(Optimizely, optlyUserContext.Optimizely);
Assert.AreNotEqual(attribute, optlyUserContext.Attributes);
Assert.AreEqual("testUserId", optlyUserContext.GetUserId());
Assert.AreEqual(Optimizely, optlyUserContext.GetOptimizely());
Assert.AreNotEqual(attribute, optlyUserContext.GetAttributes());
}

#endregion
Expand Down
40 changes: 20 additions & 20 deletions OptimizelySDK.Tests/OptimizelyUserContextTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,19 @@ public void OptimizelyUserContextWithAttributes()
var attributes = new UserAttributes() { { "house", "GRYFFINDOR" } };
OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);

Assert.AreEqual(user.Optimizely, Optimizely);
Assert.AreEqual(user.UserId, UserID);
Assert.AreEqual(user.Attributes, attributes);
Assert.AreEqual(user.GetOptimizely(), Optimizely);
Assert.AreEqual(user.GetUserId(), UserID);
Assert.AreEqual(user.GetAttributes(), attributes);
}

[Test]
public void OptimizelyUserContextNoAttributes()
{
OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, null, ErrorHandlerMock.Object, LoggerMock.Object);

Assert.AreEqual(user.Optimizely, Optimizely);
Assert.AreEqual(user.UserId, UserID);
Assert.True(user.Attributes.Count == 0);
Assert.AreEqual(user.GetOptimizely(), Optimizely);
Assert.AreEqual(user.GetUserId(), UserID);
Assert.True(user.GetAttributes().Count == 0);
}

[Test]
Expand All @@ -91,9 +91,9 @@ public void SetAttribute()
user.SetAttribute("k3", 100);
user.SetAttribute("k4", 3.5);

Assert.AreEqual(user.Optimizely, Optimizely);
Assert.AreEqual(user.UserId, UserID);
var newAttributes = user.Attributes;
Assert.AreEqual(user.GetOptimizely(), Optimizely);
Assert.AreEqual(user.GetUserId(), UserID);
var newAttributes = user.GetAttributes();
Assert.AreEqual(newAttributes["house"], "GRYFFINDOR");
Assert.AreEqual(newAttributes["k1"], "v1");
Assert.AreEqual(newAttributes["k2"], true);
Expand All @@ -109,9 +109,9 @@ public void SetAttributeNoAttribute()
user.SetAttribute("k1", "v1");
user.SetAttribute("k2", true);

Assert.AreEqual(user.Optimizely, Optimizely);
Assert.AreEqual(user.UserId, UserID);
var newAttributes = user.Attributes;
Assert.AreEqual(user.GetOptimizely(), Optimizely);
Assert.AreEqual(user.GetUserId(), UserID);
var newAttributes = user.GetAttributes();
Assert.AreEqual(newAttributes["k1"], "v1");
Assert.AreEqual(newAttributes["k2"], true);
}
Expand All @@ -125,7 +125,7 @@ public void SetAttributeOverride()
user.SetAttribute("k1", "v1");
user.SetAttribute("house", "v2");

var newAttributes = user.Attributes;
var newAttributes = user.GetAttributes();
Assert.AreEqual(newAttributes["k1"], "v1");
Assert.AreEqual(newAttributes["house"], "v2");
}
Expand All @@ -136,15 +136,15 @@ public void SetAttributeNullValue()
var attributes = new UserAttributes() { { "k1", null } };
OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, attributes, ErrorHandlerMock.Object, LoggerMock.Object);

var newAttributes = user.Attributes;
var newAttributes = user.GetAttributes();
Assert.AreEqual(newAttributes["k1"], null);

user.SetAttribute("k1", true);
newAttributes = user.Attributes;
newAttributes = user.GetAttributes();
Assert.AreEqual(newAttributes["k1"], true);

user.SetAttribute("k1", null);
newAttributes = user.Attributes;
newAttributes = user.GetAttributes();
Assert.AreEqual(newAttributes["k1"], null);
}

Expand All @@ -154,14 +154,14 @@ public void SetAttributeToOverrideAttribute()
OptimizelyUserContext user = new OptimizelyUserContext(Optimizely, UserID, null, ErrorHandlerMock.Object, LoggerMock.Object);


Assert.AreEqual(user.Optimizely, Optimizely);
Assert.AreEqual(user.UserId, UserID);
Assert.AreEqual(user.GetOptimizely(), Optimizely);
Assert.AreEqual(user.GetUserId(), UserID);

user.SetAttribute("k1", "v1");
Assert.AreEqual(user.Attributes["k1"], "v1");
Assert.AreEqual(user.GetAttributes()["k1"], "v1");

user.SetAttribute("k1", true);
Assert.AreEqual(user.Attributes["k1"], true);
Assert.AreEqual(user.GetAttributes()["k1"], true);
}

#region decide
Expand Down
11 changes: 8 additions & 3 deletions OptimizelySDK/Entity/UserAttributes.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2018, Optimizely
* Copyright 2017-2018,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 @@ -14,12 +14,17 @@
* limitations under the License.
*/
using System.Collections.Generic;
using OptimizelySDK.Logger;

namespace OptimizelySDK.Entity
{
public class UserAttributes : Dictionary<string, object>
{

public UserAttributes() : base()
{
}

public UserAttributes(IDictionary<string, object> dictionary) : base(dictionary)
{
}
}
}
6 changes: 2 additions & 4 deletions OptimizelySDK/Optimizely.cs
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ internal OptimizelyDecision Decide(OptimizelyUserContext user,
ErrorHandler, Logger);
}

var userId = user?.UserId;
var userId = user?.GetUserId();

var flag = config.GetFeatureFlagFromKey(key);
if (string.IsNullOrEmpty(flag.Key))
Expand All @@ -752,8 +752,7 @@ internal OptimizelyDecision Decide(OptimizelyUserContext user,
ErrorHandler, Logger);
}

var userAttributes = user.Attributes;

var userAttributes = user.GetAttributes();
var decisionEventDispatched = false;
var allOptions = GetAllOptions(options);
var decisionReasons = DefaultDecisionReasons.NewInstance(allOptions);
Expand Down Expand Up @@ -864,7 +863,6 @@ internal Dictionary<string, OptimizelyDecision> DecideAll(OptimizelyUserContext
return DecideForKeys(user, allFlagKeys, options);
}


internal Dictionary<string, OptimizelyDecision> DecideForKeys(OptimizelyUserContext user,
string[] keys,
OptimizelyDecideOption[] options)
Expand Down
38 changes: 35 additions & 3 deletions OptimizelySDK/OptimizelyUserContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ public class OptimizelyUserContext
private IErrorHandler ErrorHandler;
private object mutex = new object();
// userID for Optimizely user context
public string UserId { get; }
private string UserId;
// user attributes for Optimizely user context.
public UserAttributes Attributes { get; }
private UserAttributes Attributes;
// Optimizely object to be used.
public Optimizely Optimizely { get; }
private Optimizely Optimizely;

public OptimizelyUserContext(Optimizely optimizely, string userId, UserAttributes userAttributes, IErrorHandler errorHandler, ILogger logger)
{
Expand All @@ -46,6 +46,38 @@ public OptimizelyUserContext(Optimizely optimizely, string userId, UserAttribute
UserId = userId;
}

/// <summary>
/// Returns Optimizely instance associated with the UserContext.
/// </summary>
/// <returns> Optimizely instance.</returns>
public Optimizely GetOptimizely()
{
return Optimizely;
}

/// <summary>
/// Returns UserId associated with the UserContext
/// </summary>
/// <returns>UserId of this instance.</returns>
public string GetUserId()
{
return UserId;
}

/// <summary>
/// Returns copy of UserAttributes associated with UserContext.
/// </summary>
/// <returns>copy of UserAttributes.</returns>
public UserAttributes GetAttributes()
{
UserAttributes copiedAttributes = null;
lock(mutex) {
copiedAttributes = new UserAttributes(Attributes);
}

return copiedAttributes;
}

/// <summary>
/// Set an attribute for a given key.
/// </summary>
Expand Down