mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DataSource: Default data source is no longer a persisted state but just the default data source for new panels (#45132)
* PanelEdit: Change the meaning of default data source to be just that the default for new panels * Added migration, and also migration for annotation datasource prop to data source refs * fix * Fixing tests * Fixes to annotation * Fixing unit test
This commit is contained in:
parent
a9ee446de4
commit
79e5e5c024
@ -9,7 +9,7 @@ import { DataQuery, DataSourceRef } from './query';
|
|||||||
* This JSON object is stored in the dashboard json model.
|
* This JSON object is stored in the dashboard json model.
|
||||||
*/
|
*/
|
||||||
export interface AnnotationQuery<TQuery extends DataQuery = DataQuery> {
|
export interface AnnotationQuery<TQuery extends DataQuery = DataQuery> {
|
||||||
datasource?: DataSourceRef | string | null;
|
datasource?: DataSourceRef | null;
|
||||||
|
|
||||||
enable: boolean;
|
enable: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -22,8 +22,8 @@ export function getDataSourceRef(ds: DataSourceInstanceSettings): DataSourceRef
|
|||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function isDataSourceRef(ref: DataSourceRef | string | null): ref is DataSourceRef {
|
export function isDataSourceRef(ref: DataSourceRef | string | null | undefined): ref is DataSourceRef {
|
||||||
return typeof ref === 'object' && (typeof ref?.uid === 'string' || typeof ref?.uid === 'undefined');
|
return typeof ref === 'object' && typeof ref?.uid === 'string';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,7 +7,7 @@ import config from 'app/core/config';
|
|||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import { getBackendSrv } from '@grafana/runtime';
|
import { getBackendSrv } from '@grafana/runtime';
|
||||||
import { DashboardSrv } from '../dashboard/services/DashboardSrv';
|
import { DashboardSrv } from '../dashboard/services/DashboardSrv';
|
||||||
import DatasourceSrv from '../plugins/datasource_srv';
|
import { DatasourceSrv } from '../plugins/datasource_srv';
|
||||||
import { DataQuery, DataSourceApi, rangeUtil } from '@grafana/data';
|
import { DataQuery, DataSourceApi, rangeUtil } from '@grafana/data';
|
||||||
import { PanelModel } from 'app/features/dashboard/state';
|
import { PanelModel } from 'app/features/dashboard/state';
|
||||||
import { getDefaultCondition } from './getAlertingValidationMessage';
|
import { getDefaultCondition } from './getAlertingValidationMessage';
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
RulerRulesConfigDTO,
|
RulerRulesConfigDTO,
|
||||||
} from 'app/types/unified-alerting-dto';
|
} from 'app/types/unified-alerting-dto';
|
||||||
import { AlertingRule, Alert, RecordingRule, RuleGroup, RuleNamespace, CombinedRule } from 'app/types/unified-alerting';
|
import { AlertingRule, Alert, RecordingRule, RuleGroup, RuleNamespace, CombinedRule } from 'app/types/unified-alerting';
|
||||||
import DatasourceSrv from 'app/features/plugins/datasource_srv';
|
import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||||
import { DataSourceSrv, GetDataSourceListFilters, config } from '@grafana/runtime';
|
import { DataSourceSrv, GetDataSourceListFilters, config } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
AlertmanagerAlert,
|
AlertmanagerAlert,
|
||||||
|
@ -1,27 +1,22 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Checkbox, CollapsableSection, ColorValueEditor, Field, HorizontalGroup, Input } from '@grafana/ui';
|
import { Checkbox, CollapsableSection, ColorValueEditor, Field, HorizontalGroup, Input } from '@grafana/ui';
|
||||||
import { DashboardModel } from '../../state/DashboardModel';
|
import { DashboardModel } from '../../state/DashboardModel';
|
||||||
import { AnnotationQuery, DataSourceInstanceSettings } from '@grafana/data';
|
import { AnnotationQuery, DataSourceInstanceSettings, getDataSourceRef } from '@grafana/data';
|
||||||
import { DataSourcePicker, getDataSourceSrv } from '@grafana/runtime';
|
import { DataSourcePicker, getDataSourceSrv } from '@grafana/runtime';
|
||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
import StandardAnnotationQueryEditor from 'app/features/annotations/components/StandardAnnotationQueryEditor';
|
import StandardAnnotationQueryEditor from 'app/features/annotations/components/StandardAnnotationQueryEditor';
|
||||||
import { AngularEditorLoader } from './AngularEditorLoader';
|
import { AngularEditorLoader } from './AngularEditorLoader';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
export const newAnnotation: AnnotationQuery = {
|
|
||||||
name: 'New annotation',
|
|
||||||
enable: true,
|
|
||||||
datasource: null,
|
|
||||||
iconColor: 'red',
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
editIdx: number;
|
editIdx: number;
|
||||||
dashboard: DashboardModel;
|
dashboard: DashboardModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const newAnnotationName = 'New annotation';
|
||||||
|
|
||||||
export const AnnotationSettingsEdit: React.FC<Props> = ({ editIdx, dashboard }) => {
|
export const AnnotationSettingsEdit: React.FC<Props> = ({ editIdx, dashboard }) => {
|
||||||
const [annotation, setAnnotation] = useState(editIdx !== null ? dashboard.annotations.list[editIdx] : newAnnotation);
|
const [annotation, setAnnotation] = useState(dashboard.annotations.list[editIdx]);
|
||||||
|
|
||||||
const { value: ds } = useAsync(() => {
|
const { value: ds } = useAsync(() => {
|
||||||
return getDataSourceSrv().get(annotation.datasource);
|
return getDataSourceSrv().get(annotation.datasource);
|
||||||
@ -44,7 +39,7 @@ export const AnnotationSettingsEdit: React.FC<Props> = ({ editIdx, dashboard })
|
|||||||
const onDataSourceChange = (ds: DataSourceInstanceSettings) => {
|
const onDataSourceChange = (ds: DataSourceInstanceSettings) => {
|
||||||
onUpdate({
|
onUpdate({
|
||||||
...annotation,
|
...annotation,
|
||||||
datasource: ds.name,
|
datasource: getDataSourceRef(ds),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,7 +58,7 @@ export const AnnotationSettingsEdit: React.FC<Props> = ({ editIdx, dashboard })
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const isNewAnnotation = annotation.name === newAnnotation.name;
|
const isNewAnnotation = annotation.name === newAnnotationName;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -50,7 +50,7 @@ export const AnnotationSettingsList: React.FC<Props> = ({ dashboard, onNew, onEd
|
|||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
<td className="pointer" onClick={() => onEdit(idx)}>
|
<td className="pointer" onClick={() => onEdit(idx)}>
|
||||||
{annotation.datasource || 'Default'}
|
{annotation.datasource?.uid}
|
||||||
</td>
|
</td>
|
||||||
<td style={{ width: '1%' }}>
|
<td style={{ width: '1%' }}>
|
||||||
{idx !== 0 && (
|
{idx !== 0 && (
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
export { AnnotationSettingsEdit } from './AnnotationSettingsEdit';
|
export { AnnotationSettingsEdit, newAnnotationName } from './AnnotationSettingsEdit';
|
||||||
export { AnnotationSettingsList } from './AnnotationSettingsList';
|
export { AnnotationSettingsList } from './AnnotationSettingsList';
|
||||||
|
@ -180,7 +180,7 @@ it('handles a default datasource in a template variable', async () => {
|
|||||||
const dashboardModel = new DashboardModel(dashboard, {}, () => dashboard.templating.list);
|
const dashboardModel = new DashboardModel(dashboard, {}, () => dashboard.templating.list);
|
||||||
const exporter = new DashboardExporter();
|
const exporter = new DashboardExporter();
|
||||||
const exported: any = await exporter.makeExportable(dashboardModel);
|
const exported: any = await exporter.makeExportable(dashboardModel);
|
||||||
expect(exported.templating.list[0].datasource).toBe('${DS_GFDB}');
|
expect(exported.templating.list[0].datasource.uid).toBe('${DS_GFDB}');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given dashboard with repeated panels', () => {
|
describe('given dashboard with repeated panels', () => {
|
||||||
@ -325,7 +325,7 @@ describe('given dashboard with repeated panels', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should replace datasource in annotation query', () => {
|
it('should replace datasource in annotation query', () => {
|
||||||
expect(exported.annotations.list[1].datasource).toBe('${DS_GFDB}');
|
expect(exported.annotations.list[1].datasource.uid).toBe('${DS_GFDB}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add datasource as input', () => {
|
it('should add datasource as input', () => {
|
||||||
|
@ -6,79 +6,46 @@ import userEvent from '@testing-library/user-event';
|
|||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { setAngularLoader, setDataSourceSrv } from '@grafana/runtime';
|
import { setAngularLoader, setDataSourceSrv } from '@grafana/runtime';
|
||||||
import { AnnotationsSettings } from './AnnotationsSettings';
|
import { AnnotationsSettings } from './AnnotationsSettings';
|
||||||
|
import { mockDataSource, MockDataSourceSrv } from 'app/features/alerting/unified/mocks';
|
||||||
|
|
||||||
describe('AnnotationsSettings', () => {
|
describe('AnnotationsSettings', () => {
|
||||||
let dashboard: any;
|
let dashboard: any;
|
||||||
const datasources: Record<string, any> = {
|
|
||||||
Grafana: {
|
const dataSources = {
|
||||||
name: 'Grafana',
|
grafana: mockDataSource(
|
||||||
meta: {
|
{
|
||||||
type: 'datasource',
|
|
||||||
name: 'Grafana',
|
name: 'Grafana',
|
||||||
id: 'grafana',
|
uid: 'Grafana',
|
||||||
info: {
|
type: 'grafana',
|
||||||
logos: {
|
isDefault: true,
|
||||||
small: 'public/img/icn-datasource.svg',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
{ annotations: true }
|
||||||
Testdata: {
|
),
|
||||||
name: 'Testdata',
|
Testdata: mockDataSource(
|
||||||
id: 4,
|
{
|
||||||
meta: {
|
name: 'Testdata',
|
||||||
type: 'datasource',
|
uid: 'Testdata',
|
||||||
name: 'TestData',
|
type: 'testdata',
|
||||||
id: 'testdata',
|
isDefault: true,
|
||||||
info: {
|
|
||||||
logos: {
|
|
||||||
small: 'public/app/plugins/datasource/testdata/img/testdata.svg',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
{ annotations: true }
|
||||||
Prometheus: {
|
),
|
||||||
name: 'Prometheus',
|
Prometheus: mockDataSource(
|
||||||
id: 33,
|
{
|
||||||
meta: {
|
|
||||||
type: 'datasource',
|
|
||||||
name: 'Prometheus',
|
name: 'Prometheus',
|
||||||
id: 'prometheus',
|
uid: 'Prometheus',
|
||||||
info: {
|
type: 'prometheus',
|
||||||
logos: {
|
|
||||||
small: 'public/app/plugins/datasource/prometheus/img/prometheus_logo.svg',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
{ annotations: true }
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
||||||
|
|
||||||
const getTableBody = () => screen.getAllByRole('rowgroup')[1];
|
const getTableBody = () => screen.getAllByRole('rowgroup')[1];
|
||||||
const getTableBodyRows = () => within(getTableBody()).getAllByRole('row');
|
const getTableBodyRows = () => within(getTableBody()).getAllByRole('row');
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
setDataSourceSrv({
|
|
||||||
getList() {
|
|
||||||
return Object.values(datasources).map((d) => d);
|
|
||||||
},
|
|
||||||
getInstanceSettings(name: string) {
|
|
||||||
return name
|
|
||||||
? {
|
|
||||||
name: datasources[name].name,
|
|
||||||
value: datasources[name].name,
|
|
||||||
meta: datasources[name].meta,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
name: datasources.Testdata.name,
|
|
||||||
value: datasources.Testdata.name,
|
|
||||||
meta: datasources.Testdata.meta,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
get(name: string) {
|
|
||||||
return Promise.resolve(name ? datasources[name] : datasources.Testdata);
|
|
||||||
},
|
|
||||||
} as any);
|
|
||||||
|
|
||||||
setAngularLoader({
|
setAngularLoader({
|
||||||
load: () => ({
|
load: () => ({
|
||||||
destroy: jest.fn(),
|
destroy: jest.fn(),
|
||||||
@ -96,7 +63,7 @@ describe('AnnotationsSettings', () => {
|
|||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
builtIn: 1,
|
builtIn: 1,
|
||||||
datasource: 'Grafana',
|
datasource: { uid: 'Grafana', type: 'grafana' },
|
||||||
enable: true,
|
enable: true,
|
||||||
hide: true,
|
hide: true,
|
||||||
iconColor: 'rgba(0, 211, 255, 1)',
|
iconColor: 'rgba(0, 211, 255, 1)',
|
||||||
@ -158,7 +125,7 @@ describe('AnnotationsSettings', () => {
|
|||||||
...dashboard.annotations.list,
|
...dashboard.annotations.list,
|
||||||
{
|
{
|
||||||
builtIn: 0,
|
builtIn: 0,
|
||||||
datasource: 'Prometheus',
|
datasource: { uid: 'Prometheus', type: 'prometheus' },
|
||||||
enable: true,
|
enable: true,
|
||||||
hide: true,
|
hide: true,
|
||||||
iconColor: 'rgba(0, 211, 255, 1)',
|
iconColor: 'rgba(0, 211, 255, 1)',
|
||||||
@ -167,7 +134,7 @@ describe('AnnotationsSettings', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
builtIn: 0,
|
builtIn: 0,
|
||||||
datasource: 'Prometheus',
|
datasource: { uid: 'Prometheus', type: 'prometheus' },
|
||||||
enable: true,
|
enable: true,
|
||||||
hide: true,
|
hide: true,
|
||||||
iconColor: 'rgba(0, 211, 255, 1)',
|
iconColor: 'rgba(0, 211, 255, 1)',
|
||||||
@ -207,7 +174,7 @@ describe('AnnotationsSettings', () => {
|
|||||||
expect(within(getTableBodyRows()[2]).queryByText(/annotations & alerts/i)).toBeInTheDocument();
|
expect(within(getTableBodyRows()[2]).queryByText(/annotations & alerts/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders a form for adding/editing annotations', () => {
|
test('it renders a form for adding/editing annotations', async () => {
|
||||||
render(<AnnotationsSettings dashboard={dashboard} />);
|
render(<AnnotationsSettings dashboard={dashboard} />);
|
||||||
|
|
||||||
userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query')));
|
userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query')));
|
||||||
@ -224,7 +191,7 @@ describe('AnnotationsSettings', () => {
|
|||||||
|
|
||||||
userEvent.click(screen.getByText(/testdata/i));
|
userEvent.click(screen.getByText(/testdata/i));
|
||||||
|
|
||||||
expect(screen.queryByText(/prometheus/i)).toBeVisible();
|
expect(await screen.findByText(/Prometheus/i)).toBeVisible();
|
||||||
expect(screen.queryAllByText(/testdata/i)).toHaveLength(2);
|
expect(screen.queryAllByText(/testdata/i)).toHaveLength(2);
|
||||||
|
|
||||||
userEvent.click(screen.getByText(/prometheus/i));
|
userEvent.click(screen.getByText(/prometheus/i));
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import { AnnotationQuery, getDataSourceRef } from '@grafana/data';
|
||||||
|
import { getDataSourceSrv } from '@grafana/runtime';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { DashboardModel } from '../../state/DashboardModel';
|
import { DashboardModel } from '../../state/DashboardModel';
|
||||||
import { AnnotationSettingsEdit, AnnotationSettingsList } from '../AnnotationSettings';
|
import { AnnotationSettingsEdit, AnnotationSettingsList, newAnnotationName } from '../AnnotationSettings';
|
||||||
import { newAnnotation } from '../AnnotationSettings/AnnotationSettingsEdit';
|
|
||||||
import { DashboardSettingsHeader } from './DashboardSettingsHeader';
|
import { DashboardSettingsHeader } from './DashboardSettingsHeader';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -16,6 +17,13 @@ export const AnnotationsSettings: React.FC<Props> = ({ dashboard }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onNew = () => {
|
const onNew = () => {
|
||||||
|
const newAnnotation: AnnotationQuery = {
|
||||||
|
name: newAnnotationName,
|
||||||
|
enable: true,
|
||||||
|
datasource: getDataSourceRef(getDataSourceSrv().getInstanceSettings(null)!),
|
||||||
|
iconColor: 'red',
|
||||||
|
};
|
||||||
|
|
||||||
dashboard.annotations.list = [...dashboard.annotations.list, { ...newAnnotation }];
|
dashboard.annotations.list = [...dashboard.annotations.list, { ...newAnnotation }];
|
||||||
setEditIdx(dashboard.annotations.list.length - 1);
|
setEditIdx(dashboard.annotations.list.length - 1);
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@ import { QueryGroup } from 'app/features/query/components/QueryGroup';
|
|||||||
import { PanelModel } from '../../state';
|
import { PanelModel } from '../../state';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import { QueryGroupDataSource, QueryGroupOptions } from 'app/types';
|
import { QueryGroupDataSource, QueryGroupOptions } from 'app/types';
|
||||||
import { DataQuery } from '@grafana/data';
|
import { DataQuery, getDataSourceRef } from '@grafana/data';
|
||||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -42,6 +42,18 @@ export class PanelEditorQueries extends PureComponent<Props> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
const { panel } = this.props;
|
||||||
|
|
||||||
|
// If the panel model has no datasource property load the default data source property and update the persisted model
|
||||||
|
// Because this part of the panel model is not in redux yet we do a forceUpdate.
|
||||||
|
if (!panel.datasource) {
|
||||||
|
const ds = getDatasourceSrv().getInstanceSettings(null);
|
||||||
|
panel.datasource = getDataSourceRef(ds!);
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onRunQueries = () => {
|
onRunQueries = () => {
|
||||||
this.props.panel.refresh();
|
this.props.panel.refresh();
|
||||||
};
|
};
|
||||||
@ -56,11 +68,9 @@ export class PanelEditorQueries extends PureComponent<Props> {
|
|||||||
onOptionsChange = (options: QueryGroupOptions) => {
|
onOptionsChange = (options: QueryGroupOptions) => {
|
||||||
const { panel } = this.props;
|
const { panel } = this.props;
|
||||||
|
|
||||||
const newDataSourceID = options.dataSource.default ? null : options.dataSource.uid!;
|
|
||||||
const dataSourceChanged = newDataSourceID !== panel.datasource?.uid;
|
|
||||||
panel.updateQueries(options);
|
panel.updateQueries(options);
|
||||||
|
|
||||||
if (dataSourceChanged) {
|
if (options.dataSource.uid !== panel.datasource?.uid) {
|
||||||
// trigger queries when changing data source
|
// trigger queries when changing data source
|
||||||
setTimeout(this.onRunQueries, 10);
|
setTimeout(this.onRunQueries, 10);
|
||||||
}
|
}
|
||||||
@ -70,6 +80,12 @@ export class PanelEditorQueries extends PureComponent<Props> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { panel } = this.props;
|
const { panel } = this.props;
|
||||||
|
|
||||||
|
// If no panel data soruce set, wait with render. Will be set to default in componentDidMount
|
||||||
|
if (!panel.datasource) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const options = this.buildQueryOptions(panel);
|
const options = this.buildQueryOptions(panel);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -127,7 +127,7 @@ export class ShareSnapshot extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
// remove annotation queries
|
// remove annotation queries
|
||||||
const annotations = dash.annotations.list.filter((annotation) => annotation.enable);
|
const annotations = dash.annotations.list.filter((annotation) => annotation.enable);
|
||||||
dash.annotations.list = annotations.map((annotation: any) => {
|
dash.annotations.list = annotations.map((annotation) => {
|
||||||
return {
|
return {
|
||||||
name: annotation.name,
|
name: annotation.name,
|
||||||
enable: annotation.enable,
|
enable: annotation.enable,
|
||||||
|
@ -16,6 +16,12 @@ jest.mock('app/core/services/context_srv', () => ({}));
|
|||||||
const dataSources = {
|
const dataSources = {
|
||||||
prom: mockDataSource({
|
prom: mockDataSource({
|
||||||
name: 'prom',
|
name: 'prom',
|
||||||
|
uid: 'prom-uid',
|
||||||
|
type: 'prometheus',
|
||||||
|
}),
|
||||||
|
prom2: mockDataSource({
|
||||||
|
name: 'prom2',
|
||||||
|
uid: 'prom2-uid',
|
||||||
type: 'prometheus',
|
type: 'prometheus',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
}),
|
}),
|
||||||
@ -186,7 +192,7 @@ describe('DashboardModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('dashboard schema version should be set to latest', () => {
|
it('dashboard schema version should be set to latest', () => {
|
||||||
expect(model.schemaVersion).toBe(35);
|
expect(model.schemaVersion).toBe(36);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('graph thresholds should be migrated', () => {
|
it('graph thresholds should be migrated', () => {
|
||||||
@ -1827,11 +1833,11 @@ describe('DashboardModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update panel datasource props to refs for named data source', () => {
|
it('should update panel datasource props to refs for named data source', () => {
|
||||||
expect(model.panels[0].datasource).toEqual({ type: 'prometheus', uid: 'mock-ds-2' });
|
expect(model.panels[0].datasource).toEqual({ type: 'prometheus', uid: 'prom-uid' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update panel datasource props to refs for default data source', () => {
|
it('should update panel datasource props to refs for default data source', () => {
|
||||||
expect(model.panels[1].datasource).toEqual(null);
|
expect(model.panels[1].datasource).toEqual({ type: 'prometheus', uid: 'prom2-uid' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update panel datasource props to refs for mixed data source', () => {
|
it('should update panel datasource props to refs for mixed data source', () => {
|
||||||
@ -1839,11 +1845,11 @@ describe('DashboardModel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update target datasource props to refs', () => {
|
it('should update target datasource props to refs', () => {
|
||||||
expect(model.panels[2].targets[0].datasource).toEqual({ type: 'prometheus', uid: 'mock-ds-2' });
|
expect(model.panels[2].targets[0].datasource).toEqual({ type: 'prometheus', uid: 'prom-uid' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update datasources in panels collapsed rows', () => {
|
it('should update datasources in panels collapsed rows', () => {
|
||||||
expect(model.panels[3].panels[0].datasource).toEqual({ type: 'prometheus', uid: 'mock-ds-2' });
|
expect(model.panels[3].panels[0].datasource).toEqual({ type: 'prometheus', uid: 'prom-uid' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1869,7 +1875,8 @@ describe('DashboardModel', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not update panel datasource to that of query level ds', () => {
|
it('should use data source on query level as source of truth', () => {
|
||||||
|
expect(model.panels[0].targets[0]?.datasource?.uid).toEqual('prom-not-default-uid');
|
||||||
expect(model.panels[0].datasource?.uid).toEqual('prom-not-default-uid');
|
expect(model.panels[0].datasource?.uid).toEqual('prom-not-default-uid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1910,6 +1917,80 @@ describe('DashboardModel', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when migrating default (null) datasource', () => {
|
||||||
|
let model: DashboardModel;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
model = new DashboardModel({
|
||||||
|
templating: {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
type: 'query',
|
||||||
|
name: 'var',
|
||||||
|
options: [{ text: 'A', value: 'A' }],
|
||||||
|
refresh: 0,
|
||||||
|
datasource: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
annotations: {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
datasource: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
datasource: 'prom',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
panels: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
datasource: null,
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
datasource: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
refId: 'A',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
schemaVersion: 35,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set data source to current default', () => {
|
||||||
|
expect(model.templating.list[0].datasource).toEqual({ type: 'prometheus', uid: 'prom2-uid' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should migrate annotation null query to default ds', () => {
|
||||||
|
expect(model.annotations.list[1].datasource).toEqual({ type: 'prometheus', uid: 'prom2-uid' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should migrate annotation query to refs', () => {
|
||||||
|
expect(model.annotations.list[2].datasource).toEqual({ type: 'prometheus', uid: 'prom-uid' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update panel datasource props to refs for named data source', () => {
|
||||||
|
expect(model.panels[0].datasource).toEqual({ type: 'prometheus', uid: 'prom2-uid' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update panel datasource props even when undefined', () => {
|
||||||
|
expect(model.panels[1].datasource).toEqual({ type: 'prometheus', uid: 'prom2-uid' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update target datasource props to refs', () => {
|
||||||
|
expect(model.panels[0].targets[0].datasource).toEqual({ type: 'prometheus', uid: 'prom2-uid' });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function createRow(options: any, panelDescriptions: any[]) {
|
function createRow(options: any, panelDescriptions: any[]) {
|
||||||
|
@ -45,7 +45,7 @@ import { config } from 'app/core/config';
|
|||||||
import { plugin as statPanelPlugin } from 'app/plugins/panel/stat/module';
|
import { plugin as statPanelPlugin } from 'app/plugins/panel/stat/module';
|
||||||
import { plugin as gaugePanelPlugin } from 'app/plugins/panel/gauge/module';
|
import { plugin as gaugePanelPlugin } from 'app/plugins/panel/gauge/module';
|
||||||
import { AxisPlacement, GraphFieldConfig } from '@grafana/ui';
|
import { AxisPlacement, GraphFieldConfig } from '@grafana/ui';
|
||||||
import { getDataSourceSrv } from '@grafana/runtime';
|
import { getDataSourceSrv, setDataSourceSrv } from '@grafana/runtime';
|
||||||
import { labelsToFieldsTransformer } from '../../../../../packages/grafana-data/src/transformations/transformers/labelsToFields';
|
import { labelsToFieldsTransformer } from '../../../../../packages/grafana-data/src/transformations/transformers/labelsToFields';
|
||||||
import { mergeTransformer } from '../../../../../packages/grafana-data/src/transformations/transformers/merge';
|
import { mergeTransformer } from '../../../../../packages/grafana-data/src/transformations/transformers/merge';
|
||||||
import {
|
import {
|
||||||
@ -55,6 +55,7 @@ import {
|
|||||||
} from 'app/plugins/datasource/cloudwatch/migrations';
|
} from 'app/plugins/datasource/cloudwatch/migrations';
|
||||||
import { CloudWatchAnnotationQuery, CloudWatchMetricsQuery } from 'app/plugins/datasource/cloudwatch/types';
|
import { CloudWatchAnnotationQuery, CloudWatchMetricsQuery } from 'app/plugins/datasource/cloudwatch/types';
|
||||||
import { getAllOptionEditors, getAllStandardFieldConfigs } from 'app/core/components/editors/registry';
|
import { getAllOptionEditors, getAllStandardFieldConfigs } from 'app/core/components/editors/registry';
|
||||||
|
import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||||
|
|
||||||
standardEditorsRegistry.setInit(getAllOptionEditors);
|
standardEditorsRegistry.setInit(getAllOptionEditors);
|
||||||
standardFieldConfigEditorRegistry.setInit(getAllStandardFieldConfigs);
|
standardFieldConfigEditorRegistry.setInit(getAllStandardFieldConfigs);
|
||||||
@ -65,39 +66,10 @@ export class DashboardMigrator {
|
|||||||
|
|
||||||
constructor(dashboardModel: DashboardModel) {
|
constructor(dashboardModel: DashboardModel) {
|
||||||
this.dashboard = dashboardModel;
|
this.dashboard = dashboardModel;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// for tests to pass
|
||||||
* When changing default datasource which is stored as null Grafana get's into a mixed state where queries have
|
if (!getDataSourceSrv()) {
|
||||||
* data source uid & type set that is different from the now new default
|
setDataSourceSrv(new DatasourceSrv());
|
||||||
*/
|
|
||||||
syncQueryDataSources() {
|
|
||||||
const dataSourceSrv = getDataSourceSrv();
|
|
||||||
// This only happens in some unit tests that does not set a DataSourceSrv
|
|
||||||
if (!dataSourceSrv) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultDS = getDataSourceSrv().getInstanceSettings(null);
|
|
||||||
// if default ds is mixed then skip this
|
|
||||||
if (!defaultDS || defaultDS.meta.mixed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const panel of this.dashboard.panels) {
|
|
||||||
// only interested in panels that use default (null) data source
|
|
||||||
if (panel.datasource) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const target of panel.targets) {
|
|
||||||
// If query level data source is different from panel
|
|
||||||
if (target.datasource && target.datasource.uid !== defaultDS?.uid) {
|
|
||||||
// set panel level data source to data source on the query as this is more likely the correct one
|
|
||||||
// But impossible to say, and this changes the behavior of of what default means ahead of the big change to default
|
|
||||||
panel.datasource = target.datasource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +77,7 @@ export class DashboardMigrator {
|
|||||||
let i, j, k, n;
|
let i, j, k, n;
|
||||||
const oldVersion = this.dashboard.schemaVersion;
|
const oldVersion = this.dashboard.schemaVersion;
|
||||||
const panelUpgrades: PanelSchemeUpgradeHandler[] = [];
|
const panelUpgrades: PanelSchemeUpgradeHandler[] = [];
|
||||||
this.dashboard.schemaVersion = 35;
|
this.dashboard.schemaVersion = 36;
|
||||||
|
|
||||||
if (oldVersion === this.dashboard.schemaVersion) {
|
if (oldVersion === this.dashboard.schemaVersion) {
|
||||||
return;
|
return;
|
||||||
@ -736,14 +708,14 @@ export class DashboardMigrator {
|
|||||||
// Replace datasource name with reference, uid and type
|
// Replace datasource name with reference, uid and type
|
||||||
if (oldVersion < 33) {
|
if (oldVersion < 33) {
|
||||||
panelUpgrades.push((panel) => {
|
panelUpgrades.push((panel) => {
|
||||||
panel.datasource = migrateDatasourceNameToRef(panel.datasource);
|
panel.datasource = migrateDatasourceNameToRef(panel.datasource, { returnDefaultAsNull: true });
|
||||||
|
|
||||||
if (!panel.targets) {
|
if (!panel.targets) {
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const target of panel.targets) {
|
for (const target of panel.targets) {
|
||||||
const targetRef = migrateDatasourceNameToRef(target.datasource);
|
const targetRef = migrateDatasourceNameToRef(target.datasource, { returnDefaultAsNull: true });
|
||||||
if (targetRef != null) {
|
if (targetRef != null) {
|
||||||
target.datasource = targetRef;
|
target.datasource = targetRef;
|
||||||
}
|
}
|
||||||
@ -766,6 +738,46 @@ export class DashboardMigrator {
|
|||||||
panelUpgrades.push(ensureXAxisVisibility);
|
panelUpgrades.push(ensureXAxisVisibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 36) {
|
||||||
|
// Migrate datasource to refs in annotations
|
||||||
|
for (const query of this.dashboard.annotations.list) {
|
||||||
|
query.datasource = migrateDatasourceNameToRef(query.datasource, { returnDefaultAsNull: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate datasource: null to current default
|
||||||
|
const defaultDs = getDataSourceSrv().getInstanceSettings(null);
|
||||||
|
if (defaultDs) {
|
||||||
|
for (const variable of this.dashboard.templating.list) {
|
||||||
|
if (variable.type === 'query' && variable.datasource === null) {
|
||||||
|
variable.datasource = getDataSourceRef(defaultDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panelUpgrades.push((panel: PanelModel) => {
|
||||||
|
if (panel.targets) {
|
||||||
|
let panelDataSourceWasDefault = false;
|
||||||
|
if (panel.datasource == null && panel.targets.length > 0) {
|
||||||
|
panel.datasource = getDataSourceRef(defaultDs);
|
||||||
|
panelDataSourceWasDefault = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const target of panel.targets) {
|
||||||
|
if (target.datasource && panelDataSourceWasDefault) {
|
||||||
|
// We can have situations when default ds changed and the panel level data source is different from the queries
|
||||||
|
// In this case we use the query level data source as source for truth
|
||||||
|
panel.datasource = target.datasource as DataSourceRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.datasource === null) {
|
||||||
|
target.datasource = getDataSourceRef(defaultDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return panel;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (panelUpgrades.length === 0) {
|
if (panelUpgrades.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1084,8 +1096,15 @@ function migrateSinglestat(panel: PanelModel) {
|
|||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function migrateDatasourceNameToRef(nameOrRef?: string | DataSourceRef | null): DataSourceRef | null {
|
interface MigrateDatasourceNameOptions {
|
||||||
if (nameOrRef == null || nameOrRef === 'default') {
|
returnDefaultAsNull: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function migrateDatasourceNameToRef(
|
||||||
|
nameOrRef: string | DataSourceRef | null | undefined,
|
||||||
|
options: MigrateDatasourceNameOptions
|
||||||
|
): DataSourceRef | null {
|
||||||
|
if (options.returnDefaultAsNull && (nameOrRef == null || nameOrRef === 'default')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.annotations.list.unshift({
|
this.annotations.list.unshift({
|
||||||
datasource: '-- Grafana --',
|
datasource: { uid: '-- Grafana --', type: 'grafana' },
|
||||||
name: 'Annotations & Alerts',
|
name: 'Annotations & Alerts',
|
||||||
type: 'dashboard',
|
type: 'dashboard',
|
||||||
iconColor: DEFAULT_ANNOTATION_COLOR,
|
iconColor: DEFAULT_ANNOTATION_COLOR,
|
||||||
@ -1071,7 +1071,6 @@ export class DashboardModel implements TimeModel {
|
|||||||
private updateSchema(old: any) {
|
private updateSchema(old: any) {
|
||||||
const migrator = new DashboardMigrator(this);
|
const migrator = new DashboardMigrator(this);
|
||||||
migrator.updateSchema(old);
|
migrator.updateSchema(old);
|
||||||
migrator.syncQueryDataSources();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resetOriginalTime() {
|
resetOriginalTime() {
|
||||||
|
@ -196,11 +196,6 @@ describe('PanelModel', () => {
|
|||||||
expect(saveModel.gridPos).toBe(undefined);
|
expect(saveModel.gridPos).toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getSaveModel should not remove datasource default', () => {
|
|
||||||
const saveModel = model.getSaveModel();
|
|
||||||
expect(saveModel.datasource).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getSaveModel should remove nonPersistedProperties', () => {
|
it('getSaveModel should remove nonPersistedProperties', () => {
|
||||||
const saveModel = model.getSaveModel();
|
const saveModel = model.getSaveModel();
|
||||||
expect(saveModel.events).toBe(undefined);
|
expect(saveModel.events).toBe(undefined);
|
||||||
|
@ -121,7 +121,6 @@ const defaults: any = {
|
|||||||
defaults: {},
|
defaults: {},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
datasource: null,
|
|
||||||
title: '',
|
title: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -284,12 +283,6 @@ export class PanelModel implements DataConfigSource, IPanelModel {
|
|||||||
model[property] = cloneDeep(this[property]);
|
model[property] = cloneDeep(this[property]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.datasource === undefined) {
|
|
||||||
// This is part of defaults as defaults are removed in save model and
|
|
||||||
// this should not be removed in save model as exporter needs to templatize it
|
|
||||||
model.datasource = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,12 +436,10 @@ export class PanelModel implements DataConfigSource, IPanelModel {
|
|||||||
|
|
||||||
updateQueries(options: QueryGroupOptions) {
|
updateQueries(options: QueryGroupOptions) {
|
||||||
const { dataSource } = options;
|
const { dataSource } = options;
|
||||||
this.datasource = dataSource.default
|
this.datasource = {
|
||||||
? null
|
uid: dataSource.uid,
|
||||||
: {
|
type: dataSource.type,
|
||||||
uid: dataSource.uid,
|
};
|
||||||
type: dataSource.type,
|
|
||||||
};
|
|
||||||
this.cacheTimeout = options.cacheTimeout;
|
this.cacheTimeout = options.cacheTimeout;
|
||||||
this.timeFrom = options.timeRange?.from;
|
this.timeFrom = options.timeRange?.from;
|
||||||
this.timeShift = options.timeRange?.shift;
|
this.timeShift = options.timeRange?.shift;
|
||||||
|
@ -308,7 +308,7 @@ export class DatasourceSrv implements DataSourceService {
|
|||||||
return this.getList({ annotations: true, variables: true }).map((x) => {
|
return this.getList({ annotations: true, variables: true }).map((x) => {
|
||||||
return {
|
return {
|
||||||
name: x.name,
|
name: x.name,
|
||||||
value: x.isDefault ? null : x.name,
|
value: x.name,
|
||||||
meta: x.meta,
|
meta: x.meta,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -321,7 +321,7 @@ export class DatasourceSrv implements DataSourceService {
|
|||||||
return this.getList({ metrics: true, variables: !options?.skipVariables }).map((x) => {
|
return this.getList({ metrics: true, variables: !options?.skipVariables }).map((x) => {
|
||||||
return {
|
return {
|
||||||
name: x.name,
|
name: x.name,
|
||||||
value: x.isDefault ? null : x.name,
|
value: x.name,
|
||||||
meta: x.meta,
|
meta: x.meta,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -345,5 +345,3 @@ export function variableInterpolation(value: any[]) {
|
|||||||
export const getDatasourceSrv = (): DatasourceSrv => {
|
export const getDatasourceSrv = (): DatasourceSrv => {
|
||||||
return getDataSourceService() as DatasourceSrv;
|
return getDataSourceService() as DatasourceSrv;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DatasourceSrv;
|
|
||||||
|
@ -103,7 +103,7 @@ export class TemplateSrv implements BaseTemplateSrv {
|
|||||||
for (const variable of this.getAdHocVariables()) {
|
for (const variable of this.getAdHocVariables()) {
|
||||||
const variableUid = variable.datasource?.uid;
|
const variableUid = variable.datasource?.uid;
|
||||||
|
|
||||||
if (variableUid === ds.uid || (variable.datasource == null && ds?.isDefault)) {
|
if (variableUid === ds.uid) {
|
||||||
filters = filters.concat(variable.filters);
|
filters = filters.concat(variable.filters);
|
||||||
} else if (variableUid?.indexOf('$') === 0) {
|
} else if (variableUid?.indexOf('$') === 0) {
|
||||||
if (this.replace(variableUid) === datasourceName) {
|
if (this.replace(variableUid) === datasourceName) {
|
||||||
|
@ -6,6 +6,8 @@ import { SHARED_DASHBOARD_QUERY } from './types';
|
|||||||
import { DashboardQueryEditor } from './DashboardQueryEditor';
|
import { DashboardQueryEditor } from './DashboardQueryEditor';
|
||||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
|
import { setDataSourceSrv } from '@grafana/runtime';
|
||||||
|
import { mockDataSource, MockDataSourceSrv } from 'app/features/alerting/unified/mocks';
|
||||||
|
|
||||||
jest.mock('app/core/config', () => ({
|
jest.mock('app/core/config', () => ({
|
||||||
...(jest.requireActual('app/core/config') as unknown as object),
|
...(jest.requireActual('app/core/config') as unknown as object),
|
||||||
@ -20,12 +22,11 @@ jest.mock('app/core/config', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('app/features/plugins/datasource_srv', () => ({
|
setDataSourceSrv(
|
||||||
getDatasourceSrv: () => ({
|
new MockDataSourceSrv({
|
||||||
get: () => Promise.resolve({}),
|
test: mockDataSource({ isDefault: true }),
|
||||||
getInstanceSettings: () => ({}),
|
})
|
||||||
}),
|
);
|
||||||
}));
|
|
||||||
|
|
||||||
describe('DashboardQueryEditor', () => {
|
describe('DashboardQueryEditor', () => {
|
||||||
const mockOnChange = jest.fn();
|
const mockOnChange = jest.fn();
|
||||||
|
@ -48,7 +48,7 @@ export class GrafanaDatasource extends DataSourceWithBackend<GrafanaQuery> {
|
|||||||
prepareQuery(anno: AnnotationQuery<GrafanaAnnotationQuery>): GrafanaQuery {
|
prepareQuery(anno: AnnotationQuery<GrafanaAnnotationQuery>): GrafanaQuery {
|
||||||
let datasource: DataSourceRef | undefined | null = undefined;
|
let datasource: DataSourceRef | undefined | null = undefined;
|
||||||
if (isString(anno.datasource)) {
|
if (isString(anno.datasource)) {
|
||||||
const ref = migrateDatasourceNameToRef(anno.datasource);
|
const ref = migrateDatasourceNameToRef(anno.datasource, { returnDefaultAsNull: false });
|
||||||
if (ref) {
|
if (ref) {
|
||||||
datasource = ref;
|
datasource = ref;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
createResetHandler,
|
createResetHandler,
|
||||||
PasswordFieldEnum,
|
PasswordFieldEnum,
|
||||||
} from '../../../features/datasources/utils/passwordHandlers';
|
} from '../../../features/datasources/utils/passwordHandlers';
|
||||||
import DatasourceSrv from 'app/features/plugins/datasource_srv';
|
import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||||
|
|
||||||
export class PostgresConfigCtrl {
|
export class PostgresConfigCtrl {
|
||||||
static templateUrl = 'partials/config.html';
|
static templateUrl = 'partials/config.html';
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { DataQuery, DataSourceRef } from '@grafana/data';
|
import { DataQuery, DataSourceRef } from '@grafana/data';
|
||||||
import { ExpressionQuery } from '../features/expressions/types';
|
|
||||||
|
|
||||||
export interface QueryGroupOptions {
|
export interface QueryGroupOptions {
|
||||||
queries: Array<DataQuery | ExpressionQuery>;
|
queries: DataQuery[];
|
||||||
dataSource: QueryGroupDataSource;
|
dataSource: QueryGroupDataSource;
|
||||||
maxDataPoints?: number | null;
|
maxDataPoints?: number | null;
|
||||||
minInterval?: string | null;
|
minInterval?: string | null;
|
||||||
|
Loading…
Reference in New Issue
Block a user