diff --git a/integration-tests/src/main/java/com/iterable/integration/tests/utils/IntegrationTestUtils.kt b/integration-tests/src/main/java/com/iterable/integration/tests/utils/IntegrationTestUtils.kt index 6d3d21dc7..6bf77a431 100644 --- a/integration-tests/src/main/java/com/iterable/integration/tests/utils/IntegrationTestUtils.kt +++ b/integration-tests/src/main/java/com/iterable/integration/tests/utils/IntegrationTestUtils.kt @@ -116,14 +116,14 @@ class IntegrationTestUtils(private val context: Context) { val response = httpClient.newCall(request).execute() val success = response.isSuccessful - if (success) { - Log.d(TAG, "Campaign triggered successfully via API: campaignId=$campaignId, recipientEmail=$recipientEmail") - } else { - val errorBody = response.body?.string() ?: "No error body" - Log.e(TAG, "Failed to trigger campaign via API: ${response.code} - $errorBody") - // Store error message for UI display - lastErrorMessage = "HTTP ${response.code}: $errorBody" - } + if (success) { + Log.d(TAG, "Campaign triggered successfully via API: campaignId=$campaignId, recipientEmail=$recipientEmail") + } else { + val errorBody = response.body?.string() ?: "No error body" + Log.e(TAG, "Failed to trigger campaign via API: ${response.code} - $errorBody") + // Store error message for UI display + lastErrorMessage = "HTTP ${response.code}: $errorBody" + } callback?.invoke(success) } catch (e: Exception) { @@ -149,14 +149,14 @@ class IntegrationTestUtils(private val context: Context) { val response = httpClient.newCall(request).execute() val success = response.isSuccessful - if (success) { - Log.d(TAG, "Push campaign triggered successfully via API: campaignId=$campaignId, recipientEmail=$recipientEmail") - } else { - val errorBody = response.body?.string() ?: "No error body" - Log.e(TAG, "Failed to trigger push campaign via API: ${response.code} - $errorBody") - // Store error message for UI display - lastErrorMessage = "HTTP ${response.code}: $errorBody" - } + if (success) { + Log.d(TAG, "Push campaign triggered successfully via API: campaignId=$campaignId, recipientEmail=$recipientEmail") + } else { + val errorBody = response.body?.string() ?: "No error body" + Log.e(TAG, "Failed to trigger push campaign via API: ${response.code} - $errorBody") + // Store error message for UI display + lastErrorMessage = "HTTP ${response.code}: $errorBody" + } //TODO: Move callback success inside if(success) callback?.invoke(success) } catch (e: Exception) { diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/IterableApiResponseTest.java b/iterableapi/src/androidTest/java/com/iterable/iterableapi/IterableApiResponseTest.java index afab7676b..2b5b4856a 100644 --- a/iterableapi/src/androidTest/java/com/iterable/iterableapi/IterableApiResponseTest.java +++ b/iterableapi/src/androidTest/java/com/iterable/iterableapi/IterableApiResponseTest.java @@ -77,10 +77,10 @@ public void testResponseCode200() throws Exception { final JSONObject responseData = new JSONObject("{\"key\":\"value\"}"); stubAnyRequestReturningStatusCode(200, responseData); - IterableApiRequest request = new IterableApiRequest("fake_key", "", new JSONObject(), IterableApiRequest.POST, null, new IterableHelper.SuccessHandler() { + IterableApiRequest request = new IterableApiRequest("fake_key", "", new JSONObject(), IterableApiRequest.POST, null, new IterableHelper.RemoteSuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { - assertEquals(responseData.toString(), data.toString()); + public void onSuccess(@NonNull IterableResponseObject.RemoteSuccess data) { + assertEquals(responseData.toString(), data.getResponseJson().toString()); signal.countDown(); } }, null); @@ -222,11 +222,11 @@ public void onFailure(@NonNull String reason, @Nullable JSONObject data) { "}"); stubAnyRequestReturningStatusCode(200, responseData); - new IterableRequestTask().execute(new IterableApiRequest("fake_key", "", new JSONObject(), IterableApiRequest.POST, null, new IterableHelper.SuccessHandler() { + new IterableRequestTask().execute(new IterableApiRequest("fake_key", "", new JSONObject(), IterableApiRequest.POST, null, new IterableHelper.RemoteSuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject successData) { + public void onSuccess(@NonNull IterableResponseObject.RemoteSuccess successData) { try { - assertEquals(responseData.toString(), successData.toString()); + assertEquals(responseData.toString(), successData.getResponseJson().toString()); } catch (AssertionError e) { e.printStackTrace(); } finally { diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java index 1f5ed6a38..bfbcf3f09 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java @@ -43,7 +43,7 @@ public class IterableApi { private IterableNotificationData _notificationData; private String _deviceId; private boolean _firstForegroundHandled; - private IterableHelper.SuccessHandler _setUserSuccessCallbackHandler; + private IterableHelper.IterableSuccessCallback _setUserSuccessCallbackHandler; private IterableHelper.FailureHandler _setUserFailureCallbackHandler; IterableApiClient apiClient = new IterableApiClient(new IterableApiAuthProvider()); @@ -310,7 +310,7 @@ public void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull Iterable * @param onFailure */ - public void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull IterableHelper.SuccessHandler onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { + public void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull IterableHelper.IterableSuccessCallback onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { if (!checkSDKInitialization()) { return; } @@ -328,7 +328,7 @@ public void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull Iterable * @param onSuccess * @param onFailure */ - void getEmbeddedMessages(@NonNull IterableHelper.SuccessHandler onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { + void getEmbeddedMessages(@NonNull IterableHelper.IterableSuccessCallback onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { if (!checkSDKInitialization()) { return; } @@ -415,7 +415,8 @@ private void onLogin( setAuthToken(authToken); attemptMergeAndEventReplay(userIdOrEmail, isEmail, merge, replay, isUnknown, failureHandler); } else { - getAuthManager().requestNewAuthToken(false, data -> attemptMergeAndEventReplay(userIdOrEmail, isEmail, merge, replay, isUnknown, failureHandler)); + IterableHelper.AuthTokenCallback callback = data -> attemptMergeAndEventReplay(userIdOrEmail, isEmail, merge, replay, isUnknown, failureHandler); + getAuthManager().requestNewAuthToken(false, callback); } } @@ -451,13 +452,17 @@ private void completeUserLogin(@Nullable String email, @Nullable String userId, if (config.autoPushRegistration) { registerForPush(); } else if (_setUserSuccessCallbackHandler != null) { - _setUserSuccessCallbackHandler.onSuccess(new JSONObject()); // passing blank json object here as onSuccess is @Nonnull + invokeSetUserSuccessHandler(); } getInAppManager().syncInApp(); getEmbeddedManager().syncMessages(); } + private void invokeSetUserSuccessHandler() { + _setUserSuccessCallbackHandler.onSuccess(IterableResponseObject.LocalSuccessResponse); + } + private final IterableActivityMonitor.AppStateCallback activityMonitorListener = new IterableActivityMonitor.AppStateCallback() { @Override public void onSwitchToForeground() { @@ -700,7 +705,7 @@ protected void disableToken(@Nullable String email, @Nullable String userId, @No * @param authToken * @param deviceToken The device token */ - protected void disableToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String deviceToken, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + protected void disableToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String deviceToken, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { if (deviceToken == null) { IterableLogger.d(TAG, "device token not available"); return; @@ -729,16 +734,16 @@ protected void registerDeviceToken(@Nullable String email, @Nullable String user IterableLogger.e(TAG, "registerDeviceToken: applicationName is null, check that pushIntegrationName is set in IterableConfig"); } - IterableHelper.SuccessHandler wrappedSuccessHandler = getSuccessHandler(); - IterableHelper.FailureHandler wrappedFailureHandler = getFailureHandler(); + IterableHelper.IterableSuccessCallback wrappedSuccessHandler = wrapSetUserCallbackForRemoteCall(); + IterableHelper.FailureHandler wrappedFailureHandler = wrapSetUserFailureHandlerForRemoteCall(); apiClient.registerDeviceToken(email, userId, authToken, applicationName, deviceToken, dataFields, deviceAttributes, wrappedSuccessHandler, wrappedFailureHandler); } - private IterableHelper.SuccessHandler getSuccessHandler() { - IterableHelper.SuccessHandler wrappedSuccessHandler = null; + private IterableHelper.IterableSuccessCallback wrapSetUserCallbackForRemoteCall() { + IterableHelper.RemoteSuccessCallback wrappedSuccessHandler = null; if (_setUserSuccessCallbackHandler != null || (config.enableUnknownUserActivation && getVisitorUsageTracked() && config.identityResolution.getReplayOnVisitorToKnown())) { - final IterableHelper.SuccessHandler originalSuccessHandler = _setUserSuccessCallbackHandler; + final IterableHelper.IterableSuccessCallback originalSuccessHandler = _setUserSuccessCallbackHandler; wrappedSuccessHandler = data -> { trackConsentOnDeviceRegistration(); @@ -750,7 +755,7 @@ private IterableHelper.SuccessHandler getSuccessHandler() { return wrappedSuccessHandler; } - private IterableHelper.FailureHandler getFailureHandler() { + private IterableHelper.FailureHandler wrapSetUserFailureHandlerForRemoteCall() { IterableHelper.FailureHandler wrappedFailureHandler = null; if (_setUserFailureCallbackHandler != null || (config.enableUnknownUserActivation && getVisitorUsageTracked() && config.identityResolution.getReplayOnVisitorToKnown())) { final IterableHelper.FailureHandler originalFailureHandler = _setUserFailureCallbackHandler; @@ -997,11 +1002,11 @@ public void setEmail(@Nullable String email, IterableIdentityResolution identity queueOrExecute(() -> setEmail(email, null, identityResolution, null, null), "setEmail(" + maskPII(email) + ", identityResolution)"); } - public void setEmail(@Nullable String email, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void setEmail(@Nullable String email, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> setEmail(email, null, null, successHandler, failureHandler), "setEmail(" + maskPII(email) + ", callbacks)"); } - public void setEmail(@Nullable String email, IterableIdentityResolution identityResolution, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void setEmail(@Nullable String email, IterableIdentityResolution identityResolution, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> setEmail(email, null, identityResolution, successHandler, failureHandler), "setEmail(" + maskPII(email) + ", identityResolution, callbacks)"); } @@ -1013,11 +1018,11 @@ public void setEmail(@Nullable String email, @Nullable String authToken, Iterabl queueOrExecute(() -> setEmail(email, authToken, identityResolution, null, null), "setEmail(" + maskPII(email) + ", " + maskPII(authToken) + ", identityResolution)"); } - public void setEmail(@Nullable String email, @Nullable String authToken, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void setEmail(@Nullable String email, @Nullable String authToken, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> setEmail(email, authToken, null, successHandler, failureHandler), "setEmail(" + maskPII(email) + ", " + maskPII(authToken) + ", callbacks)"); } - public void setEmail(@Nullable String email, @Nullable String authToken, @Nullable IterableIdentityResolution iterableIdentityResolution, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void setEmail(@Nullable String email, @Nullable String authToken, @Nullable IterableIdentityResolution iterableIdentityResolution, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { boolean replay = isReplay(iterableIdentityResolution); boolean merge = isMerge(iterableIdentityResolution); @@ -1066,11 +1071,11 @@ public void setUserId(@Nullable String userId, IterableIdentityResolution identi queueOrExecute(() -> setUserId(userId, null, identityResolution, null, null, false), "setUserId(" + maskPII(userId) + ", identityResolution)"); } - public void setUserId(@Nullable String userId, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void setUserId(@Nullable String userId, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> setUserId(userId, null, null, successHandler, failureHandler, false), "setUserId(" + maskPII(userId) + ", callbacks)"); } - public void setUserId(@Nullable String userId, IterableIdentityResolution identityResolution, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void setUserId(@Nullable String userId, IterableIdentityResolution identityResolution, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> setUserId(userId, null, identityResolution, successHandler, failureHandler, false), "setUserId(" + maskPII(userId) + ", identityResolution, callbacks)"); } @@ -1083,11 +1088,11 @@ public void setUserId(@Nullable String userId, @Nullable String authToken, Itera } - public void setUserId(@Nullable String userId, @Nullable String authToken, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void setUserId(@Nullable String userId, @Nullable String authToken, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> setUserId(userId, authToken, null, successHandler, failureHandler, false), "setUserId(" + maskPII(userId) + ", " + maskPII(authToken) + ", callbacks)"); } - public void setUserId(@Nullable String userId, @Nullable String authToken, @Nullable IterableIdentityResolution iterableIdentityResolution, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler, boolean isUnknown) { + public void setUserId(@Nullable String userId, @Nullable String authToken, @Nullable IterableIdentityResolution iterableIdentityResolution, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler, boolean isUnknown) { boolean replay = isReplay(iterableIdentityResolution); boolean merge = isMerge(iterableIdentityResolution); @@ -1133,7 +1138,7 @@ private boolean isReplay(@Nullable IterableIdentityResolution iterableIdentityRe private void attemptMergeAndEventReplay(@Nullable String emailOrUserId, boolean isEmail, boolean merge, boolean replay, boolean isUnknown, IterableHelper.FailureHandler failureHandler) { if (config.enableUnknownUserActivation && getVisitorUsageTracked()) { - if (emailOrUserId != null && !emailOrUserId.equals(_userIdUnknown)) { + if (emailOrUserId != null && !emailOrUserId.equals(_userIdUnknown)) { //todo: when would the userIdUnknown be the same? attemptAndProcessMerge(emailOrUserId, isEmail, merge, failureHandler, _userIdUnknown); } @@ -1252,7 +1257,7 @@ public void inAppConsume(@NonNull String messageId) { * @param successHandler The callback which returns `success`. * @param failureHandler The callback which returns `failure`. */ - public void inAppConsume(@NonNull String messageId, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void inAppConsume(@NonNull String messageId, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { IterableInAppMessage message = getInAppManager().getMessageById(messageId); if (checkIfMessageIsNull(message, failureHandler)) { return; @@ -1291,7 +1296,7 @@ public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable Iterab * @param successHandler The callback which returns `success`. * @param failureHandler The callback which returns `failure`. */ - public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable IterableInAppDeleteActionType source, @Nullable IterableInAppLocation clickLocation, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable IterableInAppDeleteActionType source, @Nullable IterableInAppLocation clickLocation, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { if (!checkSDKInitialization()) { return; } @@ -1498,7 +1503,7 @@ public void updateEmail(final @NonNull String newEmail, final @NonNull String au queueOrExecute(() -> updateEmail(newEmail, authToken, null, null), "updateEmail(" + maskPII(newEmail) + ", " + maskPII(authToken) + ")"); } - public void updateEmail(final @NonNull String newEmail, final @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void updateEmail(final @NonNull String newEmail, final @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> updateEmail(newEmail, null, successHandler, failureHandler), "updateEmail(" + maskPII(newEmail) + ", callbacks)"); } @@ -1509,7 +1514,7 @@ public void updateEmail(final @NonNull String newEmail, final @Nullable Iterable * @param successHandler Success handler. Called when the server returns a success code. * @param failureHandler Failure handler. Called when the server call failed. */ - public void updateEmail(final @NonNull String newEmail, final @Nullable String authToken, final @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void updateEmail(final @NonNull String newEmail, final @Nullable String authToken, final @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { if (!checkSDKInitialization()) { IterableLogger.e(TAG, "The Iterable SDK must be initialized with email or userId before " + "calling updateEmail"); @@ -1521,9 +1526,9 @@ public void updateEmail(final @NonNull String newEmail, final @Nullable String a return; } - apiClient.updateEmail(newEmail, new IterableHelper.SuccessHandler() { + apiClient.updateEmail(newEmail, new IterableHelper.RemoteSuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { + public void onSuccess(@NonNull IterableResponseObject.RemoteSuccess data) { if (_email != null) { _email = newEmail; _authToken = authToken; diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java index bdb3a2578..af2ca3373 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java @@ -222,7 +222,7 @@ public void trackPurchase(double total, @NonNull List items, @Null } } - public void updateEmail(final @NonNull String newEmail, final @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public void updateEmail(final @NonNull String newEmail, final @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { JSONObject requestJSON = new JSONObject(); try { @@ -319,7 +319,7 @@ void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull IterableHelper. } } - void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull IterableHelper.SuccessHandler onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { + void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull IterableHelper.IterableSuccessCallback onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { JSONObject requestJSON = new JSONObject(); try { @@ -488,7 +488,7 @@ void trackEmbeddedMessageReceived(@NonNull IterableEmbeddedMessage message) { } - public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable IterableInAppDeleteActionType source, @Nullable IterableInAppLocation clickLocation, @Nullable String inboxSessionId, @Nullable final IterableHelper.SuccessHandler successHandler, @Nullable final IterableHelper.FailureHandler failureHandler) { + public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable IterableInAppDeleteActionType source, @Nullable IterableInAppLocation clickLocation, @Nullable String inboxSessionId, @Nullable final IterableHelper.IterableSuccessCallback successHandler, @Nullable final IterableHelper.FailureHandler failureHandler) { JSONObject requestJSON = new JSONObject(); try { @@ -604,7 +604,7 @@ protected void trackPushOpen(int campaignId, int templateId, @NonNull String mes } } - protected void disableToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String deviceToken, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + protected void disableToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String deviceToken, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { JSONObject requestJSON = new JSONObject(); try { requestJSON.put(IterableConstants.KEY_TOKEN, deviceToken); @@ -620,7 +620,7 @@ protected void disableToken(@Nullable String email, @Nullable String userId, @Nu } } - protected void registerDeviceToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String applicationName, @NonNull String deviceToken, @Nullable JSONObject dataFields, HashMap deviceAttributes, @Nullable final IterableHelper.SuccessHandler successHandler, @Nullable final IterableHelper.FailureHandler failureHandler) { + protected void registerDeviceToken(@Nullable String email, @Nullable String userId, @Nullable String authToken, @NonNull String applicationName, @NonNull String deviceToken, @Nullable JSONObject dataFields, HashMap deviceAttributes, @Nullable final IterableHelper.IterableSuccessCallback successHandler, @Nullable final IterableHelper.FailureHandler failureHandler) { Context context = authProvider.getContext(); JSONObject requestJSON = new JSONObject(); try { @@ -756,11 +756,11 @@ void sendPostRequest(@NonNull String resourcePath, @NonNull JSONObject json, @Nu sendPostRequest(resourcePath, json, authToken, null, null); } - void sendPostRequest(@NonNull String resourcePath, @NonNull JSONObject json, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + void sendPostRequest(@NonNull String resourcePath, @NonNull JSONObject json, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { sendPostRequest(resourcePath, json, authProvider.getAuthToken(), onSuccess, onFailure); } - void sendPostRequest(@NonNull String resourcePath, @NonNull JSONObject json, @Nullable String authToken, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + void sendPostRequest(@NonNull String resourcePath, @NonNull JSONObject json, @Nullable String authToken, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { getRequestProcessor().processPostRequest(authProvider.getApiKey(), resourcePath, json, authToken, onSuccess, onFailure); } @@ -774,7 +774,7 @@ void sendGetRequest(@NonNull String resourcePath, @NonNull JSONObject json, @Nul getRequestProcessor().processGetRequest(authProvider.getApiKey(), resourcePath, json, authProvider.getAuthToken(), onCallback); } - void sendGetRequest(@NonNull String resourcePath, @NonNull JSONObject json, @NonNull IterableHelper.SuccessHandler onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { + void sendGetRequest(@NonNull String resourcePath, @NonNull JSONObject json, @NonNull IterableHelper.IterableSuccessCallback onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { getRequestProcessor().processGetRequest(authProvider.getApiKey(), resourcePath, json, authProvider.getAuthToken(), onSuccess, onFailure); } @@ -783,7 +783,7 @@ void onLogout() { authProvider.resetAuth(); } - void mergeUser(String sourceEmail, String sourceUserId, String destinationEmail, String destinationUserId, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + void mergeUser(String sourceEmail, String sourceUserId, String destinationEmail, String destinationUserId, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { JSONObject requestJson = new JSONObject(); try { if (sourceEmail != null && !sourceEmail.isEmpty()) { @@ -808,7 +808,7 @@ void getCriteriaList(@Nullable IterableHelper.IterableActionHandler actionHandle sendGetRequest(IterableConstants.ENDPOINT_CRITERIA_LIST, new JSONObject(), actionHandler); } - void trackUnknownUserSession(long createdAt, String userId, @NonNull JSONObject requestJson, JSONObject updateUserTrack, @NonNull IterableHelper.SuccessHandler onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { + void trackUnknownUserSession(long createdAt, String userId, @NonNull JSONObject requestJson, JSONObject updateUserTrack, @NonNull IterableHelper.IterableSuccessCallback onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { try { JSONObject requestObject = new JSONObject(); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableAuthManager.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableAuthManager.java index 915dbbb2a..fc51ec6ae 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableAuthManager.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableAuthManager.java @@ -2,10 +2,10 @@ import android.util.Base64; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import org.json.JSONException; import org.json.JSONObject; import java.io.UnsupportedEncodingException; @@ -45,7 +45,7 @@ public class IterableAuthManager implements IterableActivityMonitor.AppStateCall this.activityMonitor.addCallback(this); } - public synchronized void requestNewAuthToken(boolean hasFailedPriorAuth, IterableHelper.SuccessHandler successCallback) { + public synchronized void requestNewAuthToken(boolean hasFailedPriorAuth, IterableHelper.IterableSuccessCallback successCallback) { requestNewAuthToken(hasFailedPriorAuth, successCallback, true); } @@ -67,19 +67,15 @@ void resetRetryCount() { retryCount = 0; } - private void handleSuccessForAuthToken(String authToken, IterableHelper.SuccessHandler successCallback) { - try { - JSONObject object = new JSONObject(); - object.put("newAuthToken", authToken); - successCallback.onSuccess(object); - } catch (JSONException e) { - e.printStackTrace(); - } + private void handleSuccessForAuthToken(@NonNull String authToken, IterableHelper.IterableSuccessCallback successCallback) { + IterableResponseObject.AuthTokenSuccess remoteSuccess = new IterableResponseObject.AuthTokenSuccess(authToken); + successCallback.onSuccess(remoteSuccess); + } public synchronized void requestNewAuthToken( boolean hasFailedPriorAuth, - final IterableHelper.SuccessHandler successCallback, + final IterableHelper.IterableSuccessCallback successCallback, boolean shouldIgnoreRetryPolicy) { if (!shouldIgnoreRetryPolicy && (pauseAuthRetry || (retryCount >= authRetryPolicy.maxRetry))) { return; @@ -130,7 +126,7 @@ public void run() { } } - private void handleAuthTokenSuccess(String authToken, IterableHelper.SuccessHandler successCallback) { + private void handleAuthTokenSuccess(String authToken, IterableHelper.IterableSuccessCallback successCallback) { if (authToken != null) { IterableApi.getInstance().setAuthToken(authToken); queueExpirationRefresh(authToken); @@ -210,7 +206,7 @@ long getNextRetryInterval() { return nextRetryInterval; } - void scheduleAuthTokenRefresh(long timeDuration, boolean isScheduledRefresh, final IterableHelper.SuccessHandler successCallback) { + void scheduleAuthTokenRefresh(long timeDuration, boolean isScheduledRefresh, final IterableHelper.IterableSuccessCallback successCallback) { if ((pauseAuthRetry && !isScheduledRefresh) || isTimerScheduled) { // we only stop schedule token refresh if it is called from retry (in case of failure). The normal auth token refresh schedule would work return; diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt b/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt index 8b53171bc..acb4408a4 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt @@ -1,7 +1,6 @@ package com.iterable.iterableapi import android.content.Context -import com.iterable.iterableapi.IterableHelper.SuccessHandler import org.json.JSONException import org.json.JSONObject @@ -89,14 +88,17 @@ public class IterableEmbeddedManager : IterableActivityMonitor.AppStateCallback if (iterableApi.config.enableEmbeddedMessaging) { IterableLogger.v(TAG, "Syncing messages...") - IterableApi.sharedInstance.getEmbeddedMessages(placementIds, { data -> + + + IterableApi.sharedInstance.getEmbeddedMessages(placementIds, IterableHelper.RemoteSuccessCallback { data: IterableResponseObject.RemoteSuccess -> IterableLogger.v(TAG, "Got response from network call to get embedded messages") try { val previousPlacementIds = getPlacementIds() val currentPlacementIds: MutableList = mutableListOf() + val placementsArray = - data.optJSONArray(IterableConstants.ITERABLE_EMBEDDED_MESSAGE_PLACEMENTS) + data.responseJson.optJSONArray(IterableConstants.ITERABLE_EMBEDDED_MESSAGE_PLACEMENTS) if (placementsArray != null) { //if there are no placements in the payload //reset the local message storage and trigger a UI update diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableHelper.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableHelper.java index a949a0727..ab40c7c43 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableHelper.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableHelper.java @@ -1,9 +1,11 @@ package com.iterable.iterableapi; import android.net.Uri; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.json.JSONException; import org.json.JSONObject; /** @@ -22,14 +24,206 @@ public interface IterableUrlCallback { void execute(@Nullable Uri url); } - public interface SuccessHandler { + /** + * @deprecated Use {@link IterableSuccessCallback} instead. + *

+ * This interface is deprecated and will be removed in a future version. + * Replace all usages with {@link IterableSuccessCallback} for general success handling, + * or {@link RemoteSuccessCallback} if you specifically need JSON response data from the API. + *

+ * Quick Migration (Recommended): Replace {@code SuccessHandler} with {@code IterableSuccessCallback} + *

+     * // REPLACE THIS:
+     * IterableHelper.SuccessHandler successHandler = data -> {
+     *     // Your code here
+     * };
+     *
+     * // WITH THIS (see JavaDoc on IterableSuccessCallback for more details):
+     * IterableHelper.IterableSuccessCallback successHandler = data -> {
+     *     // Your code here - see JavaDoc for accessing response data
+     * };
+     * 
+ * + * @see IterableSuccessCallback + * @see RemoteSuccessCallback + * @see LocalSuccessCallback + */ + @Deprecated + public interface SuccessHandler extends IterableSuccessCallback { void onSuccess(@NonNull JSONObject data); + + @Override + default void onSuccess(@NonNull IterableResponseObject.Success data) { + IterableLogger.w("IterableHelper", "SuccessHandler is deprecated. Please migrate to IterableSuccessCallback or RemoteSuccessCallback. " + + "See JavaDoc for migration guide. Current success type: " + data.getClass().getSimpleName()); + + JSONObject jsonObject = new JSONObject(); + try { + if (data instanceof IterableResponseObject.RemoteSuccess) { + JSONObject originalJson = ((IterableResponseObject.RemoteSuccess) data).getResponseJson(); + jsonObject = new JSONObject(originalJson.toString()); + } else if (data instanceof IterableResponseObject.AuthTokenSuccess) { + jsonObject.put("newAuthToken", ((IterableResponseObject.AuthTokenSuccess) data).getAuthToken()); + } else if (data instanceof IterableResponseObject.LocalSuccess) { + jsonObject.put("message", data.getMessage()); + } else { + jsonObject.put("message", data.getMessage()); + } + } catch (JSONException e) { + IterableLogger.e("IterableHelper", "Error creating JSON for deprecated SuccessHandler callback", e); + } + + onSuccess(jsonObject); + } + } + + /** + * Generic callback interface for successful SDK operations. + *

+ * When to use this callback: + *

    + *
  • When you want to proceed on any type of success (remote, local, or auth token)
  • + *
  • When you don't need to access specific response data (like JSON from API or auth tokens)
  • + *
  • When you just need to know the operation completed successfully, regardless of how
  • + *
+ *

+ * Example use cases: + *

    + *
  • {@code setEmail()} - Can complete locally (if autoPushRegistration is false) or remotely (via registerForPush)
  • + *
  • {@code setUserId()} - May complete locally or trigger remote operations
  • + *
  • Any operation where you just need confirmation of success without caring about the response details
  • + *
+ *

+ * When to use specialized callbacks instead: + *

    + *
  • Use {@link RemoteSuccessCallback} if you need to access JSON response data from the API
  • + *
  • Use {@link LocalSuccessCallback} if you only want to proceed when no remote call was made
  • + *
  • Use {@link AuthTokenCallback} if you need to access the authentication token
  • + *
+ * + * @see RemoteSuccessCallback + * @see LocalSuccessCallback + * @see AuthTokenCallback + */ + public interface IterableSuccessCallback { + void onSuccess(@NonNull IterableResponseObject.Success data); + } + + /** + * Callback specifically for operations that make remote API calls and return JSON response data. + *

+ * When to use this callback: + *

    + *
  • When you need to access the JSON response data from the Iterable API
  • + *
  • When you want your callback to trigger only if a remote API call was made
  • + *
  • When the operation you're calling always makes a remote API request
  • + *
+ *

+ * Example use cases: + *

    + *
  • {@code trackEvent()} - Always makes a remote API call with JSON response
  • + *
  • {@code updateUser()} - Makes a remote call to update user profile
  • + *
  • Operations where you need to inspect the server's response data
  • + *
+ *

+ * Important: If the operation completes locally (e.g., {@code setEmail} with autoPushRegistration disabled), + * your callback will NOT be triggered. Use {@link IterableSuccessCallback} instead if you want to handle both + * remote and local success cases. + */ + public interface RemoteSuccessCallback extends IterableSuccessCallback { + void onSuccess(@NonNull IterableResponseObject.RemoteSuccess data); + + @Override + default void onSuccess(@NonNull IterableResponseObject.Success data) { + // Dispatch to the specific method if it's the correct type + if (data instanceof IterableResponseObject.RemoteSuccess) { + onSuccess((IterableResponseObject.RemoteSuccess) data); + } else { + IterableLogger.w("IterableHelper", "RemoteSuccessCallback received unexpected success type: " + data.getClass().getSimpleName() + + ". This callback only triggers for remote API responses. Consider using IterableSuccessCallback if you want to handle all success types."); + } + } + } + + /** + * Callback specifically for authentication token operations. + *

+ * When to use this callback: + *

    + *
  • When you need to access the JWT authentication token from the response
  • + *
  • For operations that request or refresh auth tokens
  • + *
  • When implementing custom auth token handling or caching
  • + *
+ *

+ * Example use cases: + *

    + *
  • Auth token refresh operations triggered by {@link IterableAuthHandler}
  • + *
  • Operations that return a new JWT token for the current user
  • + *
  • Custom auth token validation or storage logic
  • + *
+ *

+ * Note: This is primarily used internally by the SDK's auth system. Most applications + * won't need to use this callback directly. + */ + public interface AuthTokenCallback extends IterableSuccessCallback { + void onSuccess(@NonNull IterableResponseObject.AuthTokenSuccess data); + + @Override + default void onSuccess(@NonNull IterableResponseObject.Success data) { + // Dispatch to the specific method if it's the correct type + if (data instanceof IterableResponseObject.AuthTokenSuccess) { + onSuccess((IterableResponseObject.AuthTokenSuccess) data); + } else { + IterableLogger.w("IterableHelper", "AuthTokenCallback received unexpected success type: " + data.getClass().getSimpleName() + + ". This callback is for auth token operations only."); + } + } + } + + /** + * Callback specifically for operations that complete locally without making a remote API call. + *

+ * When to use this callback: + *

    + *
  • When you want your callback to trigger only if the operation completed locally
  • + *
  • When you want to distinguish between operations that hit the server vs. those that don't
  • + *
  • For testing or debugging purposes to verify no remote call was made
  • + *
+ *

+ * Example use cases: + *

    + *
  • {@code setEmail()} with {@code autoPushRegistration = false} - Updates locally without calling the API
  • + *
  • Operations that only update local state or keychain
  • + *
  • Scenarios where you want different behavior based on whether a network call occurred
  • + *
+ *

+ * Important: If the operation makes a remote call (e.g., {@code setEmail} with autoPushRegistration enabled), + * your callback will NOT be triggered. Use {@link IterableSuccessCallback} instead if you want to handle both + * local and remote success cases. + */ + public interface LocalSuccessCallback extends IterableSuccessCallback { + void onSuccess(@NonNull IterableResponseObject.LocalSuccess data); + + @Override + default void onSuccess(@NonNull IterableResponseObject.Success data) { + // Dispatch to the specific method if it's the correct type + if (data instanceof IterableResponseObject.LocalSuccess) { + onSuccess((IterableResponseObject.LocalSuccess) data); + } else { + IterableLogger.w("IterableHelper", "LocalSuccessCallback received unexpected success type: " + data.getClass().getSimpleName() + + ". This callback only triggers for local-only operations. Consider using IterableSuccessCallback if you want to handle all success types."); + } + } } public interface FailureHandler { void onFailure(@NonNull String reason, @Nullable JSONObject data); } + /** + * @deprecated Use {@link AuthTokenCallback} instead for better type safety and clarity. + */ + @Deprecated public interface SuccessAuthHandler { void onSuccess(@NonNull String authToken); } diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java index 9a8589baf..c63abfc61 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java @@ -137,10 +137,10 @@ public synchronized void setRead(@NonNull IterableInAppMessage message, boolean * @param read Read state flag. true = read, false = unread * @param successHandler The callback which returns `success`. */ - public synchronized void setRead(@NonNull IterableInAppMessage message, boolean read, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public synchronized void setRead(@NonNull IterableInAppMessage message, boolean read, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { message.setRead(read); if (successHandler != null) { - successHandler.onSuccess(new JSONObject()); // passing blank json object here as onSuccess is @Nonnull + successHandler.onSuccess(IterableResponseObject.LocalSuccessResponse); // passing blank json object here as onSuccess is @Nonnull } notifyOnChange(); } @@ -279,7 +279,7 @@ public synchronized void removeMessage(@NonNull IterableInAppMessage message, @N * @param successHandler The callback which returns `success`. * @param failureHandler The callback which returns `failure`. */ - public synchronized void removeMessage(@NonNull IterableInAppMessage message, @Nullable IterableInAppDeleteActionType source, @Nullable IterableInAppLocation clickLocation, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + public synchronized void removeMessage(@NonNull IterableInAppMessage message, @Nullable IterableInAppDeleteActionType source, @Nullable IterableInAppLocation clickLocation, @Nullable IterableHelper.IterableSuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { IterableLogger.printInfo(); if (message != null) { message.setConsumed(true); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableRequestTask.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableRequestTask.java index f052da780..92815c122 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableRequestTask.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableRequestTask.java @@ -375,7 +375,8 @@ private void handleSuccessResponse(IterableApiResponse response) { } if (iterableApiRequest.successCallback != null) { - iterableApiRequest.successCallback.onSuccess(response.responseJson); + IterableResponseObject.Success iterableResponse = new IterableResponseObject.RemoteSuccess(response.responseJson); + iterableApiRequest.successCallback.onSuccess(iterableResponse); } } @@ -395,12 +396,9 @@ private static void requestNewAuthTokenAndRetry(IterableApiRequest iterableApiRe IterableApi.getInstance().getAuthManager().setIsLastAuthTokenValid(false); long retryInterval = IterableApi.getInstance().getAuthManager().getNextRetryInterval(); IterableApi.getInstance().getAuthManager().scheduleAuthTokenRefresh(retryInterval, false, data -> { - try { - String newAuthToken = data.getString("newAuthToken"); - retryRequestWithNewAuthToken(newAuthToken, iterableApiRequest); - } catch (JSONException e) { - e.printStackTrace(); - } + IterableResponseObject.AuthTokenSuccess authTokenResponse = (IterableResponseObject.AuthTokenSuccess) data; + String newAuthToken = authTokenResponse.getAuthToken(); + retryRequestWithNewAuthToken(newAuthToken, iterableApiRequest); }); } @@ -427,7 +425,7 @@ class IterableApiRequest { private ProcessorType processorType = ProcessorType.ONLINE; IterableHelper.IterableActionHandler legacyCallback; - IterableHelper.SuccessHandler successCallback; + IterableHelper.IterableSuccessCallback successCallback; IterableHelper.FailureHandler failureCallback; enum ProcessorType { @@ -455,7 +453,7 @@ void setProcessorType(ProcessorType processorType) { this.processorType = processorType; } - IterableApiRequest(String apiKey, String baseUrl, String resourcePath, JSONObject json, String requestType, String authToken, IterableHelper.SuccessHandler onSuccess, IterableHelper.FailureHandler onFailure) { + IterableApiRequest(String apiKey, String baseUrl, String resourcePath, JSONObject json, String requestType, String authToken, IterableHelper.IterableSuccessCallback onSuccess, IterableHelper.FailureHandler onFailure) { this.apiKey = apiKey; this.baseUrl = baseUrl; this.resourcePath = resourcePath; @@ -466,7 +464,7 @@ void setProcessorType(ProcessorType processorType) { this.failureCallback = onFailure; } - IterableApiRequest(String apiKey, String resourcePath, JSONObject json, String requestType, String authToken, IterableHelper.SuccessHandler onSuccess, IterableHelper.FailureHandler onFailure) { + IterableApiRequest(String apiKey, String resourcePath, JSONObject json, String requestType, String authToken, IterableHelper.IterableSuccessCallback onSuccess, IterableHelper.FailureHandler onFailure) { this.apiKey = apiKey; this.baseUrl = null; this.resourcePath = resourcePath; @@ -497,7 +495,7 @@ public JSONObject toJSONObject() throws JSONException { return jsonObject; } - static IterableApiRequest fromJSON(JSONObject jsonData, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + static IterableApiRequest fromJSON(JSONObject jsonData, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { try { String apikey = jsonData.getString("apiKey"); String resourcePath = jsonData.getString("resourcePath"); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableResponseObject.kt b/iterableapi/src/main/java/com/iterable/iterableapi/IterableResponseObject.kt new file mode 100644 index 000000000..94343de53 --- /dev/null +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableResponseObject.kt @@ -0,0 +1,51 @@ +package com.iterable.iterableapi + +import org.json.JSONObject + +sealed class IterableResponseObject( + val message: String, + val code: IterableResponseCode +) { + sealed class Success( + message: String, + ): IterableResponseObject(message, IterableResponseCode.SUCCESS) + + class GenericSuccess( + message: String, + ): Success(message) + + class RemoteSuccess(val responseJson: JSONObject): Success( + message = SuccessMessages.REMOTE_SUCCESS + ) + + class AuthTokenSuccess( + val authToken: String + ): Success( + message = SuccessMessages.AUTH_TOKEN_SUCCESS, + ) + + object LocalSuccess: Success( + message = SuccessMessages.LOCAL_SUCCESS, + ) + + + class Failure(remoteMessage: String): IterableResponseObject( + message = remoteMessage, + code = IterableResponseCode.FAILURE + ) + + companion object { + @JvmField + val LocalSuccessResponse = LocalSuccess + } + + private object SuccessMessages { + const val REMOTE_SUCCESS = "Successfully received response from remote API" + const val AUTH_TOKEN_SUCCESS = "Successfully obtained authentication token" + const val LOCAL_SUCCESS = "Operation completed locally without remote API call" + } +} + +enum class IterableResponseCode { + SUCCESS, FAILURE +} \ No newline at end of file diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/OfflineRequestProcessor.java b/iterableapi/src/main/java/com/iterable/iterableapi/OfflineRequestProcessor.java index e60b08293..684e610c2 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/OfflineRequestProcessor.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/OfflineRequestProcessor.java @@ -60,13 +60,13 @@ public void processGetRequest(@Nullable String apiKey, @NonNull String resourceP } @Override - public void processGetRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + public void processGetRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { IterableApiRequest request = new IterableApiRequest(apiKey, resourcePath, json, IterableApiRequest.GET, authToken, onSuccess, onFailure); new IterableRequestTask().execute(request); } @Override - public void processPostRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + public void processPostRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { IterableApiRequest request = new IterableApiRequest(apiKey, resourcePath, json, IterableApiRequest.POST, authToken, onSuccess, onFailure); if (isRequestOfflineCompatible(request.resourcePath) && healthMonitor.canSchedule()) { request.setProcessorType(IterableApiRequest.ProcessorType.OFFLINE); @@ -87,7 +87,7 @@ boolean isRequestOfflineCompatible(String baseUrl) { } class TaskScheduler implements IterableTaskRunner.TaskCompletedListener { - static HashMap successCallbackMap = new HashMap<>(); + static HashMap successCallbackMap = new HashMap<>(); static HashMap failureCallbackMap = new HashMap<>(); private final IterableTaskStorage taskStorage; private final IterableTaskRunner taskRunner; @@ -98,7 +98,7 @@ class TaskScheduler implements IterableTaskRunner.TaskCompletedListener { taskRunner.addTaskCompletedListener(this); } - void scheduleTask(IterableApiRequest request, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + void scheduleTask(IterableApiRequest request, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { JSONObject serializedRequest = null; try { serializedRequest = request.toJSONObject(); @@ -120,13 +120,14 @@ void scheduleTask(IterableApiRequest request, @Nullable IterableHelper.SuccessHa @MainThread @Override public void onTaskCompleted(String taskId, IterableTaskRunner.TaskResult result, IterableApiResponse response) { - IterableHelper.SuccessHandler onSuccess = successCallbackMap.get(taskId); + IterableHelper.IterableSuccessCallback onSuccess = successCallbackMap.get(taskId); IterableHelper.FailureHandler onFailure = failureCallbackMap.get(taskId); successCallbackMap.remove(taskId); failureCallbackMap.remove(taskId); if (response.success) { if (onSuccess != null) { - onSuccess.onSuccess(response.responseJson); + IterableResponseObject.RemoteSuccess successResponse = new IterableResponseObject.RemoteSuccess(response.responseJson); + onSuccess.onSuccess(successResponse); } } else { if (onFailure != null) { diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/OnlineRequestProcessor.java b/iterableapi/src/main/java/com/iterable/iterableapi/OnlineRequestProcessor.java index 013f7f0ad..a806b7b84 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/OnlineRequestProcessor.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/OnlineRequestProcessor.java @@ -22,13 +22,13 @@ public void processGetRequest(@Nullable String apiKey, @NonNull String resourceP } @Override - public void processGetRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + public void processGetRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { IterableApiRequest request = new IterableApiRequest(apiKey, resourcePath, addCreatedAtToJson(json), IterableApiRequest.GET, authToken, onSuccess, onFailure); new IterableRequestTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, request); } @Override - public void processPostRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { + public void processPostRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { IterableApiRequest request = new IterableApiRequest(apiKey, resourcePath, addCreatedAtToJson(json), IterableApiRequest.POST, authToken, onSuccess, onFailure); new IterableRequestTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, request); } diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/RequestProcessor.java b/iterableapi/src/main/java/com/iterable/iterableapi/RequestProcessor.java index 4de818fd6..497f94112 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/RequestProcessor.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/RequestProcessor.java @@ -10,8 +10,8 @@ public interface RequestProcessor { void processGetRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.IterableActionHandler onCallback); - void processGetRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure); + void processGetRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure); - void processPostRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.SuccessHandler onSuccess, @Nullable IterableHelper.FailureHandler onFailure); + void processPostRequest(@Nullable String apiKey, @NonNull String resourcePath, @NonNull JSONObject json, String authToken, @Nullable IterableHelper.IterableSuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure); void onLogout(Context context); } diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/UnknownUserMerge.java b/iterableapi/src/main/java/com/iterable/iterableapi/UnknownUserMerge.java index 4a63e3222..db4485318 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/UnknownUserMerge.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/UnknownUserMerge.java @@ -5,7 +5,7 @@ public class UnknownUserMerge { void tryMergeUser(IterableApiClient apiClient, String unknownUserId, String destinationUser, boolean isEmail, boolean merge, MergeResultCallback callback) { IterableLogger.v(TAG, "tryMergeUser"); - if (unknownUserId != null && merge) { + if (unknownUserId != null && merge) { //todo: why can we try to merge and have merge false? String destinationEmail = isEmail ? destinationUser : null; String destinationUserId = isEmail ? null : destinationUser; apiClient.mergeUser(null, unknownUserId, destinationEmail, destinationUserId, data -> { diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthSecurityTests.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthSecurityTests.java index 2fa168b59..dd441f568 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthSecurityTests.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthSecurityTests.java @@ -18,7 +18,6 @@ import com.iterable.iterableapi.unit.PathBasedQueueDispatcher; -import org.json.JSONObject; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -201,9 +200,9 @@ public void testStoreAuthData_CompletionHandler_ReceivesStoredCredentials() thro final CountDownLatch latch = new CountDownLatch(1); // Capture what the completion handler receives - spyApi.setEmail(originalEmail, new IterableHelper.SuccessHandler() { + spyApi.setEmail(originalEmail, new IterableHelper.IterableSuccessCallback() { @Override - public void onSuccess(JSONObject data) { + public void onSuccess(IterableResponseObject.Success data) { // This callback happens after completeUserLogin latch.countDown(); } diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java index bb781b9ad..1c443618d 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java @@ -59,9 +59,9 @@ public void testRequestSerialization() throws Exception { String requestType = "api"; String authToken = "authToken123##"; - IterableHelper.SuccessHandler successHandler = new IterableHelper.SuccessHandler() { + IterableHelper.IterableSuccessCallback successHandler = new IterableHelper.IterableSuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { + public void onSuccess(@NonNull IterableResponseObject.Success data) { IterableLogger.v("RequestSerializationTest", "Passed"); } }; diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java index 463cf2b7c..635883563 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java @@ -161,7 +161,7 @@ public void testUpdateEmailPersistence() throws Exception { IterableApi.getInstance().updateEmail(newEmail); shadowOf(getMainLooper()).idle(); - verify(mockApiClient).updateEmail(eq(newEmail), nullable(IterableHelper.SuccessHandler.class), nullable(IterableHelper.FailureHandler.class)); + verify(mockApiClient).updateEmail(eq(newEmail), nullable(IterableHelper.IterableSuccessCallback.class), nullable(IterableHelper.FailureHandler.class)); server.takeRequest(1, TimeUnit.SECONDS); assertEquals("new@email.com", IterableApi.getInstance().getEmail()); @@ -175,9 +175,9 @@ public void testSetEmailWithCallback() { IterableApi.initialize(getContext(), "apiKey"); String email = "test@example.com"; - IterableApi.getInstance().setEmail(email, new IterableHelper.SuccessHandler() { + IterableApi.getInstance().setEmail(email, new IterableHelper.IterableSuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { + public void onSuccess(@NonNull IterableResponseObject.Success data) { assertTrue(true); // callback should be called with success } }, new IterableHelper.FailureHandler() { @@ -193,9 +193,9 @@ public void testSetUserIdWithCallback() { IterableApi.initialize(getContext(), "apiKey"); String userId = "test_user_id"; - IterableApi.getInstance().setUserId(userId, new IterableHelper.SuccessHandler() { + IterableApi.getInstance().setUserId(userId, new IterableHelper.IterableSuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { + public void onSuccess(@NonNull IterableResponseObject.Success data) { assertTrue(true); // callback should be called with success } }, new IterableHelper.FailureHandler() { @@ -1015,7 +1015,7 @@ public void testRegisterDeviceTokenSuccessCallback_CreatesWrappedHandler() throw IterableApi.getInstance().setVisitorUsageTracked(true); // Create a mock success handler - IterableHelper.SuccessHandler originalHandler = mock(IterableHelper.SuccessHandler.class); + IterableHelper.IterableSuccessCallback originalHandler = mock(IterableHelper.IterableSuccessCallback.class); // Set up user with success handler IterableApi.getInstance().setEmail("test@example.com", originalHandler, null); @@ -1032,7 +1032,7 @@ public void testRegisterDeviceTokenSuccessCallback_CreatesWrappedHandler() throw shadowOf(getMainLooper()).idle(); // Verify: registerDeviceToken was called with a success handler - ArgumentCaptor successCaptor = ArgumentCaptor.forClass(IterableHelper.SuccessHandler.class); + ArgumentCaptor successCaptor = ArgumentCaptor.forClass(IterableHelper.IterableSuccessCallback.class); verify(mockClient, timeout(1000)).registerDeviceToken( eq("test@example.com"), nullable(String.class), @@ -1075,7 +1075,7 @@ public void testRegisterDeviceTokenSuccessCallback_WithoutOriginalHandler() thro shadowOf(getMainLooper()).idle(); // Verify: registerDeviceToken was called with a success handler (the wrapper) - ArgumentCaptor successCaptor = ArgumentCaptor.forClass(IterableHelper.SuccessHandler.class); + ArgumentCaptor successCaptor = ArgumentCaptor.forClass(IterableHelper.IterableSuccessCallback.class); verify(mockClient, timeout(1000)).registerDeviceToken( nullable(String.class), eq("test_user_123"), @@ -1112,7 +1112,7 @@ public void testRegisterDeviceTokenIntegration_ConsentLoggingTriggered() throws IterableApi.getInstance().setVisitorUsageTracked(true); // Create a success handler and set user - IterableHelper.SuccessHandler successHandler = mock(IterableHelper.SuccessHandler.class); + IterableHelper.IterableSuccessCallback successHandler = mock(IterableHelper.IterableSuccessCallback.class); IterableApi.getInstance().setEmail("test@example.com", successHandler, null); // Execute: Register device token @@ -1144,7 +1144,7 @@ public void testRegisterDeviceTokenIntegration_ConsentLoggingTriggered() throws assertTrue("Should have made consent request", foundConsentRequest); // Verify: Original success handler was called at least once - verify(successHandler, atLeastOnce()).onSuccess(any(JSONObject.class)); + verify(successHandler, atLeastOnce()).onSuccess(any(IterableResponseObject.Success.class)); } @Test @@ -1162,7 +1162,7 @@ public void testRegisterDeviceTokenIntegration_ConsentLoggingNotTriggeredWhenDis // Set up other conditions IterableApi.getInstance().setVisitorUsageTracked(true); - IterableHelper.SuccessHandler successHandler = mock(IterableHelper.SuccessHandler.class); + IterableHelper.IterableSuccessCallback successHandler = mock(IterableHelper.IterableSuccessCallback.class); IterableApi.getInstance().setEmail("test@example.com", successHandler, null); // Execute: Register device token @@ -1194,7 +1194,7 @@ public void testRegisterDeviceTokenIntegration_ConsentLoggingNotTriggeredWhenDis assertFalse("Should NOT have made consent request", foundConsentRequest); // Verify: Original success handler was called at least once - verify(successHandler, atLeastOnce()).onSuccess(any(JSONObject.class)); + verify(successHandler, atLeastOnce()).onSuccess(any(IterableResponseObject.Success.class)); } //endregion diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableAsyncInitializationTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableAsyncInitializationTest.java index bd387d64c..f7b9b3546 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableAsyncInitializationTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableAsyncInitializationTest.java @@ -1317,9 +1317,9 @@ public void onSDKInitialized() { // Call multiple overloaded method chains during initialization // Each overload internally delegates to the full signature IterableApi.getInstance().setEmail("user1@test.com"); - IterableApi.getInstance().setEmail("user2@test.com", (IterableHelper.SuccessHandler) null, null); + IterableApi.getInstance().setEmail("user2@test.com", (IterableHelper.IterableSuccessCallback) null, null); IterableApi.getInstance().setUserId("user123"); - IterableApi.getInstance().setUserId("user456", (IterableHelper.SuccessHandler) null, null); + IterableApi.getInstance().setUserId("user456", (IterableHelper.IterableSuccessCallback) null, null); IterableApi.getInstance().updateEmail("newemail@test.com"); // Verify operations are queued diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableHelperUnitTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableHelperUnitTest.java new file mode 100644 index 000000000..1407bc6f8 --- /dev/null +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableHelperUnitTest.java @@ -0,0 +1,343 @@ +package com.iterable.iterableapi; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Tests the functionality of IterableHelper callback interfaces + */ +public class IterableHelperUnitTest { + @Test + public void actionHandlerCallback() throws Exception { + final String resultString = "testString"; + + IterableHelper.IterableActionHandler clickCallback = new IterableHelper.IterableActionHandler() { + @Override + public void execute(String result) { + assertEquals(result, resultString); + } + }; + clickCallback.execute(resultString); + } + + // ========== IterableSuccessCallback Tests ========== + + @Test + public void testIterableSuccessCallback_WithRemoteSuccess() throws Exception { + JSONObject testJson = new JSONObject().put("key", "value"); + IterableResponseObject.RemoteSuccess remoteSuccess = new IterableResponseObject.RemoteSuccess(testJson); + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + + IterableHelper.IterableSuccessCallback callback = data -> { + callbackInvoked.set(true); + assertTrue(data instanceof IterableResponseObject.RemoteSuccess); + try { + assertEquals("value", ((IterableResponseObject.RemoteSuccess) data).getResponseJson().getString("key")); + } catch (JSONException e) { + throw new RuntimeException(e); + } + }; + + callback.onSuccess(remoteSuccess); + assertTrue("Callback should be invoked", callbackInvoked.get()); + } + + @Test + public void testIterableSuccessCallback_WithLocalSuccess() { + IterableResponseObject.Success localSuccess = IterableResponseObject.LocalSuccessResponse; + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + + IterableHelper.IterableSuccessCallback callback = data -> { + callbackInvoked.set(true); + assertTrue(data instanceof IterableResponseObject.LocalSuccess); + assertNotNull(data.getMessage()); + }; + + callback.onSuccess(localSuccess); + assertTrue("Callback should be invoked", callbackInvoked.get()); + } + + @Test + public void testIterableSuccessCallback_WithAuthTokenSuccess() { + String testToken = "test-jwt-token-123"; + IterableResponseObject.AuthTokenSuccess authSuccess = new IterableResponseObject.AuthTokenSuccess(testToken); + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + + IterableHelper.IterableSuccessCallback callback = data -> { + callbackInvoked.set(true); + assertTrue(data instanceof IterableResponseObject.AuthTokenSuccess); + assertEquals(testToken, ((IterableResponseObject.AuthTokenSuccess) data).getAuthToken()); + }; + + callback.onSuccess(authSuccess); + assertTrue("Callback should be invoked", callbackInvoked.get()); + } + + // ========== RemoteSuccessCallback Tests ========== + + @Test + public void testRemoteSuccessCallback_WithCorrectType() throws Exception { + JSONObject testJson = new JSONObject().put("status", "success"); + IterableResponseObject.RemoteSuccess remoteSuccess = new IterableResponseObject.RemoteSuccess(testJson); + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableHelper.RemoteSuccessCallback callback = new IterableHelper.RemoteSuccessCallback() { + @Override + public void onSuccess(IterableResponseObject.RemoteSuccess data) { + typedCallbackInvoked.set(true); + assertEquals(testJson, data.getResponseJson()); + } + }; + + callback.onSuccess((IterableResponseObject.Success) remoteSuccess); + assertTrue("Typed callback should be invoked for RemoteSuccess", typedCallbackInvoked.get()); + } + + @Test + public void testRemoteSuccessCallback_WithLocalSuccess_LogsWarning() { + IterableResponseObject.Success localSuccess = IterableResponseObject.LocalSuccessResponse; + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableHelper.RemoteSuccessCallback callback = new IterableHelper.RemoteSuccessCallback() { + @Override + public void onSuccess(IterableResponseObject.RemoteSuccess data) { + typedCallbackInvoked.set(true); + } + }; + + // Should not invoke typed callback, should log warning instead + callback.onSuccess(localSuccess); + assertFalse("Typed callback should NOT be invoked for LocalSuccess", typedCallbackInvoked.get()); + } + + // ========== LocalSuccessCallback Tests ========== + + @Test + public void testLocalSuccessCallback_WithCorrectType() { + IterableResponseObject.Success localSuccess = IterableResponseObject.LocalSuccessResponse; + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableHelper.LocalSuccessCallback callback = new IterableHelper.LocalSuccessCallback() { + @Override + public void onSuccess(IterableResponseObject.LocalSuccess data) { + typedCallbackInvoked.set(true); + assertNotNull(data.getMessage()); + } + }; + + callback.onSuccess(localSuccess); + assertTrue("Typed callback should be invoked for LocalSuccess", typedCallbackInvoked.get()); + } + + @Test + public void testLocalSuccessCallback_WithRemoteSuccess_LogsWarning() throws Exception { + JSONObject testJson = new JSONObject().put("key", "value"); + IterableResponseObject.RemoteSuccess remoteSuccess = new IterableResponseObject.RemoteSuccess(testJson); + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableHelper.LocalSuccessCallback callback = new IterableHelper.LocalSuccessCallback() { + @Override + public void onSuccess(IterableResponseObject.LocalSuccess data) { + typedCallbackInvoked.set(true); + } + }; + + // Should not invoke typed callback, should log warning instead + callback.onSuccess((IterableResponseObject.Success) remoteSuccess); + assertFalse("Typed callback should NOT be invoked for RemoteSuccess", typedCallbackInvoked.get()); + } + + // ========== AuthTokenCallback Tests ========== + + @Test + public void testAuthTokenCallback_WithCorrectType() { + String testToken = "jwt-token-xyz"; + IterableResponseObject.AuthTokenSuccess authSuccess = new IterableResponseObject.AuthTokenSuccess(testToken); + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableHelper.AuthTokenCallback callback = new IterableHelper.AuthTokenCallback() { + @Override + public void onSuccess(IterableResponseObject.AuthTokenSuccess data) { + typedCallbackInvoked.set(true); + assertEquals(testToken, data.getAuthToken()); + } + }; + + callback.onSuccess((IterableResponseObject.Success) authSuccess); + assertTrue("Typed callback should be invoked for AuthTokenSuccess", typedCallbackInvoked.get()); + } + + @Test + public void testAuthTokenCallback_WithWrongType_LogsWarning() { + IterableResponseObject.Success localSuccess = IterableResponseObject.LocalSuccessResponse; + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableHelper.AuthTokenCallback callback = new IterableHelper.AuthTokenCallback() { + @Override + public void onSuccess(IterableResponseObject.AuthTokenSuccess data) { + typedCallbackInvoked.set(true); + } + }; + + // Should not invoke typed callback, should log warning instead + callback.onSuccess(localSuccess); + assertFalse("Typed callback should NOT be invoked for LocalSuccess", typedCallbackInvoked.get()); + } + + // ========== SuccessHandler (Deprecated) Backward Compatibility Tests ========== + + @Test + public void testSuccessHandler_WithRemoteSuccess_PassesCorrectJSON() throws Exception { + JSONObject testJson = new JSONObject() + .put("key1", "value1") + .put("key2", 123); + IterableResponseObject.RemoteSuccess remoteSuccess = new IterableResponseObject.RemoteSuccess(testJson); + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + AtomicReference receivedJson = new AtomicReference<>(); + + IterableHelper.SuccessHandler handler = new IterableHelper.SuccessHandler() { + @Override + public void onSuccess(JSONObject data) { + callbackInvoked.set(true); + receivedJson.set(data); + } + }; + + handler.onSuccess((IterableResponseObject.Success) remoteSuccess); + + assertTrue("Callback should be invoked", callbackInvoked.get()); + assertNotNull("JSON should not be null", receivedJson.get()); + assertEquals("value1", receivedJson.get().getString("key1")); + assertEquals(123, receivedJson.get().getInt("key2")); + } + + @Test + public void testSuccessHandler_WithLocalSuccess_PassesMessageJSON() throws Exception { + IterableResponseObject.Success localSuccess = IterableResponseObject.LocalSuccessResponse; + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + AtomicReference receivedJson = new AtomicReference<>(); + + IterableHelper.SuccessHandler handler = new IterableHelper.SuccessHandler() { + @Override + public void onSuccess(JSONObject data) { + callbackInvoked.set(true); + receivedJson.set(data); + } + }; + + handler.onSuccess(localSuccess); + + assertTrue("Callback should be invoked", callbackInvoked.get()); + assertNotNull("JSON should not be null", receivedJson.get()); + assertTrue("JSON should contain message", receivedJson.get().has("message")); + assertNotNull(receivedJson.get().getString("message")); + } + + @Test + public void testSuccessHandler_WithAuthTokenSuccess_PassesTokenJSON() throws Exception { + String testToken = "test-auth-token"; + IterableResponseObject.AuthTokenSuccess authSuccess = new IterableResponseObject.AuthTokenSuccess(testToken); + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + AtomicReference receivedJson = new AtomicReference<>(); + + IterableHelper.SuccessHandler handler = new IterableHelper.SuccessHandler() { + @Override + public void onSuccess(JSONObject data) { + callbackInvoked.set(true); + receivedJson.set(data); + } + }; + + handler.onSuccess((IterableResponseObject.Success) authSuccess); + + assertTrue("Callback should be invoked", callbackInvoked.get()); + assertNotNull("JSON should not be null", receivedJson.get()); + assertTrue("JSON should contain newAuthToken", receivedJson.get().has("newAuthToken")); + assertEquals(testToken, receivedJson.get().getString("newAuthToken")); + } + + @Test + public void testSuccessHandler_DoesNotMutateOriginalJSON() throws Exception { + JSONObject originalJson = new JSONObject().put("original", "value"); + IterableResponseObject.RemoteSuccess remoteSuccess = new IterableResponseObject.RemoteSuccess(originalJson); + + IterableHelper.SuccessHandler handler = new IterableHelper.SuccessHandler() { + @Override + public void onSuccess(JSONObject data) { + // Received JSON is a copy, mutations here shouldn't affect original + try { + data.put("modified", "newValue"); + } catch (JSONException e) { + // Ignore + } + } + }; + + handler.onSuccess((IterableResponseObject.Success) remoteSuccess); + + // Original JSON should not have the "modified" field + assertFalse("Original JSON should not be mutated", originalJson.has("modified")); + assertTrue("Original JSON should still have original field", originalJson.has("original")); + } + + // ========== FailureHandler Tests ========== + + @Test + public void testFailureHandler_ReceivesReasonAndData() throws Exception { + String expectedReason = "Network error"; + JSONObject expectedData = new JSONObject().put("errorCode", 500); + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + + IterableHelper.FailureHandler handler = (reason, data) -> { + callbackInvoked.set(true); + assertEquals(expectedReason, reason); + assertNotNull(data); + try { + assertEquals(500, data.getInt("errorCode")); + } catch (JSONException e) { + throw new RuntimeException(e); + } + }; + + handler.onFailure(expectedReason, expectedData); + assertTrue("Failure handler should be invoked", callbackInvoked.get()); + } + + @Test + public void testFailureHandler_HandlesNullData() { + String expectedReason = "Unknown error"; + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + + IterableHelper.FailureHandler handler = (reason, data) -> { + callbackInvoked.set(true); + assertEquals(expectedReason, reason); + // Data can be null, should not throw + }; + + handler.onFailure(expectedReason, null); + assertTrue("Failure handler should be invoked with null data", callbackInvoked.get()); + } +} \ No newline at end of file diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppHTMLNotificationTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppHTMLNotificationTest.java index ceaf7c586..68f3472f2 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppHTMLNotificationTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppHTMLNotificationTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertTrue; import static org.robolectric.Shadows.shadowOf; + public class IterableInAppHTMLNotificationTest extends BaseTest { private ActivityController controller; diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java index 3dd8a191c..a0b248b4d 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java @@ -19,6 +19,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; + public class IterableInAppManagerSyncTest extends BaseTest { @Mock diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java index e18614061..f030e85c3 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java @@ -8,7 +8,6 @@ import androidx.fragment.app.FragmentActivity; import java.util.List; - import com.iterable.iterableapi.unit.PathBasedQueueDispatcher; import org.json.JSONArray; diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java index 868cb12b6..25a5fc3be 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java @@ -98,9 +98,9 @@ public void testRemoveMessageSuccessCallbackOnSuccessfulResponse() throws Except final JSONObject responseData = new JSONObject("{\"key\":\"value\"}"); dispatcher.enqueueResponse("/events/inAppConsume", new MockResponse().setResponseCode(200).setBody(responseData.toString())); - inAppManager.removeMessage(inboxMessages.get(0), null, null, new IterableHelper.SuccessHandler() { + inAppManager.removeMessage(inboxMessages.get(0), null, null, new IterableHelper.IterableSuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { + public void onSuccess(@NonNull IterableResponseObject.Success data) { signal.countDown(); } }, new IterableHelper.FailureHandler() { @@ -127,9 +127,9 @@ public void testRemoveMessageFailureCallbackOnFailedResponse() throws Exception final JSONObject responseData = new JSONObject("{\"key\":\"value\"}"); dispatcher.enqueueResponse("/events/inAppConsume", new MockResponse().setResponseCode(500).setBody(responseData.toString())); - inAppManager.removeMessage(inboxMessages.get(0), null, null, new IterableHelper.SuccessHandler() { + inAppManager.removeMessage(inboxMessages.get(0), null, null, new IterableHelper.IterableSuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { + public void onSuccess(@NonNull IterableResponseObject.Success data) { assertFalse(true); } }, new IterableHelper.FailureHandler() { @@ -163,9 +163,9 @@ public void testSetRead() throws Exception { // Set first message as read with a callback final boolean[] callbackCalled = { false }; - inAppManager.setRead(inboxMessages.get(0), true, new IterableHelper.SuccessHandler() { + inAppManager.setRead(inboxMessages.get(0), true, new IterableHelper.IterableSuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { + public void onSuccess(@NonNull IterableResponseObject.Success data) { callbackCalled[0] = true; assertTrue(callbackCalled[0]); } diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java index 214b667ae..c3104a19e 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java @@ -75,7 +75,7 @@ public void testEnableDevice() throws Exception { verify(apiMock, timeout(100)).registerDeviceToken(eq(IterableTestUtils.userEmail), nullable(String.class), isNull(), eq(INTEGRATION_NAME), eq(TEST_TOKEN), eq(deviceAttributes)); - verify(apiMock, never()).disableToken(eq(IterableTestUtils.userEmail), nullable(String.class), nullable(String.class), any(String.class), nullable(IterableHelper.SuccessHandler.class), nullable(IterableHelper.FailureHandler.class)); + verify(apiMock, never()).disableToken(eq(IterableTestUtils.userEmail), nullable(String.class), nullable(String.class), any(String.class), nullable(IterableHelper.IterableSuccessCallback.class), nullable(IterableHelper.FailureHandler.class)); } @Test @@ -87,6 +87,6 @@ public void testDisableDevice() throws Exception { new IterablePushRegistrationTask().execute(data); shadowOf(getMainLooper()).idle(); - verify(apiMock, timeout(100)).disableToken(eq(IterableTestUtils.userEmail), isNull(), isNull(), eq(TEST_TOKEN), nullable(IterableHelper.SuccessHandler.class), nullable(IterableHelper.FailureHandler.class)); + verify(apiMock, timeout(100)).disableToken(eq(IterableTestUtils.userEmail), isNull(), isNull(), eq(TEST_TOKEN), nullable(IterableHelper.IterableSuccessCallback.class), nullable(IterableHelper.FailureHandler.class)); } } \ No newline at end of file diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/TaskSchedulerTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/TaskSchedulerTest.java index c79956909..cb003a8b6 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/TaskSchedulerTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/TaskSchedulerTest.java @@ -35,12 +35,12 @@ public void testScheduleTaskCreatesTaskInStorage() throws Exception { @Test public void testSuccessCallbackIsCalledOnCompletion() throws Exception { - IterableHelper.SuccessHandler successHandler = mock(IterableHelper.SuccessHandler.class); + IterableHelper.IterableSuccessCallback successHandler = mock(IterableHelper.IterableSuccessCallback.class); IterableApiRequest request = new IterableApiRequest("apiKey", "api/test", new JSONObject(), "POST", null, null, null); when(mockTaskStorage.createTask(any(String.class), any(IterableTaskType.class), any(String.class))).thenReturn("testTaskId"); taskScheduler.scheduleTask(request, successHandler, null); taskScheduler.onTaskCompleted("testTaskId", IterableTaskRunner.TaskResult.SUCCESS, IterableApiResponse.success(200, "", new JSONObject())); - verify(successHandler).onSuccess(any(JSONObject.class)); + verify(successHandler).onSuccess(any(IterableResponseObject.Success.class)); } @Test diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/unit/IterableHelperUnitTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/unit/IterableHelperUnitTest.java deleted file mode 100644 index 14ff7eabb..000000000 --- a/iterableapi/src/test/java/com/iterable/iterableapi/unit/IterableHelperUnitTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.iterable.iterableapi.unit; - -import com.iterable.iterableapi.IterableHelper; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -/** - * IterableConstants tests the functionality in IterableHelper - */ -public class IterableHelperUnitTest { - - @Test - public void actionHandlerCallback() throws Exception { - final String resultString = "testString"; - - IterableHelper.IterableActionHandler clickCallback = new IterableHelper.IterableActionHandler() { - @Override - public void execute(String result) { - assertEquals(result, resultString); - } - }; - clickCallback.execute(resultString); - } -} \ No newline at end of file