diff --git a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx index ce2450ee61..dcf8f187e2 100644 --- a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx +++ b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx @@ -41,6 +41,7 @@ import { import {useChangedQuerySettings} from '../../../../utils/hooks/useChangedQuerySettings'; import {useLastQueryExecutionSettings} from '../../../../utils/hooks/useLastQueryExecutionSettings'; import {DEFAULT_QUERY_SETTINGS, QUERY_ACTIONS, QUERY_MODES} from '../../../../utils/query'; +import {reachMetricaGoal} from '../../../../utils/yaMetrica'; import {useCurrentSchema} from '../../TenantContext'; import type {InitialPaneState} from '../../utils/paneVisibilityToggleHelpers'; import { @@ -148,6 +149,11 @@ export default function QueryEditor(props: QueryEditorProps) { queryManagerInstance.abortQuery(); if (isStreamingEnabled) { + reachMetricaGoal('runQuery', { + actionType: 'execute', + isStreaming: true, + ...querySettings, + }); const query = streamQuery({ actionType: 'execute', query: text, @@ -158,6 +164,7 @@ export default function QueryEditor(props: QueryEditorProps) { queryManagerInstance.registerQuery(query); } else { + reachMetricaGoal('runQuery', {actionType: 'execute', ...querySettings}); const query = sendQuery({ actionType: 'execute', query: text, @@ -196,6 +203,8 @@ export default function QueryEditor(props: QueryEditorProps) { const queryId = uuidv4(); + reachMetricaGoal('runQuery', {actionType: 'explain', ...querySettings}); + const query = sendQuery({ actionType: 'explain', query: text, diff --git a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx index 001d3431bc..fb26ab0bfe 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +++ b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx @@ -6,6 +6,7 @@ import type {QueryAction} from '../../../../types/store/query'; import {cn} from '../../../../utils/cn'; import createToast from '../../../../utils/createToast'; import {useTypedSelector} from '../../../../utils/hooks'; +import {reachMetricaGoal} from '../../../../utils/yaMetrica'; import {NewSQL} from '../NewSQL/NewSQL'; import {queryManagerInstance} from '../QueryEditor/helpers'; import {SaveQuery} from '../SaveQuery/SaveQuery'; @@ -90,6 +91,7 @@ export const QueryEditorControls = ({ const [cancelQueryError, setCancelQueryError] = React.useState(false); const onStopButtonClick = React.useCallback(async () => { + reachMetricaGoal('stopQuery'); try { if (isStreamingEnabled) { queryManagerInstance.abortQuery(); diff --git a/src/uiFactory/types.ts b/src/uiFactory/types.ts index ed04f72635..d66a79424e 100644 --- a/src/uiFactory/types.ts +++ b/src/uiFactory/types.ts @@ -16,7 +16,7 @@ import type {ETenantType} from '../types/api/tenant'; import type {GetLogsLink} from '../utils/logs'; import type {GetMonitoringClusterLink, GetMonitoringLink} from '../utils/monitoring'; -export interface UIFactory { +export interface UIFactory { onCreateDB?: HandleCreateDB; onEditDB?: HandleEditDB; onDeleteDB?: HandleDeleteDB; @@ -46,11 +46,16 @@ export interface UIFactory { }; hasAccess?: boolean; hideGrantAccess?: boolean; - yaMetricaMap?: Record; useDatabaseId?: boolean; useMetaProxy?: boolean; + + yaMetricaConfig?: { + yaMetricaMap: Record; + goals: UiMetricaGoals; + getMetricaName: (goalKey: UiMetricaGoal) => T; + }; } export type HandleCreateDB = (params: {clusterName: string}) => Promise; @@ -97,3 +102,9 @@ export type RenderMonitoring = (props: { additionalTenantProps?: AdditionalTenantsProps; scrollContainerRef: React.RefObject; }) => React.ReactNode; +export interface UiMetricaGoals { + runQuery?: string; + stopQuery?: string; +} + +export type UiMetricaGoal = keyof UiMetricaGoals; diff --git a/src/uiFactory/uiFactory.ts b/src/uiFactory/uiFactory.ts index af1efdd9ca..521b7e4f8c 100644 --- a/src/uiFactory/uiFactory.ts +++ b/src/uiFactory/uiFactory.ts @@ -22,7 +22,9 @@ const uiFactoryBase: UIFactory = { useDatabaseId: false, }; -export function configureUIFactory(overrides: Partial>) { +export function configureUIFactory( + overrides: Partial>, +) { Object.assign(uiFactoryBase, overrides); } diff --git a/src/utils/yaMetrica.ts b/src/utils/yaMetrica.ts index 2b8f37b125..535e4a1543 100644 --- a/src/utils/yaMetrica.ts +++ b/src/utils/yaMetrica.ts @@ -1,3 +1,4 @@ +import type {UiMetricaGoal} from '../uiFactory/types'; import {uiFactory} from '../uiFactory/uiFactory'; /** @@ -14,7 +15,7 @@ export interface Counter { reachGoal: (...args: unknown[]) => void; } -const yaMetricaMap = uiFactory.yaMetricaMap; +const yaMetricaMap = uiFactory.yaMetricaConfig?.yaMetricaMap; /** * A fake implementation of a counter metric for Yandex.Metrica. @@ -62,7 +63,10 @@ class FakeMetrica implements Counter { * @param name The name of the metrica to retrieve * @returns The Yandex Metrica instance if found, otherwise a FakeMetrica instance */ -export function getMetrica(name: string) { +export function getMetrica(name?: string) { + if (!name) { + return undefined; + } const yaMetricaId = yaMetricaMap?.[name]; const metricaInstance = yaMetricaId ? (window[`yaCounter${yaMetricaId}`] as Counter) @@ -70,3 +74,21 @@ export function getMetrica(name: string) { return metricaInstance ?? new FakeMetrica(name); } + +export function reachMetricaGoal( + goalKey: UiMetricaGoal, + params?: Record, +) { + const metricaName = uiFactory.yaMetricaConfig?.getMetricaName(goalKey); + const metrica = getMetrica(metricaName); + if (!metrica) { + console.warn('Metrica is not defined'); + return; + } + const goal = uiFactory.yaMetricaConfig?.goals[goalKey]; + if (!goal) { + console.warn('Metrica goal is not defined'); + return; + } + metrica.reachGoal(goal, params); +}