chore: send segment anonymous id (#19122)

Add segment's `anonymousId` as a header in all API calls.

cached id ->
[details](https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/identity/#segment-id-persistence)

On Page load actions:
- If segment is enabled:
  - and cached id exists -> trigger with cached id
  - if cached id doesn’t exist, we wait for max 2 seconds.
    - if segment init is success -> trigger with anonymous id
    - if failed/delayed -> trigger without anonymous id
- If segment is disabled we don’t wait at all and anonymous id is not
sent.

Signed-off-by: Shrikant Sharat Kandula <shrikant@appsmith.com>
Co-authored-by: Shrikant Sharat Kandula <shrikant@appsmith.com>
Co-authored-by: Hetu Nandu <hetu@appsmith.com>
This commit is contained in:
Anand Srinivasan 2023-01-06 19:39:38 +05:30 committed by GitHub
parent 537186f70c
commit 071b992710
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 276 additions and 110 deletions

View File

@ -0,0 +1,9 @@
import { ReduxActionTypes } from "ce/constants/ReduxActionConstants";
export const segmentInitSuccess = () => ({
type: ReduxActionTypes.SEGMENT_INITIALIZED,
});
export const segmentInitUncertain = () => ({
type: ReduxActionTypes.SEGMENT_INIT_UNCERTAIN,
});

View File

@ -18,10 +18,13 @@ import { AUTH_LOGIN_URL } from "constants/routes";
import { getCurrentGitBranch } from "selectors/gitSyncSelectors";
import getQueryParamsObject from "utils/getQueryParamsObject";
import { UserCancelledActionExecutionError } from "sagas/ActionExecution/errorUtils";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { getAppsmithConfigs } from "ce/configs";
const executeActionRegex = /actions\/execute/;
const timeoutErrorRegex = /timeout of (\d+)ms exceeded/;
export const axiosConnectionAbortedCode = "ECONNABORTED";
const appsmithConfig = getAppsmithConfigs();
const makeExecuteActionResponse = (response: any): ActionExecutionResponse => ({
...response.data,
@ -40,6 +43,7 @@ const is404orAuthPath = () => {
// this will be used to calculate the time taken for an action
// execution request
export const apiRequestInterceptor = (config: AxiosRequestConfig) => {
config.headers = config.headers ?? {};
const branch =
getCurrentGitBranch(store.getState()) || getQueryParamsObject().branch;
if (branch && config.headers) {
@ -49,6 +53,11 @@ export const apiRequestInterceptor = (config: AxiosRequestConfig) => {
config.timeout = 1000 * 120; // increase timeout for git specific APIs
}
const anonymousId = AnalyticsUtil.getAnonymousId();
appsmithConfig.segment.enabled &&
anonymousId &&
(config.headers["x-anonymous-user-id"] = anonymousId);
return { ...config, timer: performance.now() };
};

View File

@ -757,6 +757,8 @@ export const ReduxActionTypes = {
"SET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET",
RESET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET:
"RESET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET",
SEGMENT_INITIALIZED: "SEGMENT_INITIALIZED",
SEGMENT_INIT_UNCERTAIN: "SEGMENT_INIT_UNCERTAIN",
};
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];

View File

@ -73,6 +73,7 @@ import { CanvasLevelsReduxState } from "reducers/entityReducers/autoHeightReduce
import { LintErrors } from "reducers/lintingReducers/lintErrorsReducers";
import lintErrorReducer from "reducers/lintingReducers";
import { AutoHeightUIState } from "reducers/uiReducers/autoHeightReducer";
import { AnalyticsReduxState } from "reducers/uiReducers/analyticsReducer";
export const reducerObject = {
entities: entityReducer,
@ -86,6 +87,7 @@ export const reducerObject = {
export interface AppState {
ui: {
analytics: AnalyticsReduxState;
editor: EditorReduxState;
propertyPane: PropertyPaneReduxState;
tableFilterPane: TableFilterPaneReduxState;

View File

@ -1,4 +1,4 @@
import { call, put, select, take } from "redux-saga/effects";
import { call, put, race, select, take } from "redux-saga/effects";
import {
ReduxAction,
ReduxActionWithPromise,
@ -56,6 +56,13 @@ import {
getFirstTimeUserOnboardingIntroModalVisibility,
} from "utils/storage";
import { initializeAnalyticsAndTrackers } from "utils/AppsmithUtils";
import { getAppsmithConfigs } from "ce/configs";
import { getSegmentState } from "selectors/analyticsSelectors";
import {
segmentInitUncertain,
segmentInitSuccess,
} from "actions/analyticsActions";
import { SegmentState } from "reducers/uiReducers/analyticsReducer";
export function* createUserSaga(
action: ReduxActionWithPromise<CreateUserRequest>,
@ -96,6 +103,25 @@ export function* createUserSaga(
}
}
export function* waitForSegmentInit(skipWithAnonymousId: boolean) {
if (skipWithAnonymousId && AnalyticsUtil.getAnonymousId()) return;
yield call(waitForFetchUserSuccess);
const currentUser: User | undefined = yield select(getCurrentUser);
const segmentState: SegmentState | undefined = yield select(getSegmentState);
const appsmithConfig = getAppsmithConfigs();
if (
currentUser?.enableTelemetry &&
appsmithConfig.segment.enabled &&
!segmentState
) {
yield race([
take(ReduxActionTypes.SEGMENT_INITIALIZED),
take(ReduxActionTypes.SEGMENT_INIT_UNCERTAIN),
]);
}
}
export function* getCurrentUserSaga() {
try {
PerformanceTracker.startAsyncTracking(
@ -108,7 +134,15 @@ export function* getCurrentUserSaga() {
//@ts-expect-error: response is of type unknown
const { enableTelemetry } = response.data;
if (enableTelemetry) {
initializeAnalyticsAndTrackers();
const promise = initializeAnalyticsAndTrackers();
if (promise instanceof Promise) {
const result: boolean = yield promise;
if (result) {
yield put(segmentInitSuccess());
} else {
yield put(segmentInitUncertain());
}
}
}
yield put(initAppLevelSocketConnection());
yield put(initPageLevelSocketConnection());

View File

@ -51,6 +51,7 @@ import { fetchJSLibraries } from "actions/JSLibraryActions";
import CodemirrorTernService from "utils/autocomplete/CodemirrorTernService";
import { selectFeatureFlags } from "selectors/usersSelectors";
import FeatureFlags from "entities/FeatureFlags";
import { waitForSegmentInit } from "ce/sagas/userSagas";
export default class AppEditorEngine extends AppEngine {
constructor(mode: APP_MODE) {
@ -135,6 +136,8 @@ export default class AppEditorEngine extends AppEngine {
throw new ActionsNotFoundError(
`Unable to fetch actions for the application: ${applicationId}`,
);
yield call(waitForSegmentInit, true);
yield put(fetchAllPageEntityCompletion([executePageLoadActions()]));
}

View File

@ -26,6 +26,7 @@ import AppEngine, { ActionsNotFoundError, AppEnginePayload } from ".";
import { fetchJSLibraries } from "actions/JSLibraryActions";
import FeatureFlags from "entities/FeatureFlags";
import { selectFeatureFlags } from "selectors/usersSelectors";
import { waitForSegmentInit } from "ce/sagas/userSagas";
export default class AppViewerEngine extends AppEngine {
constructor(mode: APP_MODE) {
@ -113,6 +114,7 @@ export default class AppViewerEngine extends AppEngine {
`Unable to fetch actions for the application: ${applicationId}`,
);
yield call(waitForSegmentInit, true);
yield put(fetchAllPageEntityCompletion([executePageLoadActions()]));
}
}

View File

@ -0,0 +1,37 @@
import { ReduxActionTypes } from "ce/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);

View File

@ -45,8 +45,10 @@ import guidedTourReducer from "./guidedTourReducer";
import libraryReducer from "./libraryReducer";
import appSettingsPaneReducer from "./appSettingsPaneReducer";
import autoHeightUIReducer from "./autoHeightReducer";
import analyticsReducer from "./analyticsReducer";
const uiReducer = combineReducers({
analytics: analyticsReducer,
editor: editorReducer,
errors: errorReducer,
propertyPane: propertyPaneReducer,

View File

@ -5,6 +5,11 @@ import AppEngineFactory from "entities/Engine/factory";
import { call } from "redux-saga/effects";
import { startAppEngine } from "sagas/InitSagas";
jest.mock("../../api/Api", () => ({
__esModule: true,
default: class Api {},
}));
describe("tests the sagas in initSagas", () => {
it("tests the order of execute in startAppEngine", () => {
const action = {

View File

@ -0,0 +1,4 @@
import { AppState } from "ce/reducers";
export const getSegmentState = (state: AppState) =>
state.ui.analytics.telemetry.segmentState;

View File

@ -323,59 +323,68 @@ class AnalyticsUtil {
}
static initializeSegment(key: string) {
(function init(window: any) {
const analytics = (window.analytics = window.analytics || []);
if (!analytics.initialize) {
if (analytics.invoked) {
log.error("Segment snippet included twice.");
} else {
analytics.invoked = !0;
analytics.methods = [
"trackSubmit",
"trackClick",
"trackLink",
"trackForm",
"pageview",
"identify",
"reset",
"group",
"track",
"ready",
"alias",
"debug",
"page",
"once",
"off",
"on",
];
analytics.factory = function(t: any) {
return function() {
const e = Array.prototype.slice.call(arguments); //eslint-disable-line prefer-rest-params
e.unshift(t);
analytics.push(e);
return analytics;
const initPromise = new Promise<boolean>((resolve) => {
(function init(window: any) {
const analytics = (window.analytics = window.analytics || []);
if (!analytics.initialize) {
if (analytics.invoked) {
log.error("Segment snippet included twice.");
} else {
analytics.invoked = !0;
analytics.methods = [
"trackSubmit",
"trackClick",
"trackLink",
"trackForm",
"pageview",
"identify",
"reset",
"group",
"track",
"ready",
"alias",
"debug",
"page",
"once",
"off",
"on",
];
analytics.factory = function(t: any) {
return function() {
const e = Array.prototype.slice.call(arguments); //eslint-disable-line prefer-rest-params
e.unshift(t);
analytics.push(e);
return analytics;
};
};
}
for (let t: any = 0; t < analytics.methods.length; t++) {
const e = analytics.methods[t];
analytics[e] = analytics.factory(e);
}
analytics.load = function(t: any, e: any) {
const n = document.createElement("script");
n.type = "text/javascript";
n.async = !0;
// Ref: https://www.notion.so/appsmith/530051a2083040b5bcec15a46121aea3
n.src = "https://a.appsmith.com/reroute/" + t + "/main.js";
const a: any = document.getElementsByTagName("script")[0];
a.parentNode.insertBefore(n, a);
analytics._loadOptions = e;
};
analytics.ready(() => {
resolve(true);
});
setTimeout(() => {
resolve(false);
}, 2000);
analytics.SNIPPET_VERSION = "4.1.0";
analytics.load(key);
analytics.page();
}
for (let t: any = 0; t < analytics.methods.length; t++) {
const e = analytics.methods[t];
analytics[e] = analytics.factory(e);
}
analytics.load = function(t: any, e: any) {
const n = document.createElement("script");
n.type = "text/javascript";
n.async = !0;
// Ref: https://www.notion.so/appsmith/530051a2083040b5bcec15a46121aea3
n.src = "https://a.appsmith.com/reroute/" + t + "/main.js";
const a: any = document.getElementsByTagName("script")[0];
a.parentNode.insertBefore(n, a);
analytics._loadOptions = e;
};
analytics.SNIPPET_VERSION = "4.1.0";
analytics.load(key);
analytics.page();
}
})(window);
})(window);
});
return initPromise;
}
static logEvent(eventName: EventName, eventData: any = {}) {
@ -478,6 +487,16 @@ class AnalyticsUtil {
}
}
static getAnonymousId() {
const windowDoc: any = window;
const { segment } = getAppsmithConfigs();
if (windowDoc.analytics && windowDoc.analytics.user) {
return windowDoc.analytics.user().anonymousId();
} else if (segment.enabled) {
return localStorage.getItem("ajs_anonymous_id")?.replaceAll('"', "");
}
}
static reset() {
const windowDoc: any = window;
if (windowDoc.Intercom) {

View File

@ -72,10 +72,10 @@ export const initializeAnalyticsAndTrackers = () => {
if (appsmithConfigs.segment.enabled && !(window as any).analytics) {
if (appsmithConfigs.segment.apiKey) {
// This value is only enabled for Appsmith's cloud hosted version. It is not set in self-hosted environments
AnalyticsUtil.initializeSegment(appsmithConfigs.segment.apiKey);
return AnalyticsUtil.initializeSegment(appsmithConfigs.segment.apiKey);
} else if (appsmithConfigs.segment.ceKey) {
// This value is set in self-hosted environments. But if the analytics are disabled, it's never used.
AnalyticsUtil.initializeSegment(appsmithConfigs.segment.ceKey);
return AnalyticsUtil.initializeSegment(appsmithConfigs.segment.ceKey);
}
}
} catch (e) {

View File

@ -0,0 +1,27 @@
package com.appsmith.server.helpers;
import com.appsmith.server.constants.FieldName;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class ExchangeUtils {
public static final String HEADER_ANONYMOUS_USER_ID = "X-Anonymous-User-Id";
/**
* Returns the value of `X-Anonymous-User-Id` header, from the _current_ request. Since this gets the header from
* the current request, it has to be called from a request context. It won't work in new background contexts, like
* when calling `.subscribe()` on a Mono.
* @return a Mono that resolves to the value of the `X-Anonymous-User-Id` header, if present. Else, `FieldName.ANONYMOUS_USER`.
*/
public static Mono<String> getAnonymousUserIdFromCurrentRequest() {
return Mono.deferContextual(Mono::just)
.map(contextView -> ObjectUtils.defaultIfNull(
contextView.get(ServerWebExchange.class).getRequest().getHeaders().getFirst(HEADER_ANONYMOUS_USER_ID),
FieldName.ANONYMOUS_USER
))
.defaultIfEmpty(FieldName.ANONYMOUS_USER);
}
}

View File

@ -94,7 +94,7 @@ public class GitFileUtils {
try {
Mono<Path> repoPathMono = fileUtils.saveApplicationToGitRepo(baseRepoSuffix, applicationReference, branchName).cache();
return Mono.zip(repoPathMono, sessionUserService.getCurrentUser())
.map(tuple -> {
.flatMap(tuple -> {
stopwatch.stopTimer();
Path repoPath = tuple.getT1();
// Path to repo will be : ./container-volumes/git-repo/workspaceId/defaultApplicationId/repoName/
@ -104,8 +104,8 @@ public class GitFileUtils {
FieldName.FLOW_NAME, stopwatch.getFlow(),
"executionTime", stopwatch.getExecutionTime()
);
analyticsService.sendEvent(AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), tuple.getT2().getUsername(), data);
return repoPath;
return analyticsService.sendEvent(AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), tuple.getT2().getUsername(), data)
.thenReturn(repoPath);
});
} catch (IOException | GitAPIException e) {
log.error("Error occurred while saving files to local git repo: ", e);
@ -239,7 +239,7 @@ public class GitFileUtils {
Mono<ApplicationGitReference> appReferenceMono = fileUtils
.reconstructApplicationReferenceFromGitRepo(workspaceId, defaultApplicationId, repoName, branchName);
return Mono.zip(appReferenceMono, sessionUserService.getCurrentUser())
.map(tuple -> {
.flatMap(tuple -> {
ApplicationGitReference applicationReference = tuple.getT1();
// Extract application metadata from the json
ApplicationJson metadata = getApplicationResource(applicationReference.getMetadata(), ApplicationJson.class);
@ -252,8 +252,8 @@ public class GitFileUtils {
FieldName.FLOW_NAME, stopwatch.getFlow(),
"executionTime", stopwatch.getExecutionTime()
);
analyticsService.sendEvent(AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), tuple.getT2().getUsername(), data);
return applicationJson;
return analyticsService.sendEvent(AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), tuple.getT2().getUsername(), data)
.thenReturn(applicationJson);
});
}

View File

@ -16,9 +16,9 @@ public interface AnalyticsServiceCE {
void identifyInstance(String instanceId, String role, String useCase);
void sendEvent(String event, String userId, Map<String, ?> properties);
Mono<Void> sendEvent(String event, String userId, Map<String, ?> properties);
void sendEvent(String event, String userId, Map<String, ?> properties, boolean hashUserId);
Mono<Void> sendEvent(String event, String userId, Map<String, ?> properties, boolean hashUserId);
<T extends BaseDomain> Mono<T> sendObjectEvent(AnalyticsEvents event, T object, Map<String, Object> extraProperties);

View File

@ -8,6 +8,7 @@ import com.appsmith.server.domains.NewAction;
import com.appsmith.server.domains.NewPage;
import com.appsmith.server.domains.User;
import com.appsmith.server.domains.UserData;
import com.appsmith.server.helpers.ExchangeUtils;
import com.appsmith.server.helpers.PolicyUtils;
import com.appsmith.server.helpers.UserUtils;
import com.appsmith.server.services.ConfigService;
@ -19,9 +20,9 @@ import com.segment.analytics.messages.TrackMessage;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.util.HashMap;
import java.util.List;
@ -118,14 +119,14 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
}
@Override
public void sendEvent(String event, String userId, Map<String, ?> properties) {
sendEvent(event, userId, properties, true);
public Mono<Void> sendEvent(String event, String userId, Map<String, ?> properties) {
return sendEvent(event, userId, properties, true);
}
@Override
public void sendEvent(String event, String userId, Map<String, ?> properties, boolean hashUserId) {
public Mono<Void> sendEvent(String event, String userId, Map<String, ?> properties, boolean hashUserId) {
if (!isActive()) {
return;
return Mono.empty();
}
// Can't update the properties directly as it's throwing ImmutableCollection error
@ -158,17 +159,26 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
}
final String finalUserId = userId;
configService.getInstanceId()
.map(instanceId -> {
TrackMessage.Builder messageBuilder = TrackMessage.builder(event).userId(finalUserId);
return Mono.zip(
ExchangeUtils.getAnonymousUserIdFromCurrentRequest(),
configService.getInstanceId()
.defaultIfEmpty("unknown-instance-id")
).map(tuple -> {
final String userIdFromClient = tuple.getT1();
final String instanceId = tuple.getT2();
String userIdToSend = finalUserId;
if (FieldName.ANONYMOUS_USER.equals(finalUserId)) {
userIdToSend = StringUtils.defaultIfEmpty(userIdFromClient, FieldName.ANONYMOUS_USER);
}
TrackMessage.Builder messageBuilder = TrackMessage.builder(event).userId(userIdToSend);
analyticsProperties.put("originService", "appsmith-server");
analyticsProperties.put("instanceId", instanceId);
messageBuilder = messageBuilder.properties(analyticsProperties);
analytics.enqueue(messageBuilder);
return instanceId;
})
.subscribeOn(Schedulers.boundedElastic())
.subscribe();
.then();
}
@Override
@ -197,7 +207,15 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
.switchIfEmpty(Mono.just(anonymousUser));
return userMono
.map(user -> {
.flatMap(user -> Mono.zip(
user.isAnonymous()
? ExchangeUtils.getAnonymousUserIdFromCurrentRequest()
: Mono.just(user.getUsername()),
Mono.just(user)
))
.flatMap(tuple -> {
final String id = tuple.getT1();
final User user = tuple.getT2();
// In case the user is anonymous, don't raise an event, unless it's a signup, logout, page view or action execution event.
boolean isEventUserSignUpOrLogout = object instanceof User && (event == AnalyticsEvents.CREATE || event == AnalyticsEvents.LOGOUT);
@ -205,13 +223,13 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
boolean isEventActionExecution = object instanceof NewAction && event == AnalyticsEvents.EXECUTE_ACTION;
boolean isAvoidLoggingEvent = user.isAnonymous() && !(isEventUserSignUpOrLogout || isEventPageView || isEventActionExecution);
if (isAvoidLoggingEvent) {
return object;
return Mono.just(object);
}
final String username = (object instanceof User ? (User) object : user).getUsername();
HashMap<String, Object> analyticsProperties = new HashMap<>();
analyticsProperties.put("id", username);
analyticsProperties.put("id", id);
analyticsProperties.put("oid", object.getId());
if (extraProperties != null) {
analyticsProperties.putAll(extraProperties);
@ -219,8 +237,8 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
analyticsProperties.remove(FieldName.EVENT_DATA);
}
sendEvent(eventTag, username, analyticsProperties);
return object;
return sendEvent(eventTag, username, analyticsProperties)
.thenReturn(object);
});
}

View File

@ -18,7 +18,6 @@ import com.appsmith.server.constants.GitDefaultCommitMessage;
import com.appsmith.server.constants.SerialiseApplicationObjective;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.ApplicationMode;
import com.appsmith.server.dtos.ApplicationJson;
import com.appsmith.server.domains.GitApplicationMetadata;
import com.appsmith.server.domains.GitAuth;
import com.appsmith.server.domains.GitDeployKeys;
@ -27,6 +26,7 @@ import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.UserData;
import com.appsmith.server.domains.Workspace;
import com.appsmith.server.dtos.ApplicationImportDTO;
import com.appsmith.server.dtos.ApplicationJson;
import com.appsmith.server.dtos.GitCommitDTO;
import com.appsmith.server.dtos.GitConnectDTO;
import com.appsmith.server.dtos.GitDocsDTO;
@ -93,8 +93,6 @@ import static com.appsmith.external.constants.GitConstants.EMPTY_COMMIT_ERROR_ME
import static com.appsmith.external.constants.GitConstants.GIT_CONFIG_ERROR;
import static com.appsmith.external.constants.GitConstants.GIT_PROFILE_ERROR;
import static com.appsmith.external.constants.GitConstants.MERGE_CONFLICT_BRANCH_NAME;
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
import static com.appsmith.server.constants.CommentConstants.APPSMITH_BOT_USERNAME;
import static com.appsmith.server.constants.FieldName.DEFAULT;
import static com.appsmith.server.helpers.DefaultResourcesUtils.createDefaultIdsOrUpdateWithGivenResourceIds;
@ -2455,10 +2453,7 @@ public class GitServiceCEImpl implements GitServiceCE {
);
analyticsProps.put(FieldName.EVENT_DATA, eventData);
return sessionUserService.getCurrentUser()
.map(user -> {
analyticsService.sendEvent(eventName, user.getUsername(), analyticsProps);
return application;
});
.flatMap(user -> analyticsService.sendEvent(eventName, user.getUsername(), analyticsProps).thenReturn(application));
}
@Override

View File

@ -237,17 +237,16 @@ public class MockDataServiceCEImpl implements MockDataServiceCE {
}
return sessionUserService.getCurrentUser()
.map(user -> {
analyticsService.sendEvent(
AnalyticsEvents.CREATE.getEventName(),
user.getUsername(),
Map.of(
"MockDataSource", defaultIfNull(name, ""),
"orgId", defaultIfNull(workspaceId, "")
)
);
return user;
});
.flatMap(user ->
analyticsService.sendEvent(
AnalyticsEvents.CREATE.getEventName(),
user.getUsername(),
Map.of(
"MockDataSource", defaultIfNull(name, ""),
"orgId", defaultIfNull(workspaceId, "")
)
).thenReturn(user)
);
}
}

View File

@ -1059,7 +1059,7 @@ public class CreateDBTablePageSolutionCEImpl implements CreateDBTablePageSolutio
private Mono<CRUDPageResponseDTO> sendGenerateCRUDPageAnalyticsEvent(CRUDPageResponseDTO crudPage, Datasource datasource, String pluginName) {
PageDTO page = crudPage.getPage();
return sessionUserService.getCurrentUser()
.map(currentUser -> {
.flatMap(currentUser -> {
try {
final Map<String, Object> data = Map.of(
"applicationId", page.getApplicationId(),
@ -1069,13 +1069,13 @@ public class CreateDBTablePageSolutionCEImpl implements CreateDBTablePageSolutio
"datasourceId", datasource.getId(),
"organizationId", datasource.getWorkspaceId()
);
analyticsService.sendEvent(AnalyticsEvents.GENERATE_CRUD_PAGE.getEventName(), currentUser.getUsername(), data);
return analyticsService.sendEvent(AnalyticsEvents.GENERATE_CRUD_PAGE.getEventName(), currentUser.getUsername(), data)
.thenReturn(crudPage);
} catch (Exception e) {
log.warn("Error sending generate CRUD DB table page data point", e);
}
return crudPage;
return Mono.just(crudPage);
});
}
}
}

View File

@ -480,7 +480,7 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
});
})
.then(currentUserMono)
.map(user -> {
.flatMap(user -> {
stopwatch.stopTimer();
final Map<String, Object> data = Map.of(
FieldName.APPLICATION_ID, applicationId,
@ -490,8 +490,8 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
FieldName.FLOW_NAME, stopwatch.getFlow(),
"executionTime", stopwatch.getExecutionTime()
);
analyticsService.sendEvent(AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), user.getUsername(), data);
return applicationJson;
return analyticsService.sendEvent(AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), user.getUsername(), data)
.thenReturn(applicationJson);
})
.then(allCustomJSLibListMono)
.map(allCustomLibList -> {
@ -1182,7 +1182,7 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
.then(applicationService.update(importedApplication.getId(), importedApplication))
.then(sendImportExportApplicationAnalyticsEvent(importedApplication.getId(), AnalyticsEvents.IMPORT))
.zipWith(currUserMono)
.map(tuple -> {
.flatMap(tuple -> {
Application application = tuple.getT1();
stopwatch.stopTimer();
stopwatch.stopAndLogTimeInMillis();
@ -1195,8 +1195,8 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
FieldName.FLOW_NAME, stopwatch.getFlow(),
"executionTime", stopwatch.getExecutionTime()
);
analyticsService.sendEvent(AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), tuple.getT2().getUsername(), data);
return application;
return analyticsService.sendEvent(AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), tuple.getT2().getUsername(), data)
.thenReturn(application);
});
})
.onErrorResume(throwable -> {

View File

@ -227,7 +227,8 @@ public class UserSignupCEImpl implements UserSignupCE {
configService.getInstanceId()
.map(instanceId -> {
log.debug("Installation setup complete.");
analyticsService.sendEvent(
analyticsService.identifyInstance(instanceId, userData.getRole(), userData.getUseCase());
return analyticsService.sendEvent(
AnalyticsEvents.INSTALLATION_SETUP_COMPLETE.getEventName(),
instanceId,
Map.of(
@ -238,9 +239,7 @@ public class UserSignupCEImpl implements UserSignupCE {
"goal", ObjectUtils.defaultIfNull(userData.getUseCase(), "")
),
false
);
analyticsService.identifyInstance(instanceId, userData.getRole(), userData.getUseCase());
return instanceId;
).thenReturn(instanceId);
}),
envManager.applyChanges(Map.of(
APPSMITH_DISABLE_TELEMETRY.name(),