Commit 068fef8c by Torkel Ödegaard Committed by GitHub

QueryEditors: Refactoring & rewriting out dependency on PanelModel (#29419)

* Removing PanelModel usage from query rows

* More work removing dependency on panel model

* Before big change to query options

* Query options now have no dependency on panel model

* Test page is working

* Shared query now works again

* Rename component

* fix after merge

* Fixed issue with old angular editors
parent b8fec209
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { QueriesTab } from 'app/features/query/components/QueriesTab'; import { QueryGroup } from 'app/features/query/components/QueryGroup';
import { DashboardModel, PanelModel } from '../../state'; import { QueryGroupOptions } from 'app/features/query/components/QueryGroupOptions';
import { PanelModel } from '../../state';
import { DataQuery, DataSourceSelectItem } from '@grafana/data';
import { getLocationSrv } from '@grafana/runtime';
interface Props { interface Props {
panel: PanelModel; panel: PanelModel;
dashboard: DashboardModel;
} }
export class PanelEditorQueries extends PureComponent<Props> { interface State {
options: QueryGroupOptions;
}
export class PanelEditorQueries extends PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = { options: this.buildQueryOptions(props) };
}
buildQueryOptions({ panel }: Props): QueryGroupOptions {
return {
maxDataPoints: panel.maxDataPoints,
minInterval: panel.interval,
timeRange: {
from: panel.timeFrom,
shift: panel.timeShift,
hide: panel.hideTimeOverride,
},
};
}
onDataSourceChange = (ds: DataSourceSelectItem, queries: DataQuery[]) => {
const { panel } = this.props;
panel.datasource = ds.value;
panel.targets = queries;
panel.refresh();
this.forceUpdate();
};
onRunQueries = () => {
this.props.panel.refresh();
};
onQueriesChange = (queries: DataQuery[]) => {
const { panel } = this.props;
panel.targets = queries;
panel.refresh();
this.forceUpdate();
};
onOpenQueryInspector = () => {
getLocationSrv().update({
query: { inspect: this.props.panel.id, inspectTab: 'query' },
partial: true,
});
};
onQueryOptionsChange = (options: QueryGroupOptions) => {
const { panel } = this.props;
panel.timeFrom = options.timeRange?.from;
panel.timeShift = options.timeRange?.shift;
panel.hideTimeOverride = options.timeRange?.hide;
panel.interval = options.minInterval;
panel.maxDataPoints = options.maxDataPoints;
panel.refresh();
this.setState({ options: options });
};
render() { render() {
const { panel, dashboard } = this.props; const { panel } = this.props;
const { options } = this.state;
return <QueriesTab panel={panel} dashboard={dashboard} />; return (
<QueryGroup
dataSourceName={panel.datasource}
options={options}
queryRunner={panel.getQueryRunner()}
queries={panel.targets}
onQueriesChange={this.onQueriesChange}
onDataSourceChange={this.onDataSourceChange}
onRunQueries={this.onRunQueries}
onOpenQueryInspector={this.onOpenQueryInspector}
onOptionsChange={this.onQueryOptionsChange}
/>
);
} }
} }
...@@ -76,7 +76,7 @@ export class PanelEditorTabs extends PureComponent<PanelEditorTabsProps> { ...@@ -76,7 +76,7 @@ export class PanelEditorTabs extends PureComponent<PanelEditorTabsProps> {
})} })}
</TabsBar> </TabsBar>
<TabContent className={styles.tabContent}> <TabContent className={styles.tabContent}>
{activeTab.id === PanelEditorTabId.Query && <PanelEditorQueries panel={panel} dashboard={dashboard} />} {activeTab.id === PanelEditorTabId.Query && <PanelEditorQueries panel={panel} />}
{activeTab.id === PanelEditorTabId.Alert && <AlertTab panel={panel} dashboard={dashboard} />} {activeTab.id === PanelEditorTabId.Alert && <AlertTab panel={panel} dashboard={dashboard} />}
{activeTab.id === PanelEditorTabId.Transform && <TransformationsEditor panel={panel} />} {activeTab.id === PanelEditorTabId.Transform && <TransformationsEditor panel={panel} />}
</TabContent> </TabContent>
......
...@@ -132,8 +132,8 @@ export class PanelModel implements DataConfigSource { ...@@ -132,8 +132,8 @@ export class PanelModel implements DataConfigSource {
}; };
fieldConfig: FieldConfigSource; fieldConfig: FieldConfigSource;
maxDataPoints?: number; maxDataPoints?: number | null;
interval?: string; interval?: string | null;
description?: string; description?: string;
links?: DataLink[]; links?: DataLink[];
transparent: boolean; transparent: boolean;
......
...@@ -6,9 +6,6 @@ import _ from 'lodash'; ...@@ -6,9 +6,6 @@ import _ from 'lodash';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { AngularComponent, getAngularLoader } from '@grafana/runtime'; import { AngularComponent, getAngularLoader } from '@grafana/runtime';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
// Types
import { PanelModel } from '../../dashboard/state/PanelModel';
import { ErrorBoundaryAlert, HorizontalGroup } from '@grafana/ui'; import { ErrorBoundaryAlert, HorizontalGroup } from '@grafana/ui';
import { import {
DataQuery, DataQuery,
...@@ -25,12 +22,11 @@ import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOp ...@@ -25,12 +22,11 @@ import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOp
import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction'; import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction';
import { DashboardModel } from '../../dashboard/state/DashboardModel'; import { DashboardModel } from '../../dashboard/state/DashboardModel';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { PanelModel } from 'app/features/dashboard/state';
interface Props { interface Props {
panel: PanelModel;
data: PanelData; data: PanelData;
query: DataQuery; query: DataQuery;
dashboard?: DashboardModel;
dataSourceValue: string | null; dataSourceValue: string | null;
inMixedMode?: boolean; inMixedMode?: boolean;
id: string; id: string;
...@@ -38,6 +34,7 @@ interface Props { ...@@ -38,6 +34,7 @@ interface Props {
onAddQuery: (query?: DataQuery) => void; onAddQuery: (query?: DataQuery) => void;
onRemoveQuery: (query: DataQuery) => void; onRemoveQuery: (query: DataQuery) => void;
onChange: (query: DataQuery) => void; onChange: (query: DataQuery) => void;
onRunQuery: () => void;
} }
interface State { interface State {
...@@ -72,15 +69,20 @@ export class QueryEditorRow extends PureComponent<Props, State> { ...@@ -72,15 +69,20 @@ export class QueryEditorRow extends PureComponent<Props, State> {
} }
getAngularQueryComponentScope(): AngularQueryComponentScope { getAngularQueryComponentScope(): AngularQueryComponentScope {
const { panel, query, dashboard } = this.props; const { query, onChange } = this.props;
const { datasource } = this.state; const { datasource } = this.state;
const panel = new PanelModel({});
const dashboard = {} as DashboardModel;
return { return {
datasource: datasource, datasource: datasource,
target: query, target: query,
panel: panel, panel: panel,
dashboard: dashboard!, dashboard: dashboard,
refresh: () => panel.refresh(), refresh: () => {
// Old angular editors modify the query model and just call refresh
onChange(query);
},
render: () => () => console.log('legacy render function called, it does nothing'), render: () => () => console.log('legacy render function called, it does nothing'),
events: panel.events, events: panel.events,
range: getTimeSrv().timeRange(), range: getTimeSrv().timeRange(),
...@@ -88,12 +90,12 @@ export class QueryEditorRow extends PureComponent<Props, State> { ...@@ -88,12 +90,12 @@ export class QueryEditorRow extends PureComponent<Props, State> {
} }
async loadDatasource() { async loadDatasource() {
const { query, panel, dataSourceValue } = this.props; const { query, dataSourceValue } = this.props;
const dataSourceSrv = getDatasourceSrv(); const dataSourceSrv = getDatasourceSrv();
let datasource; let datasource;
try { try {
const datasourceName = dataSourceValue || query.datasource || panel.datasource; const datasourceName = dataSourceValue || query.datasource;
datasource = await dataSourceSrv.get(datasourceName); datasource = await dataSourceSrv.get(datasourceName);
} catch (error) { } catch (error) {
datasource = await dataSourceSrv.get(); datasource = await dataSourceSrv.get();
...@@ -108,7 +110,7 @@ export class QueryEditorRow extends PureComponent<Props, State> { ...@@ -108,7 +110,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
componentDidUpdate(prevProps: Props) { componentDidUpdate(prevProps: Props) {
const { loadedDataSourceValue } = this.state; const { loadedDataSourceValue } = this.state;
const { data, query, panel } = this.props; const { data, query } = this.props;
if (data !== prevProps.data) { if (data !== prevProps.data) {
this.setState({ data: filterPanelDataToQuery(data, query.refId) }); this.setState({ data: filterPanelDataToQuery(data, query.refId) });
...@@ -118,7 +120,7 @@ export class QueryEditorRow extends PureComponent<Props, State> { ...@@ -118,7 +120,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
} }
if (this.angularQueryEditor) { if (this.angularQueryEditor) {
notifyAngularQueryEditorsOfData(panel, data, this.angularQueryEditor); notifyAngularQueryEditorsOfData(this.angularScope?.panel!, data, this.angularQueryEditor);
} }
} }
...@@ -161,7 +163,7 @@ export class QueryEditorRow extends PureComponent<Props, State> { ...@@ -161,7 +163,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
}; };
onRunQuery = () => { onRunQuery = () => {
this.props.panel.refresh(); this.props.onRunQuery();
}; };
renderPluginEditor = () => { renderPluginEditor = () => {
......
...@@ -2,11 +2,8 @@ ...@@ -2,11 +2,8 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
// Types // Types
import { PanelModel } from '../../dashboard/state/PanelModel';
import { DataQuery, PanelData, DataSourceSelectItem } from '@grafana/data'; import { DataQuery, PanelData, DataSourceSelectItem } from '@grafana/data';
import { DashboardModel } from '../../dashboard/state/DashboardModel';
import { QueryEditorRow } from './QueryEditorRow'; import { QueryEditorRow } from './QueryEditorRow';
import { addQuery } from 'app/core/utils/query';
import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
interface Props { interface Props {
...@@ -15,35 +12,21 @@ interface Props { ...@@ -15,35 +12,21 @@ interface Props {
datasource: DataSourceSelectItem; datasource: DataSourceSelectItem;
// Query editing // Query editing
onChangeQueries: (queries: DataQuery[]) => void; onQueriesChange: (queries: DataQuery[]) => void;
onScrollBottom: () => void; onAddQuery: (query: DataQuery) => void;
onRunQueries: () => void;
// Dashboard Configs
panel: PanelModel;
dashboard?: DashboardModel;
// Query Response Data // Query Response Data
data: PanelData; data: PanelData;
} }
export class QueryEditorRows extends PureComponent<Props> { export class QueryEditorRows extends PureComponent<Props> {
onAddQuery = (query?: Partial<DataQuery>) => {
const { queries, onChangeQueries } = this.props;
onChangeQueries(addQuery(queries, query));
this.props.onScrollBottom();
};
onRemoveQuery = (query: DataQuery) => { onRemoveQuery = (query: DataQuery) => {
const { queries, onChangeQueries, panel } = this.props; this.props.onQueriesChange(this.props.queries.filter(item => item !== query));
const removed = queries.filter(q => {
return q !== query;
});
onChangeQueries(removed);
panel.refresh();
}; };
onChangeQuery(query: DataQuery, index: number) { onChangeQuery(query: DataQuery, index: number) {
const { queries, onChangeQueries } = this.props; const { queries, onQueriesChange } = this.props;
const old = queries[index]; const old = queries[index];
...@@ -54,7 +37,7 @@ export class QueryEditorRows extends PureComponent<Props> { ...@@ -54,7 +37,7 @@ export class QueryEditorRows extends PureComponent<Props> {
} }
// update query in array // update query in array
onChangeQueries( onQueriesChange(
queries.map((item, itemIndex) => { queries.map((item, itemIndex) => {
if (itemIndex === index) { if (itemIndex === index) {
return query; return query;
...@@ -65,7 +48,7 @@ export class QueryEditorRows extends PureComponent<Props> { ...@@ -65,7 +48,7 @@ export class QueryEditorRows extends PureComponent<Props> {
} }
onDragEnd = (result: DropResult) => { onDragEnd = (result: DropResult) => {
const { queries, onChangeQueries, panel } = this.props; const { queries, onQueriesChange } = this.props;
if (!result || !result.destination) { if (!result || !result.destination) {
return; return;
...@@ -80,12 +63,12 @@ export class QueryEditorRows extends PureComponent<Props> { ...@@ -80,12 +63,12 @@ export class QueryEditorRows extends PureComponent<Props> {
const update = Array.from(queries); const update = Array.from(queries);
const [removed] = update.splice(startIndex, 1); const [removed] = update.splice(startIndex, 1);
update.splice(endIndex, 0, removed); update.splice(endIndex, 0, removed);
onChangeQueries(update); onQueriesChange(update);
panel.refresh();
}; };
render() { render() {
const { props } = this; const { props } = this;
return ( return (
<DragDropContext onDragEnd={this.onDragEnd}> <DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="transformations-list" direction="vertical"> <Droppable droppableId="transformations-list" direction="vertical">
...@@ -98,13 +81,12 @@ export class QueryEditorRows extends PureComponent<Props> { ...@@ -98,13 +81,12 @@ export class QueryEditorRows extends PureComponent<Props> {
id={query.refId} id={query.refId}
index={index} index={index}
key={query.refId} key={query.refId}
panel={props.panel}
dashboard={props.dashboard}
data={props.data} data={props.data}
query={query} query={query}
onChange={query => this.onChangeQuery(query, index)} onChange={query => this.onChangeQuery(query, index)}
onRemoveQuery={this.onRemoveQuery} onRemoveQuery={this.onRemoveQuery}
onAddQuery={this.onAddQuery} onAddQuery={this.props.onAddQuery}
onRunQuery={this.props.onRunQueries}
inMixedMode={props.datasource.meta.mixed} inMixedMode={props.datasource.meta.mixed}
/> />
))} ))}
......
...@@ -34,7 +34,7 @@ export interface QueryRunnerOptions< ...@@ -34,7 +34,7 @@ export interface QueryRunnerOptions<
> { > {
datasource: string | DataSourceApi<TQuery, TOptions> | null; datasource: string | DataSourceApi<TQuery, TOptions> | null;
queries: TQuery[]; queries: TQuery[];
panelId: number; panelId?: number;
dashboardId?: number; dashboardId?: number;
timezone: TimeZone; timezone: TimeZone;
timeRange: TimeRange; timeRange: TimeRange;
......
import { EventBusExtended } from '@grafana/data';
export interface PanelModelForLegacyQueryEditors {
events: EventBusExtended;
}
import React, { FC, useState } from 'react'; import {
import { PanelModel } from '../dashboard/state'; ApplyFieldOverrideOptions,
import { QueriesTab } from '../query/components/QueriesTab'; DataQuery,
DataSourceSelectItem,
DataTransformerConfig,
dateMath,
FieldColorModeId,
PanelData,
} from '@grafana/data';
import { GraphNG, Table } from '@grafana/ui';
import { config } from 'app/core/config';
import React, { FC, useMemo, useState } from 'react';
import { useObservable } from 'react-use';
import { QueryGroup } from '../query/components/QueryGroup';
import { QueryGroupOptions } from '../query/components/QueryGroupOptions';
import { PanelQueryRunner } from '../query/state/PanelQueryRunner';
interface State { interface State {
panel: PanelModel; queries: DataQuery[];
queryRunner: PanelQueryRunner;
dataSourceName: string | null;
queryOptions: QueryGroupOptions;
data?: PanelData;
} }
export const TestStuffPage: FC = () => { export const TestStuffPage: FC = () => {
const [state] = useState<State>(getDefaultState()); const [state, setState] = useState<State>(getDefaultState());
const { queryOptions, queryRunner, queries, dataSourceName } = state;
const onDataSourceChange = (ds: DataSourceSelectItem, queries: DataQuery[]) => {
setState({
...state,
dataSourceName: ds.value,
queries: queries,
});
};
const onRunQueries = () => {
const timeRange = { from: 'now-1h', to: 'now' };
queryRunner.run({
queries,
timezone: 'browser',
datasource: dataSourceName,
timeRange: { from: dateMath.parse(timeRange.from)!, to: dateMath.parse(timeRange.to)!, raw: timeRange },
maxDataPoints: queryOptions.maxDataPoints ?? 100,
minInterval: queryOptions.minInterval,
});
};
const onQueriesChange = (queries: DataQuery[]) => {
setState({ ...state, queries: queries });
};
const onQueryOptionsChange = (queryOptions: QueryGroupOptions) => {
setState({ ...state, queryOptions });
};
/**
* Subscribe to data
*/
const observable = useMemo(() => queryRunner.getData({ withFieldConfig: true, withTransforms: true }), []);
const data = useObservable(observable);
return ( return (
<div style={{ padding: '50px', height: '100%', flexGrow: 1 }} className="page-scrollbar-wrapper"> <div style={{ padding: '30px 50px' }} className="page-scrollbar-wrapper">
<h2>Hello</h2> <h3>New page</h3>
<div>
<QueryGroup
options={queryOptions}
dataSourceName={dataSourceName}
queryRunner={queryRunner}
queries={queries}
onDataSourceChange={onDataSourceChange}
onRunQueries={onRunQueries}
onQueriesChange={onQueriesChange}
onOptionsChange={onQueryOptionsChange}
/>
</div>
<QueriesTab panel={state.panel} /> {data && (
<div style={{ padding: '16px' }}>
<GraphNG width={1200} height={300} data={data.series} timeRange={data.timeRange} timeZone="browser" />
<hr></hr>
<Table data={data.series[0]} width={1200} height={300} />
</div>
)}
</div> </div>
); );
}; };
export function getDefaultState(): State { export function getDefaultState(): State {
const panel = new PanelModel({ const options: ApplyFieldOverrideOptions = {
datasource: 'gdev-testdata', fieldConfig: {
id: 10, defaults: {
targets: [], color: {
}); mode: FieldColorModeId.PaletteClassic,
},
},
overrides: [],
},
replaceVariables: (v: string) => v,
theme: config.theme,
};
const dataConfig = {
getTransformations: () => [] as DataTransformerConfig[],
getFieldOverrideOptions: () => options,
};
return { return {
panel, queries: [],
dataSourceName: 'gdev-testdata',
queryRunner: new PanelQueryRunner(dataConfig),
queryOptions: {
maxDataPoints: 100,
},
}; };
} }
......
...@@ -28,9 +28,9 @@ function getQueryDisplayText(query: DataQuery): string { ...@@ -28,9 +28,9 @@ function getQueryDisplayText(query: DataQuery): string {
} }
interface Props { interface Props {
panel: PanelModel; queries: DataQuery[];
panelData: PanelData; panelData: PanelData;
onChange: (query: DashboardQuery) => void; onChange: (queries: DataQuery[]) => void;
} }
type State = { type State = {
...@@ -48,8 +48,7 @@ export class DashboardQueryEditor extends PureComponent<Props, State> { ...@@ -48,8 +48,7 @@ export class DashboardQueryEditor extends PureComponent<Props, State> {
} }
getQuery(): DashboardQuery { getQuery(): DashboardQuery {
const { panel } = this.props; return this.props.queries[0] as DashboardQuery;
return panel.targets[0] as DashboardQuery;
} }
async componentDidMount() { async componentDidMount() {
...@@ -57,10 +56,14 @@ export class DashboardQueryEditor extends PureComponent<Props, State> { ...@@ -57,10 +56,14 @@ export class DashboardQueryEditor extends PureComponent<Props, State> {
} }
async componentDidUpdate(prevProps: Props) { async componentDidUpdate(prevProps: Props) {
const { panelData } = this.props; const { panelData, queries } = this.props;
if (queries.length < 0) {
return;
}
if (!prevProps || prevProps.panelData !== panelData) { if (!prevProps || prevProps.panelData !== panelData) {
const query = this.props.panel.targets[0] as DashboardQuery; const query = queries[0] as DashboardQuery;
const defaultDS = await getDatasourceSrv().get(); const defaultDS = await getDatasourceSrv().get();
const dashboard = getDashboardSrv().getCurrent(); const dashboard = getDashboardSrv().getCurrent();
const panel = dashboard.getPanelById(query.panelId ?? -124134); const panel = dashboard.getPanelById(query.panelId ?? -124134);
...@@ -97,10 +100,7 @@ export class DashboardQueryEditor extends PureComponent<Props, State> { ...@@ -97,10 +100,7 @@ export class DashboardQueryEditor extends PureComponent<Props, State> {
const { onChange } = this.props; const { onChange } = this.props;
const query = this.getQuery(); const query = this.getQuery();
query.panelId = id; query.panelId = id;
onChange(query); onChange([query]);
// Update the
this.props.panel.refresh();
}; };
renderQueryData(editURL: string) { renderQueryData(editURL: string) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment