Dashboard Imports: Fix creating new library panels (#107219)

pull/107244/head
Stephanie Hingtgen 24 hours ago committed by GitHub
parent 88d113f37c
commit f8189eece9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 22
      pkg/services/dashboardimport/service/service.go
  2. 3
      pkg/services/dashboardimport/service/service_test.go
  3. 211
      pkg/tests/api/dashboards/api_dashboards_test.go

@ -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)
})
})
}

Loading…
Cancel
Save