Skip to content

Commit 5fa3361

Browse files
authored
Merge pull request #727 from Iterable/jwt/MOB-10947-task-3-android-retrypolicy-config-at-android-br
[MOB-10947][SDK-88] Configure JWT for Android
2 parents 0eedca5 + 0191b7b commit 5fa3361

File tree

8 files changed

+116
-35
lines changed

8 files changed

+116
-35
lines changed

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
105105
dependencies {
106106
implementation "com.facebook.react:react-android"
107107
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
108-
api "com.iterable:iterableapi:3.5.2"
108+
api "com.iterable:iterableapi:3.6.1"
109109
// api project(":iterableapi") // links to local android SDK repo rather than by release
110110
}
111111

android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.facebook.react.bridge.WritableMap;
1919
import com.facebook.react.modules.core.DeviceEventManagerModule;
2020

21+
import com.iterable.iterableapi.AuthFailure;
2122
import com.iterable.iterableapi.InboxSessionManager;
2223
import com.iterable.iterableapi.IterableAction;
2324
import com.iterable.iterableapi.IterableActionContext;
@@ -572,19 +573,33 @@ public String onAuthTokenRequested() {
572573
}
573574
}
574575

576+
@Override
577+
public void onAuthFailure(AuthFailure authFailure) {
578+
// Create a JSON object for the authFailure object
579+
JSONObject messageJson = new JSONObject();
580+
try {
581+
messageJson.put("userKey", authFailure.userKey);
582+
messageJson.put("failedAuthToken", authFailure.failedAuthToken);
583+
messageJson.put("failedRequestTime", authFailure.failedRequestTime);
584+
messageJson.put("failureReason", authFailure.failureReason.name());
585+
WritableMap eventData = Serialization.convertJsonToMap(messageJson);
586+
sendEvent(EventName.handleAuthFailureCalled.name(), eventData);
587+
} catch (JSONException e) {
588+
IterableLogger.v(TAG, "Failed to set authToken");
589+
}
590+
}
591+
592+
public void pauseAuthRetries(boolean pauseRetry) {
593+
IterableApi.getInstance().pauseAuthRetries(pauseRetry);
594+
}
595+
575596
@Override
576597
public void onTokenRegistrationSuccessful(String authToken) {
577598
IterableLogger.v(TAG, "authToken successfully set");
578599
// MOB-10422: Pass successhandler to event listener
579600
sendEvent(EventName.handleAuthSuccessCalled.name(), null);
580601
}
581602

582-
@Override
583-
public void onTokenRegistrationFailed(Throwable object) {
584-
IterableLogger.v(TAG, "Failed to set authToken");
585-
sendEvent(EventName.handleAuthFailureCalled.name(), null);
586-
}
587-
588603
public void addListener(String eventName) {
589604
// Keep: Required for RN built in Event Emitter Calls.
590605
}

android/src/main/java/com/iterable/reactnative/Serialization.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.iterable.iterableapi.IterableInboxSession;
2525
import com.iterable.iterableapi.IterableLogger;
2626
import com.iterable.iterableapi.RNIterableInternal;
27+
import com.iterable.iterableapi.RetryPolicy;
2728

2829
import org.json.JSONArray;
2930
import org.json.JSONException;
@@ -94,7 +95,7 @@ static CommerceItem commerceItemFromMap(JSONObject itemMap) throws JSONException
9495
categories[i] = categoriesArray.getString(i);
9596
}
9697
}
97-
98+
9899
return new CommerceItem(itemMap.getString("id"),
99100
itemMap.getString("name"),
100101
itemMap.getDouble("price"),
@@ -216,9 +217,17 @@ static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableConte
216217

217218
configBuilder.setDataRegion(iterableDataRegion);
218219
}
219-
220-
if (iterableContextJSON.has("encryptionEnforced")) {
221-
configBuilder.setEncryptionEnforced(iterableContextJSON.optBoolean("encryptionEnforced"));
220+
221+
if (iterableContextJSON.has("retryPolicy")) {
222+
JSONObject retryPolicyJson = iterableContextJSON.getJSONObject("retryPolicy");
223+
int maxRetry = retryPolicyJson.getInt("maxRetry");
224+
long retryInterval = retryPolicyJson.getLong("retryInterval");
225+
String retryBackoff = retryPolicyJson.getString("retryBackoff");
226+
RetryPolicy.Type retryPolicyType = RetryPolicy.Type.LINEAR;
227+
if (retryBackoff.equals("EXPONENTIAL")) {
228+
retryPolicyType = RetryPolicy.Type.EXPONENTIAL;
229+
}
230+
configBuilder.setAuthRetryPolicy(new RetryPolicy(maxRetry, retryInterval, retryPolicyType));
222231
}
223232

224233
return configBuilder;
@@ -257,7 +266,13 @@ static JSONObject actionContextToJson(IterableActionContext iterableActionContex
257266
}
258267

259268
static IterableInboxSession.Impression inboxImpressionFromMap(JSONObject impressionMap) throws JSONException {
260-
return new IterableInboxSession.Impression(impressionMap.getString("messageId"),
269+
// Add null check for messageId to prevent NullPointerException
270+
String messageId = impressionMap.optString("messageId", null);
271+
if (messageId == null || messageId.isEmpty()) {
272+
throw new JSONException("messageId is null or empty");
273+
}
274+
275+
return new IterableInboxSession.Impression(messageId,
261276
impressionMap.getBoolean("silentInbox"),
262277
impressionMap.optInt("displayCount", 0),
263278
(float) impressionMap.optDouble("duration", 0)
@@ -271,8 +286,13 @@ static List<IterableInboxSession.Impression> impressionsFromReadableArray(Readab
271286
JSONArray impressionJsonArray = convertArrayToJson(array);
272287

273288
for (int i = 0; i < impressionJsonArray.length(); i++) {
274-
JSONObject impressionObj = impressionJsonArray.getJSONObject(i);
275-
list.add(inboxImpressionFromMap(impressionObj));
289+
try {
290+
JSONObject impressionObj = impressionJsonArray.getJSONObject(i);
291+
list.add(inboxImpressionFromMap(impressionObj));
292+
} catch (JSONException e) {
293+
// Skip invalid entries instead of failing completely
294+
IterableLogger.w(TAG, "Skipping invalid impression at index " + i + ": " + e.getLocalizedMessage());
295+
}
276296
}
277297
} catch (JSONException e) {
278298
IterableLogger.e(TAG, "Failed converting to JSONObject");
@@ -286,7 +306,7 @@ static List<IterableInboxSession.Impression> impressionsFromReadableArray(Readab
286306
// ---------------------------------------------------------------------------------------
287307
// region React Native JSON conversion methods
288308
// obtained from https://gist.github.com/viperwarp/2beb6bbefcc268dee7ad
289-
309+
290310
static WritableMap convertJsonToMap(JSONObject jsonObject) throws JSONException {
291311
WritableMap map = new WritableNativeMap();
292312

android/src/newarch/java/com/RNIterableAPIModule.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.facebook.react.bridge.ReactApplicationContext;
88
import com.facebook.react.bridge.ReadableArray;
99
import com.facebook.react.bridge.ReadableMap;
10+
import com.iterable.iterableapi.AuthFailure;
11+
import com.iterable.iterableapi.IterableLogger;
1012

1113
public class RNIterableAPIModule extends NativeRNIterableAPISpec {
1214
private final ReactApplicationContext reactContext;
@@ -217,6 +219,11 @@ public void passAlongAuthToken(@Nullable String authToken) {
217219
moduleImpl.passAlongAuthToken(authToken);
218220
}
219221

222+
@Override
223+
public void pauseAuthRetries(boolean pauseRetry) {
224+
moduleImpl.pauseAuthRetries(pauseRetry);
225+
}
226+
220227
public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
221228
moduleImpl.sendEvent(eventName, eventData);
222229
}

android/src/oldarch/java/com/RNIterableAPIModule.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ public void passAlongAuthToken(@Nullable String authToken) {
223223
moduleImpl.passAlongAuthToken(authToken);
224224
}
225225

226+
@ReactMethod
227+
public void pauseAuthRetries(boolean pauseRetry) {
228+
moduleImpl.pauseAuthRetries(pauseRetry);
229+
}
230+
226231

227232
public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
228233
moduleImpl.sendEvent(eventName, eventData);

example/src/hooks/useIterableApp.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { StackNavigationProp } from '@react-navigation/stack';
22
import {
3-
type FunctionComponent,
43
createContext,
54
useCallback,
65
useContext,
76
useState,
7+
type FunctionComponent,
88
} from 'react';
99
import { Alert } from 'react-native';
1010

@@ -135,6 +135,10 @@ export const IterableAppProvider: FunctionComponent<
135135

136136
config.onJWTError = (authFailure) => {
137137
console.error('Error fetching JWT:', authFailure);
138+
Alert.alert(
139+
`Error fetching JWT: ${authFailure.failureReason}`,
140+
`Token: ${authFailure.failedAuthToken}`
141+
);
138142
};
139143

140144
config.urlHandler = (url: string) => {
@@ -162,6 +166,22 @@ export const IterableAppProvider: FunctionComponent<
162166

163167
config.inAppHandler = () => IterableInAppShowResponse.show;
164168

169+
// NOTE: Uncomment to test authHandler failure
170+
// config.authHandler = () => {
171+
// console.log(`authHandler`);
172+
173+
// return Promise.resolve({
174+
// authToken: 'SomethingNotValid',
175+
// successCallback: () => {
176+
// console.log(`authHandler > success`);
177+
// },
178+
// // This is not firing
179+
// failureCallback: () => {
180+
// console.log(`authHandler > failure`);
181+
// },
182+
// });
183+
// };
184+
165185
setItblConfig(config);
166186

167187
const key = apiKey ?? process.env.ITBL_API_KEY;

src/inApp/classes/IterableInAppMessage.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,19 +135,19 @@ export class IterableInAppMessage {
135135
* @returns A new instance of `IterableInAppMessage` populated with data from the `viewToken`.
136136
*/
137137
static fromViewToken(viewToken: ViewToken) {
138-
const inAppMessage = viewToken.item.inAppMessage as IterableInAppMessage;
138+
const inAppMessage = viewToken?.item?.inAppMessage as IterableInAppMessage;
139139

140140
return new IterableInAppMessage(
141-
inAppMessage.messageId,
142-
inAppMessage.campaignId,
143-
inAppMessage.trigger,
144-
inAppMessage.createdAt,
145-
inAppMessage.expiresAt,
146-
inAppMessage.saveToInbox,
147-
inAppMessage.inboxMetadata,
148-
inAppMessage.customPayload,
149-
inAppMessage.read,
150-
inAppMessage.priorityLevel
141+
inAppMessage?.messageId,
142+
inAppMessage?.campaignId,
143+
inAppMessage?.trigger,
144+
inAppMessage?.createdAt,
145+
inAppMessage?.expiresAt,
146+
inAppMessage?.saveToInbox,
147+
inAppMessage?.inboxMetadata,
148+
inAppMessage?.customPayload,
149+
inAppMessage?.read,
150+
inAppMessage?.priorityLevel
151151
);
152152
}
153153

src/inbox/components/IterableInboxMessageList.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,30 @@ export const IterableInboxMessageList = ({
9595
function getRowInfosFromViewTokens(
9696
viewTokens: Array<ViewToken>
9797
): Array<IterableInboxImpressionRowInfo> {
98-
return viewTokens.map(function (viewToken) {
99-
const inAppMessage = IterableInAppMessage.fromViewToken(viewToken);
98+
return viewTokens
99+
.filter((viewToken) => {
100+
// Filter out viewTokens that don't have valid items or inAppMessage
101+
return viewToken?.item?.inAppMessage?.messageId;
102+
})
103+
.map(function (viewToken) {
104+
try {
105+
const inAppMessage = IterableInAppMessage.fromViewToken(viewToken);
100106

101-
const impression = {
102-
messageId: inAppMessage.messageId,
103-
silentInbox: inAppMessage.isSilentInbox(),
104-
} as IterableInboxImpressionRowInfo;
107+
const impression = {
108+
messageId: inAppMessage?.messageId,
109+
silentInbox: inAppMessage?.isSilentInbox(),
110+
} as IterableInboxImpressionRowInfo;
105111

106-
return impression;
107-
});
112+
return impression;
113+
} catch (error) {
114+
// Log the error and return null to be filtered out
115+
console.warn('Failed to create impression from ViewToken:', error);
116+
return null;
117+
}
118+
})
119+
.filter(
120+
(impression) => impression !== null
121+
) as Array<IterableInboxImpressionRowInfo>;
108122
}
109123

110124
const inboxSessionViewabilityConfig: ViewabilityConfig = {

0 commit comments

Comments
 (0)