LibraryElements: Creates usage stats for panels and variables (#34476)

* LibraryPanels: Adds usage collection

* Refactor: renames Panel and Variable consts

* Chore: initialize stats

* Refactor: moves library element migrations to migration namespace
pull/34557/head
Hugo Häggmark 5 years ago committed by GitHub
parent abe5c06d69
commit 7204a64717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      pkg/infra/metrics/metrics.go
  2. 4
      pkg/infra/usagestats/usage_stats.go
  3. 4
      pkg/infra/usagestats/usage_stats_test.go
  4. 13
      pkg/models/libraryelements.go
  5. 2
      pkg/models/stats.go
  6. 18
      pkg/services/libraryelements/database.go
  7. 6
      pkg/services/libraryelements/guard.go
  8. 51
      pkg/services/libraryelements/libraryelements.go
  9. 6
      pkg/services/libraryelements/libraryelements_create_test.go
  10. 50
      pkg/services/libraryelements/libraryelements_get_all_test.go
  11. 4
      pkg/services/libraryelements/libraryelements_get_test.go
  12. 32
      pkg/services/libraryelements/libraryelements_patch_test.go
  13. 10
      pkg/services/libraryelements/libraryelements_permissions_test.go
  14. 6
      pkg/services/libraryelements/libraryelements_test.go
  15. 7
      pkg/services/libraryelements/models.go
  16. 3
      pkg/services/libraryelements/writers.go
  17. 2
      pkg/services/librarypanels/librarypanels.go
  18. 4
      pkg/services/librarypanels/librarypanels_test.go
  19. 53
      pkg/services/sqlstore/migrations/libraryelements.go
  20. 1
      pkg/services/sqlstore/migrations/migrations.go
  21. 2
      pkg/services/sqlstore/stats.go
  22. 2
      pkg/services/sqlstore/stats_test.go

@ -185,6 +185,12 @@ var (
grafanaBuildVersion *prometheus.GaugeVec grafanaBuildVersion *prometheus.GaugeVec
grafanaPluginBuildInfoDesc *prometheus.GaugeVec grafanaPluginBuildInfoDesc *prometheus.GaugeVec
// StatsTotalLibraryPanels is a metric of total number of library panels stored in Grafana.
StatsTotalLibraryPanels prometheus.Gauge
// StatsTotalLibraryVariables is a metric of total number of library variables stored in Grafana.
StatsTotalLibraryVariables prometheus.Gauge
) )
func init() { func init() {
@ -547,6 +553,18 @@ func init() {
Help: "number of evaluation calls", Help: "number of evaluation calls",
Namespace: ExporterName, Namespace: ExporterName,
}) })
StatsTotalLibraryPanels = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_totals_library_panels",
Help: "total amount of library panels in the database",
Namespace: ExporterName,
})
StatsTotalLibraryVariables = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_totals_library_variables",
Help: "total amount of library variables in the database",
Namespace: ExporterName,
})
} }
// SetBuildInformation sets the build information for this binary // SetBuildInformation sets the build information for this binary
@ -640,6 +658,8 @@ func initMetricVars() {
StatsTotalDashboardVersions, StatsTotalDashboardVersions,
StatsTotalAnnotations, StatsTotalAnnotations,
MAccessEvaluationCount, MAccessEvaluationCount,
StatsTotalLibraryPanels,
StatsTotalLibraryVariables,
) )
} }

