2020-12-09 13:10:18 +01:00
package librarypanels
import (
2021-02-02 06:25:35 +01:00
"context"
2020-12-21 09:40:27 +01:00
"encoding/json"
2020-12-09 13:10:18 +01:00
"testing"
"time"
2020-12-23 12:42:52 +01:00
"github.com/google/go-cmp/cmp"
2022-12-15 16:34:17 +02:00
"github.com/stretchr/testify/mock"
2022-03-10 12:19:50 -05:00
"github.com/stretchr/testify/require"
2022-02-16 14:15:44 +01:00
"github.com/grafana/grafana/pkg/api/routing"
2022-10-18 21:31:56 +08:00
"github.com/grafana/grafana/pkg/bus"
2021-02-02 06:25:35 +01:00
"github.com/grafana/grafana/pkg/components/simplejson"
2022-11-10 11:41:03 +02:00
"github.com/grafana/grafana/pkg/infra/appcontext"
2022-10-19 09:02:15 -04:00
"github.com/grafana/grafana/pkg/infra/db"
2023-01-18 13:52:41 +01:00
"github.com/grafana/grafana/pkg/infra/slugify"
2022-10-18 21:31:56 +08:00
"github.com/grafana/grafana/pkg/infra/tracing"
2023-01-29 20:14:12 -08:00
"github.com/grafana/grafana/pkg/kinds/librarypanel"
2023-04-20 10:24:41 +01:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
2022-03-10 12:58:18 +01:00
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
2022-02-28 09:54:56 +01:00
"github.com/grafana/grafana/pkg/services/alerting"
2021-02-24 14:06:22 +01:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-02-16 14:15:44 +01:00
"github.com/grafana/grafana/pkg/services/dashboards/database"
2022-05-17 14:52:22 -04:00
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
2022-03-10 12:58:18 +01:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2022-11-10 11:41:03 +02:00
"github.com/grafana/grafana/pkg/services/folder"
2022-10-10 21:47:53 +02:00
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
2023-01-26 10:21:10 +02:00
"github.com/grafana/grafana/pkg/services/folder/foldertest"
2022-03-21 10:49:49 +01:00
"github.com/grafana/grafana/pkg/services/guardian"
2021-05-11 07:10:19 +02:00
"github.com/grafana/grafana/pkg/services/libraryelements"
2023-02-01 17:32:05 +01:00
"github.com/grafana/grafana/pkg/services/libraryelements/model"
2022-08-10 11:56:48 +02:00
"github.com/grafana/grafana/pkg/services/org"
2022-12-07 17:03:22 +01:00
"github.com/grafana/grafana/pkg/services/org/orgimpl"
2022-11-14 21:08:10 +02:00
"github.com/grafana/grafana/pkg/services/quota/quotatest"
2023-02-06 17:50:03 +01:00
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
2022-09-21 14:04:01 +02:00
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
2022-06-28 14:32:25 +02:00
"github.com/grafana/grafana/pkg/services/user"
2022-12-07 17:03:22 +01:00
"github.com/grafana/grafana/pkg/services/user/userimpl"
2020-12-09 13:10:18 +01:00
"github.com/grafana/grafana/pkg/setting"
)
2021-09-20 10:58:24 +02:00
const userInDbName = "user_in_db"
const userInDbAvatar = "/avatar/402d08de060496d6b6874495fe20f5ad"
2020-12-23 12:42:52 +01:00
2021-01-20 09:28:10 +01:00
func TestConnectLibraryPanelsForDashboard ( t * testing . T ) {
2021-02-24 14:06:22 +01:00
scenarioWithLibraryPanel ( t , "When an admin tries to store a dashboard with a library panel, it should connect the two" ,
2021-01-20 09:28:10 +01:00
func ( t * testing . T , sc scenarioContext ) {
dashJSON := map [ string ] interface { } {
"panels" : [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
} ,
} ,
map [ string ] interface { } {
"id" : int64 ( 2 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 6 ,
"y" : 0 ,
} ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
2022-04-20 02:05:59 -07:00
"uid" : sc . initialResult . Result . UID ,
2021-01-20 09:28:10 +01:00
} ,
"title" : "Text - Library Panel" ,
"type" : "text" ,
} ,
} ,
}
2023-01-16 16:33:55 +01:00
dash := dashboards . Dashboard {
2021-05-11 07:10:19 +02:00
Title : "Testing ConnectLibraryPanelsForDashboard" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
2021-01-20 09:28:10 +01:00
}
2023-01-25 09:14:32 +01:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID )
2021-01-20 09:28:10 +01:00
2021-09-27 09:04:36 +02:00
err := sc . service . ConnectLibraryPanelsForDashboard ( sc . ctx , sc . user , dashInDB )
2021-01-20 09:28:10 +01:00
require . NoError ( t , err )
2023-01-16 16:33:55 +01:00
elements , err := sc . elementService . GetElementsForDashboard ( sc . ctx , dashInDB . ID )
2021-01-20 09:28:10 +01:00
require . NoError ( t , err )
2021-05-11 07:10:19 +02:00
require . Len ( t , elements , 1 )
require . Equal ( t , sc . initialResult . Result . UID , elements [ sc . initialResult . Result . UID ] . UID )
2021-01-20 09:28:10 +01:00
} )
2021-09-08 10:53:55 +02:00
scenarioWithLibraryPanel ( t , "When an admin tries to store a dashboard with library panels inside and outside of rows, it should connect all" ,
func ( t * testing . T , sc scenarioContext ) {
2023-02-01 17:32:05 +01:00
cmd := model . CreateLibraryElementCommand {
2021-09-08 10:53:55 +02:00
FolderID : sc . initialResult . Result . FolderID ,
Name : "Outside row" ,
Model : [ ] byte ( `
{
"datasource" : "${DS_GDEV-TESTDATA}" ,
"id" : 1 ,
"title" : "Text - Library Panel" ,
"type" : "text" ,
"description" : "A description"
}
` ) ,
2023-02-01 17:32:05 +01:00
Kind : int64 ( model . PanelElement ) ,
2021-09-08 10:53:55 +02:00
}
2021-09-27 09:04:36 +02:00
outsidePanel , err := sc . elementService . CreateElement ( sc . ctx , sc . user , cmd )
2021-09-08 10:53:55 +02:00
require . NoError ( t , err )
dashJSON := map [ string ] interface { } {
"panels" : [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
} ,
} ,
map [ string ] interface { } {
"collapsed" : true ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 6 ,
} ,
"id" : int64 ( 2 ) ,
"type" : "row" ,
"panels" : [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 3 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 7 ,
} ,
} ,
map [ string ] interface { } {
"id" : int64 ( 4 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 6 ,
"y" : 13 ,
} ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
2022-04-20 02:05:59 -07:00
"uid" : sc . initialResult . Result . UID ,
2021-09-08 10:53:55 +02:00
} ,
"title" : "Inside row" ,
"type" : "text" ,
} ,
} ,
} ,
map [ string ] interface { } {
"id" : int64 ( 5 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 19 ,
} ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
2022-04-20 02:05:59 -07:00
"uid" : outsidePanel . UID ,
2021-09-08 10:53:55 +02:00
} ,
"title" : "Outside row" ,
"type" : "text" ,
} ,
} ,
}
2023-01-16 16:33:55 +01:00
dash := dashboards . Dashboard {
2021-09-08 10:53:55 +02:00
Title : "Testing ConnectLibraryPanelsForDashboard" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
}
2023-01-25 09:14:32 +01:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID )
2021-09-08 10:53:55 +02:00
2021-09-27 09:04:36 +02:00
err = sc . service . ConnectLibraryPanelsForDashboard ( sc . ctx , sc . user , dashInDB )
2021-09-08 10:53:55 +02:00
require . NoError ( t , err )
2023-01-16 16:33:55 +01:00
elements , err := sc . elementService . GetElementsForDashboard ( sc . ctx , dashInDB . ID )
2021-09-08 10:53:55 +02:00
require . NoError ( t , err )
require . Len ( t , elements , 2 )
require . Equal ( t , sc . initialResult . Result . UID , elements [ sc . initialResult . Result . UID ] . UID )
require . Equal ( t , outsidePanel . UID , elements [ outsidePanel . UID ] . UID )
} )
2021-02-24 14:06:22 +01:00
scenarioWithLibraryPanel ( t , "When an admin tries to store a dashboard with a library panel without uid, it should fail" ,
2021-01-20 09:28:10 +01:00
func ( t * testing . T , sc scenarioContext ) {
dashJSON := map [ string ] interface { } {
"panels" : [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
} ,
} ,
map [ string ] interface { } {
"id" : int64 ( 2 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 6 ,
"y" : 0 ,
} ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
2021-02-24 14:06:22 +01:00
"name" : sc . initialResult . Result . Name ,
2021-01-20 09:28:10 +01:00
} ,
"title" : "Text - Library Panel" ,
"type" : "text" ,
} ,
} ,
}
2023-01-16 16:33:55 +01:00
dash := dashboards . Dashboard {
2021-05-11 07:10:19 +02:00
Title : "Testing ConnectLibraryPanelsForDashboard" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
2021-01-20 09:28:10 +01:00
}
2023-01-25 09:14:32 +01:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID )
2021-01-20 09:28:10 +01:00
2021-09-27 09:04:36 +02:00
err := sc . service . ConnectLibraryPanelsForDashboard ( sc . ctx , sc . user , dashInDB )
2021-01-20 09:28:10 +01:00
require . EqualError ( t , err , errLibraryPanelHeaderUIDMissing . Error ( ) )
} )
2021-02-16 13:00:56 +01:00
2021-02-24 14:06:22 +01:00
scenarioWithLibraryPanel ( t , "When an admin tries to store a dashboard with unused/removed library panels, it should disconnect unused/removed library panels" ,
2021-02-16 13:00:56 +01:00
func ( t * testing . T , sc scenarioContext ) {
2023-02-01 17:32:05 +01:00
unused , err := sc . elementService . CreateElement ( sc . ctx , sc . user , model . CreateLibraryElementCommand {
2023-01-25 09:14:32 +01:00
FolderID : sc . folder . ID ,
2021-05-11 07:10:19 +02:00
Name : "Unused Libray Panel" ,
Model : [ ] byte ( `
{
"datasource" : "${DS_GDEV-TESTDATA}" ,
"id" : 4 ,
"title" : "Unused Libray Panel" ,
"type" : "text" ,
"description" : "Unused description"
2021-02-16 13:00:56 +01:00
}
2021-05-11 07:10:19 +02:00
` ) ,
2023-02-01 17:32:05 +01:00
Kind : int64 ( model . PanelElement ) ,
2021-05-11 07:10:19 +02:00
} )
2021-02-16 13:00:56 +01:00
require . NoError ( t , err )
2021-01-20 09:28:10 +01:00
dashJSON := map [ string ] interface { } {
"panels" : [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
} ,
} ,
map [ string ] interface { } {
2021-05-11 07:10:19 +02:00
"id" : int64 ( 4 ) ,
2021-01-20 09:28:10 +01:00
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 6 ,
"y" : 0 ,
} ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
2022-04-20 02:05:59 -07:00
"uid" : unused . UID ,
2021-01-20 09:28:10 +01:00
} ,
2021-05-11 07:10:19 +02:00
"title" : "Unused Libray Panel" ,
"description" : "Unused description" ,
2021-01-20 09:28:10 +01:00
} ,
} ,
}
2021-05-11 07:10:19 +02:00
2023-01-16 16:33:55 +01:00
dash := dashboards . Dashboard {
2021-05-11 07:10:19 +02:00
Title : "Testing ConnectLibraryPanelsForDashboard" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
2021-01-20 09:28:10 +01:00
}
2023-01-25 09:14:32 +01:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID )
2023-01-16 16:33:55 +01:00
err = sc . elementService . ConnectElementsToDashboard ( sc . ctx , sc . user , [ ] string { sc . initialResult . Result . UID } , dashInDB . ID )
2021-01-20 09:28:10 +01:00
require . NoError ( t , err )
2021-05-11 07:10:19 +02:00
panelJSON := [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
2021-01-20 09:28:10 +01:00
} ,
2021-05-11 07:10:19 +02:00
} ,
map [ string ] interface { } {
"id" : int64 ( 2 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 6 ,
"y" : 0 ,
} ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
2022-04-20 02:05:59 -07:00
"uid" : sc . initialResult . Result . UID ,
2021-01-20 09:28:10 +01:00
} ,
2021-05-11 07:10:19 +02:00
"title" : "Text - Library Panel" ,
"type" : "text" ,
2021-01-20 09:28:10 +01:00
} ,
}
2021-05-11 07:10:19 +02:00
dashInDB . Data . Set ( "panels" , panelJSON )
2021-09-27 09:04:36 +02:00
err = sc . service . ConnectLibraryPanelsForDashboard ( sc . ctx , sc . user , dashInDB )
2021-03-02 10:34:01 +01:00
require . NoError ( t , err )
2023-01-16 16:33:55 +01:00
elements , err := sc . elementService . GetElementsForDashboard ( sc . ctx , dashInDB . ID )
2021-03-02 10:34:01 +01:00
require . NoError ( t , err )
2021-05-11 07:10:19 +02:00
require . Len ( t , elements , 1 )
require . Equal ( t , sc . initialResult . Result . UID , elements [ sc . initialResult . Result . UID ] . UID )
2021-03-02 10:34:01 +01:00
} )
}
2021-09-20 10:58:24 +02:00
func TestImportLibraryPanelsForDashboard ( t * testing . T ) {
testScenario ( t , "When an admin tries to import a dashboard with a library panel that does not exist, it should import the library panel" ,
func ( t * testing . T , sc scenarioContext ) {
var missingUID = "jL6MrxCMz"
var missingName = "Missing Library Panel"
var missingModel = map [ string ] interface { } {
"id" : int64 ( 2 ) ,
"gridPos" : map [ string ] interface { } {
"h" : int64 ( 6 ) ,
"w" : int64 ( 6 ) ,
"x" : int64 ( 0 ) ,
"y" : int64 ( 0 ) ,
} ,
"description" : "" ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
"uid" : missingUID ,
"name" : missingName ,
} ,
"title" : "Text - Library Panel" ,
"type" : "text" ,
}
2022-06-29 16:18:27 +01:00
var libraryElements = map [ string ] interface { } {
missingUID : map [ string ] interface { } {
"model" : missingModel ,
} ,
}
2021-09-20 10:58:24 +02:00
2022-06-29 16:18:27 +01:00
panels := [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
} ,
} ,
map [ string ] interface { } {
"libraryPanel" : map [ string ] interface { } {
"uid" : missingUID ,
"name" : missingName ,
2021-09-20 10:58:24 +02:00
} ,
} ,
}
2022-06-29 16:18:27 +01:00
2021-09-27 09:04:36 +02:00
_ , err := sc . elementService . GetElement ( sc . ctx , sc . user , missingUID )
2022-06-29 16:18:27 +01:00
2023-02-01 17:32:05 +01:00
require . EqualError ( t , err , model . ErrLibraryElementNotFound . Error ( ) )
2021-09-20 10:58:24 +02:00
2022-06-29 16:18:27 +01:00
err = sc . service . ImportLibraryPanelsForDashboard ( sc . ctx , sc . user , simplejson . NewFromAny ( libraryElements ) , panels , 0 )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
2021-09-27 09:04:36 +02:00
element , err := sc . elementService . GetElement ( sc . ctx , sc . user , missingUID )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
var expected = getExpected ( t , element , missingUID , missingName , missingModel )
var result = toLibraryElement ( t , element )
if diff := cmp . Diff ( expected , result , getCompareOptions ( ) ... ) ; diff != "" {
t . Fatalf ( "Result mismatch (-want +got):\n%s" , diff )
}
} )
scenarioWithLibraryPanel ( t , "When an admin tries to import a dashboard with a library panel that already exist, it should not import the library panel and existing library panel should be unchanged" ,
func ( t * testing . T , sc scenarioContext ) {
var existingUID = sc . initialResult . Result . UID
var existingName = sc . initialResult . Result . Name
2022-06-29 16:18:27 +01:00
panels := [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
2021-09-20 10:58:24 +02:00
} ,
2022-06-29 16:18:27 +01:00
} ,
map [ string ] interface { } {
"libraryPanel" : map [ string ] interface { } {
"uid" : sc . initialResult . Result . UID ,
"name" : sc . initialResult . Result . Name ,
2021-09-20 10:58:24 +02:00
} ,
} ,
}
2022-06-29 16:18:27 +01:00
2021-09-27 09:04:36 +02:00
_ , err := sc . elementService . GetElement ( sc . ctx , sc . user , existingUID )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
2023-01-25 09:14:32 +01:00
err = sc . service . ImportLibraryPanelsForDashboard ( sc . ctx , sc . user , simplejson . New ( ) , panels , sc . folder . ID )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
2021-09-27 09:04:36 +02:00
element , err := sc . elementService . GetElement ( sc . ctx , sc . user , existingUID )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
var expected = getExpected ( t , element , existingUID , existingName , sc . initialResult . Result . Model )
expected . FolderID = sc . initialResult . Result . FolderID
expected . Description = sc . initialResult . Result . Description
2023-01-25 09:14:32 +01:00
expected . Meta . FolderUID = sc . folder . UID
2021-09-20 10:58:24 +02:00
expected . Meta . FolderName = sc . folder . Title
var result = toLibraryElement ( t , element )
if diff := cmp . Diff ( expected , result , getCompareOptions ( ) ... ) ; diff != "" {
t . Fatalf ( "Result mismatch (-want +got):\n%s" , diff )
}
} )
testScenario ( t , "When an admin tries to import a dashboard with library panels inside and outside of rows, it should import all that do not exist" ,
func ( t * testing . T , sc scenarioContext ) {
var outsideUID = "jL6MrxCMz"
var outsideName = "Outside Library Panel"
var outsideModel = map [ string ] interface { } {
"id" : int64 ( 5 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 19 ,
} ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
"uid" : outsideUID ,
"name" : outsideName ,
} ,
"title" : "Outside row" ,
"type" : "text" ,
}
2022-06-29 16:18:27 +01:00
2021-09-20 10:58:24 +02:00
var insideUID = "iK7NsyDNz"
var insideName = "Inside Library Panel"
var insideModel = map [ string ] interface { } {
"id" : int64 ( 4 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 6 ,
"y" : 13 ,
} ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
"uid" : insideUID ,
"name" : insideName ,
} ,
"title" : "Inside row" ,
"type" : "text" ,
}
2022-06-29 16:18:27 +01:00
var libraryElements = map [ string ] interface { } {
outsideUID : map [ string ] interface { } {
"model" : outsideModel ,
} ,
insideUID : map [ string ] interface { } {
"model" : insideModel ,
} ,
}
panels := [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
2021-09-20 10:58:24 +02:00
} ,
2022-06-29 16:18:27 +01:00
} ,
map [ string ] interface { } {
"libraryPanel" : map [ string ] interface { } {
"uid" : outsideUID ,
"name" : outsideName ,
} ,
} ,
map [ string ] interface { } {
"collapsed" : true ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 6 ,
} ,
"id" : int64 ( 2 ) ,
"type" : "row" ,
"panels" : [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 3 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 7 ,
} ,
2021-09-20 10:58:24 +02:00
} ,
2022-06-29 16:18:27 +01:00
map [ string ] interface { } {
"libraryPanel" : map [ string ] interface { } {
"uid" : insideUID ,
"name" : insideName ,
2021-09-20 10:58:24 +02:00
} ,
} ,
} ,
} ,
}
2022-06-29 16:18:27 +01:00
2021-09-27 09:04:36 +02:00
_ , err := sc . elementService . GetElement ( sc . ctx , sc . user , outsideUID )
2023-02-01 17:32:05 +01:00
require . EqualError ( t , err , model . ErrLibraryElementNotFound . Error ( ) )
2021-09-27 09:04:36 +02:00
_ , err = sc . elementService . GetElement ( sc . ctx , sc . user , insideUID )
2023-02-01 17:32:05 +01:00
require . EqualError ( t , err , model . ErrLibraryElementNotFound . Error ( ) )
2021-09-20 10:58:24 +02:00
2022-06-29 16:18:27 +01:00
err = sc . service . ImportLibraryPanelsForDashboard ( sc . ctx , sc . user , simplejson . NewFromAny ( libraryElements ) , panels , 0 )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
2021-09-27 09:04:36 +02:00
element , err := sc . elementService . GetElement ( sc . ctx , sc . user , outsideUID )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
expected := getExpected ( t , element , outsideUID , outsideName , outsideModel )
result := toLibraryElement ( t , element )
if diff := cmp . Diff ( expected , result , getCompareOptions ( ) ... ) ; diff != "" {
t . Fatalf ( "Result mismatch (-want +got):\n%s" , diff )
}
2021-09-27 09:04:36 +02:00
element , err = sc . elementService . GetElement ( sc . ctx , sc . user , insideUID )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
expected = getExpected ( t , element , insideUID , insideName , insideModel )
result = toLibraryElement ( t , element )
if diff := cmp . Diff ( expected , result , getCompareOptions ( ) ... ) ; diff != "" {
t . Fatalf ( "Result mismatch (-want +got):\n%s" , diff )
}
} )
}
2020-12-21 09:40:27 +01:00
type libraryPanel struct {
2021-05-11 07:10:19 +02:00
ID int64
OrgID int64
FolderID int64
UID string
Name string
2021-03-24 13:43:51 +01:00
Type string
Description string
2021-05-11 07:10:19 +02:00
Model map [ string ] interface { }
Version int64
2023-02-01 17:32:05 +01:00
Meta model . LibraryElementDTOMeta
2020-12-21 09:40:27 +01:00
}
2020-12-11 06:08:32 +01:00
2021-09-20 10:58:24 +02:00
type libraryElementGridPos struct {
H int64 ` json:"h" `
W int64 ` json:"w" `
X int64 ` json:"x" `
Y int64 ` json:"y" `
}
type libraryElementLibraryPanel struct {
UID string ` json:"uid" `
Name string ` json:"name" `
}
type libraryElementModel struct {
ID int64 ` json:"id" `
Datasource string ` json:"datasource" `
Description string ` json:"description" `
Title string ` json:"title" `
Type string ` json:"type" `
GridPos libraryElementGridPos ` json:"gridPos" `
LibraryPanel libraryElementLibraryPanel ` json:"libraryPanel" `
}
type libraryElement struct {
2023-02-01 17:32:05 +01:00
ID int64 ` json:"id" `
OrgID int64 ` json:"orgId" `
FolderID int64 ` json:"folderId" `
UID string ` json:"uid" `
Name string ` json:"name" `
Kind int64 ` json:"kind" `
Type string ` json:"type" `
Description string ` json:"description" `
Model libraryElementModel ` json:"model" `
Version int64 ` json:"version" `
Meta model . LibraryElementDTOMeta ` json:"meta" `
2021-09-20 10:58:24 +02:00
}
2020-12-21 09:40:27 +01:00
type libraryPanelResult struct {
Result libraryPanel ` json:"result" `
}
2020-12-11 06:08:32 +01:00
2020-12-21 09:40:27 +01:00
type scenarioContext struct {
2021-09-27 09:04:36 +02:00
ctx context . Context
2021-05-12 08:48:17 +02:00
service Service
elementService libraryelements . Service
2022-08-10 11:56:48 +02:00
user * user . SignedInUser
2023-01-25 09:14:32 +01:00
folder * folder . Folder
2021-05-11 07:10:19 +02:00
initialResult libraryPanelResult
2022-10-19 09:02:15 -04:00
sqlStore db . DB
2021-02-24 14:06:22 +01:00
}
2023-02-01 17:32:05 +01:00
func toLibraryElement ( t * testing . T , res model . LibraryElementDTO ) libraryElement {
var libraryElementModel = libraryElementModel { }
err := json . Unmarshal ( res . Model , & libraryElementModel )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
return libraryElement {
ID : res . ID ,
OrgID : res . OrgID ,
FolderID : res . FolderID ,
UID : res . UID ,
Name : res . Name ,
Type : res . Type ,
Description : res . Description ,
Kind : res . Kind ,
2023-02-01 17:32:05 +01:00
Model : libraryElementModel ,
2021-09-20 10:58:24 +02:00
Version : res . Version ,
2023-02-01 17:32:05 +01:00
Meta : model . LibraryElementDTOMeta {
2021-09-20 10:58:24 +02:00
FolderName : res . Meta . FolderName ,
FolderUID : res . Meta . FolderUID ,
ConnectedDashboards : res . Meta . ConnectedDashboards ,
Created : res . Meta . Created ,
Updated : res . Meta . Updated ,
2023-01-29 20:14:12 -08:00
CreatedBy : librarypanel . LibraryElementDTOMetaUser {
Id : res . Meta . CreatedBy . Id ,
2021-09-20 10:58:24 +02:00
Name : res . Meta . CreatedBy . Name ,
2023-01-29 20:14:12 -08:00
AvatarUrl : res . Meta . CreatedBy . AvatarUrl ,
2021-09-20 10:58:24 +02:00
} ,
2023-01-29 20:14:12 -08:00
UpdatedBy : librarypanel . LibraryElementDTOMetaUser {
Id : res . Meta . UpdatedBy . Id ,
2021-09-20 10:58:24 +02:00
Name : res . Meta . UpdatedBy . Name ,
2023-01-29 20:14:12 -08:00
AvatarUrl : res . Meta . UpdatedBy . AvatarUrl ,
2021-09-20 10:58:24 +02:00
} ,
} ,
}
}
2023-02-01 17:32:05 +01:00
func getExpected ( t * testing . T , res model . LibraryElementDTO , UID string , name string , lEModel map [ string ] interface { } ) libraryElement {
marshalled , err := json . Marshal ( lEModel )
2021-09-20 10:58:24 +02:00
require . NoError ( t , err )
var libModel libraryElementModel
err = json . Unmarshal ( marshalled , & libModel )
require . NoError ( t , err )
return libraryElement {
ID : res . ID ,
OrgID : 1 ,
FolderID : 0 ,
UID : UID ,
Name : name ,
Type : "text" ,
Description : "" ,
Kind : 1 ,
Model : libModel ,
Version : 1 ,
2023-02-01 17:32:05 +01:00
Meta : model . LibraryElementDTOMeta {
2021-09-20 10:58:24 +02:00
FolderName : "General" ,
FolderUID : "" ,
ConnectedDashboards : 0 ,
Created : res . Meta . Created ,
Updated : res . Meta . Updated ,
2023-01-29 20:14:12 -08:00
CreatedBy : librarypanel . LibraryElementDTOMetaUser {
Id : 1 ,
2021-09-20 10:58:24 +02:00
Name : userInDbName ,
2023-01-29 20:14:12 -08:00
AvatarUrl : userInDbAvatar ,
2021-09-20 10:58:24 +02:00
} ,
2023-01-29 20:14:12 -08:00
UpdatedBy : librarypanel . LibraryElementDTOMetaUser {
Id : 1 ,
2021-09-20 10:58:24 +02:00
Name : userInDbName ,
2023-01-29 20:14:12 -08:00
AvatarUrl : userInDbAvatar ,
2021-09-20 10:58:24 +02:00
} ,
} ,
}
}
2023-01-16 16:33:55 +01:00
func createDashboard ( t * testing . T , sqlStore db . DB , user * user . SignedInUser , dash * dashboards . Dashboard , folderID int64 ) * dashboards . Dashboard {
dash . FolderID = folderID
2021-03-01 15:33:17 +01:00
dashItem := & dashboards . SaveDashboardDTO {
Dashboard : dash ,
Message : "" ,
2023-01-16 16:33:55 +01:00
OrgID : user . OrgID ,
2021-09-27 09:04:36 +02:00
User : user ,
2021-03-01 15:33:17 +01:00
Overwrite : false ,
}
2022-05-05 16:31:14 +01:00
cfg := setting . NewCfg ( )
cfg . IsFeatureToggleEnabled = featuremgmt . WithFeatures ( ) . IsEnabled
2022-11-14 21:08:10 +02:00
quotaService := quotatest . New ( false , nil )
dashboardStore , err := database . ProvideDashboardStore ( sqlStore , cfg , featuremgmt . WithFeatures ( ) , tagimpl . ProvideService ( sqlStore , cfg ) , quotaService )
require . NoError ( t , err )
2022-10-14 15:33:06 -04:00
dashAlertService := alerting . ProvideDashAlertExtractorService ( nil , nil , nil )
2023-04-20 10:24:41 +01:00
ac := actest . FakeAccessControl { ExpectedEvaluate : true }
2023-03-03 15:56:33 +00:00
folderStore := folderimpl . ProvideDashboardFolderStore ( sqlStore )
2023-04-20 10:24:41 +01:00
dashPermissionService := acmock . NewMockedPermissionsService ( )
dashPermissionService . On ( "SetPermissions" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( [ ] accesscontrol . ResourcePermission { } , nil )
2023-04-14 11:17:23 +02:00
service , err := dashboardservice . ProvideDashboardServiceImpl (
2023-03-03 15:56:33 +00:00
cfg , dashboardStore , folderStore , dashAlertService ,
2023-04-20 10:24:41 +01:00
featuremgmt . WithFeatures ( ) , acmock . NewMockedPermissionsService ( ) , dashPermissionService , ac ,
2023-01-26 10:21:10 +02:00
foldertest . NewFakeService ( ) ,
2022-03-10 12:58:18 +01:00
)
2023-04-14 11:17:23 +02:00
require . NoError ( t , err )
2022-03-10 12:58:18 +01:00
dashboard , err := service . SaveDashboard ( context . Background ( ) , dashItem , true )
2021-02-24 14:06:22 +01:00
require . NoError ( t , err )
2021-03-01 15:33:17 +01:00
return dashboard
}
2023-04-20 10:24:41 +01:00
func createFolder ( t * testing . T , sc scenarioContext , title string ) * folder . Folder {
2021-03-17 16:06:10 +01:00
t . Helper ( )
2023-04-20 10:24:41 +01:00
ac := actest . FakeAccessControl { ExpectedEvaluate : true }
2022-03-10 12:58:18 +01:00
cfg := setting . NewCfg ( )
2022-05-05 16:31:14 +01:00
cfg . IsFeatureToggleEnabled = featuremgmt . WithFeatures ( ) . IsEnabled
2022-03-10 12:58:18 +01:00
features := featuremgmt . WithFeatures ( )
2022-11-14 21:08:10 +02:00
quotaService := quotatest . New ( false , nil )
2023-04-20 10:24:41 +01:00
dashboardStore , err := database . ProvideDashboardStore ( sc . sqlStore , cfg , featuremgmt . WithFeatures ( ) , tagimpl . ProvideService ( sc . sqlStore , cfg ) , quotaService )
2022-11-14 21:08:10 +02:00
require . NoError ( t , err )
2023-04-20 10:24:41 +01:00
folderStore := folderimpl . ProvideDashboardFolderStore ( sc . sqlStore )
2023-02-01 15:43:21 +02:00
s := folderimpl . ProvideService ( ac , bus . ProvideBus ( tracing . InitializeTracerForTest ( ) ) , cfg , dashboardStore , folderStore , nil , features )
2022-03-10 12:58:18 +01:00
2021-03-17 16:06:10 +01:00
t . Logf ( "Creating folder with title and UID %q" , title )
2023-04-20 10:24:41 +01:00
ctx := appcontext . WithUser ( context . Background ( ) , sc . user )
folder , err := s . Create ( ctx , & folder . CreateFolderCommand { OrgID : sc . user . OrgID , Title : title , UID : title , SignedInUser : sc . user } )
2021-03-01 15:33:17 +01:00
require . NoError ( t , err )
2021-03-17 16:06:10 +01:00
return folder
2021-02-24 14:06:22 +01:00
}
func scenarioWithLibraryPanel ( t * testing . T , desc string , fn func ( t * testing . T , sc scenarioContext ) ) {
2021-03-17 16:06:10 +01:00
t . Helper ( )
2021-02-24 14:06:22 +01:00
testScenario ( t , desc , func ( t * testing . T , sc scenarioContext ) {
2023-02-01 17:32:05 +01:00
command := model . CreateLibraryElementCommand {
2023-01-25 09:14:32 +01:00
FolderID : sc . folder . ID ,
2021-05-11 07:10:19 +02:00
Name : "Text - Library Panel" ,
Model : [ ] byte ( `
{
"datasource" : "${DS_GDEV-TESTDATA}" ,
"id" : 1 ,
"title" : "Text - Library Panel" ,
"type" : "text" ,
"description" : "A description"
}
` ) ,
2023-02-01 17:32:05 +01:00
Kind : int64 ( model . PanelElement ) ,
2021-05-11 07:10:19 +02:00
}
2021-09-27 09:04:36 +02:00
resp , err := sc . elementService . CreateElement ( sc . ctx , sc . user , command )
2021-05-11 07:10:19 +02:00
require . NoError ( t , err )
var model map [ string ] interface { }
err = json . Unmarshal ( resp . Model , & model )
require . NoError ( t , err )
sc . initialResult = libraryPanelResult {
Result : libraryPanel {
ID : resp . ID ,
OrgID : resp . OrgID ,
FolderID : resp . FolderID ,
UID : resp . UID ,
Name : resp . Name ,
Type : resp . Type ,
Description : resp . Description ,
Model : model ,
Version : resp . Version ,
Meta : resp . Meta ,
} ,
}
2021-02-24 14:06:22 +01:00
fn ( t , sc )
} )
2020-12-21 09:40:27 +01:00
}
// testScenario is a wrapper around t.Run performing common setup for library panel tests.
// It takes your real test function as a callback.
2020-12-22 12:00:41 +01:00
func testScenario ( t * testing . T , desc string , fn func ( t * testing . T , sc scenarioContext ) ) {
2020-12-21 09:40:27 +01:00
t . Helper ( )
t . Run ( desc , func ( t * testing . T ) {
orgID := int64 ( 1 )
2022-08-10 11:56:48 +02:00
role := org . RoleAdmin
2022-10-19 09:02:15 -04:00
sqlStore , cfg := db . InitTestDBwithCfg ( t )
2022-11-14 21:08:10 +02:00
quotaService := quotatest . New ( false , nil )
2023-04-20 10:24:41 +01:00
ac := actest . FakeAccessControl { ExpectedEvaluate : true }
dashStore := & dashboards . FakeDashboardStore { }
dashStore . On ( "GetDashboard" , mock . Anything , mock . Anything ) . Return ( & dashboards . Dashboard { ID : 1 } , nil )
folderStore := folderimpl . ProvideDashboardFolderStore ( sqlStore )
dashAlertService := alerting . ProvideDashAlertExtractorService ( nil , nil , nil )
dashPermissionService := acmock . NewMockedPermissionsService ( )
dashService , err := dashboardservice . ProvideDashboardServiceImpl (
setting . NewCfg ( ) , dashStore , folderStore , dashAlertService ,
featuremgmt . WithFeatures ( ) , acmock . NewMockedPermissionsService ( ) , dashPermissionService , ac ,
foldertest . NewFakeService ( ) ,
)
2022-11-14 21:08:10 +02:00
require . NoError ( t , err )
2023-04-20 10:24:41 +01:00
guardian . InitAccessControlGuardian ( setting . NewCfg ( ) , sqlStore , ac , acmock . NewMockedPermissionsService ( ) , acmock . NewMockedPermissionsService ( ) , dashService )
2022-03-10 12:58:18 +01:00
2023-04-20 10:24:41 +01:00
dashboardStore , err := database . ProvideDashboardStore ( sqlStore , cfg , featuremgmt . WithFeatures ( ) , tagimpl . ProvideService ( sqlStore , sqlStore . Cfg ) , quotaService )
require . NoError ( t , err )
2022-03-10 12:58:18 +01:00
features := featuremgmt . WithFeatures ( )
2023-02-01 15:43:21 +02:00
folderService := folderimpl . ProvideService ( ac , bus . ProvideBus ( tracing . InitializeTracerForTest ( ) ) , cfg , dashboardStore , folderStore , nil , features )
2022-02-16 14:15:44 +01:00
2023-04-06 11:16:15 +03:00
elementService := libraryelements . ProvideService ( cfg , sqlStore , routing . NewRouteRegister ( ) , folderService , featuremgmt . WithFeatures ( ) )
2021-05-12 08:48:17 +02:00
service := LibraryPanelService {
Cfg : cfg ,
SQLStore : sqlStore ,
2022-02-16 14:15:44 +01:00
LibraryElementService : elementService ,
2021-05-12 08:48:17 +02:00
}
2020-12-21 09:40:27 +01:00
2022-08-10 11:56:48 +02:00
usr := & user . SignedInUser {
2022-08-11 13:28:55 +02:00
UserID : 1 ,
2021-02-02 06:25:35 +01:00
Name : "Signed In User" ,
Login : "signed_in_user" ,
Email : "signed.in.user@test.com" ,
2022-08-11 13:28:55 +02:00
OrgID : orgID ,
2020-12-21 09:40:27 +01:00
OrgRole : role ,
LastSeenAt : time . Now ( ) ,
2023-04-20 10:24:41 +01:00
// Allow the user to create folders
Permissions : map [ int64 ] map [ string ] [ ] string {
orgID : {
dashboards . ActionFoldersRead : { dashboards . ScopeFoldersAll } ,
} ,
} ,
2020-12-21 09:40:27 +01:00
}
2021-02-24 14:06:22 +01:00
2021-02-02 06:25:35 +01:00
// deliberate difference between signed in user and user in db to make it crystal clear
// what to expect in the tests
// In the real world these are identical
2022-06-28 14:32:25 +02:00
cmd := user . CreateUserCommand {
2021-02-02 06:25:35 +01:00
Email : "user.in.db@test.com" ,
Name : "User In DB" ,
2021-09-20 10:58:24 +02:00
Login : userInDbName ,
2021-02-02 06:25:35 +01:00
}
2022-11-11 14:28:24 +01:00
ctx := appcontext . WithUser ( context . Background ( ) , usr )
2022-12-07 17:03:22 +01:00
orgSvc , err := orgimpl . ProvideService ( sqlStore , sqlStore . Cfg , quotaService )
require . NoError ( t , err )
2023-02-06 17:50:03 +01:00
usrSvc , err := userimpl . ProvideService ( sqlStore , orgSvc , sqlStore . Cfg , nil , nil , quotaService , supportbundlestest . NewFakeBundleService ( ) )
2022-12-07 17:03:22 +01:00
require . NoError ( t , err )
_ , err = usrSvc . Create ( context . Background ( ) , & cmd )
2021-02-02 06:25:35 +01:00
require . NoError ( t , err )
2020-12-21 09:40:27 +01:00
sc := scenarioContext {
2022-06-28 14:32:25 +02:00
user : usr ,
2022-11-11 14:28:24 +01:00
ctx : ctx ,
2021-05-12 08:48:17 +02:00
service : & service ,
2022-02-16 14:15:44 +01:00
elementService : elementService ,
2021-05-11 07:10:19 +02:00
sqlStore : sqlStore ,
2020-12-21 09:40:27 +01:00
}
2021-02-24 14:06:22 +01:00
2023-04-20 10:24:41 +01:00
foldr := createFolder ( t , sc , "ScenarioFolder" )
2023-01-25 09:14:32 +01:00
sc . folder = & folder . Folder {
ID : foldr . ID ,
UID : foldr . UID ,
Title : foldr . Title ,
URL : dashboards . GetFolderURL ( foldr . UID , slugify . Slugify ( foldr . Title ) ) ,
2023-01-18 13:52:41 +01:00
Version : 0 ,
2023-01-25 09:14:32 +01:00
Created : foldr . Created ,
Updated : foldr . Updated ,
2023-01-18 13:52:41 +01:00
UpdatedBy : 0 ,
CreatedBy : 0 ,
HasACL : false ,
}
2020-12-21 09:40:27 +01:00
fn ( t , sc )
} )
}
2020-12-23 12:42:52 +01:00
func getCompareOptions ( ) [ ] cmp . Option {
return [ ] cmp . Option {
cmp . Transformer ( "Time" , func ( in time . Time ) int64 {
return in . UTC ( ) . Unix ( )
} ) ,
}
}