diff --git a/redisinsight/ui/src/components/analytics-tabs/constants.ts b/redisinsight/ui/src/components/analytics-tabs/constants.ts index ca59090a43..0b12ebfa04 100644 --- a/redisinsight/ui/src/components/analytics-tabs/constants.ts +++ b/redisinsight/ui/src/components/analytics-tabs/constants.ts @@ -12,7 +12,7 @@ export const analyticsViewTabs: AnalyticsTabs[] = [ }, { id: AnalyticsViewTab.DatabaseAnalysis, - label: 'Memory Efficiency', + label: 'Redis Database Analysis', }, { id: AnalyticsViewTab.SlowLog, diff --git a/redisinsight/ui/src/pages/databaseAnalysis/DatabaseAnalysisPage.tsx b/redisinsight/ui/src/pages/databaseAnalysis/DatabaseAnalysisPage.tsx index b961564655..61cb5a42ed 100644 --- a/redisinsight/ui/src/pages/databaseAnalysis/DatabaseAnalysisPage.tsx +++ b/redisinsight/ui/src/pages/databaseAnalysis/DatabaseAnalysisPage.tsx @@ -4,7 +4,7 @@ import { useParams } from 'react-router-dom' import { dbAnalysisSelector, - DBAnalysisReportsSelector, + dbAnalysisReportsSelector, fetchDBAnalysisAction, fetchDBAnalysisReportsHistory, setSelectedAnalysisId @@ -14,6 +14,7 @@ import { appAnalyticsInfoSelector } from 'uiSrc/slices/app/info' import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances' import { AnalyticsViewTab } from 'uiSrc/slices/interfaces/analytics' import { sendPageViewTelemetry, sendEventTelemetry, TelemetryPageView, TelemetryEvent } from 'uiSrc/telemetry' +import { formatLongName, getDbIndex, setTitle } from 'uiSrc/utils' import Header from './components/header' import AnalysisDataView from './components/analysis-data-view' @@ -24,12 +25,14 @@ const DatabaseAnalysisPage = () => { const { viewTab } = useSelector(analyticsSettingsSelector) const { identified: analyticsIdentified } = useSelector(appAnalyticsInfoSelector) const { loading: analysisLoading, data } = useSelector(dbAnalysisSelector) - const { data: reports, selectedAnalysis } = useSelector(DBAnalysisReportsSelector) - const { name: connectedInstanceName } = useSelector(connectedInstanceSelector) + const { data: reports, selectedAnalysis } = useSelector(dbAnalysisReportsSelector) + const { name: connectedInstanceName, db } = useSelector(connectedInstanceSelector) const [isPageViewSent, setIsPageViewSent] = useState(false) const dispatch = useDispatch() + const dbName = `${formatLongName(connectedInstanceName, 33, 0, '...')} ${getDbIndex(db)}` + setTitle(`${dbName} - Database Analysis`) useEffect(() => { dispatch(fetchDBAnalysisReportsHistory(instanceId)) @@ -51,7 +54,7 @@ const DatabaseAnalysisPage = () => { const handleSelectAnalysis = (reportId: string) => { sendEventTelemetry({ - event: TelemetryEvent.MEMORY_ANALYSIS_HISTORY_VIEWED, + event: TelemetryEvent.DATABASE_ANALYSIS_HISTORY_VIEWED, eventData: { databaseId: instanceId, } diff --git a/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-data-view/AnalysisDataView.spec.tsx b/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-data-view/AnalysisDataView.spec.tsx index a594d888e3..cad25c72c7 100644 --- a/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-data-view/AnalysisDataView.spec.tsx +++ b/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-data-view/AnalysisDataView.spec.tsx @@ -1,14 +1,21 @@ import React from 'react' import { instance, mock } from 'ts-mockito' import { MOCK_ANALYSIS_REPORT_DATA } from 'uiSrc/mocks/data/analysis' +import { INSTANCE_ID_MOCK } from 'uiSrc/mocks/handlers/analytics/clusterDetailsHandlers' +import { SectionName } from 'uiSrc/pages/databaseAnalysis' +import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' import { formatBytes, getGroupTypeDisplay } from 'uiSrc/utils' import { numberWithSpaces } from 'uiSrc/utils/numbers' import { fireEvent, render, screen, within } from 'uiSrc/utils/test-utils' import AnalysisDataView, { Props } from './AnalysisDataView' -const mockedProps = mock() +jest.mock('uiSrc/telemetry', () => ({ + ...jest.requireActual('uiSrc/telemetry'), + sendEventTelemetry: jest.fn(), +})) +const mockedProps = mock() const mockReports = [ { id: MOCK_ANALYSIS_REPORT_DATA.id, @@ -20,6 +27,11 @@ const mockReports = [ } ] +const summaryContainerId = 'summary-per-data' +const analyticsTTLContainerId = 'analysis-ttl' +const topNameSpacesContainerId = 'top-namespaces' +const extrapolateResultsId = 'extrapolate-results' + describe('AnalysisDataView', () => { it('should render', () => { expect(render()).toBeTruthy() @@ -104,7 +116,7 @@ describe('AnalysisDataView', () => { ) - fireEvent.click(within(screen.getByTestId('summary-per-data')).getByTestId('extrapolate-results')) + fireEvent.click(within(screen.getByTestId(summaryContainerId)).getByTestId(extrapolateResultsId)) expect(screen.getByTestId('total-memory-value')).toHaveTextContent(`${formatBytes(mockedData.totalMemory.total, 3)}`) expect(screen.getByTestId('total-keys-value')).toHaveTextContent(`${numberWithSpaces(mockedData.totalKeys.total)}`) @@ -157,7 +169,7 @@ describe('AnalysisDataView', () => { render( ) - fireEvent.click(within(screen.getByTestId('analysis-ttl')).getByTestId('extrapolate-results')) + fireEvent.click(within(screen.getByTestId(analyticsTTLContainerId)).getByTestId(extrapolateResultsId)) const expirationGroup = mockedData.expirationGroups[1] @@ -198,7 +210,7 @@ describe('AnalysisDataView', () => { render( ) - fireEvent.click(within(screen.getByTestId('top-namespaces')).getByTestId('extrapolate-results')) + fireEvent.click(within(screen.getByTestId(topNameSpacesContainerId)).getByTestId(extrapolateResultsId)) const nspTopKeyItem = mockedData.topKeysNsp[0] expect(screen.getByTestId(`nsp-usedMemory-value=${nspTopKeyItem.memory}`)) @@ -221,7 +233,7 @@ describe('AnalysisDataView', () => { ) - expect(screen.queryByTestId('extrapolate-results')).not.toBeInTheDocument() + expect(screen.queryByTestId(extrapolateResultsId)).not.toBeInTheDocument() expect(screen.getByTestId('total-memory-value')).toHaveTextContent(`${formatBytes(mockedData.totalMemory.total, 3)}`) expect(screen.getByTestId('total-keys-value')).toHaveTextContent(`${numberWithSpaces(mockedData.totalKeys.total)}`) @@ -238,4 +250,45 @@ describe('AnalysisDataView', () => { expect(screen.getAllByTestId(`keys-value-${nspTopKeyItem.keys}`)[0]) .toHaveTextContent(`${numberWithSpaces(nspTopKeyItem.keys)}`) }) + + it('should call proper telemetry events after click extrapolation', () => { + const mockedData = { + ...MOCK_ANALYSIS_REPORT_DATA, + progress: { + total: 80, + scanned: 10000, + processed: 40 + } + } + const sendEventTelemetryMock = jest.fn() + sendEventTelemetry.mockImplementation(() => sendEventTelemetryMock) + + render( + + ) + + const clickAndCheckTelemetry = (el: HTMLInputElement, section: SectionName) => { + fireEvent.click(el) + expect(sendEventTelemetry).toBeCalledWith({ + event: TelemetryEvent.DATABASE_ANALYSIS_EXTRAPOLATION_CHANGED, + eventData: { + databaseId: INSTANCE_ID_MOCK, + from: !el.checked, + to: el.checked, + section + } + }) + sendEventTelemetry.mockRestore() + } + + [ + { id: summaryContainerId, section: SectionName.SUMMARY_PER_DATA }, + { id: analyticsTTLContainerId, section: SectionName.MEMORY_LIKELY_TO_BE_FREED }, + { id: topNameSpacesContainerId, section: SectionName.TOP_NAMESPACES }, + ].forEach(({ id, section }) => { + const extrapolateSwitch = within(screen.getByTestId(id)).getByTestId(extrapolateResultsId) + clickAndCheckTelemetry(extrapolateSwitch as HTMLInputElement, section) + clickAndCheckTelemetry(extrapolateSwitch as HTMLInputElement, section) + }) + }) }) diff --git a/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-data-view/AnalysisDataView.tsx b/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-data-view/AnalysisDataView.tsx index f5823a2e51..1c6e84cf7f 100644 --- a/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-data-view/AnalysisDataView.tsx +++ b/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-data-view/AnalysisDataView.tsx @@ -4,7 +4,7 @@ import { isNull } from 'lodash' import { useParams } from 'react-router-dom' import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' import { Nullable } from 'uiSrc/utils' -import { DEFAULT_EXTRAPOLATION, EmptyMessage } from 'uiSrc/pages/databaseAnalysis/constants' +import { DEFAULT_EXTRAPOLATION, EmptyMessage, SectionName } from 'uiSrc/pages/databaseAnalysis/constants' import { TopKeys, EmptyAnalysisMessage, @@ -34,13 +34,14 @@ const AnalysisDataView = (props: Props) => { } }, [data]) - const onSwitchExtrapolation = (value: boolean) => { + const onSwitchExtrapolation = (value: boolean, section: SectionName) => { sendEventTelemetry({ - event: TelemetryEvent.MEMORY_ANALYSIS_EXTRAPOLATION_CHANGED, + event: TelemetryEvent.DATABASE_ANALYSIS_EXTRAPOLATION_CHANGED, eventData: { databaseId: instanceId, from: !value, - to: value + to: value, + section } }) } diff --git a/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-ttl-view/ExpirationGroupsView.tsx b/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-ttl-view/ExpirationGroupsView.tsx index b7f31be235..054f1baa00 100644 --- a/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-ttl-view/ExpirationGroupsView.tsx +++ b/redisinsight/ui/src/pages/databaseAnalysis/components/analysis-ttl-view/ExpirationGroupsView.tsx @@ -1,29 +1,31 @@ -import React, { useEffect, useState } from 'react' import { EuiSwitch, EuiTitle } from '@elastic/eui' -import AutoSizer from 'react-virtualized-auto-sizer' -import { useDispatch, useSelector } from 'react-redux' -import cx from 'classnames' -import { DEFAULT_EXTRAPOLATION } from 'uiSrc/pages/databaseAnalysis' -import { extrapolate, formatBytes, formatExtrapolation, Nullable } from 'uiSrc/utils' +import cx from 'classnames' +import React, { useEffect, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import AutoSizer from 'react-virtualized-auto-sizer' import { AreaChart } from 'uiSrc/components/charts' import { AreaChartData, AreaChartDataType, DEFAULT_MULTIPLIER_GRID } from 'uiSrc/components/charts/area-chart/AreaChart' -import { DBAnalysisReportsSelector, setShowNoExpiryGroup } from 'uiSrc/slices/analytics/dbAnalysis' + +import { DEFAULT_EXTRAPOLATION, SectionName } from 'uiSrc/pages/databaseAnalysis' +import { dbAnalysisReportsSelector, setShowNoExpiryGroup } from 'uiSrc/slices/analytics/dbAnalysis' +import { extrapolate, formatBytes, formatExtrapolation, Nullable } from 'uiSrc/utils' import { DatabaseAnalysis } from 'apiSrc/modules/database-analysis/models' + import styles from './styles.module.scss' export interface Props { data: Nullable loading: boolean extrapolation: number - onSwitchExtrapolation?: (value: boolean) => void + onSwitchExtrapolation?: (value: boolean, section: SectionName) => void } const ExpirationGroupsView = (props: Props) => { const { data, loading, extrapolation, onSwitchExtrapolation } = props const { totalMemory, totalKeys } = data || {} - const { showNoExpiryGroup } = useSelector(DBAnalysisReportsSelector) + const { showNoExpiryGroup } = useSelector(dbAnalysisReportsSelector) const [expirationGroups, setExpirationGroups] = useState([]) const [isExtrapolated, setIsExtrapolated] = useState(true) @@ -90,7 +92,7 @@ const ExpirationGroupsView = (props: Props) => { checked={isExtrapolated} onChange={(e) => { setIsExtrapolated(e.target.checked) - onSwitchExtrapolation?.(e.target.checked) + onSwitchExtrapolation?.(e.target.checked, SectionName.MEMORY_LIKELY_TO_BE_FREED) }} data-testid="extrapolate-results" /> diff --git a/redisinsight/ui/src/pages/databaseAnalysis/components/header/Header.spec.tsx b/redisinsight/ui/src/pages/databaseAnalysis/components/header/Header.spec.tsx index 493aad8af3..b0daae85d2 100644 --- a/redisinsight/ui/src/pages/databaseAnalysis/components/header/Header.spec.tsx +++ b/redisinsight/ui/src/pages/databaseAnalysis/components/header/Header.spec.tsx @@ -74,7 +74,7 @@ describe('DatabaseAnalysisHeader', () => { fireEvent.click(screen.getByTestId('start-database-analysis-btn')) expect(sendEventTelemetry).toBeCalledWith({ - event: TelemetryEvent.MEMORY_ANALYSIS_STARTED, + event: TelemetryEvent.DATABASE_ANALYSIS_STARTED, eventData: { databaseId: INSTANCE_ID_MOCK, } diff --git a/redisinsight/ui/src/pages/databaseAnalysis/components/header/Header.tsx b/redisinsight/ui/src/pages/databaseAnalysis/components/header/Header.tsx index e4007adfdf..19ccdbcf1d 100644 --- a/redisinsight/ui/src/pages/databaseAnalysis/components/header/Header.tsx +++ b/redisinsight/ui/src/pages/databaseAnalysis/components/header/Header.tsx @@ -69,7 +69,7 @@ const Header = (props: Props) => { const handleClick = () => { sendEventTelemetry({ - event: TelemetryEvent.MEMORY_ANALYSIS_STARTED, + event: TelemetryEvent.DATABASE_ANALYSIS_STARTED, eventData: { databaseId: instanceId, } diff --git a/redisinsight/ui/src/pages/databaseAnalysis/components/summary-per-data/SummaryPerData.tsx b/redisinsight/ui/src/pages/databaseAnalysis/components/summary-per-data/SummaryPerData.tsx index 9f87476fe2..a579c90fbf 100644 --- a/redisinsight/ui/src/pages/databaseAnalysis/components/summary-per-data/SummaryPerData.tsx +++ b/redisinsight/ui/src/pages/databaseAnalysis/components/summary-per-data/SummaryPerData.tsx @@ -6,7 +6,7 @@ import { DonutChart } from 'uiSrc/components/charts' import { ChartData } from 'uiSrc/components/charts/donut-chart/DonutChart' import { KeyIconSvg, MemoryIconSvg } from 'uiSrc/components/database-overview/components/icons' import { GROUP_TYPES_COLORS, GroupTypesColors } from 'uiSrc/constants' -import { DEFAULT_EXTRAPOLATION } from 'uiSrc/pages/databaseAnalysis' +import { DEFAULT_EXTRAPOLATION, SectionName } from 'uiSrc/pages/databaseAnalysis' import { extrapolate, formatBytes, getGroupTypeDisplay, Nullable } from 'uiSrc/utils' import { getPercentage, numberWithSpaces } from 'uiSrc/utils/numbers' @@ -18,7 +18,7 @@ export interface Props { data: Nullable loading: boolean extrapolation?: number - onSwitchExtrapolation?: (value: boolean) => void + onSwitchExtrapolation?: (value: boolean, section: SectionName) => void } const widthResponsiveSize = 1024 @@ -132,7 +132,7 @@ const SummaryPerData = ({ data, loading, extrapolation, onSwitchExtrapolation }: checked={isExtrapolated} onChange={(e) => { setIsExtrapolated(e.target.checked) - onSwitchExtrapolation?.(e.target.checked) + onSwitchExtrapolation?.(e.target.checked, SectionName.SUMMARY_PER_DATA) }} data-testid="extrapolate-results" /> diff --git a/redisinsight/ui/src/pages/databaseAnalysis/components/top-namespace/TopNamespace.tsx b/redisinsight/ui/src/pages/databaseAnalysis/components/top-namespace/TopNamespace.tsx index 6375b26b60..19e5e2de19 100644 --- a/redisinsight/ui/src/pages/databaseAnalysis/components/top-namespace/TopNamespace.tsx +++ b/redisinsight/ui/src/pages/databaseAnalysis/components/top-namespace/TopNamespace.tsx @@ -1,11 +1,10 @@ -import React, { useEffect, useState } from 'react' -import cx from 'classnames' import { EuiButton, EuiSwitch, EuiTitle } from '@elastic/eui' -import { Nullable } from 'uiSrc/utils' -import { DEFAULT_EXTRAPOLATION, TableView } from 'uiSrc/pages/databaseAnalysis' +import cx from 'classnames' +import React, { useEffect, useState } from 'react' +import { DEFAULT_EXTRAPOLATION, SectionName, TableView } from 'uiSrc/pages/databaseAnalysis' import { TableLoader } from 'uiSrc/pages/databaseAnalysis/components' +import { Nullable } from 'uiSrc/utils' import { DatabaseAnalysis } from 'apiSrc/modules/database-analysis/models' - import Table from './Table' import styles from './styles.module.scss' @@ -13,7 +12,7 @@ export interface Props { data: Nullable loading: boolean extrapolation: number - onSwitchExtrapolation?: (value: boolean) => void + onSwitchExtrapolation?: (value: boolean, section: SectionName) => void } const TopNamespace = (props: Props) => { @@ -70,7 +69,7 @@ const TopNamespace = (props: Props) => { checked={isExtrapolated} onChange={(e) => { setIsExtrapolated(e.target.checked) - onSwitchExtrapolation?.(e.target.checked) + onSwitchExtrapolation?.(e.target.checked, SectionName.TOP_NAMESPACES) }} data-testid="extrapolate-results" /> diff --git a/redisinsight/ui/src/pages/databaseAnalysis/constants.ts b/redisinsight/ui/src/pages/databaseAnalysis/constants.ts index 48af53b199..d1f7f4ea16 100644 --- a/redisinsight/ui/src/pages/databaseAnalysis/constants.ts +++ b/redisinsight/ui/src/pages/databaseAnalysis/constants.ts @@ -17,3 +17,9 @@ export type Content = { } export const DEFAULT_EXTRAPOLATION = 1 + +export enum SectionName { + SUMMARY_PER_DATA = 'SUMMARY_PER_DATA', + MEMORY_LIKELY_TO_BE_FREED = 'MEMORY_LIKELY_TO_BE_FREED', + TOP_NAMESPACES = 'TOP_NAMESPACES', +} diff --git a/redisinsight/ui/src/slices/analytics/dbAnalysis.ts b/redisinsight/ui/src/slices/analytics/dbAnalysis.ts index c659f067c2..773983dd78 100644 --- a/redisinsight/ui/src/slices/analytics/dbAnalysis.ts +++ b/redisinsight/ui/src/slices/analytics/dbAnalysis.ts @@ -59,7 +59,7 @@ const databaseAnalysisSlice = createSlice({ }) export const dbAnalysisSelector = (state: RootState) => state.analytics.databaseAnalysis -export const DBAnalysisReportsSelector = (state: RootState) => state.analytics.databaseAnalysis.history +export const dbAnalysisReportsSelector = (state: RootState) => state.analytics.databaseAnalysis.history export const { setDatabaseAnalysisInitialState, diff --git a/redisinsight/ui/src/slices/tests/analytics/dbAnalysis.spec.ts b/redisinsight/ui/src/slices/tests/analytics/dbAnalysis.spec.ts index 63f8caca26..122f66975c 100644 --- a/redisinsight/ui/src/slices/tests/analytics/dbAnalysis.spec.ts +++ b/redisinsight/ui/src/slices/tests/analytics/dbAnalysis.spec.ts @@ -15,7 +15,7 @@ import reducer, { fetchDBAnalysisAction, createNewAnalysis, fetchDBAnalysisReportsHistory, - DBAnalysisReportsSelector, + dbAnalysisReportsSelector, dbAnalysisSelector, setShowNoExpiryGroup, } from 'uiSrc/slices/analytics/dbAnalysis' @@ -110,7 +110,7 @@ describe('db analysis slice', () => { const rootState = Object.assign(initialStateDefault, { analytics: { databaseAnalysis: nextState }, }) - expect(DBAnalysisReportsSelector(rootState)).toEqual(stateHistory) + expect(dbAnalysisReportsSelector(rootState)).toEqual(stateHistory) }) }) @@ -235,7 +235,7 @@ describe('db analysis slice', () => { const rootState = Object.assign(initialStateDefault, { analytics: { databaseAnalysis: nextState }, }) - expect(DBAnalysisReportsSelector(rootState)).toEqual(stateHistory) + expect(dbAnalysisReportsSelector(rootState)).toEqual(stateHistory) }) }) describe('setShowNoExpiryGroup', () => { @@ -254,7 +254,7 @@ describe('db analysis slice', () => { const rootState = Object.assign(initialStateDefault, { analytics: { databaseAnalysis: nextState }, }) - expect(DBAnalysisReportsSelector(rootState)).toEqual(stateHistory) + expect(dbAnalysisReportsSelector(rootState)).toEqual(stateHistory) }) }) }) diff --git a/redisinsight/ui/src/telemetry/events.ts b/redisinsight/ui/src/telemetry/events.ts index a7630f205c..96c66005ea 100644 --- a/redisinsight/ui/src/telemetry/events.ts +++ b/redisinsight/ui/src/telemetry/events.ts @@ -182,9 +182,9 @@ export enum TelemetryEvent { BULK_ACTIONS_WARNING = 'BULK_ACTIONS_WARNING', BULK_ACTIONS_CANCELLED = 'BULK_ACTIONS_CANCELLED', - MEMORY_ANALYSIS_STARTED = 'MEMORY_ANALYSIS_STARTED', - MEMORY_ANALYSIS_HISTORY_VIEWED = 'MEMORY_ANALYSIS_HISTORY_VIEWED', - MEMORY_ANALYSIS_EXTRAPOLATION_CHANGED = 'MEMORY_ANALYSIS_EXTRAPOLATION_CHANGED', + DATABASE_ANALYSIS_STARTED = 'DATABASE_ANALYSIS_STARTED', + DATABASE_ANALYSIS_HISTORY_VIEWED = 'DATABASE_ANALYSIS_HISTORY_VIEWED', + DATABASE_ANALYSIS_EXTRAPOLATION_CHANGED = 'DATABASE_ANALYSIS_EXTRAPOLATION_CHANGED', USER_SURVEY_LINK_CLICKED = 'USER_SURVEY_LINK_CLICKED', } diff --git a/redisinsight/ui/src/telemetry/pageViews.ts b/redisinsight/ui/src/telemetry/pageViews.ts index 8dd77e8455..6a60459319 100644 --- a/redisinsight/ui/src/telemetry/pageViews.ts +++ b/redisinsight/ui/src/telemetry/pageViews.ts @@ -7,5 +7,5 @@ export enum TelemetryPageView { SLOWLOG_PAGE = 'Slow Log', CLUSTER_DETAILS_PAGE = 'Overview', PUBSUB_PAGE = 'Pub/Sub', - DATABASE_ANALYSIS = 'Memory analysis' + DATABASE_ANALYSIS = 'Database Analysis' }