@ -72,6 +72,8 @@ func (uss *UsageStatsService) GetUsageReport(ctx context.Context) (UsageReport,
metrics["stats.dashboard_versions.count"] = statsQuery.Result.DashboardVersions metrics["stats.dashboard_versions.count"] = statsQuery.Result.DashboardVersions
metrics["stats.annotations.count"] = statsQuery.Result.Annotations metrics["stats.annotations.count"] = statsQuery.Result.Annotations
metrics["stats.alert_rules.count"] = statsQuery.Result.AlertRules metrics["stats.alert_rules.count"] = statsQuery.Result.AlertRules
metrics["stats.library_panels.count"] = statsQuery.Result.LibraryPanels
metrics["stats.library_variables.count"] = statsQuery.Result.LibraryVariables
validLicCount := 0 validLicCount := 0
if uss.License.HasValidLicense() { if uss.License.HasValidLicense() {
validLicCount = 1 validLicCount = 1
@ -317,6 +319,8 @@ func (uss *UsageStatsService) updateTotalStats() {
metrics.StatsTotalDashboardVersions.Set(float64(statsQuery.Result.DashboardVersions)) metrics.StatsTotalDashboardVersions.Set(float64(statsQuery.Result.DashboardVersions))
metrics.StatsTotalAnnotations.Set(float64(statsQuery.Result.Annotations)) metrics.StatsTotalAnnotations.Set(float64(statsQuery.Result.Annotations))
metrics.StatsTotalAlertRules.Set(float64(statsQuery.Result.AlertRules)) metrics.StatsTotalAlertRules.Set(float64(statsQuery.Result.AlertRules))
metrics.StatsTotalLibraryPanels.Set(float64(statsQuery.Result.LibraryPanels))
metrics.StatsTotalLibraryVariables.Set(float64(statsQuery.Result.LibraryVariables))
dsStats := models.GetDataSourceStatsQuery{} dsStats := models.GetDataSourceStatsQuery{}
if err := uss.Bus.Dispatch(&dsStats); err != nil { if err := uss.Bus.Dispatch(&dsStats); err != nil {

@ -63,6 +63,8 @@ func TestMetrics(t *testing.T) {
DashboardVersions: 16, DashboardVersions: 16,
Annotations: 17, Annotations: 17,
AlertRules: 18, AlertRules: 18,
LibraryPanels: 19,
LibraryVariables: 20,
} }
getSystemStatsQuery = query getSystemStatsQuery = query
return nil return nil
@ -313,6 +315,8 @@ func TestMetrics(t *testing.T) {
assert.Equal(t, 16, metrics.Get("stats.dashboard_versions.count").MustInt()) assert.Equal(t, 16, metrics.Get("stats.dashboard_versions.count").MustInt())
assert.Equal(t, 17, metrics.Get("stats.annotations.count").MustInt()) assert.Equal(t, 17, metrics.Get("stats.annotations.count").MustInt())
assert.Equal(t, 18, metrics.Get("stats.alert_rules.count").MustInt()) assert.Equal(t, 18, metrics.Get("stats.alert_rules.count").MustInt())
assert.Equal(t, 19, metrics.Get("stats.library_panels.count").MustInt())
assert.Equal(t, 20, metrics.Get("stats.library_variables.count").MustInt())
assert.Equal(t, 9, metrics.Get("stats.ds."+models.DS_ES+".count").MustInt()) assert.Equal(t, 9, metrics.Get("stats.ds."+models.DS_ES+".count").MustInt())
assert.Equal(t, 10, metrics.Get("stats.ds."+models.DS_PROMETHEUS+".count").MustInt()) assert.Equal(t, 10, metrics.Get("stats.ds."+models.DS_PROMETHEUS+".count").MustInt())

@ -0,0 +1,13 @@
package models
// LibraryElementKind is used for the kind of library element
type LibraryElementKind int
const (
// PanelElement is used for library elements that are of the Panel kind
PanelElement LibraryElementKind = iota + 1
// VariableElement is used for library elements that are of the Variable kind
VariableElement
)
const LibraryElementConnectionTableName = "library_element_connection"

@ -19,6 +19,8 @@ type SystemStats struct {
DashboardVersions int64 DashboardVersions int64
Annotations int64 Annotations int64
AlertRules int64 AlertRules int64
LibraryPanels int64
LibraryVariables int64
Admins int Admins int
Editors int Editors int

@ -22,7 +22,7 @@ SELECT DISTINCT
, u1.email AS created_by_email , u1.email AS created_by_email
, u2.login AS updated_by_name , u2.login AS updated_by_name
, u2.email AS updated_by_email , u2.email AS updated_by_email
, (SELECT COUNT(connection_id) FROM ` + connectionTableName + ` WHERE element_id = le.id AND kind=1) AS connected_dashboards` , (SELECT COUNT(connection_id) FROM ` + models.LibraryElementConnectionTableName + ` WHERE element_id = le.id AND kind=1) AS connected_dashboards`
) )
func getFromLibraryElementDTOWithMeta(dialect migrator.Dialect) string { func getFromLibraryElementDTOWithMeta(dialect migrator.Dialect) string {
@ -41,9 +41,9 @@ func syncFieldsWithModel(libraryElement *LibraryElement) error {
return err return err
} }
if LibraryElementKind(libraryElement.Kind) == Panel { if models.LibraryElementKind(libraryElement.Kind) == models.PanelElement {
model["title"] = libraryElement.Name model["title"] = libraryElement.Name
} else if LibraryElementKind(libraryElement.Kind) == Variable { } else if models.LibraryElementKind(libraryElement.Kind) == models.VariableElement {
model["name"] = libraryElement.Name model["name"] = libraryElement.Name
} }
if model["type"] != nil { if model["type"] != nil {
@ -520,7 +520,7 @@ func (l *LibraryElementService) getConnections(c *models.ReqContext, uid string)
var libraryElementConnections []libraryElementConnectionWithMeta var libraryElementConnections []libraryElementConnectionWithMeta
builder := sqlstore.SQLBuilder{} builder := sqlstore.SQLBuilder{}
builder.Write("SELECT lec.*, u1.login AS created_by_name, u1.email AS created_by_email") builder.Write("SELECT lec.*, u1.login AS created_by_name, u1.email AS created_by_email")
builder.Write(" FROM " + connectionTableName + " AS lec") builder.Write(" FROM " + models.LibraryElementConnectionTableName + " AS lec")
builder.Write(" LEFT JOIN " + l.SQLStore.Dialect.Quote("user") + " AS u1 ON lec.created_by = u1.id") builder.Write(" LEFT JOIN " + l.SQLStore.Dialect.Quote("user") + " AS u1 ON lec.created_by = u1.id")
builder.Write(" INNER JOIN dashboard AS dashboard on lec.connection_id = dashboard.id") builder.Write(" INNER JOIN dashboard AS dashboard on lec.connection_id = dashboard.id")
builder.Write(` WHERE lec.element_id=?`, element.ID) builder.Write(` WHERE lec.element_id=?`, element.ID)
@ -562,7 +562,7 @@ func (l *LibraryElementService) getElementsForDashboardID(c *models.ReqContext,
", coalesce(dashboard.uid, '') AS folder_uid" + ", coalesce(dashboard.uid, '') AS folder_uid" +
getFromLibraryElementDTOWithMeta(l.SQLStore.Dialect) + getFromLibraryElementDTOWithMeta(l.SQLStore.Dialect) +
" LEFT JOIN dashboard AS dashboard ON dashboard.id = le.folder_id" + " LEFT JOIN dashboard AS dashboard ON dashboard.id = le.folder_id" +
" INNER JOIN " + connectionTableName + " AS lce ON lce.element_id = le.id AND lce.kind=1 AND lce.connection_id=?" " INNER JOIN " + models.LibraryElementConnectionTableName + " AS lce ON lce.element_id = le.id AND lce.kind=1 AND lce.connection_id=?"
sess := session.SQL(sql, dashboardID) sess := session.SQL(sql, dashboardID)
err := sess.Find(&libraryElements) err := sess.Find(&libraryElements)
if err != nil { if err != nil {
@ -610,7 +610,7 @@ func (l *LibraryElementService) getElementsForDashboardID(c *models.ReqContext,
// connectElementsToDashboardID adds connections for all elements Library Elements in a Dashboard. // connectElementsToDashboardID adds connections for all elements Library Elements in a Dashboard.
func (l *LibraryElementService) connectElementsToDashboardID(c *models.ReqContext, elementUIDs []string, dashboardID int64) error { func (l *LibraryElementService) connectElementsToDashboardID(c *models.ReqContext, elementUIDs []string, dashboardID int64) error {
err := l.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error { err := l.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID) _, err := session.Exec("DELETE FROM "+models.LibraryElementConnectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID)
if err != nil { if err != nil {
return err return err
} }
@ -646,7 +646,7 @@ func (l *LibraryElementService) connectElementsToDashboardID(c *models.ReqContex
// disconnectElementsFromDashboardID deletes connections for all Library Elements in a Dashboard. // disconnectElementsFromDashboardID deletes connections for all Library Elements in a Dashboard.
func (l *LibraryElementService) disconnectElementsFromDashboardID(c *models.ReqContext, dashboardID int64) error { func (l *LibraryElementService) disconnectElementsFromDashboardID(c *models.ReqContext, dashboardID int64) error {
return l.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error { return l.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID) _, err := session.Exec("DELETE FROM "+models.LibraryElementConnectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID)
if err != nil { if err != nil {
return err return err
} }
@ -676,7 +676,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c *models.ReqCo
ConnectionID int64 `xorm:"connection_id"` ConnectionID int64 `xorm:"connection_id"`
} }
sql := "SELECT lec.connection_id FROM library_element AS le" sql := "SELECT lec.connection_id FROM library_element AS le"
sql += " INNER JOIN " + connectionTableName + " AS lec on le.id = lec.element_id" sql += " INNER JOIN " + models.LibraryElementConnectionTableName + " AS lec on le.id = lec.element_id"
sql += " WHERE le.folder_id=? AND le.org_id=?" sql += " WHERE le.folder_id=? AND le.org_id=?"
err = session.SQL(sql, folderID, c.SignedInUser.OrgId).Find(&connectionIDs) err = session.SQL(sql, folderID, c.SignedInUser.OrgId).Find(&connectionIDs)
if err != nil { if err != nil {
@ -694,7 +694,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c *models.ReqCo
return err return err
} }
for _, elementID := range elementIDs { for _, elementID := range elementIDs {
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE element_id=?", elementID.ID) _, err := session.Exec("DELETE FROM "+models.LibraryElementConnectionTableName+" WHERE element_id=?", elementID.ID)
if err != nil { if err != nil {
return err return err
} }

@ -11,11 +11,11 @@ func isGeneralFolder(folderID int64) bool {
} }
func (l *LibraryElementService) requireSupportedElementKind(kindAsInt int64) error { func (l *LibraryElementService) requireSupportedElementKind(kindAsInt int64) error {
kind := LibraryElementKind(kindAsInt) kind := models.LibraryElementKind(kindAsInt)
switch kind { switch kind {
case Panel: case models.PanelElement:
return nil return nil
case Variable: case models.VariableElement:
return nil return nil
default: default:
return errLibraryElementUnSupportedElementKind return errLibraryElementUnSupportedElementKind

@ -6,7 +6,6 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -27,8 +26,6 @@ type LibraryElementService struct {
log log.Logger log log.Logger
} }
const connectionTableName = "library_element_connection"
func init() { func init() {
registry.RegisterService(&LibraryElementService{}) registry.RegisterService(&LibraryElementService{})
} }
@ -66,51 +63,3 @@ func (l *LibraryElementService) DisconnectElementsFromDashboard(c *models.ReqCon
func (l *LibraryElementService) DeleteLibraryElementsInFolder(c *models.ReqContext, folderUID string) error { func (l *LibraryElementService) DeleteLibraryElementsInFolder(c *models.ReqContext, folderUID string) error {
return l.deleteLibraryElementsInFolderUID(c, folderUID) return l.deleteLibraryElementsInFolderUID(c, folderUID)
} }
// AddMigration defines database migrations.
// If Panel Library is not enabled does nothing.
func (l *LibraryElementService) AddMigration(mg *migrator.Migrator) {
libraryElementsV1 := migrator.Table{
Name: "library_element",
Columns: []*migrator.Column{
{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: migrator.DB_BigInt, Nullable: false},
{Name: "folder_id", Type: migrator.DB_BigInt, Nullable: false},
{Name: "uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
{Name: "name", Type: migrator.DB_NVarchar, Length: 150, Nullable: false},
{Name: "kind", Type: migrator.DB_BigInt, Nullable: false},
{Name: "type", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
{Name: "description", Type: migrator.DB_NVarchar, Length: 255, Nullable: false},
{Name: "model", Type: migrator.DB_Text, Nullable: false},
{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
{Name: "created_by", Type: migrator.DB_BigInt, Nullable: false},
{Name: "updated", Type: migrator.DB_DateTime, Nullable: false},
{Name: "updated_by", Type: migrator.DB_BigInt, Nullable: false},
{Name: "version", Type: migrator.DB_BigInt, Nullable: false},
},
Indices: []*migrator.Index{
{Cols: []string{"org_id", "folder_id", "name", "kind"}, Type: migrator.UniqueIndex},
},
}
mg.AddMigration("create library_element table v1", migrator.NewAddTableMigration(libraryElementsV1))
mg.AddMigration("add index library_element org_id-folder_id-name-kind", migrator.NewAddIndexMigration(libraryElementsV1, libraryElementsV1.Indices[0]))
libraryElementConnectionV1 := migrator.Table{
Name: connectionTableName,
Columns: []*migrator.Column{
{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "element_id", Type: migrator.DB_BigInt, Nullable: false},
{Name: "kind", Type: migrator.DB_BigInt, Nullable: false},
{Name: "connection_id", Type: migrator.DB_BigInt, Nullable: false},
{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
{Name: "created_by", Type: migrator.DB_BigInt, Nullable: false},
},
Indices: []*migrator.Index{
{Cols: []string{"element_id", "kind", "connection_id"}, Type: migrator.UniqueIndex},
},
}
mg.AddMigration("create "+connectionTableName+" table v1", migrator.NewAddTableMigration(libraryElementConnectionV1))
mg.AddMigration("add index "+connectionTableName+" element_id-kind-connection_id", migrator.NewAddIndexMigration(libraryElementConnectionV1, libraryElementConnectionV1.Indices[0]))
}

@ -5,6 +5,8 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models"
) )
func TestCreateLibraryElement(t *testing.T) { func TestCreateLibraryElement(t *testing.T) {
@ -24,7 +26,7 @@ func TestCreateLibraryElement(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: sc.initialResult.Result.UID, UID: sc.initialResult.Result.UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -69,7 +71,7 @@ func TestCreateLibraryElement(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.UID, UID: result.Result.UID,
Name: "Library Panel Name", Name: "Library Panel Name",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{

@ -42,7 +42,7 @@ func TestGetAllLibraryElements(t *testing.T) {
err := sc.reqContext.Req.ParseForm() err := sc.reqContext.Req.ParseForm()
require.NoError(t, err) require.NoError(t, err)
sc.reqContext.Req.Form.Add("kind", strconv.FormatInt(int64(Panel), 10)) sc.reqContext.Req.Form.Add("kind", strconv.FormatInt(int64(models.PanelElement), 10))
resp = sc.service.getAllHandler(sc.reqContext) resp = sc.service.getAllHandler(sc.reqContext)
require.Equal(t, 200, resp.Status()) require.Equal(t, 200, resp.Status())
@ -62,7 +62,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -107,7 +107,7 @@ func TestGetAllLibraryElements(t *testing.T) {
err := sc.reqContext.Req.ParseForm() err := sc.reqContext.Req.ParseForm()
require.NoError(t, err) require.NoError(t, err)
sc.reqContext.Req.Form.Add("kind", strconv.FormatInt(int64(Variable), 10)) sc.reqContext.Req.Form.Add("kind", strconv.FormatInt(int64(models.VariableElement), 10))
resp = sc.service.getAllHandler(sc.reqContext) resp = sc.service.getAllHandler(sc.reqContext)
require.Equal(t, 200, resp.Status()) require.Equal(t, 200, resp.Status())
@ -127,7 +127,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "query0", Name: "query0",
Kind: int64(Variable), Kind: int64(models.VariableElement),
Type: "query", Type: "query",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -187,7 +187,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -222,7 +222,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[1].UID, UID: result.Result.Elements[1].UID,
Name: "Text - Library Panel2", Name: "Text - Library Panel2",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -286,7 +286,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel2", Name: "Text - Library Panel2",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -321,7 +321,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[1].UID, UID: result.Result.Elements[1].UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -360,7 +360,7 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to existing types, it should succeed and the result should be correct", scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to existing types, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
command := getCreateCommandWithModel(sc.folder.Id, "Gauge - Library Panel", Panel, []byte(` command := getCreateCommandWithModel(sc.folder.Id, "Gauge - Library Panel", models.PanelElement, []byte(`
{ {
"datasource": "${DS_GDEV-TESTDATA}", "datasource": "${DS_GDEV-TESTDATA}",
"id": 1, "id": 1,
@ -372,7 +372,7 @@ func TestGetAllLibraryElements(t *testing.T) {
resp := sc.service.createHandler(sc.reqContext, command) resp := sc.service.createHandler(sc.reqContext, command)
require.Equal(t, 200, resp.Status()) require.Equal(t, 200, resp.Status())
command = getCreateCommandWithModel(sc.folder.Id, "BarGauge - Library Panel", Panel, []byte(` command = getCreateCommandWithModel(sc.folder.Id, "BarGauge - Library Panel", models.PanelElement, []byte(`
{ {
"datasource": "${DS_GDEV-TESTDATA}", "datasource": "${DS_GDEV-TESTDATA}",
"id": 1, "id": 1,
@ -405,7 +405,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "BarGauge - Library Panel", Name: "BarGauge - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "bargauge", Type: "bargauge",
Description: "BarGauge description", Description: "BarGauge description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -440,7 +440,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[1].UID, UID: result.Result.Elements[1].UID,
Name: "Gauge - Library Panel", Name: "Gauge - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "gauge", Type: "gauge",
Description: "Gauge description", Description: "Gauge description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -479,7 +479,7 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to a nonexistent type, it should succeed and the result should be correct", scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to a nonexistent type, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
command := getCreateCommandWithModel(sc.folder.Id, "Gauge - Library Panel", Panel, []byte(` command := getCreateCommandWithModel(sc.folder.Id, "Gauge - Library Panel", models.PanelElement, []byte(`
{ {
"datasource": "${DS_GDEV-TESTDATA}", "datasource": "${DS_GDEV-TESTDATA}",
"id": 1, "id": 1,
@ -542,7 +542,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: newFolder.Id, FolderID: newFolder.Id,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel2", Name: "Text - Library Panel2",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -637,7 +637,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -672,7 +672,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[1].UID, UID: result.Result.Elements[1].UID,
Name: "Text - Library Panel2", Name: "Text - Library Panel2",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -736,7 +736,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel2", Name: "Text - Library Panel2",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -800,7 +800,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -865,7 +865,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel2", Name: "Text - Library Panel2",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -904,7 +904,7 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in the description, it should succeed and the result should be correct", scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in the description, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
command := getCreateCommandWithModel(sc.folder.Id, "Text - Library Panel2", Panel, []byte(` command := getCreateCommandWithModel(sc.folder.Id, "Text - Library Panel2", models.PanelElement, []byte(`
{ {
"datasource": "${DS_GDEV-TESTDATA}", "datasource": "${DS_GDEV-TESTDATA}",
"id": 1, "id": 1,
@ -939,7 +939,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -978,7 +978,7 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in both name and description, it should succeed and the result should be correct", scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in both name and description, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
command := getCreateCommandWithModel(sc.folder.Id, "Some Other", Panel, []byte(` command := getCreateCommandWithModel(sc.folder.Id, "Some Other", models.PanelElement, []byte(`
{ {
"datasource": "${DS_GDEV-TESTDATA}", "datasource": "${DS_GDEV-TESTDATA}",
"id": 1, "id": 1,
@ -1011,7 +1011,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Some Other", Name: "Some Other",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A Library Panel", Description: "A Library Panel",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -1046,7 +1046,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[1].UID, UID: result.Result.Elements[1].UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -1112,7 +1112,7 @@ func TestGetAllLibraryElements(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: result.Result.Elements[0].UID, UID: result.Result.Elements[0].UID,
Name: "Text - Library Panel2", Name: "Text - Library Panel2",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{

@ -35,7 +35,7 @@ func TestGetLibraryElement(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: res.Result.UID, UID: res.Result.UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -130,7 +130,7 @@ func TestGetLibraryElement(t *testing.T) {
FolderID: 1, FolderID: 1,
UID: res.Result.UID, UID: res.Result.UID,
Name: "Text - Library Panel", Name: "Text - Library Panel",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "text", Type: "text",
Description: "A description", Description: "A description",
Model: map[string]interface{}{ Model: map[string]interface{}{

@ -5,12 +5,14 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models"
) )
func TestPatchLibraryElement(t *testing.T) { func TestPatchLibraryElement(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to patch a library panel that does not exist, it should fail", scenarioWithPanel(t, "When an admin tries to patch a library panel that does not exist, it should fail",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
cmd := patchLibraryElementCommand{Kind: int64(Panel)} cmd := patchLibraryElementCommand{Kind: int64(models.PanelElement)}
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown"}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown"})
resp := sc.service.patchHandler(sc.reqContext, cmd) resp := sc.service.patchHandler(sc.reqContext, cmd)
require.Equal(t, 404, resp.Status()) require.Equal(t, 404, resp.Status())
@ -31,7 +33,7 @@ func TestPatchLibraryElement(t *testing.T) {
"type": "graph" "type": "graph"
} }
`), `),
Kind: int64(Panel), Kind: int64(models.PanelElement),
Version: 1, Version: 1,
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
@ -45,7 +47,7 @@ func TestPatchLibraryElement(t *testing.T) {
FolderID: newFolder.Id, FolderID: newFolder.Id,
UID: sc.initialResult.Result.UID, UID: sc.initialResult.Result.UID,
Name: "Panel - New name", Name: "Panel - New name",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Type: "graph", Type: "graph",
Description: "An updated description", Description: "An updated description",
Model: map[string]interface{}{ Model: map[string]interface{}{
@ -83,7 +85,7 @@ func TestPatchLibraryElement(t *testing.T) {
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{}) newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
FolderID: newFolder.Id, FolderID: newFolder.Id,
Kind: int64(Panel), Kind: int64(models.PanelElement),
Version: 1, Version: 1,
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
@ -104,7 +106,7 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
FolderID: -1, FolderID: -1,
Name: "New Name", Name: "New Name",
Kind: int64(Panel), Kind: int64(models.PanelElement),
Version: 1, Version: 1,
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
@ -125,7 +127,7 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
FolderID: -1, FolderID: -1,
Model: []byte(`{ "title": "New Model Title", "name": "New Model Name", "type":"graph", "description": "New description" }`), Model: []byte(`{ "title": "New Model Title", "name": "New Model Name", "type":"graph", "description": "New description" }`),
Kind: int64(Panel), Kind: int64(models.PanelElement),
Version: 1, Version: 1,
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
@ -152,7 +154,7 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
FolderID: -1, FolderID: -1,
Model: []byte(`{ "description": "New description" }`), Model: []byte(`{ "description": "New description" }`),
Kind: int64(Panel), Kind: int64(models.PanelElement),
Version: 1, Version: 1,
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
@ -178,7 +180,7 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
FolderID: -1, FolderID: -1,
Model: []byte(`{ "type": "graph" }`), Model: []byte(`{ "type": "graph" }`),
Kind: int64(Panel), Kind: int64(models.PanelElement),
Version: 1, Version: 1,
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
@ -201,7 +203,7 @@ func TestPatchLibraryElement(t *testing.T) {
scenarioWithPanel(t, "When another admin tries to patch a library panel, it should change UpdatedBy successfully and return correct result", scenarioWithPanel(t, "When another admin tries to patch a library panel, it should change UpdatedBy successfully and return correct result",
func(t *testing.T, sc scenarioContext) { func(t *testing.T, sc scenarioContext) {
cmd := patchLibraryElementCommand{FolderID: -1, Version: 1, Kind: int64(Panel)} cmd := patchLibraryElementCommand{FolderID: -1, Version: 1, Kind: int64(models.PanelElement)}
sc.reqContext.UserId = 2 sc.reqContext.UserId = 2
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd) resp := sc.service.patchHandler(sc.reqContext, cmd)
@ -223,7 +225,7 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
Name: "Text - Library Panel", Name: "Text - Library Panel",
Version: 1, Version: 1,
Kind: int64(Panel), Kind: int64(models.PanelElement),
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd) resp = sc.service.patchHandler(sc.reqContext, cmd)
@ -239,7 +241,7 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
FolderID: 1, FolderID: 1,
Version: 1, Version: 1,
Kind: int64(Panel), Kind: int64(models.PanelElement),
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd) resp = sc.service.patchHandler(sc.reqContext, cmd)
@ -251,7 +253,7 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
FolderID: sc.folder.Id, FolderID: sc.folder.Id,
Version: 1, Version: 1,
Kind: int64(Panel), Kind: int64(models.PanelElement),
} }
sc.reqContext.OrgId = 2 sc.reqContext.OrgId = 2
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
@ -264,7 +266,7 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
FolderID: sc.folder.Id, FolderID: sc.folder.Id,
Version: 1, Version: 1,
Kind: int64(Panel), Kind: int64(models.PanelElement),
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd) resp := sc.service.patchHandler(sc.reqContext, cmd)
@ -278,14 +280,14 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{ cmd := patchLibraryElementCommand{
FolderID: sc.folder.Id, FolderID: sc.folder.Id,
Version: 1, Version: 1,
Kind: int64(Variable), Kind: int64(models.VariableElement),
} }
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd) resp := sc.service.patchHandler(sc.reqContext, cmd)
require.Equal(t, 200, resp.Status()) require.Equal(t, 200, resp.Status())
var result = validateAndUnMarshalResponse(t, resp) var result = validateAndUnMarshalResponse(t, resp)
sc.initialResult.Result.Type = "text" sc.initialResult.Result.Type = "text"
sc.initialResult.Result.Kind = int64(Panel) sc.initialResult.Result.Kind = int64(models.PanelElement)
sc.initialResult.Result.Description = "A description" sc.initialResult.Result.Description = "A description"
sc.initialResult.Result.Model = map[string]interface{}{ sc.initialResult.Result.Model = map[string]interface{}{
"datasource": "${DS_GDEV-TESTDATA}", "datasource": "${DS_GDEV-TESTDATA}",

@ -84,7 +84,7 @@ func TestLibraryElementPermissions(t *testing.T) {
toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items) toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
sc.reqContext.SignedInUser.OrgRole = testCase.role sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: toFolder.Id, Version: 1, Kind: int64(Panel)} cmd := patchLibraryElementCommand{FolderID: toFolder.Id, Version: 1, Kind: int64(models.PanelElement)}
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd) resp = sc.service.patchHandler(sc.reqContext, cmd)
require.Equal(t, testCase.status, resp.Status()) require.Equal(t, testCase.status, resp.Status())
@ -99,7 +99,7 @@ func TestLibraryElementPermissions(t *testing.T) {
toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions) toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
sc.reqContext.SignedInUser.OrgRole = testCase.role sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: toFolder.Id, Version: 1, Kind: int64(Panel)} cmd := patchLibraryElementCommand{FolderID: toFolder.Id, Version: 1, Kind: int64(models.PanelElement)}
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd) resp = sc.service.patchHandler(sc.reqContext, cmd)
require.Equal(t, testCase.status, resp.Status()) require.Equal(t, testCase.status, resp.Status())
@ -146,7 +146,7 @@ func TestLibraryElementPermissions(t *testing.T) {
result := validateAndUnMarshalResponse(t, resp) result := validateAndUnMarshalResponse(t, resp)
sc.reqContext.SignedInUser.OrgRole = testCase.role sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: 0, Version: 1, Kind: int64(Panel)} cmd := patchLibraryElementCommand{FolderID: 0, Version: 1, Kind: int64(models.PanelElement)}
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd) resp = sc.service.patchHandler(sc.reqContext, cmd)
require.Equal(t, testCase.status, resp.Status()) require.Equal(t, testCase.status, resp.Status())
@ -160,7 +160,7 @@ func TestLibraryElementPermissions(t *testing.T) {
result := validateAndUnMarshalResponse(t, resp) result := validateAndUnMarshalResponse(t, resp)
sc.reqContext.SignedInUser.OrgRole = testCase.role sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: folder.Id, Version: 1, Kind: int64(Panel)} cmd := patchLibraryElementCommand{FolderID: folder.Id, Version: 1, Kind: int64(models.PanelElement)}
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd) resp = sc.service.patchHandler(sc.reqContext, cmd)
require.Equal(t, testCase.status, resp.Status()) require.Equal(t, testCase.status, resp.Status())
@ -205,7 +205,7 @@ func TestLibraryElementPermissions(t *testing.T) {
result := validateAndUnMarshalResponse(t, resp) result := validateAndUnMarshalResponse(t, resp)
sc.reqContext.SignedInUser.OrgRole = testCase.role sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: -100, Version: 1, Kind: int64(Panel)} cmd := patchLibraryElementCommand{FolderID: -100, Version: 1, Kind: int64(models.PanelElement)}
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID}) sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd) resp = sc.service.patchHandler(sc.reqContext, cmd)
require.Equal(t, 404, resp.Status()) require.Equal(t, 404, resp.Status())

@ -126,7 +126,7 @@ type libraryElementsSearchResult struct {
} }
func getCreatePanelCommand(folderID int64, name string) CreateLibraryElementCommand { func getCreatePanelCommand(folderID int64, name string) CreateLibraryElementCommand {
command := getCreateCommandWithModel(folderID, name, Panel, []byte(` command := getCreateCommandWithModel(folderID, name, models.PanelElement, []byte(`
{ {
"datasource": "${DS_GDEV-TESTDATA}", "datasource": "${DS_GDEV-TESTDATA}",
"id": 1, "id": 1,
@ -140,7 +140,7 @@ func getCreatePanelCommand(folderID int64, name string) CreateLibraryElementComm
} }
func getCreateVariableCommand(folderID int64, name string) CreateLibraryElementCommand { func getCreateVariableCommand(folderID int64, name string) CreateLibraryElementCommand {
command := getCreateCommandWithModel(folderID, name, Variable, []byte(` command := getCreateCommandWithModel(folderID, name, models.VariableElement, []byte(`
{ {
"datasource": "${DS_GDEV-TESTDATA}", "datasource": "${DS_GDEV-TESTDATA}",
"name": "query0", "name": "query0",
@ -152,7 +152,7 @@ func getCreateVariableCommand(folderID int64, name string) CreateLibraryElementC
return command return command
} }
func getCreateCommandWithModel(folderID int64, name string, kind LibraryElementKind, model []byte) CreateLibraryElementCommand { func getCreateCommandWithModel(folderID int64, name string, kind models.LibraryElementKind, model []byte) CreateLibraryElementCommand {
command := CreateLibraryElementCommand{ command := CreateLibraryElementCommand{
FolderID: folderID, FolderID: folderID,
Name: name, Name: name,

@ -6,13 +6,6 @@ import (
"time" "time"
) )
type LibraryElementKind int
const (
Panel LibraryElementKind = iota + 1
Variable
)
type LibraryConnectionKind int type LibraryConnectionKind int
const ( const (

@ -5,6 +5,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
) )
@ -38,7 +39,7 @@ func writePerPageSQL(query searchLibraryElementsQuery, sqlStore *sqlstore.SQLSto
} }
func writeKindSQL(query searchLibraryElementsQuery, builder *sqlstore.SQLBuilder) { func writeKindSQL(query searchLibraryElementsQuery, builder *sqlstore.SQLBuilder) {
if LibraryElementKind(query.kind) == Panel || LibraryElementKind(query.kind) == Variable { if models.LibraryElementKind(query.kind) == models.PanelElement || models.LibraryElementKind(query.kind) == models.VariableElement {
builder.Write(" AND le.kind = ?", query.kind) builder.Write(" AND le.kind = ?", query.kind)
} }
} }

@ -76,7 +76,7 @@ func (lps *LibraryPanelService) LoadLibraryPanelsForDashboard(c *models.ReqConte
continue continue
} }
if libraryelements.LibraryElementKind(elementInDB.Kind) != libraryelements.Panel { if models.LibraryElementKind(elementInDB.Kind) != models.PanelElement {
continue continue
} }

@ -493,7 +493,7 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) {
"description": "Unused description" "description": "Unused description"
} }
`), `),
Kind: int64(libraryelements.Panel), Kind: int64(models.PanelElement),
}) })
require.NoError(t, err) require.NoError(t, err)
dashJSON := map[string]interface{}{ dashJSON := map[string]interface{}{
@ -783,7 +783,7 @@ func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, s
"description": "A description" "description": "A description"
} }
`), `),
Kind: int64(libraryelements.Panel), Kind: int64(models.PanelElement),
} }
resp, err := sc.elementService.CreateElement(sc.reqContext, command) resp, err := sc.elementService.CreateElement(sc.reqContext, command)
require.NoError(t, err) require.NoError(t, err)

@ -0,0 +1,53 @@
package migrations
import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)
// addLibraryElementsMigrations defines database migrations for library elements.
func addLibraryElementsMigrations(mg *migrator.Migrator) {
libraryElementsV1 := migrator.Table{
Name: "library_element",
Columns: []*migrator.Column{
{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: migrator.DB_BigInt, Nullable: false},
{Name: "folder_id", Type: migrator.DB_BigInt, Nullable: false},
{Name: "uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
{Name: "name", Type: migrator.DB_NVarchar, Length: 150, Nullable: false},
{Name: "kind", Type: migrator.DB_BigInt, Nullable: false},
{Name: "type", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
{Name: "description", Type: migrator.DB_NVarchar, Length: 255, Nullable: false},
{Name: "model", Type: migrator.DB_Text, Nullable: false},
{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
{Name: "created_by", Type: migrator.DB_BigInt, Nullable: false},
{Name: "updated", Type: migrator.DB_DateTime, Nullable: false},
{Name: "updated_by", Type: migrator.DB_BigInt, Nullable: false},
{Name: "version", Type: migrator.DB_BigInt, Nullable: false},
},
Indices: []*migrator.Index{
{Cols: []string{"org_id", "folder_id", "name", "kind"}, Type: migrator.UniqueIndex},
},
}
mg.AddMigration("create library_element table v1", migrator.NewAddTableMigration(libraryElementsV1))
mg.AddMigration("add index library_element org_id-folder_id-name-kind", migrator.NewAddIndexMigration(libraryElementsV1, libraryElementsV1.Indices[0]))
libraryElementConnectionV1 := migrator.Table{
Name: models.LibraryElementConnectionTableName,
Columns: []*migrator.Column{
{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "element_id", Type: migrator.DB_BigInt, Nullable: false},
{Name: "kind", Type: migrator.DB_BigInt, Nullable: false},
{Name: "connection_id", Type: migrator.DB_BigInt, Nullable: false},
{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
{Name: "created_by", Type: migrator.DB_BigInt, Nullable: false},
},
Indices: []*migrator.Index{
{Cols: []string{"element_id", "kind", "connection_id"}, Type: migrator.UniqueIndex},
},
}
mg.AddMigration("create "+models.LibraryElementConnectionTableName+" table v1", migrator.NewAddTableMigration(libraryElementConnectionV1))
mg.AddMigration("add index "+models.LibraryElementConnectionTableName+" element_id-kind-connection_id", migrator.NewAddIndexMigration(libraryElementConnectionV1, libraryElementConnectionV1.Indices[0]))
}

@ -40,6 +40,7 @@ func AddMigrations(mg *Migrator) {
addShortURLMigrations(mg) addShortURLMigrations(mg)
ualert.AddTablesMigrations(mg) ualert.AddTablesMigrations(mg)
ualert.AddDashAlertMigration(mg) ualert.AddDashAlertMigration(mg)
addLibraryElementsMigrations(mg)
} }
func addMigrationLogMigrations(mg *Migrator) { func addMigrationLogMigrations(mg *Migrator) {

@ -80,6 +80,8 @@ func GetSystemStats(query *models.GetSystemStatsQuery) error {
sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("team") + `) AS teams,`) sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("team") + `) AS teams,`)
sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("user_auth_token") + `) AS auth_tokens,`) sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("user_auth_token") + `) AS auth_tokens,`)
sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("alert_rule") + `) AS alert_rules,`) sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("alert_rule") + `) AS alert_rules,`)
sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("library_element")+` WHERE kind = ?) AS library_panels,`, models.PanelElement)
sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("library_element")+` WHERE kind = ?) AS library_variables,`, models.VariableElement)
sb.Write(roleCounterSQL()) sb.Write(roleCounterSQL())

@ -24,6 +24,8 @@ func TestStatsDataAccess(t *testing.T) {
assert.Equal(t, 0, query.Result.Editors) assert.Equal(t, 0, query.Result.Editors)
assert.Equal(t, 0, query.Result.Viewers) assert.Equal(t, 0, query.Result.Viewers)
assert.Equal(t, 3, query.Result.Admins) assert.Equal(t, 3, query.Result.Admins)
assert.Equal(t, int64(0), query.Result.LibraryPanels)
assert.Equal(t, int64(0), query.Result.LibraryVariables)
}) })
t.Run("Get system user count stats should not results in error", func(t *testing.T) { t.Run("Get system user count stats should not results in error", func(t *testing.T) {

Loading…
Cancel
Save