Skip to content

Commit

Permalink
[v9.4.x] Dashboards: Evaluate provisioned dashboard titles in a backw…
Browse files Browse the repository at this point in the history
…ards compatible way (#65440)

Backport from #65184 using github.com/xorcare/pointer instead of the newer util.Pointer.

(cherry picked from commit b210a39)

---------

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
  • Loading branch information
grafanabot and sakjur committed Mar 29, 2023
1 parent 90db06d commit 266a740
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 16 deletions.
16 changes: 13 additions & 3 deletions pkg/services/dashboards/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -937,12 +937,22 @@ func (d *DashboardStore) deleteAlertDefinition(dashboardId int64, sess *db.Sessi
func (d *DashboardStore) GetDashboard(ctx context.Context, query *dashboards.GetDashboardQuery) (*dashboards.Dashboard, error) {
var queryResult *dashboards.Dashboard
err := d.store.WithDbSession(ctx, func(sess *db.Session) error {
if query.ID == 0 && len(query.Slug) == 0 && len(query.UID) == 0 {
if query.ID == 0 && len(query.UID) == 0 && (query.Title == nil || query.FolderID == nil) {
return dashboards.ErrDashboardIdentifierNotSet
}

dashboard := dashboards.Dashboard{Slug: query.Slug, OrgID: query.OrgID, ID: query.ID, UID: query.UID}
has, err := sess.Get(&dashboard)
dashboard := dashboards.Dashboard{OrgID: query.OrgID, ID: query.ID, UID: query.UID}
mustCols := []string{}
if query.Title != nil {
dashboard.Title = *query.Title
mustCols = append(mustCols, "title")
}
if query.FolderID != nil {
dashboard.FolderID = *query.FolderID
mustCols = append(mustCols, "folder_id")
}

has, err := sess.MustCols(mustCols...).Get(&dashboard)

if err != nil {
return err
Expand Down
33 changes: 29 additions & 4 deletions pkg/services/dashboards/database/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import (
"testing"
"time"

"github.com/grafana/grafana/pkg/expr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/xorcare/pointer"

"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/expr"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
Expand Down Expand Up @@ -91,11 +92,12 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
require.False(t, queryResult.IsFolder)
})

t.Run("Should be able to get dashboard by slug", func(t *testing.T) {
t.Run("Should be able to get dashboard by title and folderID", func(t *testing.T) {
setup()
query := dashboards.GetDashboardQuery{
Slug: "test-dash-23",
OrgID: 1,
Title: pointer.String("test dash 23"),
FolderID: &savedFolder.ID,
OrgID: 1,
}

queryResult, err := dashboardStore.GetDashboard(context.Background(), &query)
Expand All @@ -108,6 +110,29 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
require.False(t, queryResult.IsFolder)
})

t.Run("Should not be able to get dashboard by title alone", func(t *testing.T) {
setup()
query := dashboards.GetDashboardQuery{
Title: pointer.String("test dash 23"),
OrgID: 1,
}

_, err := dashboardStore.GetDashboard(context.Background(), &query)
require.ErrorIs(t, err, dashboards.ErrDashboardIdentifierNotSet)
})

t.Run("Folder=0 should not be able to get a dashboard in a folder", func(t *testing.T) {
setup()
query := dashboards.GetDashboardQuery{
Title: pointer.String("test dash 23"),
FolderID: pointer.Int64(0),
OrgID: 1,
}

_, err := dashboardStore.GetDashboard(context.Background(), &query)
require.ErrorIs(t, err, dashboards.ErrDashboardNotFound)
})

t.Run("Should be able to get dashboard by uid", func(t *testing.T) {
setup()
query := dashboards.GetDashboardQuery{
Expand Down
22 changes: 18 additions & 4 deletions pkg/services/dashboards/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,25 @@ type DeleteOrphanedProvisionedDashboardsCommand struct {
// QUERIES
//

// GetDashboardQuery is used to query for a single dashboard matching
// a unique constraint within the provided OrgID.
//
// Available constraints:
// - ID uses Grafana's internal numeric database identifier to get a
// dashboard.
// - UID use the unique identifier to get a dashboard.
// - Title + FolderID uses the combination of the dashboard's
// human-readable title and its parent folder's ID
// (or zero, for top level items). Both are required if no other
// constraint is set.
//
// Multiple constraints can be combined.
type GetDashboardQuery struct {
Slug string // required if no ID or Uid is specified
ID int64 // optional if slug is set
UID string // optional if slug is set
OrgID int64
ID int64
UID string
Title *string
FolderID *int64
OrgID int64
}

type DashboardTagCloudItem struct {
Expand Down
8 changes: 5 additions & 3 deletions pkg/services/provisioning/alerting/rules_provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"errors"
"fmt"

"github.com/xorcare/pointer"

"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/slugify"
"github.com/grafana/grafana/pkg/services/dashboards"
alert_models "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
Expand Down Expand Up @@ -97,8 +98,9 @@ func (prov *defaultAlertRuleProvisioner) provisionRule(
func (prov *defaultAlertRuleProvisioner) getOrCreateFolderUID(
ctx context.Context, folderName string, orgID int64) (string, error) {
cmd := &dashboards.GetDashboardQuery{
Slug: slugify.Slugify(folderName),
OrgID: orgID,
Title: &folderName,
FolderID: pointer.Int64(0),
OrgID: orgID,
}
cmdResult, err := prov.dashboardService.GetDashboard(ctx, cmd)
if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
Expand Down
9 changes: 7 additions & 2 deletions pkg/services/provisioning/dashboards/file_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import (
"sync"
"time"

"github.com/xorcare/pointer"

"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/slugify"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/provisioning/utils"
Expand Down Expand Up @@ -299,7 +300,11 @@ func (fr *FileReader) getOrCreateFolderID(ctx context.Context, cfg *config, serv
return 0, ErrFolderNameMissing
}

cmd := &dashboards.GetDashboardQuery{Slug: slugify.Slugify(folderName), OrgID: cfg.OrgID}
cmd := &dashboards.GetDashboardQuery{
Title: &folderName,
FolderID: pointer.Int64(0),
OrgID: cfg.OrgID,
}
result, err := fr.dashboardStore.GetDashboard(ctx, cmd)

if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
Expand Down

0 comments on commit 266a740

Please sign in to comment.