Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import { handlePasteHostName } from 'uiSrc/utils'
import { APPLICATION_NAME, PageNames, Pages } from 'uiSrc/constants'
import { useResizableFormField } from 'uiSrc/services'
import validationErrors from 'uiSrc/constants/validationErrors'
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
import { sendEventTelemetry, TelemetryEvent, getRedisModulesSummary } from 'uiSrc/telemetry'
import { resetKeys } from 'uiSrc/slices/browser/keys'
import { appContextSelector, setAppContextInitialState } from 'uiSrc/slices/app/context'
import DatabaseAlias from 'uiSrc/pages/home/components/DatabaseAlias'
Expand Down Expand Up @@ -363,12 +363,13 @@ const AddStandaloneForm = (props: Props) => {
}

const handleCheckConnectToInstance = () => {
const modulesSummary = getRedisModulesSummary(modules)
sendEventTelemetry({
event: TelemetryEvent.CONFIG_DATABASES_OPEN_DATABASE_BUTTON_CLICKED,
eventData: {
databaseId: id,
provider,
modules,
...modulesSummary,
}
})
dispatch(checkConnectToInstanceAction(id, connectToInstance))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
} from 'uiSrc/slices/interfaces'
import { resetKeys } from 'uiSrc/slices/browser/keys'
import { PageNames, Pages, Theme } from 'uiSrc/constants'
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
import { sendEventTelemetry, TelemetryEvent, getRedisModulesSummary } from 'uiSrc/telemetry'
import { ThemeContext } from 'uiSrc/contexts/themeContext'
import { formatLongName, getDbIndex, lastConnectionFormat, Nullable, replaceSpaces } from 'uiSrc/utils'
import { appContextSelector, setAppContextInitialState } from 'uiSrc/slices/app/context'
Expand Down Expand Up @@ -125,12 +125,13 @@ const DatabasesListWrapper = ({
{ id, provider, modules }: Instance
) => {
event.preventDefault()
const modulesSummary = getRedisModulesSummary(modules)
sendEventTelemetry({
event: TelemetryEvent.CONFIG_DATABASES_OPEN_DATABASE,
eventData: {
databaseId: id,
provider,
modules,
...modulesSummary,
}
})
dispatch(checkConnectToInstanceAction(id, connectToInstance))
Expand Down
20 changes: 20 additions & 0 deletions redisinsight/ui/src/telemetry/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RedisModuleDto } from 'apiSrc/modules/instances/dto/database-instance.dto'
import { TelemetryEvent } from './events'

export interface ITelemetryIdentify {
Expand Down Expand Up @@ -34,3 +35,22 @@ export enum MatchType {
EXACT_VALUE_NAME = 'EXACT_VALUE_NAME',
PATTERN = 'PATTERN'
}

export enum RedisModules {
RedisAI = 'ai',
RedisGraph = 'graph',
RedisGears = 'rg',
RedisBloom = 'bf',
RedisJSON = 'ReJSON',
RediSearch = 'search',
RedisTimeSeries = 'timeseries',
}

interface IModuleSummary {
loaded: boolean;
version?: number;
semanticVersion?: number;
}
export interface IRedisModulesSummary extends Record<keyof typeof RedisModules, IModuleSummary> {
customModules: RedisModuleDto[]
}
71 changes: 71 additions & 0 deletions redisinsight/ui/src/telemetry/telemetryUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { getRedisModulesSummary } from './telemetryUtils'

const DEFAULT_SUMMARY = Object.freeze(
{
RediSearch: { loaded: false },
RedisAI: { loaded: false },
RedisGraph: { loaded: false },
RedisGears: { loaded: false },
RedisBloom: { loaded: false },
RedisJSON: { loaded: false },
RedisTimeSeries: { loaded: false },
customModules: [],
},
)

const getRedisModulesSummaryTests = [
{
input: [{ name: 'ai', version: 20000 }],
expected: { ...DEFAULT_SUMMARY, RedisAI: { loaded: true, version: 20000 }, customModules: [] },
},
{
input: [{ name: 'search', version: 10000 }],
expected: { ...DEFAULT_SUMMARY, RediSearch: { loaded: true, version: 10000 } },
},
{
input: [{ name: 'bf', version: 1000 }, { name: 'rediSQL', version: 1 }],
expected: {
...DEFAULT_SUMMARY,
RedisBloom: { loaded: true, version: 1000 },
customModules: [{ name: 'rediSQL', version: 1 }],
},
},
{
input: [{ name: 'ReJSON' }],
expected: { ...DEFAULT_SUMMARY, RedisJSON: { loaded: true } },
},
{
input: [
{ name: 'ai', version: 10000, semanticVersion: '1.0.0' },
{ name: 'graph', version: 20000, semanticVersion: '2.0.0' },
{ name: 'rg', version: 10000, semanticVersion: '1.0.0' },
{ name: 'bf' },
{ name: 'ReJSON', version: 10000, semanticVersion: '1.0.0' },
{ name: 'search', version: 10000, semanticVersion: '1.0.0' },
{ name: 'timeseries', version: 10000, semanticVersion: '1.0.0' },
],
expected: {
RedisAI: { loaded: true, version: 10000, semanticVersion: '1.0.0' },
RedisGraph: { loaded: true, version: 20000, semanticVersion: '2.0.0' },
RedisGears: { loaded: true, version: 10000, semanticVersion: '1.0.0' },
RedisBloom: { loaded: true },
RedisJSON: { loaded: true, version: 10000, semanticVersion: '1.0.0' },
RediSearch: { loaded: true, version: 10000, semanticVersion: '1.0.0' },
RedisTimeSeries: { loaded: true, version: 10000, semanticVersion: '1.0.0' },
customModules: [],
},
},
{ input: [], expected: DEFAULT_SUMMARY },
{ input: {}, expected: DEFAULT_SUMMARY },
{ input: undefined, expected: DEFAULT_SUMMARY },
{ input: null, expected: DEFAULT_SUMMARY },
{ input: 1, expected: DEFAULT_SUMMARY },
]

describe('getRedisModulesSummary', () => {
test.each(getRedisModulesSummaryTests)('%j', ({ input, expected }) => {
// @ts-ignore
const result = getRedisModulesSummary(input)
expect(result).toEqual(expected)
})
})
64 changes: 62 additions & 2 deletions redisinsight/ui/src/telemetry/telemetryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,23 @@
* This module abstracts the exact service/framework used for tracking usage.
*/
import isGlob from 'is-glob'
import { cloneDeep } from 'lodash'
import * as jsonpath from 'jsonpath'
import { Nullable } from 'uiSrc/utils'
import { localStorageService } from 'uiSrc/services'
import { ApiEndpoints, BrowserStorageItem, KeyTypes, StreamViews } from 'uiSrc/constants'
import { KeyViewType } from 'uiSrc/slices/interfaces/keys'
import { StreamViewType } from 'uiSrc/slices/interfaces/stream'
import { checkIsAnalyticsGranted, getAppType } from 'uiSrc/telemetry/checkAnalytics'
import { ITelemetrySendEvent, ITelemetrySendPageView, ITelemetryService, MatchType } from './interfaces'
import { RedisModuleDto } from 'apiSrc/modules/instances/dto/database-instance.dto'
import {
ITelemetrySendEvent,
ITelemetrySendPageView,
ITelemetryService,
IRedisModulesSummary,
MatchType,
RedisModules,
} from './interfaces'
import { TelemetryEvent } from './events'
import { NON_TRACKING_ANONYMOUS_ID, SegmentTelemetryService } from './segment'

Expand Down Expand Up @@ -179,6 +188,56 @@ export const getRefreshEventData = (eventData: any, type: string, streamViewType
return eventData
}

const SUPPORTED_REDIS_MODULES = Object.freeze({
ai: RedisModules.RedisAI,
graph: RedisModules.RedisGraph,
rg: RedisModules.RedisGears,
bf: RedisModules.RedisBloom,
ReJSON: RedisModules.RedisJSON,
search: RedisModules.RediSearch,
timeseries: RedisModules.RedisTimeSeries,
})

const DEFAULT_SUMMARY: IRedisModulesSummary = Object.freeze(
{
RediSearch: { loaded: false },
RedisAI: { loaded: false },
RedisGraph: { loaded: false },
RedisGears: { loaded: false },
RedisBloom: { loaded: false },
RedisJSON: { loaded: false },
RedisTimeSeries: { loaded: false },
customModules: [],
},
)

const getEnumKeyBValue = (myEnum: any, enumValue: number | string): string => {
const keys = Object.keys(myEnum)
const index = keys.findIndex((x) => myEnum[x] === enumValue)
return index > -1 ? keys[index] : ''
}

const getRedisModulesSummary = (modules: RedisModuleDto[] = []): IRedisModulesSummary => {
const summary = cloneDeep(DEFAULT_SUMMARY)
try {
modules.forEach(((module) => {
if (SUPPORTED_REDIS_MODULES[module.name]) {
const moduleName = getEnumKeyBValue(RedisModules, module.name)
summary[moduleName] = {
loaded: true,
version: module.version,
semanticVersion: module.semanticVersion,
}
} else {
summary.customModules.push(module)
}
}))
} catch (e) {
// continue regardless of error
}
return summary
}

export {
getTelemetryService,
sendEventTelemetry,
Expand All @@ -187,5 +246,6 @@ export {
getBasedOnViewTypeEvent,
getJsonPathLevel,
getAdditionalAddedEventData,
getMatchType
getMatchType,
getRedisModulesSummary
}