@@ -31,7 +31,7 @@ import OneSignalOSCore
3131
3232/**
3333 Involved in the login process and responsible for Identify User and Create User.
34- Can execute `OSRequestCreateUser`, `OSRequestIdentifyUser`, `OSRequestTransferSubscription`, `OSRequestFetchUser`.
34+ Can execute `OSRequestCreateUser`, `OSRequestIdentifyUser`, `OSRequestTransferSubscription`, `OSRequestFetchUser`, `OSRequestFetchIdentityBySubscription` .
3535 */
3636class OSUserExecutor {
3737 static var userRequestQueue : [ OSUserRequest ] = [ ]
@@ -42,11 +42,24 @@ class OSUserExecutor {
4242 static func start( ) {
4343 var userRequestQueue : [ OSUserRequest ] = [ ]
4444
45- // Read unfinished Create User + Identify User requests from cache, if any...
45+ // Read unfinished Create User + Identify User + Get Identity By Subscription requests from cache, if any...
4646 if let cachedRequestQueue = OneSignalUserDefaults . initShared ( ) . getSavedCodeableData ( forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, defaultValue: [ ] ) as? [ OSUserRequest ] {
4747 // Hook each uncached Request to the right model reference
4848 for request in cachedRequestQueue {
49- if request. isKind ( of: OSRequestCreateUser . self) , let req = request as? OSRequestCreateUser {
49+ if request. isKind ( of: OSRequestFetchIdentityBySubscription . self) , let req = request as? OSRequestFetchIdentityBySubscription {
50+ if let identityModel = OneSignalUserManagerImpl . sharedInstance. identityModelStore. getModel ( modelId: req. identityModel. modelId) {
51+ // 1. The model exist in the store, set it to be the Request's model
52+ req. identityModel = identityModel
53+ } else if let identityModel = identityModels [ req. identityModel. modelId] {
54+ // 2. The model exists in the dict of identityModels already processed to use
55+ req. identityModel = identityModel
56+ } else {
57+ // 3. The models do not exist, use the model on the request, and add to dict.
58+ identityModels [ req. identityModel. modelId] = req. identityModel
59+ }
60+ userRequestQueue. append ( req)
61+
62+ } else if request. isKind ( of: OSRequestCreateUser . self) , let req = request as? OSRequestCreateUser {
5063 if let identityModel = OneSignalUserManagerImpl . sharedInstance. identityModelStore. getModel ( modelId: req. identityModel. modelId) {
5164 // 1. The model exist in the store, set it to be the Request's model
5265 req. identityModel = identityModel
@@ -144,8 +157,11 @@ class OSUserExecutor {
144157 if !request. prepareForExecution ( ) {
145158 return
146159 }
147-
148- if request. isKind ( of: OSRequestCreateUser . self) , let createUserRequest = request as? OSRequestCreateUser {
160+
161+ if request. isKind ( of: OSRequestFetchIdentityBySubscription . self) , let fetchIdentityRequest = request as? OSRequestFetchIdentityBySubscription {
162+ executeFetchIdentityBySubscriptionRequest ( fetchIdentityRequest)
163+ return
164+ } else if request. isKind ( of: OSRequestCreateUser . self) , let createUserRequest = request as? OSRequestCreateUser {
149165 executeCreateUserRequest ( createUserRequest)
150166 return
151167 } else if request. isKind ( of: OSRequestIdentifyUser . self) , let identifyUserRequest = request as? OSRequestIdentifyUser {
@@ -262,8 +278,8 @@ class OSUserExecutor {
262278 return response ? [ " properties " ] as? [ String : Any ]
263279 }
264280
265- static func parseIdentityObjectResponse( _ response: [ AnyHashable : Any ] ? ) -> [ String : Any ] ? {
266- return response ? [ " identity " ] as? [ String : Any ]
281+ static func parseIdentityObjectResponse( _ response: [ AnyHashable : Any ] ? ) -> [ String : String ] ? {
282+ return response ? [ " identity " ] as? [ String : String ]
267283 }
268284
269285 // We will pass minimal properties to this request
@@ -304,7 +320,7 @@ class OSUserExecutor {
304320 // If this user already exists and we logged into an external_id, fetch the user data
305321 // TODO: Only do this if response code is 200 or 202
306322 // Fetch the user only if its the current user
307- if let _ = OneSignalUserManagerImpl . sharedInstance. identityModelStore . getModel ( modelId : request. identityModel. modelId ) ,
323+ if OneSignalUserManagerImpl . sharedInstance. isCurrentUser ( request. identityModel) ,
308324 let identity = request. parameters ? [ " identity " ] as? [ String : String ] ,
309325 let externalId = identity [ OS_EXTERNAL_ID] {
310326 fetchUser ( aliasLabel: OS_EXTERNAL_ID, aliasId: externalId, identityModel: request. identityModel)
@@ -323,7 +339,60 @@ class OSUserExecutor {
323339 executePendingRequests ( )
324340 }
325341 }
342+
343+ static func fetchIdentityBySubscription( _ user: OSUserInternal ) {
344+ let request = OSRequestFetchIdentityBySubscription ( identityModel: user. identityModel, pushSubscriptionModel: user. pushSubscriptionModel)
345+
346+ appendToQueue ( request)
347+ executePendingRequests ( )
348+ }
349+
350+ /**
351+ For migrating legacy players from 3.x to 5.x. This request will fetch the identity object for a subscription ID, and we will use the returned onesignalId to fetch and hydrate the local user.
352+ */
353+ static func executeFetchIdentityBySubscriptionRequest( _ request: OSRequestFetchIdentityBySubscription ) {
354+ guard !request. sentToClient else {
355+ return
356+ }
357+ guard request. prepareForExecution ( ) else {
358+ return
359+ }
360+ request. sentToClient = true
326361
362+ OneSignalClient . shared ( ) . execute ( request) { response in
363+ removeFromQueue ( request)
364+
365+ if let identityObject = parseIdentityObjectResponse ( response) ,
366+ let onesignalId = identityObject [ OS_ONESIGNAL_ID]
367+ {
368+ request. identityModel. hydrate ( identityObject)
369+
370+ // Fetch this user's data if it is the current user
371+ guard OneSignalUserManagerImpl . sharedInstance. isCurrentUser ( request. identityModel)
372+ else {
373+ executePendingRequests ( )
374+ return
375+ }
376+
377+ fetchUser ( aliasLabel: OS_ONESIGNAL_ID, aliasId: onesignalId, identityModel: request. identityModel)
378+ }
379+ } onFailure: { error in
380+ OneSignalLog . onesignalLog ( . LL_ERROR, message: " OSUserExecutor executeFetchIdentityBySubscriptionRequest failed with error: \( error. debugDescription) " )
381+
382+ // TODO: Differentiate error cases
383+
384+ // If the error is not retryable, remove from cache and queue
385+ if let nsError = error as? NSError ,
386+ nsError. code < 500 && nsError. code != 0 {
387+ // remove the subscription_id but keep the same push subscription model
388+ OneSignalUserManagerImpl . sharedInstance. pushSubscriptionModelStore. getModels ( ) [ OS_PUSH_SUBSCRIPTION_MODEL_KEY] ? . subscriptionId = nil
389+ removeFromQueue ( request)
390+ }
391+ // Otherwise it is a retryable error
392+ executePendingRequests ( )
393+ }
394+ }
395+
327396 static func identifyUser( externalId: String , identityModelToIdentify: OSIdentityModel , identityModelToUpdate: OSIdentityModel ) {
328397 let request = OSRequestIdentifyUser (
329398 aliasLabel: OS_EXTERNAL_ID,
@@ -352,22 +421,21 @@ class OSUserExecutor {
352421
353422 // the anonymous user has been identified, still need to Fetch User as we cleared local data
354423 // Fetch the user only if its the current user
355- if let _ = OneSignalUserManagerImpl . sharedInstance. identityModelStore . getModel ( modelId : request. identityModelToUpdate. modelId ) {
424+ if OneSignalUserManagerImpl . sharedInstance. isCurrentUser ( request. identityModelToUpdate) {
356425 fetchUser ( aliasLabel: OS_EXTERNAL_ID, aliasId: request. aliasId, identityModel: request. identityModelToUpdate)
357426 } else {
358427 executePendingRequests ( )
359428 }
360429 } onFailure: { error in
361430 OneSignalLog . onesignalLog ( . LL_VERBOSE, message: " executeIdentifyUserRequest failed with error \( error. debugDescription) " )
362- removeFromQueue ( request)
363431 // Returns 409 if any provided (label, id) pair exists on another User, so the SDK will switch to this user.
364432 if let nsError = error as? NSError {
365433 if nsError. code == 409 {
366434 OneSignalLog . onesignalLog ( . LL_VERBOSE, message: " executeIdentifyUserRequest returned 409, failed due to alias already assigned to a different user. Now switch to this user. " )
367435
368436 removeFromQueue ( request)
369437 // Fetch the user only if its the current user
370- if let _ = OneSignalUserManagerImpl . sharedInstance. identityModelStore . getModel ( modelId : request. identityModelToUpdate. modelId ) {
438+ if OneSignalUserManagerImpl . sharedInstance. isCurrentUser ( request. identityModelToUpdate) {
371439 fetchUser ( aliasLabel: OS_EXTERNAL_ID, aliasId: request. aliasId, identityModel: request. identityModelToUpdate)
372440 // TODO: Link ^ to the new user... what was this todo for?
373441 }
@@ -581,6 +649,69 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest {
581649 }
582650}
583651
652+ class OSRequestFetchIdentityBySubscription : OneSignalRequest , OSUserRequest {
653+ var sentToClient = false
654+ let stringDescription : String
655+
656+ override var description : String {
657+ return stringDescription
658+ }
659+
660+ var identityModel : OSIdentityModel
661+ var pushSubscriptionModel : OSSubscriptionModel
662+
663+ func prepareForExecution( ) -> Bool {
664+ guard let appId = OneSignalConfigManager . getAppId ( ) else {
665+ OneSignalLog . onesignalLog ( . LL_DEBUG, message: " Cannot generate the FetchIdentityBySubscription request due to null app ID. " )
666+ return false
667+ }
668+
669+ if let subscriptionId = pushSubscriptionModel. subscriptionId {
670+ self . path = " apps/ \( appId) /subscriptions/ \( subscriptionId) /user/identity "
671+ return true
672+ } else {
673+ // This is an error, and should never happen
674+ OneSignalLog . onesignalLog ( . LL_ERROR, message: " Cannot generate the FetchIdentityBySubscription request due to null subscriptionId. " )
675+ self . path = " "
676+ return false
677+ }
678+ }
679+
680+ init ( identityModel: OSIdentityModel , pushSubscriptionModel: OSSubscriptionModel ) {
681+ self . identityModel = identityModel
682+ self . pushSubscriptionModel = pushSubscriptionModel
683+ self . stringDescription = " OSRequestFetchIdentityBySubscription with subscriptionId: \( pushSubscriptionModel. subscriptionId ?? " nil " ) "
684+ super. init ( )
685+ self . method = GET
686+ }
687+
688+ func encode( with coder: NSCoder ) {
689+ coder. encode ( identityModel, forKey: " identityModel " )
690+ coder. encode ( pushSubscriptionModel, forKey: " pushSubscriptionModel " )
691+ coder. encode ( method. rawValue, forKey: " method " ) // Encodes as String
692+ coder. encode ( timestamp, forKey: " timestamp " )
693+ }
694+
695+ required init ? ( coder: NSCoder ) {
696+ guard
697+ let identityModel = coder. decodeObject ( forKey: " identityModel " ) as? OSIdentityModel ,
698+ let pushSubscriptionModel = coder. decodeObject ( forKey: " pushSubscriptionModel " ) as? OSSubscriptionModel ,
699+ let rawMethod = coder. decodeObject ( forKey: " method " ) as? UInt32 ,
700+ let timestamp = coder. decodeObject ( forKey: " timestamp " ) as? Date
701+ else {
702+ // Log error
703+ return nil
704+ }
705+ self . identityModel = identityModel
706+ self . pushSubscriptionModel = pushSubscriptionModel
707+
708+ self . stringDescription = " OSRequestFetchIdentityBySubscription with subscriptionId: \( pushSubscriptionModel. subscriptionId ?? " nil " ) "
709+ super. init ( )
710+ self . method = HTTPMethod ( rawValue: rawMethod)
711+ self . timestamp = timestamp
712+ }
713+ }
714+
584715/**
585716 The `identityModelToIdentify` is used for the `onesignal_id` of the user we want to associate with this alias.
586717 This request will tell us if we should continue with the previous user who is now identitfied, or to change users to the one this alias already exists on.
0 commit comments