Git sync / Schema v2: Make preview work with v2 dashboards (#103709)

* Git sync / Schema v2: Make preview work with v2 dashboards

* Refactor

* Add tests

* Clean!

* Clean more!

* Update public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts

Co-authored-by: Ivan Ortega Alba <ivanortegaalba@gmail.com>

* Add basic v2 dashboard sample

* Refactor to be instance independent

* Remove commented out code

---------

Co-authored-by: Ivan Ortega Alba <ivanortegaalba@gmail.com>
This commit is contained in:
Dominik Prokop
2025-04-11 17:54:45 +02:00
committed by GitHub
parent 710a5d29e9
commit 40127072e5
7 changed files with 829 additions and 84 deletions
+3
View File
@@ -1530,6 +1530,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
"public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/dashboard-scene/panel-edit/LibraryVizPanelInfo.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
],
+246
View File
@@ -0,0 +1,246 @@
{
"apiVersion": "dashboard.grafana.app/v2alpha1",
"kind": "Dashboard",
"metadata": {
"name": "sample-dash-v2"
},
"spec": {
"annotations": [
{
"kind": "AnnotationQuery",
"spec": {
"builtIn": true,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts"
}
}
],
"cursorSync": "Off",
"description": "",
"editable": true,
"elements": {
"panel-1": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"kind": "grafana-testdata-datasource",
"spec": {}
},
"refId": "A"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 1,
"links": [],
"title": "Simle timeseries",
"vizConfig": {
"kind": "timeseries",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.0.0-pre"
}
}
}
},
"panel-2": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"kind": "grafana-testdata-datasource",
"spec": {}
},
"refId": "A"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 2,
"links": [],
"title": "Simple stat",
"vizConfig": {
"kind": "stat",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "12.0.0-pre"
}
}
}
}
},
"layout": {
"kind": "AutoGridLayout",
"spec": {
"columnWidthMode": "standard",
"items": [
{
"kind": "AutoGridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-2"
}
}
},
{
"kind": "AutoGridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-1"
}
}
}
],
"maxColumnCount": 3,
"rowHeightMode": "standard"
}
},
"links": [],
"liveNow": false,
"preload": false,
"tags": [],
"timeSettings": {
"autoRefresh": "",
"autoRefreshIntervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
"fiscalYearStartMonth": 0,
"from": "now-6h",
"hideTimepicker": false,
"timezone": "browser",
"to": "now"
},
"title": "v2alpha1 dashboard",
"variables": []
}
}
@@ -1004,6 +1004,34 @@ describe('UnifiedDashboardScenePageStateManager', () => {
});
});
describe('Provisioned dashboard', () => {
it('should load a provisioned v1 dashboard', async () => {
const loader = new UnifiedDashboardScenePageStateManager({});
setBackendSrv({
get: () => Promise.resolve(v1ProvisionedDashboardResource),
} as unknown as BackendSrv);
await loader.loadDashboard({ uid: 'blah-blah', route: DashboardRoutes.Provisioning });
expect(loader.state.dashboard).toBeDefined();
expect(loader.state.dashboard!.serializer.initialSaveModel).toEqual(
v1ProvisionedDashboardResource.resource.dryRun.spec
);
});
it('should load a provisioned v2 dashboard', async () => {
const loader = new UnifiedDashboardScenePageStateManager({});
setBackendSrv({
get: () => Promise.resolve(v2ProvisionedDashboardResource),
} as unknown as BackendSrv);
await loader.loadDashboard({ uid: 'blah-blah', route: DashboardRoutes.Provisioning });
expect(loader.state.dashboard).toBeDefined();
expect(loader.state.dashboard!.serializer.initialSaveModel).toEqual(
v2ProvisionedDashboardResource.resource.dryRun.spec
);
});
});
describe('New dashboards', () => {
it('should use v1 manager for new dashboards when dashboardNewLayouts feature toggle is disabled', async () => {
config.featureToggles.dashboardNewLayouts = false;
@@ -1184,3 +1212,446 @@ const customHomeDashboardV2Spec = {
},
},
};
const v1ProvisionedDashboardResource = {
kind: 'ResourceWrapper',
apiVersion: 'provisioning.grafana.app/v0alpha1',
path: 'new-dashboard.json',
ref: 'dashboard/2025-04-11-nkXIe',
hash: '28e6dd34e226ec27e19f9894270290fc105a77b0',
repository: {
type: 'github',
title: 'https://github.com/dprokop/grafana-git-sync-test',
namespace: 'default',
name: 'repository-643e5fb',
},
urls: {
sourceURL: 'https://github.com/dprokop/grafana-git-sync-test/blob/dashboard/2025-04-11-nkXIe/new-dashboard.json',
repositoryURL: 'https://github.com/dprokop/grafana-git-sync-test',
newPullRequestURL:
'https://github.com/dprokop/grafana-git-sync-test/compare/main...dashboard/2025-04-11-nkXIe?quick_pull=1&labels=grafana',
compareURL: 'https://github.com/dprokop/grafana-git-sync-test/compare/main...dashboard/2025-04-11-nkXIe',
},
resource: {
type: {
group: 'dashboard.grafana.app',
version: 'v1alpha1',
kind: 'Dashboard',
resource: 'dashboards',
},
file: {},
existing: {},
action: 'update',
dryRun: {
apiVersion: 'dashboard.grafana.app/v1alpha1',
kind: 'Dashboard',
metadata: {
annotations: {
'grafana.app/managedBy': 'repo',
'grafana.app/managerId': 'repository-643e5fb',
'grafana.app/sourceChecksum': '28e6dd34e226ec27e19f9894270290fc105a77b0',
'grafana.app/sourcePath': 'new-dashboard.json',
},
creationTimestamp: '2025-04-09T07:27:46Z',
generation: 1,
managedFields: [
{
apiVersion: 'dashboard.grafana.app/v1alpha1',
fieldsType: 'FieldsV1',
fieldsV1: {
'f:metadata': {
'f:annotations': {
'.': {},
'f:grafana.app/managedBy': {},
'f:grafana.app/managerId': {},
'f:grafana.app/sourceChecksum': {},
'f:grafana.app/sourcePath': {},
},
},
'f:spec': {
'f:annotations': {
'.': {},
'f:list': {},
},
'f:editable': {},
'f:fiscalYearStartMonth': {},
'f:graphTooltip': {},
'f:links': {},
'f:panels': {},
'f:preload': {},
'f:schemaVersion': {},
'f:tags': {},
'f:templating': {
'.': {},
'f:list': {},
},
'f:time': {
'.': {},
'f:from': {},
'f:to': {},
},
'f:timepicker': {},
'f:timezone': {},
'f:title': {},
},
},
manager: 'grafana',
operation: 'Update',
time: '2025-04-11T10:35:05Z',
},
],
name: 'adsm7zf',
namespace: 'default',
resourceVersion: '1744183666927980',
uid: 'ef523e2b-1e66-4921-b3f9-e7a9b215c988',
},
spec: {
annotations: {
list: [
{
builtIn: 1,
datasource: {
type: 'grafana',
uid: '-- Grafana --',
},
enable: true,
hide: true,
iconColor: 'rgba(0, 211, 255, 1)',
name: 'Annotations & Alerts',
type: 'dashboard',
},
],
},
editable: true,
fiscalYearStartMonth: 0,
graphTooltip: 0,
links: [],
panels: [
{
datasource: {
type: 'grafana-testdata-datasource',
uid: 'PD8C576611E62080A',
},
fieldConfig: {
defaults: {
color: {
mode: 'palette-classic',
},
custom: {
axisBorderShow: false,
axisCenteredZero: false,
axisColorMode: 'text',
axisLabel: '',
axisPlacement: 'auto',
barAlignment: 0,
barWidthFactor: 0.6,
drawStyle: 'line',
fillOpacity: 0,
gradientMode: 'none',
hideFrom: {
legend: false,
tooltip: false,
viz: false,
},
insertNulls: false,
lineInterpolation: 'linear',
lineWidth: 1,
pointSize: 5,
scaleDistribution: {
type: 'linear',
},
showPoints: 'auto',
spanNulls: false,
stacking: {
group: 'A',
mode: 'none',
},
thresholdsStyle: {
mode: 'off',
},
},
mappings: [],
thresholds: {
mode: 'absolute',
steps: [
{
color: 'green',
},
{
color: 'red',
value: 80,
},
],
},
},
overrides: [],
},
gridPos: {
h: 8,
w: 10,
x: 0,
y: 0,
},
id: 1,
options: {
legend: {
calcs: [],
displayMode: 'list',
placement: 'bottom',
showLegend: true,
},
tooltip: {
hideZeros: false,
mode: 'single',
sort: 'none',
},
},
pluginVersion: '12.0.0-pre',
targets: [
{
refId: 'A',
},
],
title: 'New panel',
type: 'timeseries',
},
],
preload: false,
schemaVersion: 41,
tags: [],
templating: {
list: [],
},
time: {
from: 'now-6h',
to: 'now',
},
timepicker: {},
timezone: 'browser',
title: 'New dashboard',
},
status: {},
},
upsert: null,
},
};
const v2ProvisionedDashboardResource = {
kind: 'ResourceWrapper',
apiVersion: 'provisioning.grafana.app/v0alpha1',
path: 'v2dashboards/new-dashboard-2025-04-09-nTqgq.json',
ref: 'dashboard/2025-04-11-FzFKZ',
hash: '2d1c7981d4327f5c75afd920e910f1f82e9be706',
repository: {
type: 'github',
title: 'https://github.com/dprokop/grafana-git-sync-test',
namespace: 'default',
name: 'repository-643e5fb',
},
urls: {
sourceURL:
'https://github.com/dprokop/grafana-git-sync-test/blob/dashboard/2025-04-11-FzFKZ/v2dashboards/new-dashboard-2025-04-09-nTqgq.json',
repositoryURL: 'https://github.com/dprokop/grafana-git-sync-test',
newPullRequestURL:
'https://github.com/dprokop/grafana-git-sync-test/compare/main...dashboard/2025-04-11-FzFKZ?quick_pull=1&labels=grafana',
compareURL: 'https://github.com/dprokop/grafana-git-sync-test/compare/main...dashboard/2025-04-11-FzFKZ',
},
resource: {
type: {
group: 'dashboard.grafana.app',
version: 'v2alpha1',
kind: 'Dashboard',
resource: 'dashboards',
},
file: {},
existing: {},
action: 'update',
dryRun: {
apiVersion: 'dashboard.grafana.app/v2alpha1',
kind: 'Dashboard',
metadata: {
annotations: {
'grafana.app/folder': 'v2dashboards-8mdocprxtfyldpbpod3ayidiitt',
'grafana.app/managedBy': 'repo',
'grafana.app/managerId': 'repository-643e5fb',
'grafana.app/sourceChecksum': '2d1c7981d4327f5c75afd920e910f1f82e9be706',
'grafana.app/sourcePath': 'v2dashboards/new-dashboard-2025-04-09-nTqgq.json',
},
creationTimestamp: '2025-04-09T12:11:20Z',
generation: 1,
name: 'dfeidsuico01kwc',
namespace: 'default',
resourceVersion: '1744200680060000',
uid: '47ed4201-a181-4d1c-b755-17548526d294',
},
spec: {
annotations: [
{
kind: 'AnnotationQuery',
spec: {
builtIn: true,
datasource: {
type: 'grafana',
uid: '-- Grafana --',
},
enable: true,
hide: true,
iconColor: 'rgba(0, 211, 255, 1)',
name: 'Annotations & Alerts',
},
},
],
cursorSync: 'Off',
description: '',
editable: true,
elements: {
'panel-1': {
kind: 'Panel',
spec: {
data: {
kind: 'QueryGroup',
spec: {
queries: [
{
kind: 'PanelQuery',
spec: {
datasource: {
type: 'grafana-testdata-datasource',
uid: 'PD8C576611E62080A',
},
hidden: false,
query: {
kind: 'grafana-testdata-datasource',
spec: {
scenarioId: 'random_walk',
seriesCount: 2,
},
},
refId: 'A',
},
},
],
queryOptions: {},
transformations: [],
},
},
description: '',
id: 1,
links: [],
title: 'New panel',
vizConfig: {
kind: 'timeseries',
spec: {
fieldConfig: {
defaults: {
color: {
mode: 'palette-classic',
},
custom: {
axisBorderShow: false,
axisCenteredZero: false,
axisColorMode: 'text',
axisLabel: '',
axisPlacement: 'auto',
barAlignment: 0,
barWidthFactor: 0.6,
drawStyle: 'line',
fillOpacity: 0,
gradientMode: 'none',
hideFrom: {
legend: false,
tooltip: false,
viz: false,
},
insertNulls: false,
lineInterpolation: 'linear',
lineWidth: 1,
pointSize: 5,
scaleDistribution: {
type: 'linear',
},
showPoints: 'auto',
spanNulls: false,
stacking: {
group: 'A',
mode: 'none',
},
thresholdsStyle: {
mode: 'off',
},
},
thresholds: {
mode: 'absolute',
steps: [
{
color: 'green',
value: 0,
},
{
color: 'red',
value: 80,
},
],
},
},
overrides: [],
},
options: {
legend: {
calcs: [],
displayMode: 'list',
placement: 'bottom',
showLegend: true,
},
tooltip: {
hideZeros: false,
mode: 'single',
sort: 'none',
},
},
pluginVersion: '12.0.0-pre',
},
},
},
},
},
layout: {
kind: 'AutoGridLayout',
spec: {
columnWidthMode: 'standard',
items: [
{
kind: 'AutoGridLayoutItem',
spec: {
element: {
kind: 'ElementReference',
name: 'panel-1',
},
},
},
],
maxColumnCount: 3,
rowHeightMode: 'short',
},
},
links: [],
liveNow: false,
preload: false,
tags: [],
timeSettings: {
autoRefresh: '',
autoRefreshIntervals: ['5s', '10s', '30s', '1m', '5m', '15m', '30m', '1h', '2h', '1d'],
fiscalYearStartMonth: 0,
from: 'now-6h',
hideTimepicker: false,
timezone: 'browser',
to: 'now',
},
title: 'v2 test - auto grid',
variables: [],
},
status: {},
},
upsert: null,
},
};
@@ -4,10 +4,16 @@ import { locationUtil, UrlQueryMap } from '@grafana/data';
import { config, getBackendSrv, isFetchError, locationService } from '@grafana/runtime';
import { sceneGraph } from '@grafana/scenes';
import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
import { BASE_URL } from 'app/api/clients/provisioning/baseAPI';
import { StateManagerBase } from 'app/core/services/StateManagerBase';
import { getMessageFromError, getMessageIdFromError, getStatusFromError } from 'app/core/utils/errors';
import { startMeasure, stopMeasure } from 'app/core/utils/metrics';
import { AnnoKeyFolder } from 'app/features/apiserver/types';
import {
AnnoKeyFolder,
AnnoKeyManagerIdentity,
AnnoKeyManagerKind,
AnnoKeySourcePath,
} from 'app/features/apiserver/types';
import { transformDashboardV2SpecToV1 } from 'app/features/dashboard/api/ResponseTransformers';
import { DashboardVersionError, DashboardWithAccessInfo } from 'app/features/dashboard/api/types';
import { isDashboardV2Resource, isDashboardV2Spec } from 'app/features/dashboard/api/utils';
@@ -15,6 +21,7 @@ import { dashboardLoaderSrv, DashboardLoaderSrvV2 } from 'app/features/dashboard
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { emitDashboardViewEvent } from 'app/features/dashboard/state/analyticsProcessor';
import { trackDashboardSceneLoaded } from 'app/features/dashboard/utils/tracking';
import { ProvisioningPreview } from 'app/features/provisioning/types';
import {
DashboardDataDTO,
DashboardDTO,
@@ -176,6 +183,84 @@ abstract class DashboardScenePageStateManagerBase<T>
}
}
protected async loadProvisioningDashboard(repo: string, path: string): Promise<T> {
const params = new URLSearchParams(window.location.search);
const ref = params.get('ref') ?? undefined; // commit hash or branch
const url = `${BASE_URL}/repositories/${repo}/files/${path}`;
return getBackendSrv()
.get(url, ref ? { ref } : undefined)
.then((v) => {
// Load the results from dryRun
const dryRun = v.resource.dryRun;
if (!dryRun) {
return Promise.reject('failed to read provisioned dashboard');
}
if (!dryRun.apiVersion.startsWith('dashboard.grafana.app')) {
return Promise.reject('unexpected resource type: ' + dryRun.apiVersion);
}
return this.processDashboardFromProvisioning(repo, path, dryRun, {
file: url,
ref: ref,
repo: repo,
});
});
}
private processDashboardFromProvisioning(
repo: string,
path: string,
dryRun: any,
provisioningPreview: ProvisioningPreview
) {
if (dryRun.apiVersion.split('/')[1] === 'v2alpha1') {
return {
...dryRun,
kind: 'DashboardWithAccessInfo',
access: {
canStar: false,
isSnapshot: false,
canShare: false,
// Should come from the repo settings
canDelete: true,
canSave: true,
canEdit: true,
},
};
}
let anno = dryRun.metadata.annotations;
if (!anno) {
dryRun.metadata.annotations = {};
}
anno[AnnoKeyManagerKind] = 'repo';
anno[AnnoKeyManagerIdentity] = repo;
anno[AnnoKeySourcePath] = provisioningPreview.ref ? path + '#' + provisioningPreview.ref : path;
return {
meta: {
canStar: false,
isSnapshot: false,
canShare: false,
// Should come from the repo settings
canDelete: true,
canSave: true,
canEdit: true,
// Includes additional k8s metadata
k8s: dryRun.metadata,
// lookup info
provisioning: provisioningPreview,
},
dashboard: dryRun.spec,
};
}
public async loadDashboard(options: LoadDashboardOptions) {
try {
startMeasure(LOAD_SCENE_MEASUREMENT);
@@ -230,15 +315,15 @@ abstract class DashboardScenePageStateManagerBase<T>
// Handling home dashboard flow separately from regular dashboard flow.
if (options.route === DashboardRoutes.Home) {
return await this.loadHomeDashboard();
} else {
const rsp = await this.fetchDashboard(options);
if (!rsp) {
return null;
}
return this.transformResponseToScene(rsp, options);
}
const rsp = await this.fetchDashboard(options);
if (!rsp) {
return null;
}
return this.transformResponseToScene(rsp, options);
}
public getDashboardFromCache(cacheKey: string): T | null {
@@ -356,9 +441,8 @@ export class DashboardScenePageStateManager extends DashboardScenePageStateManag
case DashboardRoutes.New:
rsp = await buildNewDashboardSaveModel(urlFolderUid);
break;
case DashboardRoutes.Provisioning: {
return await dashboardLoaderSrv.loadDashboard('provisioning', slug, uid);
}
case DashboardRoutes.Provisioning:
return this.loadProvisioningDashboard(slug || '', uid);
case DashboardRoutes.Public: {
return await dashboardLoaderSrv.loadDashboard('public', '', uid);
}
@@ -537,6 +621,9 @@ export class DashboardScenePageStateManagerV2 extends DashboardScenePageStateMan
case DashboardRoutes.New:
rsp = await buildNewDashboardSaveModelV2(urlFolderUid);
break;
case DashboardRoutes.Provisioning: {
return await this.loadProvisioningDashboard(slug || '', uid);
}
case DashboardRoutes.Public: {
return await this.dashboardLoader.loadDashboard('public', '', uid);
}
@@ -703,7 +790,6 @@ export class UnifiedDashboardScenePageStateManager extends DashboardScenePageSta
if (!rsp) {
return null;
}
if (isDashboardV2Resource(rsp)) {
this.activeManager = this.v2Manager;
return this.v2Manager.transformResponseToScene(rsp, options);
@@ -39,7 +39,11 @@ import { PanelEditor } from '../panel-edit/PanelEditor';
import { DashboardSceneChangeTracker } from '../saving/DashboardSceneChangeTracker';
import { SaveDashboardDrawer } from '../saving/SaveDashboardDrawer';
import { DashboardChangeInfo } from '../saving/shared';
import { DashboardSceneSerializerLike, getDashboardSceneSerializer } from '../serialization/DashboardSceneSerializer';
import {
DashboardSceneSerializerLike,
getDashboardSceneSerializer,
V2DashboardSerializer,
} from '../serialization/DashboardSceneSerializer';
import { serializeAutoGridItem } from '../serialization/layoutSerializers/AutoGridLayoutSerializer';
import { gridItemToGridLayoutItemKind } from '../serialization/layoutSerializers/DefaultGridLayoutSerializer';
import { getElement } from '../serialization/layoutSerializers/utils';
@@ -767,8 +771,10 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> impleme
getSaveResource(options: SaveDashboardAsOptions): ResourceForCreate<unknown> {
const { meta } = this.state;
const spec = this.getSaveAsModel(options);
const apiVersion = this.serializer instanceof V2DashboardSerializer ? 'v2alpha1' : 'v1alpha1'; // get from the dashboard?
return {
apiVersion: 'dashboard.grafana.app/v1alpha1', // get from the dashboard?
apiVersion: `dashboard.grafana.app/${apiVersion}`,
kind: 'Dashboard',
metadata: {
...meta.k8s,
@@ -13,7 +13,6 @@ import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { DashboardDTO } from 'app/types';
import { appEvents } from '../../../core/core';
import { loadDashboardFromProvisioning } from '../../provisioning/dashboardLoader';
import { ResponseTransformers } from '../api/ResponseTransformers';
import { getDashboardAPI } from '../api/dashboard_api';
import { DashboardVersionError, DashboardWithAccessInfo } from '../api/types';
@@ -37,6 +36,7 @@ abstract class DashboardLoaderSrvBase<T> implements DashboardLoaderSrvLike<T> {
uid: string | undefined,
params?: UrlQueryMap
): Promise<T>;
abstract loadSnapshot(slug: string): Promise<T>;
protected loadScriptedDashboard(file: string) {
@@ -124,10 +124,6 @@ export class DashboardLoaderSrv extends DashboardLoaderSrvBase<DashboardDTO> {
if (type === 'script' && slug) {
promise = this.loadScriptedDashboard(slug);
} else if (type === 'provisioning' && uid && slug) {
promise = loadDashboardFromProvisioning(slug, uid);
// needed for the old architecture
// in scenes this is handled through loadSnapshot method
} else if (type === 'snapshot' && slug) {
promise = getDashboardSnapshotSrv().getSnapshot(slug);
} else if (type === 'public' && uid) {
@@ -200,8 +196,6 @@ export class DashboardLoaderSrvV2 extends DashboardLoaderSrvBase<DashboardWithAc
promise = backendSrv.getPublicDashboardByUid(uid).then((result) => {
return ResponseTransformers.ensureV2Response(result);
});
} else if (type === 'provisioning' && uid && slug) {
promise = loadDashboardFromProvisioning(slug, uid).then((r) => ResponseTransformers.ensureV2Response(r));
} else if (uid) {
if (!params) {
const cachedDashboard = stateManager.getDashboardFromCache(uid);
@@ -263,5 +257,6 @@ export const setDashboardLoaderSrv = (srv: DashboardLoaderSrv) => {
if (process.env.NODE_ENV !== 'test') {
throw new Error('dashboardLoaderSrv can be only overriden in test environment');
}
dashboardLoaderSrv = srv;
};
@@ -1,62 +0,0 @@
import { getBackendSrv } from '@grafana/runtime';
import { BASE_URL } from 'app/api/clients/provisioning/baseAPI';
import { DashboardDTO } from 'app/types';
import { AnnoKeyManagerIdentity, AnnoKeyManagerKind, AnnoKeySourcePath } from '../apiserver/types';
/**
*
* Load a dashboard from repository
*/
export async function loadDashboardFromProvisioning(repo: string, path: string): Promise<DashboardDTO> {
const params = new URLSearchParams(window.location.search);
const ref = params.get('ref') ?? undefined; // commit hash or branch
const url = `${BASE_URL}/repositories/${repo}/files/${path}`;
return getBackendSrv()
.get(url, ref ? { ref } : undefined)
.then((v) => {
// Load the results from dryRun
const dryRun = v.resource.dryRun;
if (!dryRun) {
return Promise.reject('failed to read provisioned dashboard');
}
if (!dryRun.apiVersion.startsWith('dashboard.grafana.app')) {
return Promise.reject('unexpected resource type: ' + dryRun.apiVersion);
}
// Make sure the annotation key exists
let anno = dryRun.metadata.annotations;
if (!anno) {
dryRun.metadata.annotations = {};
}
anno[AnnoKeyManagerKind] = 'repo';
anno[AnnoKeyManagerIdentity] = repo;
anno[AnnoKeySourcePath] = ref ? path + '#' + ref : path;
return {
meta: {
canStar: false,
isSnapshot: false,
canShare: false,
// Should come from the repo settings
canDelete: true,
canSave: true,
canEdit: true,
// Includes additional k8s metadata
k8s: dryRun.metadata,
// lookup info
provisioning: {
file: url,
ref: ref,
repo: repo,
},
},
dashboard: dryRun.spec,
};
});
}