Skip to content

feat(android): add session sync callback #1281

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
77b8993
feat(android): add SRSyncCallback
kholood-ea Aug 28, 2024
e6b816f
feat: implement and test syncCallback CP side
kholood-ea Aug 28, 2024
bad72d6
feat(example): use SRSyncCallback in example app
kholood-ea Aug 28, 2024
2b01c9d
ci: fix tests
kholood-ea Aug 28, 2024
61d6e52
fix: export session data type
kholood-ea Sep 2, 2024
849648a
fix(example): use session data type
kholood-ea Sep 2, 2024
02c621c
fix(android):remove data modifier
kholood-ea Sep 2, 2024
0d9c7f1
fix(android): add property modifiers
kholood-ea Sep 2, 2024
261aa76
fix(android): update test case
kholood-ea Sep 2, 2024
5056f58
fix: enhance test case
kholood-ea Sep 5, 2024
2ff447f
fix: update session data type
kholood-ea Sep 9, 2024
131a795
fix: add more session metadata to setSyncCallback
kholood-ea Sep 10, 2024
2b53766
fix: update syncCallback test
kholood-ea Sep 10, 2024
41e6b52
feat: add launchType to session metadata for setSyncCallback
kholood-ea Sep 11, 2024
c6b9173
fix: import type
kholood-ea Sep 12, 2024
3b1c37b
fix: assert evaluate sync returns correct value
kholood-ea Sep 16, 2024
446e9e4
fix: import type
kholood-ea Sep 16, 2024
8187f60
fix: cleanup
kholood-ea Sep 16, 2024
d0e972c
chore: update js doc
kholood-ea Sep 16, 2024
c700600
fix: typo
kholood-ea Sep 16, 2024
38bf28f
fix: follow interface naming convention
kholood-ea Sep 16, 2024
8cef372
fix: update type
kholood-ea Sep 16, 2024
40556ec
fix: refactor syncCallback
kholood-ea Sep 16, 2024
bc4f699
fix: default syncing session to true
kholood-ea Sep 16, 2024
6eec19f
fix: convert network logs to readable array
kholood-ea Sep 16, 2024
b3c0a31
chore: add discriptive comment
kholood-ea Sep 16, 2024
e50fbef
chore: use readable map for session metadata
a7medev Sep 16, 2024
d08d84c
fix: setSyncCallback should sync in case of exception
kholood-ea Sep 18, 2024
6ee32a8
fix: move SessionMetadata to models
kholood-ea Sep 18, 2024
6bf9f32
fix: update SessionMetadata type import
kholood-ea Sep 18, 2024
df89665
fix: report bug e2e test
kholood-ea Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/native.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
project.ext.instabug = [
version: '13.3.0'
version: '13.3.0.6212131-SNAPSHOT',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has the callback been released in v13.4.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it hasn't been just yet.

]

dependencies {
Expand Down
17 changes: 17 additions & 0 deletions android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.instabug.library.invocation.InstabugInvocationEvent;
import com.instabug.library.invocation.util.InstabugFloatingButtonEdge;
import com.instabug.library.invocation.util.InstabugVideoRecordingButtonPosition;
import com.instabug.library.sessionreplay.model.SessionMetadata;
import com.instabug.library.ui.onboarding.WelcomeMessage;

import java.util.ArrayList;
Expand Down Expand Up @@ -58,6 +59,7 @@ static Map<String, Object> getAll() {
putAll(nonFatalExceptionLevel);
putAll(locales);
putAll(placeholders);
putAll(launchType);
}};
}

Expand Down Expand Up @@ -238,4 +240,19 @@ static Map<String, Object> getAll() {
put("team", Key.CHATS_TEAM_STRING_NAME);
put("insufficientContentMessage", Key.COMMENT_FIELD_INSUFFICIENT_CONTENT);
}};

public static ArgsMap<String> launchType = new ArgsMap<String>() {{
put("cold", SessionMetadata.LaunchType.COLD);
put("hot",SessionMetadata.LaunchType.HOT );
put("warm",SessionMetadata.LaunchType.WARM );
}};

