Skip to content

Commit

Permalink
fix: UsagePulse initialisation flow (#38555)
Browse files Browse the repository at this point in the history
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
hetunandu and coderabbitai[bot] authored Jan 9, 2025
1 parent dfd7fde commit 388fd81
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 19 deletions.
8 changes: 8 additions & 0 deletions app/client/src/actions/analyticsActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";

export const segmentInitSuccess = () => ({
type: ReduxActionTypes.SEGMENT_INITIALIZED,
});
export const segmentInitUncertain = () => ({
type: ReduxActionTypes.SEGMENT_INIT_UNCERTAIN,
});
2 changes: 2 additions & 0 deletions app/client/src/ce/constants/ReduxActionConstants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,8 @@ const TenantActionErrorTypes = {
};

const AnalyticsActionTypes = {
SEGMENT_INITIALIZED: "SEGMENT_INITIALIZED",
SEGMENT_INIT_UNCERTAIN: "SEGMENT_INIT_UNCERTAIN",
SET_BUILDING_BLOCK_DRAG_START_TIME: "SET_BUILDING_BLOCK_DRAG_START_TIME",
RESET_BUILDING_BLOCK_DRAG_START_TIME: "RESET_BUILDING_BLOCK_DRAG_START_TIME",
SEND_ANALYTICS_FOR_SIDE_BY_SIDE_HOVER:
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/reducers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import type { CanvasLevelsReduxState } from "reducers/entityReducers/autoHeightR
import type { LintErrorsStore } from "reducers/lintingReducers/lintErrorsReducers";
import lintErrorReducer from "reducers/lintingReducers";
import type { AutoHeightUIState } from "reducers/uiReducers/autoHeightReducer";
import type { AnalyticsReduxState } from "reducers/uiReducers/analyticsReducer";
import type { MetaWidgetsReduxState } from "reducers/entityReducers/metaWidgetsReducer";
import type { layoutConversionReduxState } from "reducers/uiReducers/layoutConversionReducer";
import type { OneClickBindingState } from "reducers/uiReducers/oneClickBindingReducer";
Expand Down Expand Up @@ -94,6 +95,7 @@ export const reducerObject = {
export interface AppState {
ui: {
consolidatedPageLoad: ConsolidatedPageLoadState;
analytics: AnalyticsReduxState;
editor: EditorReduxState;
propertyPane: PropertyPaneReduxState;
tableFilterPane: TableFilterPaneReduxState;
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/reducers/uiReducers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { editorContextReducer } from "ee/reducers/uiReducers/editorContextReduce
import libraryReducer from "reducers/uiReducers/libraryReducer";
import appSettingsPaneReducer from "reducers/uiReducers/appSettingsPaneReducer";
import autoHeightUIReducer from "reducers/uiReducers/autoHeightReducer";
import analyticsReducer from "reducers/uiReducers/analyticsReducer";
import layoutConversionReducer from "reducers/uiReducers/layoutConversionReducer";
import oneClickBindingReducer from "reducers/uiReducers/oneClickBindingReducer";
import activeFieldReducer from "reducers/uiReducers/activeFieldEditorReducer";
Expand All @@ -48,6 +49,7 @@ import consolidatedPageLoadReducer from "reducers/uiReducers/consolidatedPageLoa
import { pluginActionReducer } from "PluginActionEditor/store";

export const uiReducerObject = {
analytics: analyticsReducer,
editor: editorReducer,
errors: errorReducer,
propertyPane: propertyPaneReducer,
Expand Down
91 changes: 72 additions & 19 deletions app/client/src/ce/sagas/userSagas.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { call, fork, put, select, take } from "redux-saga/effects";
import {
all,
call,
fork,
put,
select,
take,
type TakeEffect,
} from "redux-saga/effects";
import type {
ReduxAction,
ReduxActionWithPromise,
Expand Down Expand Up @@ -75,6 +83,11 @@ import type {
import { selectFeatureFlags } from "ee/selectors/featureFlagsSelectors";
import { getFromServerWhenNoPrefetchedResult } from "sagas/helper";
import type { SessionRecordingConfig } from "utils/Analytics/mixpanel";
import {
segmentInitSuccess,
segmentInitUncertain,
} from "actions/analyticsActions";
import { getSegmentState } from "selectors/analyticsSelectors";

export function* getCurrentUserSaga(action?: {
payload?: { userProfile?: ApiResponse };
Expand Down Expand Up @@ -139,44 +152,82 @@ function* getSessionRecordingConfig() {

function* initTrackers(currentUser: User) {
try {
const isFFFetched: boolean = yield select(getFeatureFlagsFetched);

if (!isFFFetched) {
yield take(ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS);
}

const sessionRecordingConfig: SessionRecordingConfig = yield call(
getSessionRecordingConfig,
);

yield call(AnalyticsUtil.initialize, currentUser, sessionRecordingConfig);
yield put(segmentInitSuccess());
} catch (e) {
log.error(e);
yield put(segmentInitUncertain());
}
}

function* restartUserTracking() {
function* waitForInitialization() {
const currentUser: User = yield select(getCurrentUser);
const { enableTelemetry } = currentUser;
const isAirgappedInstance = isAirgapped();

// Dependents for starting tracking
const isFFFetched: boolean = yield select(getFeatureFlagsFetched);
const isSegmentInitialized: string | undefined =
yield select(getSegmentState);

const waits: TakeEffect[] = [];

// FF is required to know if GAC is enabled for the user
if (!isFFFetched) {
yield call(fetchFeatureFlagsInit);
yield take(ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS);
yield fork(fetchFeatureFlagsInit);
waits.push(take(ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS));
}

const featureFlags: FeatureFlags = yield select(selectFeatureFlags);
// If the user is anonymous, we need to wait for the editor or viewer to initialize
if (currentUser?.isAnonymous) {
waits.push(
take([
ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
]),
);

const isGACEnabled = featureFlags?.license_gac_enabled;
// If the user is anonymous, we need to wait for the segment to initialize
// As it will provide the anonymous id
if (isSegmentInitialized === undefined) {
waits.push(
take([
ReduxActionTypes.SEGMENT_INITIALIZED,
ReduxActionTypes.SEGMENT_INIT_UNCERTAIN,
]),
);
}
}

const isFreeLicense = !isGACEnabled;
// Only wait for actions that are still pending
yield all(waits);
}

function* restartUserTracking() {
const currentUser: User = yield select(getCurrentUser);
const { enableTelemetry } = currentUser;
const isAirgappedInstance = isAirgapped();

if (!isAirgappedInstance) {
// We need to stop and start tracking activity to ensure that the tracking from previous session is not carried forward
// We need to stop and start tracking activity to ensure that the tracking
// from previous session is not carried forward
yield call(UsagePulse.stopTrackingActivity);

if (currentUser?.isAnonymous) {
yield take([
ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
]);
}
// Wait for any items that are required for tracking
yield call(waitForInitialization);

const featureFlags: FeatureFlags = yield select(selectFeatureFlags);

const isGACEnabled = featureFlags?.license_gac_enabled;

const isFreeLicense = !isGACEnabled;

yield call(
UsagePulse.startTrackingActivity,
Expand All @@ -191,12 +242,14 @@ export function* runUserSideEffectsSaga() {
const currentUser: User = yield select(getCurrentUser);
const { enableTelemetry } = currentUser;

yield fork(restartUserTracking);

if (enableTelemetry) {
yield fork(initTrackers, currentUser);
} else {
yield put(segmentInitSuccess());
}

yield fork(restartUserTracking);

if (currentUser.emptyInstance) {
history.replace(SETUP);
}
Expand Down
35 changes: 35 additions & 0 deletions app/client/src/reducers/uiReducers/analyticsReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import { createReducer } from "utils/ReducerUtils";

export type SegmentState = "INIT_SUCCESS" | "INIT_UNCERTAIN";
export const initialState: AnalyticsReduxState = {
telemetry: {},
};

export interface AnalyticsReduxState {
telemetry: {
segmentState?: SegmentState;
};
}

export const handlers = {
[ReduxActionTypes.SEGMENT_INITIALIZED]: (
state: AnalyticsReduxState,
): AnalyticsReduxState => ({
...state,
telemetry: {
...state.telemetry,
segmentState: "INIT_SUCCESS",
},
}),
[ReduxActionTypes.SEGMENT_INIT_UNCERTAIN]: (
state: AnalyticsReduxState,
): AnalyticsReduxState => ({
...state,
telemetry: {
...state.telemetry,
segmentState: "INIT_UNCERTAIN",
},
}),
};
export default createReducer(initialState, handlers);
4 changes: 4 additions & 0 deletions app/client/src/selectors/analyticsSelectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { AppState } from "ee/reducers";

export const getSegmentState = (state: AppState) =>
state.ui.analytics.telemetry.segmentState;

0 comments on commit 388fd81

Please sign in to comment.