Skip to content

Commit 30560d7

Browse files
authored
RI-7218 Add telemetry collection for Vector Search (#4799)
- extract the common logic for the page view telemetry events in a custom hook - add page view telemetry for the vector search page re #RI-7218
1 parent bb18d5d commit 30560d7

File tree

5 files changed

+212
-0
lines changed

5 files changed

+212
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from 'react'
2+
import * as reactRedux from 'react-redux'
3+
import { cleanup, render } from 'uiSrc/utils/test-utils'
4+
import { TelemetryPageView } from 'uiSrc/telemetry/pageViews'
5+
import { sendPageViewTelemetry } from 'uiSrc/telemetry'
6+
import {
7+
INSTANCE_ID_MOCK,
8+
INSTANCES_MOCK,
9+
} from 'uiSrc/mocks/handlers/instances/instancesHandlers'
10+
import { VectorSearchPage } from './VectorSearchPage'
11+
12+
// Mock the telemetry module, so we don't send actual telemetry data during tests
13+
jest.mock('uiSrc/telemetry', () => ({
14+
...jest.requireActual('uiSrc/telemetry'),
15+
sendPageViewTelemetry: jest.fn(),
16+
}))
17+
18+
const renderVectorSearchPageComponent = () => render(<VectorSearchPage />)
19+
20+
describe('VectorSearchPage', () => {
21+
beforeEach(() => {
22+
cleanup()
23+
})
24+
25+
it('should render ', () => {
26+
const { container } = renderVectorSearchPageComponent()
27+
28+
expect(container).toBeTruthy()
29+
})
30+
31+
describe('Telemetry', () => {
32+
let mockUseSelector: jest.SpyInstance
33+
34+
beforeEach(() => {
35+
jest.clearAllMocks()
36+
37+
mockUseSelector = jest.spyOn(reactRedux, 'useSelector')
38+
mockUseSelector.mockReturnValue(INSTANCES_MOCK[0])
39+
})
40+
41+
afterEach(() => {
42+
jest.restoreAllMocks()
43+
})
44+
45+
it('should send page view telemetry on mount', () => {
46+
renderVectorSearchPageComponent()
47+
48+
expect(sendPageViewTelemetry).toHaveBeenCalledTimes(1)
49+
expect(sendPageViewTelemetry).toHaveBeenCalledWith({
50+
name: TelemetryPageView.VECTOR_SEARCH_PAGE,
51+
eventData: { databaseId: INSTANCE_ID_MOCK },
52+
})
53+
})
54+
})
55+
})

redisinsight/ui/src/pages/vector-search/VectorSearchPage.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
import React from 'react'
2+
import { TelemetryPageView } from 'uiSrc/telemetry'
3+
import { usePageViewTelemetry } from 'uiSrc/telemetry/usePageViewTelemetry'
24

35
import { VectorSearchCreateIndex } from './create-index/VectorSearchCreateIndex'
46
import { VectorSearchQuery } from './query/VectorSearchQuery'
57

68
export const VectorSearchPage = () => {
79
const hasIndexes = true
810

11+
usePageViewTelemetry({
12+
page: TelemetryPageView.VECTOR_SEARCH_PAGE,
13+
})
14+
15+
// TODO: Set title, once we know the name of the page
16+
// setTitle(
17+
// `${formatLongName(connectedInstanceName, 33, 0, '...')} ${getDbIndex(db)} - Vector Search`,
18+
// )
19+
920
if (!hasIndexes) {
1021
return <VectorSearchCreateIndex />
1122
}

redisinsight/ui/src/telemetry/pageViews.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ export enum TelemetryPageView {
1313
RDI_CONFIG = 'RDI Configuration',
1414
RDI_JOBS = 'RDI Jobs',
1515
RDI_STATUS = 'RDI Status',
16+
VECTOR_SEARCH_PAGE = 'Vector Search',
1617
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { renderHook, act, cleanup } from '@testing-library/react-hooks'
2+
import * as reactRedux from 'react-redux'
3+
import { faker } from '@faker-js/faker'
4+
import { cloneDeep } from 'lodash'
5+
6+
import { mockedStore } from 'uiSrc/utils/test-utils'
7+
import { sendPageViewTelemetry } from 'uiSrc/telemetry'
8+
import {
9+
INSTANCE_ID_MOCK,
10+
INSTANCES_MOCK,
11+
} from 'uiSrc/mocks/handlers/instances/instancesHandlers'
12+
13+
import { usePageViewTelemetry } from '../usePageViewTelemetry'
14+
import { TelemetryPageView } from '../pageViews'
15+
16+
// Mock the telemetry module, so we don't send actual telemetry data during tests
17+
jest.mock('uiSrc/telemetry', () => ({
18+
...jest.requireActual('uiSrc/telemetry'),
19+
sendPageViewTelemetry: jest.fn(),
20+
}))
21+
22+
describe('usePageViewTelemetry', () => {
23+
let store: typeof mockedStore
24+
let mockUseSelector: jest.SpyInstance
25+
26+
const mockPage = faker.helpers.enumValue(TelemetryPageView)
27+
28+
beforeEach(() => {
29+
jest.clearAllMocks()
30+
31+
cleanup()
32+
store = cloneDeep(mockedStore)
33+
store.clearActions()
34+
35+
mockUseSelector = jest.spyOn(reactRedux, 'useSelector')
36+
mockUseSelector.mockReturnValue(INSTANCES_MOCK[0])
37+
})
38+
39+
afterEach(() => {
40+
jest.restoreAllMocks()
41+
})
42+
43+
it('should send page view telemetry on mount if connected to instance', () => {
44+
renderHook(() => usePageViewTelemetry({ page: mockPage }))
45+
46+
expect(sendPageViewTelemetry).toHaveBeenCalledTimes(1)
47+
expect(sendPageViewTelemetry).toHaveBeenCalledWith({
48+
name: mockPage,
49+
eventData: { databaseId: INSTANCE_ID_MOCK },
50+
})
51+
})
52+
53+
it('should not send page view telemetry if instanceId is not available', () => {
54+
mockUseSelector.mockReturnValueOnce(null)
55+
56+
renderHook(() => usePageViewTelemetry({ page: mockPage }))
57+
58+
expect(sendPageViewTelemetry).not.toHaveBeenCalled()
59+
})
60+
61+
it('should not send page view telemetry if already sent', () => {
62+
const { rerender } = renderHook(() =>
63+
usePageViewTelemetry({ page: mockPage }),
64+
)
65+
66+
// Verify initial telemetry call
67+
expect(sendPageViewTelemetry).toHaveBeenCalledTimes(1)
68+
69+
// Simulate instance id change in the selector
70+
mockUseSelector.mockReturnValue(INSTANCES_MOCK[1])
71+
rerender()
72+
73+
// Should not send telemetry again
74+
expect(sendPageViewTelemetry).toHaveBeenCalledTimes(1)
75+
})
76+
77+
it('should send page view telemetry with when called manually', () => {
78+
const { result } = renderHook(() =>
79+
usePageViewTelemetry({ page: mockPage }),
80+
)
81+
82+
// Verify initial telemetry call
83+
expect(sendPageViewTelemetry).toHaveBeenCalledTimes(1)
84+
expect(sendPageViewTelemetry).toHaveBeenCalledWith({
85+
name: mockPage,
86+
eventData: { databaseId: INSTANCE_ID_MOCK },
87+
})
88+
89+
// Call the sendPageView method manually, with custom parameters
90+
const customPage = faker.helpers.enumValue(TelemetryPageView)
91+
const customInstanceId = 'custom-instance-1'
92+
93+
act(() => {
94+
result.current.sendPageView(customPage, customInstanceId)
95+
})
96+
97+
// Verify that the telemetry was sent with the custom parameters
98+
expect(sendPageViewTelemetry).toHaveBeenCalledTimes(2)
99+
expect(sendPageViewTelemetry).toHaveBeenCalledWith({
100+
name: customPage,
101+
eventData: { databaseId: customInstanceId },
102+
})
103+
})
104+
})
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useEffect, useState } from 'react'
2+
import { useSelector } from 'react-redux'
3+
4+
import { sendPageViewTelemetry, TelemetryPageView } from 'uiSrc/telemetry'
5+
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
6+
7+
interface PageViewTelemetryProps {
8+
page: TelemetryPageView
9+
}
10+
11+
interface PageViewTelemetryHook {
12+
sendPageView: (page: TelemetryPageView, instanceId: string) => void // Let the developers manually send page view telemetry, with custom parameters when needed
13+
}
14+
15+
export const usePageViewTelemetry = ({
16+
page,
17+
}: PageViewTelemetryProps): PageViewTelemetryHook => {
18+
const [isPageViewSent, setIsPageViewSent] = useState(false)
19+
const { id: instanceId } = useSelector(connectedInstanceSelector)
20+
21+
// By default, send page view telemetry on page mount if instanceId is available
22+
useEffect(() => {
23+
if (instanceId && !isPageViewSent) {
24+
sendPageView(page, instanceId)
25+
}
26+
}, [instanceId, isPageViewSent])
27+
28+
const sendPageView = (page: TelemetryPageView, instanceId: string) => {
29+
sendPageViewTelemetry({
30+
name: page,
31+
eventData: {
32+
databaseId: instanceId,
33+
},
34+
})
35+
setIsPageViewSent(true)
36+
}
37+
38+
return {
39+
sendPageView,
40+
}
41+
}

0 commit comments

Comments
 (0)