From 48f4c9aa0a95de6230d19afa437254221bd69ed3 Mon Sep 17 00:00:00 2001 From: jinliu9508 Date: Thu, 14 Nov 2024 14:16:15 -0500 Subject: [PATCH] fixup: identity verification improvement --- .../sdktest/application/MainApplication.java | 5 +- .../sdktest/model/MainActivityViewModel.java | 5 +- .../sdktest/util/SharedPreferenceUtil.java | 2 +- .../src/main/java/com/onesignal/IOneSignal.kt | 5 -- .../onesignal/IUserJwtInvalidatedListener.kt | 4 +- .../src/main/java/com/onesignal/OneSignal.kt | 7 -- .../com/onesignal/UserJwtInvalidatedEvent.kt | 3 +- .../java/com/onesignal/core/CoreModule.kt | 3 + .../core/internal/config/ConfigModel.kt | 20 ----- .../config/impl/ConfigModelStoreListener.kt | 2 - .../core/internal/http/impl/HttpClient.kt | 27 ++----- .../http/impl/HttpConnectionFactory.kt | 2 +- .../internal/http/impl/OptionalHeaders.kt | 1 - .../internal/operations/IOperationRepo.kt | 2 - .../internal/operations/impl/OperationRepo.kt | 40 +++------ .../com/onesignal/internal/OneSignalImp.kt | 81 ++++--------------- .../onesignal/user/internal/UserManager.kt | 16 ++-- .../backend/ISubscriptionBackendService.kt | 1 - .../internal/backend/IUserBackendService.kt | 1 - .../impl/SubscriptionBackendService.kt | 17 +--- .../backend/impl/UserBackendService.kt | 3 +- .../user/internal/identity/IdentityModel.kt | 2 +- .../internal/identity/IdentityModelStore.kt | 4 +- .../executors/IdentityOperationExecutor.kt | 2 - ...inUserFromSubscriptionOperationExecutor.kt | 8 +- .../executors/LoginUserOperationExecutor.kt | 4 - .../executors/RefreshUserOperationExecutor.kt | 1 - .../SubscriptionOperationExecutor.kt | 1 - .../executors/UpdateUserOperationExecutor.kt | 1 - .../service/IdentityVerificationService.kt | 52 ++++++++++++ .../internal/operations/OperationRepoTests.kt | 65 ++++++++------- .../onesignal/internal/OneSignalImpTests.kt | 14 ---- .../user/internal/UserManagerTests.kt | 68 ---------------- .../backend/IdentityBackendServiceTests.kt | 7 +- .../SubscriptionBackendServiceTests.kt | 8 +- .../backend/UserBackendServiceTests.kt | 51 ++++++++---- .../IdentityOperationExecutorTests.kt | 11 ++- .../backend/InAppBackendServiceTests.kt | 20 +++-- .../java/com/onesignal/mocks/MockHelper.kt | 1 + 39 files changed, 224 insertions(+), 343 deletions(-) create mode 100644 OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/service/IdentityVerificationService.kt diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/application/MainApplication.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/application/MainApplication.java index 69e3875ed0..37d0b6c542 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/application/MainApplication.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/application/MainApplication.java @@ -66,7 +66,7 @@ public void onCreate() { // This will reproduce result similar to Kotlin CouroutineScope.launch{}, which may potentially crash the app ExecutorService executor = Executors.newSingleThreadExecutor(); @SuppressLint({"NewApi", "LocalSuppress"}) CompletableFuture future = CompletableFuture.runAsync(() -> { - //OneSignal.getNotifications().requestPermission(true, Continue.none()); + OneSignal.getNotifications().requestPermission(true, Continue.none()); }, executor); future.join(); // Waits for the task to complete executor.shutdown(); @@ -144,9 +144,6 @@ public void onUserStateChange(@NonNull UserChangedState state) { OneSignal.addUserJwtInvalidatedListner(new IUserJwtInvalidatedListener() { @Override public void onUserJwtInvalidated(@NonNull UserJwtInvalidatedEvent event) { - // !!! For manual testing only - String jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIwMTM5YmQ2Zi00NTFmLTQzOGMtODg4Ni00ZTBmMGZlM2EwODUiLCJleHAiOjE3MjczNjkyMjIsImlkZW50aXR5Ijp7ImV4dGVybmFsX2lkIjoiamluIn0sInN1YnNjcmlwdGlvbnMiOlt7InR5cGUiOiJFbWFpbCIsInRva2VuIjoidGVzdEBkb21haW4uY29tIn0seyJ0eXBlIjoiU01TIiwidG9rZW4iOiIrMTIzNDU2NzgifSx7InR5cGUiOiJBbmRyb2lkUHVzaCIsImlkIjoiMTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDAwIn1dfQ.6XF7wRF4lLOvKr5Gd3MHv9j7U151hcBjmqSyk6nI6JVYUgt6q0YRp2j1aSJcg8VmaejzP1DouN1DpWUT_JTRXA"; - OneSignal.updateUserJwt(event.getExternalId(), jwt); Log.v(Tag.LOG_TAG, "onUserJwtInvalidated fired with ID:" + event.getExternalId()); } }); diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java index 79930c9270..dc84e28a9e 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java @@ -411,8 +411,7 @@ private void setupAppLayout() { @Override public void onSuccess(String update) { if (update != null && !update.isEmpty()) { - String jwt = "InitialJWT"; - OneSignal.login(update, jwt); + OneSignal.login(update); refreshState(); } } @@ -450,8 +449,6 @@ private void setupJWTLayout() { public void onSuccess(String update) { if (update != null && !update.isEmpty()) { OneSignal.updateUserJwt(OneSignal.getUser().getExternalId(), update); - //String jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIxNjg4ZDhmMi1kYTdmLTQ4MTUtOGVlMy05ZDEzNzg4NDgyYzgiLCJpYXQiOjE3MTQwODA4MTMsImlkZW50aXR5Ijp7ImV4dGVybmFsX2lkIjoiMjAyNDA0MjUtYWxleDQyIn0sInN1YnNjcmlwdGlvbiI6W3sidHlwZSI6IiIsImlkIjoiMmRlZGU3MzItMTEyNi00MTlkLTk5M2UtNDIzYWQyYTZiZGU5In1dfQ.rzZ-HaDm1EwxbMxd8937bWzPhPSQDDSqSzaASgZZc5A5v8g6ZQHizN9CljOmoQ4lTLm7noD6rOmR07tlZdrI5w"; - //OneSignal.login(update, jwt); refreshState(); } } diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java index e33c8eb31f..5cb286d7bf 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java @@ -24,7 +24,7 @@ public static boolean exists(Context context, String key) { } public static String getOneSignalAppId(Context context) { - return getSharedPreference(context).getString(OS_APP_ID_SHARED_PREF, "0139bd6f-451f-438c-8886-4e0f0fe3a085"); + return getSharedPreference(context).getString(OS_APP_ID_SHARED_PREF, "77e32082-ea27-42e3-a898-c72e141824ef"); } public static boolean getUserPrivacyConsent(Context context) { diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/IOneSignal.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/IOneSignal.kt index ce60849a6d..38c4d7db33 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/IOneSignal.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/IOneSignal.kt @@ -19,11 +19,6 @@ interface IOneSignal { */ val isInitialized: Boolean - /** - * Whether the security feature to authenticate your external user ids is enabled - */ - val useIdentityVerification: Boolean - /** * The user manager for accessing user-scoped * management. diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/IUserJwtInvalidatedListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/IUserJwtInvalidatedListener.kt index 7abdf10849..46d43ea292 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/IUserJwtInvalidatedListener.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/IUserJwtInvalidatedListener.kt @@ -1,10 +1,10 @@ package com.onesignal -/** TODO: complete the comment part for this listener +/** * Implement this interface and provide an instance to [OneSignal.addUserJwtInvalidatedListner] * in order to receive control when the JWT for the current user is invalidated. * - * @see [User JWT Invalidated Event | OneSignal Docs](https://documentation.onesignal.com/docs/) + * @see [User JWT Invalidated Event | OneSignal Docs](https://documentation.onesignal.com/docs/identity-verification) */ interface IUserJwtInvalidatedListener { /** diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/OneSignal.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/OneSignal.kt index 799c50c9c9..ec686a9d10 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/OneSignal.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/OneSignal.kt @@ -29,13 +29,6 @@ object OneSignal { val isInitialized: Boolean get() = oneSignal.isInitialized - /** - * Whether the security feature to authenticate your external user ids is enabled - */ - @JvmStatic - val useIdentityVerification: Boolean - get() = oneSignal.useIdentityVerification - /** * The current SDK version as a string. */ diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/UserJwtInvalidatedEvent.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/UserJwtInvalidatedEvent.kt index 986d2eb5c9..c8c37a67e9 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/UserJwtInvalidatedEvent.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/UserJwtInvalidatedEvent.kt @@ -1,9 +1,10 @@ package com.onesignal -/** TODO: jwt documentation +/** * The event passed into [IUserJwtInvalidatedListener.onUserJwtInvalidated], it provides access * to the external ID whose JWT has just been invalidated. * + * For more information https://documentation.onesignal.com/docs/identity-verification#4-handle-jwt-lifecycle-events */ class UserJwtInvalidatedEvent( val externalId: String, diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/CoreModule.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/CoreModule.kt index 6d36caaf9f..4d1a921fef 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/CoreModule.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/CoreModule.kt @@ -31,6 +31,7 @@ import com.onesignal.core.internal.preferences.IPreferencesService import com.onesignal.core.internal.preferences.impl.PreferencesService import com.onesignal.core.internal.purchases.impl.TrackAmazonPurchase import com.onesignal.core.internal.purchases.impl.TrackGooglePurchase +import com.onesignal.core.internal.startup.IBootstrapService import com.onesignal.core.internal.startup.IStartableService import com.onesignal.core.internal.time.ITime import com.onesignal.core.internal.time.impl.Time @@ -40,6 +41,7 @@ import com.onesignal.location.ILocationManager import com.onesignal.location.internal.MisconfiguredLocationManager import com.onesignal.notifications.INotificationsManager import com.onesignal.notifications.internal.MisconfiguredNotificationsManager +import com.onesignal.user.internal.service.IdentityVerificationService internal class CoreModule : IModule { override fun register(builder: ServiceBuilder) { @@ -59,6 +61,7 @@ internal class CoreModule : IModule { builder.register().provides() builder.register().provides() builder.register().provides() + builder.register().provides() // Operations builder.register().provides() diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt index 0e25c480c3..74d31c4669 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt @@ -1,8 +1,6 @@ package com.onesignal.core.internal.config -import com.onesignal.common.events.EventProducer import com.onesignal.common.modeling.Model -import com.onesignal.core.internal.backend.ParamsObject import org.json.JSONArray import org.json.JSONObject @@ -321,20 +319,6 @@ class ConfigModel : Model() { return null } - - var fetchParamsNotifier = EventProducer() - - fun addFetchParamsObserver(observer: FetchParamsObserver) { - fetchParamsNotifier.subscribe(observer) - } - - fun removeFetchParamsObserver(observer: FetchParamsObserver) { - fetchParamsNotifier.unsubscribe(observer) - } - - fun notifyFetchParams(params: ParamsObject) { - fetchParamsNotifier.fire { it.onParamsFetched(params) } - } } /** @@ -441,7 +425,3 @@ class FCMConfigModel(parentModel: Model, parentProperty: String) : Model(parentM setOptStringProperty(::apiKey.name, value) } } - -interface FetchParamsObserver { - fun onParamsFetched(params: ParamsObject) -} diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt index 8622e05c90..16144c4167 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt @@ -72,7 +72,6 @@ internal class ConfigModelStoreListener( // copy current model into new model, then override with what comes down. val config = ConfigModel() config.initializeFromModel(null, _configModelStore.model) - config.fetchParamsNotifier = _configModelStore.model.fetchParamsNotifier config.isInitializedWithRemote = true // these are always copied from the backend params @@ -105,7 +104,6 @@ internal class ConfigModelStoreListener( _configModelStore.replace(config, ModelChangeTags.HYDRATE) success = true - config.notifyFetchParams(params) } catch (ex: BackendException) { if (ex.statusCode == HttpURLConnection.HTTP_FORBIDDEN) { Logging.fatal("403 error getting OneSignal params, omitting further retries!") diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpClient.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpClient.kt index 7bf40e1669..7183c6e56d 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpClient.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpClient.kt @@ -46,39 +46,29 @@ internal class HttpClient( url: String, body: JSONObject, headers: OptionalHeaders?, - ): HttpResponse { - return makeRequest(url, "POST", body, _configModelStore.model.httpTimeout, headers) - } + ): HttpResponse = makeRequest(url, "POST", body, _configModelStore.model.httpTimeout, headers) override suspend fun get( url: String, headers: OptionalHeaders?, - ): HttpResponse { - return makeRequest(url, null, null, _configModelStore.model.httpGetTimeout, headers) - } + ): HttpResponse = makeRequest(url, null, null, _configModelStore.model.httpGetTimeout, headers) override suspend fun put( url: String, body: JSONObject, headers: OptionalHeaders?, - ): HttpResponse { - return makeRequest(url, "PUT", body, _configModelStore.model.httpTimeout, headers) - } + ): HttpResponse = makeRequest(url, "PUT", body, _configModelStore.model.httpTimeout, headers) override suspend fun patch( url: String, body: JSONObject, headers: OptionalHeaders?, - ): HttpResponse { - return makeRequest(url, "PATCH", body, _configModelStore.model.httpTimeout, headers) - } + ): HttpResponse = makeRequest(url, "PATCH", body, _configModelStore.model.httpTimeout, headers) override suspend fun delete( url: String, headers: OptionalHeaders?, - ): HttpResponse { - return makeRequest(url, "DELETE", null, _configModelStore.model.httpTimeout, headers) - } + ): HttpResponse = makeRequest(url, "DELETE", null, _configModelStore.model.httpTimeout, headers) private suspend fun makeRequest( url: String, @@ -151,15 +141,10 @@ internal class HttpClient( con.setRequestProperty("SDK-Version", "onesignal/android/" + OneSignalUtils.SDK_VERSION) val jwt = headers?.jwt - if (!jwt.isNullOrEmpty()) { + if (jwt != null) { con.setRequestProperty("Authorization", "Bearer $jwt") } - val deviceAuthPushToken = headers?.deviceAuthPushToken - if (_configModelStore.model.useIdentityVerification && !deviceAuthPushToken.isNullOrEmpty()) { - con.setRequestProperty("Device-Auth-Push-Token", "Basic $deviceAuthPushToken") - } - if (OneSignalWrapper.sdkType != null && OneSignalWrapper.sdkVersion != null) { con.setRequestProperty("SDK-Wrapper", "onesignal/${OneSignalWrapper.sdkType}/${OneSignalWrapper.sdkVersion}") } diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpConnectionFactory.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpConnectionFactory.kt index 4921f72a64..b5f7d01746 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpConnectionFactory.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpConnectionFactory.kt @@ -10,6 +10,6 @@ internal class HttpConnectionFactory( ) : IHttpConnectionFactory { @Throws(IOException::class) override fun newHttpURLConnection(url: String): HttpURLConnection { - return URL("https://staging.onesignal.com/api/v1/" + url).openConnection() as HttpURLConnection + return URL(_configModelStore.model.apiUrl + url).openConnection() as HttpURLConnection } } diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/OptionalHeaders.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/OptionalHeaders.kt index 2e1eb8ea04..83e758599f 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/OptionalHeaders.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/OptionalHeaders.kt @@ -18,5 +18,4 @@ data class OptionalHeaders( */ val sessionDuration: Long? = null, val jwt: String? = null, - val deviceAuthPushToken: String? = null, ) diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/IOperationRepo.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/IOperationRepo.kt index 3ab6059605..d2dceea5c3 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/IOperationRepo.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/IOperationRepo.kt @@ -42,8 +42,6 @@ interface IOperationRepo { suspend fun awaitInitialized() fun forceExecuteOperations() - - fun setPaused(paused: Boolean) } // Extension function so the syntax containsInstanceOf() can be used over diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt index a2420c7f32..83912efd5a 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt @@ -99,7 +99,6 @@ internal class OperationRepo( coroutineScope.launch { // load saved operations first then start processing the queue to ensure correct operation order loadSavedOperations() - paused = false processQueueForever() } } @@ -191,10 +190,6 @@ internal class OperationRepo( waiter.wake(LoopWaiterMessage(false)) } - override fun setPaused(paused: Boolean) { - this.paused = paused - } - /** * Waits until a new operation is enqueued, then wait an additional * amount of time afterwards, so operations can be grouped/batched. @@ -270,6 +265,8 @@ internal class OperationRepo( } ExecutionResult.FAIL_UNAUTHORIZED -> { Logging.error("Operation execution failed with invalid jwt") + _identityModelStore.invalidateJwt() + // add back all operations to the front of the queue to be re-executed. synchronized(queue) { ops.reversed().forEach { queue.add(0, it) } @@ -359,31 +356,20 @@ internal class OperationRepo( internal fun getNextOps(bucketFilter: Int): List? { return synchronized(queue) { - var startingOp: OperationQueueItem? = null - // Search for the first operation that is qualified to execute - for (queueItem in queue) { - val operation = queueItem.operation - - // Ensure the operation is in an executable state - if (!operation.canStartExecute || - !_newRecordState.canAccess( - operation.applyToRecordId, - ) || queueItem.bucket > bucketFilter - ) { - continue - } + // Ensure the operation does not have empty JWT if identity verification is on + if (_configModelStore.model.useIdentityVerification && + _identityModelStore.model.jwtToken == null + ) { + return null + } - // Ensure the operation does not have empty JWT if identity verification is on - if (_configModelStore.model.useIdentityVerification && - _identityModelStore.model.jwtToken.isNullOrEmpty() - ) { - continue + val startingOp = + queue.firstOrNull { + it.operation.canStartExecute && + _newRecordState.canAccess(it.operation.applyToRecordId) && + it.bucket <= bucketFilter } - startingOp = queueItem - break - } - if (startingOp != null) { queue.remove(startingOp) getGroupableOperations(startingOp) diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt index ef08a6c05b..eee48bc89b 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt @@ -19,10 +19,8 @@ import com.onesignal.common.threading.suspendifyOnThread import com.onesignal.core.CoreModule import com.onesignal.core.internal.application.IApplicationService import com.onesignal.core.internal.application.impl.ApplicationService -import com.onesignal.core.internal.backend.ParamsObject import com.onesignal.core.internal.config.ConfigModel import com.onesignal.core.internal.config.ConfigModelStore -import com.onesignal.core.internal.config.FetchParamsObserver import com.onesignal.core.internal.operations.IOperationRepo import com.onesignal.core.internal.preferences.IPreferencesService import com.onesignal.core.internal.preferences.PreferenceOneSignalKeys @@ -48,7 +46,6 @@ import com.onesignal.user.internal.identity.IdentityModelStore import com.onesignal.user.internal.operations.LoginUserFromSubscriptionOperation import com.onesignal.user.internal.operations.LoginUserOperation import com.onesignal.user.internal.operations.TransferSubscriptionOperation -import com.onesignal.user.internal.operations.UpdateSubscriptionOperation import com.onesignal.user.internal.properties.PropertiesModel import com.onesignal.user.internal.properties.PropertiesModelStore import com.onesignal.user.internal.subscriptions.SubscriptionModel @@ -60,8 +57,6 @@ import org.json.JSONObject internal class OneSignalImp : IOneSignal, IServiceProvider { override val sdkVersion: String = OneSignalUtils.SDK_VERSION override var isInitialized: Boolean = false - override val useIdentityVerification: Boolean - get() = configModel?.useIdentityVerification ?: true override var consentRequired: Boolean get() = configModel?.consentRequired ?: (_consentRequired == true) @@ -153,6 +148,8 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { private var _disableGMSMissingPrompt: Boolean? = null private val initLock: Any = Any() private val loginLock: Any = Any() + private val useIdentityVerification: Boolean + get() = configModel?.useIdentityVerification ?: true private val listOfModules = listOf( @@ -211,7 +208,6 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { // Give the logging singleton access to the application service to support visual logging. Logging.applicationService = applicationService - // get the current config model, if there is one configModel = services.getService().model sessionModel = services.getService().model @@ -253,7 +249,6 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { // bootstrap services startupService.bootstrap() - resumeOperationRepoAfterFetchParams(configModel!!) if (forceCreateUser || !identityModelStore!!.model.hasProperty(IdentityConstants.ONESIGNAL_ID)) { val legacyPlayerId = preferencesService!!.getString( @@ -313,7 +308,8 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { createAndSwitchToNewUser(suppressBackendOperation = true) - // ** No longer allowed when identity verification is on + // This operation will be dropped if identity verification is on at the time the operation + // is being processed operationRepo!!.enqueue( LoginUserFromSubscriptionOperation( configModel!!.appId, @@ -365,7 +361,6 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { return } - // TODO: Set JWT Token for all future requests. createAndSwitchToNewUser(suppressBackendOperation = false) { identityModel, _ -> identityModel.externalId = externalId identityModel.jwtToken = jwtBearerToken @@ -383,30 +378,16 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { // provide a callback to the caller when we can absolutely say the user is logged // in, so they may take action on their own backend. - val result = - when (useIdentityVerification) { - true -> { - operationRepo!!.enqueue( - LoginUserOperation( - configModel!!.appId, - identityModelStore!!.model.onesignalId, - identityModelStore!!.model.externalId, - ), - ) - } - else -> { - operationRepo!!.enqueueAndWait( - LoginUserOperation( - configModel!!.appId, - newIdentityOneSignalId, - externalId, - if (currentIdentityExternalId == null) currentIdentityOneSignalId else null, - ), - ) - } - } + val operation = + LoginUserOperation( + configModel!!.appId, + identityModelStore?.model?.onesignalId ?: newIdentityOneSignalId, + identityModelStore?.model?.externalId ?: externalId, + if (!useIdentityVerification && currentIdentityExternalId == null) currentIdentityOneSignalId else null, + ) - if (result == false) { + val result = operationRepo!!.enqueueAndWait(operation) + if (!result) { Logging.log(LogLevel.ERROR, "Could not login user") } } @@ -430,9 +411,7 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { if (useIdentityVerification) { // disable subscription if identity verification is on - operationRepo!!.enqueue( - UpdateSubscriptionOperation(), - ) + user.pushSubscription.optOut() } else { // login to the anonymous user if identity verification is off operationRepo!!.enqueue( @@ -492,7 +471,7 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { modify(identityModel, propertiesModel) } - if (!identityModel.jwtToken.isNullOrEmpty()) { + if (identityModel.jwtToken != null) { setupNewSubscription(identityModel, propertiesModel, suppressBackendOperation, sdkId) } @@ -542,13 +521,8 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { if (suppressBackendOperation) { subscriptionModelStore!!.replaceAll(subscriptions, ModelChangeTags.NO_PROPOGATE) - } else if (currentPushSubscription != null && ( - !useIdentityVerification || useIdentityVerification && - !IDManager.isLocalId( - currentPushSubscription.id, - ) - ) - ) { + } else if (currentPushSubscription != null && (!useIdentityVerification || !IDManager.isLocalId(currentPushSubscription.id))) { + // operationRepo!!.enqueue(TransferSubscriptionOperation(configModel!!.appId, currentPushSubscription.id, sdkId)) subscriptionModelStore!!.replaceAll(subscriptions, ModelChangeTags.NO_PROPOGATE) } else { @@ -556,27 +530,6 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { } } - private fun resumeOperationRepoAfterFetchParams(configModel: ConfigModel) { - configModel.addFetchParamsObserver( - object : FetchParamsObserver { - override fun onParamsFetched(params: ParamsObject) { - // resume operations if identity verification is turned off or a jwt is cached - if (params.useIdentityVerification == false || identityModelStore!!.model.jwtToken != null) { - operationRepo!!.enqueue( - LoginUserOperation( - configModel!!.appId, - identityModelStore!!.model.onesignalId, - identityModelStore!!.model.externalId, - ), - ) - } else { - Logging.log(LogLevel.ERROR, "A valid JWT is required for user ${identityModelStore!!.model.externalId}.") - } - } - }, - ) - } - override fun hasService(c: Class): Boolean = services.hasService(c) override fun getService(c: Class): T = services.getService(c) diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserManager.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserManager.kt index 649d998d12..8183361d49 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserManager.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserManager.kt @@ -1,7 +1,6 @@ package com.onesignal.user.internal import com.onesignal.IUserJwtInvalidatedListener -import com.onesignal.OneSignal import com.onesignal.UserJwtInvalidatedEvent import com.onesignal.common.IDManager import com.onesignal.common.OneSignalUtils @@ -252,12 +251,12 @@ internal open class UserManager( } override fun addUserJwtInvalidatedListener(listener: IUserJwtInvalidatedListener) { - Logging.debug("OneSignal.addClickListener(listener: $listener)") + Logging.debug("OneSignal.addUserJwtInvalidatedListener(listener: $listener)") jwtInvalidatedCallback.subscribe(listener) } override fun removeUserJwtInvalidatedListener(listener: IUserJwtInvalidatedListener) { - Logging.debug("OneSignal.removeClickListener(listener: $listener)") + Logging.debug("OneSignal.removeUserJwtInvalidatedListener(listener: $listener)") jwtInvalidatedCallback.unsubscribe(listener) } @@ -279,17 +278,18 @@ internal open class UserManager( } IdentityConstants.JWT_TOKEN -> { // Fire the event when the JWT has been invalidated. - val oldJwt = args.oldValue.toString() - val newJwt = args.newValue.toString() + val oldJwt = args.oldValue + val newJwt = args.newValue - // prevent same JWT from being invalidated twice in a row - if (OneSignal.useIdentityVerification && jwtTokenInvalidated != oldJwt && newJwt.isEmpty()) { + // When newJwt is equals to null, we are invalidating JWT for the current user. + // We need to prevent same JWT from being invalidated twice in a row. + if (jwtTokenInvalidated != oldJwt && newJwt == null) { jwtInvalidatedCallback.fire { it.onUserJwtInvalidated(UserJwtInvalidatedEvent(externalId)) } } - jwtTokenInvalidated = oldJwt + jwtTokenInvalidated = oldJwt as String? } } } diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt index e33bdd93bd..172d91d8e8 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt @@ -77,6 +77,5 @@ interface ISubscriptionBackendService { suspend fun getIdentityFromSubscription( appId: String, subscriptionId: String, - jwt: String? = null, ): Map } diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/IUserBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/IUserBackendService.kt index f045811b96..b849fc4c42 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/IUserBackendService.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/IUserBackendService.kt @@ -25,7 +25,6 @@ interface IUserBackendService { subscriptions: List, properties: Map, jwt: String? = null, - deviceAuthPushToken: String? = null, ): CreateUserResponse // TODO: Change to send only the push subscription, optimally diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt index 7bafe2a19c..0f2d45adb1 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt @@ -1,6 +1,5 @@ package com.onesignal.user.internal.backend.impl -import android.util.Base64 import com.onesignal.common.consistency.RywData import com.onesignal.common.exceptions.BackendException import com.onesignal.common.safeJSONObject @@ -27,16 +26,11 @@ internal class SubscriptionBackendService( jsonSubscription.remove("id") val requestJSON = JSONObject().put("subscription", jsonSubscription) - val base64Token = - Base64.encodeToString( - subscription.token?.toByteArray(charset("UTF-8")), - Base64.NO_WRAP, - ) val response = _httpClient.post( "apps/$appId/users/by/$aliasLabel/$aliasValue/subscriptions", requestJSON, - OptionalHeaders(jwt = jwt, deviceAuthPushToken = base64Token), + OptionalHeaders(jwt = jwt), ) if (!response.isSuccess) { @@ -69,16 +63,10 @@ internal class SubscriptionBackendService( JSONObject() .put("subscription", JSONConverter.convertToJSON(subscription)) - val base64Token = - Base64.encodeToString( - subscription.token?.toByteArray(charset("UTF-8")), - Base64.NO_WRAP, - ) val response = _httpClient.patch( "apps/$appId/subscriptions/$subscriptionId", requestJSON, - OptionalHeaders(deviceAuthPushToken = base64Token), ) if (!response.isSuccess) { @@ -130,9 +118,8 @@ internal class SubscriptionBackendService( override suspend fun getIdentityFromSubscription( appId: String, subscriptionId: String, - jwt: String?, ): Map { - val response = _httpClient.get("apps/$appId/subscriptions/$subscriptionId/user/identity", OptionalHeaders(jwt = jwt)) + val response = _httpClient.get("apps/$appId/subscriptions/$subscriptionId/user/identity") if (!response.isSuccess) { throw BackendException(response.statusCode, response.payload, response.retryAfterSeconds) diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/UserBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/UserBackendService.kt index 835bcc50e6..f87a4e24a8 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/UserBackendService.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/UserBackendService.kt @@ -23,7 +23,6 @@ internal class UserBackendService( subscriptions: List, properties: Map, jwt: String?, - deviceAuthPushToken: String?, ): CreateUserResponse { val requestJSON = JSONObject() @@ -46,7 +45,7 @@ internal class UserBackendService( _httpClient.post( "apps/$appId/users", requestJSON, - OptionalHeaders(jwt = jwt, deviceAuthPushToken = deviceAuthPushToken), + OptionalHeaders(jwt = jwt), ) if (!response.isSuccess) { diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModel.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModel.kt index 70a5015f2e..d0017862fd 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModel.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModel.kt @@ -38,6 +38,6 @@ class IdentityModel : MapModel() { var jwtToken: String? get() = getOptStringProperty(IdentityConstants.JWT_TOKEN) set(value) { - setOptStringProperty(IdentityConstants.JWT_TOKEN, value, ModelChangeTags.NO_PROPOGATE) + setOptStringProperty(IdentityConstants.JWT_TOKEN, value, ModelChangeTags.NO_PROPOGATE, true) } } diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModelStore.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModelStore.kt index f7e5270ae4..37ea791c7d 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModelStore.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModelStore.kt @@ -9,12 +9,12 @@ open class IdentityModelStore(prefs: IPreferencesService) : SingletonModelStore< SimpleModelStore({ IdentityModel() }, "identity", prefs), ) { fun invalidateJwt() { - model.jwtToken = "" + model.jwtToken = null } // Use externalId instead of onesignalId when a jwt is present fun getIdentityAlias(): Pair { - if (model.jwtToken.isNullOrEmpty()) { + if (model.jwtToken == null) { return Pair(IdentityConstants.ONESIGNAL_ID, model.onesignalId) } diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/IdentityOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/IdentityOperationExecutor.kt index 443b900f7b..a2c59553fc 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/IdentityOperationExecutor.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/IdentityOperationExecutor.kt @@ -69,7 +69,6 @@ internal class IdentityOperationExecutor( NetworkUtils.ResponseStatusType.CONFLICT -> ExecutionResponse(ExecutionResult.FAIL_CONFLICT, retryAfterSeconds = ex.retryAfterSeconds) NetworkUtils.ResponseStatusType.UNAUTHORIZED -> { - _identityModelStore.invalidateJwt() return ExecutionResponse(ExecutionResult.FAIL_UNAUTHORIZED) } NetworkUtils.ResponseStatusType.MISSING -> { @@ -116,7 +115,6 @@ internal class IdentityOperationExecutor( NetworkUtils.ResponseStatusType.INVALID -> ExecutionResponse(ExecutionResult.FAIL_NORETRY) NetworkUtils.ResponseStatusType.UNAUTHORIZED -> { - _identityModelStore.invalidateJwt() return ExecutionResponse(ExecutionResult.FAIL_UNAUTHORIZED) } NetworkUtils.ResponseStatusType.MISSING -> { diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserFromSubscriptionOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserFromSubscriptionOperationExecutor.kt index f9cff90c21..719bc9228a 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserFromSubscriptionOperationExecutor.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserFromSubscriptionOperationExecutor.kt @@ -3,6 +3,7 @@ package com.onesignal.user.internal.operations.impl.executors import com.onesignal.common.NetworkUtils import com.onesignal.common.exceptions.BackendException import com.onesignal.common.modeling.ModelChangeTags +import com.onesignal.core.internal.config.ConfigModelStore import com.onesignal.core.internal.operations.ExecutionResponse import com.onesignal.core.internal.operations.ExecutionResult import com.onesignal.core.internal.operations.IOperationExecutor @@ -20,6 +21,7 @@ internal class LoginUserFromSubscriptionOperationExecutor( private val _subscriptionBackend: ISubscriptionBackendService, private val _identityModelStore: IdentityModelStore, private val _propertiesModelStore: PropertiesModelStore, + private val _configModelStore: ConfigModelStore, ) : IOperationExecutor { override val operations: List get() = listOf(LOGIN_USER_FROM_SUBSCRIPTION_USER) @@ -43,11 +45,14 @@ internal class LoginUserFromSubscriptionOperationExecutor( private suspend fun loginUser(loginUserOp: LoginUserFromSubscriptionOperation): ExecutionResponse { try { // Not allowed when identity verification is on + if (_configModelStore.model.useIdentityVerification) { + return ExecutionResponse(ExecutionResult.FAIL_NORETRY) + } + val identities = _subscriptionBackend.getIdentityFromSubscription( loginUserOp.appId, loginUserOp.subscriptionId, - _identityModelStore.model.jwtToken, ) val backendOneSignalId = identities.getOrDefault(IdentityConstants.ONESIGNAL_ID, null) @@ -85,7 +90,6 @@ internal class LoginUserFromSubscriptionOperationExecutor( NetworkUtils.ResponseStatusType.RETRYABLE -> ExecutionResponse(ExecutionResult.FAIL_RETRY) NetworkUtils.ResponseStatusType.UNAUTHORIZED -> { - _identityModelStore.invalidateJwt() ExecutionResponse(ExecutionResult.FAIL_UNAUTHORIZED) } else -> diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt index d32488fa9a..537a60affb 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt @@ -127,7 +127,6 @@ internal class LoginUserOperationExecutor( createUser(loginUserOp, operations) } ExecutionResult.FAIL_UNAUTHORIZED -> { - _identityModelStore.invalidateJwt() ExecutionResponse(result.result) } else -> ExecutionResponse(result.result) @@ -164,7 +163,6 @@ internal class LoginUserOperationExecutor( try { val subscriptionList = subscriptions.toList() - val pushSubscription = subscriptions.values.find { it.type == SubscriptionObjectType.ANDROID_PUSH } val response = _userBackend.createUser( createUserOperation.appId, @@ -174,7 +172,6 @@ internal class LoginUserOperationExecutor( }, properties, _identityModelStore.model.jwtToken, - pushSubscription?.token, ) val idTranslations = mutableMapOf() // Add the "local-to-backend" ID translation to the IdentifierTranslator for any operations that were @@ -228,7 +225,6 @@ internal class LoginUserOperationExecutor( NetworkUtils.ResponseStatusType.RETRYABLE -> ExecutionResponse(ExecutionResult.FAIL_RETRY, retryAfterSeconds = ex.retryAfterSeconds) NetworkUtils.ResponseStatusType.UNAUTHORIZED -> { - _identityModelStore.invalidateJwt() ExecutionResponse(ExecutionResult.FAIL_UNAUTHORIZED) } else -> diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt index 5bc703d896..cb987c3a64 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt @@ -152,7 +152,6 @@ internal class RefreshUserOperationExecutor( NetworkUtils.ResponseStatusType.RETRYABLE -> ExecutionResponse(ExecutionResult.FAIL_RETRY, retryAfterSeconds = ex.retryAfterSeconds) NetworkUtils.ResponseStatusType.UNAUTHORIZED -> { - _identityModelStore.invalidateJwt() ExecutionResponse(ExecutionResult.FAIL_UNAUTHORIZED) } NetworkUtils.ResponseStatusType.MISSING -> { diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt index cb98e4d8d0..7cc483a727 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt @@ -153,7 +153,6 @@ internal class SubscriptionOperationExecutor( -> ExecutionResponse(ExecutionResult.FAIL_NORETRY) NetworkUtils.ResponseStatusType.UNAUTHORIZED -> { - _identityModelStore.invalidateJwt() ExecutionResponse(ExecutionResult.FAIL_UNAUTHORIZED, retryAfterSeconds = ex.retryAfterSeconds) } NetworkUtils.ResponseStatusType.MISSING -> { diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/UpdateUserOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/UpdateUserOperationExecutor.kt index 55900da7a7..aeccd32177 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/UpdateUserOperationExecutor.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/UpdateUserOperationExecutor.kt @@ -197,7 +197,6 @@ internal class UpdateUserOperationExecutor( NetworkUtils.ResponseStatusType.RETRYABLE -> ExecutionResponse(ExecutionResult.FAIL_RETRY, retryAfterSeconds = ex.retryAfterSeconds) NetworkUtils.ResponseStatusType.UNAUTHORIZED -> { - _identityModelStore.invalidateJwt() ExecutionResponse(ExecutionResult.FAIL_UNAUTHORIZED, retryAfterSeconds = ex.retryAfterSeconds) } NetworkUtils.ResponseStatusType.MISSING -> { diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/service/IdentityVerificationService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/service/IdentityVerificationService.kt new file mode 100644 index 0000000000..6414b9301c --- /dev/null +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/service/IdentityVerificationService.kt @@ -0,0 +1,52 @@ +package com.onesignal.user.internal.service + +import com.onesignal.common.modeling.ISingletonModelStoreChangeHandler +import com.onesignal.common.modeling.ModelChangeTags +import com.onesignal.common.modeling.ModelChangedArgs +import com.onesignal.core.internal.config.ConfigModel +import com.onesignal.core.internal.config.ConfigModelStore +import com.onesignal.core.internal.operations.IOperationRepo +import com.onesignal.core.internal.startup.IBootstrapService +import com.onesignal.debug.LogLevel +import com.onesignal.debug.internal.logging.Logging +import com.onesignal.user.internal.identity.IdentityModelStore +import com.onesignal.user.internal.operations.LoginUserOperation + +internal class IdentityVerificationService( + private val _configModelStore: ConfigModelStore, + private val _identityModelStore: IdentityModelStore, + private val opRepo: IOperationRepo, +) : IBootstrapService, ISingletonModelStoreChangeHandler { + override fun bootstrap() { + _configModelStore.subscribe(this) + } + + override fun onModelReplaced( + model: ConfigModel, + tag: String, + ) { + if (tag != ModelChangeTags.HYDRATE) { + return + } + if (model.useIdentityVerification && _identityModelStore.model.jwtToken == null) { + Logging.log(LogLevel.INFO, "A valid JWT is required for user ${_identityModelStore.model.externalId}.") + return + } + + // calling login either identity verification is turned off or a jwt is cached + opRepo.enqueue( + LoginUserOperation( + _configModelStore.model.appId, + _identityModelStore.model.onesignalId, + _identityModelStore.model.externalId, + ), + ) + } + + override fun onModelUpdated( + args: ModelChangedArgs, + tag: String, + ) { + // left empty intentionally + } +} diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt index 85acc12b93..eeeca3afd2 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt @@ -12,6 +12,7 @@ import com.onesignal.mocks.MockHelper import com.onesignal.user.internal.operations.ExecutorMocks.Companion.getNewRecordState import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe import io.mockk.CapturingSlot import io.mockk.coEvery import io.mockk.coVerify @@ -36,7 +37,7 @@ private class Mocks { val identityModelStore = MockHelper.identityModelStore { - it.jwtToken = "" + it.jwtToken = null it.externalId = "externalId1" } @@ -694,44 +695,50 @@ class OperationRepoTests : FunSpec({ } test("operations that need to be identity verified cannot execute until JWT is provided") { - // Given val mocks = Mocks() - val waiter = Waiter() + mocks.configModelStore.model.useIdentityVerification = true + val operation = mockOperation() + val opRepo = mocks.operationRepo - every { mocks.configModelStore.model.useIdentityVerification } returns true // set identity verification on - every { mocks.identityModelStore.model.jwtToken } returns null // jwt is initially unset - every { mocks.operationModelStore.remove(any()) } answers {} andThenAnswer { waiter.wake() } + // When JWT is not supplied + opRepo.start() + opRepo.enqueue(operation) + opRepo.forceExecuteOperations() + val responseBeforeJWT = + withTimeoutOrNull(100) { + opRepo.enqueueAndWait(mockOperation()) + } - val operation1 = mockOperation("operationId1") - val operation2 = mockOperation("operationId2") + // Then response should be null + responseBeforeJWT shouldBe null + + // When JWT is updated + mocks.identityModelStore.model.jwtToken = "123" + val opToExecute = opRepo.getNextOps(0) - operation1.setStringProperty("externalId", "externalId1") - operation2.setStringProperty("externalId", "externalId1") + // Operation is ready to execute + opToExecute shouldNotBe null + } + + test("JWT will be invalidated when a FAIL_UNAUTHORIZED response is returned") { + // Given + val mocks = Mocks() + val opRepo = mocks.operationRepo + val identityModelStore = mocks.identityModelStore + coEvery { opRepo.delayBeforeNextExecution(any(), any()) } just runs + coEvery { + mocks.executor.execute(any()) + } returns ExecutionResponse(ExecutionResult.FAIL_UNAUTHORIZED) andThen ExecutionResponse(ExecutionResult.SUCCESS) + val operation = mockOperation() // When - mocks.operationRepo.enqueue(operation1) - mocks.operationRepo.enqueue(operation2) mocks.operationRepo.start() - - waiter.waitForWake() + mocks.operationRepo.enqueueAndWait(operation) // Then coVerifyOrder { - mocks.operationModelStore.add(operation1) - mocks.operationModelStore.add(operation2) - } - - // - coVerify(exactly = 0) { - mocks.executor.execute( - withArg { - it.count() shouldBe 2 - it[0] shouldBe operation1 - it[1] shouldBe operation2 - }, - ) - mocks.operationModelStore.remove("operationId1") - mocks.operationModelStore.remove("operationId2") + mocks.executor.execute(listOf(operation)) + identityModelStore.invalidateJwt() } } }) { diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/internal/OneSignalImpTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/internal/OneSignalImpTests.kt index 735a0f0570..891139a411 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/internal/OneSignalImpTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/internal/OneSignalImpTests.kt @@ -1,12 +1,10 @@ package com.onesignal.internal -import android.content.Context import com.onesignal.debug.LogLevel import com.onesignal.debug.internal.logging.Logging import io.kotest.assertions.throwables.shouldThrowUnit import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe -import io.mockk.mockk class OneSignalImpTests : FunSpec({ beforeAny { @@ -88,16 +86,4 @@ class OneSignalImpTests : FunSpec({ } } } - - test("When identity verification is on and no user is created, calling initWithContext will create a new user") { - // Given - val os = OneSignalImp() - val appId = "tempAppId" - val context = mockk() - - // When - os.initWithContext(context, appId) - - // TODO - } }) diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/UserManagerTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/UserManagerTests.kt index df32736d4f..de777eafe1 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/UserManagerTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/UserManagerTests.kt @@ -1,31 +1,17 @@ package com.onesignal.user.internal -import com.onesignal.IUserJwtInvalidatedListener import com.onesignal.core.internal.language.ILanguageContext -import com.onesignal.core.internal.operations.ExecutionResponse -import com.onesignal.core.internal.operations.ExecutionResult -import com.onesignal.core.internal.operations.Operation import com.onesignal.mocks.MockHelper -import com.onesignal.user.internal.backend.CreateUserResponse -import com.onesignal.user.internal.backend.IUserBackendService -import com.onesignal.user.internal.backend.IdentityConstants -import com.onesignal.user.internal.backend.PropertiesObject -import com.onesignal.user.internal.operations.LoginUserOperation -import com.onesignal.user.internal.operations.impl.executors.IdentityOperationExecutor -import com.onesignal.user.internal.operations.impl.executors.LoginUserOperationExecutor import com.onesignal.user.internal.subscriptions.ISubscriptionManager import com.onesignal.user.internal.subscriptions.SubscriptionList -import com.onesignal.user.internal.subscriptions.SubscriptionModelStore import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import io.mockk.coEvery import io.mockk.every import io.mockk.just import io.mockk.mockk import io.mockk.runs import io.mockk.slot -import io.mockk.spyk import io.mockk.verify class UserManagerTests : FunSpec({ @@ -206,58 +192,4 @@ class UserManagerTests : FunSpec({ verify(exactly = 1) { mockSubscriptionManager.addSmsSubscription("+15558675309") } verify(exactly = 1) { mockSubscriptionManager.removeSmsSubscription("+15558675309") } } - - test("login user with jwt calls onUserJwtInvalidated() when the jwt is unauthorized") { - // Given - val appId = "appId" - val localOneSignalId = "local-onesignalId" - val remoteOneSignalId = "remote-onesignalId" - - // mock components - val mockSubscriptionManager = mockk() - val mockIdentityModelStore = MockHelper.identityModelStore() - val mockPropertiesModelStore = MockHelper.propertiesModelStore() - val mockSubscriptionsModelStore = mockk() - val mockLanguageContext = MockHelper.languageContext() - - // mock backend service - val mockUserBackendService = mockk() - coEvery { mockUserBackendService.createUser(any(), any(), any(), any()) } returns - CreateUserResponse(mapOf(IdentityConstants.ONESIGNAL_ID to remoteOneSignalId), PropertiesObject(), listOf()) - - // mock operation for login user - val mockIdentityOperationExecutor = mockk() - coEvery { mockIdentityOperationExecutor.execute(any()) } returns - ExecutionResponse(ExecutionResult.FAIL_UNAUTHORIZED) - val loginUserOperationExecutor = - LoginUserOperationExecutor( - mockIdentityOperationExecutor, - MockHelper.applicationService(), - MockHelper.deviceService(), - mockUserBackendService, - mockIdentityModelStore, - mockPropertiesModelStore, - mockSubscriptionsModelStore, - MockHelper.configModelStore(), - mockLanguageContext, - ) - val operations = listOf(LoginUserOperation(appId, localOneSignalId, "externalId", "existingOneSignalId")) - - // mock user manager with jwtInvalidatedListener added - val userManager = - UserManager(mockSubscriptionManager, mockIdentityModelStore, mockPropertiesModelStore, mockLanguageContext) - mockIdentityModelStore.subscribe(userManager) - val spyJwtInvalidatedListener = spyk() - userManager.addUserJwtInvalidatedListener(spyJwtInvalidatedListener) - - // When - val response = loginUserOperationExecutor.execute(operations) - - // Then - userManager.jwtInvalidatedCallback.hasSubscribers shouldBe true - response.result shouldBe ExecutionResult.FAIL_UNAUTHORIZED - verify(exactly = 1) { mockIdentityModelStore.invalidateJwt() } - // Note: set the default value of useIdentityVerification in OneSignalImp.kt to pass the test - verify(exactly = 1) { spyJwtInvalidatedListener.onUserJwtInvalidated(any()) } - } }) diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/IdentityBackendServiceTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/IdentityBackendServiceTests.kt index d66ee47ebf..36c99e652f 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/IdentityBackendServiceTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/IdentityBackendServiceTests.kt @@ -21,7 +21,7 @@ class IdentityBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(200, "{ identity: { aliasKey1: \"aliasValue1\"} }") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(200, "{ identity: { aliasKey1: \"aliasValue1\"} }") val identityBackendService = IdentityBackendService(spyHttpClient) val identities = mapOf("aliasKey1" to "aliasValue1") @@ -38,6 +38,7 @@ class IdentityBackendServiceTests : FunSpec({ it.getJSONObject("identity").has("aliasKey1") shouldBe true it.getJSONObject("identity").getString("aliasKey1") shouldBe "aliasValue1" }, + any(), ) } } @@ -48,7 +49,7 @@ class IdentityBackendServiceTests : FunSpec({ val aliasValue = "11111111-1111-1111-1111-111111111111" val aliasToDelete = "aliasKey1" val spyHttpClient = mockk() - coEvery { spyHttpClient.delete(any()) } returns HttpResponse(200, "") + coEvery { spyHttpClient.delete(any(), any()) } returns HttpResponse(200, "") val identityBackendService = IdentityBackendService(spyHttpClient) // When @@ -56,7 +57,7 @@ class IdentityBackendServiceTests : FunSpec({ // Then coVerify { - spyHttpClient.delete("apps/appId/users/by/$aliasLabel/$aliasValue/identity/$aliasToDelete") + spyHttpClient.delete("apps/appId/users/by/$aliasLabel/$aliasValue/identity/$aliasToDelete", any()) } } }) diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/SubscriptionBackendServiceTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/SubscriptionBackendServiceTests.kt index 4b6ba88968..a28002a45b 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/SubscriptionBackendServiceTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/SubscriptionBackendServiceTests.kt @@ -27,7 +27,9 @@ class SubscriptionBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.post(any(), any()) } returns HttpResponse(202, "{ \"subscription\": { id: \"subscriptionId\" }, \"ryw_token\": \"123\"}") + coEvery { + spyHttpClient.post(any(), any(), any()) + } returns HttpResponse(202, "{ \"subscription\": { id: \"subscriptionId\" }, \"ryw_token\": \"123\"}") val subscriptionBackendService = SubscriptionBackendService(spyHttpClient) // When @@ -55,6 +57,7 @@ class SubscriptionBackendServiceTests : FunSpec({ sub.getBoolean("enabled") shouldBe true sub.getInt("notification_types") shouldBe 1 }, + any(), ) } } @@ -64,7 +67,7 @@ class SubscriptionBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.post(any(), any()) } returns HttpResponse(404, "NOT FOUND") + coEvery { spyHttpClient.post(any(), any(), any()) } returns HttpResponse(404, "NOT FOUND") val subscriptionBackendService = SubscriptionBackendService(spyHttpClient) // When @@ -100,6 +103,7 @@ class SubscriptionBackendServiceTests : FunSpec({ sub.getBoolean("enabled") shouldBe true sub.getInt("notification_types") shouldBe 1 }, + any(), ) } } diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/UserBackendServiceTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/UserBackendServiceTests.kt index f8b5839356..81a607a80d 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/UserBackendServiceTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/UserBackendServiceTests.kt @@ -22,7 +22,7 @@ class UserBackendServiceTests : FunSpec({ test("create user with nothing throws an exception") { // Given val spyHttpClient = mockk() - coEvery { spyHttpClient.post(any(), any()) } returns HttpResponse(403, "FORBIDDEN") + coEvery { spyHttpClient.post(any(), any(), any()) } returns HttpResponse(403, "FORBIDDEN") val userBackendService = UserBackendService(spyHttpClient) val identities = mapOf() val properties = mapOf() @@ -44,8 +44,12 @@ class UserBackendServiceTests : FunSpec({ val osId = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() coEvery { - spyHttpClient.post(any(), any()) - } returns HttpResponse(202, "{identity:{onesignal_id: \"$osId\", aliasLabel1: \"aliasValue1\"}, properties:{timezone_id: \"testTimeZone\", language: \"testLanguage\"}}") + spyHttpClient.post(any(), any(), any()) + } returns + HttpResponse( + 202, + "{identity:{onesignal_id: \"$osId\", aliasLabel1: \"aliasValue1\"}, properties:{timezone_id: \"testTimeZone\", language: \"testLanguage\"}}", + ) val userBackendService = UserBackendService(spyHttpClient) val identities = mapOf("aliasLabel1" to "aliasValue1") val properties = mapOf("timezone_id" to "testTimeZone", "language" to "testLanguage") @@ -70,6 +74,7 @@ class UserBackendServiceTests : FunSpec({ it.has("properties") shouldBe true it.has("subscriptions") shouldBe false }, + any(), ) } } @@ -79,8 +84,12 @@ class UserBackendServiceTests : FunSpec({ val osId = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() coEvery { - spyHttpClient.post(any(), any()) - } returns HttpResponse(202, "{identity:{onesignal_id: \"$osId\"}, subscriptions:[{id:\"subscriptionId1\", type:\"AndroidPush\"}], properties:{timezone_id: \"testTimeZone\", language: \"testLanguage\"}}") + spyHttpClient.post(any(), any(), any()) + } returns + HttpResponse( + 202, + "{identity:{onesignal_id: \"$osId\"}, subscriptions:[{id:\"subscriptionId1\", type:\"AndroidPush\"}], properties:{timezone_id: \"testTimeZone\", language: \"testLanguage\"}}", + ) val userBackendService = UserBackendService(spyHttpClient) val identities = mapOf() val subscriptions = mutableListOf() @@ -109,6 +118,7 @@ class UserBackendServiceTests : FunSpec({ it.getJSONArray("subscriptions").getJSONObject(0).has("type") shouldBe true it.getJSONArray("subscriptions").getJSONObject(0).getString("type") shouldBe "AndroidPush" }, + any(), ) } } @@ -118,7 +128,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(202, "{properties: { tags: {tagKey1: tagValue1}}}") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(202, "{properties: { tags: {tagKey1: tagValue1}}}") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject(tags = mapOf("tagkey1" to "tagValue1")) val propertiesDelta = PropertiesDeltasObject() @@ -136,6 +146,7 @@ class UserBackendServiceTests : FunSpec({ it.getJSONObject("properties").getJSONObject("tags").has("tagkey1") shouldBe true it.getJSONObject("properties").getJSONObject("tags").getString("tagkey1") shouldBe "tagValue1" }, + any(), ) } } @@ -145,7 +156,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(202, "{properties: { tags: {tagKey1: tagValue1}}}") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(202, "{properties: { tags: {tagKey1: tagValue1}}}") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject(language = "newLanguage") val propertiesDelta = PropertiesDeltasObject() @@ -162,6 +173,7 @@ class UserBackendServiceTests : FunSpec({ it.getJSONObject("properties").has("language") shouldBe true it.getJSONObject("properties").getString("language") shouldBe "newLanguage" }, + any(), ) } } @@ -171,7 +183,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(202, "{properties: { timezone_id: \"America/New_York\"}}") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(202, "{properties: { timezone_id: \"America/New_York\"}}") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject(timezoneId = "America/New_York") val propertiesDelta = PropertiesDeltasObject() @@ -188,6 +200,7 @@ class UserBackendServiceTests : FunSpec({ it.getJSONObject("properties").has("timezone_id") shouldBe true it.getJSONObject("properties").getString("timezone_id") shouldBe "America/New_York" }, + any(), ) } } @@ -197,7 +210,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(202, "{properties: { country: \"TV\"}}") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(202, "{properties: { country: \"TV\"}}") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject(country = "TV") val propertiesDelta = PropertiesDeltasObject() @@ -214,6 +227,7 @@ class UserBackendServiceTests : FunSpec({ it.getJSONObject("properties").has("country") shouldBe true it.getJSONObject("properties").getString("country") shouldBe "TV" }, + any(), ) } } @@ -223,7 +237,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(202, "{properties: { lat: 12.34, long: 45.67}}") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(202, "{properties: { lat: 12.34, long: 45.67}}") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject(latitude = 12.34, longitude = 45.67) val propertiesDelta = PropertiesDeltasObject() @@ -242,6 +256,7 @@ class UserBackendServiceTests : FunSpec({ it.getJSONObject("properties").has("long") shouldBe true it.getJSONObject("properties").getDouble("long") shouldBe 45.67 }, + any(), ) } } @@ -251,7 +266,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(202, "{properties: {} }") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(202, "{properties: {} }") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject(tags = mapOf("tagkey1" to "tagValue1")) val propertiesDelta = PropertiesDeltasObject() @@ -266,6 +281,7 @@ class UserBackendServiceTests : FunSpec({ withArg { it.getBoolean("refresh_device_metadata") shouldBe true }, + any(), ) } } @@ -275,7 +291,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(202, "{properties: {} }") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(202, "{properties: {} }") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject(tags = mapOf("tagkey1" to "tagValue1")) val propertiesDelta = PropertiesDeltasObject() @@ -290,6 +306,7 @@ class UserBackendServiceTests : FunSpec({ withArg { it.getBoolean("refresh_device_metadata") shouldBe false }, + any(), ) } } @@ -299,7 +316,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(202, "{properties: { }}") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(202, "{properties: { }}") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject() val propertiesDelta = PropertiesDeltasObject(sessionTime = 1111, sessionCount = 1) @@ -318,6 +335,7 @@ class UserBackendServiceTests : FunSpec({ it.getJSONObject("deltas").has("session_count") shouldBe true it.getJSONObject("deltas").getInt("session_count") shouldBe 1 }, + any(), ) } } @@ -327,7 +345,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(202, "{properties: { }}") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(202, "{properties: { }}") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject() val propertiesDelta = @@ -366,6 +384,7 @@ class UserBackendServiceTests : FunSpec({ it.getJSONObject("deltas").getJSONArray("purchases").getJSONObject(1).has("amount") shouldBe true it.getJSONObject("deltas").getJSONArray("purchases").getJSONObject(1).getDouble("amount") shouldBe 4444 }, + any(), ) } } @@ -375,7 +394,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(404, "NOT FOUND") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(404, "NOT FOUND") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject(tags = mapOf("tagkey1" to "tagValue1")) val propertiesDelta = PropertiesDeltasObject() @@ -396,7 +415,7 @@ class UserBackendServiceTests : FunSpec({ val aliasLabel = "onesignal_id" val aliasValue = "11111111-1111-1111-1111-111111111111" val spyHttpClient = mockk() - coEvery { spyHttpClient.patch(any(), any()) } returns HttpResponse(403, "FORBIDDEN") + coEvery { spyHttpClient.patch(any(), any(), any()) } returns HttpResponse(403, "FORBIDDEN") val userBackendService = UserBackendService(spyHttpClient) val properties = PropertiesObject(tags = mapOf("tagkey1" to "tagValue1")) val propertiesDelta = PropertiesDeltasObject() diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/IdentityOperationExecutorTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/IdentityOperationExecutorTests.kt index 34d0681c48..d2ae66bf5d 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/IdentityOperationExecutorTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/IdentityOperationExecutorTests.kt @@ -32,9 +32,11 @@ class IdentityOperationExecutorTests : FunSpec({ val mockIdentityModel = mockk() every { mockIdentityModel.onesignalId } returns "onesignalId" every { mockIdentityModel.setStringProperty(any(), any(), any()) } just runs + every { mockIdentityModel.jwtToken } returns null val mockIdentityModelStore = mockk() every { mockIdentityModelStore.model } returns mockIdentityModel + every { mockIdentityModelStore.getIdentityAlias() } returns Pair(IdentityConstants.ONESIGNAL_ID, "onesignalId") val mockBuildUserService = mockk() @@ -48,7 +50,13 @@ class IdentityOperationExecutorTests : FunSpec({ // Then response.result shouldBe ExecutionResult.SUCCESS coVerify(exactly = 1) { - mockIdentityBackendService.setAlias("appId", IdentityConstants.ONESIGNAL_ID, "onesignalId", mapOf("aliasKey1" to "aliasValue1")) + mockIdentityBackendService.setAlias( + "appId", + IdentityConstants.ONESIGNAL_ID, + "onesignalId", + mapOf("aliasKey1" to "aliasValue1"), + null, + ) } verify(exactly = 1) { mockIdentityModel.setStringProperty("aliasKey1", "aliasValue1", ModelChangeTags.HYDRATE) } } @@ -153,6 +161,7 @@ class IdentityOperationExecutorTests : FunSpec({ val mockIdentityModel = mockk() every { mockIdentityModel.onesignalId } returns "onesignalId" every { mockIdentityModel.setOptStringProperty(any(), any(), any()) } just runs + every { mockIdentityModel.jwtToken } returns null val mockIdentityModelStore = mockk() every { mockIdentityModelStore.model } returns mockIdentityModel diff --git a/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/backend/InAppBackendServiceTests.kt b/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/backend/InAppBackendServiceTests.kt index fdf7ee1e2e..4f97d95bdf 100644 --- a/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/backend/InAppBackendServiceTests.kt +++ b/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/backend/InAppBackendServiceTests.kt @@ -52,7 +52,9 @@ class InAppBackendServiceTests : // Then response shouldNotBe null response!!.count() shouldBe 0 - coVerify(exactly = 1) { mockHttpClient.get("apps/appId/subscriptions/subscriptionId/iams", any()) } + coVerify( + exactly = 1, + ) { mockHttpClient.get("apps/appId/users/by/onesignal_id/onesignal_id/subscriptions/subscriptionId/iams", any()) } } test("listInAppMessages with 1 message returns one-lengthed array") { @@ -98,7 +100,9 @@ class InAppBackendServiceTests : response[0].redisplayStats.displayLimit shouldBe 11111 response[0].redisplayStats.displayDelay shouldBe 22222 - coVerify(exactly = 1) { mockHttpClient.get("apps/appId/subscriptions/subscriptionId/iams", any()) } + coVerify( + exactly = 1, + ) { mockHttpClient.get("apps/appId/users/by/onesignal_id/onesignal_id/subscriptions/subscriptionId/iams", any()) } } test("listInAppMessages returns null when non-success response") { @@ -121,7 +125,9 @@ class InAppBackendServiceTests : // Then response shouldBe null - coVerify(exactly = 1) { mockHttpClient.get("apps/appId/subscriptions/subscriptionId/iams", any()) } + coVerify( + exactly = 1, + ) { mockHttpClient.get("apps/appId/users/by/onesignal_id/onesignal_id/subscriptions/subscriptionId/iams", any()) } } test( @@ -161,7 +167,7 @@ class InAppBackendServiceTests : coVerify(exactly = 1) { mockHttpClient.get( - "apps/appId/subscriptions/subscriptionId/iams", + "apps/appId/users/by/onesignal_id/onesignal_id/subscriptions/subscriptionId/iams", match { it.rywToken == "1234" && it.retryCount == null && it.sessionDuration == mockSessionDurationProvider() }, @@ -171,7 +177,7 @@ class InAppBackendServiceTests : // Verify that the get method retried twice with the RYW token coVerify(exactly = 3) { mockHttpClient.get( - "apps/appId/subscriptions/subscriptionId/iams", + "apps/appId/users/by/onesignal_id/onesignal_id/subscriptions/subscriptionId/iams", match { it.rywToken == "1234" && it.sessionDuration == mockSessionDurationProvider() && it.retryCount != null }, @@ -181,9 +187,9 @@ class InAppBackendServiceTests : // Verify that the get method was retried the final time without the RYW token coVerify(exactly = 1) { mockHttpClient.get( - "apps/appId/subscriptions/subscriptionId/iams", + any(), match { - it.rywToken == null && it.sessionDuration == mockSessionDurationProvider() && it.retryCount == null + it.rywToken == null && it.sessionDuration == null && it.retryCount == null }, ) } diff --git a/OneSignalSDK/onesignal/testhelpers/src/main/java/com/onesignal/mocks/MockHelper.kt b/OneSignalSDK/onesignal/testhelpers/src/main/java/com/onesignal/mocks/MockHelper.kt index d8fa8ed86a..2e22da10b5 100644 --- a/OneSignalSDK/onesignal/testhelpers/src/main/java/com/onesignal/mocks/MockHelper.kt +++ b/OneSignalSDK/onesignal/testhelpers/src/main/java/com/onesignal/mocks/MockHelper.kt @@ -78,6 +78,7 @@ object MockHelper { val mockIdentityStore = mockk(relaxed = true) every { mockIdentityStore.model } returns identityModel + every { mockIdentityStore.getIdentityAlias() } returns Pair("onesignal_id", "remote-onesignalId") return mockIdentityStore }