LibraryPanels: removes feature toggle (#33839)
* WIP: intial structure * Refactor: adds create library element endpoint * Feature: adds delete library element * wip * Refactor: adds get api * Refactor: adds get all api * Refactor: adds patch api * Refactor: changes to library_element_connection * Refactor: add get connections api * wip: in the middle of refactor * wip * Refactor: consolidating both api:s * Refactor: points front end to library elements api * Tests: Fixes broken test * LibraryPanels: removes feature toggle * Fix: fixes delete library elements in folder and adds tests * Tests: fixes snapshot * Refactor: adds service interfaces so they can be easily mocked * Refactor: changes order of tabs in manage folder * Refactor: fixes so link does not cover whole card * Refactor: fixes index string name * Update pkg/services/libraryelements/libraryelements.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/libraryelements/libraryelements_permissions_test.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/libraryelements/database.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Chore: changes after PR comments * Update libraryelements.go * Update libraryelements.go * Chore: updates after PR comments * Chore: trying to fix build error * Refactor: fixed stupid mistake * Update libraryelements.go * Chore: tries to fix build errors * Refactor: trying to fix MySQL key length * Update libraryelements.go * Update pkg/services/libraryelements/libraryelements.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/librarypanels/librarypanels.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Refactor: changes after PR comments * Refactor: changes after PR comments * Tests: fixes tests * Refactor: renames connections to connectedDashboards Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
co-authored by
Arve Knudsen
parent
f61328af2d
commit
69d9f427e1
+18
-25
@@ -170,13 +170,12 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
|
||||
// make sure db version is in sync with json model version
|
||||
dash.Data.Set("version", dash.Version)
|
||||
|
||||
if hs.Cfg.IsPanelLibraryEnabled() {
|
||||
// load library panels JSON for this dashboard
|
||||
err = hs.LibraryPanelService.LoadLibraryPanelsForDashboard(c, dash)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while loading library panels", err)
|
||||
}
|
||||
// load library panels JSON for this dashboard
|
||||
err = hs.LibraryPanelService.LoadLibraryPanelsForDashboard(c, dash)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while loading library panels", err)
|
||||
}
|
||||
|
||||
var trimedJson simplejson.Json
|
||||
if trimDefaults && !hs.LoadSchemaService.IsDisabled() {
|
||||
trimedJson, err = hs.LoadSchemaService.DashboardTrimDefaults(*dash.Data)
|
||||
@@ -249,16 +248,14 @@ func (hs *HTTPServer) deleteDashboard(c *models.ReqContext) response.Response {
|
||||
return dashboardGuardianResponse(err)
|
||||
}
|
||||
|
||||
if hs.Cfg.IsPanelLibraryEnabled() {
|
||||
// disconnect all library elements for this dashboard
|
||||
err := hs.LibraryElementService.DisconnectElementsFromDashboard(c, dash.Id)
|
||||
if err != nil {
|
||||
hs.log.Error("Failed to disconnect library elements", "dashboard", dash.Id, "user", c.SignedInUser.UserId, "error", err)
|
||||
}
|
||||
// disconnect all library elements for this dashboard
|
||||
err := hs.LibraryElementService.DisconnectElementsFromDashboard(c, dash.Id)
|
||||
if err != nil {
|
||||
hs.log.Error("Failed to disconnect library elements", "dashboard", dash.Id, "user", c.SignedInUser.UserId, "error", err)
|
||||
}
|
||||
|
||||
svc := dashboards.NewService(hs.SQLStore)
|
||||
err := svc.DeleteDashboard(dash.Id, c.OrgId)
|
||||
err = svc.DeleteDashboard(dash.Id, c.OrgId)
|
||||
if err != nil {
|
||||
var dashboardErr models.DashboardErr
|
||||
if ok := errors.As(err, &dashboardErr); ok {
|
||||
@@ -318,12 +315,10 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
||||
allowUiUpdate = hs.ProvisioningService.GetAllowUIUpdatesFromConfig(provisioningData.Name)
|
||||
}
|
||||
|
||||
if hs.Cfg.IsPanelLibraryEnabled() {
|
||||
// clean up all unnecessary library panels JSON properties so we store a minimum JSON
|
||||
err = hs.LibraryPanelService.CleanLibraryPanelsForDashboard(dash)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while cleaning library panels", err)
|
||||
}
|
||||
// clean up all unnecessary library panels JSON properties so we store a minimum JSON
|
||||
err = hs.LibraryPanelService.CleanLibraryPanelsForDashboard(dash)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while cleaning library panels", err)
|
||||
}
|
||||
|
||||
dashItem := &dashboards.SaveDashboardDTO{
|
||||
@@ -373,12 +368,10 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
||||
}
|
||||
}
|
||||
|
||||
if hs.Cfg.IsPanelLibraryEnabled() {
|
||||
// connect library panels for this dashboard after the dashboard is stored and has an ID
|
||||
err = hs.LibraryPanelService.ConnectLibraryPanelsForDashboard(c, dashboard)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while connecting library panels", err)
|
||||
}
|
||||
// connect library panels for this dashboard after the dashboard is stored and has an ID
|
||||
err = hs.LibraryPanelService.ConnectLibraryPanelsForDashboard(c, dashboard)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while connecting library panels", err)
|
||||
}
|
||||
|
||||
c.TimeRequest(metrics.MApiDashboardSave)
|
||||
|
||||
+99
-18
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/services/live"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
@@ -172,7 +173,11 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||
state := setUp()
|
||||
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
})
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
|
||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||
@@ -182,7 +187,11 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||
state := setUp()
|
||||
|
||||
callDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
})
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
|
||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||
@@ -237,7 +246,11 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||
state := setUp()
|
||||
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
})
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||
})
|
||||
@@ -246,7 +259,11 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||
state := setUp()
|
||||
|
||||
callDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
})
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||
})
|
||||
@@ -271,8 +288,10 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
|
||||
t.Run("Given a dashboard with a parent folder which has an ACL", func(t *testing.T) {
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
Live: newTestLive(t),
|
||||
Cfg: setting.NewCfg(),
|
||||
Live: newTestLive(t),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
}
|
||||
|
||||
setUp := func() *testState {
|
||||
@@ -1031,7 +1050,11 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
"/api/dashboards/db/:slug", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||
setUp()
|
||||
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
})
|
||||
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
result := sc.ToJSON()
|
||||
@@ -1041,7 +1064,11 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/abcdefghi", "/api/dashboards/db/:uid", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||
setUp()
|
||||
|
||||
callDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
})
|
||||
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
result := sc.ToJSON()
|
||||
@@ -1073,8 +1100,10 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
ProvisioningService: mock,
|
||||
Cfg: setting.NewCfg(),
|
||||
ProvisioningService: mock,
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
}
|
||||
callGetDashboard(sc, hs)
|
||||
|
||||
@@ -1095,10 +1124,16 @@ func getDashboardShouldReturn200WithConfig(sc *scenarioContext, provisioningServ
|
||||
provisioningService = provisioning.NewProvisioningServiceMock()
|
||||
}
|
||||
|
||||
libraryPanelsService := mockLibraryPanelService{}
|
||||
libraryElementsService := mockLibraryElementService{}
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
ProvisioningService: provisioningService,
|
||||
Cfg: setting.NewCfg(),
|
||||
LibraryPanelService: &libraryPanelsService,
|
||||
LibraryElementService: &libraryElementsService,
|
||||
ProvisioningService: provisioningService,
|
||||
}
|
||||
|
||||
callGetDashboard(sc, hs)
|
||||
|
||||
require.Equal(sc.t, 200, sc.resp.Code)
|
||||
@@ -1185,7 +1220,9 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
||||
QuotaService: "a.QuotaService{
|
||||
Cfg: cfg,
|
||||
},
|
||||
PluginManager: &fakePluginManager{},
|
||||
PluginManager: &fakePluginManager{},
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
}
|
||||
|
||||
sc := setupScenarioContext(t, url)
|
||||
@@ -1242,11 +1279,13 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
hs := HTTPServer{
|
||||
Cfg: cfg,
|
||||
Bus: bus.GetBus(),
|
||||
ProvisioningService: provisioning.NewProvisioningServiceMock(),
|
||||
Live: newTestLive(t),
|
||||
QuotaService: "a.QuotaService{Cfg: cfg},
|
||||
Cfg: cfg,
|
||||
Bus: bus.GetBus(),
|
||||
ProvisioningService: provisioning.NewProvisioningServiceMock(),
|
||||
Live: newTestLive(t),
|
||||
QuotaService: "a.QuotaService{Cfg: cfg},
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
}
|
||||
|
||||
sc := setupScenarioContext(t, url)
|
||||
@@ -1293,3 +1332,45 @@ func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboard
|
||||
*models.DashboardProvisioning, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type mockLibraryPanelService struct {
|
||||
}
|
||||
|
||||
func (m *mockLibraryPanelService) LoadLibraryPanelsForDashboard(c *models.ReqContext, dash *models.Dashboard) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockLibraryPanelService) CleanLibraryPanelsForDashboard(dash *models.Dashboard) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockLibraryPanelService) ConnectLibraryPanelsForDashboard(c *models.ReqContext, dash *models.Dashboard) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockLibraryElementService struct {
|
||||
}
|
||||
|
||||
func (l *mockLibraryElementService) CreateElement(c *models.ReqContext, cmd libraryelements.CreateLibraryElementCommand) (libraryelements.LibraryElementDTO, error) {
|
||||
return libraryelements.LibraryElementDTO{}, nil
|
||||
}
|
||||
|
||||
// GetElementsForDashboard gets all connected elements for a specific dashboard.
|
||||
func (l *mockLibraryElementService) GetElementsForDashboard(c *models.ReqContext, dashboardID int64) (map[string]libraryelements.LibraryElementDTO, error) {
|
||||
return map[string]libraryelements.LibraryElementDTO{}, nil
|
||||
}
|
||||
|
||||
// ConnectElementsToDashboard connects elements to a specific dashboard.
|
||||
func (l *mockLibraryElementService) ConnectElementsToDashboard(c *models.ReqContext, elementUIDs []string, dashboardID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisconnectElementsFromDashboard disconnects elements from a specific dashboard.
|
||||
func (l *mockLibraryElementService) DisconnectElementsFromDashboard(c *models.ReqContext, dashboardID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteLibraryElementsInFolder deletes all elements for a specific folder.
|
||||
func (l *mockLibraryElementService) DeleteLibraryElementsInFolder(c *models.ReqContext, folderUID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
+6
-9
@@ -4,13 +4,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@@ -88,14 +87,12 @@ func (hs *HTTPServer) UpdateFolder(c *models.ReqContext, cmd models.UpdateFolder
|
||||
|
||||
func (hs *HTTPServer) DeleteFolder(c *models.ReqContext) response.Response { // temporarily adding this function to HTTPServer, will be removed from HTTPServer when librarypanels featuretoggle is removed
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||
if hs.Cfg.IsPanelLibraryEnabled() {
|
||||
err := hs.LibraryElementService.DeleteLibraryElementsInFolder(c, c.Params(":uid"))
|
||||
if err != nil {
|
||||
if errors.Is(err, libraryelements.ErrFolderHasConnectedLibraryElements) {
|
||||
return response.Error(403, "Folder could not be deleted because it contains library elements in use", err)
|
||||
}
|
||||
return ToFolderErrorResponse(err)
|
||||
err := hs.LibraryElementService.DeleteLibraryElementsInFolder(c, c.Params(":uid"))
|
||||
if err != nil {
|
||||
if errors.Is(err, libraryelements.ErrFolderHasConnectedLibraryElements) {
|
||||
return response.Error(403, "Folder could not be deleted because it contains library elements in use", err)
|
||||
}
|
||||
return ToFolderErrorResponse(err)
|
||||
}
|
||||
|
||||
f, err := s.DeleteFolder(c.Params(":uid"))
|
||||
|
||||
@@ -13,6 +13,9 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
httpstatic "github.com/grafana/grafana/pkg/api/static"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@@ -34,8 +37,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/hooks"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
"github.com/grafana/grafana/pkg/services/live"
|
||||
"github.com/grafana/grafana/pkg/services/live/pushhttp"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
@@ -97,13 +98,13 @@ type HTTPServer struct {
|
||||
LivePushGateway *pushhttp.Gateway `inject:""`
|
||||
ContextHandler *contexthandler.ContextHandler `inject:""`
|
||||
SQLStore *sqlstore.SQLStore `inject:""`
|
||||
LibraryPanelService *librarypanels.LibraryPanelService `inject:""`
|
||||
LibraryElementService *libraryelements.LibraryElementService `inject:""`
|
||||
DataService *tsdb.Service `inject:""`
|
||||
PluginDashboardService *plugindashboards.Service `inject:""`
|
||||
AlertEngine *alerting.AlertEngine `inject:""`
|
||||
LoadSchemaService *schemaloader.SchemaLoaderService `inject:""`
|
||||
Alertmanager *notifier.Alertmanager `inject:""`
|
||||
LibraryPanelService librarypanels.Service `inject:""`
|
||||
LibraryElementService libraryelements.Service `inject:""`
|
||||
Listener net.Listener
|
||||
}
|
||||
|
||||
|
||||
+6
-8
@@ -168,14 +168,12 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
|
||||
Icon: "camera",
|
||||
})
|
||||
|
||||
if hs.Cfg.IsPanelLibraryEnabled() {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "Library panels",
|
||||
Id: "library-panels",
|
||||
Url: hs.Cfg.AppSubURL + "/library-panels",
|
||||
Icon: "library-panel",
|
||||
})
|
||||
}
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "Library panels",
|
||||
Id: "library-panels",
|
||||
Url: hs.Cfg.AppSubURL + "/library-panels",
|
||||
Icon: "library-panel",
|
||||
})
|
||||
}
|
||||
|
||||
navTree = append(navTree, &dtos.NavLink{
|
||||
|
||||
Reference in New Issue
Block a user