mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
SQL: Add feature tracking to sql datasources (#73996)
* 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 <gabor.farkas@gmail.com> * 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 <gabor.farkas@gmail.com>
This commit is contained in:
parent
c5a0d64652
commit
f18cd13f2b
@ -3,6 +3,7 @@ import { useCopyToClipboard } from 'react-use';
|
|||||||
|
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
import { EditorField, EditorHeader, EditorMode, EditorRow, FlexItem, InlineSelect, Space } from '@grafana/experimental';
|
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 { Button, InlineSwitch, RadioButtonGroup, Tooltip } from '@grafana/ui';
|
||||||
|
|
||||||
import { QueryWithDefaults } from '../defaults';
|
import { QueryWithDefaults } from '../defaults';
|
||||||
@ -48,6 +49,13 @@ export function QueryHeader({
|
|||||||
|
|
||||||
const onEditorModeChange = useCallback(
|
const onEditorModeChange = useCallback(
|
||||||
(newEditorMode: EditorMode) => {
|
(newEditorMode: EditorMode) => {
|
||||||
|
if (newEditorMode === EditorMode.Code) {
|
||||||
|
reportInteraction('grafana_sql_editor_mode_changed', {
|
||||||
|
datasource: query.datasource?.type,
|
||||||
|
selectedEditorMode: EditorMode.Code,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (editorMode === EditorMode.Code) {
|
if (editorMode === EditorMode.Code) {
|
||||||
setShowConfirm(true);
|
setShowConfirm(true);
|
||||||
return;
|
return;
|
||||||
@ -59,6 +67,11 @@ export function QueryHeader({
|
|||||||
|
|
||||||
const onFormatChange = (e: SelectableValue) => {
|
const onFormatChange = (e: SelectableValue) => {
|
||||||
const next = { ...query, format: e.value !== undefined ? e.value : QueryFormat.Table };
|
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);
|
onChange(next);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -123,10 +136,18 @@ export function QueryHeader({
|
|||||||
transparent={true}
|
transparent={true}
|
||||||
showLabel={true}
|
showLabel={true}
|
||||||
value={queryRowFilter.filter}
|
value={queryRowFilter.filter}
|
||||||
onChange={(ev) =>
|
onChange={(ev) => {
|
||||||
ev.target instanceof HTMLInputElement &&
|
if (!(ev.target instanceof HTMLInputElement)) {
|
||||||
onQueryRowChange({ ...queryRowFilter, filter: ev.target.checked })
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reportInteraction('grafana_sql_filter_toggled', {
|
||||||
|
datasource: query.datasource?.type,
|
||||||
|
displayed: ev.target.checked,
|
||||||
|
});
|
||||||
|
|
||||||
|
onQueryRowChange({ ...queryRowFilter, filter: ev.target.checked });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InlineSwitch
|
<InlineSwitch
|
||||||
@ -135,10 +156,18 @@ export function QueryHeader({
|
|||||||
transparent={true}
|
transparent={true}
|
||||||
showLabel={true}
|
showLabel={true}
|
||||||
value={queryRowFilter.group}
|
value={queryRowFilter.group}
|
||||||
onChange={(ev) =>
|
onChange={(ev) => {
|
||||||
ev.target instanceof HTMLInputElement &&
|
if (!(ev.target instanceof HTMLInputElement)) {
|
||||||
onQueryRowChange({ ...queryRowFilter, group: ev.target.checked })
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reportInteraction('grafana_sql_group_toggled', {
|
||||||
|
datasource: query.datasource?.type,
|
||||||
|
displayed: ev.target.checked,
|
||||||
|
});
|
||||||
|
|
||||||
|
onQueryRowChange({ ...queryRowFilter, group: ev.target.checked });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InlineSwitch
|
<InlineSwitch
|
||||||
@ -147,10 +176,18 @@ export function QueryHeader({
|
|||||||
transparent={true}
|
transparent={true}
|
||||||
showLabel={true}
|
showLabel={true}
|
||||||
value={queryRowFilter.order}
|
value={queryRowFilter.order}
|
||||||
onChange={(ev) =>
|
onChange={(ev) => {
|
||||||
ev.target instanceof HTMLInputElement &&
|
if (!(ev.target instanceof HTMLInputElement)) {
|
||||||
onQueryRowChange({ ...queryRowFilter, order: ev.target.checked })
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reportInteraction('grafana_sql_order_toggled', {
|
||||||
|
datasource: query.datasource?.type,
|
||||||
|
displayed: ev.target.checked,
|
||||||
|
});
|
||||||
|
|
||||||
|
onQueryRowChange({ ...queryRowFilter, order: ev.target.checked });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InlineSwitch
|
<InlineSwitch
|
||||||
@ -159,10 +196,18 @@ export function QueryHeader({
|
|||||||
transparent={true}
|
transparent={true}
|
||||||
showLabel={true}
|
showLabel={true}
|
||||||
value={queryRowFilter.preview}
|
value={queryRowFilter.preview}
|
||||||
onChange={(ev) =>
|
onChange={(ev) => {
|
||||||
ev.target instanceof HTMLInputElement &&
|
if (!(ev.target instanceof HTMLInputElement)) {
|
||||||
onQueryRowChange({ ...queryRowFilter, preview: ev.target.checked })
|
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({
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
isOpen={showConfirm}
|
isOpen={showConfirm}
|
||||||
onCopy={() => {
|
onCopy={() => {
|
||||||
|
reportInteraction('grafana_sql_editor_mode_changed', {
|
||||||
|
datasource: query.datasource?.type,
|
||||||
|
selectedEditorMode: EditorMode.Builder,
|
||||||
|
type: 'copy',
|
||||||
|
});
|
||||||
|
|
||||||
setShowConfirm(false);
|
setShowConfirm(false);
|
||||||
copyToClipboard(query.rawSql!);
|
copyToClipboard(query.rawSql!);
|
||||||
onChange({
|
onChange({
|
||||||
@ -204,6 +255,12 @@ export function QueryHeader({
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onDiscard={() => {
|
onDiscard={() => {
|
||||||
|
reportInteraction('grafana_sql_editor_mode_changed', {
|
||||||
|
datasource: query.datasource?.type,
|
||||||
|
selectedEditorMode: EditorMode.Builder,
|
||||||
|
type: 'discard',
|
||||||
|
});
|
||||||
|
|
||||||
setShowConfirm(false);
|
setShowConfirm(false);
|
||||||
onChange({
|
onChange({
|
||||||
...query,
|
...query,
|
||||||
@ -211,7 +268,15 @@ export function QueryHeader({
|
|||||||
editorMode: EditorMode.Builder,
|
editorMode: EditorMode.Builder,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onCancel={() => setShowConfirm(false)}
|
onCancel={() => {
|
||||||
|
reportInteraction('grafana_sql_editor_mode_changed', {
|
||||||
|
datasource: query.datasource?.type,
|
||||||
|
selectedEditorMode: EditorMode.Builder,
|
||||||
|
type: 'cancel',
|
||||||
|
});
|
||||||
|
|
||||||
|
setShowConfirm(false);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</EditorHeader>
|
</EditorHeader>
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { HorizontalGroup, Icon, IconButton, Tooltip, useTheme2 } from '@grafana/ui';
|
import { HorizontalGroup, Icon, IconButton, Tooltip, useTheme2 } from '@grafana/ui';
|
||||||
|
|
||||||
import { QueryValidator, QueryValidatorProps } from './QueryValidator';
|
import { QueryValidator, QueryValidatorProps } from './QueryValidator';
|
||||||
@ -70,11 +71,28 @@ export function QueryToolbox({ showTools, onFormatCode, onExpand, isExpanded, ..
|
|||||||
<div>
|
<div>
|
||||||
<HorizontalGroup spacing="sm">
|
<HorizontalGroup spacing="sm">
|
||||||
{onFormatCode && (
|
{onFormatCode && (
|
||||||
<IconButton onClick={onFormatCode} name="brackets-curly" size="xs" tooltip="Format query" />
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
reportInteraction('grafana_sql_query_formatted', {
|
||||||
|
datasource: validatorProps.query.datasource?.type,
|
||||||
|
});
|
||||||
|
onFormatCode();
|
||||||
|
}}
|
||||||
|
name="brackets-curly"
|
||||||
|
size="xs"
|
||||||
|
tooltip="Format query"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{onExpand && (
|
{onExpand && (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => onExpand(!isExpanded)}
|
onClick={() => {
|
||||||
|
reportInteraction('grafana_sql_editor_expand', {
|
||||||
|
datasource: validatorProps.query.datasource?.type,
|
||||||
|
expanded: !isExpanded,
|
||||||
|
});
|
||||||
|
|
||||||
|
onExpand(!isExpanded);
|
||||||
|
}}
|
||||||
name={isExpanded ? 'angle-up' : 'angle-down'}
|
name={isExpanded ? 'angle-up' : 'angle-down'}
|
||||||
size="xs"
|
size="xs"
|
||||||
tooltip={isExpanded ? 'Collapse editor' : 'Expand editor'}
|
tooltip={isExpanded ? 'Collapse editor' : 'Expand editor'}
|
||||||
|
@ -4,6 +4,7 @@ import { useMeasure } from 'react-use';
|
|||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { Modal, useStyles2, useTheme2 } from '@grafana/ui';
|
import { Modal, useStyles2, useTheme2 } from '@grafana/ui';
|
||||||
|
|
||||||
import { SQLQuery, QueryEditorProps } from '../../types';
|
import { SQLQuery, QueryEditorProps } from '../../types';
|
||||||
@ -97,6 +98,10 @@ export function RawEditor({ db, query, onChange, onRunQuery, onValidate, queryTo
|
|||||||
contentClassName={styles.modalContent}
|
contentClassName={styles.modalContent}
|
||||||
isOpen={isExpanded}
|
isOpen={isExpanded}
|
||||||
onDismiss={() => {
|
onDismiss={() => {
|
||||||
|
reportInteraction('grafana_sql_editor_expand', {
|
||||||
|
datasource: query.datasource?.type,
|
||||||
|
expanded: false,
|
||||||
|
});
|
||||||
setIsExpanded(false);
|
setIsExpanded(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -3,23 +3,32 @@ import React from 'react';
|
|||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { CodeEditor, Field, IconButton, useStyles2 } from '@grafana/ui';
|
import { CodeEditor, Field, IconButton, useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
import { formatSQL } from '../../utils/formatSQL';
|
import { formatSQL } from '../../utils/formatSQL';
|
||||||
|
|
||||||
type PreviewProps = {
|
type PreviewProps = {
|
||||||
rawSql: string;
|
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
|
// TODO: use zero index to give feedback about copy success
|
||||||
const [_, copyToClipboard] = useCopyToClipboard();
|
const [_, copyToClipboard] = useCopyToClipboard();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
|
const copyPreview = (rawSql: string) => {
|
||||||
|
copyToClipboard(rawSql);
|
||||||
|
reportInteraction('grafana_sql_preview_copied', {
|
||||||
|
datasource: datasourceType,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const labelElement = (
|
const labelElement = (
|
||||||
<div className={styles.labelWrapper}>
|
<div className={styles.labelWrapper}>
|
||||||
<span className={styles.label}>Preview</span>
|
<span className={styles.label}>Preview</span>
|
||||||
<IconButton tooltip="Copy to clipboard" onClick={() => copyToClipboard(rawSql)} name="copy" />
|
<IconButton tooltip="Copy to clipboard" onClick={() => copyPreview(rawSql)} name="copy" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ export const VisualEditor = ({ query, db, queryRowFilter, onChange, onValidate,
|
|||||||
)}
|
)}
|
||||||
{queryRowFilter.preview && query.rawSql && (
|
{queryRowFilter.preview && query.rawSql && (
|
||||||
<EditorRow>
|
<EditorRow>
|
||||||
<Preview rawSql={query.rawSql} />
|
<Preview rawSql={query.rawSql} datasourceType={query.datasource?.type} />
|
||||||
</EditorRow>
|
</EditorRow>
|
||||||
)}
|
)}
|
||||||
</EditorRows>
|
</EditorRows>
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
getBackendSrv,
|
getBackendSrv,
|
||||||
getTemplateSrv,
|
getTemplateSrv,
|
||||||
TemplateSrv,
|
TemplateSrv,
|
||||||
|
reportInteraction,
|
||||||
} from '@grafana/runtime';
|
} from '@grafana/runtime';
|
||||||
import { toDataQueryResponse } from '@grafana/runtime/src/utils/queryResponse';
|
import { toDataQueryResponse } from '@grafana/runtime/src/utils/queryResponse';
|
||||||
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||||
@ -138,6 +139,15 @@ export abstract class SqlDatasource extends DataSourceWithBackend<SQLQuery, SQLO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request.targets.forEach((target) => {
|
||||||
|
reportInteraction('grafana_sql_query_executed', {
|
||||||
|
datasource: target.datasource?.type,
|
||||||
|
editorMode: target.editorMode,
|
||||||
|
format: target.format,
|
||||||
|
app: request.app,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return super.query(request);
|
return super.query(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user