Skip to content

Commit

Permalink
Operation Repo - refactor and update for JWT
Browse files Browse the repository at this point in the history
* Refactor from a static shared instance to a instance managed by User Manager
* Operation Repo does not flush while Identity Verification is unknown
* Currently it is not going to process Deltas based on JWT. Executors will.
  • Loading branch information
nan-li committed Aug 22, 2024
1 parent 1e5a1e6 commit 02584e0
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, PATCH} HTTP
#define OS_UPDATE_SUBSCRIPTION_DELTA @"OS_UPDATE_SUBSCRIPTION_DELTA"

// Operation Repo
#define OS_OPERATION_REPO @"OS_OPERATION_REPO"
#define OS_OPERATION_REPO_DELTA_QUEUE_KEY @"OS_OPERATION_REPO_DELTA_QUEUE_KEY"

// User Executor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ import OneSignalCore
public protocol OSModelStoreListener: OSModelStoreChangedHandler {
associatedtype TModel: OSModel

var operationRepo: OSOperationRepo { get }

var store: OSModelStore<TModel> { get }

init(store: OSModelStore<TModel>)
init(store: OSModelStore<TModel>, operationRepo: OSOperationRepo)

func getAddModelDelta(_ model: TModel) -> OSDelta?

Expand All @@ -59,13 +61,13 @@ extension OSModelStoreListener {
return
}
if let delta = getAddModelDelta(addedModel) {
OSOperationRepo.sharedInstance.enqueueDelta(delta)
operationRepo.enqueueDelta(delta)
}
}

public func onUpdated(_ args: OSModelChangedArgs) {
if let delta = getUpdateModelDelta(args) {
OSOperationRepo.sharedInstance.enqueueDelta(delta)
operationRepo.enqueueDelta(delta)
}
}

