mirror of
https://github.com/grafana/grafana.git
synced 2025-12-21 12:04:45 +08:00
Compare commits
5 Commits
zoltan/pos
...
haris/dash
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44af4449a1 | ||
|
|
a5242dfaac | ||
|
|
4a5530c32f | ||
|
|
e0f210d8c4 | ||
|
|
d1d8e0b30f |
@@ -2136,11 +2136,6 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"public/app/features/dashboard-scene/sharing/ShareExportTab.tsx": {
|
||||
"no-restricted-syntax": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/dashboard-scene/sharing/ShareLinkTab.tsx": {
|
||||
"no-restricted-syntax": {
|
||||
"count": 4
|
||||
@@ -2942,11 +2937,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/manage-dashboards/components/ImportDashboardOverview.tsx": {
|
||||
"react-prefer-function-component/react-prefer-function-component": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/manage-dashboards/state/actions.ts": {
|
||||
"@typescript-eslint/consistent-type-assertions": {
|
||||
"count": 1
|
||||
|
||||
@@ -19,7 +19,6 @@ interface Props {
|
||||
dashboardJson: AsyncState<{
|
||||
json: Dashboard | DashboardJson | DashboardV2Spec | ExportableResource | { error: unknown };
|
||||
hasLibraryPanels?: boolean;
|
||||
initialSaveModelVersion: 'v1' | 'v2';
|
||||
}>;
|
||||
isSharingExternally: boolean;
|
||||
exportMode: ExportMode;
|
||||
@@ -41,13 +40,11 @@ export function ResourceExport({
|
||||
onViewYAML,
|
||||
}: Props) {
|
||||
const hasLibraryPanels = dashboardJson.value?.hasLibraryPanels;
|
||||
const initialSaveModelVersion = dashboardJson.value?.initialSaveModelVersion;
|
||||
const isV2Dashboard =
|
||||
dashboardJson.value?.json && 'spec' in dashboardJson.value.json && 'elements' in dashboardJson.value.json.spec;
|
||||
const showV2LibPanelAlert = isV2Dashboard && isSharingExternally && hasLibraryPanels;
|
||||
|
||||
const switchExportLabel =
|
||||
exportMode === ExportMode.V2Resource
|
||||
const switchExportLabel = isV2Dashboard
|
||||
? t('export.json.export-remove-ds-refs', 'Remove deployment details')
|
||||
: t('share-modal.export.share-externally-label', `Export for sharing externally`);
|
||||
const switchExportModeLabel = t('export.json.export-mode', 'Model');
|
||||
@@ -56,34 +53,14 @@ export function ResourceExport({
|
||||
return (
|
||||
<Stack gap={2} direction="column">
|
||||
<Stack gap={1} direction="column">
|
||||
{initialSaveModelVersion === 'v1' && (
|
||||
<Stack alignItems="center">
|
||||
<Label>{switchExportModeLabel}</Label>
|
||||
<RadioButtonGroup
|
||||
options={[
|
||||
{ label: t('dashboard-scene.resource-export.label.classic', 'Classic'), value: ExportMode.Classic },
|
||||
{
|
||||
label: t('dashboard-scene.resource-export.label.v1-resource', 'V1 Resource'),
|
||||
value: ExportMode.V1Resource,
|
||||
},
|
||||
{
|
||||
label: t('dashboard-scene.resource-export.label.v2-resource', 'V2 Resource'),
|
||||
value: ExportMode.V2Resource,
|
||||
},
|
||||
]}
|
||||
value={exportMode}
|
||||
onChange={(value) => onExportModeChange(value)}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
{initialSaveModelVersion === 'v2' && (
|
||||
{!isV2Dashboard && (
|
||||
<Stack alignItems="center">
|
||||
<Label>{switchExportModeLabel}</Label>
|
||||
<RadioButtonGroup
|
||||
options={[
|
||||
{
|
||||
label: t('dashboard-scene.resource-export.label.v2-resource', 'V2 Resource'),
|
||||
value: ExportMode.V2Resource,
|
||||
label: t('dashboard-scene.resource-export.label.classic', 'Classic'),
|
||||
value: ExportMode.Classic,
|
||||
},
|
||||
{
|
||||
label: t('dashboard-scene.resource-export.label.v1-resource', 'V1 Resource'),
|
||||
@@ -108,9 +85,7 @@ export function ResourceExport({
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
{(isV2Dashboard ||
|
||||
exportMode === ExportMode.Classic ||
|
||||
(initialSaveModelVersion === 'v2' && exportMode === ExportMode.V1Resource)) && (
|
||||
{exportMode !== ExportMode.V1Resource && (
|
||||
<Stack gap={1} alignItems="start">
|
||||
<Label>{switchExportLabel}</Label>
|
||||
<Switch
|
||||
|
||||
@@ -113,9 +113,6 @@ describe('ShareExportTab', () => {
|
||||
// Should call transformSceneToV1 (not transform V2→V1)
|
||||
expect(transformSceneToV1Spy).toHaveBeenCalled();
|
||||
expect(transformV2ToV1Spy).not.toHaveBeenCalled();
|
||||
|
||||
// Should report correct initial version
|
||||
expect(result.initialSaveModelVersion).toBe('v1');
|
||||
});
|
||||
|
||||
// If V2 dashboard → V1 Resource should auto-transform with V1 apiVersion
|
||||
@@ -136,9 +133,6 @@ describe('ShareExportTab', () => {
|
||||
// Should auto-transform V2→V1
|
||||
expect(transformSceneToV2Spy).toHaveBeenCalled(); // Get V2 spec first
|
||||
expect(transformV2ToV1Spy).toHaveBeenCalled(); // Then transform to V1
|
||||
|
||||
// Should report correct initial version
|
||||
expect(result.initialSaveModelVersion).toBe('v2');
|
||||
});
|
||||
|
||||
// If V2 dashboard → V1 Resource with external sharing should transform and apply external sharing
|
||||
@@ -164,9 +158,6 @@ describe('ShareExportTab', () => {
|
||||
|
||||
// Should call makeExportableV1 for external sharing
|
||||
expect(makeExportableV1Spy).toHaveBeenCalled();
|
||||
|
||||
// Should report correct initial version
|
||||
expect(result.initialSaveModelVersion).toBe('v2');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -187,9 +178,6 @@ describe('ShareExportTab', () => {
|
||||
|
||||
// Should not call V2→V1 transformation since source is already V2
|
||||
expect(transformV2ToV1Spy).not.toHaveBeenCalled();
|
||||
|
||||
// Should report correct initial version
|
||||
expect(result.initialSaveModelVersion).toBe('v2');
|
||||
});
|
||||
|
||||
// If V1 dashboard → V2 Resource should detect library panels correctly
|
||||
@@ -201,7 +189,6 @@ describe('ShareExportTab', () => {
|
||||
|
||||
// Should detect library panels from V1 dashboard
|
||||
expect(result.hasLibraryPanels).toBe(true);
|
||||
expect(result.initialSaveModelVersion).toBe('v1');
|
||||
});
|
||||
|
||||
// If V1 dashboard with dashboardNewLayouts disabled → V2 Resource should detect library panels correctly
|
||||
@@ -213,7 +200,6 @@ describe('ShareExportTab', () => {
|
||||
|
||||
// Should detect library panels from V1 dashboard (first branch of the logic)
|
||||
expect(result.hasLibraryPanels).toBe(true);
|
||||
expect(result.initialSaveModelVersion).toBe('v1');
|
||||
});
|
||||
|
||||
// If V1 dashboard without library panels → V2 Resource should return false
|
||||
@@ -225,7 +211,6 @@ describe('ShareExportTab', () => {
|
||||
|
||||
// Should not detect library panels
|
||||
expect(result.hasLibraryPanels).toBe(false);
|
||||
expect(result.initialSaveModelVersion).toBe('v1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -247,7 +232,6 @@ describe('ShareExportTab', () => {
|
||||
|
||||
// Should detect library panels from V2 dashboard elements (second branch of the logic)
|
||||
expect(result.hasLibraryPanels).toBe(true);
|
||||
expect(result.initialSaveModelVersion).toBe('v2');
|
||||
});
|
||||
|
||||
// Test the second branch: V2 dashboard with V1 initial save model
|
||||
@@ -259,7 +243,6 @@ describe('ShareExportTab', () => {
|
||||
|
||||
// Should detect library panels from V2 dashboard elements (second branch of the logic)
|
||||
expect(result.hasLibraryPanels).toBe(true);
|
||||
expect(result.initialSaveModelVersion).toBe('v1');
|
||||
});
|
||||
|
||||
// If V2 dashboard without library panels → V2 Resource should return false
|
||||
@@ -271,7 +254,6 @@ describe('ShareExportTab', () => {
|
||||
|
||||
// Should not detect library panels
|
||||
expect(result.hasLibraryPanels).toBe(false);
|
||||
expect(result.initialSaveModelVersion).toBe('v2');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -294,9 +276,6 @@ describe('ShareExportTab', () => {
|
||||
expect(result.json).not.toHaveProperty('apiVersion');
|
||||
expect(result.json).not.toHaveProperty('kind');
|
||||
expect(result.json).not.toHaveProperty('status');
|
||||
|
||||
// Should report correct initial version
|
||||
expect(result.initialSaveModelVersion).toBe('v1');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -102,28 +102,16 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
public getExportableDashboardJson = async (): Promise<{
|
||||
json: Dashboard | DashboardJson | DashboardV2Spec | ExportableResource | { error: unknown };
|
||||
hasLibraryPanels?: boolean;
|
||||
initialSaveModelVersion: 'v1' | 'v2';
|
||||
}> => {
|
||||
const { isSharingExternally, exportMode } = this.state;
|
||||
|
||||
const scene = getDashboardSceneFor(this);
|
||||
const exportableDashboard = await scene.serializer.makeExportableExternally(scene);
|
||||
const initialSaveModel = scene.getInitialSaveModel();
|
||||
const initialSaveModelVersion = initialSaveModel && isDashboardV2Spec(initialSaveModel) ? 'v2' : 'v1';
|
||||
const origDashboard = scene.serializer.getSaveModel(scene);
|
||||
const exportable = isSharingExternally ? exportableDashboard : origDashboard;
|
||||
const metadata = getMetadata(scene, Boolean(isSharingExternally));
|
||||
|
||||
if (
|
||||
isDashboardV2Spec(origDashboard) &&
|
||||
'elements' in exportable &&
|
||||
initialSaveModelVersion === 'v2' &&
|
||||
exportMode !== ExportMode.V1Resource
|
||||
) {
|
||||
this.setState({
|
||||
exportMode: ExportMode.V2Resource,
|
||||
});
|
||||
|
||||
if (isDashboardV2Spec(origDashboard) && 'elements' in exportable && exportMode !== ExportMode.V1Resource) {
|
||||
// For automatic V2 path, also process library panels when sharing externally
|
||||
let finalSpec = exportable;
|
||||
if (isSharingExternally && isDashboardV2Spec(exportable)) {
|
||||
@@ -132,7 +120,6 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
if ('error' in result) {
|
||||
return {
|
||||
json: { error: result.error },
|
||||
initialSaveModelVersion,
|
||||
hasLibraryPanels: Object.values(origDashboard.elements).some((element) => element.kind === 'LibraryPanel'),
|
||||
};
|
||||
}
|
||||
@@ -147,14 +134,13 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
spec: finalSpec,
|
||||
status: {},
|
||||
},
|
||||
initialSaveModelVersion,
|
||||
hasLibraryPanels: Object.values(origDashboard.elements).some((element) => element.kind === 'LibraryPanel'),
|
||||
};
|
||||
}
|
||||
|
||||
if (exportMode === ExportMode.V1Resource) {
|
||||
// Check if source is V2 and auto-transform to V1
|
||||
if (isDashboardV2Spec(origDashboard) && initialSaveModelVersion === 'v2') {
|
||||
if (isDashboardV2Spec(origDashboard)) {
|
||||
try {
|
||||
const spec = transformSceneToSaveModelSchemaV2(scene);
|
||||
const metadata = getMetadata(scene, Boolean(isSharingExternally));
|
||||
@@ -185,7 +171,6 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
spec: exportableV1,
|
||||
status: {},
|
||||
},
|
||||
initialSaveModelVersion,
|
||||
hasLibraryPanels: hasLibraryPanelsInV1Dashboard(spec1),
|
||||
};
|
||||
} catch (err) {
|
||||
@@ -193,7 +178,6 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
json: {
|
||||
error: `Failed to convert dashboard to v1. ${err}`,
|
||||
},
|
||||
initialSaveModelVersion,
|
||||
hasLibraryPanels: undefined,
|
||||
};
|
||||
}
|
||||
@@ -209,7 +193,6 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
spec,
|
||||
status: {},
|
||||
},
|
||||
initialSaveModelVersion,
|
||||
hasLibraryPanels: hasLibraryPanelsInV1Dashboard(spec),
|
||||
};
|
||||
}
|
||||
@@ -223,7 +206,7 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
// Check if dashboard contains library panels based on dashboard version
|
||||
let hasLibraryPanels = false;
|
||||
// Case: V1 dashboard loaded (with kubernetesDashboards enabled and dashboardNewLayouts disabled), and user explicitly selected V2Resource export mode
|
||||
if (initialSaveModelVersion === 'v1' && !isDashboardV2Spec(origDashboard)) {
|
||||
if (!isDashboardV2Spec(origDashboard)) {
|
||||
hasLibraryPanels = hasLibraryPanelsInV1Dashboard(origDashboard);
|
||||
} else if (isDashboardV2Spec(origDashboard)) {
|
||||
// Case: V2 dashboard (either originally V2 or transformed from V1) being exported as V2Resource
|
||||
@@ -239,35 +222,10 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
spec: exportableV2,
|
||||
status: {},
|
||||
},
|
||||
initialSaveModelVersion,
|
||||
hasLibraryPanels,
|
||||
};
|
||||
}
|
||||
|
||||
// Classic mode
|
||||
// This handles a case when:
|
||||
// 1. dashboardNewLayouts feature toggle is enabled
|
||||
// 2. v1 dashboard is loaded
|
||||
// 3. dashboard hasn't been edited yet - if it was edited, user would be forced to save it in v2 version
|
||||
if (
|
||||
initialSaveModelVersion === 'v1' &&
|
||||
isDashboardV2Spec(origDashboard) &&
|
||||
initialSaveModel &&
|
||||
'panels' in initialSaveModel
|
||||
) {
|
||||
const oldModel = new DashboardModel(initialSaveModel, undefined, {
|
||||
getVariablesFromState: () => {
|
||||
return getVariablesCompatibility(window.__grafanaSceneContext);
|
||||
},
|
||||
});
|
||||
const exportableV1 = isSharingExternally ? await makeExportableV1(oldModel) : initialSaveModel;
|
||||
return {
|
||||
json: exportableV1,
|
||||
hasLibraryPanels: hasLibraryPanelsInV1Dashboard(initialSaveModel),
|
||||
initialSaveModelVersion,
|
||||
};
|
||||
}
|
||||
|
||||
// legacy mode or classic mode when dashboardNewLayouts is disabled
|
||||
// At this point we know that dashboard should be V1 or could have produced an error
|
||||
return {
|
||||
@@ -276,7 +234,6 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
'error' in exportable || !isV1ClassicDashboard(origDashboard)
|
||||
? false
|
||||
: hasLibraryPanelsInV1Dashboard(origDashboard),
|
||||
initialSaveModelVersion,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -297,9 +254,11 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
const extension = isViewingYAML ? 'yaml' : 'json';
|
||||
saveAs(blob, `${title}-${time}.${extension}`);
|
||||
|
||||
const isV2Dashboard = 'spec' in dashboard.json && 'elements' in dashboard.json.spec;
|
||||
|
||||
DashboardInteractions.exportDownloadJsonClicked({
|
||||
externally: isSharingExternally,
|
||||
dashboard_schema_version: dashboard.initialSaveModelVersion,
|
||||
dashboard_schema_version: isV2Dashboard ? 'v2' : 'v1',
|
||||
has_library_panels: Boolean(dashboard.hasLibraryPanels),
|
||||
format: isViewingYAML ? 'yaml' : 'json',
|
||||
action: 'download',
|
||||
@@ -310,9 +269,11 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
|
||||
const dashboard = await this.getExportableDashboardJson();
|
||||
const { isSharingExternally, isViewingYAML, exportMode } = this.state;
|
||||
|
||||
const isV2Dashboard = 'spec' in dashboard.json && 'elements' in dashboard.json.spec;
|
||||
|
||||
DashboardInteractions.exportCopyJsonClicked({
|
||||
externally: isSharingExternally,
|
||||
dashboard_schema_version: dashboard.initialSaveModelVersion,
|
||||
dashboard_schema_version: isV2Dashboard ? 'v2' : 'v1',
|
||||
has_library_panels: Boolean(dashboard.hasLibraryPanels),
|
||||
export_mode: exportMode || 'classic',
|
||||
format: isViewingYAML ? 'yaml' : 'json',
|
||||
@@ -402,7 +363,7 @@ function ShareExportTabRenderer({ model }: SceneComponentProps<ShareExportTab>)
|
||||
/>
|
||||
) : (
|
||||
<Stack gap={2} direction="column">
|
||||
<Field label={exportExternallyTranslation}>
|
||||
<Field noMargin label={exportExternallyTranslation}>
|
||||
<Switch
|
||||
id="share-externally-toggle"
|
||||
value={isSharingExternally}
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { dateTimeFormat } from '@grafana/data';
|
||||
import { DataSourceInstanceSettings, dateTimeFormat, locationUtil, TypedVariableModel } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { locationService, reportInteraction } from '@grafana/runtime';
|
||||
import { locationService, reportInteraction, config } from '@grafana/runtime';
|
||||
import { Panel } from '@grafana/schema/dist/esm/raw/dashboard/x/dashboard_types.gen';
|
||||
import { AnnotationQuery, Dashboard } from '@grafana/schema/dist/esm/veneer/dashboard.types';
|
||||
import { Box, Legend, TextLink } from '@grafana/ui';
|
||||
import { Form } from 'app/core/components/Form/Form';
|
||||
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
|
||||
import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types';
|
||||
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
||||
import { addLibraryPanel } from 'app/features/library-panels/state/api';
|
||||
import { StoreState } from 'app/types/store';
|
||||
|
||||
import { clearLoadedDashboard, importDashboard } from '../state/actions';
|
||||
import { DashboardSource, ImportDashboardDTO } from '../state/reducers';
|
||||
import { DashboardSource, DataSourceInput, ImportDashboardDTO, LibraryPanelInputState } from '../state/reducers';
|
||||
|
||||
import { ImportDashboardForm } from './ImportDashboardForm';
|
||||
|
||||
@@ -45,9 +51,80 @@ class ImportDashboardOverviewUnConnected extends PureComponent<Props, State> {
|
||||
uidReset: false,
|
||||
};
|
||||
|
||||
onSubmit = (form: ImportDashboardDTO) => {
|
||||
onSubmit = async (form: ImportDashboardDTO) => {
|
||||
reportInteraction(IMPORT_FINISHED_EVENT_NAME);
|
||||
|
||||
const { dashboard, inputs, folder } = this.props;
|
||||
|
||||
// when kubernetesDashboard are enabled, we bypass api/dashboard/import
|
||||
// and hit the k8s dashboard API directly
|
||||
if (config.featureToggles.kubernetesDashboards) {
|
||||
// 1. process datasources so the template placeholder is replaced with the actual value user selected
|
||||
|
||||
const annotations = dashboard.annotations.list.map((annotation: AnnotationQuery) => {
|
||||
return processAnnotation(annotation, inputs, form);
|
||||
});
|
||||
|
||||
const panels = dashboard.panels.map((panel: Panel) => {
|
||||
return processPanel(panel, inputs, form);
|
||||
});
|
||||
|
||||
const variables = dashboard.templating.list.map((variable: TypedVariableModel) => {
|
||||
return processVariable(variable, inputs, form);
|
||||
});
|
||||
|
||||
const dashboardWithDataSources: Dashboard = {
|
||||
...dashboard,
|
||||
title: form.title,
|
||||
annotations,
|
||||
panels,
|
||||
templating: {
|
||||
list: variables,
|
||||
},
|
||||
uid: form.uid,
|
||||
};
|
||||
|
||||
const newLibraryPanels = inputs.libraryPanels.filter((lp) => lp.state === LibraryPanelInputState.New);
|
||||
|
||||
// for library panels that don't exist in the instance, we create them by hitting the library panel API
|
||||
for (const lp of newLibraryPanels) {
|
||||
const libPanelWithPanelModel = new PanelModel(lp.model.model);
|
||||
let { scopedVars, ...panelSaveModel } = libPanelWithPanelModel.getSaveModel();
|
||||
panelSaveModel = {
|
||||
libraryPanel: {
|
||||
name: lp.model.name,
|
||||
uid: lp.model.uid,
|
||||
},
|
||||
...panelSaveModel,
|
||||
};
|
||||
|
||||
try {
|
||||
await addLibraryPanel(panelSaveModel, folder.uid);
|
||||
} catch (error) {
|
||||
console.error('Error adding library panel during dashboard import', error);
|
||||
}
|
||||
}
|
||||
|
||||
const dashboardK8SPayload: SaveDashboardCommand<Dashboard> = {
|
||||
dashboard: dashboardWithDataSources,
|
||||
k8s: {
|
||||
annotations: {
|
||||
'grafana.app/folder': form.folder.uid,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// hit v1 API directly
|
||||
const result = await getDashboardAPI('v1').saveDashboard(dashboardK8SPayload);
|
||||
|
||||
if (result.url) {
|
||||
const dashboardUrl = locationUtil.stripBaseFromUrl(result.url);
|
||||
locationService.push(dashboardUrl);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.importDashboard(form);
|
||||
};
|
||||
|
||||
@@ -126,3 +203,116 @@ class ImportDashboardOverviewUnConnected extends PureComponent<Props, State> {
|
||||
|
||||
export const ImportDashboardOverview = connector(ImportDashboardOverviewUnConnected);
|
||||
ImportDashboardOverview.displayName = 'ImportDashboardOverview';
|
||||
|
||||
function hasUid(query: Record<string, unknown> | {}): query is { uid: string } {
|
||||
return 'uid' in query && typeof query['uid'] === 'string';
|
||||
}
|
||||
|
||||
/*
|
||||
Checks whether the templateized uid matches the user prodvided datasource input
|
||||
*/
|
||||
function checkUserInputMatch(
|
||||
templateizedUid: string,
|
||||
datasourceInputs: DataSourceInput[],
|
||||
userDsInputs: DataSourceInstanceSettings[]
|
||||
) {
|
||||
const dsName = templateizedUid.replace(/\$\{(.*)\}/, '$1');
|
||||
const input = datasourceInputs?.find((ds) => ds.name === dsName);
|
||||
const userInput = input && userDsInputs.find((ds) => ds.type === input.pluginId);
|
||||
return userInput;
|
||||
}
|
||||
|
||||
function processAnnotation(
|
||||
annotation: AnnotationQuery,
|
||||
inputs: { dataSources: DataSourceInput[] },
|
||||
form: ImportDashboardDTO
|
||||
): AnnotationQuery {
|
||||
if (annotation.datasource && annotation.datasource.uid && annotation.datasource.uid.startsWith('$')) {
|
||||
const userInput = checkUserInputMatch(annotation.datasource.uid, inputs.dataSources, form.dataSources);
|
||||
if (userInput) {
|
||||
return {
|
||||
...annotation,
|
||||
datasource: {
|
||||
...annotation.datasource,
|
||||
uid: userInput.uid,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return annotation;
|
||||
}
|
||||
|
||||
function processPanel(panel: Panel, inputs: { dataSources: DataSourceInput[] }, form: ImportDashboardDTO): Panel {
|
||||
if (panel.datasource && panel.datasource.uid && panel.datasource.uid.startsWith('$')) {
|
||||
const userInput = checkUserInputMatch(panel.datasource.uid, inputs.dataSources, form.dataSources);
|
||||
|
||||
const queries = panel.targets?.map((target) => {
|
||||
if (target.datasource && hasUid(target.datasource) && target.datasource.uid.startsWith('$')) {
|
||||
const userInput = checkUserInputMatch(target.datasource.uid, inputs.dataSources, form.dataSources);
|
||||
if (userInput) {
|
||||
return {
|
||||
...target,
|
||||
datasource: {
|
||||
...target.datasource,
|
||||
uid: userInput.uid,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return target;
|
||||
});
|
||||
|
||||
if (userInput) {
|
||||
return {
|
||||
...panel,
|
||||
targets: queries,
|
||||
datasource: {
|
||||
...panel.datasource,
|
||||
uid: userInput.uid,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
function processVariable(
|
||||
variable: TypedVariableModel,
|
||||
inputs: { dataSources: DataSourceInput[] },
|
||||
form: ImportDashboardDTO
|
||||
): TypedVariableModel {
|
||||
if (variable.type === 'query') {
|
||||
if (variable.datasource && variable.datasource.uid?.startsWith('$')) {
|
||||
const userInput = checkUserInputMatch(variable.datasource.uid, inputs.dataSources, form.dataSources);
|
||||
if (userInput) {
|
||||
return {
|
||||
...variable,
|
||||
datasource: {
|
||||
...variable.datasource,
|
||||
uid: userInput.uid,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (variable.type === 'datasource') {
|
||||
if (variable.current && variable.current.value && String(variable.current.value).startsWith('$')) {
|
||||
const userInput = checkUserInputMatch(String(variable.current.value), inputs.dataSources, form.dataSources);
|
||||
if (userInput) {
|
||||
return {
|
||||
...variable,
|
||||
current: {
|
||||
selected: variable.current.selected,
|
||||
text: userInput.name,
|
||||
value: userInput.uid,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
@@ -6215,7 +6215,6 @@
|
||||
"classic": "Classic",
|
||||
"json": "JSON",
|
||||
"v1-resource": "V1 Resource",
|
||||
"v2-resource": "V2 Resource",
|
||||
"yaml": "YAML"
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user