diff --git a/app/client/cypress/fixtures/user.json b/app/client/cypress/fixtures/user.json new file mode 100644 index 0000000000..83564398db --- /dev/null +++ b/app/client/cypress/fixtures/user.json @@ -0,0 +1,15 @@ +{ + "accountNonExpired": true, + "accountNonLocked": true, + "credentialsNonExpired": true, + "email": "test@appsmith.com", + "emptyInstance": false, + "enableTelemetry": false, + "isAnonymous": false, + "isConfigurable": true, + "isEnabled": true, + "isSuperUser": true, + "name": "test", + "organizationIds": ["61a8a112ccc8b629d92a1aad"], + "username": "test@appsmith.com" +} \ No newline at end of file diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Auth/Analytics_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Auth/Analytics_spec.js new file mode 100644 index 0000000000..1d1904df12 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Auth/Analytics_spec.js @@ -0,0 +1,116 @@ +import User from "../../../../fixtures/user.json"; + +let appId; + +describe("Checks for analytics initialization", function() { + it("Should check analytics is not initialised when enableTelemtry is false", function() { + cy.intercept("GET", "/api/v1/users/me", { + body: { responseMeta: { status: 200, success: true }, data: User }, + }).as("getUsersWithoutTelemetry"); + cy.visit("/applications"); + cy.reload(); + cy.wait("@getUsersWithoutTelemetry"); + cy.window().then((window) => { + expect(window.analytics).to.be.equal(undefined); + }); + let interceptFlag = false; + cy.intercept("POST", "https://api.segment.io/**", (req) => { + interceptFlag = true; + req.continue(); + }); + cy.generateUUID().then((id) => { + appId = id; + cy.CreateAppInFirstListedOrg(id); + localStorage.setItem("AppName", appId); + }); + cy.wait(3000); + cy.window().then(() => { + cy.wrap(interceptFlag).should("eq", false); + }); + }); + it("Should check analytics is initialised when enableTelemtry is true", function() { + cy.intercept("GET", "/api/v1/users/me", { + body: { + responseMeta: { status: 200, success: true }, + data: { + ...User, + enableTelemetry: true, + }, + }, + }).as("getUsersWithTelemetry"); + cy.visit("/applications"); + cy.reload(); + cy.wait("@getUsersWithTelemetry"); + cy.wait(5000); + cy.window().then((window) => { + expect(window.analytics).not.to.be.undefined; + }); + cy.wait(3000); + let interceptFlag = false; + cy.intercept("POST", "https://api.segment.io/**", (req) => { + interceptFlag = true; + req.continue(); + }).as("segment"); + cy.generateUUID().then((id) => { + appId = id; + cy.CreateAppInFirstListedOrg(id); + localStorage.setItem("AppName", appId); + }); + cy.wait("@segment"); + cy.window().then(() => { + cy.wrap(interceptFlag).should("eq", true); + }); + }); + + it("Should check smartlook is not initialised when enableTelemtry is false", function() { + cy.intercept("GET", "/api/v1/users/me", { + body: { responseMeta: { status: 200, success: true }, data: User }, + }).as("getUsersWithoutTelemetry"); + cy.visit("/applications"); + cy.reload(); + cy.wait("@getUsersWithoutTelemetry"); + cy.window().then((window) => { + expect(window.smartlook).to.be.equal(undefined); + }); + let interceptFlag = false; + cy.intercept("POST", "https://**.smartlook.**", (req) => { + interceptFlag = true; + req.continue(); + }); + cy.generateUUID().then((id) => { + appId = id; + cy.CreateAppInFirstListedOrg(id); + localStorage.setItem("AppName", appId); + }); + cy.wait(3000); + cy.window().then(() => { + cy.wrap(interceptFlag).should("eq", false); + }); + }); + + it("Should check Sentry is not initialised when enableTelemtry is false", function() { + cy.intercept("GET", "/api/v1/users/me", { + body: { responseMeta: { status: 200, success: true }, data: User }, + }).as("getUsersWithoutTelemetry"); + cy.visit("/applications"); + cy.reload(); + cy.wait("@getUsersWithoutTelemetry"); + cy.window().then((window) => { + expect(window.Sentry).to.be.equal(undefined); + }); + let interceptFlag = false; + cy.intercept("POST", "https://**.sentry.io/**", (req) => { + interceptFlag = true; + req.continue(); + }); + cy.generateUUID().then((id) => { + appId = id; + cy.CreateAppInFirstListedOrg(id); + localStorage.setItem("AppName", appId); + }); + cy.wait(3000); + cy.window().then(() => { + cy.wrap(interceptFlag).should("eq", false); + }); + }); +}); diff --git a/app/client/public/index.html b/app/client/public/index.html index 47c232e73c..cde337a428 100755 --- a/app/client/public/index.html +++ b/app/client/public/index.html @@ -170,7 +170,6 @@ const CONFIG_LOG_LEVEL_INDEX = LOG_LEVELS.indexOf(parseConfig("__APPSMITH_CLIENT_LOG_LEVEL__")); const INTERCOM_APP_ID = parseConfig("%REACT_APP_INTERCOM_APP_ID%") || parseConfig("__APPSMITH_INTERCOM_APP_ID__"); - const DISABLE_TELEMETRY = parseConfig("__APPSMITH_DISABLE_TELEMETRY__"); const DISABLE_INTERCOM = parseConfig("__APPSMITH_DISABLE_INTERCOM__"); // Initialize the Intercom library @@ -215,7 +214,6 @@ }, intercomAppID: INTERCOM_APP_ID, mailEnabled: parseConfig("__APPSMITH_MAIL_ENABLED__"), - disableTelemetry: DISABLE_TELEMETRY === "" || DISABLE_TELEMETRY, cloudServicesBaseUrl: parseConfig("__APPSMITH_CLOUD_SERVICES_BASE_URL__") || "https://cs.appsmith.com", googleRecaptchaSiteKey: parseConfig("__APPSMITH_RECAPTCHA_SITE_KEY__"), }; diff --git a/app/client/src/configs/index.ts b/app/client/src/configs/index.ts index 929b234b36..f143c1ff51 100644 --- a/app/client/src/configs/index.ts +++ b/app/client/src/configs/index.ts @@ -42,7 +42,6 @@ export type INJECTED_CONFIGS = { }; intercomAppID: string; mailEnabled: boolean; - disableTelemetry: boolean; cloudServicesBaseUrl: string; googleRecaptchaSiteKey: string; supportEmail: string; @@ -120,7 +119,6 @@ const getConfigsFromEnvVars = (): INJECTED_CONFIGS => { mailEnabled: process.env.REACT_APP_MAIL_ENABLED ? process.env.REACT_APP_MAIL_ENABLED.length > 0 : false, - disableTelemetry: true, cloudServicesBaseUrl: process.env.REACT_APP_CLOUD_SERVICES_BASE_URL || "", googleRecaptchaSiteKey: process.env.REACT_APP_GOOGLE_RECAPTCHA_SITE_KEY || "", @@ -212,21 +210,9 @@ export const getAppsmithConfigs = (): AppsmithUIConfigs => { // We enable segment tracking if either the Cloud API key is set or the self-hosted CE key is set segment.enabled = segment.enabled || segmentCEKey.enabled; - let sentryTelemetry = true; - // Turn off all analytics if telemetry is disabled - if (APPSMITH_FEATURE_CONFIGS.disableTelemetry) { - smartLook.enabled = false; - segment.enabled = false; - sentryTelemetry = false; - } - return { sentry: { - enabled: - sentryDSN.enabled && - sentryRelease.enabled && - sentryENV.enabled && - sentryTelemetry, + enabled: sentryDSN.enabled && sentryRelease.enabled && sentryENV.enabled, dsn: sentryDSN.value, release: sentryRelease.value, environment: sentryENV.value, @@ -288,7 +274,6 @@ export const getAppsmithConfigs = (): AppsmithUIConfigs => { intercomAppID: ENV_CONFIG.intercomAppID || APPSMITH_FEATURE_CONFIGS.intercomAppID, mailEnabled: ENV_CONFIG.mailEnabled || APPSMITH_FEATURE_CONFIGS.mailEnabled, - disableTelemetry: APPSMITH_FEATURE_CONFIGS.disableTelemetry, commentsTestModeEnabled: false, cloudServicesBaseUrl: ENV_CONFIG.cloudServicesBaseUrl || diff --git a/app/client/src/configs/types.ts b/app/client/src/configs/types.ts index 680e523a08..f6db656266 100644 --- a/app/client/src/configs/types.ts +++ b/app/client/src/configs/types.ts @@ -68,8 +68,6 @@ export type AppsmithUIConfigs = { }; intercomAppID: string; mailEnabled: boolean; - - disableTelemetry: boolean; commentsTestModeEnabled: boolean; cloudServicesBaseUrl: string; diff --git a/app/client/src/constants/userConstants.ts b/app/client/src/constants/userConstants.ts index ec4931eccb..ee02bfc186 100644 --- a/app/client/src/constants/userConstants.ts +++ b/app/client/src/constants/userConstants.ts @@ -20,6 +20,7 @@ export type User = { role?: string; useCase?: string; isConfigurable: boolean; + enableTelemetry: boolean; }; export interface UserApplication { @@ -39,6 +40,7 @@ export const DefaultCurrentUserDetails: User = { gender: "MALE", isSuperUser: false, isConfigurable: false, + enableTelemetry: false, }; // TODO keeping it here instead of the USER_API since it leads to cyclic deps errors during tests diff --git a/app/client/src/sagas/userSagas.tsx b/app/client/src/sagas/userSagas.tsx index b0a6ac1d56..562e11a93e 100644 --- a/app/client/src/sagas/userSagas.tsx +++ b/app/client/src/sagas/userSagas.tsx @@ -65,6 +65,7 @@ import { getFirstTimeUserOnboardingApplicationId, getFirstTimeUserOnboardingIntroModalVisibility, } from "utils/storage"; +import { initializeAnalyticsAndTrackers } from "utils/AppsmithUtils"; export function* createUserSaga( action: ReduxActionWithPromise, @@ -113,13 +114,17 @@ export function* getCurrentUserSaga() { const isValidResponse = yield validateResponse(response); if (isValidResponse) { + const { enableTelemetry } = response.data; + if (enableTelemetry) { + initializeAnalyticsAndTrackers(); + } yield put(initAppLevelSocketConnection()); yield put(initPageLevelSocketConnection()); if ( !response.data.isAnonymous && response.data.username !== ANONYMOUS_USERNAME ) { - AnalyticsUtil.identifyUser(response.data); + enableTelemetry && AnalyticsUtil.identifyUser(response.data); // make fetch feature call only if logged in yield put(fetchFeatureFlagsInit()); } else { diff --git a/app/client/src/utils/AnalyticsUtil.tsx b/app/client/src/utils/AnalyticsUtil.tsx index 16a4dbe506..3c9d3fcf0d 100644 --- a/app/client/src/utils/AnalyticsUtil.tsx +++ b/app/client/src/utils/AnalyticsUtil.tsx @@ -267,7 +267,7 @@ class AnalyticsUtil { if (userData) { const { segment } = getAppsmithConfigs(); let user: any = {}; - if (segment.enabled && segment.apiKey) { + if (userData.enableTelemetry && segment.apiKey) { user = { userId: userData.username, email: userData.email, @@ -291,7 +291,7 @@ class AnalyticsUtil { }; } - if (windowDoc.analytics) { + if (userData?.enableTelemetry && windowDoc.analytics) { log.debug("Event fired", eventName, finalEventData); windowDoc.analytics.track(eventName, finalEventData); } else { diff --git a/app/client/src/utils/AppsmithUtils.tsx b/app/client/src/utils/AppsmithUtils.tsx index 39384e40e3..22662630af 100644 --- a/app/client/src/utils/AppsmithUtils.tsx +++ b/app/client/src/utils/AppsmithUtils.tsx @@ -53,6 +53,11 @@ export const createImmerReducer = ( export const appInitializer = () => { FormControlRegistry.registerFormControlBuilders(); const appsmithConfigs = getAppsmithConfigs(); + log.setLevel(getEnvLogLevel(appsmithConfigs.logLevel)); +}; + +export const initializeAnalyticsAndTrackers = () => { + const appsmithConfigs = getAppsmithConfigs(); if (appsmithConfigs.sentry.enabled) { window.Sentry = Sentry; @@ -92,8 +97,6 @@ export const appInitializer = () => { AnalyticsUtil.initializeSegment(appsmithConfigs.segment.ceKey); } } - - log.setLevel(getEnvLogLevel(appsmithConfigs.logLevel)); }; export const mapToPropList = (map: Record): Property[] => { diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UserProfileDTO.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UserProfileDTO.java index 9d215e2f0f..eda8dcd240 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UserProfileDTO.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/dtos/UserProfileDTO.java @@ -41,6 +41,8 @@ public class UserProfileDTO { String useCase; + boolean enableTelemetry = false; + public boolean isAccountNonExpired() { return this.isEnabled; } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/UserServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/UserServiceImpl.java index a373b783dc..8e077ffd62 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/UserServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/UserServiceImpl.java @@ -912,6 +912,7 @@ public class UserServiceImpl extends BaseService i profile.setRole(userData.getRole()); profile.setUseCase(userData.getUseCase()); profile.setPhotoId(userData.getProfilePhotoAssetId()); + profile.setEnableTelemetry(!commonConfig.isTelemetryDisabled()); profile.setSuperUser(policyUtils.isPermissionPresentForUser( userFromDb.getPolicies(),