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..83479c723 100644 --- a/iterableapi/src/androidTest/java/com/iterable/iterableapi/IterableApiResponseTest.java +++ b/iterableapi/src/androidTest/java/com/iterable/iterableapi/IterableApiResponseTest.java @@ -30,6 +30,9 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertThat; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; + @RunWith(AndroidJUnit4.class) @MediumTest public class IterableApiResponseTest { @@ -77,10 +80,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 IterableCallbackHandlers.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 +225,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 IterableCallbackHandlers.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..697fefa8b 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java @@ -12,8 +12,13 @@ import androidx.annotation.VisibleForTesting; import androidx.core.app.NotificationManagerCompat; +import com.iterable.iterableapi.response.IterableAuthResponseObject; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; +import com.iterable.iterableapi.response.handlers.auth.IterableAuthCallbackHandlers; import com.iterable.iterableapi.util.DeviceInfoUtils; +import org.jetbrains.annotations.NotNull; import org.json.JSONException; import org.json.JSONObject; @@ -43,7 +48,7 @@ public class IterableApi { private IterableNotificationData _notificationData; private String _deviceId; private boolean _firstForegroundHandled; - private IterableHelper.SuccessHandler _setUserSuccessCallbackHandler; + private IterableCallbackHandlers.SuccessCallback _setUserSuccessCallbackHandler; private IterableHelper.FailureHandler _setUserFailureCallbackHandler; IterableApiClient apiClient = new IterableApiClient(new IterableApiAuthProvider()); @@ -310,7 +315,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 IterableCallbackHandlers.SuccessCallback onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { if (!checkSDKInitialization()) { return; } @@ -328,7 +333,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 IterableCallbackHandlers.SuccessCallback onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { if (!checkSDKInitialization()) { return; } @@ -415,7 +420,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)); + IterableAuthCallbackHandlers.AuthTokenCallback callback = data -> attemptMergeAndEventReplay(userIdOrEmail, isEmail, merge, replay, isUnknown, failureHandler); + getAuthManager().requestNewAuthToken(false, callback); } } @@ -451,13 +457,22 @@ 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(new IterableResponseObject.LocalSuccess("As autoPushRegistration is false, setEmail is completed locally")); } getInAppManager().syncInApp(); getEmbeddedManager().syncMessages(); } + private void invokeSetUserSuccessHandler(IterableResponseObject.Success responseObject) { + if (_setUserSuccessCallbackHandler != null) { + _setUserSuccessCallbackHandler.onSuccess(responseObject); + // Clear the callback after invoking to prevent double-calls + _setUserSuccessCallbackHandler = null; + _setUserFailureCallbackHandler = null; + } + } + private final IterableActivityMonitor.AppStateCallback activityMonitorListener = new IterableActivityMonitor.AppStateCallback() { @Override public void onSwitchToForeground() { @@ -700,7 +715,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 IterableCallbackHandlers.SuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { if (deviceToken == null) { IterableLogger.d(TAG, "device token not available"); return; @@ -729,36 +744,42 @@ 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(); + IterableCallbackHandlers.SuccessCallback 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 IterableCallbackHandlers.SuccessCallback wrapSetUserCallbackForRemoteCall() { + IterableCallbackHandlers.SuccessCallback wrappedSuccessHandler = null; if (_setUserSuccessCallbackHandler != null || (config.enableUnknownUserActivation && getVisitorUsageTracked() && config.identityResolution.getReplayOnVisitorToKnown())) { - final IterableHelper.SuccessHandler originalSuccessHandler = _setUserSuccessCallbackHandler; + final IterableCallbackHandlers.SuccessCallback originalSuccessHandler = _setUserSuccessCallbackHandler; // todo: not sure if we need to store it before instead of just calling the callback on success wrappedSuccessHandler = data -> { trackConsentOnDeviceRegistration(); if (originalSuccessHandler != null) { originalSuccessHandler.onSuccess(data); + + _setUserSuccessCallbackHandler = null; + _setUserFailureCallbackHandler = null; } }; } 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; + final IterableHelper.FailureHandler originalFailureHandler = _setUserFailureCallbackHandler; // todo: not sure if we need to store it before instead of just calling the callback on success wrappedFailureHandler = (reason, data) -> { trackConsentOnDeviceRegistration(); if (originalFailureHandler != null) { originalFailureHandler.onFailure(reason, data); + + _setUserSuccessCallbackHandler = null; + _setUserFailureCallbackHandler = null; } }; } @@ -997,11 +1018,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 IterableCallbackHandlers.SuccessCallback 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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> setEmail(email, null, identityResolution, successHandler, failureHandler), "setEmail(" + maskPII(email) + ", identityResolution, callbacks)"); } @@ -1013,11 +1034,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 IterableCallbackHandlers.SuccessCallback 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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { boolean replay = isReplay(iterableIdentityResolution); boolean merge = isMerge(iterableIdentityResolution); @@ -1066,11 +1087,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 IterableCallbackHandlers.SuccessCallback 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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> setUserId(userId, null, identityResolution, successHandler, failureHandler, false), "setUserId(" + maskPII(userId) + ", identityResolution, callbacks)"); } @@ -1083,11 +1104,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 IterableCallbackHandlers.SuccessCallback 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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler, boolean isUnknown) { boolean replay = isReplay(iterableIdentityResolution); boolean merge = isMerge(iterableIdentityResolution); @@ -1133,7 +1154,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 +1273,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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { IterableInAppMessage message = getInAppManager().getMessageById(messageId); if (checkIfMessageIsNull(message, failureHandler)) { return; @@ -1291,7 +1312,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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { if (!checkSDKInitialization()) { return; } @@ -1498,7 +1519,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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { queueOrExecute(() -> updateEmail(newEmail, null, successHandler, failureHandler), "updateEmail(" + maskPII(newEmail) + ", callbacks)"); } @@ -1509,7 +1530,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 IterableCallbackHandlers.SuccessCallback 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 +1542,9 @@ public void updateEmail(final @NonNull String newEmail, final @Nullable String a return; } - apiClient.updateEmail(newEmail, new IterableHelper.SuccessHandler() { + apiClient.updateEmail(newEmail, new IterableCallbackHandlers.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..f90a04779 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java @@ -7,6 +7,7 @@ import androidx.annotation.Nullable; import androidx.core.app.NotificationManagerCompat; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; import com.iterable.iterableapi.util.DeviceInfoUtils; import org.json.JSONArray; @@ -222,7 +223,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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { JSONObject requestJSON = new JSONObject(); try { @@ -319,7 +320,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 IterableCallbackHandlers.SuccessCallback onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { JSONObject requestJSON = new JSONObject(); try { @@ -488,7 +489,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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable final IterableHelper.FailureHandler failureHandler) { JSONObject requestJSON = new JSONObject(); try { @@ -604,7 +605,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 IterableCallbackHandlers.SuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { JSONObject requestJSON = new JSONObject(); try { requestJSON.put(IterableConstants.KEY_TOKEN, deviceToken); @@ -620,7 +621,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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable final IterableHelper.FailureHandler failureHandler) { Context context = authProvider.getContext(); JSONObject requestJSON = new JSONObject(); try { @@ -756,11 +757,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 IterableCallbackHandlers.SuccessCallback 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 IterableCallbackHandlers.SuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { getRequestProcessor().processPostRequest(authProvider.getApiKey(), resourcePath, json, authToken, onSuccess, onFailure); } @@ -774,7 +775,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 IterableCallbackHandlers.SuccessCallback onSuccess, @NonNull IterableHelper.FailureHandler onFailure) { getRequestProcessor().processGetRequest(authProvider.getApiKey(), resourcePath, json, authProvider.getAuthToken(), onSuccess, onFailure); } @@ -783,7 +784,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 IterableCallbackHandlers.SuccessCallback successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { JSONObject requestJson = new JSONObject(); try { if (sourceEmail != null && !sourceEmail.isEmpty()) { @@ -808,7 +809,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 IterableCallbackHandlers.SuccessCallback 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..132685a3f 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableAuthManager.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableAuthManager.java @@ -2,10 +2,14 @@ import android.util.Base64; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import org.json.JSONException; +import com.iterable.iterableapi.response.IterableAuthResponseObject; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; + import org.json.JSONObject; import java.io.UnsupportedEncodingException; @@ -45,7 +49,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, IterableCallbackHandlers.SuccessCallback successCallback) { requestNewAuthToken(hasFailedPriorAuth, successCallback, true); } @@ -67,19 +71,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, IterableCallbackHandlers.SuccessCallback successCallback) { + IterableAuthResponseObject.Success remoteSuccess = new IterableAuthResponseObject.Success(authToken); + successCallback.onSuccess(remoteSuccess); + } public synchronized void requestNewAuthToken( boolean hasFailedPriorAuth, - final IterableHelper.SuccessHandler successCallback, + final IterableCallbackHandlers.SuccessCallback successCallback, boolean shouldIgnoreRetryPolicy) { if (!shouldIgnoreRetryPolicy && (pauseAuthRetry || (retryCount >= authRetryPolicy.maxRetry))) { return; @@ -130,7 +130,7 @@ public void run() { } } - private void handleAuthTokenSuccess(String authToken, IterableHelper.SuccessHandler successCallback) { + private void handleAuthTokenSuccess(String authToken, IterableCallbackHandlers.SuccessCallback successCallback) { if (authToken != null) { IterableApi.getInstance().setAuthToken(authToken); queueExpirationRefresh(authToken); @@ -210,7 +210,7 @@ long getNextRetryInterval() { return nextRetryInterval; } - void scheduleAuthTokenRefresh(long timeDuration, boolean isScheduledRefresh, final IterableHelper.SuccessHandler successCallback) { + void scheduleAuthTokenRefresh(long timeDuration, boolean isScheduledRefresh, final IterableCallbackHandlers.SuccessCallback 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..90dac7aea 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt @@ -1,7 +1,8 @@ package com.iterable.iterableapi import android.content.Context -import com.iterable.iterableapi.IterableHelper.SuccessHandler +import com.iterable.iterableapi.response.IterableResponseObject +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers import org.json.JSONException import org.json.JSONObject @@ -89,61 +90,66 @@ public class IterableEmbeddedManager : IterableActivityMonitor.AppStateCallback if (iterableApi.config.enableEmbeddedMessaging) { IterableLogger.v(TAG, "Syncing messages...") - IterableApi.sharedInstance.getEmbeddedMessages(placementIds, { data -> - 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) - if (placementsArray != null) { - //if there are no placements in the payload - //reset the local message storage and trigger a UI update - if (placementsArray.length() == 0) { - reset() - if (previousPlacementIds.isNotEmpty()) { - updateHandleListeners.forEach { - IterableLogger.d(TAG, "Calling updateHandler") - it.onMessagesUpdated() + + IterableApi.sharedInstance.getEmbeddedMessages(placementIds, object : IterableCallbackHandlers.RemoteSuccessCallback { + override fun onSuccess(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.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 + if (placementsArray.length() == 0) { + reset() + if (previousPlacementIds.isNotEmpty()) { + updateHandleListeners.forEach { + IterableLogger.d(TAG, "Calling updateHandler") + it.onMessagesUpdated() + } + } + } else { + for (i in 0 until placementsArray.length()) { + val placementJson = placementsArray.optJSONObject(i) + val placement = + IterableEmbeddedPlacement.fromJSONObject(placementJson) + val placementId = placement.placementId + val messages = placement.messages + + currentPlacementIds.add(placementId) + updateLocalMessageMap(placementId, messages) } - } - } else { - for (i in 0 until placementsArray.length()) { - val placementJson = placementsArray.optJSONObject(i) - val placement = - IterableEmbeddedPlacement.fromJSONObject(placementJson) - val placementId = placement.placementId - val messages = placement.messages - - currentPlacementIds.add(placementId) - updateLocalMessageMap(placementId, messages) } } - } - // compare previous placements to the current placement payload - val removedPlacementIds = - previousPlacementIds.subtract(currentPlacementIds.toSet()) + // compare previous placements to the current placement payload + val removedPlacementIds = + previousPlacementIds.subtract(currentPlacementIds.toSet()) - //if there are placements removed, update the local storage and trigger UI update - if (removedPlacementIds.isNotEmpty()) { - removedPlacementIds.forEach { - localPlacementMessagesMap.remove(it) - } + //if there are placements removed, update the local storage and trigger UI update + if (removedPlacementIds.isNotEmpty()) { + removedPlacementIds.forEach { + localPlacementMessagesMap.remove(it) + } - updateHandleListeners.forEach { - IterableLogger.d(TAG, "Calling updateHandler") - it.onMessagesUpdated() + updateHandleListeners.forEach { + IterableLogger.d(TAG, "Calling updateHandler") + it.onMessagesUpdated() + } } - } - //store placements from payload for next comparison - localPlacementIds = currentPlacementIds + //store placements from payload for next comparison + localPlacementIds = currentPlacementIds - } catch (e: JSONException) { - IterableLogger.e(TAG, e.toString()) + } catch (e: JSONException) { + IterableLogger.e(TAG, e.toString()) + } } + }, object : IterableHelper.FailureHandler { override fun onFailure(reason: String, data: JSONObject?) { if (reason.equals( diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableHelper.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableHelper.java index a949a0727..f2e53013b 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableHelper.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableHelper.java @@ -1,10 +1,17 @@ package com.iterable.iterableapi; import android.net.Uri; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.iterable.iterableapi.response.IterableAuthResponseObject; +import com.iterable.iterableapi.response.IterableResponseObject; + +import org.json.JSONException; import org.json.JSONObject; +import com.iterable.iterableapi.response.handlers.auth.IterableAuthCallbackHandlers; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; /** * Created by David Truong dt@iterable.com @@ -22,14 +29,69 @@ public interface IterableUrlCallback { void execute(@Nullable Uri url); } - public interface SuccessHandler { + /** + * @deprecated Use {@link } instead. + *

+ * This interface is deprecated and will be removed in a future version. + * Replace all usages with {@link IterableCallbackHandlers.SuccessCallback} for general success handling, + * or {@link IterableCallbackHandlers.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):
+     * IterableCallbackHandlers.SuccessCallback successHandler = data -> {
+     *     // Your code here - see JavaDoc for accessing response data
+     * };
+     * 
+ * + * @see IterableCallbackHandlers.SuccessCallback + * @see IterableCallbackHandlers.RemoteSuccessCallback + * @see IterableCallbackHandlers.LocalSuccessCallback + */ + @Deprecated + public interface SuccessHandler extends IterableCallbackHandlers.SuccessCallback { 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 IterableAuthResponseObject.Success) { + jsonObject.put("newAuthToken", ((IterableAuthResponseObject.Success) 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); + } } + + public interface FailureHandler { void onFailure(@NonNull String reason, @Nullable JSONObject data); } + /** + * @deprecated Use {@link IterableAuthCallbackHandlers.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..5cb466632 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java @@ -12,6 +12,8 @@ import com.iterable.iterableapi.IterableInAppHandler.InAppResponse; import com.iterable.iterableapi.IterableInAppMessage.Trigger.TriggerType; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; import org.json.JSONArray; import org.json.JSONException; @@ -137,10 +139,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 IterableCallbackHandlers.SuccessCallback 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(new IterableResponseObject.LocalSuccess()); } notifyOnChange(); } @@ -279,7 +281,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 IterableCallbackHandlers.SuccessCallback 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..b1eca58da 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableRequestTask.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableRequestTask.java @@ -12,6 +12,10 @@ import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import com.iterable.iterableapi.response.IterableAuthResponseObject; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; + import org.json.JSONException; import org.json.JSONObject; @@ -375,7 +379,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 +400,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(); - } + IterableAuthResponseObject.Success authTokenResponse = (IterableAuthResponseObject.Success) data; + String newAuthToken = authTokenResponse.getAuthToken(); + retryRequestWithNewAuthToken(newAuthToken, iterableApiRequest); }); } @@ -427,7 +429,7 @@ class IterableApiRequest { private ProcessorType processorType = ProcessorType.ONLINE; IterableHelper.IterableActionHandler legacyCallback; - IterableHelper.SuccessHandler successCallback; + IterableCallbackHandlers.SuccessCallback successCallback; IterableHelper.FailureHandler failureCallback; enum ProcessorType { @@ -455,7 +457,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, IterableCallbackHandlers.SuccessCallback onSuccess, IterableHelper.FailureHandler onFailure) { this.apiKey = apiKey; this.baseUrl = baseUrl; this.resourcePath = resourcePath; @@ -466,7 +468,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, IterableCallbackHandlers.SuccessCallback onSuccess, IterableHelper.FailureHandler onFailure) { this.apiKey = apiKey; this.baseUrl = null; this.resourcePath = resourcePath; @@ -497,7 +499,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 IterableCallbackHandlers.SuccessCallback 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/OfflineRequestProcessor.java b/iterableapi/src/main/java/com/iterable/iterableapi/OfflineRequestProcessor.java index e60b08293..c648c584f 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/OfflineRequestProcessor.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/OfflineRequestProcessor.java @@ -7,6 +7,9 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; + import org.json.JSONException; import org.json.JSONObject; @@ -60,13 +63,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 IterableCallbackHandlers.SuccessCallback 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 IterableCallbackHandlers.SuccessCallback 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 +90,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 +101,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 IterableCallbackHandlers.SuccessCallback onSuccess, @Nullable IterableHelper.FailureHandler onFailure) { JSONObject serializedRequest = null; try { serializedRequest = request.toJSONObject(); @@ -120,13 +123,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); + IterableCallbackHandlers.SuccessCallback 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..52d7091e7 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/OnlineRequestProcessor.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/OnlineRequestProcessor.java @@ -6,6 +6,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; + import org.json.JSONException; import org.json.JSONObject; @@ -22,13 +24,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 IterableCallbackHandlers.SuccessCallback 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 IterableCallbackHandlers.SuccessCallback 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..1c4e99e00 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/RequestProcessor.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/RequestProcessor.java @@ -5,13 +5,15 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; + import org.json.JSONObject; 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 IterableCallbackHandlers.SuccessCallback 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 IterableCallbackHandlers.SuccessCallback 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/main/java/com/iterable/iterableapi/response/IterableAuthResponseObject.kt b/iterableapi/src/main/java/com/iterable/iterableapi/response/IterableAuthResponseObject.kt new file mode 100644 index 000000000..061b152b4 --- /dev/null +++ b/iterableapi/src/main/java/com/iterable/iterableapi/response/IterableAuthResponseObject.kt @@ -0,0 +1,15 @@ +package com.iterable.iterableapi.response + +sealed class IterableAuthResponseObject( + message: String, + code: IterableResponseCode +): IterableResponseObject(message, code) { + + class Success( + val authToken: String + ): IterableResponseObject.Success( + message = SuccessMessages.AUTH_TOKEN_SUCCESS, + ) + + +} \ No newline at end of file diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/response/IterableResponseObject.kt b/iterableapi/src/main/java/com/iterable/iterableapi/response/IterableResponseObject.kt new file mode 100644 index 000000000..436149640 --- /dev/null +++ b/iterableapi/src/main/java/com/iterable/iterableapi/response/IterableResponseObject.kt @@ -0,0 +1,40 @@ +package com.iterable.iterableapi.response + +import org.json.JSONObject + +sealed class IterableResponseObject( + val message: String, + val code: IterableResponseCode +) { + sealed class Success( + message: String, + ): IterableResponseObject(message, IterableResponseCode.SUCCESS) + + class RemoteSuccess(val responseJson: JSONObject): Success( + message = SuccessMessages.REMOTE_SUCCESS + ) + + class LocalSuccess(localMessage: String = SuccessMessages.LOCAL_SUCCESS): Success( + message = localMessage, + ) + + + sealed class Failure(message: String): IterableResponseObject( + message = message, + code = IterableResponseCode.FAILURE + ) + + class RemoteFailure(remoteMessage: String, val errorCode: Int): Failure( + message = remoteMessage + ) + + 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/response/handlers/IterableCallbackHandlers.java b/iterableapi/src/main/java/com/iterable/iterableapi/response/handlers/IterableCallbackHandlers.java new file mode 100644 index 000000000..576cd7cfe --- /dev/null +++ b/iterableapi/src/main/java/com/iterable/iterableapi/response/handlers/IterableCallbackHandlers.java @@ -0,0 +1,124 @@ +package com.iterable.iterableapi.response.handlers; + +import androidx.annotation.NonNull; + +import com.iterable.iterableapi.IterableLogger; +import com.iterable.iterableapi.response.IterableResponseObject; + +/** + * Callback handlers for Iterable SDK operations. + */ +public class IterableCallbackHandlers { + + /** + * 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 com.iterable.iterableapi.response.handlers.auth.IterableAuthCallbackHandlers.AuthTokenCallback} + * if you need to access the authentication token
  • + *
+ * + * @see RemoteSuccessCallback + * @see LocalSuccessCallback + */ + public interface SuccessCallback { + 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 SuccessCallback} instead if you want to handle both + * remote and local success cases.

+ */ + public interface RemoteSuccessCallback extends SuccessCallback { + 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 SuccessCallback if you want to handle all success types." + ); + } + } + } + + /** + * 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 SuccessCallback} instead if you want to handle both + * local and remote success cases.

+ */ + public interface LocalSuccessCallback extends SuccessCallback { + 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 SuccessCallback if you want to handle all success types." + ); + } + } + } + + +} diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/response/handlers/auth/IterableAuthCallbackHandlers.java b/iterableapi/src/main/java/com/iterable/iterableapi/response/handlers/auth/IterableAuthCallbackHandlers.java new file mode 100644 index 000000000..f3c5b2879 --- /dev/null +++ b/iterableapi/src/main/java/com/iterable/iterableapi/response/handlers/auth/IterableAuthCallbackHandlers.java @@ -0,0 +1,52 @@ +package com.iterable.iterableapi.response.handlers.auth; + +import androidx.annotation.NonNull; + +import com.iterable.iterableapi.IterableLogger; +import com.iterable.iterableapi.response.IterableAuthResponseObject; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; + +/** + * Callback handlers for authentication-related operations. + */ +public class IterableAuthCallbackHandlers { + + /** + * 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 com.iterable.iterableapi.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 IterableCallbackHandlers.SuccessCallback { + void onSuccess(@NonNull IterableAuthResponseObject.Success data); + + @Override + default void onSuccess(@NonNull IterableResponseObject.Success data) { + // Dispatch to the specific method if it's the correct type + if (data instanceof IterableAuthResponseObject.Success) { + onSuccess((IterableAuthResponseObject.Success) data); + } else { + IterableLogger.w( + "IterableHelper", + "AuthTokenCallback received unexpected success type: " + data.getClass().getSimpleName() + + ". This callback is for auth token operations only." + ); + } + } + } +} diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthSecurityTests.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthSecurityTests.java index 2fa168b59..5d0e080ce 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthSecurityTests.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthSecurityTests.java @@ -16,9 +16,10 @@ import android.content.Context; import android.content.SharedPreferences; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; import com.iterable.iterableapi.unit.PathBasedQueueDispatcher; -import org.json.JSONObject; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -201,9 +202,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 IterableCallbackHandlers.SuccessCallback() { @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..886fd081b 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java @@ -8,6 +8,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; import com.iterable.iterableapi.unit.TestRunner; import org.json.JSONObject; @@ -59,9 +61,9 @@ public void testRequestSerialization() throws Exception { String requestType = "api"; String authToken = "authToken123##"; - IterableHelper.SuccessHandler successHandler = new IterableHelper.SuccessHandler() { + IterableCallbackHandlers.SuccessCallback successHandler = new IterableCallbackHandlers.SuccessCallback() { @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..6a958b8da 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java @@ -1,5 +1,7 @@ package com.iterable.iterableapi; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; import com.iterable.iterableapi.util.DeviceInfoUtils; import android.app.Activity; import android.content.Context; @@ -161,7 +163,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(IterableCallbackHandlers.SuccessCallback.class), nullable(IterableHelper.FailureHandler.class)); server.takeRequest(1, TimeUnit.SECONDS); assertEquals("new@email.com", IterableApi.getInstance().getEmail()); @@ -175,9 +177,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 IterableCallbackHandlers.SuccessCallback() { @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 +195,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 IterableCallbackHandlers.SuccessCallback() { @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 +1017,7 @@ public void testRegisterDeviceTokenSuccessCallback_CreatesWrappedHandler() throw IterableApi.getInstance().setVisitorUsageTracked(true); // Create a mock success handler - IterableHelper.SuccessHandler originalHandler = mock(IterableHelper.SuccessHandler.class); + IterableCallbackHandlers.SuccessCallback originalHandler = mock(IterableCallbackHandlers.SuccessCallback.class); // Set up user with success handler IterableApi.getInstance().setEmail("test@example.com", originalHandler, null); @@ -1032,7 +1034,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(IterableCallbackHandlers.SuccessCallback.class); verify(mockClient, timeout(1000)).registerDeviceToken( eq("test@example.com"), nullable(String.class), @@ -1075,7 +1077,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(IterableCallbackHandlers.SuccessCallback.class); verify(mockClient, timeout(1000)).registerDeviceToken( nullable(String.class), eq("test_user_123"), @@ -1112,7 +1114,7 @@ public void testRegisterDeviceTokenIntegration_ConsentLoggingTriggered() throws IterableApi.getInstance().setVisitorUsageTracked(true); // Create a success handler and set user - IterableHelper.SuccessHandler successHandler = mock(IterableHelper.SuccessHandler.class); + IterableCallbackHandlers.SuccessCallback successHandler = mock(IterableCallbackHandlers.SuccessCallback.class); IterableApi.getInstance().setEmail("test@example.com", successHandler, null); // Execute: Register device token @@ -1144,7 +1146,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 +1164,7 @@ public void testRegisterDeviceTokenIntegration_ConsentLoggingNotTriggeredWhenDis // Set up other conditions IterableApi.getInstance().setVisitorUsageTracked(true); - IterableHelper.SuccessHandler successHandler = mock(IterableHelper.SuccessHandler.class); + IterableCallbackHandlers.SuccessCallback successHandler = mock(IterableCallbackHandlers.SuccessCallback.class); IterableApi.getInstance().setEmail("test@example.com", successHandler, null); // Execute: Register device token @@ -1194,7 +1196,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..4e06663ce 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableAsyncInitializationTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableAsyncInitializationTest.java @@ -27,6 +27,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; + /** * Comprehensive test suite for async initialization functionality. * Tests ANR elimination, operation queuing, callback execution, and edge cases. @@ -1317,9 +1319,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", (IterableCallbackHandlers.SuccessCallback) null, null); IterableApi.getInstance().setUserId("user123"); - IterableApi.getInstance().setUserId("user456", (IterableHelper.SuccessHandler) null, null); + IterableApi.getInstance().setUserId("user456", (IterableCallbackHandlers.SuccessCallback) 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..3f2bfd406 --- /dev/null +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableHelperUnitTest.java @@ -0,0 +1,348 @@ +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; + +import com.iterable.iterableapi.response.IterableAuthResponseObject; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; +import com.iterable.iterableapi.response.handlers.auth.IterableAuthCallbackHandlers; + +/** + * 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); + + IterableCallbackHandlers.SuccessCallback 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 = new IterableResponseObject.LocalSuccess(); + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + + IterableCallbackHandlers.SuccessCallback 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"; + IterableAuthResponseObject.Success authSuccess = new IterableAuthResponseObject.Success(testToken); + + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + + IterableCallbackHandlers.SuccessCallback callback = data -> { + callbackInvoked.set(true); + assertTrue(data instanceof IterableAuthResponseObject.Success); + assertEquals(testToken, ((IterableAuthResponseObject.Success) 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); + + IterableCallbackHandlers.RemoteSuccessCallback callback = new IterableCallbackHandlers.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 = new IterableResponseObject.LocalSuccess(); + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableCallbackHandlers.RemoteSuccessCallback callback = new IterableCallbackHandlers.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 = new IterableResponseObject.LocalSuccess(); + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableCallbackHandlers.LocalSuccessCallback callback = new IterableCallbackHandlers.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); + + IterableCallbackHandlers.LocalSuccessCallback callback = new IterableCallbackHandlers.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.Success authSuccess = new IterableAuthResponseObject.Success(testToken); + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableAuthCallbackHandlers.AuthTokenCallback callback = new IterableAuthCallbackHandlers.AuthTokenCallback() { + @Override + public void onSuccess(IterableAuthResponseObject.Success 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 = new IterableResponseObject.LocalSuccess(); + + AtomicBoolean typedCallbackInvoked = new AtomicBoolean(false); + + IterableAuthCallbackHandlers.AuthTokenCallback callback = new IterableAuthCallbackHandlers.AuthTokenCallback() { + @Override + public void onSuccess(IterableAuthResponseObject.Success 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 = new IterableResponseObject.LocalSuccess(); + + 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"; + IterableAuthResponseObject.Success authSuccess = new IterableAuthResponseObject.Success(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/IterableInboxTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java index 868cb12b6..4da4c56ef 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java @@ -5,6 +5,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; import com.iterable.iterableapi.unit.PathBasedQueueDispatcher; import org.json.JSONObject; @@ -98,9 +100,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 IterableCallbackHandlers.SuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { + public void onSuccess(@NonNull IterableResponseObject.Success data) { signal.countDown(); } }, new IterableHelper.FailureHandler() { @@ -127,9 +129,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 IterableCallbackHandlers.SuccessCallback() { @Override - public void onSuccess(@NonNull JSONObject data) { + public void onSuccess(@NonNull IterableResponseObject.Success data) { assertFalse(true); } }, new IterableHelper.FailureHandler() { @@ -163,9 +165,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 IterableCallbackHandlers.SuccessCallback() { @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..5deeb3abf 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java @@ -24,6 +24,8 @@ import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; + public class IterablePushRegistrationTaskTest extends BaseTest { private static final String TEST_TOKEN = "testToken"; @@ -75,7 +77,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(IterableCallbackHandlers.SuccessCallback.class), nullable(IterableHelper.FailureHandler.class)); } @Test @@ -87,6 +89,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(IterableCallbackHandlers.SuccessCallback.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..3f99dd2c5 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/TaskSchedulerTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/TaskSchedulerTest.java @@ -1,5 +1,7 @@ package com.iterable.iterableapi; +import com.iterable.iterableapi.response.IterableResponseObject; +import com.iterable.iterableapi.response.handlers.IterableCallbackHandlers; import com.iterable.iterableapi.unit.TestRunner; import org.json.JSONObject; @@ -35,12 +37,12 @@ public void testScheduleTaskCreatesTaskInStorage() throws Exception { @Test public void testSuccessCallbackIsCalledOnCompletion() throws Exception { - IterableHelper.SuccessHandler successHandler = mock(IterableHelper.SuccessHandler.class); + IterableCallbackHandlers.SuccessCallback successHandler = mock(IterableCallbackHandlers.SuccessCallback.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