Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 12 additions & 2 deletions OptimizelySDK.Tests/OdpTests/OdpEventManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,11 @@ public void ShouldLogWhenOdpNotIntegratedAndIdentifyUserCalled()
eventManager.UpdateSettings(mockOdpConfig.
Object); // auto-start after update; Logs 1x here

eventManager.IdentifyUser(FS_USER_ID); // Logs 1x here too
eventManager.IdentifyUser(new Dictionary<string, string>
{
{ FS_USER_ID, "test-user" },
{ "email", "user@example.com" },
}); // Logs 1x here too

_mockLogger.Verify(
l => l.Log(LogLevel.WARN, Constants.ODP_NOT_INTEGRATED_MESSAGE),
Expand Down Expand Up @@ -622,7 +626,12 @@ public void ShouldPrepareCorrectPayloadForIdentifyUser()
Build();
eventManager.UpdateSettings(_odpConfig);

eventManager.IdentifyUser(USER_ID);
var identifiers = new Dictionary<string, string>
{
{ Constants.FS_USER_ID, USER_ID },
{ "email", "user@example.com" },
};
eventManager.IdentifyUser(identifiers);
cde.Wait(MAX_COUNT_DOWN_EVENT_WAIT_MS);

var eventsSentToApi = eventsCollector.FirstOrDefault();
Expand All @@ -631,6 +640,7 @@ public void ShouldPrepareCorrectPayloadForIdentifyUser()
Assert.AreEqual(Constants.ODP_EVENT_TYPE, actualEvent.Type);
Assert.AreEqual("identified", actualEvent.Action);
Assert.AreEqual(USER_ID, actualEvent.Identifiers[Constants.FS_USER_ID]);
Assert.AreEqual("user@example.com", actualEvent.Identifiers["email"]);
var eventData = actualEvent.Data;
Assert.AreEqual(Guid.NewGuid().ToString().Length,
eventData["idempotence_id"].ToString().Length);
Expand Down
85 changes: 77 additions & 8 deletions OptimizelySDK.Tests/OdpTests/OdpManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,40 +283,109 @@ public void ShouldGetSegmentManager()
}

[Test]
public void ShouldIdentifyUserWhenOdpIsIntegrated()
public void ShouldIdentifyUserWhenMultipleIdentifiers()
{
_mockOdpEventManager.Setup(e => e.IdentifyUser(It.IsAny<string>()));
_mockOdpEventManager.Setup(e =>
e.IdentifyUser(It.IsAny<Dictionary<string, string>>()));
_mockOdpEventManager.Setup(e => e.IsStarted).Returns(true);
var manager = new OdpManager.Builder().
WithEventManager(_mockOdpEventManager.Object).
WithLogger(_mockLogger.Object).
Build();
manager.UpdateSettings(API_KEY, API_HOST, _emptySegmentsToCheck);

var identifiers = new Dictionary<string, string>
{
{ Constants.FS_USER_ID, VALID_FS_USER_ID },
{ "email", "user@example.com" },
};
manager.IdentifyUser(identifiers);
manager.Dispose();

_mockLogger.Verify(
l => l.Log(It.IsAny<LogLevel>(), It.IsAny<string>()), Times.Never);
_mockOdpEventManager.Verify(
e => e.IdentifyUser(It.IsAny<Dictionary<string, string>>()), Times.Once);
}

[Test]
public void ShouldNotIdentifyUserWhenSingleIdentifier()
{
_mockOdpEventManager.Setup(e =>
e.IdentifyUser(It.IsAny<Dictionary<string, string>>()));
_mockOdpEventManager.Setup(e => e.IsStarted).Returns(true);
var manager = new OdpManager.Builder().
WithEventManager(_mockOdpEventManager.Object).
WithLogger(_mockLogger.Object).
Build();
manager.UpdateSettings(API_KEY, API_HOST, _emptySegmentsToCheck);

manager.IdentifyUser(VALID_FS_USER_ID);
var identifiers = new Dictionary<string, string>
{
{ Constants.FS_USER_ID, VALID_FS_USER_ID },
};
manager.IdentifyUser(identifiers);
manager.Dispose();

_mockLogger.Verify(l => l.Log(It.IsAny<LogLevel>(), It.IsAny<string>()), Times.Never);
_mockOdpEventManager.Verify(e => e.IdentifyUser(It.IsAny<string>()), Times.Once);
_mockLogger.Verify(l =>
l.Log(LogLevel.DEBUG,
"ODP identify event not dispatched (fewer than 2 valid identifiers)."));
_mockOdpEventManager.Verify(
e => e.IdentifyUser(It.IsAny<Dictionary<string, string>>()), Times.Never);
}

[Test]
public void ShouldNotIdentifyUserWhenOdpDisabled()
{
_mockOdpEventManager.Setup(e => e.IdentifyUser(It.IsAny<string>()));
_mockOdpEventManager.Setup(e =>
e.IdentifyUser(It.IsAny<Dictionary<string, string>>()));
_mockOdpEventManager.Setup(e => e.IsStarted).Returns(true);
var manager = new OdpManager.Builder().
WithEventManager(_mockOdpEventManager.Object).
WithLogger(_mockLogger.Object).
Build(false);
manager.UpdateSettings(API_KEY, API_HOST, _emptySegmentsToCheck);

manager.IdentifyUser(VALID_FS_USER_ID);
var identifiers = new Dictionary<string, string>
{
{ Constants.FS_USER_ID, VALID_FS_USER_ID },
{ "email", "user@example.com" },
};
manager.IdentifyUser(identifiers);
manager.Dispose();

_mockLogger.Verify(l =>
l.Log(LogLevel.DEBUG, "ODP identify event not dispatched (ODP disabled)."));
_mockOdpEventManager.Verify(e => e.IdentifyUser(It.IsAny<string>()), Times.Never);
_mockOdpEventManager.Verify(
e => e.IdentifyUser(It.IsAny<Dictionary<string, string>>()), Times.Never);
}

[Test]
public void ShouldNotIdentifyUserWhenNullEmptyValues()
{
_mockOdpEventManager.Setup(e =>
e.IdentifyUser(It.IsAny<Dictionary<string, string>>()));
_mockOdpEventManager.Setup(e => e.IsStarted).Returns(true);
var manager = new OdpManager.Builder().
WithEventManager(_mockOdpEventManager.Object).
WithLogger(_mockLogger.Object).
Build();
manager.UpdateSettings(API_KEY, API_HOST, _emptySegmentsToCheck);

// Two identifiers but one is empty - should not send
var identifiers = new Dictionary<string, string>
{
{ Constants.FS_USER_ID, VALID_FS_USER_ID },
{ "email", "" },
};
manager.IdentifyUser(identifiers);
manager.Dispose();

_mockLogger.Verify(l =>
l.Log(LogLevel.DEBUG,
"ODP identify event not dispatched (fewer than 2 valid identifiers)."));
_mockOdpEventManager.Verify(
e => e.IdentifyUser(It.IsAny<Dictionary<string, string>>()), Times.Never);
}

[Test]
Expand Down
5 changes: 4 additions & 1 deletion OptimizelySDK.Tests/OptimizelyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6232,7 +6232,10 @@ public void TestFetchQualifiedSegmentsInvalidOptimizelyObject()
public void TestIdentifyUserInvalidOptimizelyObject()
{
var optly = new Optimizely("Random datafile", null, LoggerMock.Object);
optly.IdentifyUser("some_user");
optly.IdentifyUser(new Dictionary<string, string>
{
{ "fs_user_id", "some_user" },
});
LoggerMock.Verify(
l => l.Log(LogLevel.ERROR, "Datafile has invalid format. Failing 'IdentifyUser'."),
Times.Once);
Expand Down
7 changes: 4 additions & 3 deletions OptimizelySDK/Odp/IOdpEventManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

using System;
using System.Collections.Generic;
using OptimizelySDK.Odp.Entity;

namespace OptimizelySDK.Odp
Expand Down Expand Up @@ -52,10 +53,10 @@ public interface IOdpEventManager
void Dispose();

/// <summary>
/// Associate a full-stack userid with an established VUID
/// Send an identify event to ODP with the given identifiers
/// </summary>
/// <param name="userId">Full-stack User ID</param>
void IdentifyUser(string userId);
/// <param name="identifiers">Dictionary of identifier key-value pairs</param>
void IdentifyUser(Dictionary<string, string> identifiers);

/// <summary>
/// Update ODP configuration settings
Expand Down
6 changes: 3 additions & 3 deletions OptimizelySDK/Odp/IOdpManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ public interface IOdpManager
string[] FetchQualifiedSegments(string userId, List<OdpSegmentOption> options);

/// <summary>
/// Send identification event to ODP for a given full-stack User ID
/// Send identification event to ODP when 2+ valid identifiers exist
/// </summary>
/// <param name="userId">User ID to send</param>
void IdentifyUser(string userId);
/// <param name="identifiers">Dictionary of identifier key-value pairs</param>
void IdentifyUser(Dictionary<string, string> identifiers);

/// <summary>
/// Add event to queue for sending to ODP
Expand Down
11 changes: 3 additions & 8 deletions OptimizelySDK/Odp/OdpEventManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,16 +368,11 @@ public void Dispose()
}

