Dashboard Export: Fix datasource variable templating in dashboard export (#109764)
* wip investigation * improve * cleanup * betterer * Improve v2 import
This commit is contained in:
@@ -144,7 +144,7 @@ describe('dashboard exporter v1', () => {
|
|||||||
expect(exported.templating.list[0].datasource.uid).toBe('${DS_GFDB}');
|
expect(exported.templating.list[0].datasource.uid).toBe('${DS_GFDB}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('do not expose datasource name and id in a in a template variable of type datasource', async () => {
|
it('templateize datasource uid in a datasource variable that is used in a panel', async () => {
|
||||||
const dashboard: Dashboard = {
|
const dashboard: Dashboard = {
|
||||||
title: 'My dashboard',
|
title: 'My dashboard',
|
||||||
revision: 1,
|
revision: 1,
|
||||||
@@ -159,6 +159,10 @@ describe('dashboard exporter v1', () => {
|
|||||||
type: 'timeseries',
|
type: 'timeseries',
|
||||||
title: 'My panel title',
|
title: 'My panel title',
|
||||||
gridPos: { x: 0, y: 0, w: 1, h: 1 },
|
gridPos: { x: 0, y: 0, w: 1, h: 1 },
|
||||||
|
datasource: {
|
||||||
|
type: 'prometheus',
|
||||||
|
uid: '${ds_var}',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
templating: {
|
templating: {
|
||||||
@@ -172,7 +176,7 @@ describe('dashboard exporter v1', () => {
|
|||||||
hide: 0,
|
hide: 0,
|
||||||
includeAll: false,
|
includeAll: false,
|
||||||
multi: false,
|
multi: false,
|
||||||
name: 'query1',
|
name: 'ds_var',
|
||||||
options: [],
|
options: [],
|
||||||
query: 'prometheus',
|
query: 'prometheus',
|
||||||
refresh: 1,
|
refresh: 1,
|
||||||
@@ -188,7 +192,11 @@ describe('dashboard exporter v1', () => {
|
|||||||
});
|
});
|
||||||
const exported = (await makeExportableV1(dashboardModel)) as DashboardJson;
|
const exported = (await makeExportableV1(dashboardModel)) as DashboardJson;
|
||||||
const value = exported?.templating?.list ? exported?.templating?.list[0].current : '';
|
const value = exported?.templating?.list ? exported?.templating?.list[0].current : '';
|
||||||
expect(value).toEqual({});
|
expect(value).toEqual({
|
||||||
|
selected: true,
|
||||||
|
text: '',
|
||||||
|
value: '${DS_GFDB}',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('replaces datasource ref in library panel', async () => {
|
it('replaces datasource ref in library panel', async () => {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { buildPanelKind } from 'app/features/dashboard/api/ResponseTransformers'
|
|||||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||||
import { PanelModel, GridPos } from 'app/features/dashboard/state/PanelModel';
|
import { PanelModel, GridPos } from 'app/features/dashboard/state/PanelModel';
|
||||||
import { getLibraryPanel } from 'app/features/library-panels/state/api';
|
import { getLibraryPanel } from 'app/features/library-panels/state/api';
|
||||||
import { variableRegex } from 'app/features/variables/utils';
|
import { variableRegexExec } from 'app/features/variables/utils';
|
||||||
import { dispatch } from 'app/store/store';
|
import { dispatch } from 'app/store/store';
|
||||||
|
|
||||||
import { isPanelModelLibraryPanel } from '../../../library-panels/guard';
|
import { isPanelModelLibraryPanel } from '../../../library-panels/guard';
|
||||||
@@ -110,6 +110,8 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
|||||||
variableLookup[variable.name] = variable;
|
variableLookup[variable.name] = variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const datasourceVariableRefNameMap: { [key: string]: string } = {};
|
||||||
|
|
||||||
const templateizeDatasourceUsage = (obj: any, fallback?: DataSourceRef) => {
|
const templateizeDatasourceUsage = (obj: any, fallback?: DataSourceRef) => {
|
||||||
if (obj.datasource === undefined) {
|
if (obj.datasource === undefined) {
|
||||||
obj.datasource = fallback;
|
obj.datasource = fallback;
|
||||||
@@ -120,11 +122,11 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
|||||||
let datasourceVariable: any = null;
|
let datasourceVariable: any = null;
|
||||||
|
|
||||||
const datasourceUid: string | undefined = datasource?.uid;
|
const datasourceUid: string | undefined = datasource?.uid;
|
||||||
const match = datasourceUid && variableRegex.exec(datasourceUid);
|
const match = datasourceUid && variableRegexExec(datasourceUid);
|
||||||
|
let varName: string | undefined;
|
||||||
|
|
||||||
// ignore data source properties that contain a variable
|
|
||||||
if (match) {
|
if (match) {
|
||||||
const varName = match[1] || match[2] || match[4];
|
varName = match[1] || match[2] || match[4];
|
||||||
datasourceVariable = variableLookup[varName];
|
datasourceVariable = variableLookup[varName];
|
||||||
if (datasourceVariable && datasourceVariable.current) {
|
if (datasourceVariable && datasourceVariable.current) {
|
||||||
datasource = datasourceVariable.current.value;
|
datasource = datasourceVariable.current.value;
|
||||||
@@ -146,14 +148,10 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
|||||||
version: ds.meta.info.version || '1.0.0',
|
version: ds.meta.info.version || '1.0.0',
|
||||||
};
|
};
|
||||||
|
|
||||||
// if used via variable we can skip templatizing usage
|
|
||||||
if (datasourceVariable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const libraryPanel = obj.libraryPanel;
|
const libraryPanel = obj.libraryPanel;
|
||||||
const libraryPanelSuffix = !!libraryPanel ? '-for-library-panel' : '';
|
const libraryPanelSuffix = !!libraryPanel ? '-for-library-panel' : '';
|
||||||
let refName = 'DS_' + ds.name.replace(' ', '_').toUpperCase() + libraryPanelSuffix.toUpperCase();
|
let refName = 'DS_' + ds.name.replace(' ', '_').toUpperCase() + libraryPanelSuffix.toUpperCase();
|
||||||
|
const templatedUid = '${' + refName + '}';
|
||||||
|
|
||||||
datasources[refName] = {
|
datasources[refName] = {
|
||||||
name: refName,
|
name: refName,
|
||||||
@@ -174,7 +172,14 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.datasource = { type: ds.meta.id, uid: '${' + refName + '}' };
|
// if it panel or query is relying on a datasource variable
|
||||||
|
// skip templating datasource uid but save the reference so we can set datasource variable's current prop
|
||||||
|
if (datasourceVariable && varName) {
|
||||||
|
datasourceVariableRefNameMap[varName] = '${' + refName + '}';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.datasource = { type: ds.meta.id, uid: templatedUid };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -240,7 +245,16 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
|||||||
variable.refresh =
|
variable.refresh =
|
||||||
variable.refresh !== VariableRefresh.never ? variable.refresh : VariableRefresh.onDashboardLoad;
|
variable.refresh !== VariableRefresh.never ? variable.refresh : VariableRefresh.onDashboardLoad;
|
||||||
} else if (variable.type === 'datasource') {
|
} else if (variable.type === 'datasource') {
|
||||||
variable.current = {};
|
const templateizedUID = datasourceVariableRefNameMap[variable.name];
|
||||||
|
if (templateizedUID) {
|
||||||
|
variable.current = {
|
||||||
|
text: '',
|
||||||
|
value: templateizedUID,
|
||||||
|
selected: true,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
variable.current = {};
|
||||||
|
}
|
||||||
} else if (variable.type === 'adhoc') {
|
} else if (variable.type === 'adhoc') {
|
||||||
await templateizeDatasourceUsage(variable);
|
await templateizeDatasourceUsage(variable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export const ImportDashboardFormV2 = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
label={input.label}
|
label={input.pluginId}
|
||||||
description={input.description}
|
description={input.description}
|
||||||
key={input.pluginId}
|
key={input.pluginId}
|
||||||
invalid={!!errors[dataSourceOption]}
|
invalid={!!errors[dataSourceOption]}
|
||||||
@@ -120,6 +120,7 @@ export const ImportDashboardFormV2 = ({
|
|||||||
[input.pluginId]: {
|
[input.pluginId]: {
|
||||||
uid: ds.uid,
|
uid: ds.uid,
|
||||||
type: ds.type,
|
type: ds.type,
|
||||||
|
name: ds.name,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -98,16 +98,24 @@ export function ImportDashboardOverviewV2() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (variable.kind === 'DatasourceVariable') {
|
} else if (variable.kind === 'DatasourceVariable') {
|
||||||
return {
|
const dsType = variable.spec.pluginId;
|
||||||
...variable,
|
if (dsType) {
|
||||||
spec: {
|
if (form[`datasource-${dsType}`]) {
|
||||||
...variable.spec,
|
const ds = form[`datasource-${dsType}`];
|
||||||
current: {
|
return {
|
||||||
text: '',
|
...variable,
|
||||||
value: '',
|
spec: {
|
||||||
},
|
...variable.spec,
|
||||||
},
|
current: {
|
||||||
};
|
// @ts-ignore
|
||||||
|
text: ds.name,
|
||||||
|
// @ts-ignore
|
||||||
|
value: ds.uid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return variable;
|
return variable;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export const ImportDashboardForm = ({
|
|||||||
const current = watchDataSources ?? [];
|
const current = watchDataSources ?? [];
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
label={input.label}
|
label={input.pluginId}
|
||||||
description={input.description}
|
description={input.description}
|
||||||
key={dataSourceOption}
|
key={dataSourceOption}
|
||||||
invalid={errors.dataSources && !!errors.dataSources[index]}
|
invalid={errors.dataSources && !!errors.dataSources[index]}
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ export async function processV2DatasourceInput(
|
|||||||
dataSourceInput = {
|
dataSourceInput = {
|
||||||
name: datasource.name,
|
name: datasource.name,
|
||||||
label: datasource.name,
|
label: datasource.name,
|
||||||
info: `Select a ${datasource.name} data source`,
|
info: `Select a ${datasource.type} data source`,
|
||||||
value: datasource.uid,
|
value: datasource.uid,
|
||||||
type: InputType.DataSource,
|
type: InputType.DataSource,
|
||||||
pluginId: datasource.meta?.id,
|
pluginId: datasource.meta?.id,
|
||||||
|
|||||||
Reference in New Issue
Block a user