Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Testing] vuid checking #563

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
48f7d8d
Add vuid optln feature
muzahidul-opti Oct 15, 2024
b799412
[WIP] Refractored vuid logic
muzahidul-opti Oct 17, 2024
2cb824e
clean up
muzahidul-opti Oct 17, 2024
ea762f7
Test target build successfully
muzahidul-opti Oct 17, 2024
7c2bd51
ODP test case updated
muzahidul-opti Oct 17, 2024
fde285a
OdpManager test case added
muzahidul-opti Oct 17, 2024
283608e
Client updated
muzahidul-opti Oct 17, 2024
4adc0a0
Test case updated
muzahidul-opti Oct 17, 2024
2eeb008
WIP: VuidManager move to client
muzahidul-opti Oct 18, 2024
b89e594
WIP: Update odpmanager and client
muzahidul-opti Oct 18, 2024
94910e2
[WIP] Update test cases
muzahidul-opti Oct 18, 2024
b71e65c
WIP: client release issue fixed
muzahidul-opti Oct 18, 2024
b835b8f
WIP: Relase test fixed
muzahidul-opti Oct 18, 2024
7f49d58
WIP: clean up
muzahidul-opti Oct 22, 2024
97e6447
WIP: VuidManager singleton added
muzahidul-opti Oct 30, 2024
b54266c
WIP: OdpVuidManager renamed as VuidManager
muzahidul-opti Oct 30, 2024
f916638
WIP: Make vuid optional to identifyUser method
muzahidul-opti Oct 30, 2024
260673b
WIP: Decide reasons test cases changes reverted
muzahidul-opti Oct 30, 2024
5b971fb
WIP: Vuid initialize method name fixed
muzahidul-opti Oct 30, 2024
172a04d
WIP: VuidManager api renamed. Ignore case comparision added
muzahidul-opti Nov 1, 2024
7e49835
Merge branch 'master' into muzahid/vuid-optln
muzahidul-opti Nov 8, 2024
83ab331
wip: vuidmanager made public
muzahidul-opti Nov 15, 2024
95d71c7
wip: vuid event separated from odpmanager
muzahidul-opti Nov 17, 2024
ae71cbf
wip: update test case
muzahidul-opti Nov 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 39 additions & 39 deletions OptimizelySwiftSDK.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions Sources/ODP/OdpEventManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ open class OdpEventManager {

// MARK: - events

func registerVUID(vuid: String) {
func sendInitializedEvent(vuid: String) {
sendEvent(type: Constants.ODP.eventType,
action: "client_initialized",
identifiers: [
Expand All @@ -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
}
Expand Down
35 changes: 14 additions & 21 deletions Sources/ODP/OdpManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,13 @@ import Foundation

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

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

let logger = OPTLoggerFactory.getLogger()

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

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

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

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

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

func fetchQualifiedSegments(userId: String,
Expand All @@ -77,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,
Expand All @@ -97,15 +94,13 @@ public class OdpManager {
return
}

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

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

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

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

// add vuid to all events by default

var identifiersUpdated = identifiers
if identifiers[Constants.ODP.keyForVuid] == nil {
identifiersUpdated[Constants.ODP.keyForVuid] = vuidManager.vuid

if identifiers[Constants.ODP.keyForVuid] == nil, let _vuid = vuid {
identifiersUpdated[Constants.ODP.keyForVuid] = _vuid
}

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

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

Expand Down
5 changes: 5 additions & 0 deletions Sources/ODP/OptimizelySdkSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public struct OptimizelySdkSettings {
let timeoutForOdpEventInSecs: Int
/// ODP features are disabled if this is set to true.
let disableOdp: Bool
/// VUID is enabled if this is set to true.
let enableVuid: Bool

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

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

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

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

super.init()

self.vuidManager = VuidManager.shared
self.vuidManager.configure(enable: self.sdkSettings.enableVuid)
self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey,
disable: sdkSettings.disableOdp,
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()
type(of: logger).logLevel = defaultLogLevel ?? .info
Expand All @@ -114,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)")
}

Expand Down Expand Up @@ -971,8 +982,12 @@ extension OptimizelyClient {
}

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

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

func identifyUserToOdp(userId: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,22 @@

import Foundation

class OdpVuidManager {
var vuid: String = ""
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 = OdpVuidManager()
public static let shared = VuidManager()

init() {
self.vuid = load()
public func configure(enable: Bool) {
self.enable = enable
if enable {
self._vuid = load()
} else {
self.remove()
self._vuid = ""
}
}

static var newVuid: String {
Expand All @@ -35,16 +42,24 @@ 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_")
return visitorId.lowercased().starts(with: "vuid_")
}

}

// MARK: - VUID Store

extension OdpVuidManager {
extension VuidManager {
public var vuid: String? {
if self.enable {
return _vuid
} else {
logger.w("VUID is not enabled.")
return nil
}
}

private var keyForVuid: String {
return "optimizely-vuid"
Expand All @@ -55,11 +70,16 @@ extension OdpVuidManager {
return oldVuid
}

let vuid = OdpVuidManager.newVuid
let vuid = VuidManager.newVuid
save(vuid: vuid)
return vuid
}


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

private func save(vuid: String) {
UserDefaults.standard.set(vuid, forKey: keyForVuid)
UserDefaults.standard.synchronize()
Expand Down
14 changes: 8 additions & 6 deletions Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class OptimizelyClientTests_Decide: XCTestCase {
super.setUp()

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

try! optimizely.start(datafile: datafile)
}

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

let user = optimizely.createUserContext(attributes: attributes)

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

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

// MARK: - vuid

func testVuid() {
XCTAssert(optimizely.vuid.starts(with: "vuid_"))
func testVuidEnabled() {
let settings = OptimizelySdkSettings(enableVuid: 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)
XCTAssertNil(optimizely.vuid)
}

// MARK: - OdpConfig Update

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

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

XCTAssertEqual(1, manager.eventQueue.count)

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

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

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

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

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

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

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

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

Expand Down
Loading
Loading