/// <summary>
/// Associate a full-stack userid with an established VUID
/// Send an identify event to ODP with the given identifiers
/// </summary>
/// <param name="userId">Full-stack User ID</param>
public void IdentifyUser(string userId)
/// <param name="identifiers">Dictionary of identifier key-value pairs</param>
public void IdentifyUser(Dictionary<string, string> identifiers)
{
var identifiers = new Dictionary<string, string>
{
{ Constants.FS_USER_ID, userId },
};

var odpEvent = new OdpEvent(Constants.ODP_EVENT_TYPE, "identified", identifiers);
SendEvent(odpEvent);
}
Expand Down
26 changes: 22 additions & 4 deletions OptimizelySDK/Odp/OdpManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,36 @@ public string[] FetchQualifiedSegments(string userId, List<OdpSegmentOption> opt
}

/// <summary>
/// Send identification event to ODP for a given full-stack User ID
/// Send identification event to ODP when 2+ valid identifiers exist
/// </summary>
/// <param name="userId">User ID to send</param>
public void IdentifyUser(string userId)
/// <param name="identifiers">Dictionary of identifier key-value pairs</param>
public void IdentifyUser(Dictionary<string, string> identifiers)
{
if (EventManagerOrConfigNotReady())
{
_logger.Log(LogLevel.DEBUG, "ODP identify event not dispatched (ODP disabled).");
return;
}

EventManager.IdentifyUser(userId);
// Filter out null and empty identifier values
var validIdentifiers = new Dictionary<string, string>();
foreach (var kvp in identifiers)
{
if (!string.IsNullOrEmpty(kvp.Value))
{
validIdentifiers[kvp.Key] = kvp.Value;
}
}

// Only send identify event when 2+ valid identifiers exist
if (validIdentifiers.Count < 2)
{
_logger.Log(LogLevel.DEBUG,
"ODP identify event not dispatched (fewer than 2 valid identifiers).");
return;
}

EventManager.IdentifyUser(validIdentifiers);
}

/// <summary>
Expand Down
6 changes: 3 additions & 3 deletions OptimizelySDK/Optimizely.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@
try
{
#if USE_ODP
InitializeComponents(eventDispatcher, logger, errorHandler, userProfileService,

Check warning on line 149 in OptimizelySDK/Optimizely.cs

View workflow job for this annotation

GitHub Actions / Build Standard 2.0

'Optimizely.InitializeComponents(IEventDispatcher, ILogger, IErrorHandler, UserProfileService, NotificationCenter, EventProcessor, OptimizelyDecideOption[], IOdpManager, ICmabService, CmabConfig)' is obsolete

Check warning on line 149 in OptimizelySDK/Optimizely.cs

View workflow job for this annotation

GitHub Actions / Build Standard 2.0

'Optimizely.InitializeComponents(IEventDispatcher, ILogger, IErrorHandler, UserProfileService, NotificationCenter, EventProcessor, OptimizelyDecideOption[], IOdpManager, ICmabService, CmabConfig)' is obsolete
null, eventProcessor, defaultDecideOptions, odpManager);
#else
InitializeComponents(eventDispatcher, logger, errorHandler, userProfileService,
Expand Down Expand Up @@ -218,7 +218,7 @@
ProjectConfigManager = configManager;

#if USE_ODP && USE_CMAB
InitializeComponents(eventDispatcher, logger, errorHandler, userProfileService,

Check warning on line 221 in OptimizelySDK/Optimizely.cs

View workflow job for this annotation

GitHub Actions / Build Standard 2.0

'Optimizely.InitializeComponents(IEventDispatcher, ILogger, IErrorHandler, UserProfileService, NotificationCenter, EventProcessor, OptimizelyDecideOption[], IOdpManager, ICmabService, CmabConfig)' is obsolete

Check warning on line 221 in OptimizelySDK/Optimizely.cs

View workflow job for this annotation

GitHub Actions / Build Standard 2.0

'Optimizely.InitializeComponents(IEventDispatcher, ILogger, IErrorHandler, UserProfileService, NotificationCenter, EventProcessor, OptimizelyDecideOption[], IOdpManager, ICmabService, CmabConfig)' is obsolete
notificationCenter, eventProcessor, defaultDecideOptions, odpManager, null, cmabConfig);
#elif USE_ODP
InitializeComponents(eventDispatcher, logger, errorHandler, userProfileService,
Expand Down Expand Up @@ -1499,8 +1499,8 @@
/// <summary>
/// Send identification event to Optimizely Data Platform
/// </summary>
/// <param name="userId">FS User ID to send</param>
internal void IdentifyUser(string userId)
/// <param name="identifiers">Dictionary of identifier key-value pairs</param>
internal void IdentifyUser(Dictionary<string, string> identifiers)
{
var config = ProjectConfigManager?.GetConfig();

Expand All @@ -1510,7 +1510,7 @@
return;
}

OdpManager?.IdentifyUser(userId);
OdpManager?.IdentifyUser(identifiers);
}

/// <summary>
Expand Down
5 changes: 4 additions & 1 deletion OptimizelySDK/OptimizelyUserContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
private List<string> QualifiedSegments;

// Optimizely object to be used.
private Optimizely Optimizely;

Check warning on line 55 in OptimizelySDK/OptimizelyUserContext.cs

View workflow job for this annotation

GitHub Actions / Build Standard 1.6

'Optimizely' is obsolete: 'Net standard 1.6 SDK support is deprecated, use Net standard 2.0 or above'

/// <summary>
/// Determine if User Context has already been disposed
Expand All @@ -61,18 +61,18 @@

private ForcedDecisionsStore ForcedDecisionsStore { get; set; }

public OptimizelyUserContext(Optimizely optimizely, string userId,

Check warning on line 64 in OptimizelySDK/OptimizelyUserContext.cs

View workflow job for this annotation

GitHub Actions / Build Standard 1.6

'Optimizely' is obsolete: 'Net standard 1.6 SDK support is deprecated, use Net standard 2.0 or above'
UserAttributes userAttributes, IErrorHandler errorHandler, ILogger logger
) : this(optimizely, userId, userAttributes, null, null, errorHandler, logger) { }

public OptimizelyUserContext(Optimizely optimizely, string userId,

Check warning on line 68 in OptimizelySDK/OptimizelyUserContext.cs

View workflow job for this annotation

GitHub Actions / Build Standard 1.6

'Optimizely' is obsolete: 'Net standard 1.6 SDK support is deprecated, use Net standard 2.0 or above'
UserAttributes userAttributes, ForcedDecisionsStore forcedDecisionsStore,
IErrorHandler errorHandler, ILogger logger
) : this(optimizely, userId, userAttributes, forcedDecisionsStore, null, errorHandler,
logger)
{ }

public OptimizelyUserContext(Optimizely optimizely, string userId,

Check warning on line 75 in OptimizelySDK/OptimizelyUserContext.cs

View workflow job for this annotation

GitHub Actions / Build Standard 1.6

'Optimizely' is obsolete: 'Net standard 1.6 SDK support is deprecated, use Net standard 2.0 or above'
UserAttributes userAttributes, ForcedDecisionsStore forcedDecisionsStore,
List<string> qualifiedSegments, IErrorHandler errorHandler, ILogger logger
#if USE_ODP
Expand All @@ -91,7 +91,10 @@
#if USE_ODP
if (shouldIdentifyUser)
{
optimizely.IdentifyUser(UserId);
optimizely.IdentifyUser(new Dictionary<string, string>
{
{ Odp.Constants.FS_USER_ID, UserId },
});
}
#endif
}
Expand All @@ -108,7 +111,7 @@
/// Returns Optimizely instance associated with the UserContext.
/// </summary>
/// <returns> Optimizely instance.</returns>
public virtual Optimizely GetOptimizely()

Check warning on line 114 in OptimizelySDK/OptimizelyUserContext.cs

View workflow job for this annotation

GitHub Actions / Build Standard 1.6

'Optimizely' is obsolete: 'Net standard 1.6 SDK support is deprecated, use Net standard 2.0 or above'
{
return Optimizely;
}
Expand Down
Loading