From 7f919c0e55a7d7cba82eeb2cebbceab27c676b85 Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Mon, 24 May 2021 05:35:35 +0100 Subject: [PATCH] LibraryElements: Creates usage stats for panels and variables (#34476) (#34555) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * LibraryPanels: Adds usage collection * Refactor: renames Panel and Variable consts * Chore: initialize stats * Refactor: moves library element migrations to migration namespace (cherry picked from commit 7204a6471767371bd3656c33b34ad87c81f99c24) Co-authored-by: Hugo Häggmark --- pkg/infra/metrics/metrics.go | 20 +++++++ pkg/infra/usagestats/usage_stats.go | 4 ++ pkg/infra/usagestats/usage_stats_test.go | 4 ++ pkg/models/libraryelements.go | 13 +++++ pkg/models/stats.go | 2 + pkg/services/libraryelements/database.go | 18 +++---- pkg/services/libraryelements/guard.go | 6 +-- .../libraryelements/libraryelements.go | 51 ------------------ .../libraryelements_create_test.go | 6 ++- .../libraryelements_get_all_test.go | 50 ++++++++--------- .../libraryelements_get_test.go | 4 +- .../libraryelements_patch_test.go | 32 +++++------ .../libraryelements_permissions_test.go | 10 ++-- .../libraryelements/libraryelements_test.go | 6 +-- pkg/services/libraryelements/models.go | 7 --- pkg/services/libraryelements/writers.go | 3 +- pkg/services/librarypanels/librarypanels.go | 2 +- .../librarypanels/librarypanels_test.go | 4 +- .../sqlstore/migrations/libraryelements.go | 53 +++++++++++++++++++ .../sqlstore/migrations/migrations.go | 1 + pkg/services/sqlstore/stats.go | 2 + pkg/services/sqlstore/stats_test.go | 2 + 22 files changed, 174 insertions(+), 126 deletions(-) create mode 100644 pkg/models/libraryelements.go create mode 100644 pkg/services/sqlstore/migrations/libraryelements.go diff --git a/pkg/infra/metrics/metrics.go b/pkg/infra/metrics/metrics.go index cb81d98c7d0..e325a75d126 100644 --- a/pkg/infra/metrics/metrics.go +++ b/pkg/infra/metrics/metrics.go @@ -185,6 +185,12 @@ var ( grafanaBuildVersion *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() { @@ -547,6 +553,18 @@ func init() { Help: "number of evaluation calls", 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 @@ -640,6 +658,8 @@ func initMetricVars() { StatsTotalDashboardVersions, StatsTotalAnnotations, MAccessEvaluationCount, + StatsTotalLibraryPanels, + StatsTotalLibraryVariables, ) } diff --git a/pkg/infra/usagestats/usage_stats.go b/pkg/infra/usagestats/usage_stats.go index d2e5c78879f..2b43583acd0 100644 --- a/pkg/infra/usagestats/usage_stats.go +++ b/pkg/infra/usagestats/usage_stats.go @@ -72,6 +72,8 @@ func (uss *UsageStatsService) GetUsageReport(ctx context.Context) (UsageReport, metrics["stats.dashboard_versions.count"] = statsQuery.Result.DashboardVersions metrics["stats.annotations.count"] = statsQuery.Result.Annotations 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 if uss.License.HasValidLicense() { validLicCount = 1 @@ -317,6 +319,8 @@ func (uss *UsageStatsService) updateTotalStats() { metrics.StatsTotalDashboardVersions.Set(float64(statsQuery.Result.DashboardVersions)) metrics.StatsTotalAnnotations.Set(float64(statsQuery.Result.Annotations)) metrics.StatsTotalAlertRules.Set(float64(statsQuery.Result.AlertRules)) + metrics.StatsTotalLibraryPanels.Set(float64(statsQuery.Result.LibraryPanels)) + metrics.StatsTotalLibraryVariables.Set(float64(statsQuery.Result.LibraryVariables)) dsStats := models.GetDataSourceStatsQuery{} if err := uss.Bus.Dispatch(&dsStats); err != nil { diff --git a/pkg/infra/usagestats/usage_stats_test.go b/pkg/infra/usagestats/usage_stats_test.go index 4e068704256..3a1125d505d 100644 --- a/pkg/infra/usagestats/usage_stats_test.go +++ b/pkg/infra/usagestats/usage_stats_test.go @@ -63,6 +63,8 @@ func TestMetrics(t *testing.T) { DashboardVersions: 16, Annotations: 17, AlertRules: 18, + LibraryPanels: 19, + LibraryVariables: 20, } getSystemStatsQuery = query 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, 17, metrics.Get("stats.annotations.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, 10, metrics.Get("stats.ds."+models.DS_PROMETHEUS+".count").MustInt()) diff --git a/pkg/models/libraryelements.go b/pkg/models/libraryelements.go new file mode 100644 index 00000000000..9a989add8db --- /dev/null +++ b/pkg/models/libraryelements.go @@ -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" diff --git a/pkg/models/stats.go b/pkg/models/stats.go index 640eb535d48..4387f6e86e7 100644 --- a/pkg/models/stats.go +++ b/pkg/models/stats.go @@ -19,6 +19,8 @@ type SystemStats struct { DashboardVersions int64 Annotations int64 AlertRules int64 + LibraryPanels int64 + LibraryVariables int64 Admins int Editors int diff --git a/pkg/services/libraryelements/database.go b/pkg/services/libraryelements/database.go index fa377f029c5..9a12d6c5780 100644 --- a/pkg/services/libraryelements/database.go +++ b/pkg/services/libraryelements/database.go @@ -22,7 +22,7 @@ SELECT DISTINCT , u1.email AS created_by_email , u2.login AS updated_by_name , 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 { @@ -41,9 +41,9 @@ func syncFieldsWithModel(libraryElement *LibraryElement) error { return err } - if LibraryElementKind(libraryElement.Kind) == Panel { + if models.LibraryElementKind(libraryElement.Kind) == models.PanelElement { model["title"] = libraryElement.Name - } else if LibraryElementKind(libraryElement.Kind) == Variable { + } else if models.LibraryElementKind(libraryElement.Kind) == models.VariableElement { model["name"] = libraryElement.Name } if model["type"] != nil { @@ -520,7 +520,7 @@ func (l *LibraryElementService) getConnections(c *models.ReqContext, uid string) var libraryElementConnections []libraryElementConnectionWithMeta builder := sqlstore.SQLBuilder{} 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(" INNER JOIN dashboard AS dashboard on lec.connection_id = dashboard.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" + getFromLibraryElementDTOWithMeta(l.SQLStore.Dialect) + " 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) err := sess.Find(&libraryElements) 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. 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 := 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 { return err } @@ -646,7 +646,7 @@ func (l *LibraryElementService) connectElementsToDashboardID(c *models.ReqContex // disconnectElementsFromDashboardID deletes connections for all Library Elements in a Dashboard. func (l *LibraryElementService) disconnectElementsFromDashboardID(c *models.ReqContext, dashboardID int64) 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 { return err } @@ -676,7 +676,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c *models.ReqCo ConnectionID int64 `xorm:"connection_id"` } 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=?" err = session.SQL(sql, folderID, c.SignedInUser.OrgId).Find(&connectionIDs) if err != nil { @@ -694,7 +694,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c *models.ReqCo return err } 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 { return err } diff --git a/pkg/services/libraryelements/guard.go b/pkg/services/libraryelements/guard.go index c5d41295c0c..980eb6cb3f7 100644 --- a/pkg/services/libraryelements/guard.go +++ b/pkg/services/libraryelements/guard.go @@ -11,11 +11,11 @@ func isGeneralFolder(folderID int64) bool { } func (l *LibraryElementService) requireSupportedElementKind(kindAsInt int64) error { - kind := LibraryElementKind(kindAsInt) + kind := models.LibraryElementKind(kindAsInt) switch kind { - case Panel: + case models.PanelElement: return nil - case Variable: + case models.VariableElement: return nil default: return errLibraryElementUnSupportedElementKind diff --git a/pkg/services/libraryelements/libraryelements.go b/pkg/services/libraryelements/libraryelements.go index 3fc2eafb194..3fff1d6364c 100644 --- a/pkg/services/libraryelements/libraryelements.go +++ b/pkg/services/libraryelements/libraryelements.go @@ -6,7 +6,6 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/services/sqlstore" - "github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/setting" ) @@ -27,8 +26,6 @@ type LibraryElementService struct { log log.Logger } -const connectionTableName = "library_element_connection" - func init() { registry.RegisterService(&LibraryElementService{}) } @@ -66,51 +63,3 @@ func (l *LibraryElementService) DisconnectElementsFromDashboard(c *models.ReqCon func (l *LibraryElementService) DeleteLibraryElementsInFolder(c *models.ReqContext, folderUID string) error { 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])) -} diff --git a/pkg/services/libraryelements/libraryelements_create_test.go b/pkg/services/libraryelements/libraryelements_create_test.go index 1dce51efe0a..e200c471be0 100644 --- a/pkg/services/libraryelements/libraryelements_create_test.go +++ b/pkg/services/libraryelements/libraryelements_create_test.go @@ -5,6 +5,8 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/models" ) func TestCreateLibraryElement(t *testing.T) { @@ -24,7 +26,7 @@ func TestCreateLibraryElement(t *testing.T) { FolderID: 1, UID: sc.initialResult.Result.UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -69,7 +71,7 @@ func TestCreateLibraryElement(t *testing.T) { FolderID: 1, UID: result.Result.UID, Name: "Library Panel Name", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ diff --git a/pkg/services/libraryelements/libraryelements_get_all_test.go b/pkg/services/libraryelements/libraryelements_get_all_test.go index a20740f0c49..2358b18a21c 100644 --- a/pkg/services/libraryelements/libraryelements_get_all_test.go +++ b/pkg/services/libraryelements/libraryelements_get_all_test.go @@ -42,7 +42,7 @@ func TestGetAllLibraryElements(t *testing.T) { err := sc.reqContext.Req.ParseForm() 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) require.Equal(t, 200, resp.Status()) @@ -62,7 +62,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -107,7 +107,7 @@ func TestGetAllLibraryElements(t *testing.T) { err := sc.reqContext.Req.ParseForm() 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) require.Equal(t, 200, resp.Status()) @@ -127,7 +127,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "query0", - Kind: int64(Variable), + Kind: int64(models.VariableElement), Type: "query", Description: "A description", Model: map[string]interface{}{ @@ -187,7 +187,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -222,7 +222,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[1].UID, Name: "Text - Library Panel2", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -286,7 +286,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel2", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -321,7 +321,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[1].UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", 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", 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}", "id": 1, @@ -372,7 +372,7 @@ func TestGetAllLibraryElements(t *testing.T) { resp := sc.service.createHandler(sc.reqContext, command) 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}", "id": 1, @@ -405,7 +405,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "BarGauge - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "bargauge", Description: "BarGauge description", Model: map[string]interface{}{ @@ -440,7 +440,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[1].UID, Name: "Gauge - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "gauge", Description: "Gauge description", 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", 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}", "id": 1, @@ -542,7 +542,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: newFolder.Id, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel2", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -637,7 +637,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -672,7 +672,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[1].UID, Name: "Text - Library Panel2", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -736,7 +736,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel2", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -800,7 +800,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -865,7 +865,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel2", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", 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", 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}", "id": 1, @@ -939,7 +939,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", 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", 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}", "id": 1, @@ -1011,7 +1011,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Some Other", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A Library Panel", Model: map[string]interface{}{ @@ -1046,7 +1046,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[1].UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -1112,7 +1112,7 @@ func TestGetAllLibraryElements(t *testing.T) { FolderID: 1, UID: result.Result.Elements[0].UID, Name: "Text - Library Panel2", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ diff --git a/pkg/services/libraryelements/libraryelements_get_test.go b/pkg/services/libraryelements/libraryelements_get_test.go index 4f0218dceff..34e5d58bd1c 100644 --- a/pkg/services/libraryelements/libraryelements_get_test.go +++ b/pkg/services/libraryelements/libraryelements_get_test.go @@ -35,7 +35,7 @@ func TestGetLibraryElement(t *testing.T) { FolderID: 1, UID: res.Result.UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ @@ -130,7 +130,7 @@ func TestGetLibraryElement(t *testing.T) { FolderID: 1, UID: res.Result.UID, Name: "Text - Library Panel", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "text", Description: "A description", Model: map[string]interface{}{ diff --git a/pkg/services/libraryelements/libraryelements_patch_test.go b/pkg/services/libraryelements/libraryelements_patch_test.go index 7af2290db0e..2bda2b43019 100644 --- a/pkg/services/libraryelements/libraryelements_patch_test.go +++ b/pkg/services/libraryelements/libraryelements_patch_test.go @@ -5,12 +5,14 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/models" ) func TestPatchLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to patch a library panel that does not exist, it should fail", 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"}) resp := sc.service.patchHandler(sc.reqContext, cmd) require.Equal(t, 404, resp.Status()) @@ -31,7 +33,7 @@ func TestPatchLibraryElement(t *testing.T) { "type": "graph" } `), - Kind: int64(Panel), + Kind: int64(models.PanelElement), Version: 1, } sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) @@ -45,7 +47,7 @@ func TestPatchLibraryElement(t *testing.T) { FolderID: newFolder.Id, UID: sc.initialResult.Result.UID, Name: "Panel - New name", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Type: "graph", Description: "An updated description", Model: map[string]interface{}{ @@ -83,7 +85,7 @@ func TestPatchLibraryElement(t *testing.T) { newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{}) cmd := patchLibraryElementCommand{ FolderID: newFolder.Id, - Kind: int64(Panel), + Kind: int64(models.PanelElement), Version: 1, } sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) @@ -104,7 +106,7 @@ func TestPatchLibraryElement(t *testing.T) { cmd := patchLibraryElementCommand{ FolderID: -1, Name: "New Name", - Kind: int64(Panel), + Kind: int64(models.PanelElement), Version: 1, } sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) @@ -125,7 +127,7 @@ func TestPatchLibraryElement(t *testing.T) { cmd := patchLibraryElementCommand{ FolderID: -1, Model: []byte(`{ "title": "New Model Title", "name": "New Model Name", "type":"graph", "description": "New description" }`), - Kind: int64(Panel), + Kind: int64(models.PanelElement), Version: 1, } sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) @@ -152,7 +154,7 @@ func TestPatchLibraryElement(t *testing.T) { cmd := patchLibraryElementCommand{ FolderID: -1, Model: []byte(`{ "description": "New description" }`), - Kind: int64(Panel), + Kind: int64(models.PanelElement), Version: 1, } sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) @@ -178,7 +180,7 @@ func TestPatchLibraryElement(t *testing.T) { cmd := patchLibraryElementCommand{ FolderID: -1, Model: []byte(`{ "type": "graph" }`), - Kind: int64(Panel), + Kind: int64(models.PanelElement), Version: 1, } 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", 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.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) resp := sc.service.patchHandler(sc.reqContext, cmd) @@ -223,7 +225,7 @@ func TestPatchLibraryElement(t *testing.T) { cmd := patchLibraryElementCommand{ Name: "Text - Library Panel", Version: 1, - Kind: int64(Panel), + Kind: int64(models.PanelElement), } sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID}) resp = sc.service.patchHandler(sc.reqContext, cmd) @@ -239,7 +241,7 @@ func TestPatchLibraryElement(t *testing.T) { cmd := patchLibraryElementCommand{ FolderID: 1, Version: 1, - Kind: int64(Panel), + Kind: int64(models.PanelElement), } sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID}) resp = sc.service.patchHandler(sc.reqContext, cmd) @@ -251,7 +253,7 @@ func TestPatchLibraryElement(t *testing.T) { cmd := patchLibraryElementCommand{ FolderID: sc.folder.Id, Version: 1, - Kind: int64(Panel), + Kind: int64(models.PanelElement), } sc.reqContext.OrgId = 2 sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) @@ -264,7 +266,7 @@ func TestPatchLibraryElement(t *testing.T) { cmd := patchLibraryElementCommand{ FolderID: sc.folder.Id, Version: 1, - Kind: int64(Panel), + Kind: int64(models.PanelElement), } sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) resp := sc.service.patchHandler(sc.reqContext, cmd) @@ -278,14 +280,14 @@ func TestPatchLibraryElement(t *testing.T) { cmd := patchLibraryElementCommand{ FolderID: sc.folder.Id, Version: 1, - Kind: int64(Variable), + Kind: int64(models.VariableElement), } sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID}) resp := sc.service.patchHandler(sc.reqContext, cmd) require.Equal(t, 200, resp.Status()) var result = validateAndUnMarshalResponse(t, resp) 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.Model = map[string]interface{}{ "datasource": "${DS_GDEV-TESTDATA}", diff --git a/pkg/services/libraryelements/libraryelements_permissions_test.go b/pkg/services/libraryelements/libraryelements_permissions_test.go index 3a2ca821d69..045d41f49ee 100644 --- a/pkg/services/libraryelements/libraryelements_permissions_test.go +++ b/pkg/services/libraryelements/libraryelements_permissions_test.go @@ -84,7 +84,7 @@ func TestLibraryElementPermissions(t *testing.T) { toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items) 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}) resp = sc.service.patchHandler(sc.reqContext, cmd) 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) 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}) resp = sc.service.patchHandler(sc.reqContext, cmd) require.Equal(t, testCase.status, resp.Status()) @@ -146,7 +146,7 @@ func TestLibraryElementPermissions(t *testing.T) { result := validateAndUnMarshalResponse(t, resp) 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}) resp = sc.service.patchHandler(sc.reqContext, cmd) require.Equal(t, testCase.status, resp.Status()) @@ -160,7 +160,7 @@ func TestLibraryElementPermissions(t *testing.T) { result := validateAndUnMarshalResponse(t, resp) 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}) resp = sc.service.patchHandler(sc.reqContext, cmd) require.Equal(t, testCase.status, resp.Status()) @@ -205,7 +205,7 @@ func TestLibraryElementPermissions(t *testing.T) { result := validateAndUnMarshalResponse(t, resp) 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}) resp = sc.service.patchHandler(sc.reqContext, cmd) require.Equal(t, 404, resp.Status()) diff --git a/pkg/services/libraryelements/libraryelements_test.go b/pkg/services/libraryelements/libraryelements_test.go index 7cc9b854ae1..232dfaffe47 100644 --- a/pkg/services/libraryelements/libraryelements_test.go +++ b/pkg/services/libraryelements/libraryelements_test.go @@ -126,7 +126,7 @@ type libraryElementsSearchResult struct { } func getCreatePanelCommand(folderID int64, name string) CreateLibraryElementCommand { - command := getCreateCommandWithModel(folderID, name, Panel, []byte(` + command := getCreateCommandWithModel(folderID, name, models.PanelElement, []byte(` { "datasource": "${DS_GDEV-TESTDATA}", "id": 1, @@ -140,7 +140,7 @@ func getCreatePanelCommand(folderID int64, name string) CreateLibraryElementComm } func getCreateVariableCommand(folderID int64, name string) CreateLibraryElementCommand { - command := getCreateCommandWithModel(folderID, name, Variable, []byte(` + command := getCreateCommandWithModel(folderID, name, models.VariableElement, []byte(` { "datasource": "${DS_GDEV-TESTDATA}", "name": "query0", @@ -152,7 +152,7 @@ func getCreateVariableCommand(folderID int64, name string) CreateLibraryElementC 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{ FolderID: folderID, Name: name, diff --git a/pkg/services/libraryelements/models.go b/pkg/services/libraryelements/models.go index 6dbe926e21c..fb0670c3992 100644 --- a/pkg/services/libraryelements/models.go +++ b/pkg/services/libraryelements/models.go @@ -6,13 +6,6 @@ import ( "time" ) -type LibraryElementKind int - -const ( - Panel LibraryElementKind = iota + 1 - Variable -) - type LibraryConnectionKind int const ( diff --git a/pkg/services/libraryelements/writers.go b/pkg/services/libraryelements/writers.go index f8dcd52bcdf..d935fbe8b26 100644 --- a/pkg/services/libraryelements/writers.go +++ b/pkg/services/libraryelements/writers.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + "github.com/grafana/grafana/pkg/models" "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) { - 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) } } diff --git a/pkg/services/librarypanels/librarypanels.go b/pkg/services/librarypanels/librarypanels.go index 10a8dd9378f..90daff312a0 100644 --- a/pkg/services/librarypanels/librarypanels.go +++ b/pkg/services/librarypanels/librarypanels.go @@ -76,7 +76,7 @@ func (lps *LibraryPanelService) LoadLibraryPanelsForDashboard(c *models.ReqConte continue } - if libraryelements.LibraryElementKind(elementInDB.Kind) != libraryelements.Panel { + if models.LibraryElementKind(elementInDB.Kind) != models.PanelElement { continue } diff --git a/pkg/services/librarypanels/librarypanels_test.go b/pkg/services/librarypanels/librarypanels_test.go index 7ee014d4a34..2972a71ac93 100644 --- a/pkg/services/librarypanels/librarypanels_test.go +++ b/pkg/services/librarypanels/librarypanels_test.go @@ -493,7 +493,7 @@ func TestConnectLibraryPanelsForDashboard(t *testing.T) { "description": "Unused description" } `), - Kind: int64(libraryelements.Panel), + Kind: int64(models.PanelElement), }) require.NoError(t, err) dashJSON := map[string]interface{}{ @@ -783,7 +783,7 @@ func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, s "description": "A description" } `), - Kind: int64(libraryelements.Panel), + Kind: int64(models.PanelElement), } resp, err := sc.elementService.CreateElement(sc.reqContext, command) require.NoError(t, err) diff --git a/pkg/services/sqlstore/migrations/libraryelements.go b/pkg/services/sqlstore/migrations/libraryelements.go new file mode 100644 index 00000000000..099e204fc1e --- /dev/null +++ b/pkg/services/sqlstore/migrations/libraryelements.go @@ -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])) +} diff --git a/pkg/services/sqlstore/migrations/migrations.go b/pkg/services/sqlstore/migrations/migrations.go index 30c0afa4295..b57309ef307 100644 --- a/pkg/services/sqlstore/migrations/migrations.go +++ b/pkg/services/sqlstore/migrations/migrations.go @@ -40,6 +40,7 @@ func AddMigrations(mg *Migrator) { addShortURLMigrations(mg) ualert.AddTablesMigrations(mg) ualert.AddDashAlertMigration(mg) + addLibraryElementsMigrations(mg) } func addMigrationLogMigrations(mg *Migrator) { diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go index b7341ab7d7f..2df3117b0de 100644 --- a/pkg/services/sqlstore/stats.go +++ b/pkg/services/sqlstore/stats.go @@ -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("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("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()) diff --git a/pkg/services/sqlstore/stats_test.go b/pkg/services/sqlstore/stats_test.go index 60f073223fc..4faee99cbdd 100644 --- a/pkg/services/sqlstore/stats_test.go +++ b/pkg/services/sqlstore/stats_test.go @@ -24,6 +24,8 @@ func TestStatsDataAccess(t *testing.T) { assert.Equal(t, 0, query.Result.Editors) assert.Equal(t, 0, query.Result.Viewers) 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) {