From 48f7d8de3b8ff056e3803a6f2e8ff303829f0a3d Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Tue, 15 Oct 2024 18:23:19 +0600 Subject: [PATCH 01/23] Add vuid optln feature --- Sources/ODP/OdpManager.swift | 39 ++++++++++++------- Sources/ODP/OdpVuidManager.swift | 29 ++++++++++---- Sources/ODP/OptimizelySdkSettings.swift | 4 ++ .../OptimizelyClient+Decide.swift | 6 ++- Sources/Optimizely/OptimizelyClient.swift | 1 + 5 files changed, 58 insertions(+), 21 deletions(-) diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index 8553fdc10..ece5169bf 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -18,6 +18,7 @@ import Foundation public class OdpManager { var enabled: Bool + var enableVuid: Bool var vuidManager: OdpVuidManager var odpConfig: OdpConfig! @@ -42,6 +43,7 @@ public class OdpManager { /// - eventManager: ODPEventManager public init(sdkKey: String, disable: Bool, + enableVuid: Bool, cacheSize: Int, cacheTimeoutInSecs: Int, timeoutForSegmentFetchInSecs: Int? = nil, @@ -50,7 +52,8 @@ public class OdpManager { eventManager: OdpEventManager? = nil) { self.enabled = !disable - self.vuidManager = OdpVuidManager.shared + self.enableVuid = enableVuid + self.vuidManager = OdpVuidManager(enabled: enableVuid) guard enabled else { logger.i(.odpNotEnabled) @@ -65,8 +68,10 @@ public class OdpManager { self.odpConfig = OdpConfig() self.segmentManager.odpConfig = odpConfig self.eventManager.odpConfig = odpConfig + if enableVuid { + self.eventManager.registerVUID(vuid: self.vuidManager.vuid) + } - self.eventManager.registerVUID(vuid: self.vuidManager.vuid) } func fetchQualifiedSegments(userId: String, @@ -97,15 +102,17 @@ 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 + if enableVuid { + 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: vuid, userId: fsUserId) } - eventManager.identifyUser(vuid: vuid, userId: fsUserId) } /// Send an event to the ODP server. @@ -125,13 +132,19 @@ 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 + + if enableVuid { + // add vuid to all events by default + if identifiers[Constants.ODP.keyForVuid] == nil { + identifiersUpdated[Constants.ODP.keyForVuid] = vuidManager.vuid + } + } else { + // remove vuid events + identifiersUpdated[Constants.ODP.keyForVuid] = nil } + // replace aliases (fs-user-id, FS_USER_ID, FS-USER-ID) with "fs_user_id". for (idKey, idValue) in identifiersUpdated { diff --git a/Sources/ODP/OdpVuidManager.swift b/Sources/ODP/OdpVuidManager.swift index f1112148d..0313b4e74 100644 --- a/Sources/ODP/OdpVuidManager.swift +++ b/Sources/ODP/OdpVuidManager.swift @@ -17,14 +17,17 @@ import Foundation class OdpVuidManager { - var vuid: String = "" + private var _vuid: String = "" + var enabled: Bool let logger = OPTLoggerFactory.getLogger() - - // a single vuid should be shared for all SDK instances - static let shared = OdpVuidManager() - init() { - self.vuid = load() + init(enabled: Bool) { + self.enabled = enabled + if enabled { + self._vuid = load() + } else { + self.remove() + } } static var newVuid: String { @@ -45,6 +48,13 @@ class OdpVuidManager { // MARK: - VUID Store extension OdpVuidManager { + var vuid: String { + if enabled { + return _vuid + } else { + return "" + } + } private var keyForVuid: String { return "optimizely-vuid" @@ -59,7 +69,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() diff --git a/Sources/ODP/OptimizelySdkSettings.swift b/Sources/ODP/OptimizelySdkSettings.swift index 6a1d71225..0907c22c3 100644 --- a/Sources/ODP/OptimizelySdkSettings.swift +++ b/Sources/ODP/OptimizelySdkSettings.swift @@ -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 /// @@ -43,6 +45,7 @@ public struct OptimizelySdkSettings { timeoutForSegmentFetchInSecs: Int = 10, timeoutForOdpEventInSecs: Int = 10, disableOdp: Bool = false, + enabledVuid: Bool = false, sdkName: String? = nil, sdkVersion: String? = nil) { self.segmentsCacheSize = segmentsCacheSize @@ -50,6 +53,7 @@ public struct OptimizelySdkSettings { self.timeoutForSegmentFetchInSecs = timeoutForSegmentFetchInSecs self.timeoutForOdpEventInSecs = timeoutForOdpEventInSecs self.disableOdp = disableOdp + self.enableVuid = enabledVuid if let _sdkName = sdkName, _sdkName != "" { Utils.swiftSdkClientName = _sdkName } diff --git a/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift b/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift index da7e3c04b..1a242461e 100644 --- a/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift +++ b/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift @@ -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 sdkSettings.enableVuid else { + logger.e("Vuid is not enabled. User context creation failed.") + return nil + } return OptimizelyUserContext(optimizely: self, userId: vuid, attributes: attributes) } diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index 665bc0af6..fd01c8128 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -94,6 +94,7 @@ open class OptimizelyClient: NSObject { self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey, disable: sdkSettings.disableOdp, + enableVuid: sdkSettings.enableVuid, cacheSize: sdkSettings.segmentsCacheSize, cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs, timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs, From b7994122a97ad69fd08fb24d5bcca15dc5c9ce5b Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Thu, 17 Oct 2024 15:51:40 +0600 Subject: [PATCH 02/23] [WIP] Refractored vuid logic --- Sources/ODP/OdpManager.swift | 33 +++++-------------- Sources/ODP/OdpVuidManager.swift | 5 +-- .../OptimizelyClient+Decide.swift | 4 +-- Sources/Optimizely/OptimizelyClient.swift | 8 +++++ 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index ece5169bf..61eb3ea1b 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -68,10 +68,6 @@ public class OdpManager { self.odpConfig = OdpConfig() self.segmentManager.odpConfig = odpConfig self.eventManager.odpConfig = odpConfig - if enableVuid { - self.eventManager.registerVUID(vuid: self.vuidManager.vuid) - } - } func fetchQualifiedSegments(userId: String, @@ -102,17 +98,14 @@ public class OdpManager { return } - if enableVuid { - 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: vuid, userId: fsUserId) + 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: vuid, userId: fsUserId) } /// Send an event to the ODP server. @@ -134,19 +127,11 @@ public class OdpManager { var identifiersUpdated = identifiers - if enableVuid { - // add vuid to all events by default - if identifiers[Constants.ODP.keyForVuid] == nil { - identifiersUpdated[Constants.ODP.keyForVuid] = vuidManager.vuid - } - } else { - // remove vuid events - identifiersUpdated[Constants.ODP.keyForVuid] = nil + if identifiers[Constants.ODP.keyForVuid] == nil { + identifiersUpdated[Constants.ODP.keyForVuid] = vuidManager.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 } diff --git a/Sources/ODP/OdpVuidManager.swift b/Sources/ODP/OdpVuidManager.swift index 0313b4e74..451cfb25b 100644 --- a/Sources/ODP/OdpVuidManager.swift +++ b/Sources/ODP/OdpVuidManager.swift @@ -18,10 +18,10 @@ import Foundation class OdpVuidManager { private var _vuid: String = "" - var enabled: Bool + private(set) var enabled: Bool let logger = OPTLoggerFactory.getLogger() - init(enabled: Bool) { + init(enabled: Bool = false) { self.enabled = enabled if enabled { self._vuid = load() @@ -52,6 +52,7 @@ extension OdpVuidManager { if enabled { return _vuid } else { + logger.w("VUID is not enabled.") return "" } } diff --git a/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift b/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift index 1a242461e..f0b6344d0 100644 --- a/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift +++ b/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift @@ -36,8 +36,8 @@ 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? { - guard sdkSettings.enableVuid else { - logger.e("Vuid is not enabled. User context creation failed.") + 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) diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index fd01c8128..bd64ceaf7 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -99,6 +99,10 @@ open class OptimizelyClient: NSObject { cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs, timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs, timeoutForEventDispatchInSecs: sdkSettings.timeoutForOdpEventInSecs) + + if self.odpManager.enableVuid { + self.odpManager.eventManager.registerVUID(vuid: self.odpManager.vuid) + } let userProfileService = userProfileService ?? DefaultUserProfileService() let logger = logger ?? DefaultLogger() type(of: logger).logLevel = defaultLogLevel ?? .info @@ -976,6 +980,10 @@ extension OptimizelyClient { return odpManager.vuid } + public var enableVuid: Bool { + return odpManager.enableVuid + } + func identifyUserToOdp(userId: String) { odpManager.identifyUser(userId: userId) } From 2cb824ef5d20c97a1b97de350029d13863daf1c2 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Thu, 17 Oct 2024 20:10:34 +0600 Subject: [PATCH 03/23] clean up --- Sources/ODP/OdpManager.swift | 4 ++++ Sources/ODP/OdpVuidManager.swift | 2 +- Sources/Optimizely/OptimizelyClient.swift | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index 61eb3ea1b..8f9fb07b7 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -68,6 +68,10 @@ public class OdpManager { self.odpConfig = OdpConfig() self.segmentManager.odpConfig = odpConfig self.eventManager.odpConfig = odpConfig + + if enableVuid { + self.eventManager.registerVUID(vuid: vuidManager.vuid) + } } func fetchQualifiedSegments(userId: String, diff --git a/Sources/ODP/OdpVuidManager.swift b/Sources/ODP/OdpVuidManager.swift index 451cfb25b..48b31779d 100644 --- a/Sources/ODP/OdpVuidManager.swift +++ b/Sources/ODP/OdpVuidManager.swift @@ -21,7 +21,7 @@ class OdpVuidManager { private(set) var enabled: Bool let logger = OPTLoggerFactory.getLogger() - init(enabled: Bool = false) { + init(enabled: Bool) { self.enabled = enabled if enabled { self._vuid = load() diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index bd64ceaf7..e264213c9 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -99,10 +99,10 @@ open class OptimizelyClient: NSObject { cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs, timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs, timeoutForEventDispatchInSecs: sdkSettings.timeoutForOdpEventInSecs) - - if self.odpManager.enableVuid { - self.odpManager.eventManager.registerVUID(vuid: self.odpManager.vuid) - } +// +// if self.odpManager.enableVuid { +// self.odpManager.eventManager.registerVUID(vuid: self.odpManager.vuid) +// } let userProfileService = userProfileService ?? DefaultUserProfileService() let logger = logger ?? DefaultLogger() type(of: logger).logLevel = defaultLogLevel ?? .info From ea762f78b85251dd2dfc02507babafb13bb9a130 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Thu, 17 Oct 2024 20:45:53 +0600 Subject: [PATCH 04/23] Test target build successfully --- .../OptimizelyClientTests_Decide.swift | 14 +++-- .../OptimizelyClientTests_ODP.swift | 6 +- .../DecisionServiceTests_Experiments.swift | 1 + .../OdpManagerTests.swift | 11 +++- .../OdpVuidManagerTests.swift | 8 +-- ...izelyUserContextTests_Decide_Reasons.swift | 60 +++++++++---------- .../OptimizelyUserContextTests_ODP.swift | 2 +- .../OptimizelyUserContextTests_ODP_2.swift | 1 + ...izelyUserContextTests_ODP_Aync_Await.swift | 2 +- 9 files changed, 57 insertions(+), 48 deletions(-) diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift index c67ba7b77..72c500739 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift @@ -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) } @@ -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() { diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift index 9151bc0d6..400ff1f11 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift @@ -66,7 +66,7 @@ class OptimizelyClientTests_ODP: XCTestCase { // MARK: - sendOdpEvent func testSendOdpEvent_success() { - let odpManager = MockOdpManager(sdkKey: "any", disable: false, cacheSize: 12, cacheTimeoutInSecs: 123) + let odpManager = MockOdpManager(sdkKey: "any", disable: false, enableVuid: true, cacheSize: 12, cacheTimeoutInSecs: 123) optimizely.odpManager = odpManager try? optimizely.sendOdpEvent(type: "t1", action: "a1", identifiers: ["k1": "v1"], data: ["k21": "v2", "k22": true, "k23": 3.5, "k24": nil]) @@ -92,7 +92,7 @@ class OptimizelyClientTests_ODP: XCTestCase { func testSendOdpEvent_customClientNameAndVersion() { let odpEventManager = MockOdpEventManager(sdkKey: "any") - let odpManager = OdpManager(sdkKey: "any", disable: false, cacheSize: 12, cacheTimeoutInSecs: 123, eventManager: odpEventManager) + let odpManager = OdpManager(sdkKey: "any", disable: false, enableVuid: true, cacheSize: 12, cacheTimeoutInSecs: 123, eventManager: odpEventManager) let datafile = OTUtils.loadJSONDatafile("decide_audience_segments")! let tmpOptimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, odpManager: odpManager, settings: OptimizelySdkSettings(sdkName: "flutter-sdk", sdkVersion: "1234")) @@ -194,7 +194,7 @@ class OptimizelyClientTests_ODP: XCTestCase { // MARK: - OdpConfig Update func testUpdateOpdConfigCalled_wheneverProjectConfigUpdated_initialOrPolling() { - let odpManager = MockOdpManager(sdkKey: "any", disable: false, cacheSize: 12, cacheTimeoutInSecs: 123) + let odpManager = MockOdpManager(sdkKey: "any", disable: false, enableVuid: true, cacheSize: 12, cacheTimeoutInSecs: 123) optimizely.odpManager = odpManager XCTAssertNil(odpManager.apiKey) diff --git a/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift b/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift index fff345eab..365b0dbc9 100644 --- a/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift +++ b/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift @@ -486,6 +486,7 @@ extension DecisionServiceTests_Experiments { func testDoesMeetAudienceConditionsWithInvalidMatchType() { MockLogger.expectedLog = OptimizelyError.userAttributeInvalidMatch("{\"match\":\"\",\"value\":17,\"name\":\"age\",\"type\":\"custom_attribute\"}").localizedDescription +// "{\"match\":\"\",\"type\":\"custom_attribute\",\"name\":\"age\",\"value\":17}" self.config.project.typedAudiences = try! OTUtils.model(from: sampleTypedAudiencesData) experiment = try! OTUtils.model(from: sampleExperimentData) diff --git a/Tests/OptimizelyTests-Common/OdpManagerTests.swift b/Tests/OptimizelyTests-Common/OdpManagerTests.swift index 0d8d8db2f..f2de862c1 100644 --- a/Tests/OptimizelyTests-Common/OdpManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpManagerTests.swift @@ -31,6 +31,7 @@ class OdpManagerTests: XCTestCase { eventManager = MockOdpEventManager(sdkKey: sdkKey) manager = OdpManager(sdkKey: sdkKey, disable: false, + enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout, segmentManager: segmentManager, @@ -46,6 +47,7 @@ class OdpManagerTests: XCTestCase { func testConfigurations_cache() { let manager = OdpManager(sdkKey: sdkKey, disable: false, + enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout) XCTAssertEqual(manager.segmentManager?.segmentsCache.maxSize, cacheSize) @@ -57,6 +59,7 @@ class OdpManagerTests: XCTestCase { func testConfigurations_disableOdp() { let manager = OdpManager(sdkKey: sdkKey, disable: true, + enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout) XCTAssertTrue(manager.vuid.starts(with: "vuid_"), "vuid should be serverved even when ODP is disabled.") @@ -101,15 +104,16 @@ class OdpManagerTests: XCTestCase { // MARK: - registerVuid - func testRegisterVUIDCalledAutomatically() { - XCTAssertEqual(eventManager.receivedRegisterVuid, manager.vuid, "registerVUID is implicitly called on OdpManager init") - } +// func testRegisterVUIDCalledAutomatically() { +// XCTAssertEqual(eventManager.receivedRegisterVuid, manager.vuid, "registerVUID is implicitly called on OdpManager init") +// } func testRegisterVUIDCalledAutomatically_odpDisabled() { let newEventManager = MockOdpEventManager(sdkKey: sdkKey) _ = OdpManager(sdkKey: sdkKey, disable: true, + enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout, segmentManager: segmentManager, @@ -320,6 +324,7 @@ class OdpManagerTests: XCTestCase { func testUpdateOdpConfig_odpConfigPropagatedProperly() { let manager = OdpManager(sdkKey: sdkKey, disable: false, + enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout) diff --git a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift b/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift index 261c05446..94b3bfb42 100644 --- a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift @@ -17,7 +17,7 @@ import XCTest class OdpVuidManagerTests: XCTestCase { - var manager = OdpVuidManager() + var manager = OdpVuidManager(enabled: true) func testNewVuid() { let vuid = OdpVuidManager.newVuid @@ -35,10 +35,10 @@ class OdpVuidManagerTests: XCTestCase { func testAutoSaveAndLoad() { UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - manager = OdpVuidManager() + manager = OdpVuidManager(enabled: true) let vuid1 = manager.vuid - manager = OdpVuidManager() + manager = OdpVuidManager(enabled: true) let vuid2 = manager.vuid XCTAssertTrue(vuid1 == vuid2) @@ -47,7 +47,7 @@ class OdpVuidManagerTests: XCTestCase { UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - manager = OdpVuidManager() + manager = OdpVuidManager(enabled: true) let vuid3 = manager.vuid XCTAssertTrue(vuid1 != vuid3) diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift index f0a73284b..5b1269d8f 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift @@ -106,35 +106,35 @@ extension OptimizelyUserContextTests_Decide_Reasons { XCTAssert(decision.reasons.contains(OptimizelyError.conditionInvalidFormat("Empty condition array").reason)) } - func testDecideReasons_evaluateAttributeInvalidCondition() { - let featureKey = "feature_1" - let audienceId = "invalid_condition" - setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - - let condition = "{\"match\":\"gt\",\"value\":\"US\",\"name\":\"age\",\"type\":\"custom_attribute\"}" - user.setAttribute(key: "age", value: 25) - - var decision = user.decide(key: featureKey) - XCTAssert(decision.reasons.isEmpty) - decision = user.decide(key: featureKey, options: [.includeReasons]) - XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidCondition(condition).reason)) - } +// func testDecideReasons_evaluateAttributeInvalidCondition() { +// let featureKey = "feature_1" +// let audienceId = "invalid_condition" +// setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) +// +// let condition = "{\"match\":\"gt\",\"value\":\"US\",\"name\":\"age\",\"type\":\"custom_attribute\"}" +// user.setAttribute(key: "age", value: 25) +// +// var decision = user.decide(key: featureKey) +// XCTAssert(decision.reasons.isEmpty) +// decision = user.decide(key: featureKey, options: [.includeReasons]) +// XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidCondition(condition).reason)) +// } - func testDecideReasons_evaluateAttributeInvalidType() { - let featureKey = "feature_1" - let audienceId = "13389130056" - setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - - let condition = "{\"match\":\"exact\",\"value\":\"US\",\"name\":\"country\",\"type\":\"custom_attribute\"}" - let attributeKey = "country" - let attributeValue = 25 - user.setAttribute(key: attributeKey, value: attributeValue) - - var decision = user.decide(key: featureKey) - XCTAssert(decision.reasons.isEmpty) - decision = user.decide(key: featureKey, options: [.includeReasons]) - XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidType(condition, attributeValue, attributeKey).reason)) - } +// func testDecideReasons_evaluateAttributeInvalidType() { +// let featureKey = "feature_1" +// let audienceId = "13389130056" +// setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) +// +// let condition = "{\"match\":\"exact\",\"value\":\"US\",\"name\":\"country\",\"type\":\"custom_attribute\"}" +// let attributeKey = "country" +// let attributeValue = 25 +// user.setAttribute(key: attributeKey, value: attributeValue) +// +// var decision = user.decide(key: featureKey) +// XCTAssert(decision.reasons.isEmpty) +// decision = user.decide(key: featureKey, options: [.includeReasons]) +// XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidType(condition, attributeValue, attributeKey).reason)) +// } func testDecideReasons_evaluateAttributeValueOutOfRange() { let featureKey = "feature_1" @@ -155,7 +155,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let audienceId = "invalid_type" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - let condition = "{\"match\":\"gt\",\"value\":18,\"name\":\"age\",\"type\":\"invalid\"}" + let condition = "{\"name\":\"age\",\"match\":\"gt\",\"type\":\"invalid\",\"value\":18}" user.setAttribute(key: "age", value: 25) var decision = user.decide(key: featureKey) @@ -211,7 +211,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let audienceId = "age_18" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - let condition = "{\"match\":\"gt\",\"value\":18,\"name\":\"age\",\"type\":\"custom_attribute\"}" + let condition = "{\"type\":\"custom_attribute\",\"name\":\"age\",\"match\":\"gt\",\"value\":18}" var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift index bae1f9372..9e24c5747 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift @@ -315,7 +315,7 @@ class MockOdpManager: OdpManager { var identifyCalled = false init(sdkKey: String, disable: Bool, cacheSize: Int, cacheTimeoutInSecs: Int) { - super.init(sdkKey: sdkKey, disable: disable, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeoutInSecs) + super.init(sdkKey: sdkKey, disable: disable, enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeoutInSecs) self.segmentManager?.apiMgr = MockOdpSegmentApiManager() } diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift index 79e7a3ba8..5eb01a792 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift @@ -36,6 +36,7 @@ class OptimizelyUserContextTests_ODP_2: XCTestCase { let odpEventApiManager = MockOdpEventApiManager() optimizely.odpManager = OdpManager(sdkKey: sdkKey, disable: false, + enableVuid: true, cacheSize: 10, cacheTimeoutInSecs: 10, eventManager: OdpEventManager(sdkKey: sdkKey, diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift index 4b6e96773..26e395e46 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift @@ -128,7 +128,7 @@ extension OptimizelyUserContextTests_ODP_Aync_Await { var identifyCalled = false init(sdkKey: String, disable: Bool, cacheSize: Int, cacheTimeoutInSecs: Int) { - super.init(sdkKey: sdkKey, disable: disable, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeoutInSecs) + super.init(sdkKey: sdkKey, disable: disable, enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeoutInSecs) self.segmentManager?.apiMgr = MockOdpSegmentApiManager() } From 7c2bd516eb12706f769f5e3fee79b704db8e5e7a Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Thu, 17 Oct 2024 20:58:07 +0600 Subject: [PATCH 05/23] ODP test case updated --- .../OptimizelyClientTests_ODP.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift index 400ff1f11..cd68c711c 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift @@ -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() { From fde285a1f6e15d59590f4f9af558127be895292d Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Thu, 17 Oct 2024 21:18:54 +0600 Subject: [PATCH 06/23] OdpManager test case added --- .../OdpManagerTests.swift | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Tests/OptimizelyTests-Common/OdpManagerTests.swift b/Tests/OptimizelyTests-Common/OdpManagerTests.swift index f2de862c1..d19a3ee27 100644 --- a/Tests/OptimizelyTests-Common/OdpManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpManagerTests.swift @@ -104,10 +104,24 @@ class OdpManagerTests: XCTestCase { // MARK: - registerVuid -// func testRegisterVUIDCalledAutomatically() { -// XCTAssertEqual(eventManager.receivedRegisterVuid, manager.vuid, "registerVUID is implicitly called on OdpManager init") -// } - + func testRegisterVUIDCalledAutomatically() { + XCTAssertEqual(eventManager.receivedRegisterVuid, manager.vuid, "registerVUID is implicitly called on OdpManager init") + } + + func testRegisterVUIDDoesNotCallAutomatically_vuidDisabled() { + let newEventManager = MockOdpEventManager(sdkKey: sdkKey) + + _ = OdpManager(sdkKey: sdkKey, + disable: false, + enableVuid: false, + cacheSize: cacheSize, + cacheTimeoutInSecs: cacheTimeout, + segmentManager: segmentManager, + eventManager: newEventManager) + + XCTAssertNil(newEventManager.receivedRegisterVuid) + } + func testRegisterVUIDCalledAutomatically_odpDisabled() { let newEventManager = MockOdpEventManager(sdkKey: sdkKey) From 283608edfc74ab2a0f455d6241bdb413c9862adc Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Thu, 17 Oct 2024 21:20:44 +0600 Subject: [PATCH 07/23] Client updated --- Sources/Optimizely/OptimizelyClient.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index e264213c9..381c458ad 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -99,10 +99,7 @@ open class OptimizelyClient: NSObject { cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs, timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs, timeoutForEventDispatchInSecs: sdkSettings.timeoutForOdpEventInSecs) -// -// if self.odpManager.enableVuid { -// self.odpManager.eventManager.registerVUID(vuid: self.odpManager.vuid) -// } + let userProfileService = userProfileService ?? DefaultUserProfileService() let logger = logger ?? DefaultLogger() type(of: logger).logLevel = defaultLogLevel ?? .info From 4adc0a0f86619b3b38af04f6d1b7e670594763bc Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Thu, 17 Oct 2024 22:28:53 +0600 Subject: [PATCH 08/23] Test case updated --- ...izelyUserContextTests_Decide_Reasons.swift | 175 ++++++++++++++---- 1 file changed, 142 insertions(+), 33 deletions(-) diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift index 5b1269d8f..f35e59307 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift @@ -106,35 +106,53 @@ extension OptimizelyUserContextTests_Decide_Reasons { XCTAssert(decision.reasons.contains(OptimizelyError.conditionInvalidFormat("Empty condition array").reason)) } -// func testDecideReasons_evaluateAttributeInvalidCondition() { -// let featureKey = "feature_1" -// let audienceId = "invalid_condition" -// setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) -// -// let condition = "{\"match\":\"gt\",\"value\":\"US\",\"name\":\"age\",\"type\":\"custom_attribute\"}" -// user.setAttribute(key: "age", value: 25) -// -// var decision = user.decide(key: featureKey) -// XCTAssert(decision.reasons.isEmpty) -// decision = user.decide(key: featureKey, options: [.includeReasons]) -// XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidCondition(condition).reason)) -// } + func testDecideReasons_evaluateAttributeInvalidCondition() { + let featureKey = "feature_1" + let audienceId = "invalid_condition" + setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) + + let condition = "{\"match\":\"gt\",\"value\":\"US\",\"name\":\"age\",\"type\":\"custom_attribute\"}" + user.setAttribute(key: "age", value: 25) + + var decision = user.decide(key: featureKey) + XCTAssert(decision.reasons.isEmpty) + decision = user.decide(key: featureKey, options: [.includeReasons]) + + let expectedReason = OptimizelyError.evaluateAttributeInvalidCondition(condition).reason + + // Look for a matching reason in the decision.reasons array + let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) + + XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") + + //XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidCondition(condition).reason)) + } -// func testDecideReasons_evaluateAttributeInvalidType() { -// let featureKey = "feature_1" -// let audienceId = "13389130056" -// setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) -// -// let condition = "{\"match\":\"exact\",\"value\":\"US\",\"name\":\"country\",\"type\":\"custom_attribute\"}" -// let attributeKey = "country" -// let attributeValue = 25 -// user.setAttribute(key: attributeKey, value: attributeValue) -// -// var decision = user.decide(key: featureKey) -// XCTAssert(decision.reasons.isEmpty) + func testDecideReasons_evaluateAttributeInvalidType() { + let featureKey = "feature_1" + let audienceId = "13389130056" + setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) + + let condition = "{\"match\":\"exact\",\"value\":\"US\",\"name\":\"country\",\"type\":\"custom_attribute\"}" + let attributeKey = "country" + let attributeValue = 25 + user.setAttribute(key: attributeKey, value: attributeValue) + + var decision = user.decide(key: featureKey) + XCTAssert(decision.reasons.isEmpty) + + decision = user.decide(key: featureKey, options: [.includeReasons]) + + let expectedReason = OptimizelyError.evaluateAttributeInvalidType(condition, attributeValue, attributeKey).reason + + // Look for a matching reason in the decision.reasons array + let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) + + XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") + // decision = user.decide(key: featureKey, options: [.includeReasons]) // XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidType(condition, attributeValue, attributeKey).reason)) -// } + } func testDecideReasons_evaluateAttributeValueOutOfRange() { let featureKey = "feature_1" @@ -146,8 +164,17 @@ extension OptimizelyUserContextTests_Decide_Reasons { var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) + decision = user.decide(key: featureKey, options: [.includeReasons]) - XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeValueOutOfRange(condition, "age").reason)) + + let expectedReason = OptimizelyError.evaluateAttributeValueOutOfRange(condition, "age").reason + + // Look for a matching reason in the decision.reasons array + let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) + + XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") + + //XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeValueOutOfRange(condition, "age").reason)) } func testDecideReasons_userAttributeInvalidType() { @@ -161,7 +188,15 @@ extension OptimizelyUserContextTests_Decide_Reasons { var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) - XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidType(condition).reason)) + + let expectedReason = OptimizelyError.userAttributeInvalidType(condition).reason + + // Look for a matching reason in the decision.reasons array + let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) + + XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") + + //XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidType(condition).reason)) } func testDecideReasons_userAttributeInvalidMatch() { @@ -175,7 +210,15 @@ extension OptimizelyUserContextTests_Decide_Reasons { var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) - XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidMatch(condition).reason)) + + let expectedReason = OptimizelyError.userAttributeInvalidMatch(condition).reason + + // Look for a matching reason in the decision.reasons array + let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) + + XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") + + //XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidMatch(condition).reason)) } func testDecideReasons_userAttributeNilValue() { @@ -188,8 +231,17 @@ extension OptimizelyUserContextTests_Decide_Reasons { var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) + decision = user.decide(key: featureKey, options: [.includeReasons]) - XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeNilValue(condition).reason)) + + let expectedReason = OptimizelyError.userAttributeNilValue(condition).reason + + // Look for a matching reason in the decision.reasons array + let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) + + XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") + +// XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeNilValue(condition).reason)) } func testDecideReasons_userAttributeInvalidName() { @@ -202,8 +254,17 @@ extension OptimizelyUserContextTests_Decide_Reasons { var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) + decision = user.decide(key: featureKey, options: [.includeReasons]) - XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidName(condition).reason)) + + let expectedReason = OptimizelyError.userAttributeInvalidName(condition).reason + + // Look for a matching reason in the decision.reasons array + let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) + + XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") + + //XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidName(condition).reason)) } func testDecideReasons_missingAttributeValue() { @@ -215,8 +276,17 @@ extension OptimizelyUserContextTests_Decide_Reasons { var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) + decision = user.decide(key: featureKey, options: [.includeReasons]) - XCTAssert(decision.reasons.contains(OptimizelyError.missingAttributeValue(condition, "age").reason)) + + let expectedReason = OptimizelyError.missingAttributeValue(condition, "age").reason + + // Look for a matching reason in the decision.reasons array + let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) + + XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") + + //XCTAssert(decision.reasons.contains(OptimizelyError.missingAttributeValue(condition, "age").reason)) } } @@ -462,7 +532,46 @@ extension OptimizelyUserContextTests_Decide_Reasons { // Utils extension OptimizelyUserContextTests_Decide_Reasons { - + func matchReason(expectedReason: String, in reasons: [String]) -> Bool { + // Extract the condition JSON and remaining strings from expectedReason + let (expectedPrefix, expectedJsonCondition, expectedSuffix) = extractComponents(from: expectedReason) ?? ("", [:], "") + + // Look for a matching reason in the decision.reasons array + let foundMatchingReason = reasons.contains { reason in + guard let (actualPrefix, actualJsonCondition, actualSuffix) = extractComponents(from: reason) else { + return false + } + + // Check if the prefix and suffix match exactly and the JSON conditions match ignoring key order + return expectedPrefix == actualPrefix && + expectedSuffix == actualSuffix && + NSDictionary(dictionary: expectedJsonCondition) == NSDictionary(dictionary: actualJsonCondition) + } + return foundMatchingReason + } + + // Helper function to extract prefix, JSON condition, and suffix from the reason string + func extractComponents(from reason: String) -> (String, [String: Any], String)? { + // Use regular expression to extract the parts of the string before, inside, and after the parentheses + let pattern = "^(.*?)\\((.*?)\\)(.*?)$" + let regex = try? NSRegularExpression(pattern: pattern) + let nsString = reason as NSString + if let match = regex?.firstMatch(in: reason, range: NSRange(location: 0, length: nsString.length)) { + let prefix = nsString.substring(with: match.range(at: 1)) // Part before the parentheses + let jsonString = nsString.substring(with: match.range(at: 2)) // JSON inside parentheses + let suffix = nsString.substring(with: match.range(at: 3)) // Part after the parentheses + + // Convert the JSON string into a dictionary + if let data = jsonString.data(using: .utf8), + let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []), + let jsonDict = jsonObject as? [String: Any] { + return (prefix, jsonDict, suffix) + } + } + + return nil + } + func setAudienceForFeatureTest(featureKey: String, audienceId: String) { let experimentId = "10390977673" // "exp_with_audience" var experiment = optimizely.config!.getExperiment(id: experimentId)! From 2eeb008b824b6606e9310461a38398c7a3c26a79 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Fri, 18 Oct 2024 22:40:54 +0600 Subject: [PATCH 09/23] WIP: VuidManager move to client --- Sources/ODP/OdpManager.swift | 24 +++++++++---------- .../OptimizelyUserContext.swift | 2 +- Sources/Optimizely/OptimizelyClient.swift | 13 ++++++---- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index 8f9fb07b7..183bbf711 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -18,7 +18,7 @@ import Foundation public class OdpManager { var enabled: Bool - var enableVuid: Bool +// var enableVuid: Bool var vuidManager: OdpVuidManager var odpConfig: OdpConfig! @@ -27,9 +27,9 @@ public class OdpManager { let logger = OPTLoggerFactory.getLogger() - var vuid: String { - return vuidManager.vuid - } +// var vuid: String { +// return vuidManager.vuid +// } /// OdpManager init /// - Parameters: @@ -52,7 +52,7 @@ public class OdpManager { eventManager: OdpEventManager? = nil) { self.enabled = !disable - self.enableVuid = enableVuid +// self.enableVuid = enableVuid self.vuidManager = OdpVuidManager(enabled: enableVuid) guard enabled else { @@ -69,9 +69,9 @@ public class OdpManager { self.segmentManager.odpConfig = odpConfig self.eventManager.odpConfig = odpConfig - if enableVuid { - self.eventManager.registerVUID(vuid: vuidManager.vuid) - } +// if enableVuid { +// self.eventManager.registerVUID(vuid: vuidManager.vuid) +// } } func fetchQualifiedSegments(userId: String, @@ -91,7 +91,7 @@ public class OdpManager { completionHandler: completionHandler) } - func identifyUser(userId: String) { + func identifyUser(userId: String, vuid: String) { guard enabled else { logger.d("ODP identify event is not dispatched (ODP disabled).") return @@ -102,14 +102,14 @@ public class OdpManager { return } - var vuid = vuidManager.vuid + var _vuid = vuid var fsUserId: String? = userId if OdpVuidManager.isVuid(userId) { // overwrite if userId is vuid (when userContext is created with vuid) - vuid = userId + _vuid = userId fsUserId = nil } - eventManager.identifyUser(vuid: vuid, userId: fsUserId) + eventManager.identifyUser(vuid: _vuid, userId: fsUserId) } /// Send an event to the ODP server. diff --git a/Sources/Optimizely+Decide/OptimizelyUserContext.swift b/Sources/Optimizely+Decide/OptimizelyUserContext.swift index 7f7cf48e4..21f99bf8a 100644 --- a/Sources/Optimizely+Decide/OptimizelyUserContext.swift +++ b/Sources/Optimizely+Decide/OptimizelyUserContext.swift @@ -88,7 +88,7 @@ public class OptimizelyUserContext { if identify { // async call so event building overhead is not blocking context creation lock.async { - self.optimizely?.identifyUserToOdp(userId: userId) + self.optimizely?.identifyUserToOdp(userId: userId, vuid: optimizely.vuid) } } } diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index 381c458ad..0e8aebc3f 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -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 @@ -117,6 +118,10 @@ open class OptimizelyClient: NSObject { self.decisionService = HandlerRegistryService.shared.injectDecisionService(sdkKey: self.sdkKey) self.notificationCenter = HandlerRegistryService.shared.injectNotificationCenter(sdkKey: self.sdkKey) + self.vuidManager = OdpVuidManager(enabled: sdkSettings.enableVuid) + if self.vuidManager.enabled { + self.odpManager.eventManager.registerVUID(vuid: self.vuidManager.vuid) + } logger.d("SDK Version: \(version)") } @@ -974,15 +979,15 @@ extension OptimizelyClient { /// the device vuid (read only) public var vuid: String { - return odpManager.vuid + return self.vuidManager.vuid } public var enableVuid: Bool { - return odpManager.enableVuid + return self.vuidManager.enabled } - func identifyUserToOdp(userId: String) { - odpManager.identifyUser(userId: userId) + func identifyUserToOdp(userId: String, vuid: String) { + odpManager.identifyUser(userId: userId, vuid: self.vuid) } func fetchQualifiedSegments(userId: String, From b89e5940f898eb93b78557493b6980c8ce8d42cc Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Sat, 19 Oct 2024 00:04:15 +0600 Subject: [PATCH 10/23] WIP: Update odpmanager and client --- Sources/ODP/OdpManager.swift | 20 ++++---------------- Sources/Optimizely/OptimizelyClient.swift | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index 183bbf711..182d5b35f 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -18,18 +18,13 @@ import Foundation public class OdpManager { var enabled: Bool -// var enableVuid: 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: @@ -43,7 +38,6 @@ public class OdpManager { /// - eventManager: ODPEventManager public init(sdkKey: String, disable: Bool, - enableVuid: Bool, cacheSize: Int, cacheTimeoutInSecs: Int, timeoutForSegmentFetchInSecs: Int? = nil, @@ -52,8 +46,6 @@ public class OdpManager { eventManager: OdpEventManager? = nil) { self.enabled = !disable -// self.enableVuid = enableVuid - self.vuidManager = OdpVuidManager(enabled: enableVuid) guard enabled else { logger.i(.odpNotEnabled) @@ -68,10 +60,6 @@ public class OdpManager { self.odpConfig = OdpConfig() self.segmentManager.odpConfig = odpConfig self.eventManager.odpConfig = odpConfig - -// if enableVuid { -// self.eventManager.registerVUID(vuid: vuidManager.vuid) -// } } func fetchQualifiedSegments(userId: String, @@ -130,9 +118,9 @@ public class OdpManager { let typeUpdated = (type ?? "").isEmpty ? Constants.ODP.eventType : type! 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". diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index 0e8aebc3f..a6ba74a82 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -95,7 +95,6 @@ open class OptimizelyClient: NSObject { self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey, disable: sdkSettings.disableOdp, - enableVuid: sdkSettings.enableVuid, cacheSize: sdkSettings.segmentsCacheSize, cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs, timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs, @@ -120,11 +119,26 @@ open class OptimizelyClient: NSObject { self.vuidManager = OdpVuidManager(enabled: sdkSettings.enableVuid) if self.vuidManager.enabled { - self.odpManager.eventManager.registerVUID(vuid: self.vuidManager.vuid) + self.odpManager.vuid = vuidManager.vuid + // Cushes due to odpManager didn't initialize yet. + // self.odpManager.eventManager.registerVUID(vuid: vuidManager.vuid) + } + + // FIXME: Need a better solution + DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { + self.registerVUID() } + + logger.d("SDK Version: \(version)") } + private func registerVUID() { + if self.vuidManager.enabled { + self.odpManager.eventManager.registerVUID(vuid: self.vuidManager.vuid) + } + } + /// Start Optimizely SDK (Asynchronous) /// /// If an updated datafile is available in the server, it's downloaded and the SDK is configured with From 94910e2b18e65caa996e91228c0259a1c177462f Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Sat, 19 Oct 2024 00:04:31 +0600 Subject: [PATCH 11/23] [WIP] Update test cases --- .../OptimizelyClientTests_ODP.swift | 6 +- .../OdpManagerTests.swift | 55 ++++++++----------- .../OdpVuidManagerTests.swift | 13 +++++ .../OptimizelyUserContextTests_ODP.swift | 4 +- .../OptimizelyUserContextTests_ODP_2.swift | 3 +- ...izelyUserContextTests_ODP_Aync_Await.swift | 4 +- 6 files changed, 45 insertions(+), 40 deletions(-) diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift index cd68c711c..ce690ec6b 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift @@ -66,7 +66,7 @@ class OptimizelyClientTests_ODP: XCTestCase { // MARK: - sendOdpEvent func testSendOdpEvent_success() { - let odpManager = MockOdpManager(sdkKey: "any", disable: false, enableVuid: true, cacheSize: 12, cacheTimeoutInSecs: 123) + let odpManager = MockOdpManager(sdkKey: "any", disable: false, cacheSize: 12, cacheTimeoutInSecs: 123) optimizely.odpManager = odpManager try? optimizely.sendOdpEvent(type: "t1", action: "a1", identifiers: ["k1": "v1"], data: ["k21": "v2", "k22": true, "k23": 3.5, "k24": nil]) @@ -92,7 +92,7 @@ class OptimizelyClientTests_ODP: XCTestCase { func testSendOdpEvent_customClientNameAndVersion() { let odpEventManager = MockOdpEventManager(sdkKey: "any") - let odpManager = OdpManager(sdkKey: "any", disable: false, enableVuid: true, cacheSize: 12, cacheTimeoutInSecs: 123, eventManager: odpEventManager) + let odpManager = OdpManager(sdkKey: "any", disable: false, cacheSize: 12, cacheTimeoutInSecs: 123, eventManager: odpEventManager) let datafile = OTUtils.loadJSONDatafile("decide_audience_segments")! let tmpOptimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, odpManager: odpManager, settings: OptimizelySdkSettings(sdkName: "flutter-sdk", sdkVersion: "1234")) @@ -203,7 +203,7 @@ class OptimizelyClientTests_ODP: XCTestCase { // MARK: - OdpConfig Update func testUpdateOpdConfigCalled_wheneverProjectConfigUpdated_initialOrPolling() { - let odpManager = MockOdpManager(sdkKey: "any", disable: false, enableVuid: true, cacheSize: 12, cacheTimeoutInSecs: 123) + let odpManager = MockOdpManager(sdkKey: "any", disable: false, cacheSize: 12, cacheTimeoutInSecs: 123) optimizely.odpManager = odpManager XCTAssertNil(odpManager.apiKey) diff --git a/Tests/OptimizelyTests-Common/OdpManagerTests.swift b/Tests/OptimizelyTests-Common/OdpManagerTests.swift index d19a3ee27..dea89a1bc 100644 --- a/Tests/OptimizelyTests-Common/OdpManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpManagerTests.swift @@ -23,7 +23,6 @@ class OdpManagerTests: XCTestCase { var segmentManager: MockOdpSegmentManager! var eventManager: MockOdpEventManager! var manager: OdpManager! - override func setUp() { OTUtils.clearAllEventQueues() segmentManager = MockOdpSegmentManager(cacheSize: cacheSize, @@ -31,11 +30,11 @@ class OdpManagerTests: XCTestCase { eventManager = MockOdpEventManager(sdkKey: sdkKey) manager = OdpManager(sdkKey: sdkKey, disable: false, - enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout, segmentManager: segmentManager, eventManager: eventManager) + manager.vuid = nil } override func tearDown() { @@ -47,7 +46,7 @@ class OdpManagerTests: XCTestCase { func testConfigurations_cache() { let manager = OdpManager(sdkKey: sdkKey, disable: false, - enableVuid: true, + cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout) XCTAssertEqual(manager.segmentManager?.segmentsCache.maxSize, cacheSize) @@ -59,10 +58,8 @@ class OdpManagerTests: XCTestCase { func testConfigurations_disableOdp() { let manager = OdpManager(sdkKey: sdkKey, disable: true, - enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout) - XCTAssertTrue(manager.vuid.starts(with: "vuid_"), "vuid should be serverved even when ODP is disabled.") let sem = DispatchSemaphore(value: 0) manager.fetchQualifiedSegments(userId: "user1", options: []) { segments, error in @@ -76,8 +73,8 @@ class OdpManagerTests: XCTestCase { XCTAssertNil(manager.odpConfig) // these calls should be dropped gracefully with nil - - manager.identifyUser(userId: "user1") + let vuid = "vuid_123" + manager.identifyUser(userId: "user1", vuid: vuid) try? manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:]) XCTAssertNil(manager.eventManager) @@ -102,18 +99,12 @@ class OdpManagerTests: XCTestCase { XCTAssertEqual(segmentManager.receivedOptions, []) } - // MARK: - registerVuid - - func testRegisterVUIDCalledAutomatically() { - XCTAssertEqual(eventManager.receivedRegisterVuid, manager.vuid, "registerVUID is implicitly called on OdpManager init") - } func testRegisterVUIDDoesNotCallAutomatically_vuidDisabled() { let newEventManager = MockOdpEventManager(sdkKey: sdkKey) _ = OdpManager(sdkKey: sdkKey, disable: false, - enableVuid: false, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout, segmentManager: segmentManager, @@ -127,7 +118,6 @@ class OdpManagerTests: XCTestCase { _ = OdpManager(sdkKey: sdkKey, disable: true, - enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout, segmentManager: segmentManager, @@ -139,14 +129,17 @@ class OdpManagerTests: XCTestCase { // MARK: - identifyUser func testIdentifyUser_datafileNotReady() { - manager.identifyUser(userId: "user-1") + let vuid = "vuid_123" + manager.identifyUser(userId: "user-1", vuid: vuid) XCTAssertEqual(eventManager.receivedIdentifyUserId, "user-1") } func testIdentifyUser_odpIntegrated() { + let vuid = "vuid_123" + manager.vuid = vuid manager.updateOdpConfig(apiKey: "key-1", apiHost: "host-1", segmentsToCheck: []) - manager.identifyUser(userId: "user-1") + manager.identifyUser(userId: "user-1", vuid: "vuid_123") XCTAssert(OdpVuidManager.isVuid(eventManager.receivedIdentifyVuid)) XCTAssertEqual(eventManager.receivedIdentifyUserId, "user-1") @@ -156,7 +149,7 @@ class OdpManagerTests: XCTestCase { manager.updateOdpConfig(apiKey: "key-1", apiHost: "host-1", segmentsToCheck: []) let vuidAsUserId = OdpVuidManager.newVuid - manager.identifyUser(userId: vuidAsUserId) + manager.identifyUser(userId: vuidAsUserId, vuid: "") XCTAssertEqual(eventManager.receivedIdentifyVuid, vuidAsUserId) XCTAssertNil(eventManager.receivedIdentifyUserId) @@ -164,14 +157,14 @@ class OdpManagerTests: XCTestCase { func testIdentifyUser_odpNotIntegrated() { manager.updateOdpConfig(apiKey: nil, apiHost: nil, segmentsToCheck: []) - manager.identifyUser(userId: "user-1") + manager.identifyUser(userId: "user-1", vuid: "") XCTAssertNil(eventManager.receivedIdentifyUserId, "identifyUser event requeut should be discarded if ODP not integrated.") } func testIdentifyUser_odpDisabled() { manager.enabled = false - manager.identifyUser(userId: "user-1") + manager.identifyUser(userId: "user-1", vuid: "") XCTAssertNil(eventManager.receivedIdentifyUserId, "identifyUser event requeut should be discarded if ODP disabled.") } @@ -179,11 +172,14 @@ class OdpManagerTests: XCTestCase { // MARK: - sendEvent func testSendEvent_datafileNotReady() { + let vuid = "vuid_123" + manager.vuid = vuid + try? manager.sendEvent(type: "t1", action: "a1", identifiers: ["id-key1": "id-val-1"], data: ["key1" : "val1"]) XCTAssertEqual(eventManager.receivedType, "t1") XCTAssertEqual(eventManager.receivedAction, "a1") - XCTAssertEqual(eventManager.receivedIdentifiers, ["vuid": manager.vuid,"id-key1": "id-val-1"]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["vuid": "vuid_123","id-key1": "id-val-1"]) XCTAssert(eventManager.receivedData.count == 1) XCTAssert((eventManager.receivedData["key1"] as! String) == "val1") @@ -235,20 +231,23 @@ class OdpManagerTests: XCTestCase { } func testSendEvent_aliasIdentifiers() { + let vuid = "vuid_123" + manager.vuid = vuid + try? manager.sendEvent(type: nil, action: "a1", identifiers: ["fs_user_id": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": vuid]) try? manager.sendEvent(type: nil, action: "a1", identifiers: ["fs-user-id": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": vuid]) try? manager.sendEvent(type: nil, action: "a1", identifiers: ["FS_USER_ID": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": vuid]) try? manager.sendEvent(type: nil, action: "a1", identifiers: ["FS-USER-ID": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": vuid]) try? manager.sendEvent(type: nil, action: "a1", identifiers: ["email": "e1", "FS-USER-ID": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["email": "e1", "fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["email": "e1", "fs_user_id": "v1", "vuid": vuid]) } // MARK: - updateConfig @@ -338,7 +337,6 @@ class OdpManagerTests: XCTestCase { func testUpdateOdpConfig_odpConfigPropagatedProperly() { let manager = OdpManager(sdkKey: sdkKey, disable: false, - enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout) @@ -371,11 +369,6 @@ class OdpManagerTests: XCTestCase { XCTAssertEqual(eventManager.flushApiKeys.count, 1, "flush called when app goes to background") } - // MARK: - vuid - - func testVuid() { - XCTAssertEqual(manager.vuid, manager.vuidManager.vuid) - } // MARK: - Helpers diff --git a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift b/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift index 94b3bfb42..b15e9eff1 100644 --- a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift @@ -52,4 +52,17 @@ class OdpVuidManagerTests: XCTestCase { XCTAssertTrue(vuid1 != vuid3) } + + func testRemoveOldVuid() { + manager = OdpVuidManager(enabled: true) + let cahcedVuid1 = UserDefaults.standard.string(forKey: "optimizely-vuid") + XCTAssertNotNil(cahcedVuid1) + XCTAssertTrue(cahcedVuid1 == manager.vuid) + + _ = OdpVuidManager(enabled: false) + let cahcedVuid2 = UserDefaults.standard.string(forKey: "optimizely-vuid") + XCTAssertNil(cahcedVuid2) + + } + } diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift index 9e24c5747..d7fda53c1 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift @@ -315,7 +315,7 @@ class MockOdpManager: OdpManager { var identifyCalled = false init(sdkKey: String, disable: Bool, cacheSize: Int, cacheTimeoutInSecs: Int) { - super.init(sdkKey: sdkKey, disable: disable, enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeoutInSecs) + super.init(sdkKey: sdkKey, disable: disable, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeoutInSecs) self.segmentManager?.apiMgr = MockOdpSegmentApiManager() } @@ -327,7 +327,7 @@ class MockOdpManager: OdpManager { super.fetchQualifiedSegments(userId: userId, options: options, completionHandler: completionHandler) } - override func identifyUser(userId: String) { + override func identifyUser(userId: String, vuid: String) { self.userId = userId self.identifyCalled = true } diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift index 5eb01a792..c8b1fa32f 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift @@ -27,7 +27,7 @@ class OptimizelyUserContextTests_ODP_2: XCTestCase { // odp disabled to avoid initial noise let optimizely = OptimizelyClient(sdkKey: sdkKey, - settings: OptimizelySdkSettings(disableOdp: true)) + settings: OptimizelySdkSettings(disableOdp: true, enabledVuid: true)) // override with a custom enabled odpManager. // - client_inializatied event will be sent automatically @@ -36,7 +36,6 @@ class OptimizelyUserContextTests_ODP_2: XCTestCase { let odpEventApiManager = MockOdpEventApiManager() optimizely.odpManager = OdpManager(sdkKey: sdkKey, disable: false, - enableVuid: true, cacheSize: 10, cacheTimeoutInSecs: 10, eventManager: OdpEventManager(sdkKey: sdkKey, diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift index 26e395e46..ad43d8c07 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift @@ -128,7 +128,7 @@ extension OptimizelyUserContextTests_ODP_Aync_Await { var identifyCalled = false init(sdkKey: String, disable: Bool, cacheSize: Int, cacheTimeoutInSecs: Int) { - super.init(sdkKey: sdkKey, disable: disable, enableVuid: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeoutInSecs) + super.init(sdkKey: sdkKey, disable: disable, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeoutInSecs) self.segmentManager?.apiMgr = MockOdpSegmentApiManager() } @@ -140,7 +140,7 @@ extension OptimizelyUserContextTests_ODP_Aync_Await { super.fetchQualifiedSegments(userId: userId, options: options, completionHandler: completionHandler) } - override func identifyUser(userId: String) { + override func identifyUser(userId: String, vuid: String) { self.userId = userId self.identifyCalled = true } From b71e65c2f5567b189576fc5bd83da1443e4d4250 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Sat, 19 Oct 2024 00:29:06 +0600 Subject: [PATCH 12/23] WIP: client release issue fixed --- Sources/Optimizely+Decide/OptimizelyUserContext.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Optimizely+Decide/OptimizelyUserContext.swift b/Sources/Optimizely+Decide/OptimizelyUserContext.swift index 21f99bf8a..4d1e1929a 100644 --- a/Sources/Optimizely+Decide/OptimizelyUserContext.swift +++ b/Sources/Optimizely+Decide/OptimizelyUserContext.swift @@ -84,11 +84,11 @@ public class OptimizelyUserContext { self.atomicAttributes = AtomicProperty(property: attributes, lock: lock) self.atomicForcedDecisions = AtomicProperty(property: nil, lock: lock) self.atomicQualifiedSegments = AtomicProperty(property: nil, lock: lock) - + let _vuid = optimizely.vuid if identify { // async call so event building overhead is not blocking context creation lock.async { - self.optimizely?.identifyUserToOdp(userId: userId, vuid: optimizely.vuid) + self.optimizely?.identifyUserToOdp(userId: userId, vuid: _vuid) } } } From b835b8fb429267bb1a6cfd0c4cc6160a1fbcc823 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Sat, 19 Oct 2024 00:40:20 +0600 Subject: [PATCH 13/23] WIP: Relase test fixed --- Sources/Optimizely/OptimizelyClient.swift | 4 ++-- .../DecisionServiceTests_Experiments.swift | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index a6ba74a82..d133dd2c1 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -125,8 +125,8 @@ open class OptimizelyClient: NSObject { } // FIXME: Need a better solution - DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { - self.registerVUID() + DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { [weak self] in + self?.registerVUID() } diff --git a/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift b/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift index 365b0dbc9..fff345eab 100644 --- a/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift +++ b/Tests/OptimizelyTests-Common/DecisionServiceTests_Experiments.swift @@ -486,7 +486,6 @@ extension DecisionServiceTests_Experiments { func testDoesMeetAudienceConditionsWithInvalidMatchType() { MockLogger.expectedLog = OptimizelyError.userAttributeInvalidMatch("{\"match\":\"\",\"value\":17,\"name\":\"age\",\"type\":\"custom_attribute\"}").localizedDescription -// "{\"match\":\"\",\"type\":\"custom_attribute\",\"name\":\"age\",\"value\":17}" self.config.project.typedAudiences = try! OTUtils.model(from: sampleTypedAudiencesData) experiment = try! OTUtils.model(from: sampleExperimentData) From 7f49d58d41f46657e5256ad2c6e69b9f475e7be7 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Wed, 23 Oct 2024 00:22:52 +0600 Subject: [PATCH 14/23] WIP: clean up --- Sources/ODP/OdpEventManager.swift | 2 +- Sources/ODP/OdpManager.swift | 17 +++++++----- Sources/ODP/OdpVuidManager.swift | 2 +- .../OptimizelyUserContext.swift | 3 +-- Sources/Optimizely/OptimizelyClient.swift | 26 +++---------------- .../OdpEventManagerTests.swift | 8 +++--- .../OdpManagerTests.swift | 16 +++++------- .../OptimizelyUserContextTests_ODP.swift | 2 +- ...izelyUserContextTests_ODP_Aync_Await.swift | 2 +- 9 files changed, 30 insertions(+), 48 deletions(-) diff --git a/Sources/ODP/OdpEventManager.swift b/Sources/ODP/OdpEventManager.swift index 9ddec34a2..4fbfc8d47 100644 --- a/Sources/ODP/OdpEventManager.swift +++ b/Sources/ODP/OdpEventManager.swift @@ -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: [ diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index 182d5b35f..fc2d0bded 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -38,6 +38,7 @@ public class OdpManager { /// - eventManager: ODPEventManager public init(sdkKey: String, disable: Bool, + vuid: String? = nil, cacheSize: Int, cacheTimeoutInSecs: Int, timeoutForSegmentFetchInSecs: Int? = nil, @@ -46,7 +47,7 @@ public class OdpManager { eventManager: OdpEventManager? = nil) { self.enabled = !disable - + self.vuid = vuid guard enabled else { logger.i(.odpNotEnabled) return @@ -60,6 +61,9 @@ public class OdpManager { self.odpConfig = OdpConfig() self.segmentManager.odpConfig = odpConfig self.eventManager.odpConfig = odpConfig + if let vuid = vuid, OdpVuidManager.isVuid(vuid) { + self.eventManager.sendInitializedEvent(vuid: vuid) + } } func fetchQualifiedSegments(userId: String, @@ -79,7 +83,7 @@ public class OdpManager { completionHandler: completionHandler) } - func identifyUser(userId: String, vuid: String) { + func identifyUser(userId: String) { guard enabled else { logger.d("ODP identify event is not dispatched (ODP disabled).") return @@ -90,14 +94,13 @@ public class OdpManager { return } - var _vuid = 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 { + eventManager.identifyUser(vuid: _vuid, userId: userId) } - eventManager.identifyUser(vuid: _vuid, userId: fsUserId) + } /// Send an event to the ODP server. diff --git a/Sources/ODP/OdpVuidManager.swift b/Sources/ODP/OdpVuidManager.swift index 48b31779d..c91add12e 100644 --- a/Sources/ODP/OdpVuidManager.swift +++ b/Sources/ODP/OdpVuidManager.swift @@ -38,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_") } diff --git a/Sources/Optimizely+Decide/OptimizelyUserContext.swift b/Sources/Optimizely+Decide/OptimizelyUserContext.swift index 4d1e1929a..e21c16d69 100644 --- a/Sources/Optimizely+Decide/OptimizelyUserContext.swift +++ b/Sources/Optimizely+Decide/OptimizelyUserContext.swift @@ -84,11 +84,10 @@ public class OptimizelyUserContext { self.atomicAttributes = AtomicProperty(property: attributes, lock: lock) self.atomicForcedDecisions = AtomicProperty(property: nil, lock: lock) self.atomicQualifiedSegments = AtomicProperty(property: nil, lock: lock) - let _vuid = optimizely.vuid if identify { // async call so event building overhead is not blocking context creation lock.async { - self.optimizely?.identifyUserToOdp(userId: userId, vuid: _vuid) + self.optimizely?.identifyUserToOdp(userId: userId) } } } diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index d133dd2c1..af07dc82c 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -92,9 +92,10 @@ 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, @@ -117,28 +118,9 @@ open class OptimizelyClient: NSObject { self.decisionService = HandlerRegistryService.shared.injectDecisionService(sdkKey: self.sdkKey) self.notificationCenter = HandlerRegistryService.shared.injectNotificationCenter(sdkKey: self.sdkKey) - self.vuidManager = OdpVuidManager(enabled: sdkSettings.enableVuid) - if self.vuidManager.enabled { - self.odpManager.vuid = vuidManager.vuid - // Cushes due to odpManager didn't initialize yet. - // self.odpManager.eventManager.registerVUID(vuid: vuidManager.vuid) - } - - // FIXME: Need a better solution - DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { [weak self] in - self?.registerVUID() - } - - logger.d("SDK Version: \(version)") } - private func registerVUID() { - if self.vuidManager.enabled { - self.odpManager.eventManager.registerVUID(vuid: self.vuidManager.vuid) - } - } - /// Start Optimizely SDK (Asynchronous) /// /// If an updated datafile is available in the server, it's downloaded and the SDK is configured with @@ -1000,8 +982,8 @@ extension OptimizelyClient { return self.vuidManager.enabled } - func identifyUserToOdp(userId: String, vuid: String) { - odpManager.identifyUser(userId: userId, vuid: self.vuid) + func identifyUserToOdp(userId: String) { + odpManager.identifyUser(userId: userId) } func fetchQualifiedSegments(userId: String, diff --git a/Tests/OptimizelyTests-Common/OdpEventManagerTests.swift b/Tests/OptimizelyTests-Common/OdpEventManagerTests.swift index c6d16a873..d06562b8a 100644 --- a/Tests/OptimizelyTests-Common/OdpEventManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpEventManagerTests.swift @@ -89,7 +89,7 @@ class OdpEventManagerTests: XCTestCase { } func testRegisterVUID_noApiKey() { - manager.registerVUID(vuid: "v1") + manager.sendInitializedEvent(vuid: "v1") XCTAssertEqual(1, manager.eventQueue.count) @@ -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: [:]) @@ -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: [:]) @@ -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: [:]) diff --git a/Tests/OptimizelyTests-Common/OdpManagerTests.swift b/Tests/OptimizelyTests-Common/OdpManagerTests.swift index dea89a1bc..e9f4405af 100644 --- a/Tests/OptimizelyTests-Common/OdpManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpManagerTests.swift @@ -73,8 +73,7 @@ class OdpManagerTests: XCTestCase { XCTAssertNil(manager.odpConfig) // these calls should be dropped gracefully with nil - let vuid = "vuid_123" - manager.identifyUser(userId: "user1", vuid: vuid) + manager.identifyUser(userId: "user1") try? manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:]) XCTAssertNil(manager.eventManager) @@ -129,8 +128,7 @@ class OdpManagerTests: XCTestCase { // MARK: - identifyUser func testIdentifyUser_datafileNotReady() { - let vuid = "vuid_123" - manager.identifyUser(userId: "user-1", vuid: vuid) + manager.identifyUser(userId: "user-1") XCTAssertEqual(eventManager.receivedIdentifyUserId, "user-1") } @@ -139,7 +137,7 @@ class OdpManagerTests: XCTestCase { let vuid = "vuid_123" manager.vuid = vuid manager.updateOdpConfig(apiKey: "key-1", apiHost: "host-1", segmentsToCheck: []) - manager.identifyUser(userId: "user-1", vuid: "vuid_123") + manager.identifyUser(userId: "user-1") XCTAssert(OdpVuidManager.isVuid(eventManager.receivedIdentifyVuid)) XCTAssertEqual(eventManager.receivedIdentifyUserId, "user-1") @@ -149,7 +147,7 @@ class OdpManagerTests: XCTestCase { manager.updateOdpConfig(apiKey: "key-1", apiHost: "host-1", segmentsToCheck: []) let vuidAsUserId = OdpVuidManager.newVuid - manager.identifyUser(userId: vuidAsUserId, vuid: "") + manager.identifyUser(userId: vuidAsUserId) XCTAssertEqual(eventManager.receivedIdentifyVuid, vuidAsUserId) XCTAssertNil(eventManager.receivedIdentifyUserId) @@ -157,14 +155,14 @@ class OdpManagerTests: XCTestCase { func testIdentifyUser_odpNotIntegrated() { manager.updateOdpConfig(apiKey: nil, apiHost: nil, segmentsToCheck: []) - manager.identifyUser(userId: "user-1", vuid: "") + manager.identifyUser(userId: "user-1") XCTAssertNil(eventManager.receivedIdentifyUserId, "identifyUser event requeut should be discarded if ODP not integrated.") } func testIdentifyUser_odpDisabled() { manager.enabled = false - manager.identifyUser(userId: "user-1", vuid: "") + manager.identifyUser(userId: "user-1") XCTAssertNil(eventManager.receivedIdentifyUserId, "identifyUser event requeut should be discarded if ODP disabled.") } @@ -387,7 +385,7 @@ class OdpManagerTests: XCTestCase { var resetCalled = false - override func registerVUID(vuid: String) { + override func sendInitializedEvent(vuid: String) { self.receivedRegisterVuid = vuid } diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift index d7fda53c1..bae1f9372 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP.swift @@ -327,7 +327,7 @@ class MockOdpManager: OdpManager { super.fetchQualifiedSegments(userId: userId, options: options, completionHandler: completionHandler) } - override func identifyUser(userId: String, vuid: String) { + override func identifyUser(userId: String) { self.userId = userId self.identifyCalled = true } diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift index ad43d8c07..4b6e96773 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_Aync_Await.swift @@ -140,7 +140,7 @@ extension OptimizelyUserContextTests_ODP_Aync_Await { super.fetchQualifiedSegments(userId: userId, options: options, completionHandler: completionHandler) } - override func identifyUser(userId: String, vuid: String) { + override func identifyUser(userId: String) { self.userId = userId self.identifyCalled = true } From 97e6447a35a9884cd24b63895b09ef9c26c17121 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Wed, 30 Oct 2024 18:48:25 +0600 Subject: [PATCH 15/23] WIP: VuidManager singleton added --- Sources/ODP/OdpManager.swift | 4 +- Sources/ODP/OdpVuidManager.swift | 11 +-- Sources/ODP/OptimizelySdkSettings.swift | 5 +- .../OptimizelyClient+Decide.swift | 2 +- Sources/Optimizely/OptimizelyClient.swift | 5 +- .../OptimizelyClientTests_Decide.swift | 2 +- .../OptimizelyClientTests_ODP.swift | 6 +- .../OdpVuidManagerTests.swift | 68 ++++++++++--------- .../OptimizelyUserContextTests_ODP_2.swift | 2 +- 9 files changed, 57 insertions(+), 48 deletions(-) diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index fc2d0bded..2dede0b44 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -121,8 +121,8 @@ public class OdpManager { let typeUpdated = (type ?? "").isEmpty ? Constants.ODP.eventType : type! var identifiersUpdated = identifiers - let _vuid = vuid ?? "" - if identifiers[Constants.ODP.keyForVuid] == nil, OdpVuidManager.isVuid(_vuid) { + + if identifiers[Constants.ODP.keyForVuid] == nil, let _vuid = vuid { identifiersUpdated[Constants.ODP.keyForVuid] = _vuid } diff --git a/Sources/ODP/OdpVuidManager.swift b/Sources/ODP/OdpVuidManager.swift index c91add12e..1ff6398be 100644 --- a/Sources/ODP/OdpVuidManager.swift +++ b/Sources/ODP/OdpVuidManager.swift @@ -18,10 +18,13 @@ import Foundation class OdpVuidManager { private var _vuid: String = "" - private(set) var enabled: Bool + private(set) var enabled: Bool = false let logger = OPTLoggerFactory.getLogger() - init(enabled: Bool) { + // a single vuid should be shared for all SDK instances + static let shared = OdpVuidManager() + + func intiazialize(enabled: Bool) { self.enabled = enabled if enabled { self._vuid = load() @@ -48,12 +51,12 @@ class OdpVuidManager { // MARK: - VUID Store extension OdpVuidManager { - var vuid: String { + var vuid: String? { if enabled { return _vuid } else { logger.w("VUID is not enabled.") - return "" + return nil } } diff --git a/Sources/ODP/OptimizelySdkSettings.swift b/Sources/ODP/OptimizelySdkSettings.swift index 0907c22c3..fc3d47139 100644 --- a/Sources/ODP/OptimizelySdkSettings.swift +++ b/Sources/ODP/OptimizelySdkSettings.swift @@ -38,6 +38,7 @@ public struct OptimizelySdkSettings { /// - timeoutForSegmentFetchInSecs: The timeout in seconds of odp segment fetch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - timeoutForOdpEventInSecs: The timeout in seconds of odp event dispatch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - disableOdp: Set this flag to true (default = false) to disable ODP features + /// - enableVuid: Set this flag to true (default = false) to enable vuid. /// - sdkName: Set this flag to override sdkName included in events /// - sdkVersion: Set this flag to override sdkVersion included in events public init(segmentsCacheSize: Int = 100, @@ -45,7 +46,7 @@ public struct OptimizelySdkSettings { timeoutForSegmentFetchInSecs: Int = 10, timeoutForOdpEventInSecs: Int = 10, disableOdp: Bool = false, - enabledVuid: Bool = false, + enableVuid: Bool = false, sdkName: String? = nil, sdkVersion: String? = nil) { self.segmentsCacheSize = segmentsCacheSize @@ -53,7 +54,7 @@ public struct OptimizelySdkSettings { self.timeoutForSegmentFetchInSecs = timeoutForSegmentFetchInSecs self.timeoutForOdpEventInSecs = timeoutForOdpEventInSecs self.disableOdp = disableOdp - self.enableVuid = enabledVuid + self.enableVuid = enableVuid if let _sdkName = sdkName, _sdkName != "" { Utils.swiftSdkClientName = _sdkName } diff --git a/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift b/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift index f0b6344d0..787608961 100644 --- a/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift +++ b/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift @@ -36,7 +36,7 @@ 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? { - guard enableVuid, OdpVuidManager.isVuid(vuid) else { + guard enableVuid, let vuid = self.vuid else { logger.e("Vuid is not enabled or invalid VUID. User context not created.") return nil } diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index af07dc82c..b1ac384ca 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -92,7 +92,8 @@ open class OptimizelyClient: NSObject { self.defaultDecideOptions = defaultDecideOptions ?? [] super.init() - self.vuidManager = OdpVuidManager(enabled: sdkSettings.enableVuid) + self.vuidManager = OdpVuidManager.shared + self.vuidManager.intiazialize(enabled: self.sdkSettings.enableVuid) self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey, disable: sdkSettings.disableOdp, vuid: vuidManager.vuid, @@ -974,7 +975,7 @@ extension OptimizelyClient { } /// the device vuid (read only) - public var vuid: String { + public var vuid: String? { return self.vuidManager.vuid } diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift index 72c500739..a5628f1df 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift @@ -24,7 +24,7 @@ class OptimizelyClientTests_Decide: XCTestCase { super.setUp() let datafile = OTUtils.loadJSONDatafile("api_datafile")! - let settings = OptimizelySdkSettings(enabledVuid: true) + let settings = OptimizelySdkSettings(enableVuid: true) optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: settings) try! optimizely.start(datafile: datafile) diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift index ce690ec6b..22600e796 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift @@ -188,16 +188,16 @@ class OptimizelyClientTests_ODP: XCTestCase { // MARK: - vuid func testVuidEnabled() { - let settings = OptimizelySdkSettings(enabledVuid: true) + let settings = OptimizelySdkSettings(enableVuid: true) optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: settings) XCTAssertTrue(optimizely.enableVuid) - XCTAssert(optimizely.vuid.starts(with: "vuid_")) + XCTAssert(optimizely.vuid!.starts(with: "vuid_")) } func testVuidDiabled() { // Default client vuid diabled XCTAssertFalse(optimizely.enableVuid) - XCTAssert(optimizely.vuid.isEmpty) + XCTAssertNil(optimizely.vuid) } // MARK: - OdpConfig Update diff --git a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift b/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift index b15e9eff1..5c5eae3f4 100644 --- a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift @@ -17,7 +17,11 @@ import XCTest class OdpVuidManagerTests: XCTestCase { - var manager = OdpVuidManager(enabled: true) + var manager = OdpVuidManager.shared + + override func setUp() { + manager.intiazialize(enabled: true) + } func testNewVuid() { let vuid = OdpVuidManager.newVuid @@ -32,37 +36,37 @@ class OdpVuidManagerTests: XCTestCase { XCTAssertFalse(OdpVuidManager.isVuid("123")) } - func testAutoSaveAndLoad() { - UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - - manager = OdpVuidManager(enabled: true) - let vuid1 = manager.vuid - - manager = OdpVuidManager(enabled: true) - let vuid2 = manager.vuid - - XCTAssertTrue(vuid1 == vuid2) - XCTAssert(OdpVuidManager.isVuid(vuid1)) - XCTAssert(OdpVuidManager.isVuid(vuid2)) - - UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - - manager = OdpVuidManager(enabled: true) - let vuid3 = manager.vuid - - XCTAssertTrue(vuid1 != vuid3) - } +// func testAutoSaveAndLoad() { +// UserDefaults.standard.removeObject(forKey: "optimizely-vuid") +// +// manager.intiazialize(enabled: true) +// let vuid1 = manager.vuid +// +// manager = OdpVuidManager(enabled: true) +// let vuid2 = manager.vuid +// +// XCTAssertTrue(vuid1 == vuid2) +// XCTAssert(OdpVuidManager.isVuid(vuid1)) +// XCTAssert(OdpVuidManager.isVuid(vuid2)) +// +// UserDefaults.standard.removeObject(forKey: "optimizely-vuid") +// +// manager = OdpVuidManager(enabled: true) +// let vuid3 = manager.vuid +// +// XCTAssertTrue(vuid1 != vuid3) +// } - func testRemoveOldVuid() { - manager = OdpVuidManager(enabled: true) - let cahcedVuid1 = UserDefaults.standard.string(forKey: "optimizely-vuid") - XCTAssertNotNil(cahcedVuid1) - XCTAssertTrue(cahcedVuid1 == manager.vuid) - - _ = OdpVuidManager(enabled: false) - let cahcedVuid2 = UserDefaults.standard.string(forKey: "optimizely-vuid") - XCTAssertNil(cahcedVuid2) - - } +// func testRemoveOldVuid() { +// manager = OdpVuidManager(enabled: true) +// let cahcedVuid1 = UserDefaults.standard.string(forKey: "optimizely-vuid") +// XCTAssertNotNil(cahcedVuid1) +// XCTAssertTrue(cahcedVuid1 == manager.vuid) +// +// _ = OdpVuidManager(enabled: false) +// let cahcedVuid2 = UserDefaults.standard.string(forKey: "optimizely-vuid") +// XCTAssertNil(cahcedVuid2) +// +// } } diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift index c8b1fa32f..e6edf4a9d 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift @@ -27,7 +27,7 @@ class OptimizelyUserContextTests_ODP_2: XCTestCase { // odp disabled to avoid initial noise let optimizely = OptimizelyClient(sdkKey: sdkKey, - settings: OptimizelySdkSettings(disableOdp: true, enabledVuid: true)) + settings: OptimizelySdkSettings(disableOdp: true, enableVuid: true)) // override with a custom enabled odpManager. // - client_inializatied event will be sent automatically From b54266c5d304f04699adc99c3a5c707243c59c06 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Wed, 30 Oct 2024 18:55:48 +0600 Subject: [PATCH 16/23] WIP: OdpVuidManager renamed as VuidManager --- OptimizelySwiftSDK.xcodeproj/project.pbxproj | 80 +++++++++---------- Sources/ODP/OdpManager.swift | 6 +- Sources/Optimizely/OptimizelyClient.swift | 4 +- .../VuidManager.swift} | 8 +- .../OdpVuidManagerTests.swift | 72 ----------------- .../VuidManagerTests.swift | 71 ++++++++++++++++ 6 files changed, 120 insertions(+), 121 deletions(-) rename Sources/{ODP/OdpVuidManager.swift => Optimizely/VuidManager.swift} (94%) delete mode 100644 Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift create mode 100644 Tests/OptimizelyTests-Common/VuidManagerTests.swift diff --git a/OptimizelySwiftSDK.xcodeproj/project.pbxproj b/OptimizelySwiftSDK.xcodeproj/project.pbxproj index d5c85cdd1..dfa44e22c 100644 --- a/OptimizelySwiftSDK.xcodeproj/project.pbxproj +++ b/OptimizelySwiftSDK.xcodeproj/project.pbxproj @@ -1884,8 +1884,8 @@ 84861812286D0B8900B7F41B /* OdpSegmentManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180D286D0B8900B7F41B /* OdpSegmentManagerTests.swift */; }; 84861813286D0B8900B7F41B /* OdpManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180E286D0B8900B7F41B /* OdpManagerTests.swift */; }; 84861814286D0B8900B7F41B /* OdpManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180E286D0B8900B7F41B /* OdpManagerTests.swift */; }; - 84861815286D0B8900B7F41B /* OdpVuidManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180F286D0B8900B7F41B /* OdpVuidManagerTests.swift */; }; - 84861816286D0B8900B7F41B /* OdpVuidManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180F286D0B8900B7F41B /* OdpVuidManagerTests.swift */; }; + 84861815286D0B8900B7F41B /* VuidManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180F286D0B8900B7F41B /* VuidManagerTests.swift */; }; + 84861816286D0B8900B7F41B /* VuidManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180F286D0B8900B7F41B /* VuidManagerTests.swift */; }; 84861817286D0B8900B7F41B /* OdpEventManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84861810286D0B8900B7F41B /* OdpEventManagerTests.swift */; }; 84861818286D0B8900B7F41B /* OdpEventManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84861810286D0B8900B7F41B /* OdpEventManagerTests.swift */; }; 8486181B286D188B00B7F41B /* OdpSegmentApiManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84861819286D188B00B7F41B /* OdpSegmentApiManagerTests.swift */; }; @@ -1910,22 +1910,22 @@ 84B4D75D27E2A7550078CDA4 /* OptimizelySegmentOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B4D74F27E2A7550078CDA4 /* OptimizelySegmentOption.swift */; }; 84B4D75E27E2A7550078CDA4 /* OptimizelySegmentOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B4D74F27E2A7550078CDA4 /* OptimizelySegmentOption.swift */; }; 84B4D75F27E2A7550078CDA4 /* OptimizelySegmentOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B4D74F27E2A7550078CDA4 /* OptimizelySegmentOption.swift */; }; - 84E2E9422852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9432852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9442852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9452852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9462852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9472852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9482852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9492852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94A2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94B2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94C2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94D2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94E2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94F2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9502852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9512852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; + 84E2E9422852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9432852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9442852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9452852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9462852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9472852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9482852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9492852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94A2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94B2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94C2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94D2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94E2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94F2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9502852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9512852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; 84E2E96128540B5E001114AB /* OptimizelySdkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E96028540B5E001114AB /* OptimizelySdkSettings.swift */; }; 84E2E96228540B5E001114AB /* OptimizelySdkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E96028540B5E001114AB /* OptimizelySdkSettings.swift */; }; 84E2E96328540B5E001114AB /* OptimizelySdkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E96028540B5E001114AB /* OptimizelySdkSettings.swift */; }; @@ -2408,13 +2408,13 @@ 848617FA286CF33700B7F41B /* OdpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpEvent.swift; sourceTree = ""; }; 8486180D286D0B8900B7F41B /* OdpSegmentManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpSegmentManagerTests.swift; sourceTree = ""; }; 8486180E286D0B8900B7F41B /* OdpManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpManagerTests.swift; sourceTree = ""; }; - 8486180F286D0B8900B7F41B /* OdpVuidManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpVuidManagerTests.swift; sourceTree = ""; }; + 8486180F286D0B8900B7F41B /* VuidManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VuidManagerTests.swift; sourceTree = ""; }; 84861810286D0B8900B7F41B /* OdpEventManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpEventManagerTests.swift; sourceTree = ""; }; 84861819286D188B00B7F41B /* OdpSegmentApiManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpSegmentApiManagerTests.swift; sourceTree = ""; }; 8486181A286D188B00B7F41B /* OdpEventApiManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpEventApiManagerTests.swift; sourceTree = ""; }; 84958C5D280F22FE008655C7 /* OptimizelyUserContextTests_Performance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptimizelyUserContextTests_Performance.swift; sourceTree = ""; }; 84B4D74F27E2A7550078CDA4 /* OptimizelySegmentOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptimizelySegmentOption.swift; sourceTree = ""; }; - 84E2E9412852A378001114AB /* OdpVuidManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OdpVuidManager.swift; sourceTree = ""; }; + 84E2E9412852A378001114AB /* VuidManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VuidManager.swift; sourceTree = ""; }; 84E2E96028540B5E001114AB /* OptimizelySdkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = OptimizelySdkSettings.swift; path = ../ODP/OptimizelySdkSettings.swift; sourceTree = ""; }; 84E2E9712855875E001114AB /* OdpEventManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OdpEventManager.swift; sourceTree = ""; }; 84E7ABBA27D2A1F100447CAE /* ThreadSafeLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeLogger.swift; sourceTree = ""; }; @@ -2635,7 +2635,6 @@ isa = PBXGroup; children = ( 6E6522DD278E4F3800954EA1 /* OdpManager.swift */, - 84E2E9412852A378001114AB /* OdpVuidManager.swift */, 848617C72863DC2700B7F41B /* OdpSegmentManager.swift */, 84E2E9712855875E001114AB /* OdpEventManager.swift */, 848617D82863E21200B7F41B /* OdpSegmentApiManager.swift */, @@ -2710,6 +2709,7 @@ 6EA2CC232345618E001E7531 /* OptimizelyConfig.swift */, 6ECB60C9234D5D9C00016D41 /* OptimizelyConfig+ObjC.swift */, C78CAF572445AD8D009FE876 /* OptimizelyJSON.swift */, + 84E2E9412852A378001114AB /* VuidManager.swift */, C78CAFA324486E0A009FE876 /* OptimizelyJSON+ObjC.swift */, ); path = Optimizely; @@ -2946,7 +2946,7 @@ 84861810286D0B8900B7F41B /* OdpEventManagerTests.swift */, 8486180E286D0B8900B7F41B /* OdpManagerTests.swift */, 8486180D286D0B8900B7F41B /* OdpSegmentManagerTests.swift */, - 8486180F286D0B8900B7F41B /* OdpVuidManagerTests.swift */, + 8486180F286D0B8900B7F41B /* VuidManagerTests.swift */, 84861819286D188B00B7F41B /* OdpSegmentApiManagerTests.swift */, 8486181A286D188B00B7F41B /* OdpEventApiManagerTests.swift */, 6E27EC9A266EF11000B4A6D4 /* OptimizelyDecisionTests.swift */, @@ -4178,7 +4178,7 @@ 6E14CDA92423F9C300010234 /* Utils.swift in Sources */, 6EF8DE1F24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 6E14CD882423F9A100010234 /* AttributeValue.swift in Sources */, - 84E2E9492852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9492852A378001114AB /* VuidManager.swift in Sources */, 6E14CD822423F9A100010234 /* DataStoreFile.swift in Sources */, 6E14CDA42423F9C300010234 /* Notifications.swift in Sources */, 6E20050B26B4D28500278087 /* MockLogger.swift in Sources */, @@ -4251,7 +4251,7 @@ 6E424D0126324B620081004A /* SemanticVersion.swift in Sources */, 6E424D0226324B620081004A /* Audience.swift in Sources */, 6E424D0326324B620081004A /* AttributeValue.swift in Sources */, - 84E2E9482852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9482852A378001114AB /* VuidManager.swift in Sources */, 6E424D0426324B620081004A /* ConditionLeaf.swift in Sources */, 6E424D0526324B620081004A /* ConditionHolder.swift in Sources */, 6E424D5226324C4D0081004A /* OptimizelyClient+Decide.swift in Sources */, @@ -4359,7 +4359,7 @@ 6E75190122C520D500B2B157 /* Attribute.swift in Sources */, 848617DB2863E21200B7F41B /* OdpSegmentApiManager.swift in Sources */, 6E7516B322C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, - 84E2E9432852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9432852A378001114AB /* VuidManager.swift in Sources */, 6E75183522C520D400B2B157 /* EventForDispatch.swift in Sources */, 6E7517D522C520D400B2B157 /* DefaultNotificationCenter.swift in Sources */, 6E75187122C520D400B2B157 /* Variation.swift in Sources */, @@ -4482,7 +4482,7 @@ 6E7518F022C520D500B2B157 /* ConditionHolder.swift in Sources */, 6EF8DE2424BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 6E75183022C520D400B2B157 /* BatchEvent.swift in Sources */, - 84E2E94E2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94E2852A378001114AB /* VuidManager.swift in Sources */, 6E75192022C520D500B2B157 /* OPTNotificationCenter.swift in Sources */, 6E9B117922C5487A00C22D81 /* tvOSOnlyTests.swift in Sources */, 6E20051026B4D28500278087 /* MockLogger.swift in Sources */, @@ -4555,7 +4555,7 @@ 6ECB60D7234E601A00016D41 /* OptimizelyClientTests_OptimizelyConfig_Objc.m in Sources */, 6E75195822C520D500B2B157 /* OPTBucketer.swift in Sources */, 6E424C03263228FD0081004A /* AtomicDictionary.swift in Sources */, - 84E2E94A2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94A2852A378001114AB /* VuidManager.swift in Sources */, 6E994B3A25A3E6EA00999262 /* DecisionResponse.swift in Sources */, 6E75170A22C520D400B2B157 /* OptimizelyClient.swift in Sources */, 6E9B11AC22C5489300C22D81 /* OTUtils.swift in Sources */, @@ -4678,7 +4678,7 @@ 6E7518BF22C520D400B2B157 /* Variable.swift in Sources */, 6E75181722C520D400B2B157 /* DataStoreQueueStackImpl.swift in Sources */, 6E75185322C520D400B2B157 /* ProjectConfig.swift in Sources */, - 84E2E94D2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94D2852A378001114AB /* VuidManager.swift in Sources */, 6E75173D22C520D400B2B157 /* MurmurHash3.swift in Sources */, 6E7516E922C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 6E7518A722C520D400B2B157 /* FeatureFlag.swift in Sources */, @@ -4785,7 +4785,7 @@ 6E9B11B522C5489600C22D81 /* MockUrlSession.swift in Sources */, 6EF8DE1724BD1BB2008B9488 /* OptimizelyDecision.swift in Sources */, 8428D3D12807337400D0FB0C /* LruCacheTests.swift in Sources */, - 84861816286D0B8900B7F41B /* OdpVuidManagerTests.swift in Sources */, + 84861816286D0B8900B7F41B /* VuidManagerTests.swift in Sources */, 6E7517C522C520D400B2B157 /* DefaultDatafileHandler.swift in Sources */, 6E75190922C520D500B2B157 /* Attribute.swift in Sources */, 6E75177B22C520D400B2B157 /* SDKVersion.swift in Sources */, @@ -4831,7 +4831,7 @@ 6E9B117822C5487100C22D81 /* DataStoreTests.swift in Sources */, 6E75171B22C520D400B2B157 /* OptimizelyClient+ObjC.swift in Sources */, 6E75195122C520D500B2B157 /* OPTDatafileHandler.swift in Sources */, - 84E2E94F2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94F2852A378001114AB /* VuidManager.swift in Sources */, 6E75176322C520D400B2B157 /* AtomicProperty.swift in Sources */, 6E9B117722C5487100C22D81 /* BatchEventBuilderTests_EventTags.swift in Sources */, 6E7517DD22C520D400B2B157 /* DefaultNotificationCenter.swift in Sources */, @@ -4911,7 +4911,7 @@ 84861809286CF33700B7F41B /* OdpEvent.swift in Sources */, 6E75171022C520D400B2B157 /* OptimizelyClient.swift in Sources */, 6E7516C822C520D400B2B157 /* DefaultEventDispatcher.swift in Sources */, - 84E2E9502852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9502852A378001114AB /* VuidManager.swift in Sources */, 6E75194622C520D500B2B157 /* OPTDecisionService.swift in Sources */, 6E75185622C520D400B2B157 /* ProjectConfig.swift in Sources */, 6E20051226B4D28600278087 /* MockLogger.swift in Sources */, @@ -5069,7 +5069,7 @@ 6E75171522C520D400B2B157 /* OptimizelyClient+ObjC.swift in Sources */, 6E6522E3278E4F3800954EA1 /* OdpManager.swift in Sources */, 6EA2CC272345618E001E7531 /* OptimizelyConfig.swift in Sources */, - 84861815286D0B8900B7F41B /* OdpVuidManagerTests.swift in Sources */, + 84861815286D0B8900B7F41B /* VuidManagerTests.swift in Sources */, C78CAFA724486E0A009FE876 /* OptimizelyJSON+ObjC.swift in Sources */, 6E75185B22C520D400B2B157 /* FeatureVariable.swift in Sources */, 6E7516B522C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, @@ -5077,7 +5077,7 @@ 6E7517D722C520D400B2B157 /* DefaultNotificationCenter.swift in Sources */, 6E75181F22C520D400B2B157 /* BatchEventBuilder.swift in Sources */, 84F6BADD27FD011B004BE62A /* OptimizelyUserContextTests_ODP_Decide.swift in Sources */, - 84E2E9472852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9472852A378001114AB /* VuidManager.swift in Sources */, 6E623F06253F9045000617D0 /* DecisionInfo.swift in Sources */, 6E424BE2263228E90081004A /* AtomicArray.swift in Sources */, 6E9B115822C5486E00C22D81 /* EventDispatcherTests.swift in Sources */, @@ -5178,7 +5178,7 @@ 84861804286CF33700B7F41B /* OdpEvent.swift in Sources */, 6E75170B22C520D400B2B157 /* OptimizelyClient.swift in Sources */, 6E7516C322C520D400B2B157 /* DefaultEventDispatcher.swift in Sources */, - 84E2E94B2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94B2852A378001114AB /* VuidManager.swift in Sources */, 6E75194122C520D500B2B157 /* OPTDecisionService.swift in Sources */, 6E75185122C520D400B2B157 /* ProjectConfig.swift in Sources */, 6E20050D26B4D28500278087 /* MockLogger.swift in Sources */, @@ -5309,7 +5309,7 @@ 6E7517E622C520D400B2B157 /* DefaultDecisionService.swift in Sources */, 6E75171822C520D400B2B157 /* OptimizelyClient+ObjC.swift in Sources */, 6E75174822C520D400B2B157 /* HandlerRegistryService.swift in Sources */, - 84E2E94C2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94C2852A378001114AB /* VuidManager.swift in Sources */, 6E7518FA22C520D500B2B157 /* UserAttribute.swift in Sources */, 6E7516E822C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 6E75191222C520D500B2B157 /* BackgroundingCallbacks.swift in Sources */, @@ -5410,7 +5410,7 @@ 6E7517EB22C520D400B2B157 /* DefaultDecisionService.swift in Sources */, 6E75171D22C520D400B2B157 /* OptimizelyClient+ObjC.swift in Sources */, 6E75174D22C520D400B2B157 /* HandlerRegistryService.swift in Sources */, - 84E2E9512852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9512852A378001114AB /* VuidManager.swift in Sources */, 6E7518FF22C520D500B2B157 /* UserAttribute.swift in Sources */, 6E7516ED22C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 6E75191722C520D500B2B157 /* BackgroundingCallbacks.swift in Sources */, @@ -5509,7 +5509,7 @@ 6E75195422C520D500B2B157 /* OPTBucketer.swift in Sources */, 848617DA2863E21200B7F41B /* OdpSegmentApiManager.swift in Sources */, 6E75171E22C520D400B2B157 /* OptimizelyResult.swift in Sources */, - 84E2E9422852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9422852A378001114AB /* VuidManager.swift in Sources */, 6E75172A22C520D400B2B157 /* Constants.swift in Sources */, 6E7516A622C520D400B2B157 /* DefaultLogger.swift in Sources */, 6E75189422C520D400B2B157 /* Experiment.swift in Sources */, @@ -5632,7 +5632,7 @@ 6E7518DE22C520D400B2B157 /* ConditionLeaf.swift in Sources */, 6EF8DE1D24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 6E7518EA22C520D400B2B157 /* ConditionHolder.swift in Sources */, - 84E2E9462852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9462852A378001114AB /* VuidManager.swift in Sources */, 6E75182A22C520D400B2B157 /* BatchEvent.swift in Sources */, 6E75191A22C520D500B2B157 /* OPTNotificationCenter.swift in Sources */, 6E20050826B4D28500278087 /* MockLogger.swift in Sources */, @@ -5717,7 +5717,7 @@ 75C71A2125E454460084187E /* EventForDispatch.swift in Sources */, 75C71A2225E454460084187E /* SemanticVersion.swift in Sources */, 75C71A2325E454460084187E /* Audience.swift in Sources */, - 84E2E9452852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9452852A378001114AB /* VuidManager.swift in Sources */, 6E6522E1278E4F3800954EA1 /* OdpManager.swift in Sources */, 75C71A2425E454460084187E /* AttributeValue.swift in Sources */, 75C71A2525E454460084187E /* ConditionLeaf.swift in Sources */, @@ -5798,7 +5798,7 @@ BD6485502491474500F30986 /* OPTBucketer.swift in Sources */, 848617DC2863E21200B7F41B /* OdpSegmentApiManager.swift in Sources */, BD6485512491474500F30986 /* OptimizelyResult.swift in Sources */, - 84E2E9442852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9442852A378001114AB /* VuidManager.swift in Sources */, BD6485522491474500F30986 /* Constants.swift in Sources */, BD6485532491474500F30986 /* DefaultLogger.swift in Sources */, BD6485542491474500F30986 /* Experiment.swift in Sources */, diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index 2dede0b44..48a10f862 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -61,7 +61,7 @@ public class OdpManager { self.odpConfig = OdpConfig() self.segmentManager.odpConfig = odpConfig self.eventManager.odpConfig = odpConfig - if let vuid = vuid, OdpVuidManager.isVuid(vuid) { + if let vuid = vuid, VuidManager.isVuid(vuid) { self.eventManager.sendInitializedEvent(vuid: vuid) } } @@ -74,7 +74,7 @@ public class OdpManager { return } - let userKey = OdpVuidManager.isVuid(userId) ? Constants.ODP.keyForVuid : Constants.ODP.keyForUserId + let userKey = VuidManager.isVuid(userId) ? Constants.ODP.keyForVuid : Constants.ODP.keyForUserId let userValue = userId segmentManager.fetchQualifiedSegments(userKey: userKey, @@ -94,7 +94,7 @@ public class OdpManager { return } - if OdpVuidManager.isVuid(userId) { + if VuidManager.isVuid(userId) { // overwrite if userId is vuid (when userContext is created with vuid) eventManager.identifyUser(vuid: userId, userId: nil) } else if let _vuid = vuid { diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index b1ac384ca..41703af6c 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -60,7 +60,7 @@ open class OptimizelyClient: NSObject { var decisionService: OPTDecisionService! public var notificationCenter: OPTNotificationCenter? public var odpManager: OdpManager! - private var vuidManager: OdpVuidManager! + private var vuidManager: VuidManager! let sdkSettings: OptimizelySdkSettings // MARK: - Public interfaces @@ -92,7 +92,7 @@ open class OptimizelyClient: NSObject { self.defaultDecideOptions = defaultDecideOptions ?? [] super.init() - self.vuidManager = OdpVuidManager.shared + self.vuidManager = VuidManager.shared self.vuidManager.intiazialize(enabled: self.sdkSettings.enableVuid) self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey, disable: sdkSettings.disableOdp, diff --git a/Sources/ODP/OdpVuidManager.swift b/Sources/Optimizely/VuidManager.swift similarity index 94% rename from Sources/ODP/OdpVuidManager.swift rename to Sources/Optimizely/VuidManager.swift index 1ff6398be..5f8afa614 100644 --- a/Sources/ODP/OdpVuidManager.swift +++ b/Sources/Optimizely/VuidManager.swift @@ -16,13 +16,13 @@ import Foundation -class OdpVuidManager { +class VuidManager { private var _vuid: String = "" private(set) var enabled: Bool = false let logger = OPTLoggerFactory.getLogger() // a single vuid should be shared for all SDK instances - static let shared = OdpVuidManager() + static let shared = VuidManager() func intiazialize(enabled: Bool) { self.enabled = enabled @@ -50,7 +50,7 @@ class OdpVuidManager { // MARK: - VUID Store -extension OdpVuidManager { +extension VuidManager { var vuid: String? { if enabled { return _vuid @@ -69,7 +69,7 @@ extension OdpVuidManager { return oldVuid } - let vuid = OdpVuidManager.newVuid + let vuid = VuidManager.newVuid save(vuid: vuid) return vuid } diff --git a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift b/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift deleted file mode 100644 index 5c5eae3f4..000000000 --- a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright 2022, Optimizely, Inc. 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. -// - -import XCTest - -class OdpVuidManagerTests: XCTestCase { - var manager = OdpVuidManager.shared - - override func setUp() { - manager.intiazialize(enabled: true) - } - - func testNewVuid() { - let vuid = OdpVuidManager.newVuid - - XCTAssertTrue(vuid.starts(with: "vuid_")) - XCTAssertEqual(vuid.count, 32) - } - - func testIsVuid() { - XCTAssertTrue(OdpVuidManager.isVuid("vuid_123")) - XCTAssertFalse(OdpVuidManager.isVuid("vuid-123")) - XCTAssertFalse(OdpVuidManager.isVuid("123")) - } - -// func testAutoSaveAndLoad() { -// UserDefaults.standard.removeObject(forKey: "optimizely-vuid") -// -// manager.intiazialize(enabled: true) -// let vuid1 = manager.vuid -// -// manager = OdpVuidManager(enabled: true) -// let vuid2 = manager.vuid -// -// XCTAssertTrue(vuid1 == vuid2) -// XCTAssert(OdpVuidManager.isVuid(vuid1)) -// XCTAssert(OdpVuidManager.isVuid(vuid2)) -// -// UserDefaults.standard.removeObject(forKey: "optimizely-vuid") -// -// manager = OdpVuidManager(enabled: true) -// let vuid3 = manager.vuid -// -// XCTAssertTrue(vuid1 != vuid3) -// } - -// func testRemoveOldVuid() { -// manager = OdpVuidManager(enabled: true) -// let cahcedVuid1 = UserDefaults.standard.string(forKey: "optimizely-vuid") -// XCTAssertNotNil(cahcedVuid1) -// XCTAssertTrue(cahcedVuid1 == manager.vuid) -// -// _ = OdpVuidManager(enabled: false) -// let cahcedVuid2 = UserDefaults.standard.string(forKey: "optimizely-vuid") -// XCTAssertNil(cahcedVuid2) -// -// } - -} diff --git a/Tests/OptimizelyTests-Common/VuidManagerTests.swift b/Tests/OptimizelyTests-Common/VuidManagerTests.swift new file mode 100644 index 000000000..1725a550c --- /dev/null +++ b/Tests/OptimizelyTests-Common/VuidManagerTests.swift @@ -0,0 +1,71 @@ +// +// Copyright 2022, Optimizely, Inc. 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. +// + +import XCTest + +class VuidManagerTests: XCTestCase { + var manager = VuidManager.shared + + + func testNewVuid() { + manager.intiazialize(enabled: true) + + let vuid = VuidManager.newVuid + + XCTAssertTrue(vuid.starts(with: "vuid_")) + XCTAssertEqual(vuid.count, 32) + } + + func testIsVuid() { + manager.intiazialize(enabled: true) + XCTAssertTrue(VuidManager.isVuid("vuid_123")) + XCTAssertFalse(VuidManager.isVuid("vuid-123")) + XCTAssertFalse(VuidManager.isVuid("123")) + } + + func testAutoSaveAndLoad() { + UserDefaults.standard.removeObject(forKey: "optimizely-vuid") + + manager.intiazialize(enabled: true) + let vuid1 = manager.vuid + + let vuid2 = manager.vuid + + XCTAssertTrue(vuid1 == vuid2) + XCTAssert(VuidManager.isVuid(vuid1!)) + XCTAssert(VuidManager.isVuid(vuid2!)) + + UserDefaults.standard.removeObject(forKey: "optimizely-vuid") + + manager.intiazialize(enabled: true) + let vuid3 = manager.vuid + + XCTAssertTrue(vuid1 != vuid3) + } + + func testRemoveOldVuid() { + manager.intiazialize(enabled: true) + let cahcedVuid1 = UserDefaults.standard.string(forKey: "optimizely-vuid") + XCTAssertNotNil(cahcedVuid1) + XCTAssertTrue(cahcedVuid1 == manager.vuid) + + manager.intiazialize(enabled: false) + let cahcedVuid2 = UserDefaults.standard.string(forKey: "optimizely-vuid") + XCTAssertNil(cahcedVuid2) + + } + +} From f916638a06d4c15f20ad14c50b074ea086a751a6 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Wed, 30 Oct 2024 21:01:32 +0600 Subject: [PATCH 17/23] WIP: Make vuid optional to identifyUser method --- Sources/ODP/OdpEventManager.swift | 7 +++++-- Sources/ODP/OdpManager.swift | 4 ++-- Tests/OptimizelyTests-Common/OdpManagerTests.swift | 7 +++---- .../OptimizelyUserContextTests_ODP_2.swift | 1 + 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Sources/ODP/OdpEventManager.swift b/Sources/ODP/OdpEventManager.swift index 4fbfc8d47..9f22323d7 100644 --- a/Sources/ODP/OdpEventManager.swift +++ b/Sources/ODP/OdpEventManager.swift @@ -58,8 +58,11 @@ open class OdpEventManager { data: [:]) } - func identifyUser(vuid: String, userId: String?) { - var identifiers = [Constants.ODP.keyForVuid: vuid] + func identifyUser(vuid: String?, userId: String?) { + var identifiers = [String: String]() + if let _vuid = vuid { + identifiers[Constants.ODP.keyForVuid] = _vuid + } if let userId = userId { identifiers[Constants.ODP.keyForUserId] = userId } diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index 48a10f862..f5f49a7b7 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -97,8 +97,8 @@ public class OdpManager { if VuidManager.isVuid(userId) { // overwrite if userId is vuid (when userContext is created with vuid) eventManager.identifyUser(vuid: userId, userId: nil) - } else if let _vuid = vuid { - eventManager.identifyUser(vuid: _vuid, userId: userId) + } else { + eventManager.identifyUser(vuid: self.vuid, userId: userId) } } diff --git a/Tests/OptimizelyTests-Common/OdpManagerTests.swift b/Tests/OptimizelyTests-Common/OdpManagerTests.swift index e9f4405af..f8989a01d 100644 --- a/Tests/OptimizelyTests-Common/OdpManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpManagerTests.swift @@ -34,7 +34,6 @@ class OdpManagerTests: XCTestCase { cacheTimeoutInSecs: cacheTimeout, segmentManager: segmentManager, eventManager: eventManager) - manager.vuid = nil } override func tearDown() { @@ -139,14 +138,14 @@ class OdpManagerTests: XCTestCase { manager.updateOdpConfig(apiKey: "key-1", apiHost: "host-1", segmentsToCheck: []) manager.identifyUser(userId: "user-1") - XCTAssert(OdpVuidManager.isVuid(eventManager.receivedIdentifyVuid)) + XCTAssert(VuidManager.isVuid(eventManager.receivedIdentifyVuid)) XCTAssertEqual(eventManager.receivedIdentifyUserId, "user-1") } func testIdentifyUser_odpIntegrated_vuidAsUserId() { manager.updateOdpConfig(apiKey: "key-1", apiHost: "host-1", segmentsToCheck: []) - let vuidAsUserId = OdpVuidManager.newVuid + let vuidAsUserId = VuidManager.newVuid manager.identifyUser(userId: vuidAsUserId) XCTAssertEqual(eventManager.receivedIdentifyVuid, vuidAsUserId) @@ -389,7 +388,7 @@ class OdpManagerTests: XCTestCase { self.receivedRegisterVuid = vuid } - override func identifyUser(vuid: String, userId: String?) { + override func identifyUser(vuid: String?, userId: String?) { self.receivedIdentifyVuid = vuid self.receivedIdentifyUserId = userId } diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift index e6edf4a9d..ae8cdbfff 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift @@ -36,6 +36,7 @@ class OptimizelyUserContextTests_ODP_2: XCTestCase { let odpEventApiManager = MockOdpEventApiManager() optimizely.odpManager = OdpManager(sdkKey: sdkKey, disable: false, + vuid: "vuid_123", cacheSize: 10, cacheTimeoutInSecs: 10, eventManager: OdpEventManager(sdkKey: sdkKey, From 260673b5823e800e2bfc74da802c26ecfa6e0e14 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Wed, 30 Oct 2024 21:20:39 +0600 Subject: [PATCH 18/23] WIP: Decide reasons test cases changes reverted --- ...izelyUserContextTests_Decide_Reasons.swift | 210 +++++------------- 1 file changed, 50 insertions(+), 160 deletions(-) diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift index f35e59307..a08ba0e5c 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift @@ -24,7 +24,7 @@ class OptimizelyUserContextTests_Decide_Reasons: XCTestCase { var decisionService: DefaultDecisionService! var ups: OPTUserProfileService! var user: OptimizelyUserContext! - + override func setUp() { super.setUp() @@ -41,13 +41,13 @@ class OptimizelyUserContextTests_Decide_Reasons: XCTestCase { // MARK: - error reasons (always included) extension OptimizelyUserContextTests_Decide_Reasons { - + func testDecideReasons_sdkNotReady() { optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, userProfileService: OTUtils.createClearUserProfileService()) try? optimizely.start(datafile: OTUtils.loadJSONDatafile("unsupported_datafile")!) user = optimizely.createUserContext(userId: kUserId) - + let decision = user.decide(key: "any-key") XCTAssert(decision.hasFailed) XCTAssertEqual(decision.reasons, [OptimizelyError.sdkNotReady.reason]) @@ -59,13 +59,13 @@ extension OptimizelyUserContextTests_Decide_Reasons { XCTAssert(decision.hasFailed) XCTAssertEqual(decision.reasons, [OptimizelyError.featureKeyInvalid(key).reason]) } - + func testDecideReasons_variableValueInvalid() { let featureKey = "feature_1" let rolloutId = "3319450668" let integerVariableId = "2687470095" let integerVariableKey = "i_42" - + // inject invalid variable value var rollout = optimizely.config!.getRollout(id: rolloutId)! var rolloutVariation = rollout.experiments[0].variations[0] @@ -83,12 +83,12 @@ extension OptimizelyUserContextTests_Decide_Reasons { // MARK: - error messages (only with "includeReasons") extension OptimizelyUserContextTests_Decide_Reasons { - + func testDecideReasons_conditionNoMatchingAudience() { let featureKey = "feature_1" let audienceId = "invalid_id" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -99,7 +99,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "invalid_format" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -110,29 +110,21 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "invalid_condition" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"gt\",\"value\":\"US\",\"name\":\"age\",\"type\":\"custom_attribute\"}" user.setAttribute(key: "age", value: 25) - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) - - let expectedReason = OptimizelyError.evaluateAttributeInvalidCondition(condition).reason - - // Look for a matching reason in the decision.reasons array - let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) - - XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") - - //XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidCondition(condition).reason)) + XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidCondition(condition).reason)) } func testDecideReasons_evaluateAttributeInvalidType() { let featureKey = "feature_1" let audienceId = "13389130056" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"exact\",\"value\":\"US\",\"name\":\"country\",\"type\":\"custom_attribute\"}" let attributeKey = "country" let attributeValue = 25 @@ -140,161 +132,99 @@ extension OptimizelyUserContextTests_Decide_Reasons { var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) - decision = user.decide(key: featureKey, options: [.includeReasons]) - - let expectedReason = OptimizelyError.evaluateAttributeInvalidType(condition, attributeValue, attributeKey).reason - - // Look for a matching reason in the decision.reasons array - let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) - - XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") - -// decision = user.decide(key: featureKey, options: [.includeReasons]) -// XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidType(condition, attributeValue, attributeKey).reason)) + XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeInvalidType(condition, attributeValue, attributeKey).reason)) } func testDecideReasons_evaluateAttributeValueOutOfRange() { let featureKey = "feature_1" let audienceId = "age_18" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"gt\",\"value\":18,\"name\":\"age\",\"type\":\"custom_attribute\"}" user.setAttribute(key: "age", value: pow(2,54) as Double) // TOO-BIG value var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) - decision = user.decide(key: featureKey, options: [.includeReasons]) - - let expectedReason = OptimizelyError.evaluateAttributeValueOutOfRange(condition, "age").reason - - // Look for a matching reason in the decision.reasons array - let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) - - XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") - - //XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeValueOutOfRange(condition, "age").reason)) + XCTAssert(decision.reasons.contains(OptimizelyError.evaluateAttributeValueOutOfRange(condition, "age").reason)) } func testDecideReasons_userAttributeInvalidType() { let featureKey = "feature_1" let audienceId = "invalid_type" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - - let condition = "{\"name\":\"age\",\"match\":\"gt\",\"type\":\"invalid\",\"value\":18}" + + let condition = "{\"match\":\"gt\",\"value\":18,\"name\":\"age\",\"type\":\"invalid\"}" user.setAttribute(key: "age", value: 25) var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) - - let expectedReason = OptimizelyError.userAttributeInvalidType(condition).reason - - // Look for a matching reason in the decision.reasons array - let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) - - XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") - - //XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidType(condition).reason)) + XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidType(condition).reason)) } func testDecideReasons_userAttributeInvalidMatch() { let featureKey = "feature_1" let audienceId = "invalid_match" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"invalid\",\"value\":18,\"name\":\"age\",\"type\":\"custom_attribute\"}" user.setAttribute(key: "age", value: 25) var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) - - let expectedReason = OptimizelyError.userAttributeInvalidMatch(condition).reason - - // Look for a matching reason in the decision.reasons array - let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) - - XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") - - //XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidMatch(condition).reason)) + XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidMatch(condition).reason)) } func testDecideReasons_userAttributeNilValue() { let featureKey = "feature_1" let audienceId = "nil_value" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"name\":\"age\",\"type\":\"custom_attribute\",\"match\":\"gt\"}" user.setAttribute(key: "age", value: 25) var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) - decision = user.decide(key: featureKey, options: [.includeReasons]) - - let expectedReason = OptimizelyError.userAttributeNilValue(condition).reason - - // Look for a matching reason in the decision.reasons array - let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) - - XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") - -// XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeNilValue(condition).reason)) + XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeNilValue(condition).reason)) } func testDecideReasons_userAttributeInvalidName() { let featureKey = "feature_1" let audienceId = "invalid_name" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"type\":\"custom_attribute\",\"match\":\"gt\",\"value\":18}" user.setAttribute(key: "age", value: 25) var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) - decision = user.decide(key: featureKey, options: [.includeReasons]) - - let expectedReason = OptimizelyError.userAttributeInvalidName(condition).reason - - // Look for a matching reason in the decision.reasons array - let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) - - XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") - - //XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidName(condition).reason)) + XCTAssert(decision.reasons.contains(OptimizelyError.userAttributeInvalidName(condition).reason)) } func testDecideReasons_missingAttributeValue() { let featureKey = "feature_1" let audienceId = "age_18" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - - let condition = "{\"type\":\"custom_attribute\",\"name\":\"age\",\"match\":\"gt\",\"value\":18}" + + let condition = "{\"match\":\"gt\",\"value\":18,\"name\":\"age\",\"type\":\"custom_attribute\"}" var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) - decision = user.decide(key: featureKey, options: [.includeReasons]) - - let expectedReason = OptimizelyError.missingAttributeValue(condition, "age").reason - - // Look for a matching reason in the decision.reasons array - let foundMatchingReason = matchReason(expectedReason: expectedReason, in: decision.reasons) - - XCTAssert(foundMatchingReason, "Expected reason with matching condition not found in decision reasons") - - //XCTAssert(decision.reasons.contains(OptimizelyError.missingAttributeValue(condition, "age").reason)) + XCTAssert(decision.reasons.contains(OptimizelyError.missingAttributeValue(condition, "age").reason)) } - + } // MARK: - log messages (only with "includeReasons") extension OptimizelyUserContextTests_Decide_Reasons { - + func testDecideReasons_experimentNotRunning() { let featureKey = "feature_1" let experimentKey = "exp_with_audience" @@ -312,7 +242,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let experimentKey = "exp_with_audience" let variationKey2 = "b" let variationId2 = "10416523121" - + OTUtils.setVariationToUPS(ups: ups, userId: kUserId, experimentId: experimentId, variationId: variationId2) let decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -325,7 +255,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let variationKey = "b" let experimentKey = setForcedVariationForFeatureTest(featureKey: featureKey, userId: kUserId, variationKey: variationKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssertEqual(decision.variationKey, variationKey) XCTAssertEqual(decision.reasons, [LogMessage.userHasForcedVariation(kUserId, experimentKey, variationKey).reason]) @@ -347,7 +277,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let variationKey = "b" setWhiteListForFeatureTest(featureKey: featureKey, userId: kUserId, variationKey: variationKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssertEqual(decision.variationKey, variationKey) XCTAssertEqual(decision.reasons, [LogMessage.forcedVariationFound(variationKey, kUserId).reason]) @@ -357,12 +287,12 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let variationKey = "invalid-key" setWhiteListForFeatureTest(featureKey: featureKey, userId: kUserId, variationKey: variationKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssertNotEqual(decision.variationKey, variationKey) XCTAssert(decision.reasons.contains(LogMessage.forcedVariationFoundButInvalid(variationKey, kUserId).reason)) } - + func testDecideReasons_userMeetsConditionsForTargetingRule() { let key = "feature_1" @@ -392,7 +322,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { decision = user.decide(key: key, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userBucketedIntoTargetingRule(kUserId, "1").reason)) } - + func testDecideReasons_userBucketedIntoEveryoneTargetingRule() { let key = "feature_1" @@ -412,7 +342,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { decision = user.decide(key: key, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userNotBucketedIntoTargetingRule(kUserId, "2").reason)) } - + func testDecideReasons_userBucketedIntoVariationInExperiment() { let featureKey = "feature_2" // embedding experiment: "exp_no_audience" let experimentKey = "exp_no_audience" @@ -429,13 +359,13 @@ extension OptimizelyUserContextTests_Decide_Reasons { func testDecideReasons_userNotBucketedIntoVariation() { let featureKey = "feature_2" // embedding experiment: "exp_no_audience" let experimentId = "10420810910" // "exp_no_audience" - + var experiment = optimizely.config!.getExperiment(id: experimentId)! var trafficAllocation = experiment.trafficAllocation[0] trafficAllocation.endOfRange = 0 experiment.trafficAllocation = [trafficAllocation] optimizely.config!.experimentIdMap = [experimentId: experiment] - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -454,7 +384,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { variation.id = variationIdInvalid experiment.variations = [variation] optimizely.config!.experimentIdMap = [experimentId: experiment] - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -466,7 +396,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let experimentKey = "group_exp_1" let groupId = "13142870430" setExperimentForFeatureTest(featureKey: featureKey, experimentKey: experimentKey) - + let decision = user.decide(key: featureKey, options: [.ignoreUserProfileService, .includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userBucketedIntoExperimentInGroup(kUserId, experimentKey, @@ -478,7 +408,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let experimentKey = "group_exp_2" let groupId = "13142870430" setExperimentForFeatureTest(featureKey: featureKey, experimentKey: experimentKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userNotBucketedIntoExperimentInGroup(kUserId, experimentKey, @@ -490,12 +420,12 @@ extension OptimizelyUserContextTests_Decide_Reasons { var group = optimizely.config!.getGroup(id: groupId)! group.trafficAllocation = [] optimizely.config!.project.groups = [group] - + // set up temp feature-experiments AFTER config.project updated (otherwise overwritten) let featureKey = "feature_3" let experimentKey = "group_exp_1" setExperimentForFeatureTest(featureKey: featureKey, experimentKey: experimentKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userNotBucketedIntoAnyExperimentInGroup(kUserId, groupId).reason)) @@ -509,12 +439,12 @@ extension OptimizelyUserContextTests_Decide_Reasons { trafficAllocation.entityId = experimentIdInvalid group.trafficAllocation = [trafficAllocation] optimizely.config!.project.groups = [group] - + // set up temp feature-experiments AFTER config.project updated (otherwise overwritten) let featureKey = "feature_3" let experimentKey = "group_exp_1" setExperimentForFeatureTest(featureKey: featureKey, experimentKey: experimentKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userBucketedIntoInvalidExperiment(experimentIdInvalid).reason)) } @@ -526,51 +456,12 @@ extension OptimizelyUserContextTests_Decide_Reasons { let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userNotInExperiment(kUserId, experimentKey).reason)) } - + } // Utils extension OptimizelyUserContextTests_Decide_Reasons { - func matchReason(expectedReason: String, in reasons: [String]) -> Bool { - // Extract the condition JSON and remaining strings from expectedReason - let (expectedPrefix, expectedJsonCondition, expectedSuffix) = extractComponents(from: expectedReason) ?? ("", [:], "") - - // Look for a matching reason in the decision.reasons array - let foundMatchingReason = reasons.contains { reason in - guard let (actualPrefix, actualJsonCondition, actualSuffix) = extractComponents(from: reason) else { - return false - } - - // Check if the prefix and suffix match exactly and the JSON conditions match ignoring key order - return expectedPrefix == actualPrefix && - expectedSuffix == actualSuffix && - NSDictionary(dictionary: expectedJsonCondition) == NSDictionary(dictionary: actualJsonCondition) - } - return foundMatchingReason - } - - // Helper function to extract prefix, JSON condition, and suffix from the reason string - func extractComponents(from reason: String) -> (String, [String: Any], String)? { - // Use regular expression to extract the parts of the string before, inside, and after the parentheses - let pattern = "^(.*?)\\((.*?)\\)(.*?)$" - let regex = try? NSRegularExpression(pattern: pattern) - let nsString = reason as NSString - if let match = regex?.firstMatch(in: reason, range: NSRange(location: 0, length: nsString.length)) { - let prefix = nsString.substring(with: match.range(at: 1)) // Part before the parentheses - let jsonString = nsString.substring(with: match.range(at: 2)) // JSON inside parentheses - let suffix = nsString.substring(with: match.range(at: 3)) // Part after the parentheses - - // Convert the JSON string into a dictionary - if let data = jsonString.data(using: .utf8), - let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []), - let jsonDict = jsonObject as? [String: Any] { - return (prefix, jsonDict, suffix) - } - } - - return nil - } func setAudienceForFeatureTest(featureKey: String, audienceId: String) { let experimentId = "10390977673" // "exp_with_audience" @@ -592,7 +483,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { _ = optimizely.setForcedVariation(experimentKey: experiment.key, userId: userId, variationKey: variationKey) return experiment.key } - + func removeVariationsForFeatureTest(featureKey: String, userId: String, variationKey: String) { let experimentId = "10390977673" // "exp_with_audience" var experiment = optimizely.config!.getExperiment(id: experimentId)! @@ -600,20 +491,19 @@ extension OptimizelyUserContextTests_Decide_Reasons { optimizely.config!.experimentIdMap = [experimentId: experiment] optimizely.config!.experimentKeyMap = [experiment.key: experiment] } - + func setWhiteListForFeatureTest(featureKey: String, userId: String, variationKey: String) { let experimentId = "10390977673" // "exp_with_audience" var experiment = optimizely.config!.getExperiment(id: experimentId)! experiment.forcedVariations = [userId: variationKey] optimizely.config!.experimentIdMap = [experimentId: experiment] } - + func setExperimentForFeatureTest(featureKey: String, experimentKey: String) { let experimentId = optimizely.config!.getExperimentId(key: experimentKey)! var feature = optimizely.config!.getFeatureFlag(key: featureKey)! feature.experimentIds = [experimentId] optimizely.config!.featureFlagKeyMap = [featureKey: feature] } - + } - From 5b971fb50826c16b8e648e45ba746a07a38cb1dd Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Wed, 30 Oct 2024 21:24:05 +0600 Subject: [PATCH 19/23] WIP: Vuid initialize method name fixed --- Sources/Optimizely/OptimizelyClient.swift | 2 +- Sources/Optimizely/VuidManager.swift | 2 +- Tests/OptimizelyTests-Common/VuidManagerTests.swift | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index 41703af6c..894972adb 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -93,7 +93,7 @@ open class OptimizelyClient: NSObject { super.init() self.vuidManager = VuidManager.shared - self.vuidManager.intiazialize(enabled: self.sdkSettings.enableVuid) + self.vuidManager.initialize(enabled: self.sdkSettings.enableVuid) self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey, disable: sdkSettings.disableOdp, vuid: vuidManager.vuid, diff --git a/Sources/Optimizely/VuidManager.swift b/Sources/Optimizely/VuidManager.swift index 5f8afa614..785d17fb1 100644 --- a/Sources/Optimizely/VuidManager.swift +++ b/Sources/Optimizely/VuidManager.swift @@ -24,7 +24,7 @@ class VuidManager { // a single vuid should be shared for all SDK instances static let shared = VuidManager() - func intiazialize(enabled: Bool) { + func initialize(enabled: Bool) { self.enabled = enabled if enabled { self._vuid = load() diff --git a/Tests/OptimizelyTests-Common/VuidManagerTests.swift b/Tests/OptimizelyTests-Common/VuidManagerTests.swift index 1725a550c..c8571f612 100644 --- a/Tests/OptimizelyTests-Common/VuidManagerTests.swift +++ b/Tests/OptimizelyTests-Common/VuidManagerTests.swift @@ -21,7 +21,7 @@ class VuidManagerTests: XCTestCase { func testNewVuid() { - manager.intiazialize(enabled: true) + manager.initialize(enabled: true) let vuid = VuidManager.newVuid @@ -30,7 +30,7 @@ class VuidManagerTests: XCTestCase { } func testIsVuid() { - manager.intiazialize(enabled: true) + manager.initialize(enabled: true) XCTAssertTrue(VuidManager.isVuid("vuid_123")) XCTAssertFalse(VuidManager.isVuid("vuid-123")) XCTAssertFalse(VuidManager.isVuid("123")) @@ -39,7 +39,7 @@ class VuidManagerTests: XCTestCase { func testAutoSaveAndLoad() { UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - manager.intiazialize(enabled: true) + manager.initialize(enabled: true) let vuid1 = manager.vuid let vuid2 = manager.vuid @@ -50,19 +50,19 @@ class VuidManagerTests: XCTestCase { UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - manager.intiazialize(enabled: true) + manager.initialize(enabled: true) let vuid3 = manager.vuid XCTAssertTrue(vuid1 != vuid3) } func testRemoveOldVuid() { - manager.intiazialize(enabled: true) + manager.initialize(enabled: true) let cahcedVuid1 = UserDefaults.standard.string(forKey: "optimizely-vuid") XCTAssertNotNil(cahcedVuid1) XCTAssertTrue(cahcedVuid1 == manager.vuid) - manager.intiazialize(enabled: false) + manager.initialize(enabled: false) let cahcedVuid2 = UserDefaults.standard.string(forKey: "optimizely-vuid") XCTAssertNil(cahcedVuid2) From 172a04d0444e72a2dc4deda775d4137058237c26 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Fri, 1 Nov 2024 07:44:54 -0300 Subject: [PATCH 20/23] WIP: VuidManager api renamed. Ignore case comparision added --- Sources/Optimizely/OptimizelyClient.swift | 4 ++-- Sources/Optimizely/VuidManager.swift | 13 +++++++------ .../VuidManagerTests.swift | 19 +++++++++++++------ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index 894972adb..cc4bfe57d 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -93,7 +93,7 @@ open class OptimizelyClient: NSObject { super.init() self.vuidManager = VuidManager.shared - self.vuidManager.initialize(enabled: self.sdkSettings.enableVuid) + self.vuidManager.configure(enable: self.sdkSettings.enableVuid) self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey, disable: sdkSettings.disableOdp, vuid: vuidManager.vuid, @@ -980,7 +980,7 @@ extension OptimizelyClient { } public var enableVuid: Bool { - return self.vuidManager.enabled + return self.vuidManager.enable } func identifyUserToOdp(userId: String) { diff --git a/Sources/Optimizely/VuidManager.swift b/Sources/Optimizely/VuidManager.swift index 785d17fb1..643af7591 100644 --- a/Sources/Optimizely/VuidManager.swift +++ b/Sources/Optimizely/VuidManager.swift @@ -18,18 +18,19 @@ import Foundation class VuidManager { private var _vuid: String = "" - private(set) var enabled: Bool = false + private(set) var enable: Bool = false let logger = OPTLoggerFactory.getLogger() // a single vuid should be shared for all SDK instances static let shared = VuidManager() - func initialize(enabled: Bool) { - self.enabled = enabled - if enabled { + func configure(enable: Bool) { + self.enable = enable + if enable { self._vuid = load() } else { self.remove() + self._vuid = "" } } @@ -43,7 +44,7 @@ class VuidManager { } static func isVuid(_ visitorId: String) -> Bool { - return visitorId.starts(with: "vuid_") + return visitorId.lowercased().starts(with: "vuid_") } } @@ -52,7 +53,7 @@ class VuidManager { extension VuidManager { var vuid: String? { - if enabled { + if self.enable { return _vuid } else { logger.w("VUID is not enabled.") diff --git a/Tests/OptimizelyTests-Common/VuidManagerTests.swift b/Tests/OptimizelyTests-Common/VuidManagerTests.swift index c8571f612..35bb653b7 100644 --- a/Tests/OptimizelyTests-Common/VuidManagerTests.swift +++ b/Tests/OptimizelyTests-Common/VuidManagerTests.swift @@ -21,7 +21,7 @@ class VuidManagerTests: XCTestCase { func testNewVuid() { - manager.initialize(enabled: true) + manager.configure(enable: true) let vuid = VuidManager.newVuid @@ -30,16 +30,23 @@ class VuidManagerTests: XCTestCase { } func testIsVuid() { - manager.initialize(enabled: true) + manager.configure(enable: true) XCTAssertTrue(VuidManager.isVuid("vuid_123")) XCTAssertFalse(VuidManager.isVuid("vuid-123")) XCTAssertFalse(VuidManager.isVuid("123")) } + func testIsVuidIgnoreCase() { + manager.configure(enable: true) + XCTAssertTrue(VuidManager.isVuid("VUID_123")) + XCTAssertFalse(VuidManager.isVuid("VUID-123")) + XCTAssertFalse(VuidManager.isVuid("123")) + } + func testAutoSaveAndLoad() { UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - manager.initialize(enabled: true) + manager.configure(enable: true) let vuid1 = manager.vuid let vuid2 = manager.vuid @@ -50,19 +57,19 @@ class VuidManagerTests: XCTestCase { UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - manager.initialize(enabled: true) + manager.configure(enable: true) let vuid3 = manager.vuid XCTAssertTrue(vuid1 != vuid3) } func testRemoveOldVuid() { - manager.initialize(enabled: true) + manager.configure(enable: true) let cahcedVuid1 = UserDefaults.standard.string(forKey: "optimizely-vuid") XCTAssertNotNil(cahcedVuid1) XCTAssertTrue(cahcedVuid1 == manager.vuid) - manager.initialize(enabled: false) + manager.configure(enable: false) let cahcedVuid2 = UserDefaults.standard.string(forKey: "optimizely-vuid") XCTAssertNil(cahcedVuid2) From 83ab33180872215be97f4c2b8ff32fbd8013f7cb Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Fri, 15 Nov 2024 22:22:38 +0600 Subject: [PATCH 21/23] wip: vuidmanager made public --- Sources/Optimizely/VuidManager.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Optimizely/VuidManager.swift b/Sources/Optimizely/VuidManager.swift index 643af7591..6ffcb45a5 100644 --- a/Sources/Optimizely/VuidManager.swift +++ b/Sources/Optimizely/VuidManager.swift @@ -16,15 +16,15 @@ import Foundation -class VuidManager { +public class VuidManager { private var _vuid: String = "" private(set) var enable: Bool = false let logger = OPTLoggerFactory.getLogger() // a single vuid should be shared for all SDK instances - static let shared = VuidManager() + public static let shared = VuidManager() - func configure(enable: Bool) { + public func configure(enable: Bool) { self.enable = enable if enable { self._vuid = load() @@ -52,7 +52,7 @@ class VuidManager { // MARK: - VUID Store extension VuidManager { - var vuid: String? { + public var vuid: String? { if self.enable { return _vuid } else { From 95d71c7b07d8d10492674a76ed594d6a01114d5f Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Sun, 17 Nov 2024 22:56:54 +0600 Subject: [PATCH 22/23] wip: vuid event separated from odpmanager --- Sources/ODP/OdpManager.swift | 6 +++--- Sources/Optimizely/OptimizelyClient.swift | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index f5f49a7b7..b04b5ec82 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -61,9 +61,9 @@ public class OdpManager { self.odpConfig = OdpConfig() self.segmentManager.odpConfig = odpConfig self.eventManager.odpConfig = odpConfig - if let vuid = vuid, VuidManager.isVuid(vuid) { - self.eventManager.sendInitializedEvent(vuid: vuid) - } +// if let vuid = vuid, VuidManager.isVuid(vuid) { +// self.eventManager.sendInitializedEvent(vuid: vuid) +// } } func fetchQualifiedSegments(userId: String, diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index cc4bfe57d..ab160f0c5 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -101,7 +101,7 @@ open class OptimizelyClient: NSObject { cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs, timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs, timeoutForEventDispatchInSecs: sdkSettings.timeoutForOdpEventInSecs) - + let userProfileService = userProfileService ?? DefaultUserProfileService() let logger = logger ?? DefaultLogger() type(of: logger).logLevel = defaultLogLevel ?? .info @@ -118,7 +118,14 @@ open class OptimizelyClient: NSObject { self.datafileHandler = HandlerRegistryService.shared.injectDatafileHandler(sdkKey: self.sdkKey) self.decisionService = HandlerRegistryService.shared.injectDecisionService(sdkKey: self.sdkKey) self.notificationCenter = HandlerRegistryService.shared.injectNotificationCenter(sdkKey: self.sdkKey) - + if VuidManager.shared.enable { + try? sendOdpEvent(type: Constants.ODP.eventType, + action: "client_initialized", + identifiers: [ + Constants.ODP.keyForVuid: VuidManager.shared.vuid! + ], + data: [:]) + } logger.d("SDK Version: \(version)") } From ae71cbf0c2fb3e9aab45e3ae90e1737ac2aef7af Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Mon, 18 Nov 2024 07:30:20 +0600 Subject: [PATCH 23/23] wip: update test case --- Sources/Optimizely/OptimizelyClient.swift | 2 +- .../OptimizelyUserContextTests_ODP_2.swift | 25 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index ab160f0c5..52de5247b 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -96,11 +96,11 @@ open class OptimizelyClient: NSObject { self.vuidManager.configure(enable: self.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) + self.odpManager.vuid = VuidManager.shared.vuid let userProfileService = userProfileService ?? DefaultUserProfileService() let logger = logger ?? DefaultLogger() diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift index ae8cdbfff..0b693b6f4 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift @@ -26,21 +26,28 @@ class OptimizelyUserContextTests_ODP_2: XCTestCase { // odp disabled to avoid initial noise - let optimizely = OptimizelyClient(sdkKey: sdkKey, - settings: OptimizelySdkSettings(disableOdp: true, enableVuid: true)) // override with a custom enabled odpManager. // - client_inializatied event will be sent automatically // - will wait in the queue until project config is ready let odpEventApiManager = MockOdpEventApiManager() - optimizely.odpManager = OdpManager(sdkKey: sdkKey, - disable: false, - vuid: "vuid_123", - cacheSize: 10, - cacheTimeoutInSecs: 10, - eventManager: OdpEventManager(sdkKey: sdkKey, - apiManager: odpEventApiManager)) +// optimizely.odpManager = OdpManager(sdkKey: sdkKey, +// disable: false, +// vuid: "vuid_123", +// cacheSize: 10, +// cacheTimeoutInSecs: 10, +// eventManager: OdpEventManager(sdkKey: sdkKey, +// apiManager: odpEventApiManager)) + let optimizely = OptimizelyClient(sdkKey: sdkKey, + odpManager: OdpManager(sdkKey: sdkKey, + disable: false, + vuid: "vuid_123", + cacheSize: 10, + cacheTimeoutInSecs: 10, + eventManager: OdpEventManager(sdkKey: sdkKey, + apiManager: odpEventApiManager)), settings: OptimizelySdkSettings(disableOdp: true, enableVuid: true)) + // identified event will sent but wait in the queue until project config is ready _ = optimizely.createUserContext(userId: "tester")