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
pull/46335/head
Torkel Ödegaard 3 years ago committed by GitHub
parent a9ee446de4
commit 79e5e5c024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/grafana-data/src/types/annotations.ts
  2. 4
      packages/grafana-data/src/utils/datasource.ts
  3. 2
      public/app/features/alerting/AlertTabCtrl.ts
  4. 2
      public/app/features/alerting/unified/mocks.ts
  5. 17
      public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsEdit.tsx
  6. 2
      public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsList.tsx
  7. 2
      public/app/features/dashboard/components/AnnotationSettings/index.tsx
  8. 4
      public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts
  9. 93
      public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx
  10. 12
      public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.tsx
  11. 24
      public/app/features/dashboard/components/PanelEditor/PanelEditorQueries.tsx
  12. 2
      public/app/features/dashboard/components/ShareModal/ShareSnapshot.tsx
  13. 93
      public/app/features/dashboard/state/DashboardMigrator.test.ts
  14. 95
      public/app/features/dashboard/state/DashboardMigrator.ts
  15. 3
      public/app/features/dashboard/state/DashboardModel.ts
  16. 5
      public/app/features/dashboard/state/PanelModel.test.ts
  17. 11
      public/app/features/dashboard/state/PanelModel.ts
  18. 6
      public/app/features/plugins/datasource_srv.ts
  19. 2
      public/app/features/templating/template_srv.ts
  20. 13
      public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx
  21. 2
      public/app/plugins/datasource/grafana/datasource.ts
  22. 2
      public/app/plugins/datasource/postgres/config_ctrl.ts
  23. 3
      public/app/types/query.ts

