Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
48f7d8d
Add vuid optln feature
muzahidul-opti Oct 15, 2024
b799412
[WIP] Refractored vuid logic
muzahidul-opti Oct 17, 2024
2cb824e
clean up
muzahidul-opti Oct 17, 2024
ea762f7
Test target build successfully
muzahidul-opti Oct 17, 2024
7c2bd51
ODP test case updated
muzahidul-opti Oct 17, 2024
fde285a
OdpManager test case added
muzahidul-opti Oct 17, 2024
283608e
Client updated
muzahidul-opti Oct 17, 2024
4adc0a0
Test case updated
muzahidul-opti Oct 17, 2024
2eeb008
WIP: VuidManager move to client
muzahidul-opti Oct 18, 2024
b89e594
WIP: Update odpmanager and client
muzahidul-opti Oct 18, 2024
94910e2
[WIP] Update test cases
muzahidul-opti Oct 18, 2024
b71e65c
WIP: client release issue fixed
muzahidul-opti Oct 18, 2024
b835b8f
WIP: Relase test fixed
muzahidul-opti Oct 18, 2024
7f49d58
WIP: clean up
muzahidul-opti Oct 22, 2024
97e6447
WIP: VuidManager singleton added
muzahidul-opti Oct 30, 2024
b54266c
WIP: OdpVuidManager renamed as VuidManager
muzahidul-opti Oct 30, 2024
f916638
WIP: Make vuid optional to identifyUser method
muzahidul-opti Oct 30, 2024
260673b
WIP: Decide reasons test cases changes reverted
muzahidul-opti Oct 30, 2024
5b971fb
WIP: Vuid initialize method name fixed
muzahidul-opti Oct 30, 2024
172a04d
WIP: VuidManager api renamed. Ignore case comparision added
muzahidul-opti Nov 1, 2024
7e49835
Merge branch 'master' into muzahid/vuid-optln
muzahidul-opti Nov 8, 2024
83ab331
wip: vuidmanager made public
muzahidul-opti Nov 15, 2024
6b1acd8
Client initialize event sperated from odp manager
muzahidul-opti Nov 20, 2024
aa9c0dc
Merge branch 'master' into muzahid/vuid-optln
muzahidul-opti Nov 20, 2024
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
2 changes: 1 addition & 1 deletion Sources/ODP/OdpEventManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ open class OdpEventManager {

// MARK: - events

func registerVUID(vuid: String) {
func sendInitializedEvent(vuid: String) {
sendEvent(type: Constants.ODP.eventType,
action: "client_initialized",
identifiers: [
Expand Down
31 changes: 12 additions & 19 deletions Sources/ODP/OdpManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,13 @@ import Foundation

public class OdpManager {
var enabled: Bool
var vuidManager: OdpVuidManager

var odpConfig: OdpConfig!
var segmentManager: OdpSegmentManager!
var eventManager: OdpEventManager!

let logger = OPTLoggerFactory.getLogger()

var vuid: String {
return vuidManager.vuid
}
var vuid: String?

/// OdpManager init
/// - Parameters:
Expand All @@ -42,6 +38,7 @@ public class OdpManager {
/// - eventManager: ODPEventManager
public init(sdkKey: String,
disable: Bool,
vuid: String? = nil,
cacheSize: Int,
cacheTimeoutInSecs: Int,
timeoutForSegmentFetchInSecs: Int? = nil,
Expand All @@ -50,8 +47,7 @@ public class OdpManager {
eventManager: OdpEventManager? = nil) {

self.enabled = !disable
self.vuidManager = OdpVuidManager.shared

self.vuid = vuid
guard enabled else {
logger.i(.odpNotEnabled)
return
Expand All @@ -65,8 +61,9 @@ public class OdpManager {
self.odpConfig = OdpConfig()
self.segmentManager.odpConfig = odpConfig
self.eventManager.odpConfig = odpConfig

self.eventManager.registerVUID(vuid: self.vuidManager.vuid)
if let vuid = vuid, OdpVuidManager.isVuid(vuid) {
self.eventManager.sendInitializedEvent(vuid: vuid)
}
}

func fetchQualifiedSegments(userId: String,
Expand Down Expand Up @@ -97,15 +94,13 @@ public class OdpManager {
return
}

var vuid = vuidManager.vuid
var fsUserId: String? = userId
if OdpVuidManager.isVuid(userId) {
// overwrite if userId is vuid (when userContext is created with vuid)
vuid = userId
fsUserId = nil
eventManager.identifyUser(vuid: userId, userId: nil)
} else if let _vuid = vuid {
Copy link
Contributor

Choose a reason for hiding this comment

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

We also need the case covered -
vuid: nil and
user_id: valid not vuid

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe we have unit tests for all these combos. If missed, can we add one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yah, test case is failing. Made the vuid optional to identifyUser.

func identifyUser(vuid: String?, userId: String?) {
        var identifiers = [String: String]()
        if let _vuid = vuid {
            identifiers[Constants.ODP.keyForVuid] = _vuid
        }

eventManager.identifyUser(vuid: _vuid, userId: userId)
}

eventManager.identifyUser(vuid: vuid, userId: fsUserId)
}

/// Send an event to the ODP server.
Expand All @@ -125,15 +120,13 @@ public class OdpManager {

let typeUpdated = (type ?? "").isEmpty ? Constants.ODP.eventType : type!

// add vuid to all events by default

var identifiersUpdated = identifiers
if identifiers[Constants.ODP.keyForVuid] == nil {
identifiersUpdated[Constants.ODP.keyForVuid] = vuidManager.vuid
let _vuid = vuid ?? ""
if identifiers[Constants.ODP.keyForVuid] == nil, OdpVuidManager.isVuid(_vuid) {
identifiersUpdated[Constants.ODP.keyForVuid] = _vuid
}

// replace aliases (fs-user-id, FS_USER_ID, FS-USER-ID) with "fs_user_id".

for (idKey, idValue) in identifiersUpdated {
if idKey == Constants.ODP.keyForUserId { break }

Expand Down
32 changes: 24 additions & 8 deletions Sources/ODP/OdpVuidManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@
import Foundation

class OdpVuidManager {
var vuid: String = ""
private var _vuid: String = ""
private(set) var enabled: Bool
let logger = OPTLoggerFactory.getLogger()

// a single vuid should be shared for all SDK instances
static let shared = OdpVuidManager()
Copy link
Contributor

Choose a reason for hiding this comment

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

We should keep it a singleton, so can share a single vuid per device.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Declared singleton and add initialize method.

static let shared = VuidManager()
    
    func initialize(enabled: Bool) {
        self.enabled = enabled
        if enabled {
            self._vuid = load()
        } else {
            self.remove()
        }
    }
    


init() {
self.vuid = load()
init(enabled: Bool) {
self.enabled = enabled
if enabled {
self._vuid = load()
} else {
self.remove()
}
}

static var newVuid: String {
Expand All @@ -35,7 +38,7 @@ class OdpVuidManager {
let vuid = (vuidFull.count <= maxLength) ? vuidFull : String(vuidFull.prefix(maxLength))
return vuid
}

static func isVuid(_ visitorId: String) -> Bool {
return visitorId.starts(with: "vuid_")
}
Expand All @@ -45,6 +48,14 @@ class OdpVuidManager {
// MARK: - VUID Store

extension OdpVuidManager {
var vuid: String {
if enabled {
return _vuid
} else {
logger.w("VUID is not enabled.")
return ""
}
}

private var keyForVuid: String {
return "optimizely-vuid"
Expand All @@ -59,7 +70,12 @@ extension OdpVuidManager {
save(vuid: vuid)
return vuid
}


private func remove() {
UserDefaults.standard.set(nil, forKey: keyForVuid)
UserDefaults.standard.synchronize()
}

private func save(vuid: String) {
UserDefaults.standard.set(vuid, forKey: keyForVuid)
UserDefaults.standard.synchronize()
Expand Down
4 changes: 4 additions & 0 deletions Sources/ODP/OptimizelySdkSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public struct OptimizelySdkSettings {
let timeoutForOdpEventInSecs: Int
/// ODP features are disabled if this is set to true.
let disableOdp: Bool
/// VUID is enabled if this is set to true.
let enableVuid: Bool

/// Optimizely SDK Settings
///
Expand All @@ -43,13 +45,15 @@ public struct OptimizelySdkSettings {
timeoutForSegmentFetchInSecs: Int = 10,
timeoutForOdpEventInSecs: Int = 10,
disableOdp: Bool = false,
enabledVuid: Bool = false,
sdkName: String? = nil,
sdkVersion: String? = nil) {
self.segmentsCacheSize = segmentsCacheSize
self.segmentsCacheTimeoutInSecs = segmentsCacheTimeoutInSecs
self.timeoutForSegmentFetchInSecs = timeoutForSegmentFetchInSecs
self.timeoutForOdpEventInSecs = timeoutForOdpEventInSecs
self.disableOdp = disableOdp
self.enableVuid = enabledVuid
if let _sdkName = sdkName, _sdkName != "" {
Utils.swiftSdkClientName = _sdkName
}
Expand Down
6 changes: 5 additions & 1 deletion Sources/Optimizely+Decide/OptimizelyClient+Decide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ extension OptimizelyClient {
///
/// - Parameter attributes: A map of attribute names to current user attribute values.
/// - Returns: An OptimizelyUserContext associated with this OptimizelyClient
public func createUserContext(attributes: [String: Any]? = nil) -> OptimizelyUserContext {
public func createUserContext(attributes: [String: Any]? = nil) -> OptimizelyUserContext? {
guard enableVuid, OdpVuidManager.isVuid(vuid) else {
logger.e("Vuid is not enabled or invalid VUID. User context not created.")
return nil
}
return OptimizelyUserContext(optimizely: self, userId: vuid, attributes: attributes)
}

Expand Down
1 change: 0 additions & 1 deletion Sources/Optimizely+Decide/OptimizelyUserContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ public class OptimizelyUserContext {
self.atomicAttributes = AtomicProperty(property: attributes, lock: lock)
self.atomicForcedDecisions = AtomicProperty(property: nil, lock: lock)
self.atomicQualifiedSegments = AtomicProperty(property: nil, lock: lock)

if identify {
// async call so event building overhead is not blocking context creation
lock.async {
Expand Down
11 changes: 9 additions & 2 deletions Sources/Optimizely/OptimizelyClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ open class OptimizelyClient: NSObject {
var decisionService: OPTDecisionService!
public var notificationCenter: OPTNotificationCenter?
public var odpManager: OdpManager!
private var vuidManager: OdpVuidManager!
let sdkSettings: OptimizelySdkSettings

// MARK: - Public interfaces
Expand Down Expand Up @@ -91,13 +92,15 @@ open class OptimizelyClient: NSObject {
self.defaultDecideOptions = defaultDecideOptions ?? []

super.init()

self.vuidManager = OdpVuidManager(enabled: sdkSettings.enableVuid)
self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey,
disable: sdkSettings.disableOdp,
vuid: vuidManager.vuid,
cacheSize: sdkSettings.segmentsCacheSize,
cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs,
timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs,
timeoutForEventDispatchInSecs: sdkSettings.timeoutForOdpEventInSecs)

let userProfileService = userProfileService ?? DefaultUserProfileService()
let logger = logger ?? DefaultLogger()
type(of: logger).logLevel = defaultLogLevel ?? .info
Expand Down Expand Up @@ -972,7 +975,11 @@ extension OptimizelyClient {

/// the device vuid (read only)
public var vuid: String {
return odpManager.vuid
return self.vuidManager.vuid
}

public var enableVuid: Bool {
return self.vuidManager.enabled
}

func identifyUserToOdp(userId: String) {
Expand Down
14 changes: 8 additions & 6 deletions Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class OptimizelyClientTests_Decide: XCTestCase {
super.setUp()

let datafile = OTUtils.loadJSONDatafile("api_datafile")!
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey)
let settings = OptimizelySdkSettings(enabledVuid: true)
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: settings)

try! optimizely.start(datafile: datafile)
}

Expand Down Expand Up @@ -54,11 +56,11 @@ class OptimizelyClientTests_Decide: XCTestCase {

let user = optimizely.createUserContext(attributes: attributes)

XCTAssert(user.optimizely == optimizely)
XCTAssert(user.userId == optimizely.vuid, "vuid should be used as the default userId when not given")
XCTAssert(user.attributes["country"] as! String == "us")
XCTAssert(user.attributes["age"] as! Int == 100)
XCTAssert(user.attributes["old"] as! Bool == true)
XCTAssert(user?.optimizely == optimizely)
XCTAssert(user?.userId == optimizely.vuid, "vuid should be used as the default userId when not given")
XCTAssert(user?.attributes["country"] as! String == "us")
XCTAssert(user?.attributes["age"] as! Int == 100)
XCTAssert(user?.attributes["old"] as! Bool == true)
}

func testCreateUserContext_multiple() {
Expand Down
11 changes: 10 additions & 1 deletion Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,19 @@ class OptimizelyClientTests_ODP: XCTestCase {

// MARK: - vuid

func testVuid() {
func testVuidEnabled() {
let settings = OptimizelySdkSettings(enabledVuid: true)
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: settings)
XCTAssertTrue(optimizely.enableVuid)
XCTAssert(optimizely.vuid.starts(with: "vuid_"))
}

func testVuidDiabled() {
// Default client vuid diabled
XCTAssertFalse(optimizely.enableVuid)
XCTAssert(optimizely.vuid.isEmpty)
}

// MARK: - OdpConfig Update

func testUpdateOpdConfigCalled_wheneverProjectConfigUpdated_initialOrPolling() {
Expand Down
8 changes: 4 additions & 4 deletions Tests/OptimizelyTests-Common/OdpEventManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class OdpEventManagerTests: XCTestCase {
}

func testRegisterVUID_noApiKey() {
manager.registerVUID(vuid: "v1")
manager.sendInitializedEvent(vuid: "v1")

XCTAssertEqual(1, manager.eventQueue.count)

Expand Down Expand Up @@ -149,7 +149,7 @@ class OdpEventManagerTests: XCTestCase {

XCTAssertTrue(manager.odpConfig.eventQueueingAllowed, "initially datafile not ready and assumed queueing is allowed")

manager.registerVUID(vuid: "v1") // each of these will try to flush
manager.sendInitializedEvent(vuid: "v1") // each of these will try to flush
manager.identifyUser(vuid: "v1", userId: "u1")
manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:])

Expand Down Expand Up @@ -184,7 +184,7 @@ class OdpEventManagerTests: XCTestCase {

XCTAssertTrue(manager.odpConfig.eventQueueingAllowed, "initially datafile not ready and assumed queueing is allowed")

manager.registerVUID(vuid: "v1") // each of these will try to flush
manager.sendInitializedEvent(vuid: "v1") // each of these will try to flush
manager.identifyUser(vuid: "v1", userId: "u1")
manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:])

Expand Down Expand Up @@ -215,7 +215,7 @@ class OdpEventManagerTests: XCTestCase {
func testFlush_maxSize() {
manager.maxQueueSize = 2

manager.registerVUID(vuid: "v1") // each of these will try to flush
manager.sendInitializedEvent(vuid: "v1") // each of these will try to flush
manager.identifyUser(vuid: "v1", userId: "u1")
manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:])

Expand Down
Loading
Loading