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 { QueriesTab } from 'app/features/query/components/QueriesTab';
import { DashboardModel, PanelModel } from '../../state';
import { QueryGroup } from 'app/features/query/components/QueryGroup';
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 {
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() {
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> {
})}
</TabsBar>
<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.Transform && <TransformationsEditor panel={panel} />}
</TabContent>
......
......@@ -132,8 +132,8 @@ export class PanelModel implements DataConfigSource {
};
fieldConfig: FieldConfigSource;
maxDataPoints?: number;
interval?: string;
maxDataPoints?: number | null;
interval?: string | null;
description?: string;
links?: DataLink[];
transparent: boolean;
......
......@@ -6,9 +6,6 @@ import _ from 'lodash';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
// Types
import { PanelModel } from '../../dashboard/state/PanelModel';
import { ErrorBoundaryAlert, HorizontalGroup } from '@grafana/ui';
import {
DataQuery,
......@@ -25,12 +22,11 @@ import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOp
import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction';
import { DashboardModel } from '../../dashboard/state/DashboardModel';
import { selectors } from '@grafana/e2e-selectors';
import { PanelModel } from 'app/features/dashboard/state';
interface Props {
panel: PanelModel;
data: PanelData;
query: DataQuery;
dashboard?: DashboardModel;
dataSourceValue: string | null;
inMixedMode?: boolean;
id: string;
......@@ -38,6 +34,7 @@ interface Props {
onAddQuery: (query?: DataQuery) => void;
onRemoveQuery: (query: DataQuery) => void;
onChange: (query: DataQuery) => void;
onRunQuery: () => void;
}
interface State {
......@@ -72,15 +69,20 @@ export class QueryEditorRow extends PureComponent<Props, State> {
}
getAngularQueryComponentScope(): AngularQueryComponentScope {
const { panel, query, dashboard } = this.props;
const { query, onChange } = this.props;
const { datasource } = this.state;
const panel = new PanelModel({});
const dashboard = {} as DashboardModel;
return {
datasource: datasource,
target: query,
panel: panel,
dashboard: dashboard!,
refresh: () => panel.refresh(),
dashboard: dashboard,
refresh: () => {
// Old angular editors modify the query model and just call refresh
onChange(query);
},
render: () => () => console.log('legacy render function called, it does nothing'),
events: panel.events,
range: getTimeSrv().timeRange(),
......@@ -88,12 +90,12 @@ export class QueryEditorRow extends PureComponent<Props, State> {
}
async loadDatasource() {
const { query, panel, dataSourceValue } = this.props;
const { query, dataSourceValue } = this.props;
const dataSourceSrv = getDatasourceSrv();
let datasource;
try {
const datasourceName = dataSourceValue || query.datasource || panel.datasource;
const datasourceName = dataSourceValue || query.datasource;
datasource = await dataSourceSrv.get(datasourceName);
} catch (error) {
datasource = await dataSourceSrv.get();
......@@ -108,7 +110,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
componentDidUpdate(prevProps: Props) {
const { loadedDataSourceValue } = this.state;
const { data, query, panel } = this.props;
const { data, query } = this.props;
if (data !== prevProps.data) {
this.setState({ data: filterPanelDataToQuery(data, query.refId) });
......@@ -118,7 +120,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
}
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> {
};
onRunQuery = () => {
this.props.panel.refresh();
this.props.onRunQuery();
};
renderPluginEditor = () => {
......
......@@ -2,11 +2,8 @@
import React, { PureComponent } from 'react';
// Types
import { PanelModel } from '../../dashboard/state/PanelModel';
import { DataQuery, PanelData, DataSourceSelectItem } from '@grafana/data';
import { DashboardModel } from '../../dashboard/state/DashboardModel';
import { QueryEditorRow } from './QueryEditorRow';
import { addQuery } from 'app/core/utils/query';
import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
interface Props {
......@@ -15,35 +12,21 @@ interface Props {
datasource: DataSourceSelectItem;
// Query editing
onChangeQueries: (queries: DataQuery[]) => void;
onScrollBottom: () => void;
// Dashboard Configs
panel: PanelModel;
dashboard?: DashboardModel;
onQueriesChange: (queries: DataQuery[]) => void;
onAddQuery: (query: DataQuery) => void;
onRunQueries: () => void;
// Query Response Data
data: PanelData;
}
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) => {
const { queries, onChangeQueries, panel } = this.props;
const removed = queries.filter(q => {
return q !== query;
});
onChangeQueries(removed);
panel.refresh();
this.props.onQueriesChange(this.props.queries.filter(item => item !== query));
};
onChangeQuery(query: DataQuery, index: number) {
const { queries, onChangeQueries } = this.props;
const { queries, onQueriesChange } = this.props;
const old = queries[index];
......@@ -54,7 +37,7 @@ export class QueryEditorRows extends PureComponent<Props> {
}
// update query in array
onChangeQueries(
onQueriesChange(
queries.map((item, itemIndex) => {
if (itemIndex === index) {
return query;
......@@ -65,7 +48,7 @@ export class QueryEditorRows extends PureComponent<Props> {
}
onDragEnd = (result: DropResult) => {
const { queries, onChangeQueries, panel } = this.props;
const { queries, onQueriesChange } = this.props;
if (!result || !result.destination) {
return;
......@@ -80,12 +63,12 @@ export class QueryEditorRows extends PureComponent<Props> {
const update = Array.from(queries);
const [removed] = update.splice(startIndex, 1);
update.splice(endIndex, 0, removed);
onChangeQueries(update);
panel.refresh();
onQueriesChange(update);
};
render() {
const { props } = this;
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="transformations-list" direction="vertical">
......@@ -98,13 +81,12 @@ export class QueryEditorRows extends PureComponent<Props> {
id={query.refId}
index={index}
key={query.refId}
panel={props.panel}
dashboard={props.dashboard}
data={props.data}
query={query}
onChange={query => this.onChangeQuery(query, index)}
onRemoveQuery={this.onRemoveQuery}
onAddQuery={this.onAddQuery}
onAddQuery={this.props.onAddQuery}
onRunQuery={this.props.onRunQueries}
inMixedMode={props.datasource.meta.mixed}
/>
))}
......
......@@ -34,7 +34,7 @@ export interface QueryRunnerOptions<
> {
datasource: string | DataSourceApi<TQuery, TOptions> | null;
queries: TQuery[];
panelId: number;
panelId?: number;
dashboardId?: number;
timezone: TimeZone;
timeRange: TimeRange;
......
import { EventBusExtended } from '@grafana/data';
export interface PanelModelForLegacyQueryEditors {
events: EventBusExtended;
}
import React, { FC, useState } from 'react';
import { PanelModel } from '../dashboard/state';
import { QueriesTab } from '../query/components/QueriesTab';
import {
ApplyFieldOverrideOptions,
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 {
panel: PanelModel;
queries: DataQuery[];
queryRunner: PanelQueryRunner;
dataSourceName: string | null;
queryOptions: QueryGroupOptions;
data?: PanelData;
}
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 (
<div style={{ padding: '50px', height: '100%', flexGrow: 1 }} className="page-scrollbar-wrapper">
<h2>Hello</h2>
<div style={{ padding: '30px 50px' }} className="page-scrollbar-wrapper">
<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>
);
};
export function getDefaultState(): State {
const panel = new PanelModel({
datasource: 'gdev-testdata',
id: 10,
targets: [],
});
const options: ApplyFieldOverrideOptions = {
fieldConfig: {
defaults: {
color: {
mode: FieldColorModeId.PaletteClassic,
},
},
overrides: [],
},
replaceVariables: (v: string) => v,
theme: config.theme,
};
const dataConfig = {
getTransformations: () => [] as DataTransformerConfig[],
getFieldOverrideOptions: () => options,
};
return {
panel,
queries: [],
dataSourceName: 'gdev-testdata',
queryRunner: new PanelQueryRunner(dataConfig),
queryOptions: {
maxDataPoints: 100,
},
};
}
......
......@@ -28,9 +28,9 @@ function getQueryDisplayText(query: DataQuery): string {
}
interface Props {
panel: PanelModel;
queries: DataQuery[];
panelData: PanelData;
onChange: (query: DashboardQuery) => void;
onChange: (queries: DataQuery[]) => void;
}
type State = {
......@@ -48,8 +48,7 @@ export class DashboardQueryEditor extends PureComponent<Props, State> {
}
getQuery(): DashboardQuery {
const { panel } = this.props;
return panel.targets[0] as DashboardQuery;
return this.props.queries[0] as DashboardQuery;
}
async componentDidMount() {
......@@ -57,10 +56,14 @@ export class DashboardQueryEditor extends PureComponent<Props, State> {
}
async componentDidUpdate(prevProps: Props) {
const { panelData } = this.props;
const { panelData, queries } = this.props;
if (queries.length < 0) {
return;
}
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 dashboard = getDashboardSrv().getCurrent();
const panel = dashboard.getPanelById(query.panelId ?? -124134);
......@@ -97,10 +100,7 @@ export class DashboardQueryEditor extends PureComponent<Props, State> {
const { onChange } = this.props;
const query = this.getQuery();
query.panelId = id;
onChange(query);
// Update the
this.props.panel.refresh();
onChange([query]);
};
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