// Temporary workaround to be removed in future release
// This is used for mapping native `LaunchType` values into React Native enum values.
public static HashMap<String,String> launchTypeReversed = new HashMap<String,String>() {{
put(SessionMetadata.LaunchType.COLD,"cold");
put(SessionMetadata.LaunchType.HOT,"hot" );
put(SessionMetadata.LaunchType.WARM,"warm" );
}};

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ final class Constants {

final static String IBG_ON_NEW_MESSAGE_HANDLER = "IBGonNewMessageHandler";
final static String IBG_ON_NEW_REPLY_RECEIVED_CALLBACK = "IBGOnNewReplyReceivedCallback";
final static String IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION = "IBGSessionReplayOnSyncCallback";

}
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
package com.instabug.reactlibrary;


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.instabug.chat.Replies;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.instabug.library.OnSessionReplayLinkReady;
import com.instabug.library.SessionSyncListener;
import com.instabug.library.sessionreplay.SessionReplay;
import com.instabug.library.sessionreplay.model.SessionMetadata;
import com.instabug.reactlibrary.utils.EventEmitterModule;
import com.instabug.reactlibrary.utils.MainThreadHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import javax.annotation.Nonnull;

public class RNInstabugSessionReplayModule extends ReactContextBaseJavaModule {
public class RNInstabugSessionReplayModule extends EventEmitterModule {

public RNInstabugSessionReplayModule(ReactApplicationContext reactApplicationContext) {
super(reactApplicationContext);
}

@ReactMethod
public void addListener(String event) {
super.addListener(event);
}

@ReactMethod
public void removeListeners(Integer count) {
super.removeListeners(count);
}

@Nonnull
@Override
public String getName() {
Expand Down Expand Up @@ -79,7 +99,7 @@ public void run() {
e.printStackTrace();
}
}
});
});
}

@ReactMethod
Expand All @@ -97,6 +117,85 @@ public void onSessionReplayLinkReady(@Nullable String link) {
}
});

}

public ReadableMap getSessionMetadataMap(SessionMetadata sessionMetadata){
WritableMap params = Arguments.createMap();
params.putString("appVersion",sessionMetadata.getAppVersion());
params.putString("OS",sessionMetadata.getOs());
params.putString("device",sessionMetadata.getDevice());
params.putDouble("sessionDurationInSeconds",(double)sessionMetadata.getSessionDurationInSeconds());
params.putBoolean("hasLinkToAppReview",sessionMetadata.getLinkedToReview());
params.putString("launchType",ArgsRegistry.launchTypeReversed.get(sessionMetadata.getLaunchType()) );
params.putDouble("launchDuration", sessionMetadata.getLaunchDuration());
params.putArray("networkLogs",getNetworkLogsArray(sessionMetadata.getNetworkLogs()));

// TODO:Add rest of sessionMetadata
// params.putDouble("bugsCount", ??);
// params.putDouble("fatalCrashCount",??);
// params.putDouble("oomCrashCount",??);
return params;
}

public ReadableArray getNetworkLogsArray(List<SessionMetadata.NetworkLog> networkLogList ){
WritableArray networkLogs = Arguments.createArray();

for (SessionMetadata.NetworkLog log : networkLogList) {
WritableMap networkLog = Arguments.createMap();
networkLog.putString("url", log.getUrl());
networkLog.putDouble("duration", log.getDuration());
networkLog.putInt("statusCode", log.getStatusCode());

networkLogs.pushMap(networkLog);
}

return networkLogs;
}

private boolean shouldSync = true;
private CountDownLatch latch;
@ReactMethod
public void setSyncCallback() {
MainThreadHandler.runOnMainThread(new Runnable() {
@Override
public void run() {
try {
SessionReplay.setSyncCallback(new SessionSyncListener() {
@Override
public boolean onSessionReadyToSync(@NonNull SessionMetadata sessionMetadata) {

sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION,getSessionMetadataMap(sessionMetadata));

latch = new CountDownLatch(1);

try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
return true;
}

return shouldSync;
}
});
}
catch(Exception e){
e.printStackTrace();
}

}
});
}

@ReactMethod
public void evaluateSync(boolean result) {
shouldSync = result;

if (latch != null) {
latch.countDown();
}
}



}
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
package com.instabug.reactlibrary;

import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.os.Handler;
import android.os.Looper;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableArray;
import com.instabug.chat.Replies;
import com.instabug.featuresrequest.ActionType;
import com.instabug.featuresrequest.FeatureRequests;
import com.instabug.library.Feature;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.instabug.library.OnSessionReplayLinkReady;
import com.instabug.library.SessionSyncListener;
import com.instabug.library.sessionreplay.SessionReplay;
import com.instabug.library.sessionreplay.model.SessionMetadata;
import com.instabug.reactlibrary.utils.MainThreadHandler;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;