Expand All @@ -76,7 +78,7 @@ extension OSModelStoreListener {
return
}
if let delta = getRemoveModelDelta(removedModel) {
OSOperationRepo.sharedInstance.enqueueDelta(delta)
operationRepo.enqueueDelta(delta)
}
}
}
67 changes: 50 additions & 17 deletions iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ import OneSignalCore
The OSOperationRepo is a static singleton.
OSDeltas are enqueued when model store observers observe changes to their models, and sorted to their appropriate executors.
*/
public class OSOperationRepo: NSObject {
public static let sharedInstance = OSOperationRepo()
public class OSOperationRepo {
private var hasCalledStart = false

// The Operation Repo dispatch queue, serial. This synchronizes access to `deltaQueue` and flushing behavior.
Expand All @@ -47,16 +46,37 @@ public class OSOperationRepo: NSObject {
// TODO: This could come from a config, plist, method, remote params
var pollIntervalMilliseconds = Int(POLL_INTERVAL_MS)
public var paused = false
let jwtConfig: OSUserJwtConfig

/**
Initilize this Operation Repo. Read from the cache. Executors may not be available by this time.
If everything starts up on initialize(), order can matter, ideally not but it can.
Likely call init on this from oneSignal but exeuctors can come from diff modules.
Sets the jwt config and uncaches
*/
public init(jwtConfig: OSUserJwtConfig) {
self.jwtConfig = jwtConfig
self.jwtConfig.subscribe(self, key: OS_OPERATION_REPO)
print("❌ OSOperationRepo init(\(String(describing: jwtConfig.isRequired))) called")

// Read the Deltas from cache, if any...
guard let deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_OPERATION_REPO_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] else {
OneSignalLog.onesignalLog(.LL_ERROR, message: "OSOperationRepo is unable to uncache the OSDelta queue.")
return
}
self.deltaQueue = deltaQueue
}

/**
Start this Operation Repo.
*/
public func start() {
guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else {
return
}

guard jwtConfig.isRequired != nil else {
print("❌ OSOperationRepo.start() returning early due to unknown Identity Verification status.")
return
}

guard !hasCalledStart else {
return
}
Expand All @@ -68,13 +88,6 @@ public class OSOperationRepo: NSObject {
selector: #selector(self.addFlushDeltaQueueToDispatchQueue),
name: Notification.Name(OS_ON_USER_WILL_CHANGE),
object: nil)
// Read the Deltas from cache, if any...
if let deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_OPERATION_REPO_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] {
self.deltaQueue = deltaQueue
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo.start() with deltaQueue: \(deltaQueue)")
} else {
OneSignalLog.onesignalLog(.LL_ERROR, message: "OSOperationRepo.start() is unable to uncache the OSDelta queue.")
}

pollFlushQueue()
}
Expand All @@ -87,13 +100,12 @@ public class OSOperationRepo: NSObject {
}

/**
Add and start an executor.
Add an executor.
*/
public func addExecutor(_ executor: OSOperationExecutor) {
guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else {
return
}
start()
executors.append(executor)
for delta in executor.supportedDeltas {
deltasToExecutorMap[delta] = executor
Expand All @@ -111,7 +123,6 @@ public class OSOperationRepo: NSObject {
guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else {
return
}
start()
self.dispatchQueue.async {
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo enqueueDelta: \(delta)")
self.deltaQueue.append(delta)
Expand Down Expand Up @@ -140,8 +151,6 @@ public class OSOperationRepo: NSObject {
OSBackgroundTaskManager.beginBackgroundTask(OPERATION_REPO_BACKGROUND_TASK)
}

self.start()

if !self.deltaQueue.isEmpty {
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo flushDeltaQueue in background: \(inBackground) with queue: \(self.deltaQueue)")
}
Expand Down Expand Up @@ -174,6 +183,30 @@ public class OSOperationRepo: NSObject {
}
}

extension OSOperationRepo: OSUserJwtConfigListener {
public func onRequiresUserAuthChanged(from: Bool?, to: Bool?) {
print("❌ OSOperationRepo onRequiresUserAuthChanged from \(String(describing: from)) to \(String(describing: to))")
// If auth changed from false or unknown to true, process deltas
if to == true {
removeInvalidDeltas()
}
start()
}

public func onJwtUpdated(externalId: String, to: String?) {
print("❌ OSOperationRepo onJwtUpdated for \(externalId) to \(String(describing: to))")
}

/**
TODO: The operation repo cannot easily remove invalid Deltas that do not have an External ID.
Deltas have an Identity Model ID only and would need to access the User module to find the corresponding Identity Model.
Executors will handle this.
*/
func removeInvalidDeltas() {
print("❌ OSOperationRepo removeInvalidDeltas TODO!")
}
}

extension OSOperationRepo: OSLoggable {
public func logSelf() {
print("💛 Operation Repo: executors")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ extension OSUserExecutor {
self.executePendingRequests()
}
}
OSOperationRepo.sharedInstance.paused = false
OneSignalUserManagerImpl.sharedInstance.operationRepo.paused = false
} onFailure: { error in
OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor create user request failed with error: \(error.debugDescription)")
if let nsError = error as? NSError {
Expand All @@ -344,7 +344,7 @@ extension OSUserExecutor {
// A failed create user request would leave the SDK in a bad state
// Don't remove the request from cache and pause the operation repo
// We will retry this request on a new session
OSOperationRepo.sharedInstance.paused = true
OneSignalUserManagerImpl.sharedInstance.operationRepo.paused = true
request.sentToClient = false
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ import OneSignalCore
import OneSignalOSCore

class OSIdentityModelStoreListener: OSModelStoreListener {
let operationRepo: OSOperationRepo
var store: OSModelStore<OSIdentityModel>

required init(store: OSModelStore<OSIdentityModel>) {
required init(store: OSModelStore<OSIdentityModel>, operationRepo: OSOperationRepo) {
self.operationRepo = operationRepo
self.store = store
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ import OneSignalCore
import OneSignalOSCore

class OSPropertiesModelStoreListener: OSModelStoreListener {
let operationRepo: OSOperationRepo
var store: OSModelStore<OSPropertiesModel>

required init(store: OSModelStore<OSPropertiesModel>) {
required init(store: OSModelStore<OSPropertiesModel>, operationRepo: OSOperationRepo) {
self.operationRepo = operationRepo
self.store = store
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ import OneSignalCore
import OneSignalOSCore

class OSSubscriptionModelStoreListener: OSModelStoreListener {
let operationRepo: OSOperationRepo
var store: OSModelStore<OSSubscriptionModel>

required init(store: OSModelStore<OSSubscriptionModel>) {
required init(store: OSModelStore<OSSubscriptionModel>, operationRepo: OSOperationRepo) {
self.operationRepo = operationRepo
self.store = store
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager {
let pushSubscriptionModelStore = OSModelStore<OSSubscriptionModel>(changeSubscription: OSEventProducer(), storeKey: OS_PUSH_SUBSCRIPTION_MODEL_STORE_KEY)

// These must be initialized in init()
let operationRepo: OSOperationRepo
let identityModelStoreListener: OSIdentityModelStoreListener
let propertiesModelStoreListener: OSPropertiesModelStoreListener
let subscriptionModelStoreListener: OSSubscriptionModelStoreListener
Expand All @@ -186,10 +187,11 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager {

private init(jwtConfig: OSUserJwtConfig) {
self.jwtConfig = jwtConfig
self.identityModelStoreListener = OSIdentityModelStoreListener(store: identityModelStore)
self.propertiesModelStoreListener = OSPropertiesModelStoreListener(store: propertiesModelStore)
self.subscriptionModelStoreListener = OSSubscriptionModelStoreListener(store: subscriptionModelStore)
self.pushSubscriptionModelStoreListener = OSSubscriptionModelStoreListener(store: pushSubscriptionModelStore)
self.operationRepo = OSOperationRepo(jwtConfig: jwtConfig)
self.identityModelStoreListener = OSIdentityModelStoreListener(store: identityModelStore, operationRepo: operationRepo)
self.propertiesModelStoreListener = OSPropertiesModelStoreListener(store: propertiesModelStore, operationRepo: operationRepo)
self.subscriptionModelStoreListener = OSSubscriptionModelStoreListener(store: subscriptionModelStore, operationRepo: operationRepo)
self.pushSubscriptionModelStoreListener = OSSubscriptionModelStoreListener(store: pushSubscriptionModelStore, operationRepo: operationRepo)
self.pushSubscriptionImpl = OSPushSubscriptionImpl(pushSubscriptionModelStore: pushSubscriptionModelStore)
}

Expand Down Expand Up @@ -226,7 +228,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager {
// Setup the executors
// The OSUserExecutor has to run first, before other executors
self.userExecutor = OSUserExecutor(newRecordsState: newRecordsState, jwtConfig: jwtConfig)
OSOperationRepo.sharedInstance.start()
operationRepo.start()

// Cannot initialize these executors in `init` as they reference the sharedInstance
let propertyExecutor = OSPropertyOperationExecutor(newRecordsState: newRecordsState, jwtConfig: jwtConfig)
Expand All @@ -235,9 +237,9 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager {
self.propertyExecutor = propertyExecutor
self.identityExecutor = identityExecutor
self.subscriptionExecutor = subscriptionExecutor
OSOperationRepo.sharedInstance.addExecutor(identityExecutor)
OSOperationRepo.sharedInstance.addExecutor(propertyExecutor)
OSOperationRepo.sharedInstance.addExecutor(subscriptionExecutor)
operationRepo.addExecutor(identityExecutor)
operationRepo.addExecutor(propertyExecutor)
operationRepo.addExecutor(subscriptionExecutor)

// Path 2. There is a legacy player to migrate
if let legacyPlayerId = OneSignalUserDefaults.initShared().getSavedString(forKey: OSUD_LEGACY_PLAYER_ID, defaultValue: nil) {
Expand Down Expand Up @@ -561,7 +563,7 @@ extension OneSignalUserManagerImpl {
start()

userExecutor!.executePendingRequests()
OSOperationRepo.sharedInstance.paused = false
operationRepo.paused = false
updatePropertiesDeltas(property: .session_count, value: 1)

// Fetch the user's data if there is a onesignal_id
Expand Down Expand Up @@ -594,7 +596,7 @@ extension OneSignalUserManagerImpl {
property: property.rawValue,
value: value
)
OSOperationRepo.sharedInstance.enqueueDelta(delta)
operationRepo.enqueueDelta(delta)
}

/// Time processors forward the session time to this method.
Expand All @@ -612,7 +614,7 @@ extension OneSignalUserManagerImpl {
*/
@objc
public func runBackgroundTasks() {
OSOperationRepo.sharedInstance.addFlushDeltaQueueToDispatchQueue(inBackground: true)
operationRepo.addFlushDeltaQueueToDispatchQueue(inBackground: true)
}
}

Expand Down

0 comments on commit 02584e0

Please sign in to comment.