The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/publicdashboards/service/service_test.go

1833 lines
57 KiB

package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/dashboards"
dashboardsDB "github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/featuremgmt"
. "github.com/grafana/grafana/pkg/services/publicdashboards"
"github.com/grafana/grafana/pkg/services/publicdashboards/database"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/publicdashboards/validation"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
"github.com/grafana/grafana/pkg/util"
)
var timeSettings = &TimeSettings{From: "now-12h", To: "now"}
var defaultPubdashTimeSettings = &TimeSettings{}
var dashboardData = simplejson.NewFromAny(map[string]any{"time": map[string]any{"from": "now-8h", "to": "now"}})
var SignedInUser = &user.SignedInUser{UserID: 1234, Login: "user@login.com"}
func TestLogPrefix(t *testing.T) {
assert.Equal(t, LogPrefix, "publicdashboards.service")
}
func TestGetPublicDashboardForView(t *testing.T) {
type storeResp struct {
pd *PublicDashboard
d *dashboards.Dashboard
err error
}
const dashboardWithRowsAndHiddenQueries = `
{
"panels": [
{
"id": 2,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "_yxMP8Ynk"
},
"exemplar": true,
"expr": "go_goroutines{job=\"$job\"}",
"interval": "",
"legendFormat": "",
"refId": "A",
"hide": true
},
{
"datasource": {
"type": "prometheus",
"uid": "promds2"
},
"exemplar": true,
"expr": "query2",
"interval": "",
"legendFormat": "",
"refId": "B"
}
],
"title": "Panel Title",
"type": "timeseries"
},
{
"id": 3,
"collapsed": true,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 9
},
"title": "This panel is a Row",
"type": "row",
"panels": [
{
"id": 4,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "_yxMP8Ynk"
},
"exemplar": true,
"expr": "go_goroutines{job=\"$job\"}",
"interval": "",
"legendFormat": "",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "promds2"
},
"exemplar": true,
"expr": "query2",
"interval": "",
"legendFormat": "",
"refId": "B"
}
],
"title": "Panel inside a row",
"type": "timeseries"
}
]
},
{
"aliasColors": {
"total avg": "#6ed0e0"
},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "mssql",
"uid": "P6B08AC199690F328"
},
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 2,
"fillGradient": 0,
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 0
},
"hiddenSeries": false,
"id": 4,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "10.2.0-pre",
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [
{
"alias": "total avg",
"fill": 0,
"pointradius": 3,
"points": true
}
],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "",
"datasource": {
"type": "mssql",
"uid": "P6B08AC199690F328"
},
"format": "time_series",
"rawSql": "SELECT\n $__timeGroup(createdAt,'$summarize') as time,\n avg(value) as value,\n hostname as metric\nFROM \n grafana_metric\nWHERE\n $__timeFilter(createdAt) AND\n measurement = 'logins.count' AND\n hostname IN($host)\nGROUP BY $__timeGroup(createdAt,'$summarize'), hostname\nORDER BY 1",
"refId": "A"
},
{
"alias": "",
"datasource": {
"type": "mssql",
"uid": "P6B08AC199690F328"
},
"format": "time_series",
"rawSql": "SELECT\n $__timeGroup(createdAt,'$summarize') as time,\n min(value) as value,\n 'total avg' as metric\nFROM \n grafana_metric\nWHERE\n $__timeFilter(createdAt) AND\n measurement = 'logins.count'\nGROUP BY $__timeGroup(createdAt,'$summarize')\nORDER BY 1",
"refId": "B"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Average logins / $summarize",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"logBase": 1,
"show": true
},
{
"format": "short",
"logBase": 1,
"show": true
}
],
"yaxis": {
"align": false
}
},
{
"datasource": {
"type": "influxdb",
"uid": "P49A45DF074423DFB"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 0
},
"id": 5,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "/^retentionPeriod 4a2f27036bf63a3c$/",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.2.0-pre",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "P49A45DF074423DFB"
},
"query": "buckets()",
"refId": "A"
}
],
"title": "Panel Title",
"type": "stat"
}
],
"schemaVersion": 35,
"timepicker": {
"hidden": false
}
}`
data, _ := simplejson.NewJson([]byte(dashboardWithRowsAndHiddenQueries))
now := time.Now()
// #nosec G101 -- This is dummy/test token
accessToken := "c54b1c4dd2b143a1a7a43005264d256d"
d := &dashboards.Dashboard{UID: "mydashboard", Data: data, Slug: "dashboardSlug", Created: now, Updated: now, Version: 1, FolderID: 1}
testCases := []struct {
Name string
AccessToken string
StoreResp *storeResp
ErrResp error
DashResp *dtos.DashboardFullWithMeta
}{
{
Name: "returns a dashboard with the time picker shown",
AccessToken: accessToken,
StoreResp: &storeResp{
pd: &PublicDashboard{AccessToken: accessToken, IsEnabled: true, TimeSelectionEnabled: true},
d: d,
err: nil,
},
ErrResp: nil,
DashResp: &dtos.DashboardFullWithMeta{
Dashboard: data,
Meta: dtos.DashboardMeta{
Slug: d.Slug,
Type: dashboards.DashTypeDB,
CanStar: false,
CanSave: false,
CanEdit: false,
CanAdmin: false,
CanDelete: false,
Created: d.Created,
Updated: d.Updated,
Version: d.Version,
IsFolder: false,
FolderId: d.FolderID,
PublicDashboardEnabled: true,
},
},
},
{
Name: "returns a dashboard with the time picker hidden",
AccessToken: accessToken,
StoreResp: &storeResp{
pd: &PublicDashboard{AccessToken: accessToken, IsEnabled: true, TimeSelectionEnabled: false},
d: d,
err: nil,
},
ErrResp: nil,
DashResp: &dtos.DashboardFullWithMeta{
Dashboard: data,
Meta: dtos.DashboardMeta{
Slug: d.Slug,
Type: dashboards.DashTypeDB,
CanStar: false,
CanSave: false,
CanEdit: false,
CanAdmin: false,
CanDelete: false,
Created: d.Created,
Updated: d.Updated,
Version: d.Version,
IsFolder: false,
FolderId: d.FolderID,
PublicDashboardEnabled: true,
},
},
},
}
for _, test := range testCases {
t.Run(test.Name, func(t *testing.T) {
fakeStore := FakePublicDashboardStore{}
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: &fakeStore,
}
fakeStore.On("FindByAccessToken", mock.Anything, mock.Anything).Return(test.StoreResp.pd, test.StoreResp.err)
fakeStore.On("FindDashboard", mock.Anything, mock.Anything, mock.Anything).Return(test.StoreResp.d, test.StoreResp.err)
dashboardFullWithMeta, err := service.GetPublicDashboardForView(context.Background(), test.AccessToken)
if test.ErrResp != nil {
assert.Error(t, test.ErrResp, err)
} else {
require.NoError(t, err)
}
//assert.Equal(t, test.DashResp, dash)
if test.DashResp != nil {
assert.Equal(t, test.DashResp.Meta.Slug, dashboardFullWithMeta.Meta.Slug)
assert.Equal(t, test.DashResp.Meta.Type, dashboardFullWithMeta.Meta.Type)
assert.Equal(t, false, dashboardFullWithMeta.Meta.CanStar)
assert.Equal(t, false, dashboardFullWithMeta.Meta.CanSave)
assert.Equal(t, false, dashboardFullWithMeta.Meta.CanEdit)
assert.Equal(t, false, dashboardFullWithMeta.Meta.CanAdmin)
assert.Equal(t, false, dashboardFullWithMeta.Meta.CanDelete)
assert.Equal(t, test.DashResp.Meta.Created, dashboardFullWithMeta.Meta.Created)
assert.Equal(t, test.DashResp.Meta.Updated, dashboardFullWithMeta.Meta.Updated)
assert.Equal(t, test.DashResp.Meta.Version, dashboardFullWithMeta.Meta.Version)
assert.Equal(t, false, dashboardFullWithMeta.Meta.IsFolder)
assert.Equal(t, test.DashResp.Meta.FolderId, dashboardFullWithMeta.Meta.FolderId)
assert.Equal(t, test.DashResp.Meta.PublicDashboardEnabled, dashboardFullWithMeta.Meta.PublicDashboardEnabled)
// hide the timepicker if the time selection is disabled
assert.Equal(t, test.StoreResp.pd.TimeSelectionEnabled, !dashboardFullWithMeta.Dashboard.Get("timepicker").Get("hidden").MustBool())
for _, panelObj := range dashboardFullWithMeta.Dashboard.Get("panels").MustArray() {
panel := simplejson.NewFromAny(panelObj)
// if the panel is a row and it is collapsed, get the queries from the panels inside the row
if panel.Get("type").MustString() == "row" && panel.Get("collapsed").MustBool() {
// recursive call to get queries from panels inside a row
sanitizeData(panel)
continue
}
for _, targetObj := range panel.Get("targets").MustArray() {
target := simplejson.NewFromAny(targetObj)
assert.Empty(t, target.Get("expr").MustString())
assert.Empty(t, target.Get("query").MustString())
assert.Empty(t, target.Get("rawSql").MustString())
}
}
}
})
}
}
func TestGetPublicDashboard(t *testing.T) {
type storeResp struct {
pd *PublicDashboard
d *dashboards.Dashboard
err error
}
testCases := []struct {
Name string
AccessToken string
StoreResp *storeResp
ErrResp error
DashResp *dashboards.Dashboard
}{
{
Name: "returns a dashboard",
AccessToken: "abc123",
StoreResp: &storeResp{
pd: &PublicDashboard{AccessToken: "abcdToken", IsEnabled: true},
d: &dashboards.Dashboard{UID: "mydashboard", Data: dashboardData},
err: nil,
},
ErrResp: nil,
DashResp: &dashboards.Dashboard{UID: "mydashboard", Data: dashboardData},
},
{
Name: "returns dashboard when isEnabled is false",
AccessToken: "abc123",
StoreResp: &storeResp{
pd: &PublicDashboard{AccessToken: "abcdToken", IsEnabled: false},
d: &dashboards.Dashboard{UID: "mydashboard", Data: dashboardData},
err: nil,
},
ErrResp: nil,
DashResp: &dashboards.Dashboard{UID: "mydashboard", Data: dashboardData},
},
{
Name: "returns ErrPublicDashboardNotFound if PublicDashboard missing",
AccessToken: "abc123",
StoreResp: &storeResp{pd: nil, d: nil, err: nil},
ErrResp: ErrPublicDashboardNotFound,
DashResp: nil,
},
{
Name: "returns ErrPublicDashboardNotFound if Dashboard missing",
AccessToken: "abc123",
StoreResp: &storeResp{pd: nil, d: nil, err: nil},
ErrResp: ErrPublicDashboardNotFound,
DashResp: nil,
},
}
for _, test := range testCases {
t.Run(test.Name, func(t *testing.T) {
fakeStore := FakePublicDashboardStore{}
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: &fakeStore,
}
fakeStore.On("FindByAccessToken", mock.Anything, mock.Anything).Return(test.StoreResp.pd, test.StoreResp.err)
fakeStore.On("FindDashboard", mock.Anything, mock.Anything, mock.Anything).Return(test.StoreResp.d, test.StoreResp.err)
pdc, dash, err := service.FindPublicDashboardAndDashboardByAccessToken(context.Background(), test.AccessToken)
if test.ErrResp != nil {
assert.Error(t, test.ErrResp, err)
} else {
require.NoError(t, err)
}
assert.Equal(t, test.DashResp, dash)
if test.DashResp != nil {
assert.NotNil(t, dash.CreatedBy)
assert.Equal(t, test.StoreResp.pd, pdc)
}
})
}
}
func TestGetEnabledPublicDashboard(t *testing.T) {
type storeResp struct {
pd *PublicDashboard
d *dashboards.Dashboard
err error
}
testCases := []struct {
Name string
AccessToken string
StoreResp *storeResp
ErrResp error
DashResp *dashboards.Dashboard
}{
{
Name: "returns a dashboard",
AccessToken: "abc123",
StoreResp: &storeResp{
pd: &PublicDashboard{AccessToken: "abcdToken", IsEnabled: true},
d: &dashboards.Dashboard{UID: "mydashboard", Data: dashboardData},
err: nil,
},
ErrResp: nil,
DashResp: &dashboards.Dashboard{UID: "mydashboard", Data: dashboardData},
},
{
Name: "returns ErrPublicDashboardNotFound when isEnabled is false",
AccessToken: "abc123",
StoreResp: &storeResp{
pd: &PublicDashboard{AccessToken: "abcdToken", IsEnabled: false},
d: &dashboards.Dashboard{UID: "mydashboard"},
err: nil,
},
ErrResp: ErrPublicDashboardNotFound,
DashResp: nil,
},
}
for _, test := range testCases {
t.Run(test.Name, func(t *testing.T) {
fakeStore := FakePublicDashboardStore{}
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: &fakeStore,
}
fakeStore.On("FindByAccessToken", mock.Anything, mock.Anything).Return(test.StoreResp.pd, test.StoreResp.err)
fakeStore.On("FindDashboard", mock.Anything, mock.Anything, mock.Anything).Return(test.StoreResp.d, test.StoreResp.err)
pdc, dash, err := service.FindEnabledPublicDashboardAndDashboardByAccessToken(context.Background(), test.AccessToken)
if test.ErrResp != nil {
assert.Error(t, test.ErrResp, err)
} else {
require.NoError(t, err)
}
assert.Equal(t, test.DashResp, dash)
if test.DashResp != nil {
assert.NotNil(t, dash.CreatedBy)
assert.Equal(t, test.StoreResp.pd, pdc)
}
})
}
}
// We're using sqlite here because testing all of the behaviors with mocks in
// the correct order is convoluted.
func TestCreatePublicDashboard(t *testing.T) {
t.Run("Create public dashboard", func(t *testing.T) {
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled, annotationsEnabled, timeSelectionEnabled := true, false, true
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
UserId: 7,
OrgID: dashboard.OrgID,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
AnnotationsEnabled: &annotationsEnabled,
TimeSelectionEnabled: &timeSelectionEnabled,
Share: EmailShareType,
},
}
_, err = service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgID, dashboard.UID)
require.NoError(t, err)
// DashboardUid/OrgId/CreatedBy set by the command, not parameters
assert.Equal(t, dashboard.UID, pubdash.DashboardUid)
assert.Equal(t, dashboard.OrgID, pubdash.OrgId)
assert.Equal(t, dto.UserId, pubdash.CreatedBy)
assert.Equal(t, *dto.PublicDashboard.AnnotationsEnabled, pubdash.AnnotationsEnabled)
assert.Equal(t, *dto.PublicDashboard.TimeSelectionEnabled, pubdash.TimeSelectionEnabled)
// ExistsEnabledByDashboardUid set by parameters
assert.Equal(t, *dto.PublicDashboard.IsEnabled, pubdash.IsEnabled)
// CreatedAt set to non-zero time
assert.NotEqual(t, &time.Time{}, pubdash.CreatedAt)
assert.Equal(t, dto.PublicDashboard.Share, pubdash.Share)
// accessToken is valid uuid
_, err = uuid.Parse(pubdash.AccessToken)
require.NoError(t, err, "expected a valid UUID, got %s", pubdash.AccessToken)
})
trueBooleanField := true
testCases := []struct {
Name string
IsEnabled *bool
TimeSelectionEnabled *bool
AnnotationsEnabled *bool
}{
{
Name: "isEnabled",
IsEnabled: nil,
TimeSelectionEnabled: &trueBooleanField,
AnnotationsEnabled: &trueBooleanField,
},
{
Name: "timeSelectionEnabled",
IsEnabled: &trueBooleanField,
TimeSelectionEnabled: nil,
AnnotationsEnabled: &trueBooleanField,
},
{
Name: "annotationsEnabled",
IsEnabled: &trueBooleanField,
TimeSelectionEnabled: &trueBooleanField,
AnnotationsEnabled: nil,
},
{
Name: "isEnabled, timeSelectionEnabled and annotationsEnabled",
IsEnabled: nil,
TimeSelectionEnabled: nil,
AnnotationsEnabled: nil,
},
}
for _, tt := range testCases {
t.Run(fmt.Sprintf("Create public dashboard with %s null boolean fields stores them as false", tt.Name), func(t *testing.T) {
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
UserId: 7,
OrgID: dashboard.OrgID,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: tt.IsEnabled,
TimeSelectionEnabled: tt.TimeSelectionEnabled,
AnnotationsEnabled: tt.AnnotationsEnabled,
Share: PublicShareType,
},
}
_, err = service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgID, dashboard.UID)
require.NoError(t, err)
assertFalseIfNull(t, pubdash.IsEnabled, dto.PublicDashboard.IsEnabled)
assertFalseIfNull(t, pubdash.TimeSelectionEnabled, dto.PublicDashboard.TimeSelectionEnabled)
assertFalseIfNull(t, pubdash.AnnotationsEnabled, dto.PublicDashboard.AnnotationsEnabled)
})
}
t.Run("Validate pubdash has default time setting value", func(t *testing.T) {
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled := true
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
OrgID: dashboard.OrgID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
},
}
_, err = service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgID, dashboard.UID)
require.NoError(t, err)
assert.Equal(t, defaultPubdashTimeSettings, pubdash.TimeSettings)
})
t.Run("Creates pubdash whose dashboard has template variables successfully", func(t *testing.T) {
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
templateVars := make([]map[string]any, 1)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, templateVars, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled := true
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
OrgID: dashboard.OrgID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
},
}
_, err = service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgID, dashboard.UID)
require.NoError(t, err)
assert.Equal(t, dashboard.UID, pubdash.DashboardUid)
assert.Equal(t, dashboard.OrgID, pubdash.OrgId)
})
t.Run("Throws an error when given pubdash uid already exists", func(t *testing.T) {
dashboard := dashboards.NewDashboard("testDashie")
pubdash := &PublicDashboard{
Uid: "ExistingUid",
IsEnabled: true,
AnnotationsEnabled: false,
DashboardUid: "NOTTHESAME",
OrgId: dashboard.OrgID,
TimeSettings: timeSettings,
}
publicDashboardStore := &FakePublicDashboardStore{}
publicDashboardStore.On("FindDashboard", mock.Anything, mock.Anything, mock.Anything).Return(dashboard, nil)
publicDashboardStore.On("Find", mock.Anything, "ExistingUid").Return(pubdash, nil)
publicDashboardStore.On("FindByDashboardUid", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrPublicDashboardNotFound.Errorf(""))
serviceWrapper := ProvideServiceWrapper(publicDashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicDashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled := true
dto := &SavePublicDashboardDTO{
DashboardUid: "an-id",
OrgID: dashboard.OrgID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
Uid: "ExistingUid",
IsEnabled: &isEnabled,
},
}
_, err := service.Create(context.Background(), SignedInUser, dto)
require.Error(t, err)
require.Equal(t, err, ErrPublicDashboardUidExists.Errorf("Create: public dashboard uid %s already exists", dto.PublicDashboard.Uid))
})
t.Run("Create public dashboard with given pubdash uid", func(t *testing.T) {
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled := true
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
UserId: 7,
OrgID: dashboard.OrgID,
PublicDashboard: &PublicDashboardDTO{
Uid: "GivenUid",
IsEnabled: &isEnabled,
},
}
_, err = service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgID, dashboard.UID)
require.NoError(t, err)
assert.Equal(t, dto.PublicDashboard.Uid, pubdash.Uid)
})
t.Run("Throws an error when pubdash with given access token input already exists", func(t *testing.T) {
dashboard := dashboards.NewDashboard("testDashie")
pubdash := &PublicDashboard{
Uid: "ExistingUid",
AccessToken: "ExistingAccessToken",
IsEnabled: true,
AnnotationsEnabled: false,
DashboardUid: "NOTTHESAME",
OrgId: dashboard.OrgID,
TimeSettings: timeSettings,
}
publicDashboardStore := &FakePublicDashboardStore{}
publicDashboardStore.On("FindDashboard", mock.Anything, mock.Anything, mock.Anything).Return(dashboard, nil)
publicDashboardStore.On("Find", mock.Anything, mock.Anything).Return(nil, nil)
publicDashboardStore.On("FindByAccessToken", mock.Anything, "ExistingAccessToken").Return(pubdash, nil)
publicDashboardStore.On("FindByDashboardUid", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrPublicDashboardNotFound.Errorf(""))
serviceWrapper := ProvideServiceWrapper(publicDashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicDashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled := true
dto := &SavePublicDashboardDTO{
DashboardUid: "an-id",
OrgID: dashboard.OrgID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
AccessToken: "ExistingAccessToken",
IsEnabled: &isEnabled,
},
}
_, err := service.Create(context.Background(), SignedInUser, dto)
require.Error(t, err)
require.Equal(t, err, ErrPublicDashboardAccessTokenExists.Errorf("Create: public dashboard access token %s already exists", dto.PublicDashboard.AccessToken))
})
t.Run("Create public dashboard with given pubdash access token", func(t *testing.T) {
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled := true
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
UserId: 7,
OrgID: dashboard.OrgID,
PublicDashboard: &PublicDashboardDTO{
AccessToken: "GivenAccessToken",
IsEnabled: &isEnabled,
},
}
_, err = service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgID, dashboard.UID)
require.NoError(t, err)
assert.Equal(t, dto.PublicDashboard.AccessToken, pubdash.AccessToken)
})
t.Run("Throws an error when pubdash with generated access token already exists", func(t *testing.T) {
dashboard := dashboards.NewDashboard("testDashie")
pubdash := &PublicDashboard{
IsEnabled: true,
AnnotationsEnabled: false,
DashboardUid: "NOTTHESAME",
OrgId: dashboard.OrgID,
TimeSettings: timeSettings,
}
publicDashboardStore := &FakePublicDashboardStore{}
publicDashboardStore.On("FindByAccessToken", mock.Anything, mock.Anything).Return(pubdash, nil)
serviceWrapper := ProvideServiceWrapper(publicDashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicDashboardStore,
serviceWrapper: serviceWrapper,
}
_, err := service.NewPublicDashboardAccessToken(context.Background())
require.Error(t, err)
require.Equal(t, err, ErrInternalServerError.Errorf("failed to generate a unique accessToken for public dashboard"))
})
t.Run("Returns error if public dashboard exists", func(t *testing.T) {
sqlStore := db.InitTestDB(t)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
require.NoError(t, err)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
publicdashboardStore := &FakePublicDashboardStore{}
publicdashboardStore.On("FindByDashboardUid", mock.Anything, mock.Anything, mock.Anything).Return(&PublicDashboard{Uid: "newPubdashUid"}, nil)
publicdashboardStore.On("FindDashboard", mock.Anything, mock.Anything, mock.Anything).Return(dashboard, nil)
publicdashboardStore.On("Find", mock.Anything, mock.Anything).Return(nil, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled, annotationsEnabled := true, false
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
AnnotationsEnabled: &annotationsEnabled,
IsEnabled: &isEnabled,
},
}
savedPubdash, err := service.Create(context.Background(), SignedInUser, dto)
assert.Error(t, err)
assert.Nil(t, savedPubdash)
assert.True(t, ErrDashboardIsPublic.Is(err))
})
t.Run("Validate pubdash has default share value", func(t *testing.T) {
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled := true
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
OrgID: dashboard.OrgID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
},
}
_, err = service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgID, dashboard.UID)
require.NoError(t, err)
// if share type is empty should be populated with public by default
assert.Equal(t, PublicShareType, pubdash.Share)
})
}
func assertFalseIfNull(t *testing.T, expectedValue bool, nullableValue *bool) {
if nullableValue == nil {
assert.Equal(t, expectedValue, false)
} else {
assert.Equal(t, expectedValue, *nullableValue)
}
}
func TestUpdatePublicDashboard(t *testing.T) {
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
dashboard2 := insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, true, []map[string]any{}, nil)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
t.Run("Updating public dashboard", func(t *testing.T) {
isEnabled, annotationsEnabled, timeSelectionEnabled := true, false, false
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
AnnotationsEnabled: &annotationsEnabled,
TimeSelectionEnabled: &timeSelectionEnabled,
},
}
// insert initial pubdash
savedPubdash, err := service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
isEnabled, annotationsEnabled, timeSelectionEnabled = true, true, true
dto = &SavePublicDashboardDTO{
Uid: savedPubdash.Uid,
DashboardUid: dashboard.UID,
OrgID: 9,
UserId: 8,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
AnnotationsEnabled: &annotationsEnabled,
TimeSelectionEnabled: &timeSelectionEnabled,
},
}
updatedPubdash, err := service.Update(context.Background(), SignedInUser, dto)
require.NoError(t, err)
// don't get updated
assert.Equal(t, savedPubdash.DashboardUid, updatedPubdash.DashboardUid)
assert.Equal(t, savedPubdash.OrgId, updatedPubdash.OrgId)
assert.Equal(t, savedPubdash.CreatedAt, updatedPubdash.CreatedAt)
assert.Equal(t, savedPubdash.CreatedBy, updatedPubdash.CreatedBy)
assert.Equal(t, savedPubdash.AccessToken, updatedPubdash.AccessToken)
// gets updated
assert.Equal(t, *dto.PublicDashboard.IsEnabled, updatedPubdash.IsEnabled)
assert.Equal(t, *dto.PublicDashboard.AnnotationsEnabled, updatedPubdash.AnnotationsEnabled)
assert.Equal(t, *dto.PublicDashboard.TimeSelectionEnabled, updatedPubdash.TimeSelectionEnabled)
assert.Equal(t, dto.UserId, updatedPubdash.UpdatedBy)
assert.NotEqual(t, &time.Time{}, updatedPubdash.UpdatedAt)
})
t.Run("Updating set empty time settings", func(t *testing.T) {
isEnabled := true
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
},
}
savedPubdash, err := service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
dto = &SavePublicDashboardDTO{
Uid: savedPubdash.Uid,
DashboardUid: dashboard.UID,
OrgID: 9,
UserId: 8,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
},
}
updatedPubdash, err := service.Update(context.Background(), SignedInUser, dto)
require.NoError(t, err)
assert.Equal(t, &TimeSettings{}, updatedPubdash.TimeSettings)
})
t.Run("Should fail when public dashboard uid does not match dashboard uid", func(t *testing.T) {
isEnabled := true
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
},
}
// insert initial pubdash
savedPubdash, err := service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
dto = &SavePublicDashboardDTO{
Uid: savedPubdash.Uid,
DashboardUid: dashboard2.UID,
OrgID: 9,
UserId: 8,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
},
}
_, err = service.Update(context.Background(), SignedInUser, dto)
assert.Error(t, err)
})
trueBooleanField := true
timeSettings := &TimeSettings{From: "now-8", To: "now"}
shareType := EmailShareType
testCases := []struct {
Name string
IsEnabled *bool
TimeSelectionEnabled *bool
AnnotationsEnabled *bool
TimeSettings *TimeSettings
ShareType ShareType
}{
{
Name: "isEnabled",
IsEnabled: nil,
TimeSelectionEnabled: &trueBooleanField,
AnnotationsEnabled: &trueBooleanField,
TimeSettings: timeSettings,
ShareType: shareType,
},
{
Name: "timeSelectionEnabled",
IsEnabled: &trueBooleanField,
TimeSelectionEnabled: nil,
AnnotationsEnabled: &trueBooleanField,
TimeSettings: timeSettings,
ShareType: shareType,
},
{
Name: "annotationsEnabled",
IsEnabled: &trueBooleanField,
TimeSelectionEnabled: &trueBooleanField,
AnnotationsEnabled: nil,
TimeSettings: timeSettings,
ShareType: shareType,
},
{
Name: "isEnabled, timeSelectionEnabled and annotationsEnabled",
IsEnabled: nil,
TimeSelectionEnabled: nil,
AnnotationsEnabled: nil,
TimeSettings: nil,
ShareType: "",
},
}
for _, tt := range testCases {
t.Run(fmt.Sprintf("Update public dashboard with %s null boolean fields let those fields with old persisted value", tt.Name), func(t *testing.T) {
sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: publicdashboardStore,
serviceWrapper: serviceWrapper,
}
isEnabled, annotationsEnabled, timeSelectionEnabled := true, true, false
dto := &SavePublicDashboardDTO{
DashboardUid: dashboard.UID,
UserId: 7,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: &isEnabled,
AnnotationsEnabled: &annotationsEnabled,
TimeSelectionEnabled: &timeSelectionEnabled,
Share: PublicShareType,
},
}
// insert initial pubdash
savedPubdash, err := service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err)
dto = &SavePublicDashboardDTO{
Uid: savedPubdash.Uid,
DashboardUid: dashboard.UID,
OrgID: 9,
UserId: 8,
PublicDashboard: &PublicDashboardDTO{
IsEnabled: tt.IsEnabled,
AnnotationsEnabled: tt.AnnotationsEnabled,
TimeSelectionEnabled: tt.TimeSelectionEnabled,
Share: tt.ShareType,
},
}
updatedPubdash, err := service.Update(context.Background(), SignedInUser, dto)
require.NoError(t, err)
assertOldValueIfNull(t, updatedPubdash.IsEnabled, savedPubdash.IsEnabled, dto.PublicDashboard.IsEnabled)
assertOldValueIfNull(t, updatedPubdash.AnnotationsEnabled, savedPubdash.AnnotationsEnabled, dto.PublicDashboard.AnnotationsEnabled)
assertOldValueIfNull(t, updatedPubdash.TimeSelectionEnabled, savedPubdash.TimeSelectionEnabled, dto.PublicDashboard.TimeSelectionEnabled)
if dto.PublicDashboard.Share == "" {
assert.Equal(t, updatedPubdash.Share, savedPubdash.Share)
} else {
assert.Equal(t, updatedPubdash.Share, dto.PublicDashboard.Share)
}
})
}
}
func assertOldValueIfNull(t *testing.T, expectedValue bool, oldValue bool, nullableValue *bool) {
if nullableValue == nil {
assert.Equal(t, expectedValue, oldValue)
} else {
assert.Equal(t, expectedValue, *nullableValue)
}
}
func TestDeletePublicDashboard(t *testing.T) {
testCases := []struct {
Name string
AffectedRowsResp int64
ExpectedErrResp error
StoreRespErr error
}{
{
Name: "Successfully deletes a public dashboards",
AffectedRowsResp: 1,
ExpectedErrResp: nil,
StoreRespErr: nil,
},
{
Name: "Public dashboard not found",
AffectedRowsResp: 0,
ExpectedErrResp: nil,
StoreRespErr: nil,
},
{
Name: "Database error",
AffectedRowsResp: 0,
ExpectedErrResp: ErrInternalServerError.Errorf("Delete: failed to delete a public dashboard by Uid: uid db error!"),
StoreRespErr: errors.New("db error!"),
},
}
for _, tt := range testCases {
t.Run(tt.Name, func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
store.On("Delete", mock.Anything, mock.Anything).Return(tt.AffectedRowsResp, tt.StoreRespErr)
serviceWrapper := &PublicDashboardServiceWrapperImpl{
log: log.New("test.logger"),
store: store,
}
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: store,
serviceWrapper: serviceWrapper,
}
err := service.Delete(context.Background(), "uid")
if tt.ExpectedErrResp != nil {
assert.Equal(t, tt.ExpectedErrResp.Error(), err.Error())
assert.Equal(t, tt.ExpectedErrResp.Error(), err.Error())
} else {
assert.NoError(t, err)
}
})
}
}
func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T) {
type args struct {
reqDTO PublicDashboardQueryDTO
ts TimeSettings
}
tests := []struct {
name string
args args
wantSafeInterval int64
wantSafeMaxDataPoints int64
}{
{
name: "return original interval",
args: args{
reqDTO: PublicDashboardQueryDTO{
IntervalMs: 10000,
MaxDataPoints: 300,
},
ts: TimeSettings{
From: "now-3h",
To: "now",
},
},
wantSafeInterval: 10000,
wantSafeMaxDataPoints: 300,
},
{
name: "return safe interval because of a small interval",
args: args{
reqDTO: PublicDashboardQueryDTO{
IntervalMs: 1000,
MaxDataPoints: 300,
},
ts: TimeSettings{
From: "now-6h",
To: "now",
},
},
wantSafeInterval: 2000,
wantSafeMaxDataPoints: 11000,
},
{
name: "return safe interval for long time range",
args: args{
reqDTO: PublicDashboardQueryDTO{
IntervalMs: 100,
MaxDataPoints: 300,
},
ts: TimeSettings{
From: "now-90d",
To: "now",
},
},
wantSafeInterval: 600000,
wantSafeMaxDataPoints: 11000,
},
{
name: "return safe interval when reqDTO is empty",
args: args{
reqDTO: PublicDashboardQueryDTO{},
ts: TimeSettings{
From: "now-90d",
To: "now",
},
},
wantSafeInterval: 600000,
wantSafeMaxDataPoints: 11000,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pd := &PublicDashboardServiceImpl{
intervalCalculator: intervalv2.NewCalculator(),
}
got, got1 := pd.getSafeIntervalAndMaxDataPoints(tt.args.reqDTO, tt.args.ts)
assert.Equalf(t, tt.wantSafeInterval, got, "getSafeIntervalAndMaxDataPoints(%v, %v)", tt.args.reqDTO, tt.args.ts)
assert.Equalf(t, tt.wantSafeMaxDataPoints, got1, "getSafeIntervalAndMaxDataPoints(%v, %v)", tt.args.reqDTO, tt.args.ts)
})
}
}
func TestDashboardEnabledChanged(t *testing.T) {
t.Run("created isEnabled: false", func(t *testing.T) {
assert.False(t, publicDashboardIsEnabledChanged(nil, &PublicDashboard{IsEnabled: false}))
})
t.Run("created isEnabled: true", func(t *testing.T) {
assert.True(t, publicDashboardIsEnabledChanged(nil, &PublicDashboard{IsEnabled: true}))
})
t.Run("updated isEnabled same", func(t *testing.T) {
assert.False(t, publicDashboardIsEnabledChanged(&PublicDashboard{IsEnabled: true}, &PublicDashboard{IsEnabled: true}))
})
t.Run("updated isEnabled changed", func(t *testing.T) {
assert.True(t, publicDashboardIsEnabledChanged(&PublicDashboard{IsEnabled: false}, &PublicDashboard{IsEnabled: true}))
})
}
func TestPublicDashboardServiceImpl_ListPublicDashboards(t *testing.T) {
type args struct {
ctx context.Context
query *PublicDashboardListQuery
}
type mockResponse struct {
PublicDashboardListResponseWithPagination *PublicDashboardListResponseWithPagination
Err error
}
mockedDashboards := []*PublicDashboardListResponse{
{
Uid: "0GwW7mgVk",
AccessToken: "0b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "0S6TmO67z",
Title: "my zero dashboard",
IsEnabled: true,
},
{
Uid: "1GwW7mgVk",
AccessToken: "1b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "1S6TmO67z",
Title: "my first dashboard",
IsEnabled: true,
},
{
Uid: "2GwW7mgVk",
AccessToken: "2b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "2S6TmO67z",
Title: "my second dashboard",
IsEnabled: false,
},
{
Uid: "9GwW7mgVk",
AccessToken: "deletedashboardaccesstoken",
DashboardUid: "9S6TmO67z",
Title: "",
IsEnabled: true,
},
}
testCases := []struct {
name string
args args
want *PublicDashboardListResponseWithPagination
mockResponse *mockResponse
wantErr assert.ErrorAssertionFunc
}{
{
name: "should return correct pagination response",
args: args{
ctx: context.Background(),
query: &PublicDashboardListQuery{
User: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"dashboards:read": {"dashboards:uid:0S6TmO67z"}}},
},
OrgID: 1,
Page: 1,
Limit: 50,
},
},
mockResponse: &mockResponse{
PublicDashboardListResponseWithPagination: &PublicDashboardListResponseWithPagination{
TotalCount: int64(len(mockedDashboards)),
PublicDashboards: mockedDashboards,
},
Err: nil,
},
want: &PublicDashboardListResponseWithPagination{
Page: 1,
PerPage: 50,
TotalCount: int64(len(mockedDashboards)),
PublicDashboards: mockedDashboards,
},
wantErr: assert.NoError,
},
{
name: "should return error when store returns error",
args: args{
ctx: context.Background(),
query: &PublicDashboardListQuery{
User: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"dashboards:read": {"dashboards:uid:0S6TmO67z"}}},
},
OrgID: 1,
Page: 1,
Limit: 50,
},
},
mockResponse: &mockResponse{
PublicDashboardListResponseWithPagination: nil,
Err: errors.New("an err"),
},
want: nil,
wantErr: assert.Error,
},
}
ac := acimpl.ProvideAccessControl(setting.NewCfg())
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
store.On("FindAllWithPagination", mock.Anything, mock.Anything).
Return(tt.mockResponse.PublicDashboardListResponseWithPagination, tt.mockResponse.Err)
pd := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: store,
ac: ac,
}
got, err := pd.FindAllWithPagination(tt.args.ctx, tt.args.query)
if !tt.wantErr(t, err, fmt.Sprintf("FindAllWithPagination(%v, %v)", tt.args.ctx, tt.args.query)) {
return
}
assert.Equalf(t, tt.want, got, "FindAllWithPagination(%v, %v)", tt.args.ctx, tt.args.query)
})
}
}
func TestPublicDashboardServiceImpl_NewPublicDashboardUid(t *testing.T) {
mockedDashboard := &PublicDashboard{
IsEnabled: true,
AnnotationsEnabled: false,
DashboardUid: "NOTTHESAME",
OrgId: 9999999,
TimeSettings: timeSettings,
}
type args struct {
ctx context.Context
}
type mockResponse struct {
PublicDashboard *PublicDashboard
Err error
}
tests := []struct {
name string
args args
mockStore *mockResponse
want string
wantErr assert.ErrorAssertionFunc
}{
{
name: "should return a new uid",
args: args{ctx: context.Background()},
mockStore: &mockResponse{nil, nil},
want: "NOTTHESAME",
wantErr: assert.NoError,
},
{
name: "should return an error if the generated uid exists 3 times",
args: args{ctx: context.Background()},
mockStore: &mockResponse{mockedDashboard, nil},
want: "",
wantErr: assert.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
store.On("Find", mock.Anything, mock.Anything).
Return(tt.mockStore.PublicDashboard, tt.mockStore.Err)
pd := &PublicDashboardServiceImpl{store: store}
got, err := pd.NewPublicDashboardUid(tt.args.ctx)
if !tt.wantErr(t, err, fmt.Sprintf("NewPublicDashboardUid(%v)", tt.args.ctx)) {
return
}
if err == nil {
assert.NotEqual(t, got, tt.want, "NewPublicDashboardUid(%v)", tt.args.ctx)
assert.True(t, util.IsValidShortUID(got), "NewPublicDashboardUid(%v)", tt.args.ctx)
store.AssertNumberOfCalls(t, "Find", 1)
} else {
store.AssertNumberOfCalls(t, "Find", 3)
assert.True(t, ErrInternalServerError.Is(err))
}
})
}
}
func TestPublicDashboardServiceImpl_NewPublicDashboardAccessToken(t *testing.T) {
mockedDashboard := &PublicDashboard{
IsEnabled: true,
AnnotationsEnabled: false,
DashboardUid: "NOTTHESAME",
OrgId: 9999999,
TimeSettings: timeSettings,
}
type args struct {
ctx context.Context
}
type mockResponse struct {
PublicDashboard *PublicDashboard
Err error
}
tests := []struct {
name string
args args
mockStore *mockResponse
want string
wantErr assert.ErrorAssertionFunc
}{
{
name: "should return a new access token",
args: args{ctx: context.Background()},
mockStore: &mockResponse{nil, nil},
want: "6522e152530f4ee76522e152530f4ee7",
wantErr: assert.NoError,
},
{
name: "should return an error if the generated access token exists 3 times",
args: args{ctx: context.Background()},
mockStore: &mockResponse{mockedDashboard, nil},
want: "",
wantErr: assert.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
store.On("FindByAccessToken", mock.Anything, mock.Anything).
Return(tt.mockStore.PublicDashboard, tt.mockStore.Err)
pd := &PublicDashboardServiceImpl{store: store}
got, err := pd.NewPublicDashboardAccessToken(tt.args.ctx)
if !tt.wantErr(t, err, fmt.Sprintf("NewPublicDashboardAccessToken(%v)", tt.args.ctx)) {
return
}
if err == nil {
assert.NotEqual(t, got, tt.want, "NewPublicDashboardAccessToken(%v)", tt.args.ctx)
assert.True(t, validation.IsValidAccessToken(got), "NewPublicDashboardAccessToken(%v)", tt.args.ctx)
store.AssertNumberOfCalls(t, "FindByAccessToken", 1)
} else {
store.AssertNumberOfCalls(t, "FindByAccessToken", 3)
assert.True(t, ErrInternalServerError.Is(err))
}
})
}
}
func TestDeleteByDashboard(t *testing.T) {
t.Run("will return nil when pubdash not found", func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
pd := &PublicDashboardServiceImpl{store: store, serviceWrapper: ProvideServiceWrapper(store)}
dashboard := &dashboards.Dashboard{UID: "1", OrgID: 1, IsFolder: false}
store.On("FindByDashboardUid", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
err := pd.DeleteByDashboard(context.Background(), dashboard)
assert.Nil(t, err)
})
t.Run("will delete pubdash when dashboard deleted", func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
pd := &PublicDashboardServiceImpl{store: store, serviceWrapper: ProvideServiceWrapper(store)}
dashboard := &dashboards.Dashboard{UID: "1", OrgID: 1, IsFolder: false}
pubdash := &PublicDashboard{Uid: "2", OrgId: 1, DashboardUid: dashboard.UID}
store.On("FindByDashboardUid", mock.Anything, mock.Anything, mock.Anything).Return(pubdash, nil)
store.On("Delete", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
err := pd.DeleteByDashboard(context.Background(), dashboard)
require.NoError(t, err)
})
t.Run("will delete pubdashes when dashboard folder deleted", func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
pd := &PublicDashboardServiceImpl{store: store, serviceWrapper: ProvideServiceWrapper(store)}
dashboard := &dashboards.Dashboard{UID: "1", OrgID: 1, IsFolder: true}
pubdash1 := &PublicDashboard{Uid: "2", OrgId: 1, DashboardUid: dashboard.UID}
pubdash2 := &PublicDashboard{Uid: "3", OrgId: 1, DashboardUid: dashboard.UID}
store.On("FindByDashboardFolder", mock.Anything, mock.Anything).Return([]*PublicDashboard{pubdash1, pubdash2}, nil)
store.On("Delete", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
store.On("Delete", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
err := pd.DeleteByDashboard(context.Background(), dashboard)
require.NoError(t, err)
})
}
func TestGenerateAccessToken(t *testing.T) {
accessToken, err := GenerateAccessToken()
t.Run("length", func(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, 32, len(accessToken))
})
t.Run("no - ", func(t *testing.T) {
assert.False(t, strings.Contains("-", accessToken))
})
}
func CreateDatasource(dsType string, uid string) struct {
Type *string `json:"type,omitempty"`
Uid *string `json:"uid,omitempty"`
} {
return struct {
Type *string `json:"type,omitempty"`
Uid *string `json:"uid,omitempty"`
}{
Type: &dsType,
Uid: &uid,
}
}
func AddAnnotationsToDashboard(t *testing.T, dash *dashboards.Dashboard, annotations []DashAnnotation) *dashboards.Dashboard {
type annotationsDto struct {
List []DashAnnotation `json:"list"`
}
annos := annotationsDto{}
annos.List = annotations
annoJSON, err := json.Marshal(annos)
require.NoError(t, err)
dashAnnos, err := simplejson.NewJson(annoJSON)
require.NoError(t, err)
dash.Data.Set("annotations", dashAnnos)
return dash
}
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64,
folderId int64, isFolder bool, templateVars []map[string]any, customPanels []any, tags ...any) *dashboards.Dashboard {
t.Helper()
var dashboardPanels []any
if customPanels != nil {
dashboardPanels = customPanels
} else {
dashboardPanels = []any{
map[string]any{
"id": 1,
"datasource": map[string]any{
"uid": "ds1",
},
"targets": []any{
map[string]any{
"datasource": map[string]any{
"type": "mysql",
"uid": "ds1",
},
"refId": "A",
},
map[string]any{
"datasource": map[string]any{
"type": "prometheus",
"uid": "ds2",
},
"refId": "B",
},
},
},
map[string]any{
"id": 2,
"datasource": map[string]any{
"uid": "ds3",
},
"targets": []any{
map[string]any{
"datasource": map[string]any{
"type": "mysql",
"uid": "ds3",
},
"refId": "C",
},
},
},
}
}
cmd := dashboards.SaveDashboardCommand{
OrgID: orgId,
FolderID: folderId,
IsFolder: isFolder,
Dashboard: simplejson.NewFromAny(map[string]any{
"id": nil,
"title": title,
"tags": tags,
"panels": dashboardPanels,
"templating": map[string]any{
"list": templateVars,
},
"time": map[string]any{
"from": "2022-09-01T00:00:00.000Z",
"to": "2022-09-01T12:00:00.000Z",
},
}),
}
dash, err := dashboardStore.SaveDashboard(context.Background(), cmd)
require.NoError(t, err)
require.NotNil(t, dash)
dash.Data.Set("id", dash.ID)
dash.Data.Set("uid", dash.UID)
return dash
}