public class RNInstabugSessionReplayModuleTest {
Expand All @@ -44,8 +48,8 @@ public class RNInstabugSessionReplayModuleTest {

// Mock Objects
private MockedStatic<Looper> mockLooper;
private MockedStatic <MainThreadHandler> mockMainThreadHandler;
private MockedStatic <SessionReplay> mockSessionReplay;
private MockedStatic<MainThreadHandler> mockMainThreadHandler;
private MockedStatic<SessionReplay> mockSessionReplay;

@Before
public void mockMainThreadHandler() throws Exception {
Expand Down Expand Up @@ -107,7 +111,7 @@ public void testSetInstabugLogsEnabled() {
@Test
public void testGetSessionReplayLink() {
Promise promise = mock(Promise.class);
String link="instabug link";
String link = "instabug link";

mockSessionReplay.when(() -> SessionReplay.getSessionReplayLink(any())).thenAnswer(
invocation -> {
Expand Down Expand Up @@ -136,5 +140,40 @@ public void testSetUserStepsEnabled() {
mockSessionReplay.verifyNoMoreInteractions();
}

@Test
public void testSetSyncCallback() throws Exception {
MockedStatic<Arguments> mockArguments = mockStatic(Arguments.class);
MockedConstruction<CountDownLatch> mockCountDownLatch = mockConstruction(CountDownLatch.class);
RNInstabugSessionReplayModule SRModule = spy(new RNInstabugSessionReplayModule(mock(ReactApplicationContext.class)));

final boolean shouldSync = true;
final AtomicBoolean actual = new AtomicBoolean();

mockArguments.when(Arguments::createMap).thenReturn(new JavaOnlyMap());

mockSessionReplay.when(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class)))
.thenAnswer((invocation) -> {
SessionSyncListener listener = (SessionSyncListener) invocation.getArguments()[0];
SessionMetadata metadata = mock(SessionMetadata.class);
actual.set(listener.onSessionReadyToSync(metadata));
return null;
});

doAnswer((invocation) -> {
SRModule.evaluateSync(shouldSync);
return null;
}).when(SRModule).sendEvent(eq(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION), any());

WritableMap params = Arguments.createMap();

SRModule.setSyncCallback();

assertEquals(shouldSync, actual.get());
verify(SRModule).sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, params);
mockSessionReplay.verify(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class)));

mockArguments.close();
mockCountDownLatch.close();
}

}
4 changes: 3 additions & 1 deletion examples/default/e2e/reportBug.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ it('reports a bug', async () => {
await waitFor(floatingButton).toBeVisible().withTimeout(30000);
await floatingButton.tap();

await getElement('reportBugMenuItem').tap();
const reportBugMenuItemButton = getElement('reportBugMenuItem');
await waitFor(reportBugMenuItemButton).toBeVisible().withTimeout(30000);
await reportBugMenuItemButton.tap();

await getElement('emailField').typeText(mockData.email);
await getElement('commentField').typeText(mockData.bugComment);
Expand Down
19 changes: 19 additions & 0 deletions examples/default/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import Instabug, {
InvocationEvent,
LogLevel,
ReproStepsMode,
SessionReplay,
LaunchType,
} from 'instabug-reactnative';
import type { SessionMetadata } from 'instabug-reactnative';
import { NativeBaseProvider } from 'native-base';

import { RootTabNavigator } from './navigation/RootTab';
Expand All @@ -20,8 +23,24 @@ import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();

export const App: React.FC = () => {
const shouldSyncSession = (data: SessionMetadata) => {
if (data.launchType === LaunchType.cold) {
return true;
}
if (data.sessionDurationInSeconds > 20) {
return true;
}
if (data.OS === 'OS Level 34') {
return true;
}
return false;
};

const navigationRef = useNavigationContainerRef();

useEffect(() => {
SessionReplay.setSyncCallback((data) => shouldSyncSession(data));

Instabug.init({
token: 'deb1910a7342814af4e4c9210c786f35',
invocationEvents: [InvocationEvent.floatingButton],
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as Replies from './modules/Replies';
import type { Survey } from './modules/Surveys';
import * as Surveys from './modules/Surveys';
import * as SessionReplay from './modules/SessionReplay';
import type { SessionMetadata } from './models/SessionMetadata';

export * from './utils/Enums';
export {
Expand All @@ -28,6 +29,6 @@ export {
Replies,
Surveys,
};
export type { InstabugConfig, Survey, NetworkData, NetworkDataObfuscationHandler };
export type { InstabugConfig, Survey, NetworkData, NetworkDataObfuscationHandler, SessionMetadata };

export default Instabug;
Loading