mirror of https://github.com/grafana/grafana
PanelLibrary: Adds api and db to create Library/Shared/Reusable Panel (#29642)
* PanelLibrary: Adds panellib table
* Refactor: removes drop table migration
* Refactor: fixes spelling mistake
* Refactor: changes after PR comments
* Refactor: some more renames
* PanelLibrary: Adds api and db to create Library/Shared/Reusable Panel
* Refactor: reverts SqlStore change and uses RegisterOverride instead
* Refactor: fixes lint error
* Refactor: fixes imports
* Refactor: reverts unintentional changes
* Refactor: Adds repository
* Revert "Refactor: Adds repository"
This reverts commit 4c46e8a6c4
.
* Refactor: changes after PR comments
* Refactor: Simplfies further
* Chore: fixes linting
* Chore: Changes after PR comments
* Update pkg/services/librarypanels/api.go
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
* Chore: fixes import after commited suggestion
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
pull/29721/head
parent
82b21fe35e
commit
941ba1d2f7
@ -0,0 +1,36 @@ |
||||
package librarypanels |
||||
|
||||
import ( |
||||
"errors" |
||||
|
||||
"github.com/go-macaron/binding" |
||||
"github.com/grafana/grafana/pkg/api" |
||||
"github.com/grafana/grafana/pkg/api/routing" |
||||
"github.com/grafana/grafana/pkg/middleware" |
||||
"github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/util" |
||||
) |
||||
|
||||
func (lps *LibraryPanelService) registerAPIEndpoints() { |
||||
if !lps.IsEnabled() { |
||||
return |
||||
} |
||||
|
||||
lps.RouteRegister.Group("/api/library-panels", func(libraryPanels routing.RouteRegister) { |
||||
libraryPanels.Post("/", middleware.ReqSignedIn, binding.Bind(addLibraryPanelCommand{}), api.Wrap(lps.createHandler)) |
||||
}) |
||||
} |
||||
|
||||
// createHandler handles POST /api/library-panels.
|
||||
func (lps *LibraryPanelService) createHandler(c *models.ReqContext, cmd addLibraryPanelCommand) api.Response { |
||||
panel, err := lps.createLibraryPanel(c, cmd) |
||||
|
||||
if err != nil { |
||||
if errors.Is(err, errLibraryPanelAlreadyAdded) { |
||||
return api.Error(400, errLibraryPanelAlreadyAdded.Error(), err) |
||||
} |
||||
return api.Error(500, "Failed to create library panel", err) |
||||
} |
||||
|
||||
return api.JSON(200, util.DynMap{"panel": panel}) |
||||
} |
@ -0,0 +1,41 @@ |
||||
package librarypanels |
||||
|
||||
import ( |
||||
"context" |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/models" |
||||
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore" |
||||
) |
||||
|
||||
// createLibraryPanel function adds a LibraryPanel
|
||||
func (lps *LibraryPanelService) createLibraryPanel(c *models.ReqContext, cmd addLibraryPanelCommand) (LibraryPanel, error) { |
||||
libraryPanel := LibraryPanel{ |
||||
OrgID: c.SignedInUser.OrgId, |
||||
FolderID: cmd.FolderID, |
||||
Title: cmd.Title, |
||||
Model: cmd.Model, |
||||
|
||||
Created: time.Now(), |
||||
Updated: time.Now(), |
||||
|
||||
CreatedBy: c.SignedInUser.UserId, |
||||
UpdatedBy: c.SignedInUser.UserId, |
||||
} |
||||
|
||||
err := lps.SQLStore.WithTransactionalDbSession(context.Background(), func(session *sqlstore.DBSession) error { |
||||
if res, err := session.Query("SELECT 1 from library_panel WHERE org_id=? and folder_id=? and title=?", c.SignedInUser.OrgId, cmd.FolderID, cmd.Title); err != nil { |
||||
return err |
||||
} else if len(res) == 1 { |
||||
return errLibraryPanelAlreadyAdded |
||||
} |
||||
|
||||
if _, err := session.Insert(&libraryPanel); err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
}) |
||||
|
||||
return libraryPanel, err |
||||
} |
@ -0,0 +1,100 @@ |
||||
package librarypanels |
||||
|
||||
import ( |
||||
"testing" |
||||
"time" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
|
||||
"github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/registry" |
||||
"github.com/grafana/grafana/pkg/services/sqlstore" |
||||
"github.com/grafana/grafana/pkg/setting" |
||||
) |
||||
|
||||
func TestCreateLibraryPanel(t *testing.T) { |
||||
t.Run("should fail if library panel already exists", func(t *testing.T) { |
||||
lps, context := setupTestEnv(t, models.ROLE_EDITOR) |
||||
command := addLibraryPanelCommand{ |
||||
FolderID: 1, |
||||
Title: "Text - Library Panel", |
||||
Model: []byte(` |
||||
{ |
||||
"datasource": "${DS_GDEV-TESTDATA}", |
||||
"id": 1, |
||||
"title": "Text - Library Panel", |
||||
"type": "text" |
||||
} |
||||
`), |
||||
} |
||||
|
||||
response := lps.createHandler(&context, command) |
||||
require.Equal(t, 200, response.Status()) |
||||
|
||||
response = lps.createHandler(&context, command) |
||||
require.Equal(t, 400, response.Status()) |
||||
|
||||
t.Cleanup(registry.ClearOverrides) |
||||
}) |
||||
} |
||||
|
||||
func setupMigrations(cfg *setting.Cfg) LibraryPanelService { |
||||
lps := LibraryPanelService{ |
||||
SQLStore: nil, |
||||
Cfg: cfg, |
||||
} |
||||
|
||||
overrideServiceFunc := func(d registry.Descriptor) (*registry.Descriptor, bool) { |
||||
descriptor := registry.Descriptor{ |
||||
Name: "LibraryPanelService", |
||||
Instance: &lps, |
||||
InitPriority: 0, |
||||
} |
||||
|
||||
return &descriptor, true |
||||
} |
||||
|
||||
registry.RegisterOverride(overrideServiceFunc) |
||||
|
||||
return lps |
||||
} |
||||
|
||||
func setupTestEnv(t *testing.T, orgRole models.RoleType) (LibraryPanelService, models.ReqContext) { |
||||
cfg := setting.NewCfg() |
||||
cfg.FeatureToggles = map[string]bool{"panelLibrary": true} |
||||
|
||||
service := setupMigrations(cfg) |
||||
|
||||
sqlStore := sqlstore.InitTestDB(t) |
||||
service.SQLStore = sqlStore |
||||
|
||||
user := models.SignedInUser{ |
||||
UserId: 1, |
||||
OrgId: 1, |
||||
OrgName: "", |
||||
OrgRole: orgRole, |
||||
Login: "", |
||||
Name: "", |
||||
Email: "", |
||||
ApiKeyId: 0, |
||||
OrgCount: 0, |
||||
IsGrafanaAdmin: false, |
||||
IsAnonymous: false, |
||||
HelpFlags1: 0, |
||||
LastSeenAt: time.Now(), |
||||
Teams: nil, |
||||
} |
||||
|
||||
context := models.ReqContext{ |
||||
Context: nil, |
||||
SignedInUser: &user, |
||||
UserToken: nil, |
||||
IsSignedIn: false, |
||||
IsRenderCall: false, |
||||
AllowAnonymous: false, |
||||
SkipCache: false, |
||||
Logger: nil, |
||||
} |
||||
|
||||
return service, context |
||||
} |
@ -0,0 +1,36 @@ |
||||
package librarypanels |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"time" |
||||
) |
||||
|
||||
// LibraryPanel is the model for library panel definitions.
|
||||
type LibraryPanel struct { |
||||
ID int64 `xorm:"pk autoincr 'id'"` |
||||
OrgID int64 `xorm:"org_id"` |
||||
FolderID int64 `xorm:"folder_id"` |
||||
Title string |
||||
Model json.RawMessage |
||||
|
||||
Created time.Time |
||||
Updated time.Time |
||||
|
||||
CreatedBy int64 |
||||
UpdatedBy int64 |
||||
} |
||||
|
||||
var ( |
||||
// errLibraryPanelAlreadyAdded is an error when you add a library panel that already exists.
|
||||
errLibraryPanelAlreadyAdded = fmt.Errorf("library panel with that title already exists") |
||||
) |
||||
|
||||
// Commands
|
||||
|
||||
// addLibraryPanelCommand is the command for adding a LibraryPanel
|
||||
type addLibraryPanelCommand struct { |
||||
FolderID int64 `json:"folderId"` |
||||
Title string `json:"title"` |
||||
Model json.RawMessage `json:"model"` |
||||
} |
Loading…
Reference in new issue