@ -9,7 +9,7 @@ import { DataQuery, DataSourceRef } from './query';
* This JSON object is stored in the dashboard json model.
*/
export interface AnnotationQuery<TQuery extends DataQuery = DataQuery> {
datasource?: DataSourceRef | string | null;
datasource?: DataSourceRef | null;
enable: boolean;
name: string;

@ -22,8 +22,8 @@ export function getDataSourceRef(ds: DataSourceInstanceSettings): DataSourceRef
*
* @public
*/
export function isDataSourceRef(ref: DataSourceRef | string | null): ref is DataSourceRef {
return typeof ref === 'object' && (typeof ref?.uid === 'string' || typeof ref?.uid === 'undefined');
export function isDataSourceRef(ref: DataSourceRef | string | null | undefined): ref is DataSourceRef {
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 { getBackendSrv } from '@grafana/runtime';
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 { PanelModel } from 'app/features/dashboard/state';
import { getDefaultCondition } from './getAlertingValidationMessage';

@ -17,7 +17,7 @@ import {
RulerRulesConfigDTO,
} from 'app/types/unified-alerting-dto';
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 {
AlertmanagerAlert,

@ -1,27 +1,22 @@
import React, { useState } from 'react';
import { Checkbox, CollapsableSection, ColorValueEditor, Field, HorizontalGroup, Input } from '@grafana/ui';
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 { useAsync } from 'react-use';
import StandardAnnotationQueryEditor from 'app/features/annotations/components/StandardAnnotationQueryEditor';
import { AngularEditorLoader } from './AngularEditorLoader';
import { selectors } from '@grafana/e2e-selectors';
export const newAnnotation: AnnotationQuery = {
name: 'New annotation',
enable: true,
datasource: null,
iconColor: 'red',
};
type Props = {
editIdx: number;
dashboard: DashboardModel;
};
export const newAnnotationName = 'New annotation';
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(() => {
return getDataSourceSrv().get(annotation.datasource);
@ -44,7 +39,7 @@ export const AnnotationSettingsEdit: React.FC<Props> = ({ editIdx, dashboard })
const onDataSourceChange = (ds: DataSourceInstanceSettings) => {
onUpdate({
...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 (
<div>

@ -50,7 +50,7 @@ export const AnnotationSettingsList: React.FC<Props> = ({ dashboard, onNew, onEd
</td>
)}
<td className="pointer" onClick={() => onEdit(idx)}>
{annotation.datasource || 'Default'}
{annotation.datasource?.uid}
</td>
<td style={{ width: '1%' }}>
{idx !== 0 && (

@ -1,2 +1,2 @@
export { AnnotationSettingsEdit } from './AnnotationSettingsEdit';
export { AnnotationSettingsEdit, newAnnotationName } from './AnnotationSettingsEdit';
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 exporter = new DashboardExporter();
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', () => {
@ -325,7 +325,7 @@ describe('given dashboard with repeated panels', () => {
});
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', () => {

@ -6,79 +6,46 @@ import userEvent from '@testing-library/user-event';
import { selectors } from '@grafana/e2e-selectors';
import { setAngularLoader, setDataSourceSrv } from '@grafana/runtime';
import { AnnotationsSettings } from './AnnotationsSettings';
import { mockDataSource, MockDataSourceSrv } from 'app/features/alerting/unified/mocks';
describe('AnnotationsSettings', () => {
let dashboard: any;
const datasources: Record<string, any> = {
Grafana: {
name: 'Grafana',
meta: {
type: 'datasource',
const dataSources = {
grafana: mockDataSource(
{
name: 'Grafana',
id: 'grafana',
info: {
logos: {
small: 'public/img/icn-datasource.svg',
},
},
},
uid: 'Grafana',
type: 'grafana',
isDefault: true,
},
Testdata: {
{ annotations: true }
),
Testdata: mockDataSource(
{
name: 'Testdata',
id: 4,
meta: {
type: 'datasource',
name: 'TestData',
id: 'testdata',
info: {
logos: {
small: 'public/app/plugins/datasource/testdata/img/testdata.svg',
},
},
uid: 'Testdata',
type: 'testdata',
isDefault: true,
},
},
Prometheus: {
name: 'Prometheus',
id: 33,
meta: {
type: 'datasource',
{ annotations: true }
),
Prometheus: mockDataSource(
{
name: 'Prometheus',
id: 'prometheus',
info: {
logos: {
small: 'public/app/plugins/datasource/prometheus/img/prometheus_logo.svg',
},
},
},
uid: 'Prometheus',
type: 'prometheus',
},
{ annotations: true }
),
};
setDataSourceSrv(new MockDataSourceSrv(dataSources));
const getTableBody = () => screen.getAllByRole('rowgroup')[1];
const getTableBodyRows = () => within(getTableBody()).getAllByRole('row');
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({
load: () => ({
destroy: jest.fn(),
@ -96,7 +63,7 @@ describe('AnnotationsSettings', () => {
list: [
{
builtIn: 1,
datasource: 'Grafana',
datasource: { uid: 'Grafana', type: 'grafana' },
enable: true,
hide: true,
iconColor: 'rgba(0, 211, 255, 1)',
@ -158,7 +125,7 @@ describe('AnnotationsSettings', () => {
...dashboard.annotations.list,
{
builtIn: 0,
datasource: 'Prometheus',
datasource: { uid: 'Prometheus', type: 'prometheus' },
enable: true,
hide: true,
iconColor: 'rgba(0, 211, 255, 1)',
@ -167,7 +134,7 @@ describe('AnnotationsSettings', () => {
},
{
builtIn: 0,
datasource: 'Prometheus',
datasource: { uid: 'Prometheus', type: 'prometheus' },
enable: true,
hide: true,
iconColor: 'rgba(0, 211, 255, 1)',
@ -207,7 +174,7 @@ describe('AnnotationsSettings', () => {
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} />);
userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query')));
@ -224,7 +191,7 @@ describe('AnnotationsSettings', () => {
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);
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 { DashboardModel } from '../../state/DashboardModel';
import { AnnotationSettingsEdit, AnnotationSettingsList } from '../AnnotationSettings';
import { newAnnotation } from '../AnnotationSettings/AnnotationSettingsEdit';
import { AnnotationSettingsEdit, AnnotationSettingsList, newAnnotationName } from '../AnnotationSettings';
import { DashboardSettingsHeader } from './DashboardSettingsHeader';
interface Props {
@ -16,6 +17,13 @@ export const AnnotationsSettings: React.FC<Props> = ({ dashboard }) => {
};
const onNew = () => {
const newAnnotation: AnnotationQuery = {
name: newAnnotationName,
enable: true,
datasource: getDataSourceRef(getDataSourceSrv().getInstanceSettings(null)!),
iconColor: 'red',
};
dashboard.annotations.list = [...dashboard.annotations.list, { ...newAnnotation }];
setEditIdx(dashboard.annotations.list.length - 1);
};

@ -3,7 +3,7 @@ import { QueryGroup } from 'app/features/query/components/QueryGroup';
import { PanelModel } from '../../state';
import { locationService } from '@grafana/runtime';
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';
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 = () => {
this.props.panel.refresh();
};
@ -56,11 +68,9 @@ export class PanelEditorQueries extends PureComponent<Props> {
onOptionsChange = (options: QueryGroupOptions) => {
const { panel } = this.props;
const newDataSourceID = options.dataSource.default ? null : options.dataSource.uid!;
const dataSourceChanged = newDataSourceID !== panel.datasource?.uid;
panel.updateQueries(options);
if (dataSourceChanged) {
if (options.dataSource.uid !== panel.datasource?.uid) {
// trigger queries when changing data source
setTimeout(this.onRunQueries, 10);
}
@ -70,6 +80,12 @@ export class PanelEditorQueries extends PureComponent<Props> {
render() {
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);
return (

@ -127,7 +127,7 @@ export class ShareSnapshot extends PureComponent<Props, State> {
// remove annotation queries
const annotations = dash.annotations.list.filter((annotation) => annotation.enable);
dash.annotations.list = annotations.map((annotation: any) => {
dash.annotations.list = annotations.map((annotation) => {
return {
name: annotation.name,
enable: annotation.enable,

@ -16,6 +16,12 @@ jest.mock('app/core/services/context_srv', () => ({}));
const dataSources = {
prom: mockDataSource({
name: 'prom',
uid: 'prom-uid',
type: 'prometheus',
}),
prom2: mockDataSource({
name: 'prom2',
uid: 'prom2-uid',
type: 'prometheus',
isDefault: true,
}),
@ -186,7 +192,7 @@ describe('DashboardModel', () => {
});
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', () => {
@ -1827,11 +1833,11 @@ describe('DashboardModel', () => {
});
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', () => {
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', () => {
@ -1839,11 +1845,11 @@ describe('DashboardModel', () => {
});
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', () => {
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');
});
});
@ -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[]) {

@ -45,7 +45,7 @@ import { config } from 'app/core/config';
import { plugin as statPanelPlugin } from 'app/plugins/panel/stat/module';
import { plugin as gaugePanelPlugin } from 'app/plugins/panel/gauge/module';
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 { mergeTransformer } from '../../../../../packages/grafana-data/src/transformations/transformers/merge';
import {
@ -55,6 +55,7 @@ import {
} from 'app/plugins/datasource/cloudwatch/migrations';
import { CloudWatchAnnotationQuery, CloudWatchMetricsQuery } from 'app/plugins/datasource/cloudwatch/types';
import { getAllOptionEditors, getAllStandardFieldConfigs } from 'app/core/components/editors/registry';
import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
standardEditorsRegistry.setInit(getAllOptionEditors);
standardFieldConfigEditorRegistry.setInit(getAllStandardFieldConfigs);
@ -65,39 +66,10 @@ export class DashboardMigrator {
constructor(dashboardModel: DashboardModel) {
this.dashboard = dashboardModel;
}
/**
* When changing default datasource which is stored as null Grafana get's into a mixed state where queries have
* data source uid & type set that is different from the now new default
*/
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;
}
}
// for tests to pass
if (!getDataSourceSrv()) {
setDataSourceSrv(new DatasourceSrv());
}
}
@ -105,7 +77,7 @@ export class DashboardMigrator {
let i, j, k, n;
const oldVersion = this.dashboard.schemaVersion;
const panelUpgrades: PanelSchemeUpgradeHandler[] = [];
this.dashboard.schemaVersion = 35;
this.dashboard.schemaVersion = 36;
if (oldVersion === this.dashboard.schemaVersion) {
return;
@ -736,14 +708,14 @@ export class DashboardMigrator {
// Replace datasource name with reference, uid and type
if (oldVersion < 33) {
panelUpgrades.push((panel) => {
panel.datasource = migrateDatasourceNameToRef(panel.datasource);
panel.datasource = migrateDatasourceNameToRef(panel.datasource, { returnDefaultAsNull: true });
if (!panel.targets) {
return panel;
}
for (const target of panel.targets) {
const targetRef = migrateDatasourceNameToRef(target.datasource);
const targetRef = migrateDatasourceNameToRef(target.datasource, { returnDefaultAsNull: true });
if (targetRef != null) {
target.datasource = targetRef;
}
@ -766,6 +738,46 @@ export class DashboardMigrator {
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) {
return;
}
@ -1084,8 +1096,15 @@ function migrateSinglestat(panel: PanelModel) {
return panel;
}
export function migrateDatasourceNameToRef(nameOrRef?: string | DataSourceRef | null): DataSourceRef | null {
if (nameOrRef == null || nameOrRef === 'default') {
interface MigrateDatasourceNameOptions {
returnDefaultAsNull: boolean;
}
export function migrateDatasourceNameToRef(
nameOrRef: string | DataSourceRef | null | undefined,
options: MigrateDatasourceNameOptions
): DataSourceRef | null {
if (options.returnDefaultAsNull && (nameOrRef == null || nameOrRef === 'default')) {
return null;
}

@ -206,7 +206,7 @@ export class DashboardModel implements TimeModel {
}
this.annotations.list.unshift({
datasource: '-- Grafana --',
datasource: { uid: '-- Grafana --', type: 'grafana' },
name: 'Annotations & Alerts',
type: 'dashboard',
iconColor: DEFAULT_ANNOTATION_COLOR,
@ -1071,7 +1071,6 @@ export class DashboardModel implements TimeModel {
private updateSchema(old: any) {
const migrator = new DashboardMigrator(this);
migrator.updateSchema(old);
migrator.syncQueryDataSources();
}
resetOriginalTime() {

@ -196,11 +196,6 @@ describe('PanelModel', () => {
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', () => {
const saveModel = model.getSaveModel();
expect(saveModel.events).toBe(undefined);

@ -121,7 +121,6 @@ const defaults: any = {
defaults: {},
overrides: [],
},
datasource: null,
title: '',
};
@ -284,12 +283,6 @@ export class PanelModel implements DataConfigSource, IPanelModel {
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;
}
@ -443,9 +436,7 @@ export class PanelModel implements DataConfigSource, IPanelModel {
updateQueries(options: QueryGroupOptions) {
const { dataSource } = options;
this.datasource = dataSource.default
? null
: {
this.datasource = {
uid: dataSource.uid,
type: dataSource.type,
};

@ -308,7 +308,7 @@ export class DatasourceSrv implements DataSourceService {
return this.getList({ annotations: true, variables: true }).map((x) => {
return {
name: x.name,
value: x.isDefault ? null : x.name,
value: x.name,
meta: x.meta,
};
});
@ -321,7 +321,7 @@ export class DatasourceSrv implements DataSourceService {
return this.getList({ metrics: true, variables: !options?.skipVariables }).map((x) => {
return {
name: x.name,
value: x.isDefault ? null : x.name,
value: x.name,
meta: x.meta,
};
});
@ -345,5 +345,3 @@ export function variableInterpolation(value: any[]) {
export const getDatasourceSrv = (): DatasourceSrv => {
return getDataSourceService() as DatasourceSrv;
};
export default DatasourceSrv;

@ -103,7 +103,7 @@ export class TemplateSrv implements BaseTemplateSrv {
for (const variable of this.getAdHocVariables()) {
const variableUid = variable.datasource?.uid;
if (variableUid === ds.uid || (variable.datasource == null && ds?.isDefault)) {
if (variableUid === ds.uid) {
filters = filters.concat(variable.filters);
} else if (variableUid?.indexOf('$') === 0) {
if (this.replace(variableUid) === datasourceName) {

@ -6,6 +6,8 @@ import { SHARED_DASHBOARD_QUERY } from './types';
import { DashboardQueryEditor } from './DashboardQueryEditor';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
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.requireActual('app/core/config') as unknown as object),
@ -20,12 +22,11 @@ jest.mock('app/core/config', () => ({
},
}));
jest.mock('app/features/plugins/datasource_srv', () => ({
getDatasourceSrv: () => ({
get: () => Promise.resolve({}),
getInstanceSettings: () => ({}),
}),
}));
setDataSourceSrv(
new MockDataSourceSrv({
test: mockDataSource({ isDefault: true }),
})
);
describe('DashboardQueryEditor', () => {
const mockOnChange = jest.fn();

@ -48,7 +48,7 @@ export class GrafanaDatasource extends DataSourceWithBackend<GrafanaQuery> {
prepareQuery(anno: AnnotationQuery<GrafanaAnnotationQuery>): GrafanaQuery {
let datasource: DataSourceRef | undefined | null = undefined;
if (isString(anno.datasource)) {
const ref = migrateDatasourceNameToRef(anno.datasource);
const ref = migrateDatasourceNameToRef(anno.datasource, { returnDefaultAsNull: false });
if (ref) {
datasource = ref;
}

@ -4,7 +4,7 @@ import {
createResetHandler,
PasswordFieldEnum,
} from '../../../features/datasources/utils/passwordHandlers';
import DatasourceSrv from 'app/features/plugins/datasource_srv';
import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
export class PostgresConfigCtrl {
static templateUrl = 'partials/config.html';

@ -1,8 +1,7 @@
import { DataQuery, DataSourceRef } from '@grafana/data';
import { ExpressionQuery } from '../features/expressions/types';
export interface QueryGroupOptions {
queries: Array<DataQuery | ExpressionQuery>;
queries: DataQuery[];
dataSource: QueryGroupDataSource;
maxDataPoints?: number | null;
minInterval?: string | null;

Loading…
Cancel
Save