mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Track data links usage (#56868)
* Add tracking for data links in explore * Add tracking for data links in explore * Fix tests * Retrigger build
This commit is contained in:
@@ -62,6 +62,7 @@ const dummyProps: Props = {
|
||||
scanStopAction: scanStopAction,
|
||||
setQueries: jest.fn(),
|
||||
queryKeys: [],
|
||||
queries: [],
|
||||
isLive: false,
|
||||
syncedTimes: false,
|
||||
updateTimeRange: jest.fn(),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { get } from 'lodash';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import React, { createRef } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
@@ -6,13 +7,22 @@ import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { compose } from 'redux';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
|
||||
import { AbsoluteTimeRange, DataQuery, GrafanaTheme2, LoadingState, QueryFixAction, RawTimeRange } from '@grafana/data';
|
||||
import {
|
||||
AbsoluteTimeRange,
|
||||
DataQuery,
|
||||
GrafanaTheme2,
|
||||
LoadingState,
|
||||
QueryFixAction,
|
||||
RawTimeRange,
|
||||
SplitOpenOptions,
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { config, getDataSourceSrv } from '@grafana/runtime';
|
||||
import { config, getDataSourceSrv, reportInteraction } from '@grafana/runtime';
|
||||
import { Collapse, CustomScrollbar, ErrorBoundaryAlert, Themeable2, withTheme2, PanelContainer } from '@grafana/ui';
|
||||
import { FILTER_FOR_OPERATOR, FILTER_OUT_OPERATOR, FilterItem } from '@grafana/ui/src/components/Table/types';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { supportedFeatures } from 'app/core/history/richHistoryStorageProvider';
|
||||
import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource';
|
||||
import { getNodeGraphDataFrames } from 'app/plugins/panel/nodeGraph/utils';
|
||||
import { StoreState } from 'app/types';
|
||||
import { AbsoluteTimeEvent } from 'app/types/events';
|
||||
@@ -228,6 +238,27 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
});
|
||||
};
|
||||
|
||||
onSplitOpen = (panelType: string) => {
|
||||
return async (options?: SplitOpenOptions<DataQuery>) => {
|
||||
this.props.splitOpen(options);
|
||||
if (options && this.props.datasourceInstance) {
|
||||
const target = (await getDataSourceSrv().get(options.datasourceUid)).type;
|
||||
const source =
|
||||
this.props.datasourceInstance.uid === MIXED_DATASOURCE_NAME
|
||||
? get(this.props.queries, '0.datasource.type')
|
||||
: this.props.datasourceInstance.type;
|
||||
const tracking = {
|
||||
origin: 'panel',
|
||||
panelType,
|
||||
source,
|
||||
target,
|
||||
exploreId: this.props.exploreId,
|
||||
};
|
||||
reportInteraction('grafana_explore_split_view_opened', tracking);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
renderEmptyState(exploreContainerStyles: string) {
|
||||
return (
|
||||
<div className={cx(exploreContainerStyles)}>
|
||||
@@ -241,17 +272,8 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
}
|
||||
|
||||
renderGraphPanel(width: number) {
|
||||
const {
|
||||
graphResult,
|
||||
absoluteRange,
|
||||
timeZone,
|
||||
splitOpen,
|
||||
queryResponse,
|
||||
loading,
|
||||
theme,
|
||||
graphStyle,
|
||||
showFlameGraph,
|
||||
} = this.props;
|
||||
const { graphResult, absoluteRange, timeZone, queryResponse, loading, theme, graphStyle, showFlameGraph } =
|
||||
this.props;
|
||||
const spacing = parseInt(theme.spacing(2).slice(0, -2), 10);
|
||||
const label = <ExploreGraphLabel graphStyle={graphStyle} onChangeGraphStyle={this.onChangeGraphStyle} />;
|
||||
|
||||
@@ -266,7 +288,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
onChangeTime={this.onUpdateTimeRange}
|
||||
timeZone={timeZone}
|
||||
annotations={queryResponse.annotations}
|
||||
splitOpenFn={splitOpen}
|
||||
splitOpenFn={this.onSplitOpen('graph')}
|
||||
loadingState={queryResponse.state}
|
||||
anchorToZero={false}
|
||||
/>
|
||||
@@ -283,6 +305,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
exploreId={exploreId}
|
||||
onCellFilterAdded={this.onCellFilterAdded}
|
||||
timeZone={timeZone}
|
||||
splitOpenFn={this.onSplitOpen('table')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -301,6 +324,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
onStartScanning={this.onStartScanning}
|
||||
onStopScanning={this.onStopScanning}
|
||||
scrollElement={this.scrollElement}
|
||||
splitOpenFn={this.onSplitOpen('logs')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -315,6 +339,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
exploreId={exploreId}
|
||||
withTraceView={showTrace}
|
||||
datasourceType={datasourceType}
|
||||
splitOpenFn={this.onSplitOpen('nodeGraph')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -327,7 +352,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
}
|
||||
|
||||
renderTraceViewPanel() {
|
||||
const { queryResponse, splitOpen, exploreId } = this.props;
|
||||
const { queryResponse, exploreId } = this.props;
|
||||
const dataFrames = queryResponse.series.filter((series) => series.meta?.preferredVisualisationType === 'trace');
|
||||
|
||||
return (
|
||||
@@ -336,7 +361,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
<TraceViewContainer
|
||||
exploreId={exploreId}
|
||||
dataFrames={dataFrames}
|
||||
splitOpenFn={splitOpen}
|
||||
splitOpenFn={this.onSplitOpen('traceView')}
|
||||
scrollElement={this.scrollElement}
|
||||
queryResponse={queryResponse}
|
||||
topOfViewRef={this.topOfViewRef}
|
||||
@@ -470,6 +495,7 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
datasourceInstance,
|
||||
datasourceMissing,
|
||||
queryKeys,
|
||||
queries,
|
||||
isLive,
|
||||
graphResult,
|
||||
logsResult,
|
||||
@@ -489,6 +515,7 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
datasourceInstance,
|
||||
datasourceMissing,
|
||||
queryKeys,
|
||||
queries,
|
||||
isLive,
|
||||
graphResult,
|
||||
logsResult: logsResult ?? undefined,
|
||||
|
||||
@@ -50,7 +50,7 @@ interface Props {
|
||||
annotations?: DataFrame[];
|
||||
onHiddenSeriesChanged?: (hiddenSeries: string[]) => void;
|
||||
tooltipDisplayMode?: TooltipDisplayMode;
|
||||
splitOpenFn?: SplitOpen;
|
||||
splitOpenFn: SplitOpen;
|
||||
onChangeTime: (timeRange: AbsoluteTimeRange) => void;
|
||||
graphStyle: ExploreGraphStyle;
|
||||
anchorToZero: boolean;
|
||||
|
||||
@@ -77,7 +77,7 @@ class UnConnectedExploreToolbar extends PureComponent<Props> {
|
||||
onOpenSplitView = () => {
|
||||
const { split } = this.props;
|
||||
split();
|
||||
reportInteraction('grafana_explore_split_view_opened');
|
||||
reportInteraction('grafana_explore_split_view_opened', { origin: 'menu' });
|
||||
};
|
||||
|
||||
onCloseSplitView = () => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
LoadingState,
|
||||
LogRowModel,
|
||||
RawTimeRange,
|
||||
SplitOpen,
|
||||
} from '@grafana/data';
|
||||
import { Collapse } from '@grafana/ui';
|
||||
import { StoreState } from 'app/types';
|
||||
@@ -17,7 +18,6 @@ import { getTimeZone } from '../profile/state/selectors';
|
||||
|
||||
import { LiveLogsWithTheme } from './LiveLogs';
|
||||
import { Logs } from './Logs';
|
||||
import { splitOpen } from './state/main';
|
||||
import { addResultsToCache, clearCache, loadLogsVolumeData, setLogsVolumeEnabled } from './state/query';
|
||||
import { updateTimeRange } from './state/time';
|
||||
import { LiveTailControls } from './useLiveTailControls';
|
||||
@@ -35,6 +35,7 @@ interface LogsContainerProps extends PropsFromRedux {
|
||||
onClickFilterOutLabel?: (key: string, value: string) => void;
|
||||
onStartScanning: () => void;
|
||||
onStopScanning: () => void;
|
||||
splitOpenFn: SplitOpen;
|
||||
}
|
||||
|
||||
class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
@@ -69,7 +70,7 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
};
|
||||
|
||||
getFieldLinks = (field: Field, rowIndex: number) => {
|
||||
const { splitOpen: splitOpenFn, range } = this.props;
|
||||
const { splitOpenFn, range } = this.props;
|
||||
return getFieldLinksForExplore({ field, rowIndex, splitOpenFn, range });
|
||||
};
|
||||
|
||||
@@ -93,7 +94,7 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
scanning,
|
||||
range,
|
||||
width,
|
||||
splitOpen,
|
||||
splitOpenFn,
|
||||
isLive,
|
||||
exploreId,
|
||||
addResultsToCache,
|
||||
@@ -135,7 +136,7 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
logsVolumeData={logsVolumeData}
|
||||
logsQueries={logsQueries}
|
||||
width={width}
|
||||
splitOpen={splitOpen}
|
||||
splitOpen={splitOpenFn}
|
||||
loading={loading}
|
||||
loadingState={loadingState}
|
||||
loadLogsVolumeData={loadLogsVolumeData}
|
||||
@@ -203,7 +204,6 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
|
||||
|
||||
const mapDispatchToProps = {
|
||||
updateTimeRange,
|
||||
splitOpen,
|
||||
addResultsToCache,
|
||||
clearCache,
|
||||
loadLogsVolumeData,
|
||||
|
||||
@@ -14,7 +14,7 @@ describe('NodeGraphContainer', () => {
|
||||
dataFrames={[emptyFrame]}
|
||||
exploreId={ExploreId.left}
|
||||
range={getDefaultTimeRange()}
|
||||
splitOpen={(() => {}) as any}
|
||||
splitOpenFn={(() => {}) as any}
|
||||
withTraceView={true}
|
||||
datasourceType={''}
|
||||
/>
|
||||
@@ -30,7 +30,7 @@ describe('NodeGraphContainer', () => {
|
||||
dataFrames={[nodes]}
|
||||
exploreId={ExploreId.left}
|
||||
range={getDefaultTimeRange()}
|
||||
splitOpen={(() => {}) as any}
|
||||
splitOpenFn={(() => {}) as any}
|
||||
datasourceType={''}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { useToggle, useWindowSize } from 'react-use';
|
||||
|
||||
import { applyFieldOverrides, DataFrame, GrafanaTheme2 } from '@grafana/data';
|
||||
import { applyFieldOverrides, DataFrame, GrafanaTheme2, SplitOpen } from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Badge, Collapse, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
|
||||
@@ -11,7 +11,6 @@ import { NodeGraph } from '../../plugins/panel/nodeGraph';
|
||||
import { useCategorizeFrames } from '../../plugins/panel/nodeGraph/useCategorizeFrames';
|
||||
import { ExploreId, StoreState } from '../../types';
|
||||
|
||||
import { splitOpen } from './state/main';
|
||||
import { useLinks } from './utils/links';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
@@ -29,13 +28,14 @@ interface OwnProps {
|
||||
// When showing the node graph together with trace view we do some changes so it works better.
|
||||
withTraceView?: boolean;
|
||||
datasourceType: string;
|
||||
splitOpenFn: SplitOpen;
|
||||
}
|
||||
|
||||
type Props = OwnProps & ConnectedProps<typeof connector>;
|
||||
|
||||
export function UnconnectedNodeGraphContainer(props: Props) {
|
||||
const { dataFrames, range, splitOpen, withTraceView, datasourceType } = props;
|
||||
const getLinks = useLinks(range, splitOpen);
|
||||
const { dataFrames, range, splitOpenFn, withTraceView, datasourceType } = props;
|
||||
const getLinks = useLinks(range, splitOpenFn);
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
@@ -116,9 +116,5 @@ function mapStateToProps(state: StoreState, { exploreId }: OwnProps) {
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
splitOpen,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
const connector = connect(mapStateToProps, {});
|
||||
export const NodeGraphContainer = connector(UnconnectedNodeGraphContainer);
|
||||
|
||||
@@ -53,7 +53,7 @@ const defaultProps = {
|
||||
width: 800,
|
||||
onCellFilterAdded: jest.fn(),
|
||||
tableResult: dataFrame,
|
||||
splitOpen: (() => {}) as any,
|
||||
splitOpenFn: (() => {}) as any,
|
||||
range: {} as any,
|
||||
timeZone: InternalTimeZones.utc,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { ValueLinkConfig, applyFieldOverrides, TimeZone } from '@grafana/data';
|
||||
import { ValueLinkConfig, applyFieldOverrides, TimeZone, SplitOpen } from '@grafana/data';
|
||||
import { Collapse, Table } from '@grafana/ui';
|
||||
import { FilterItem } from '@grafana/ui/src/components/Table/types';
|
||||
import { config } from 'app/core/config';
|
||||
@@ -10,7 +10,6 @@ import { StoreState } from 'app/types';
|
||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||
|
||||
import { MetaInfoText } from './MetaInfoText';
|
||||
import { splitOpen } from './state/main';
|
||||
import { getFieldLinksForExplore } from './utils/links';
|
||||
|
||||
interface TableContainerProps {
|
||||
@@ -19,6 +18,7 @@ interface TableContainerProps {
|
||||
width: number;
|
||||
timeZone: TimeZone;
|
||||
onCellFilterAdded?: (filter: FilterItem) => void;
|
||||
splitOpenFn: SplitOpen;
|
||||
}
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: TableContainerProps) {
|
||||
@@ -30,11 +30,7 @@ function mapStateToProps(state: StoreState, { exploreId }: TableContainerProps)
|
||||
return { loading, tableResult, range };
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
splitOpen,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
const connector = connect(mapStateToProps, {});
|
||||
|
||||
type Props = TableContainerProps & ConnectedProps<typeof connector>;
|
||||
|
||||
@@ -51,7 +47,7 @@ export class TableContainer extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, onCellFilterAdded, tableResult, width, splitOpen, range, ariaLabel, timeZone } = this.props;
|
||||
const { loading, onCellFilterAdded, tableResult, width, splitOpenFn, range, ariaLabel, timeZone } = this.props;
|
||||
const height = this.getTableHeight();
|
||||
const tableWidth = width - config.theme.panelPadding * 2 - PANEL_BORDER;
|
||||
|
||||
@@ -76,7 +72,7 @@ export class TableContainer extends PureComponent<Props> {
|
||||
return getFieldLinksForExplore({
|
||||
field,
|
||||
rowIndex: config.valueRowIndex!,
|
||||
splitOpenFn: splitOpen,
|
||||
splitOpenFn,
|
||||
range,
|
||||
dataFrame: dataFrame!,
|
||||
});
|
||||
|
||||
@@ -55,6 +55,19 @@ export function generateRandomNodes(count = 10) {
|
||||
[NodeGraphDataFrameFieldNames.id]: {
|
||||
values: new ArrayVector(),
|
||||
type: FieldType.string,
|
||||
config: {
|
||||
links: [
|
||||
{
|
||||
title: 'test data link',
|
||||
url: '',
|
||||
internal: {
|
||||
query: { scenarioId: 'logs', alias: 'from service graph', stringInput: 'tes' },
|
||||
datasourceUid: 'gdev-testdata',
|
||||
datasourceName: 'gdev-testdata',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
[NodeGraphDataFrameFieldNames.title]: {
|
||||
values: new ArrayVector(),
|
||||
|
||||
Reference in New Issue
Block a user