QueryInspector: add common way to show the raw query (#25204)

This commit is contained in:
Ryan McKinley
2020-05-29 11:06:33 -07:00
committed by GitHub
parent c563633870
commit 2a6ac88a73
15 changed files with 140 additions and 60 deletions

View File

@@ -354,7 +354,7 @@ export class PanelInspectorUnconnected extends PureComponent<Props, State> {
)}
{activeTab === InspectTab.Error && this.renderErrorTab(error)}
{activeTab === InspectTab.Stats && this.renderStatsTab()}
{activeTab === InspectTab.Query && <QueryInspector panel={panel} />}
{activeTab === InspectTab.Query && <QueryInspector panel={panel} data={last.series} />}
</TabContent>
</CustomScrollbar>
</Drawer>

View File

@@ -1,7 +1,7 @@
import React, { PureComponent } from 'react';
import { Button, JSONFormatter, LoadingPlaceholder } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import { AppEvents, PanelEvents } from '@grafana/data';
import { AppEvents, PanelEvents, DataFrame } from '@grafana/data';
import appEvents from 'app/core/app_events';
import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
@@ -9,14 +9,24 @@ import { CoreEvents } from 'app/types';
import { PanelModel } from 'app/features/dashboard/state';
import { getPanelInspectorStyles } from './styles';
import { supportsDataQuery } from '../PanelEditor/utils';
import { config } from '@grafana/runtime';
import { css } from 'emotion';
interface DsQuery {
isLoading: boolean;
response: {};
}
interface ExecutedQueryInfo {
refId: string;
query: string;
frames: number;
rows: number;
}
interface Props {
panel: PanelModel;
data: DataFrame[];
}
interface State {
@@ -24,6 +34,7 @@ interface State {
isMocking: boolean;
mockedResponse: string;
dsQuery: DsQuery;
executedQueries: ExecutedQueryInfo[];
}
export class QueryInspector extends PureComponent<Props, State> {
@@ -33,6 +44,7 @@ export class QueryInspector extends PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
executedQueries: [],
allNodesExpanded: null,
isMocking: false,
mockedResponse: '',
@@ -47,6 +59,43 @@ export class QueryInspector extends PureComponent<Props, State> {
appEvents.on(CoreEvents.dsRequestResponse, this.onDataSourceResponse);
appEvents.on(CoreEvents.dsRequestError, this.onRequestError);
this.props.panel.events.on(PanelEvents.refresh, this.onPanelRefresh);
this.updateQueryList();
}
componentDidUpdate(oldProps: Props) {
if (this.props.data !== oldProps.data) {
this.updateQueryList();
}
}
/**
* Find the list of executed queries
*/
updateQueryList() {
const { data } = this.props;
const executedQueries: ExecutedQueryInfo[] = [];
if (data?.length) {
let last: ExecutedQueryInfo | undefined = undefined;
data.forEach((frame, idx) => {
const query = frame.meta?.executedQueryString;
if (query) {
const refId = frame.refId || '?';
if (last?.refId === refId) {
last.frames++;
last.rows += frame.length;
} else {
last = {
refId,
frames: 0,
rows: frame.length,
query,
};
executedQueries.push(last);
}
}
});
}
this.setState({ executedQueries });
}
onIssueNewQuery = () => {
@@ -182,8 +231,39 @@ export class QueryInspector extends PureComponent<Props, State> {
}));
};
renderExecutedQueries(executedQueries: ExecutedQueryInfo[]) {
if (!executedQueries.length) {
return null;
}
const styles = {
refId: css`
font-weight: ${config.theme.typography.weight.semibold};
color: ${config.theme.colors.textBlue};
margin-right: 8px;
`,
};
return (
<div>
{executedQueries.map(info => {
return (
<div key={info.refId}>
<div>
<span className={styles.refId}>{info.refId}:</span>
{info.frames > 1 && <span>{info.frames} frames, </span>}
<span>{info.rows} rows</span>
</div>
<pre>{info.query}</pre>
</div>
);
})}
</div>
);
}
render() {
const { allNodesExpanded } = this.state;
const { allNodesExpanded, executedQueries } = this.state;
const { response, isLoading } = this.state.dsQuery;
const openNodes = this.getNrOfOpenNodes();
const styles = getPanelInspectorStyles();
@@ -202,6 +282,7 @@ export class QueryInspector extends PureComponent<Props, State> {
new query. Hit refresh button below to trigger a new query.
</p>
</div>
{this.renderExecutedQueries(executedQueries)}
<div className={styles.toolbar}>
<Button
icon="sync"

View File

@@ -20,11 +20,10 @@
<icon name="'angle-right'" ng-hide="ctrl.showHelp" style="margin-top: 3px;"></icon>
</label>
</div>
<div class="gf-form" ng-show="ctrl.lastQueryMeta">
<label class="gf-form-label query-keyword" ng-click="ctrl.showLastQuerySQL = !ctrl.showLastQuerySQL">
<div class="gf-form">
<label class="gf-form-label query-keyword" ng-click="ctrl.showQueryInspector()">
Generated SQL
<icon name="'angle-down'" ng-show="ctrl.showLastQuerySQL" style="margin-top: 3px;"></icon>
<icon name="'angle-right'" ng-hide="ctrl.showLastQuerySQL" style="margin-top: 3px;"></icon>
<icon name="'angle-right'" style="margin-top: 3px;"></icon>
</label>
</div>
<div class="gf-form gf-form--grow">

View File

@@ -2,6 +2,7 @@ import _ from 'lodash';
import { QueryCtrl } from 'app/plugins/sdk';
import { auto } from 'angular';
import { PanelEvents } from '@grafana/data';
import { getLocationSrv } from '@grafana/runtime';
export interface MssqlQuery {
refId: string;
@@ -28,10 +29,8 @@ ORDER BY
export class MssqlQueryCtrl extends QueryCtrl {
static templateUrl = 'partials/query.editor.html';
showLastQuerySQL: boolean;
formats: any[];
target: MssqlQuery;
lastQueryMeta: QueryMeta;
lastQueryError: string;
showHelp: boolean;
@@ -60,21 +59,21 @@ export class MssqlQueryCtrl extends QueryCtrl {
this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope);
}
onDataReceived(dataList: any) {
this.lastQueryMeta = null;
this.lastQueryError = null;
showQueryInspector() {
getLocationSrv().update({
query: { inspect: this.panel.id, inspectTab: 'query' },
partial: true,
});
}
const anySeriesFromQuery: any = _.find(dataList, { refId: this.target.refId });
if (anySeriesFromQuery) {
this.lastQueryMeta = anySeriesFromQuery.meta;
}
onDataReceived(dataList: any) {
this.lastQueryError = null;
}
onDataError(err: any) {
if (err.data && err.data.results) {
const queryRes = err.data.results[this.target.refId];
if (queryRes) {
this.lastQueryMeta = queryRes.meta;
this.lastQueryError = queryRes.error;
}
}

View File

@@ -120,11 +120,10 @@
<icon name="'angle-right'" ng-hide="ctrl.showHelp" style="margin-top: 3px;"></icon>
</label>
</div>
<div class="gf-form" ng-show="ctrl.lastQueryMeta">
<label class="gf-form-label query-keyword pointer" ng-click="ctrl.showLastQuerySQL = !ctrl.showLastQuerySQL">
<div class="gf-form">
<label class="gf-form-label query-keyword pointer" ng-click="ctrl.showQueryInspector()">
Generated SQL
<icon name="'angle-down'" ng-show="ctrl.showLastQuerySQL" style="margin-top: 3px;"></icon>
<icon name="'angle-right'" ng-hide="ctrl.showLastQuerySQL" style="margin-top: 3px;"></icon>
<icon name="'angle-right'" style="margin-top: 3px;"></icon>
</label>
</div>
<div class="gf-form gf-form--grow">
@@ -132,10 +131,6 @@
</div>
</div>
<div class="gf-form" ng-show="ctrl.showLastQuerySQL">
<pre class="gf-form-pre">{{ctrl.lastQueryMeta.sql}}</pre>
</div>
<div class="gf-form" ng-show="ctrl.showHelp">
<pre class="gf-form-pre alert alert-info">Time series:
- return column named time or time_sec (in UTC), as a unix time stamp or any sql native date data type. You can use the macros below.

View File

@@ -10,6 +10,7 @@ import { TemplateSrv } from 'app/features/templating/template_srv';
import { CoreEvents } from 'app/types';
import { PanelEvents } from '@grafana/data';
import { VariableWithMultiSupport } from 'app/features/templating/types';
import { getLocationSrv } from '@grafana/runtime';
export interface QueryMeta {
sql: string;
@@ -27,9 +28,7 @@ ORDER BY <time_column> ASC
export class MysqlQueryCtrl extends QueryCtrl {
static templateUrl = 'partials/query.editor.html';
showLastQuerySQL: boolean;
formats: any[];
lastQueryMeta: QueryMeta;
lastQueryError: string;
showHelp: boolean;
@@ -110,6 +109,13 @@ export class MysqlQueryCtrl extends QueryCtrl {
this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope);
}
showQueryInspector() {
getLocationSrv().update({
query: { inspect: this.panel.id, inspectTab: 'query' },
partial: true,
});
}
updateRawSqlAndRefresh() {
if (!this.target.rawQuery) {
this.target.rawSql = this.queryModel.buildQuery();
@@ -273,20 +279,13 @@ export class MysqlQueryCtrl extends QueryCtrl {
}
onDataReceived(dataList: any) {
this.lastQueryMeta = null;
this.lastQueryError = null;
const anySeriesFromQuery: any = _.find(dataList, { refId: this.target.refId });
if (anySeriesFromQuery) {
this.lastQueryMeta = anySeriesFromQuery.meta;
}
}
onDataError(err: any) {
if (err.data && err.data.results) {
const queryRes = err.data.results[this.target.refId];
if (queryRes) {
this.lastQueryMeta = queryRes.meta;
this.lastQueryError = queryRes.error;
}
}

View File

@@ -120,11 +120,10 @@
<icon name="'angle-right'" ng-hide="ctrl.showHelp" style="margin-top: 3px;"></icon>
</label>
</div>
<div class="gf-form" ng-show="ctrl.lastQueryMeta">
<label class="gf-form-label query-keyword pointer" ng-click="ctrl.showLastQuerySQL = !ctrl.showLastQuerySQL">
<div class="gf-form">
<label class="gf-form-label query-keyword pointer" ng-click="ctrl.showQueryInspector()">
Generated SQL
<icon name="'angle-down'" ng-show="ctrl.showLastQuerySQL" style="margin-top: 3px;"></icon>
<icon name="'angle-right'" ng-hide="ctrl.showLastQuerySQL" style="margin-top: 3px;"></icon>
<icon name="'angle-right'" style="margin-top: 3px;"></icon>
</label>
</div>
<div class="gf-form gf-form--grow">
@@ -132,9 +131,6 @@
</div>
</div>
<div class="gf-form" ng-show="ctrl.showLastQuerySQL">
<pre class="gf-form-pre">{{ctrl.lastQueryMeta.sql}}</pre>
</div>
<div class="gf-form" ng-show="ctrl.showHelp">
<pre class="gf-form-pre alert alert-info">Time series:

View File

@@ -10,6 +10,7 @@ import { TemplateSrv } from 'app/features/templating/template_srv';
import { CoreEvents } from 'app/types';
import { PanelEvents } from '@grafana/data';
import { VariableWithMultiSupport } from 'app/features/templating/types';
import { getLocationSrv } from '@grafana/runtime';
export interface QueryMeta {
sql: string;
@@ -31,7 +32,6 @@ export class PostgresQueryCtrl extends QueryCtrl {
formats: any[];
queryModel: PostgresQuery;
metaBuilder: PostgresMetaQuery;
lastQueryMeta: QueryMeta;
lastQueryError: string;
showHelp: boolean;
tableSegment: any;
@@ -108,6 +108,13 @@ export class PostgresQueryCtrl extends QueryCtrl {
this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope);
}
showQueryInspector() {
getLocationSrv().update({
query: { inspect: this.panel.id, inspectTab: 'query' },
partial: true,
});
}
updateRawSqlAndRefresh() {
if (!this.target.rawQuery) {
this.target.rawSql = this.queryModel.buildQuery();
@@ -306,21 +313,13 @@ export class PostgresQueryCtrl extends QueryCtrl {
}
onDataReceived(dataList: any) {
this.lastQueryMeta = null;
this.lastQueryError = null;
console.log('postgres query data received', dataList);
const anySeriesFromQuery: any = _.find(dataList, { refId: this.target.refId });
if (anySeriesFromQuery) {
this.lastQueryMeta = anySeriesFromQuery.meta;
}
}
onDataError(err: any) {
if (err.data && err.data.results) {
const queryRes = err.data.results[this.target.refId];
if (queryRes) {
this.lastQueryMeta = queryRes.meta;
this.lastQueryError = queryRes.error;
}
}

View File

@@ -107,7 +107,10 @@ export class QueryEditor extends PureComponent<Props, State> {
query={sloQuery}
></SLOQueryEditor>
)}
<Help rawQuery={decodeURIComponent(meta?.rawQuery ?? '')} lastQueryError={this.state.lastQueryError} />
<Help
rawQuery={decodeURIComponent(meta?.executedQueryString ?? '')}
lastQueryError={this.state.lastQueryError}
/>
</>
);
}