Dashboard Imports: Fix creating new library panels (#107219)
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport/api"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport/utils"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||
@@ -23,13 +24,14 @@ func ProvideService(routeRegister routing.RouteRegister,
|
||||
quotaService quota.Service,
|
||||
pluginDashboardService plugindashboards.Service, pluginStore pluginstore.Store,
|
||||
libraryPanelService librarypanels.Service, dashboardService dashboards.DashboardService,
|
||||
ac accesscontrol.AccessControl, folderService folder.Service,
|
||||
ac accesscontrol.AccessControl, folderService folder.Service, features featuremgmt.FeatureToggles,
|
||||
) *ImportDashboardService {
|
||||
s := &ImportDashboardService{
|
||||
pluginDashboardService: pluginDashboardService,
|
||||
dashboardService: dashboardService,
|
||||
libraryPanelService: libraryPanelService,
|
||||
folderService: folderService,
|
||||
features: features,
|
||||
}
|
||||
|
||||
dashboardImportAPI := api.New(s, quotaService, pluginStore, ac)
|
||||
@@ -43,6 +45,7 @@ type ImportDashboardService struct {
|
||||
dashboardService dashboards.DashboardService
|
||||
libraryPanelService librarypanels.Service
|
||||
folderService folder.Service
|
||||
features featuremgmt.FeatureToggles
|
||||
}
|
||||
|
||||
func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashboardimport.ImportDashboardRequest) (*dashboardimport.ImportDashboardResponse, error) {
|
||||
@@ -131,22 +134,25 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
|
||||
User: req.User,
|
||||
}
|
||||
|
||||
savedDashboard, err := s.dashboardService.ImportDashboard(ctx, dto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
|
||||
// nolint:staticcheck
|
||||
err = s.libraryPanelService.ImportLibraryPanelsForDashboard(ctx, req.User, libraryElements, generatedDash.Get("panels").MustArray(), req.FolderId, req.FolderUid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.libraryPanelService.ConnectLibraryPanelsForDashboard(ctx, req.User, savedDashboard)
|
||||
savedDashboard, err := s.dashboardService.ImportDashboard(ctx, dto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
|
||||
|
||||
// in the k8s flow, we connect the library panels in pkg/registry/apis/dashboard/legacy/sql_dashboards.go
|
||||
if !s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
err = s.libraryPanelService.ConnectLibraryPanelsForDashboard(ctx, req.User, savedDashboard)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
revision := savedDashboard.Data.Get("revision").MustInt64(0)
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
@@ -67,6 +68,7 @@ func TestImportDashboardService(t *testing.T) {
|
||||
dashboardService: dashboardService,
|
||||
libraryPanelService: libraryPanelService,
|
||||
folderService: folderService,
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
req := &dashboardimport.ImportDashboardRequest{
|
||||
@@ -127,6 +129,7 @@ func TestImportDashboardService(t *testing.T) {
|
||||
dashboardService: dashboardService,
|
||||
libraryPanelService: libraryPanelService,
|
||||
folderService: folderService,
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
loadResp, err := loadTestDashboard(context.Background(), &plugindashboards.LoadPluginDashboardRequest{
|
||||
|
||||
@@ -551,3 +551,214 @@ func testPreserveSchemaVersion(t *testing.T, featureToggles []string) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationImportDashboardWithLibraryPanels(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
testImportDashboardWithLibraryPanels(t, []string{})
|
||||
}
|
||||
|
||||
func TestIntegrationImportDashboardWithLibraryPanelsK8s(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
testImportDashboardWithLibraryPanels(t, []string{featuremgmt.FlagKubernetesClientDashboardsFolders})
|
||||
}
|
||||
|
||||
func testImportDashboardWithLibraryPanels(t *testing.T, featureToggles []string) {
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: featureToggles,
|
||||
})
|
||||
|
||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
||||
t.Run("import dashboard with library panels should create library panels and connections", func(t *testing.T) {
|
||||
dashboardJSON := `{
|
||||
"title": "Test Dashboard with Library Panels",
|
||||
"panels": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Library Panel 1",
|
||||
"type": "text",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 0},
|
||||
"libraryPanel": {
|
||||
"uid": "test-lib-panel-1",
|
||||
"name": "Test Library Panel 1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Library Panel 2",
|
||||
"type": "stat",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 0},
|
||||
"libraryPanel": {
|
||||
"uid": "test-lib-panel-2",
|
||||
"name": "Test Library Panel 2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"__elements": {
|
||||
"test-lib-panel-1": {
|
||||
"uid": "test-lib-panel-1",
|
||||
"name": "Test Library Panel 1",
|
||||
"kind": 1,
|
||||
"type": "text",
|
||||
"model": {
|
||||
"title": "Test Library Panel 1",
|
||||
"type": "text",
|
||||
"options": {
|
||||
"content": "This is a test library panel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"test-lib-panel-2": {
|
||||
"uid": "test-lib-panel-2",
|
||||
"name": "Test Library Panel 2",
|
||||
"kind": 1,
|
||||
"type": "stat",
|
||||
"model": {
|
||||
"title": "Test Library Panel 2",
|
||||
"type": "stat",
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "area",
|
||||
"justifyMode": "auto",
|
||||
"orientation": "auto",
|
||||
"reduceOptions": {
|
||||
"calcs": ["lastNotNull"],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "1,20,90,30,5,0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
data, err := simplejson.NewJson([]byte(dashboardJSON))
|
||||
require.NoError(t, err)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
err = json.NewEncoder(buf).Encode(dashboardimport.ImportDashboardRequest{
|
||||
Dashboard: data,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
u := fmt.Sprintf("http://admin:admin@%s/api/dashboards/import", grafanaListedAddr)
|
||||
// nolint:gosec
|
||||
resp, err := http.Post(u, "application/json", buf)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
t.Cleanup(func() {
|
||||
err := resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
var importResp struct {
|
||||
UID string `json:"uid"`
|
||||
}
|
||||
err = json.Unmarshal(b, &importResp)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, importResp.UID)
|
||||
|
||||
t.Run("library panels should be created", func(t *testing.T) {
|
||||
url := fmt.Sprintf("http://admin:admin@%s/api/library-elements/test-lib-panel-1", grafanaListedAddr)
|
||||
// nolint:gosec
|
||||
resp, err := http.Get(url)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
t.Cleanup(func() {
|
||||
err := resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
panel, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
var panelRes struct {
|
||||
Result struct {
|
||||
UID string `json:"uid"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
} `json:"result"`
|
||||
}
|
||||
err = json.Unmarshal(panel, &panelRes)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "test-lib-panel-1", panelRes.Result.UID)
|
||||
assert.Equal(t, "Test Library Panel 1", panelRes.Result.Name)
|
||||
assert.Equal(t, "text", panelRes.Result.Type)
|
||||
|
||||
url = fmt.Sprintf("http://admin:admin@%s/api/library-elements/test-lib-panel-2", grafanaListedAddr)
|
||||
// nolint:gosec
|
||||
resp, err = http.Get(url)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
t.Cleanup(func() {
|
||||
err := resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
panel, err = io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(panel, &panelRes)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "test-lib-panel-2", panelRes.Result.UID)
|
||||
assert.Equal(t, "Test Library Panel 2", panelRes.Result.Name)
|
||||
assert.Equal(t, "stat", panelRes.Result.Type)
|
||||
})
|
||||
|
||||
t.Run("library panels should be connected to dashboard", func(t *testing.T) {
|
||||
url := fmt.Sprintf("http://admin:admin@%s/api/library-elements/test-lib-panel-1/connections", grafanaListedAddr)
|
||||
// nolint:gosec
|
||||
connectionsResp, err := http.Get(url)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, connectionsResp.StatusCode)
|
||||
t.Cleanup(func() {
|
||||
err := connectionsResp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
connections, err := io.ReadAll(connectionsResp.Body)
|
||||
require.NoError(t, err)
|
||||
var connectionsRes struct {
|
||||
Result []struct {
|
||||
ConnectionUID string `json:"connectionUid"`
|
||||
} `json:"result"`
|
||||
}
|
||||
err = json.Unmarshal(connections, &connectionsRes)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, connectionsRes.Result, 1)
|
||||
assert.Equal(t, importResp.UID, connectionsRes.Result[0].ConnectionUID)
|
||||
|
||||
url = fmt.Sprintf("http://admin:admin@%s/api/library-elements/test-lib-panel-2/connections", grafanaListedAddr)
|
||||
// nolint:gosec
|
||||
connectionsResp, err = http.Get(url)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, connectionsResp.StatusCode)
|
||||
t.Cleanup(func() {
|
||||
err := connectionsResp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
connections, err = io.ReadAll(connectionsResp.Body)
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(connections, &connectionsRes)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, connectionsRes.Result, 1)
|
||||
assert.Equal(t, importResp.UID, connectionsRes.Result[0].ConnectionUID)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user