mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
LibraryElements: add fake service implementation and replace its usage in Dashboard API (#93783)
* LibraryElements: add fake service implementation * Dashboards: replace fake LibraryElements implementation
This commit is contained in:
parent
3437c8be7f
commit
fcb17379ea
@ -42,7 +42,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
libraryelementsfake "github.com/grafana/grafana/pkg/services/libraryelements/fake"
|
||||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||||
"github.com/grafana/grafana/pkg/services/licensing/licensingtest"
|
"github.com/grafana/grafana/pkg/services/licensing/licensingtest"
|
||||||
"github.com/grafana/grafana/pkg/services/live"
|
"github.com/grafana/grafana/pkg/services/live"
|
||||||
@ -271,7 +271,7 @@ func TestHTTPServer_DeleteDashboardByUID_AccessControl(t *testing.T) {
|
|||||||
hs.starService = startest.NewStarServiceFake()
|
hs.starService = startest.NewStarServiceFake()
|
||||||
|
|
||||||
hs.LibraryPanelService = &mockLibraryPanelService{}
|
hs.LibraryPanelService = &mockLibraryPanelService{}
|
||||||
hs.LibraryElementService = &mockLibraryElementService{}
|
hs.LibraryElementService = &libraryelementsfake.LibraryElementService{}
|
||||||
|
|
||||||
pubDashService := publicdashboards.NewFakePublicDashboardService(t)
|
pubDashService := publicdashboards.NewFakePublicDashboardService(t)
|
||||||
pubDashService.On("DeleteByDashboard", mock.Anything, mock.Anything).Return(nil).Maybe()
|
pubDashService.On("DeleteByDashboard", mock.Anything, mock.Anything).Return(nil).Maybe()
|
||||||
@ -677,7 +677,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
Cfg: setting.NewCfg(),
|
Cfg: setting.NewCfg(),
|
||||||
ProvisioningService: fakeProvisioningService,
|
ProvisioningService: fakeProvisioningService,
|
||||||
LibraryPanelService: &mockLibraryPanelService{},
|
LibraryPanelService: &mockLibraryPanelService{},
|
||||||
LibraryElementService: &mockLibraryElementService{},
|
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||||
dashboardProvisioningService: mockDashboardProvisioningService{},
|
dashboardProvisioningService: mockDashboardProvisioningService{},
|
||||||
SQLStore: mockSQLStore,
|
SQLStore: mockSQLStore,
|
||||||
AccessControl: accesscontrolmock.New(),
|
AccessControl: accesscontrolmock.New(),
|
||||||
@ -827,7 +827,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
libraryPanelsService := mockLibraryPanelService{}
|
libraryPanelsService := mockLibraryPanelService{}
|
||||||
libraryElementsService := mockLibraryElementService{}
|
libraryElementsService := libraryelementsfake.LibraryElementService{}
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
ac := accesscontrolmock.New()
|
ac := accesscontrolmock.New()
|
||||||
folderPermissions := accesscontrolmock.NewMockedPermissionsService()
|
folderPermissions := accesscontrolmock.NewMockedPermissionsService()
|
||||||
@ -909,7 +909,7 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
|||||||
QuotaService: quotatest.New(false, nil),
|
QuotaService: quotatest.New(false, nil),
|
||||||
pluginStore: &pluginstore.FakePluginStore{},
|
pluginStore: &pluginstore.FakePluginStore{},
|
||||||
LibraryPanelService: &mockLibraryPanelService{},
|
LibraryPanelService: &mockLibraryPanelService{},
|
||||||
LibraryElementService: &mockLibraryElementService{},
|
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||||
DashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
folderService: folderService,
|
folderService: folderService,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
@ -947,7 +947,7 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string
|
|||||||
Live: newTestLive(t, db.InitTestDB(t)),
|
Live: newTestLive(t, db.InitTestDB(t)),
|
||||||
QuotaService: quotatest.New(false, nil),
|
QuotaService: quotatest.New(false, nil),
|
||||||
LibraryPanelService: &mockLibraryPanelService{},
|
LibraryPanelService: &mockLibraryPanelService{},
|
||||||
LibraryElementService: &mockLibraryElementService{},
|
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||||
SQLStore: sqlmock,
|
SQLStore: sqlmock,
|
||||||
dashboardVersionService: fakeDashboardVersionService,
|
dashboardVersionService: fakeDashboardVersionService,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
@ -990,7 +990,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
|||||||
Live: newTestLive(t, db.InitTestDB(t)),
|
Live: newTestLive(t, db.InitTestDB(t)),
|
||||||
QuotaService: quotatest.New(false, nil),
|
QuotaService: quotatest.New(false, nil),
|
||||||
LibraryPanelService: &mockLibraryPanelService{},
|
LibraryPanelService: &mockLibraryPanelService{},
|
||||||
LibraryElementService: &mockLibraryElementService{},
|
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||||
DashboardService: mock,
|
DashboardService: mock,
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
@ -1050,39 +1050,3 @@ func (m *mockLibraryPanelService) ConnectLibraryPanelsForDashboard(c context.Con
|
|||||||
func (m *mockLibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
func (m *mockLibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockLibraryElementService struct{}
|
|
||||||
|
|
||||||
func (l *mockLibraryElementService) CreateElement(c context.Context, signedInUser identity.Requester, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error) {
|
|
||||||
return model.LibraryElementDTO{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetElement gets an element from a UID.
|
|
||||||
func (l *mockLibraryElementService) GetElement(c context.Context, signedInUser identity.Requester, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
|
|
||||||
return model.LibraryElementDTO{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetElementsForDashboard gets all connected elements for a specific dashboard.
|
|
||||||
func (l *mockLibraryElementService) GetElementsForDashboard(c context.Context, dashboardID int64) (map[string]model.LibraryElementDTO, error) {
|
|
||||||
return map[string]model.LibraryElementDTO{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectElementsToDashboard connects elements to a specific dashboard.
|
|
||||||
func (l *mockLibraryElementService) ConnectElementsToDashboard(c context.Context, signedInUser identity.Requester, elementUIDs []string, dashboardID int64) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisconnectElementsFromDashboard disconnects elements from a specific dashboard.
|
|
||||||
func (l *mockLibraryElementService) DisconnectElementsFromDashboard(c context.Context, dashboardID int64) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteLibraryElementsInFolder deletes all elements for a specific folder.
|
|
||||||
func (l *mockLibraryElementService) DeleteLibraryElementsInFolder(c context.Context, signedInUser identity.Requester, folderUID string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAll gets all library elements with support to query filters.
|
|
||||||
func (l *mockLibraryElementService) GetAllElements(c context.Context, signedInUser identity.Requester, query model.SearchLibraryElementsQuery) (model.LibraryElementSearchResult, error) {
|
|
||||||
return model.LibraryElementSearchResult{}, nil
|
|
||||||
}
|
|
||||||
|
125
pkg/services/libraryelements/fake/libraryelements_service.go
Normal file
125
pkg/services/libraryelements/fake/libraryelements_service.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
|
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||||
|
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LibraryElementService is a fake with only the required methods implemented while the others are stubbed.
|
||||||
|
type LibraryElementService struct {
|
||||||
|
elements map[string]model.LibraryElementDTO
|
||||||
|
mx sync.RWMutex
|
||||||
|
idCounter int64
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ libraryelements.Service = (*LibraryElementService)(nil)
|
||||||
|
|
||||||
|
func (l *LibraryElementService) CreateElement(c context.Context, signedInUser identity.Requester, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error) {
|
||||||
|
l.mx.Lock()
|
||||||
|
defer l.mx.Unlock()
|
||||||
|
|
||||||
|
if len(l.elements) == 0 {
|
||||||
|
l.elements = make(map[string]model.LibraryElementDTO, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgID int64 = 1
|
||||||
|
if signedInOrgID := signedInUser.GetOrgID(); signedInOrgID != 0 {
|
||||||
|
orgID = signedInOrgID
|
||||||
|
}
|
||||||
|
|
||||||
|
var folderUID string
|
||||||
|
if cmd.FolderUID != nil {
|
||||||
|
folderUID = *cmd.FolderUID
|
||||||
|
}
|
||||||
|
|
||||||
|
createUID := cmd.UID
|
||||||
|
if len(createUID) == 0 {
|
||||||
|
createUID = util.GenerateShortUID()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := l.elements[createUID]; exists {
|
||||||
|
return model.LibraryElementDTO{}, model.ErrLibraryElementAlreadyExists
|
||||||
|
}
|
||||||
|
|
||||||
|
l.idCounter++
|
||||||
|
|
||||||
|
dto := model.LibraryElementDTO{
|
||||||
|
ID: l.idCounter,
|
||||||
|
OrgID: orgID,
|
||||||
|
FolderID: cmd.FolderID, //nolint: staticcheck
|
||||||
|
FolderUID: folderUID,
|
||||||
|
UID: createUID,
|
||||||
|
Name: cmd.Name,
|
||||||
|
Kind: cmd.Kind,
|
||||||
|
Type: "text",
|
||||||
|
Description: "A description",
|
||||||
|
Model: cmd.Model,
|
||||||
|
Version: 1,
|
||||||
|
Meta: model.LibraryElementDTOMeta{},
|
||||||
|
}
|
||||||
|
|
||||||
|
l.elements[createUID] = dto
|
||||||
|
|
||||||
|
return dto, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LibraryElementService) GetElement(c context.Context, signedInUser identity.Requester, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
|
||||||
|
l.mx.RLock()
|
||||||
|
defer l.mx.RUnlock()
|
||||||
|
|
||||||
|
libraryElement, exists := l.elements[cmd.UID]
|
||||||
|
if !exists {
|
||||||
|
return model.LibraryElementDTO{}, model.ErrLibraryElementNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return libraryElement, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LibraryElementService) GetElementsForDashboard(c context.Context, dashboardID int64) (map[string]model.LibraryElementDTO, error) {
|
||||||
|
return map[string]model.LibraryElementDTO{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LibraryElementService) ConnectElementsToDashboard(c context.Context, signedInUser identity.Requester, elementUIDs []string, dashboardID int64) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LibraryElementService) DisconnectElementsFromDashboard(c context.Context, dashboardID int64) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LibraryElementService) DeleteLibraryElementsInFolder(c context.Context, signedInUser identity.Requester, folderUID string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LibraryElementService) GetAllElements(c context.Context, signedInUser identity.Requester, query model.SearchLibraryElementsQuery) (model.LibraryElementSearchResult, error) {
|
||||||
|
elements := make([]model.LibraryElementDTO, 0, len(l.elements))
|
||||||
|
|
||||||
|
var orgID int64 = 1
|
||||||
|
if signedInOrgID := signedInUser.GetOrgID(); signedInOrgID != 0 {
|
||||||
|
orgID = signedInOrgID
|
||||||
|
}
|
||||||
|
|
||||||
|
l.mx.RLock()
|
||||||
|
defer l.mx.RUnlock()
|
||||||
|
|
||||||
|
for _, element := range l.elements {
|
||||||
|
if element.OrgID != orgID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
elements = append(elements, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For this fake ignore pagination to make it simpler.
|
||||||
|
return model.LibraryElementSearchResult{
|
||||||
|
TotalCount: int64(len(elements)),
|
||||||
|
Elements: elements,
|
||||||
|
Page: 1,
|
||||||
|
PerPage: len(elements),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,132 @@
|
|||||||
|
package fake_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
|
"github.com/grafana/grafana/pkg/services/libraryelements/fake"
|
||||||
|
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLibraryElementService(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
|
||||||
|
t.Run("GetElement", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
user := &identity.StaticRequester{}
|
||||||
|
|
||||||
|
t.Run("when the element does not exist, it returns a `model.ErrLibraryElementNotFound` error", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
svc := &fake.LibraryElementService{}
|
||||||
|
|
||||||
|
element, err := svc.GetElement(ctx, user, model.GetLibraryElementCommand{UID: "does-not-exist"})
|
||||||
|
require.ErrorIs(t, err, model.ErrLibraryElementNotFound)
|
||||||
|
require.Empty(t, element)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("when the element exists, it returns it", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
svc := &fake.LibraryElementService{}
|
||||||
|
|
||||||
|
uid := "uid-1"
|
||||||
|
|
||||||
|
createdElement, err := svc.CreateElement(ctx, user, model.CreateLibraryElementCommand{
|
||||||
|
Name: "ElementName",
|
||||||
|
Model: []byte{},
|
||||||
|
Kind: int64(model.PanelElement),
|
||||||
|
UID: uid,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, createdElement)
|
||||||
|
|
||||||
|
element, err := svc.GetElement(ctx, user, model.GetLibraryElementCommand{UID: uid})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, element, createdElement)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CreateElement", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
user := &identity.StaticRequester{}
|
||||||
|
|
||||||
|
t.Run("when the uid already exists, it returns a `model.ErrLibraryElementAlreadyExists` error", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
svc := &fake.LibraryElementService{}
|
||||||
|
|
||||||
|
cmd := model.CreateLibraryElementCommand{
|
||||||
|
Name: "ElementName",
|
||||||
|
Model: []byte{},
|
||||||
|
Kind: int64(model.PanelElement),
|
||||||
|
UID: "uid-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
createdElement, err := svc.CreateElement(ctx, user, cmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, createdElement)
|
||||||
|
|
||||||
|
createdElement, err = svc.CreateElement(ctx, user, cmd)
|
||||||
|
require.ErrorIs(t, err, model.ErrLibraryElementAlreadyExists)
|
||||||
|
require.Empty(t, createdElement)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("when the uid is not passed in, it generates a new one", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
svc := &fake.LibraryElementService{}
|
||||||
|
|
||||||
|
cmd := model.CreateLibraryElementCommand{
|
||||||
|
Name: "ElementName",
|
||||||
|
Model: []byte{},
|
||||||
|
Kind: int64(model.PanelElement),
|
||||||
|
}
|
||||||
|
|
||||||
|
createdElement, err := svc.CreateElement(ctx, user, cmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, createdElement)
|
||||||
|
require.NotEmpty(t, createdElement.UID)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetAllElements", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("only returns the elements belonging to the requestor org", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
user1 := &identity.StaticRequester{OrgID: 1}
|
||||||
|
user2 := &identity.StaticRequester{OrgID: 2}
|
||||||
|
|
||||||
|
svc := &fake.LibraryElementService{}
|
||||||
|
|
||||||
|
cmd := model.CreateLibraryElementCommand{
|
||||||
|
Name: "ElementName",
|
||||||
|
Model: []byte{},
|
||||||
|
Kind: int64(model.PanelElement),
|
||||||
|
}
|
||||||
|
|
||||||
|
createdElement1, err := svc.CreateElement(ctx, user1, cmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, createdElement1)
|
||||||
|
|
||||||
|
createdElement2, err := svc.CreateElement(ctx, user2, cmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, createdElement2)
|
||||||
|
|
||||||
|
result, err := svc.GetAllElements(ctx, user2, model.SearchLibraryElementsQuery{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, result.Elements, 1)
|
||||||
|
require.Equal(t, createdElement2.UID, result.Elements[0].UID)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user