From f18cd13f2b6b634bfe4f9fc1857bfe31075a605c Mon Sep 17 00:00:00 2001 From: Gareth Dawson Date: Tue, 5 Sep 2023 12:45:41 +0100 Subject: [PATCH] SQL: Add feature tracking to sql datasources (#73996) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add sql ds feature tracking * feature tracking * remove unused imports * Update public/app/features/plugins/sql/components/QueryHeader.tsx Co-authored-by: Gábor Farkas * suggestion * add datasource to report interaction * report editor collapse * pass ds on preview copy * update missing instance checks * add confirm modal data to report --------- Co-authored-by: Gábor Farkas --- .../plugins/sql/components/QueryHeader.tsx | 99 +++++++++++++++---- .../query-editor-raw/QueryToolbox.tsx | 22 ++++- .../components/query-editor-raw/RawEditor.tsx | 5 + .../visual-query-builder/Preview.tsx | 13 ++- .../visual-query-builder/VisualEditor.tsx | 2 +- .../plugins/sql/datasource/SqlDatasource.ts | 10 ++ 6 files changed, 129 insertions(+), 22 deletions(-) diff --git a/public/app/features/plugins/sql/components/QueryHeader.tsx b/public/app/features/plugins/sql/components/QueryHeader.tsx index 87471504564..37869dbe1a5 100644 --- a/public/app/features/plugins/sql/components/QueryHeader.tsx +++ b/public/app/features/plugins/sql/components/QueryHeader.tsx @@ -3,6 +3,7 @@ import { useCopyToClipboard } from 'react-use'; import { SelectableValue } from '@grafana/data'; import { EditorField, EditorHeader, EditorMode, EditorRow, FlexItem, InlineSelect, Space } from '@grafana/experimental'; +import { reportInteraction } from '@grafana/runtime'; import { Button, InlineSwitch, RadioButtonGroup, Tooltip } from '@grafana/ui'; import { QueryWithDefaults } from '../defaults'; @@ -48,6 +49,13 @@ export function QueryHeader({ const onEditorModeChange = useCallback( (newEditorMode: EditorMode) => { + if (newEditorMode === EditorMode.Code) { + reportInteraction('grafana_sql_editor_mode_changed', { + datasource: query.datasource?.type, + selectedEditorMode: EditorMode.Code, + }); + } + if (editorMode === EditorMode.Code) { setShowConfirm(true); return; @@ -59,6 +67,11 @@ export function QueryHeader({ const onFormatChange = (e: SelectableValue) => { const next = { ...query, format: e.value !== undefined ? e.value : QueryFormat.Table }; + + reportInteraction('grafana_sql_format_changed', { + datasource: query.datasource?.type, + selectedFormat: next.format, + }); onChange(next); }; @@ -123,10 +136,18 @@ export function QueryHeader({ transparent={true} showLabel={true} value={queryRowFilter.filter} - onChange={(ev) => - ev.target instanceof HTMLInputElement && - onQueryRowChange({ ...queryRowFilter, filter: ev.target.checked }) - } + onChange={(ev) => { + if (!(ev.target instanceof HTMLInputElement)) { + return; + } + + reportInteraction('grafana_sql_filter_toggled', { + datasource: query.datasource?.type, + displayed: ev.target.checked, + }); + + onQueryRowChange({ ...queryRowFilter, filter: ev.target.checked }); + }} /> - ev.target instanceof HTMLInputElement && - onQueryRowChange({ ...queryRowFilter, group: ev.target.checked }) - } + onChange={(ev) => { + if (!(ev.target instanceof HTMLInputElement)) { + return; + } + + reportInteraction('grafana_sql_group_toggled', { + datasource: query.datasource?.type, + displayed: ev.target.checked, + }); + + onQueryRowChange({ ...queryRowFilter, group: ev.target.checked }); + }} /> - ev.target instanceof HTMLInputElement && - onQueryRowChange({ ...queryRowFilter, order: ev.target.checked }) - } + onChange={(ev) => { + if (!(ev.target instanceof HTMLInputElement)) { + return; + } + + reportInteraction('grafana_sql_order_toggled', { + datasource: query.datasource?.type, + displayed: ev.target.checked, + }); + + onQueryRowChange({ ...queryRowFilter, order: ev.target.checked }); + }} /> - ev.target instanceof HTMLInputElement && - onQueryRowChange({ ...queryRowFilter, preview: ev.target.checked }) - } + onChange={(ev) => { + if (!(ev.target instanceof HTMLInputElement)) { + return; + } + + reportInteraction('grafana_sql_preview_toggled', { + datasource: query.datasource?.type, + displayed: ev.target.checked, + }); + + onQueryRowChange({ ...queryRowFilter, preview: ev.target.checked }); + }} /> )} @@ -195,6 +240,12 @@ export function QueryHeader({ { + reportInteraction('grafana_sql_editor_mode_changed', { + datasource: query.datasource?.type, + selectedEditorMode: EditorMode.Builder, + type: 'copy', + }); + setShowConfirm(false); copyToClipboard(query.rawSql!); onChange({ @@ -204,6 +255,12 @@ export function QueryHeader({ }); }} onDiscard={() => { + reportInteraction('grafana_sql_editor_mode_changed', { + datasource: query.datasource?.type, + selectedEditorMode: EditorMode.Builder, + type: 'discard', + }); + setShowConfirm(false); onChange({ ...query, @@ -211,7 +268,15 @@ export function QueryHeader({ editorMode: EditorMode.Builder, }); }} - onCancel={() => setShowConfirm(false)} + onCancel={() => { + reportInteraction('grafana_sql_editor_mode_changed', { + datasource: query.datasource?.type, + selectedEditorMode: EditorMode.Builder, + type: 'cancel', + }); + + setShowConfirm(false); + }} /> diff --git a/public/app/features/plugins/sql/components/query-editor-raw/QueryToolbox.tsx b/public/app/features/plugins/sql/components/query-editor-raw/QueryToolbox.tsx index 3a8f26689a0..a8817e102f6 100644 --- a/public/app/features/plugins/sql/components/query-editor-raw/QueryToolbox.tsx +++ b/public/app/features/plugins/sql/components/query-editor-raw/QueryToolbox.tsx @@ -1,6 +1,7 @@ import { css } from '@emotion/css'; import React, { useMemo, useState } from 'react'; +import { reportInteraction } from '@grafana/runtime'; import { HorizontalGroup, Icon, IconButton, Tooltip, useTheme2 } from '@grafana/ui'; import { QueryValidator, QueryValidatorProps } from './QueryValidator'; @@ -70,11 +71,28 @@ export function QueryToolbox({ showTools, onFormatCode, onExpand, isExpanded, ..
{onFormatCode && ( - + { + reportInteraction('grafana_sql_query_formatted', { + datasource: validatorProps.query.datasource?.type, + }); + onFormatCode(); + }} + name="brackets-curly" + size="xs" + tooltip="Format query" + /> )} {onExpand && ( onExpand(!isExpanded)} + onClick={() => { + reportInteraction('grafana_sql_editor_expand', { + datasource: validatorProps.query.datasource?.type, + expanded: !isExpanded, + }); + + onExpand(!isExpanded); + }} name={isExpanded ? 'angle-up' : 'angle-down'} size="xs" tooltip={isExpanded ? 'Collapse editor' : 'Expand editor'} diff --git a/public/app/features/plugins/sql/components/query-editor-raw/RawEditor.tsx b/public/app/features/plugins/sql/components/query-editor-raw/RawEditor.tsx index 9844a934cdb..19215827b74 100644 --- a/public/app/features/plugins/sql/components/query-editor-raw/RawEditor.tsx +++ b/public/app/features/plugins/sql/components/query-editor-raw/RawEditor.tsx @@ -4,6 +4,7 @@ import { useMeasure } from 'react-use'; import AutoSizer from 'react-virtualized-auto-sizer'; import { GrafanaTheme2 } from '@grafana/data'; +import { reportInteraction } from '@grafana/runtime'; import { Modal, useStyles2, useTheme2 } from '@grafana/ui'; import { SQLQuery, QueryEditorProps } from '../../types'; @@ -97,6 +98,10 @@ export function RawEditor({ db, query, onChange, onRunQuery, onValidate, queryTo contentClassName={styles.modalContent} isOpen={isExpanded} onDismiss={() => { + reportInteraction('grafana_sql_editor_expand', { + datasource: query.datasource?.type, + expanded: false, + }); setIsExpanded(false); }} > diff --git a/public/app/features/plugins/sql/components/visual-query-builder/Preview.tsx b/public/app/features/plugins/sql/components/visual-query-builder/Preview.tsx index 9ef56b7ea82..2e80220ce54 100644 --- a/public/app/features/plugins/sql/components/visual-query-builder/Preview.tsx +++ b/public/app/features/plugins/sql/components/visual-query-builder/Preview.tsx @@ -3,23 +3,32 @@ import React from 'react'; import { useCopyToClipboard } from 'react-use'; import { GrafanaTheme2 } from '@grafana/data'; +import { reportInteraction } from '@grafana/runtime'; import { CodeEditor, Field, IconButton, useStyles2 } from '@grafana/ui'; import { formatSQL } from '../../utils/formatSQL'; type PreviewProps = { rawSql: string; + datasourceType?: string; }; -export function Preview({ rawSql }: PreviewProps) { +export function Preview({ rawSql, datasourceType }: PreviewProps) { // TODO: use zero index to give feedback about copy success const [_, copyToClipboard] = useCopyToClipboard(); const styles = useStyles2(getStyles); + const copyPreview = (rawSql: string) => { + copyToClipboard(rawSql); + reportInteraction('grafana_sql_preview_copied', { + datasource: datasourceType, + }); + }; + const labelElement = (
Preview - copyToClipboard(rawSql)} name="copy" /> + copyPreview(rawSql)} name="copy" />
); diff --git a/public/app/features/plugins/sql/components/visual-query-builder/VisualEditor.tsx b/public/app/features/plugins/sql/components/visual-query-builder/VisualEditor.tsx index 9f6bbd222d4..837ef900e2e 100644 --- a/public/app/features/plugins/sql/components/visual-query-builder/VisualEditor.tsx +++ b/public/app/features/plugins/sql/components/visual-query-builder/VisualEditor.tsx @@ -51,7 +51,7 @@ export const VisualEditor = ({ query, db, queryRowFilter, onChange, onValidate, )} {queryRowFilter.preview && query.rawSql && ( - + )} diff --git a/public/app/features/plugins/sql/datasource/SqlDatasource.ts b/public/app/features/plugins/sql/datasource/SqlDatasource.ts index 3f9b51a2c83..66589d99029 100644 --- a/public/app/features/plugins/sql/datasource/SqlDatasource.ts +++ b/public/app/features/plugins/sql/datasource/SqlDatasource.ts @@ -23,6 +23,7 @@ import { getBackendSrv, getTemplateSrv, TemplateSrv, + reportInteraction, } from '@grafana/runtime'; import { toDataQueryResponse } from '@grafana/runtime/src/utils/queryResponse'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; @@ -138,6 +139,15 @@ export abstract class SqlDatasource extends DataSourceWithBackend { + reportInteraction('grafana_sql_query_executed', { + datasource: target.datasource?.type, + editorMode: target.editorMode, + format: target.format, + app: request.app, + }); + }); + return super.query(request); }