2020-12-09 06:10:18 -06:00
package librarypanels
import (
2021-02-01 23:25:35 -06:00
"context"
2020-12-21 02:40:27 -06:00
"encoding/json"
2020-12-09 06:10:18 -06:00
"testing"
"time"
2020-12-23 05:42:52 -06:00
"github.com/google/go-cmp/cmp"
2022-12-15 08:34:17 -06:00
"github.com/stretchr/testify/mock"
2022-03-10 11:19:50 -06:00
"github.com/stretchr/testify/require"
2022-02-16 07:15:44 -06:00
"github.com/grafana/grafana/pkg/api/routing"
2022-10-18 08:31:56 -05:00
"github.com/grafana/grafana/pkg/bus"
2021-02-01 23:25:35 -06:00
"github.com/grafana/grafana/pkg/components/simplejson"
2022-11-10 03:41:03 -06:00
"github.com/grafana/grafana/pkg/infra/appcontext"
2022-10-19 08:02:15 -05:00
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/db/dbtest"
2023-01-18 06:52:41 -06:00
"github.com/grafana/grafana/pkg/infra/slugify"
2022-10-18 08:31:56 -05:00
"github.com/grafana/grafana/pkg/infra/tracing"
2023-01-29 22:14:12 -06:00
"github.com/grafana/grafana/pkg/kinds/librarypanel"
2022-03-10 05:58:18 -06:00
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
2022-02-28 02:54:56 -06:00
"github.com/grafana/grafana/pkg/services/alerting"
2021-02-24 07:06:22 -06:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-02-16 07:15:44 -06:00
"github.com/grafana/grafana/pkg/services/dashboards/database"
2022-05-17 13:52:22 -05:00
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
2022-03-10 05:58:18 -06:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2022-11-10 03:41:03 -06:00
"github.com/grafana/grafana/pkg/services/folder"
2022-10-10 14:47:53 -05:00
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
2023-01-26 02:21:10 -06:00
"github.com/grafana/grafana/pkg/services/folder/foldertest"
2022-03-21 04:49:49 -05:00
"github.com/grafana/grafana/pkg/services/guardian"
2021-05-11 00:10:19 -05:00
"github.com/grafana/grafana/pkg/services/libraryelements"
2023-02-01 10:32:05 -06:00
"github.com/grafana/grafana/pkg/services/libraryelements/model"
2022-08-10 04:56:48 -05:00
"github.com/grafana/grafana/pkg/services/org"
2022-12-07 10:03:22 -06:00
"github.com/grafana/grafana/pkg/services/org/orgimpl"
2022-11-14 13:08:10 -06:00
"github.com/grafana/grafana/pkg/services/quota/quotatest"
2022-09-21 07:04:01 -05:00
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
2022-09-20 11:58:04 -05:00
"github.com/grafana/grafana/pkg/services/team/teamtest"
2022-06-28 07:32:25 -05:00
"github.com/grafana/grafana/pkg/services/user"
2022-12-07 10:03:22 -06:00
"github.com/grafana/grafana/pkg/services/user/userimpl"
2020-12-09 06:10:18 -06:00
"github.com/grafana/grafana/pkg/setting"
)
2021-09-20 03:58:24 -05:00
const userInDbName = "user_in_db"
const userInDbAvatar = "/avatar/402d08de060496d6b6874495fe20f5ad"
2020-12-23 05:42:52 -06:00
2021-01-20 02:28:10 -06:00
func TestConnectLibraryPanelsForDashboard ( t * testing . T ) {
2021-02-24 07:06:22 -06:00
scenarioWithLibraryPanel ( t , "When an admin tries to store a dashboard with a library panel, it should connect the two" ,
2021-01-20 02:28:10 -06: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 04:05:59 -05:00
"uid" : sc . initialResult . Result . UID ,
2021-01-20 02:28:10 -06:00
} ,
"title" : "Text - Library Panel" ,
"type" : "text" ,
} ,
} ,
}
2023-01-16 09:33:55 -06:00
dash := dashboards . Dashboard {
2021-05-11 00:10:19 -05:00
Title : "Testing ConnectLibraryPanelsForDashboard" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
2021-01-20 02:28:10 -06:00
}
2023-01-25 02:14:32 -06:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID )
2021-01-20 02:28:10 -06:00
2021-09-27 02:04:36 -05:00
err := sc . service . ConnectLibraryPanelsForDashboard ( sc . ctx , sc . user , dashInDB )
2021-01-20 02:28:10 -06:00
require . NoError ( t , err )
2023-01-16 09:33:55 -06:00
elements , err := sc . elementService . GetElementsForDashboard ( sc . ctx , dashInDB . ID )
2021-01-20 02:28:10 -06:00
require . NoError ( t , err )
2021-05-11 00:10:19 -05:00
require . Len ( t , elements , 1 )
require . Equal ( t , sc . initialResult . Result . UID , elements [ sc . initialResult . Result . UID ] . UID )
2021-01-20 02:28:10 -06:00
} )
2021-09-08 03:53:55 -05: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 10:32:05 -06:00
cmd := model . CreateLibraryElementCommand {
2021-09-08 03:53:55 -05: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 10:32:05 -06:00
Kind : int64 ( model . PanelElement ) ,
2021-09-08 03:53:55 -05:00
}
2021-09-27 02:04:36 -05:00
outsidePanel , err := sc . elementService . CreateElement ( sc . ctx , sc . user , cmd )
2021-09-08 03:53:55 -05: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 04:05:59 -05:00
"uid" : sc . initialResult . Result . UID ,
2021-09-08 03:53:55 -05: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 04:05:59 -05:00
"uid" : outsidePanel . UID ,
2021-09-08 03:53:55 -05:00
} ,
"title" : "Outside row" ,
"type" : "text" ,
} ,
} ,
}
2023-01-16 09:33:55 -06:00
dash := dashboards . Dashboard {
2021-09-08 03:53:55 -05:00
Title : "Testing ConnectLibraryPanelsForDashboard" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
}
2023-01-25 02:14:32 -06:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID )
2021-09-08 03:53:55 -05:00
2021-09-27 02:04:36 -05:00
err = sc . service . ConnectLibraryPanelsForDashboard ( sc . ctx , sc . user , dashInDB )
2021-09-08 03:53:55 -05:00
require . NoError ( t , err )
2023-01-16 09:33:55 -06:00
elements , err := sc . elementService . GetElementsForDashboard ( sc . ctx , dashInDB . ID )
2021-09-08 03:53:55 -05: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 07:06:22 -06:00
scenarioWithLibraryPanel ( t , "When an admin tries to store a dashboard with a library panel without uid, it should fail" ,
2021-01-20 02:28:10 -06: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 07:06:22 -06:00
"name" : sc . initialResult . Result . Name ,
2021-01-20 02:28:10 -06:00
} ,
"title" : "Text - Library Panel" ,
"type" : "text" ,
} ,
} ,
}
2023-01-16 09:33:55 -06:00
dash := dashboards . Dashboard {
2021-05-11 00:10:19 -05:00
Title : "Testing ConnectLibraryPanelsForDashboard" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
2021-01-20 02:28:10 -06:00
}
2023-01-25 02:14:32 -06:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID )
2021-01-20 02:28:10 -06:00
2021-09-27 02:04:36 -05:00
err := sc . service . ConnectLibraryPanelsForDashboard ( sc . ctx , sc . user , dashInDB )
2021-01-20 02:28:10 -06:00
require . EqualError ( t , err , errLibraryPanelHeaderUIDMissing . Error ( ) )
} )
2021-02-16 06:00:56 -06:00
2021-02-24 07:06:22 -06: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 06:00:56 -06:00
func ( t * testing . T , sc scenarioContext ) {
2023-02-01 10:32:05 -06:00
unused , err := sc . elementService . CreateElement ( sc . ctx , sc . user , model . CreateLibraryElementCommand {
2023-01-25 02:14:32 -06:00
FolderID : sc . folder . ID ,
2021-05-11 00:10:19 -05: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 06:00:56 -06:00
}
2021-05-11 00:10:19 -05:00
` ) ,
2023-02-01 10:32:05 -06:00
Kind : int64 ( model . PanelElement ) ,
2021-05-11 00:10:19 -05:00
} )
2021-02-16 06:00:56 -06:00
require . NoError ( t , err )
2021-01-20 02:28:10 -06: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 00:10:19 -05:00
"id" : int64 ( 4 ) ,
2021-01-20 02:28:10 -06:00
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 6 ,
"y" : 0 ,
} ,
"datasource" : "${DS_GDEV-TESTDATA}" ,
"libraryPanel" : map [ string ] interface { } {
2022-04-20 04:05:59 -05:00
"uid" : unused . UID ,
2021-01-20 02:28:10 -06:00
} ,
2021-05-11 00:10:19 -05:00
"title" : "Unused Libray Panel" ,
"description" : "Unused description" ,
2021-01-20 02:28:10 -06:00
} ,
} ,
}
2021-05-11 00:10:19 -05:00
2023-01-16 09:33:55 -06:00
dash := dashboards . Dashboard {
2021-05-11 00:10:19 -05:00
Title : "Testing ConnectLibraryPanelsForDashboard" ,
Data : simplejson . NewFromAny ( dashJSON ) ,
2021-01-20 02:28:10 -06:00
}
2023-01-25 02:14:32 -06:00
dashInDB := createDashboard ( t , sc . sqlStore , sc . user , & dash , sc . folder . ID )
2023-01-16 09:33:55 -06:00
err = sc . elementService . ConnectElementsToDashboard ( sc . ctx , sc . user , [ ] string { sc . initialResult . Result . UID } , dashInDB . ID )
2021-01-20 02:28:10 -06:00
require . NoError ( t , err )
2021-05-11 00:10:19 -05:00
panelJSON := [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
2021-01-20 02:28:10 -06:00
} ,
2021-05-11 00:10:19 -05: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 04:05:59 -05:00
"uid" : sc . initialResult . Result . UID ,
2021-01-20 02:28:10 -06:00
} ,
2021-05-11 00:10:19 -05:00
"title" : "Text - Library Panel" ,
"type" : "text" ,
2021-01-20 02:28:10 -06:00
} ,
}
2021-05-11 00:10:19 -05:00
dashInDB . Data . Set ( "panels" , panelJSON )
2021-09-27 02:04:36 -05:00
err = sc . service . ConnectLibraryPanelsForDashboard ( sc . ctx , sc . user , dashInDB )
2021-03-02 03:34:01 -06:00
require . NoError ( t , err )
2023-01-16 09:33:55 -06:00
elements , err := sc . elementService . GetElementsForDashboard ( sc . ctx , dashInDB . ID )
2021-03-02 03:34:01 -06:00
require . NoError ( t , err )
2021-05-11 00:10:19 -05:00
require . Len ( t , elements , 1 )
require . Equal ( t , sc . initialResult . Result . UID , elements [ sc . initialResult . Result . UID ] . UID )
2021-03-02 03:34:01 -06:00
} )
}
2021-09-20 03:58:24 -05: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 10:18:27 -05:00
var libraryElements = map [ string ] interface { } {
missingUID : map [ string ] interface { } {
"model" : missingModel ,
} ,
}
2021-09-20 03:58:24 -05:00
2022-06-29 10:18:27 -05: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 03:58:24 -05:00
} ,
} ,
}
2022-06-29 10:18:27 -05:00
2021-09-27 02:04:36 -05:00
_ , err := sc . elementService . GetElement ( sc . ctx , sc . user , missingUID )
2022-06-29 10:18:27 -05:00
2023-02-01 10:32:05 -06:00
require . EqualError ( t , err , model . ErrLibraryElementNotFound . Error ( ) )
2021-09-20 03:58:24 -05:00
2022-06-29 10:18:27 -05:00
err = sc . service . ImportLibraryPanelsForDashboard ( sc . ctx , sc . user , simplejson . NewFromAny ( libraryElements ) , panels , 0 )
2021-09-20 03:58:24 -05:00
require . NoError ( t , err )
2021-09-27 02:04:36 -05:00
element , err := sc . elementService . GetElement ( sc . ctx , sc . user , missingUID )
2021-09-20 03:58:24 -05: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 10:18:27 -05:00
panels := [ ] interface { } {
map [ string ] interface { } {
"id" : int64 ( 1 ) ,
"gridPos" : map [ string ] interface { } {
"h" : 6 ,
"w" : 6 ,
"x" : 0 ,
"y" : 0 ,
2021-09-20 03:58:24 -05:00
} ,
2022-06-29 10:18:27 -05:00
} ,
map [ string ] interface { } {
"libraryPanel" : map [ string ] interface { } {
"uid" : sc . initialResult . Result . UID ,
"name" : sc . initialResult . Result . Name ,
2021-09-20 03:58:24 -05:00
} ,
} ,
}
2022-06-29 10:18:27 -05:00
2021-09-27 02:04:36 -05:00
_ , err := sc . elementService . GetElement ( sc . ctx , sc . user , existingUID )
2021-09-20 03:58:24 -05:00
require . NoError ( t , err )
2023-01-25 02:14:32 -06:00
err = sc . service . ImportLibraryPanelsForDashboard ( sc . ctx , sc . user , simplejson . New ( ) , panels , sc . folder . ID )
2021-09-20 03:58:24 -05:00
require . NoError ( t , err )
2021-09-27 02:04:36 -05:00
element , err := sc . elementService . GetElement ( sc . ctx , sc . user , existingUID )
2021-09-20 03:58:24 -05: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 02:14:32 -06:00
expected . Meta . FolderUID = sc . folder . UID
2021-09-20 03:58:24 -05: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 10:18:27 -05:00
2021-09-20 03:58:24 -05: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 10:18:27 -05: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 03:58:24 -05:00
} ,
2022-06-29 10:18:27 -05: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 03:58:24 -05:00
} ,
2022-06-29 10:18:27 -05:00
map [ string ] interface { } {
"libraryPanel" : map [ string ] interface { } {
"uid" : insideUID ,
"name" : insideName ,
2021-09-20 03:58:24 -05:00
} ,
} ,
} ,
} ,
}
2022-06-29 10:18:27 -05:00
2021-09-27 02:04:36 -05:00
_ , err := sc . elementService . GetElement ( sc . ctx , sc . user , outsideUID )
2023-02-01 10:32:05 -06:00
require . EqualError ( t , err , model . ErrLibraryElementNotFound . Error ( ) )
2021-09-27 02:04:36 -05:00
_ , err = sc . elementService . GetElement ( sc . ctx , sc . user , insideUID )
2023-02-01 10:32:05 -06:00
require . EqualError ( t , err , model . ErrLibraryElementNotFound . Error ( ) )
2021-09-20 03:58:24 -05:00
2022-06-29 10:18:27 -05:00
err = sc . service . ImportLibraryPanelsForDashboard ( sc . ctx , sc . user , simplejson . NewFromAny ( libraryElements ) , panels , 0 )
2021-09-20 03:58:24 -05:00
require . NoError ( t , err )
2021-09-27 02:04:36 -05:00
element , err := sc . elementService . GetElement ( sc . ctx , sc . user , outsideUID )
2021-09-20 03:58:24 -05: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 02:04:36 -05:00
element , err = sc . elementService . GetElement ( sc . ctx , sc . user , insideUID )
2021-09-20 03:58:24 -05: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 02:40:27 -06:00
type libraryPanel struct {
2021-05-11 00:10:19 -05:00
ID int64
OrgID int64
FolderID int64
UID string
Name string
2021-03-24 07:43:51 -05:00
Type string
Description string
2021-05-11 00:10:19 -05:00
Model map [ string ] interface { }
Version int64
2023-02-01 10:32:05 -06:00
Meta model . LibraryElementDTOMeta
2020-12-21 02:40:27 -06:00
}
2020-12-10 23:08:32 -06:00
2021-09-20 03:58:24 -05: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 10:32:05 -06: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 03:58:24 -05:00
}
2020-12-21 02:40:27 -06:00
type libraryPanelResult struct {
Result libraryPanel ` json:"result" `
}
2020-12-10 23:08:32 -06:00
2020-12-21 02:40:27 -06:00
type scenarioContext struct {
2021-09-27 02:04:36 -05:00
ctx context . Context
2021-05-12 01:48:17 -05:00
service Service
elementService libraryelements . Service
2022-08-10 04:56:48 -05:00
user * user . SignedInUser
2023-01-25 02:14:32 -06:00
folder * folder . Folder
2021-05-11 00:10:19 -05:00
initialResult libraryPanelResult
2022-10-19 08:02:15 -05:00
sqlStore db . DB
2021-02-24 07:06:22 -06:00
}
2021-03-01 08:33:17 -06:00
type folderACLItem struct {
2022-08-10 04:56:48 -05:00
roleType org . RoleType
2023-01-26 07:46:30 -06:00
permission dashboards . PermissionType
2021-03-01 08:33:17 -06:00
}
2023-02-01 10:32:05 -06:00
func toLibraryElement ( t * testing . T , res model . LibraryElementDTO ) libraryElement {
var libraryElementModel = libraryElementModel { }
err := json . Unmarshal ( res . Model , & libraryElementModel )
2021-09-20 03:58:24 -05: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 10:32:05 -06:00
Model : libraryElementModel ,
2021-09-20 03:58:24 -05:00
Version : res . Version ,
2023-02-01 10:32:05 -06:00
Meta : model . LibraryElementDTOMeta {
2021-09-20 03:58:24 -05:00
FolderName : res . Meta . FolderName ,
FolderUID : res . Meta . FolderUID ,
ConnectedDashboards : res . Meta . ConnectedDashboards ,
Created : res . Meta . Created ,
Updated : res . Meta . Updated ,
2023-01-29 22:14:12 -06:00
CreatedBy : librarypanel . LibraryElementDTOMetaUser {
Id : res . Meta . CreatedBy . Id ,
2021-09-20 03:58:24 -05:00
Name : res . Meta . CreatedBy . Name ,
2023-01-29 22:14:12 -06:00
AvatarUrl : res . Meta . CreatedBy . AvatarUrl ,
2021-09-20 03:58:24 -05:00
} ,
2023-01-29 22:14:12 -06:00
UpdatedBy : librarypanel . LibraryElementDTOMetaUser {
Id : res . Meta . UpdatedBy . Id ,
2021-09-20 03:58:24 -05:00
Name : res . Meta . UpdatedBy . Name ,
2023-01-29 22:14:12 -06:00
AvatarUrl : res . Meta . UpdatedBy . AvatarUrl ,
2021-09-20 03:58:24 -05:00
} ,
} ,
}
}
2023-02-01 10:32:05 -06: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 03:58:24 -05: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 10:32:05 -06:00
Meta : model . LibraryElementDTOMeta {
2021-09-20 03:58:24 -05:00
FolderName : "General" ,
FolderUID : "" ,
ConnectedDashboards : 0 ,
Created : res . Meta . Created ,
Updated : res . Meta . Updated ,
2023-01-29 22:14:12 -06:00
CreatedBy : librarypanel . LibraryElementDTOMetaUser {
Id : 1 ,
2021-09-20 03:58:24 -05:00
Name : userInDbName ,
2023-01-29 22:14:12 -06:00
AvatarUrl : userInDbAvatar ,
2021-09-20 03:58:24 -05:00
} ,
2023-01-29 22:14:12 -06:00
UpdatedBy : librarypanel . LibraryElementDTOMetaUser {
Id : 1 ,
2021-09-20 03:58:24 -05:00
Name : userInDbName ,
2023-01-29 22:14:12 -06:00
AvatarUrl : userInDbAvatar ,
2021-09-20 03:58:24 -05:00
} ,
} ,
}
}
2023-01-16 09:33:55 -06: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 08:33:17 -06:00
dashItem := & dashboards . SaveDashboardDTO {
Dashboard : dash ,
Message : "" ,
2023-01-16 09:33:55 -06:00
OrgID : user . OrgID ,
2021-09-27 02:04:36 -05:00
User : user ,
2021-03-01 08:33:17 -06:00
Overwrite : false ,
}
2022-05-05 10:31:14 -05:00
cfg := setting . NewCfg ( )
2022-08-11 08:37:31 -05:00
cfg . RBACEnabled = false
2022-05-05 10:31:14 -05:00
cfg . IsFeatureToggleEnabled = featuremgmt . WithFeatures ( ) . IsEnabled
2022-11-14 13:08:10 -06: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 14:33:06 -05:00
dashAlertService := alerting . ProvideDashAlertExtractorService ( nil , nil , nil )
2022-06-07 04:02:20 -05:00
ac := acmock . New ( )
2023-02-01 07:43:21 -06:00
folderStore := folderimpl . ProvideDashboardFolderStore ( sqlStore )
2022-03-10 05:58:18 -06:00
service := dashboardservice . ProvideDashboardService (
2023-02-01 07:43:21 -06:00
cfg , dashboardStore , folderStore , dashAlertService ,
2022-06-07 04:02:20 -05:00
featuremgmt . WithFeatures ( ) , acmock . NewMockedPermissionsService ( ) , acmock . NewMockedPermissionsService ( ) , ac ,
2023-01-26 02:21:10 -06:00
foldertest . NewFakeService ( ) ,
2022-03-10 05:58:18 -06:00
)
dashboard , err := service . SaveDashboard ( context . Background ( ) , dashItem , true )
2021-02-24 07:06:22 -06:00
require . NoError ( t , err )
2021-03-01 08:33:17 -06:00
return dashboard
}
2022-10-14 14:33:06 -05:00
func createFolderWithACL ( t * testing . T , sqlStore db . DB , title string , user * user . SignedInUser ,
2022-11-10 03:41:03 -06:00
items [ ] folderACLItem ) * folder . Folder {
2021-03-17 10:06:10 -05:00
t . Helper ( )
2022-05-10 08:48:47 -05:00
ac := acmock . New ( )
2022-03-10 05:58:18 -06:00
cfg := setting . NewCfg ( )
2022-08-11 08:37:31 -05:00
cfg . RBACEnabled = false
2022-05-05 10:31:14 -05:00
cfg . IsFeatureToggleEnabled = featuremgmt . WithFeatures ( ) . IsEnabled
2022-03-10 05:58:18 -06:00
features := featuremgmt . WithFeatures ( )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dashboardStore , err := database . ProvideDashboardStore ( sqlStore , cfg , featuremgmt . WithFeatures ( ) , tagimpl . ProvideService ( sqlStore , cfg ) , quotaService )
require . NoError ( t , err )
2023-02-01 07:43:21 -06:00
folderStore := folderimpl . ProvideDashboardFolderStore ( sqlStore )
s := folderimpl . ProvideService ( ac , bus . ProvideBus ( tracing . InitializeTracerForTest ( ) ) , cfg , dashboardStore , folderStore , nil , features )
2022-03-10 05:58:18 -06:00
2021-03-17 10:06:10 -05:00
t . Logf ( "Creating folder with title and UID %q" , title )
2022-11-10 03:41:03 -06:00
ctx := appcontext . WithUser ( context . Background ( ) , user )
2022-11-23 03:13:47 -06:00
folder , err := s . Create ( ctx , & folder . CreateFolderCommand { OrgID : user . OrgID , Title : title , UID : title , SignedInUser : user } )
2021-03-01 08:33:17 -06:00
require . NoError ( t , err )
2022-11-10 03:41:03 -06:00
updateFolderACL ( t , dashboardStore , folder . ID , items )
2021-03-01 08:33:17 -06:00
2021-03-17 10:06:10 -05:00
return folder
2021-02-24 07:06:22 -06:00
}
2022-02-16 07:15:44 -06:00
func updateFolderACL ( t * testing . T , dashboardStore * database . DashboardStore , folderID int64 , items [ ] folderACLItem ) {
2021-03-17 10:06:10 -05:00
t . Helper ( )
2021-03-01 08:33:17 -06:00
if len ( items ) == 0 {
return
}
2023-01-20 07:58:47 -06:00
var aclItems [ ] * dashboards . DashboardACL
2021-03-01 08:33:17 -06:00
for _ , item := range items {
role := item . roleType
permission := item . permission
2023-01-20 07:58:47 -06:00
aclItems = append ( aclItems , & dashboards . DashboardACL {
2021-03-01 08:33:17 -06:00
DashboardID : folderID ,
Role : & role ,
Permission : permission ,
Created : time . Now ( ) ,
Updated : time . Now ( ) ,
} )
}
2022-02-16 07:15:44 -06:00
err := dashboardStore . UpdateDashboardACL ( context . Background ( ) , folderID , aclItems )
2021-02-24 07:06:22 -06:00
require . NoError ( t , err )
}
func scenarioWithLibraryPanel ( t * testing . T , desc string , fn func ( t * testing . T , sc scenarioContext ) ) {
2022-10-19 08:02:15 -05:00
store := dbtest . NewFakeDB ( )
2022-12-15 08:34:17 -06:00
dashSvc := dashboards . NewFakeDashboardService ( t )
2023-01-25 03:36:26 -06:00
var result * dashboards . Dashboard
2023-01-16 09:33:55 -06:00
dashSvc . On ( "GetDashboard" , mock . Anything , mock . AnythingOfType ( "*dashboards.GetDashboardQuery" ) ) . Run ( func ( args mock . Arguments ) {
q := args . Get ( 1 ) . ( * dashboards . GetDashboardQuery )
2023-01-25 03:36:26 -06:00
result = & dashboards . Dashboard {
2023-01-16 09:33:55 -06:00
ID : q . ID ,
UID : q . UID ,
2022-12-15 08:34:17 -06:00
}
2023-01-25 03:36:26 -06:00
} ) . Return ( result , nil )
2022-12-15 08:34:17 -06:00
guardian . InitLegacyGuardian ( store , dashSvc , & teamtest . FakeService { } )
2021-03-17 10:06:10 -05:00
t . Helper ( )
2021-02-24 07:06:22 -06:00
testScenario ( t , desc , func ( t * testing . T , sc scenarioContext ) {
2023-02-01 10:32:05 -06:00
command := model . CreateLibraryElementCommand {
2023-01-25 02:14:32 -06:00
FolderID : sc . folder . ID ,
2021-05-11 00:10:19 -05: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 10:32:05 -06:00
Kind : int64 ( model . PanelElement ) ,
2021-05-11 00:10:19 -05:00
}
2021-09-27 02:04:36 -05:00
resp , err := sc . elementService . CreateElement ( sc . ctx , sc . user , command )
2021-05-11 00:10:19 -05: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 07:06:22 -06:00
fn ( t , sc )
} )
2020-12-21 02:40:27 -06: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 05:00:41 -06:00
func testScenario ( t * testing . T , desc string , fn func ( t * testing . T , sc scenarioContext ) ) {
2020-12-21 02:40:27 -06:00
t . Helper ( )
t . Run ( desc , func ( t * testing . T ) {
2021-05-12 01:48:17 -05:00
cfg := setting . NewCfg ( )
2022-08-11 08:37:31 -05:00
cfg . RBACEnabled = false
2020-12-21 02:40:27 -06:00
orgID := int64 ( 1 )
2022-08-10 04:56:48 -05:00
role := org . RoleAdmin
2022-10-19 08:02:15 -05:00
sqlStore , cfg := db . InitTestDBwithCfg ( t )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dashboardStore , err := database . ProvideDashboardStore ( sqlStore , cfg , featuremgmt . WithFeatures ( ) , tagimpl . ProvideService ( sqlStore , sqlStore . Cfg ) , quotaService )
require . NoError ( t , err )
2022-03-10 05:58:18 -06:00
features := featuremgmt . WithFeatures ( )
2022-06-07 04:02:20 -05:00
ac := acmock . New ( )
2023-02-01 07:43:21 -06:00
folderStore := folderimpl . ProvideDashboardFolderStore ( sqlStore )
folderService := folderimpl . ProvideService ( ac , bus . ProvideBus ( tracing . InitializeTracerForTest ( ) ) , cfg , dashboardStore , folderStore , nil , features )
2022-02-16 07:15:44 -06:00
elementService := libraryelements . ProvideService ( cfg , sqlStore , routing . NewRouteRegister ( ) , folderService )
2021-05-12 01:48:17 -05:00
service := LibraryPanelService {
Cfg : cfg ,
SQLStore : sqlStore ,
2022-02-16 07:15:44 -06:00
LibraryElementService : elementService ,
2021-05-12 01:48:17 -05:00
}
2020-12-21 02:40:27 -06:00
2022-08-10 04:56:48 -05:00
usr := & user . SignedInUser {
2022-08-11 06:28:55 -05:00
UserID : 1 ,
2021-02-01 23:25:35 -06:00
Name : "Signed In User" ,
Login : "signed_in_user" ,
Email : "signed.in.user@test.com" ,
2022-08-11 06:28:55 -05:00
OrgID : orgID ,
2020-12-21 02:40:27 -06:00
OrgRole : role ,
LastSeenAt : time . Now ( ) ,
}
2021-02-24 07:06:22 -06:00
2021-02-01 23:25:35 -06: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 07:32:25 -05:00
cmd := user . CreateUserCommand {
2021-02-01 23:25:35 -06:00
Email : "user.in.db@test.com" ,
Name : "User In DB" ,
2021-09-20 03:58:24 -05:00
Login : userInDbName ,
2021-02-01 23:25:35 -06:00
}
2022-11-11 07:28:24 -06:00
ctx := appcontext . WithUser ( context . Background ( ) , usr )
2022-12-07 10:03:22 -06:00
orgSvc , err := orgimpl . ProvideService ( sqlStore , sqlStore . Cfg , quotaService )
require . NoError ( t , err )
usrSvc , err := userimpl . ProvideService ( sqlStore , orgSvc , sqlStore . Cfg , nil , nil , quotaService )
require . NoError ( t , err )
_ , err = usrSvc . Create ( context . Background ( ) , & cmd )
2021-02-01 23:25:35 -06:00
require . NoError ( t , err )
2020-12-21 02:40:27 -06:00
sc := scenarioContext {
2022-06-28 07:32:25 -05:00
user : usr ,
2022-11-11 07:28:24 -06:00
ctx : ctx ,
2021-05-12 01:48:17 -05:00
service : & service ,
2022-02-16 07:15:44 -06:00
elementService : elementService ,
2021-05-11 00:10:19 -05:00
sqlStore : sqlStore ,
2020-12-21 02:40:27 -06:00
}
2021-02-24 07:06:22 -06:00
2023-01-25 02:14:32 -06:00
foldr := createFolderWithACL ( t , sc . sqlStore , "ScenarioFolder" , sc . user , [ ] folderACLItem { } )
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 06:52:41 -06:00
Version : 0 ,
2023-01-25 02:14:32 -06:00
Created : foldr . Created ,
Updated : foldr . Updated ,
2023-01-18 06:52:41 -06:00
UpdatedBy : 0 ,
CreatedBy : 0 ,
HasACL : false ,
}
2020-12-21 02:40:27 -06:00
fn ( t , sc )
} )
}
2020-12-23 05:42:52 -06:00
func getCompareOptions ( ) [ ] cmp . Option {
return [ ] cmp . Option {
cmp . Transformer ( "Time" , func ( in time . Time ) int64 {
return in . UTC ( ) . Unix ( )
} ) ,
}
}