mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 21:19:28 -06:00
6e9419ea80
The DashboardVersion struct is the database object; the DashboardVersionDTO is the object that should be sent to the API layer. In the future I'd like to move DashboardVersion to dashverimpl and un-export it, but there are a few places that Insert directly into that table, not all of which are test fixtures, so that should wait until we clean up at least the DashboardService's use of it.
1298 lines
50 KiB
Go
1298 lines
50 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"testing"
|
|
|
|
"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/api/response"
|
|
"github.com/grafana/grafana/pkg/api/routing"
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
|
"github.com/grafana/grafana/pkg/models"
|
|
"github.com/grafana/grafana/pkg/plugins"
|
|
"github.com/grafana/grafana/pkg/registry/corekind"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
|
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
|
"github.com/grafana/grafana/pkg/services/alerting"
|
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
|
"github.com/grafana/grafana/pkg/services/dashboards/service"
|
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
|
"github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
"github.com/grafana/grafana/pkg/services/folder"
|
|
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
|
"github.com/grafana/grafana/pkg/services/guardian"
|
|
"github.com/grafana/grafana/pkg/services/libraryelements"
|
|
"github.com/grafana/grafana/pkg/services/live"
|
|
"github.com/grafana/grafana/pkg/services/org"
|
|
pref "github.com/grafana/grafana/pkg/services/preference"
|
|
"github.com/grafana/grafana/pkg/services/preference/preftest"
|
|
"github.com/grafana/grafana/pkg/services/provisioning"
|
|
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
|
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
|
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/web"
|
|
)
|
|
|
|
func TestGetHomeDashboard(t *testing.T) {
|
|
httpReq, err := http.NewRequest(http.MethodGet, "", nil)
|
|
require.NoError(t, err)
|
|
httpReq.Header.Add("Content-Type", "application/json")
|
|
req := &models.ReqContext{SignedInUser: &user.SignedInUser{}, Context: &web.Context{Req: httpReq}}
|
|
cfg := setting.NewCfg()
|
|
cfg.StaticRootPath = "../../public/"
|
|
prefService := preftest.NewPreferenceServiceFake()
|
|
dashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
|
|
|
hs := &HTTPServer{
|
|
Cfg: cfg,
|
|
pluginStore: &plugins.FakePluginStore{},
|
|
SQLStore: mockstore.NewSQLStoreMock(),
|
|
preferenceService: prefService,
|
|
dashboardVersionService: dashboardVersionService,
|
|
Kinds: corekind.NewBase(nil),
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
defaultSetting string
|
|
expectedDashboardPath string
|
|
}{
|
|
{name: "using default config", defaultSetting: "", expectedDashboardPath: "../../public/dashboards/home.json"},
|
|
{name: "custom path", defaultSetting: "../../public/dashboards/default.json", expectedDashboardPath: "../../public/dashboards/default.json"},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
dash := dtos.DashboardFullWithMeta{}
|
|
dash.Meta.FolderTitle = "General"
|
|
|
|
homeDashJSON, err := os.ReadFile(tc.expectedDashboardPath)
|
|
require.NoError(t, err, "must be able to read expected dashboard file")
|
|
hs.Cfg.DefaultHomeDashboardPath = tc.defaultSetting
|
|
bytes, err := simplejson.NewJson(homeDashJSON)
|
|
require.NoError(t, err, "must be able to encode file as JSON")
|
|
|
|
prefService.ExpectedPreference = &pref.Preference{}
|
|
|
|
dash.Dashboard = bytes
|
|
|
|
b, err := json.Marshal(dash)
|
|
require.NoError(t, err, "must be able to marshal object to JSON")
|
|
|
|
res := hs.GetHomeDashboard(req)
|
|
nr, ok := res.(*response.NormalResponse)
|
|
require.True(t, ok, "should return *NormalResponse")
|
|
require.Equal(t, b, nr.Body(), "default home dashboard should equal content on disk")
|
|
})
|
|
}
|
|
}
|
|
|
|
func newTestLive(t *testing.T, store db.DB) *live.GrafanaLive {
|
|
features := featuremgmt.WithFeatures()
|
|
cfg := &setting.Cfg{AppURL: "http://localhost:3000/"}
|
|
cfg.IsFeatureToggleEnabled = features.IsEnabled
|
|
gLive, err := live.ProvideService(nil, cfg,
|
|
routing.NewRouteRegister(),
|
|
nil, nil, nil,
|
|
store,
|
|
nil,
|
|
&usagestats.UsageStatsMock{T: t},
|
|
nil,
|
|
features, accesscontrolmock.New(), &dashboards.FakeDashboardService{}, annotationstest.NewFakeAnnotationsRepo(), nil)
|
|
require.NoError(t, err)
|
|
return gLive
|
|
}
|
|
|
|
// This tests three main scenarios.
|
|
// If a user has access to execute an action on a dashboard:
|
|
// 1. and the dashboard is in a folder which does not have an acl
|
|
// 2. and the dashboard is in a folder which does have an acl
|
|
// 3. Post dashboard response tests
|
|
|
|
func TestDashboardAPIEndpoint(t *testing.T) {
|
|
t.Run("Given a dashboard with a parent folder which does not have an ACL", func(t *testing.T) {
|
|
fakeDash := models.NewDashboard("Child dash")
|
|
fakeDash.Id = 1
|
|
fakeDash.FolderId = 1
|
|
fakeDash.HasACL = false
|
|
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
|
fakeDashboardVersionService.ExpectedDashboardVersion = &dashver.DashboardVersionDTO{}
|
|
teamService := &teamtest.FakeService{}
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardQuery)
|
|
q.Result = fakeDash
|
|
}).Return(nil)
|
|
mockSQLStore := mockstore.NewSQLStoreMock()
|
|
|
|
hs := &HTTPServer{
|
|
Cfg: setting.NewCfg(),
|
|
pluginStore: &plugins.FakePluginStore{},
|
|
SQLStore: mockSQLStore,
|
|
AccessControl: accesscontrolmock.New(),
|
|
Features: featuremgmt.WithFeatures(),
|
|
DashboardService: dashboardService,
|
|
dashboardVersionService: fakeDashboardVersionService,
|
|
Kinds: corekind.NewBase(nil),
|
|
QuotaService: quotatest.New(false, nil),
|
|
}
|
|
|
|
setUp := func() {
|
|
viewerRole := org.RoleViewer
|
|
editorRole := org.RoleEditor
|
|
dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
|
q.Result = []*models.DashboardACLInfoDTO{
|
|
{Role: &viewerRole, Permission: models.PERMISSION_VIEW},
|
|
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
|
|
}
|
|
}).Return(nil)
|
|
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
|
}
|
|
|
|
// This tests two scenarios:
|
|
// 1. user is an org viewer
|
|
// 2. user is an org editor
|
|
|
|
t.Run("When user is an Org Viewer", func(t *testing.T) {
|
|
role := org.RoleViewer
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
|
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
sc.sqlStore = mockSQLStore
|
|
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService)
|
|
|
|
assert.False(t, dash.Meta.CanEdit)
|
|
assert.False(t, dash.Meta.CanSave)
|
|
assert.False(t, dash.Meta.CanAdmin)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1",
|
|
"/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
sc.sqlStore = mockSQLStore
|
|
|
|
hs.callGetDashboardVersion(sc)
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions",
|
|
"/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
sc.sqlStore = mockSQLStore
|
|
|
|
hs.callGetDashboardVersions(sc)
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
|
|
t.Run("When user is an Org Editor", func(t *testing.T) {
|
|
role := org.RoleEditor
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
|
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
sc.sqlStore = mockSQLStore
|
|
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService)
|
|
|
|
assert.True(t, dash.Meta.CanEdit)
|
|
assert.True(t, dash.Meta.CanSave)
|
|
assert.False(t, dash.Meta.CanAdmin)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1",
|
|
"/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
sc.sqlStore = mockSQLStore
|
|
hs.callGetDashboardVersion(sc)
|
|
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions",
|
|
"/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
hs.callGetDashboardVersions(sc)
|
|
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
})
|
|
|
|
t.Run("Given a dashboard with a parent folder which has an ACL", func(t *testing.T) {
|
|
fakeDash := models.NewDashboard("Child dash")
|
|
fakeDash.Id = 1
|
|
fakeDash.FolderId = 1
|
|
fakeDash.HasACL = true
|
|
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
|
fakeDashboardVersionService.ExpectedDashboardVersion = &dashver.DashboardVersionDTO{}
|
|
teamService := &teamtest.FakeService{}
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardQuery)
|
|
q.Result = fakeDash
|
|
}).Return(nil)
|
|
dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
|
q.Result = []*models.DashboardACLInfoDTO{
|
|
{
|
|
DashboardId: 1,
|
|
Permission: models.PERMISSION_EDIT,
|
|
UserId: 200,
|
|
},
|
|
}
|
|
}).Return(nil)
|
|
|
|
mockSQLStore := mockstore.NewSQLStoreMock()
|
|
cfg := setting.NewCfg()
|
|
sql := db.InitTestDB(t)
|
|
|
|
hs := &HTTPServer{
|
|
Cfg: cfg,
|
|
Live: newTestLive(t, sql),
|
|
LibraryPanelService: &mockLibraryPanelService{},
|
|
LibraryElementService: &mockLibraryElementService{},
|
|
SQLStore: mockSQLStore,
|
|
AccessControl: accesscontrolmock.New(),
|
|
DashboardService: dashboardService,
|
|
dashboardVersionService: fakeDashboardVersionService,
|
|
Features: featuremgmt.WithFeatures(),
|
|
Kinds: corekind.NewBase(nil),
|
|
}
|
|
|
|
setUp := func() {
|
|
origCanEdit := setting.ViewersCanEdit
|
|
t.Cleanup(func() {
|
|
setting.ViewersCanEdit = origCanEdit
|
|
})
|
|
setting.ViewersCanEdit = false
|
|
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
|
}
|
|
|
|
// This tests six scenarios:
|
|
// 1. user is an org viewer AND has no permissions for this dashboard
|
|
// 2. user is an org editor AND has no permissions for this dashboard
|
|
// 3. user is an org viewer AND has been granted edit permission for the dashboard
|
|
// 4. user is an org viewer AND all viewers have edit permission for this dashboard
|
|
// 5. user is an org viewer AND has been granted an admin permission
|
|
// 6. user is an org editor AND has been granted a view permission
|
|
|
|
t.Run("When user is an Org Viewer and has no permissions for this dashboard", func(t *testing.T) {
|
|
role := org.RoleViewer
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
|
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
sc.sqlStore = mockSQLStore
|
|
sc.handlerFunc = hs.GetDashboard
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi",
|
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
sc.sqlStore = mockSQLStore
|
|
hs.callDeleteDashboardByUID(t, sc, dashboardService)
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1",
|
|
"/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
sc.sqlStore = mockSQLStore
|
|
hs.callGetDashboardVersion(sc)
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions",
|
|
"/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
hs.callGetDashboardVersions(sc)
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
|
|
t.Run("When user is an Org Editor and has no permissions for this dashboard", func(t *testing.T) {
|
|
role := org.RoleEditor
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
|
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
sc.sqlStore = mockSQLStore
|
|
sc.handlerFunc = hs.GetDashboard
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi",
|
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
hs.callDeleteDashboardByUID(t, sc, dashboardService)
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1",
|
|
"/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
hs.callGetDashboardVersion(sc)
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions",
|
|
"/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
|
|
setUp()
|
|
hs.callGetDashboardVersions(sc)
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
|
|
t.Run("When user is an Org Viewer but has an edit permission", func(t *testing.T) {
|
|
role := org.RoleViewer
|
|
|
|
setUpInner := func() {
|
|
origCanEdit := setting.ViewersCanEdit
|
|
t.Cleanup(func() {
|
|
setting.ViewersCanEdit = origCanEdit
|
|
})
|
|
setting.ViewersCanEdit = false
|
|
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
|
q.Result = []*models.DashboardACLInfoDTO{
|
|
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_EDIT},
|
|
}
|
|
}).Return(nil)
|
|
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
|
}
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
|
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
sc.sqlStore = mockSQLStore
|
|
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService)
|
|
|
|
assert.True(t, dash.Meta.CanEdit)
|
|
assert.True(t, dash.Meta.CanSave)
|
|
assert.False(t, dash.Meta.CanAdmin)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardQuery)
|
|
q.Result = models.NewDashboard("test")
|
|
}).Return(nil)
|
|
dashboardService.On("DeleteDashboard", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(nil)
|
|
|
|
hs.callDeleteDashboardByUID(t, sc, dashboardService)
|
|
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
sc.sqlStore = mockSQLStore
|
|
sc.dashboardVersionService = fakeDashboardVersionService
|
|
hs.callGetDashboardVersion(sc)
|
|
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
hs.callGetDashboardVersions(sc)
|
|
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
|
|
t.Run("When user is an Org Viewer and viewers can edit", func(t *testing.T) {
|
|
role := org.RoleViewer
|
|
|
|
setUpInner := func() {
|
|
origCanEdit := setting.ViewersCanEdit
|
|
t.Cleanup(func() {
|
|
setting.ViewersCanEdit = origCanEdit
|
|
})
|
|
setting.ViewersCanEdit = true
|
|
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
|
q.Result = []*models.DashboardACLInfoDTO{
|
|
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_VIEW},
|
|
}
|
|
}).Return(nil)
|
|
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
|
}
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
|
|
require.True(t, setting.ViewersCanEdit)
|
|
sc.sqlStore = mockSQLStore
|
|
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService)
|
|
|
|
assert.True(t, dash.Meta.CanEdit)
|
|
assert.False(t, dash.Meta.CanSave)
|
|
assert.False(t, dash.Meta.CanAdmin)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
|
|
hs.callDeleteDashboardByUID(t, sc, dashboardService)
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
|
|
t.Run("When user is an Org Viewer but has an admin permission", func(t *testing.T) {
|
|
role := org.RoleViewer
|
|
|
|
setUpInner := func() {
|
|
origCanEdit := setting.ViewersCanEdit
|
|
t.Cleanup(func() {
|
|
setting.ViewersCanEdit = origCanEdit
|
|
})
|
|
setting.ViewersCanEdit = true
|
|
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
|
q.Result = []*models.DashboardACLInfoDTO{
|
|
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_ADMIN},
|
|
}
|
|
}).Return(nil)
|
|
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
|
}
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
sc.sqlStore = mockSQLStore
|
|
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService)
|
|
|
|
assert.True(t, dash.Meta.CanEdit)
|
|
assert.True(t, dash.Meta.CanSave)
|
|
assert.True(t, dash.Meta.CanAdmin)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
sc.sqlStore = mockSQLStore
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardQuery)
|
|
q.Result = models.NewDashboard("test")
|
|
}).Return(nil)
|
|
dashboardService.On("DeleteDashboard", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(nil)
|
|
hs.callDeleteDashboardByUID(t, sc, dashboardService)
|
|
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
|
|
hs.callGetDashboardVersion(sc)
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
|
|
hs.callGetDashboardVersions(sc)
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
|
|
t.Run("When user is an Org Editor but has a view permission", func(t *testing.T) {
|
|
role := org.RoleEditor
|
|
|
|
setUpInner := func() {
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
|
q.Result = []*models.DashboardACLInfoDTO{
|
|
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_VIEW},
|
|
}
|
|
}).Return(nil)
|
|
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
|
}
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
sc.sqlStore = mockSQLStore
|
|
dash := getDashboardShouldReturn200WithConfig(t, sc, nil, nil, dashboardService)
|
|
|
|
assert.False(t, dash.Meta.CanEdit)
|
|
assert.False(t, dash.Meta.CanSave)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
hs.callDeleteDashboardByUID(t, sc, dashboardService)
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
hs.callGetDashboardVersion(sc)
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
|
|
setUpInner()
|
|
hs.callGetDashboardVersions(sc)
|
|
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
})
|
|
|
|
t.Run("Given two dashboards with the same title in different folders", func(t *testing.T) {
|
|
dashOne := models.NewDashboard("dash")
|
|
dashOne.Id = 2
|
|
dashOne.FolderId = 1
|
|
dashOne.HasACL = false
|
|
|
|
dashTwo := models.NewDashboard("dash")
|
|
dashTwo.Id = 4
|
|
dashTwo.FolderId = 3
|
|
dashTwo.HasACL = false
|
|
})
|
|
|
|
t.Run("Post dashboard response tests", func(t *testing.T) {
|
|
dashboardStore := &dashboards.FakeDashboardStore{}
|
|
defer dashboardStore.AssertExpectations(t)
|
|
// This tests that a valid request returns correct response
|
|
t.Run("Given a correct request for creating a dashboard", func(t *testing.T) {
|
|
const folderID int64 = 3
|
|
const dashID int64 = 2
|
|
|
|
cmd := models.SaveDashboardCommand{
|
|
OrgId: 1,
|
|
UserId: 5,
|
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
|
"title": "Dash",
|
|
}),
|
|
Overwrite: true,
|
|
FolderId: folderID,
|
|
IsFolder: false,
|
|
Message: "msg",
|
|
}
|
|
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")).
|
|
Return(&models.Dashboard{Id: dashID, Uid: "uid", Title: "Dash", Slug: "dash", Version: 2}, nil)
|
|
|
|
postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, nil, func(sc *scenarioContext) {
|
|
callPostDashboardShouldReturnSuccess(sc)
|
|
|
|
result := sc.ToJSON()
|
|
assert.Equal(t, "success", result.Get("status").MustString())
|
|
assert.Equal(t, dashID, result.Get("id").MustInt64())
|
|
assert.Equal(t, "uid", result.Get("uid").MustString())
|
|
assert.Equal(t, "dash", result.Get("slug").MustString())
|
|
assert.Equal(t, "/d/uid/dash", result.Get("url").MustString())
|
|
})
|
|
})
|
|
|
|
t.Run("Given a correct request for creating a dashboard with folder uid", func(t *testing.T) {
|
|
const folderUid string = "folderUID"
|
|
const dashID int64 = 2
|
|
|
|
cmd := models.SaveDashboardCommand{
|
|
OrgId: 1,
|
|
UserId: 5,
|
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
|
"title": "Dash",
|
|
}),
|
|
Overwrite: true,
|
|
FolderUid: folderUid,
|
|
IsFolder: false,
|
|
Message: "msg",
|
|
}
|
|
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")).
|
|
Return(&models.Dashboard{Id: dashID, Uid: "uid", Title: "Dash", Slug: "dash", Version: 2}, nil)
|
|
|
|
mockFolder := &foldertest.FakeService{
|
|
ExpectedFolder: &folder.Folder{ID: 1, UID: "folderUID", Title: "Folder"},
|
|
}
|
|
|
|
postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, mockFolder, func(sc *scenarioContext) {
|
|
callPostDashboardShouldReturnSuccess(sc)
|
|
|
|
result := sc.ToJSON()
|
|
assert.Equal(t, "success", result.Get("status").MustString())
|
|
assert.Equal(t, dashID, result.Get("id").MustInt64())
|
|
assert.Equal(t, "uid", result.Get("uid").MustString())
|
|
assert.Equal(t, "dash", result.Get("slug").MustString())
|
|
assert.Equal(t, "/d/uid/dash", result.Get("url").MustString())
|
|
})
|
|
})
|
|
|
|
t.Run("Given a request with incorrect folder uid for creating a dashboard with", func(t *testing.T) {
|
|
cmd := models.SaveDashboardCommand{
|
|
OrgId: 1,
|
|
UserId: 5,
|
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
|
"title": "Dash",
|
|
}),
|
|
Overwrite: true,
|
|
FolderUid: "folderUID",
|
|
IsFolder: false,
|
|
Message: "msg",
|
|
}
|
|
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
|
|
mockFolder := &foldertest.FakeService{
|
|
ExpectedError: errors.New("Error while searching Folder ID"),
|
|
}
|
|
|
|
postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, mockFolder, func(sc *scenarioContext) {
|
|
callPostDashboard(sc)
|
|
assert.Equal(t, 500, sc.resp.Code)
|
|
})
|
|
})
|
|
|
|
// This tests that invalid requests returns expected error responses
|
|
t.Run("Given incorrect requests for creating a dashboard", func(t *testing.T) {
|
|
testCases := []struct {
|
|
SaveError error
|
|
ExpectedStatusCode int
|
|
}{
|
|
{SaveError: dashboards.ErrDashboardNotFound, ExpectedStatusCode: 404},
|
|
{SaveError: dashboards.ErrFolderNotFound, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.ErrDashboardWithSameUIDExists, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.ErrDashboardWithSameNameInFolderExists, ExpectedStatusCode: 412},
|
|
{SaveError: dashboards.ErrDashboardVersionMismatch, ExpectedStatusCode: 412},
|
|
{SaveError: dashboards.ErrDashboardTitleEmpty, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.ErrDashboardFolderCannotHaveParent, ExpectedStatusCode: 400},
|
|
{SaveError: alerting.ValidationError{Reason: "Mu"}, ExpectedStatusCode: 422},
|
|
{SaveError: dashboards.ErrDashboardFailedGenerateUniqueUid, ExpectedStatusCode: 500},
|
|
{SaveError: dashboards.ErrDashboardTypeMismatch, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.ErrDashboardFolderWithSameNameAsDashboard, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.ErrDashboardWithSameNameAsFolder, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.ErrDashboardFolderNameExists, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403},
|
|
{SaveError: dashboards.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.ErrDashboardCannotSaveProvisionedDashboard, ExpectedStatusCode: 400},
|
|
{SaveError: dashboards.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412},
|
|
}
|
|
|
|
cmd := models.SaveDashboardCommand{
|
|
OrgId: 1,
|
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
|
"title": "",
|
|
}),
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")).Return(nil, tc.SaveError)
|
|
|
|
postDashboardScenario(t, fmt.Sprintf("Expect '%s' error when calling POST on", tc.SaveError.Error()),
|
|
"/api/dashboards", "/api/dashboards", cmd, dashboardService, nil, func(sc *scenarioContext) {
|
|
callPostDashboard(sc)
|
|
assert.Equal(t, tc.ExpectedStatusCode, sc.resp.Code)
|
|
})
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("Given a dashboard to validate", func(t *testing.T) {
|
|
sqlmock := mockstore.SQLStoreMock{}
|
|
|
|
t.Run("When an invalid dashboard json is posted", func(t *testing.T) {
|
|
cmd := models.ValidateDashboardCommand{
|
|
Dashboard: "{\"hello\": \"world\"}",
|
|
}
|
|
|
|
role := org.RoleAdmin
|
|
postValidateScenario(t, "When calling POST on", "/api/dashboards/validate", "/api/dashboards/validate", cmd, role, func(sc *scenarioContext) {
|
|
callPostDashboard(sc)
|
|
|
|
result := sc.ToJSON()
|
|
assert.Equal(t, 422, sc.resp.Code)
|
|
assert.False(t, result.Get("isValid").MustBool())
|
|
assert.NotEmpty(t, result.Get("message").MustString())
|
|
}, &sqlmock)
|
|
})
|
|
|
|
t.Run("When a dashboard with a too-low schema version is posted", func(t *testing.T) {
|
|
cmd := models.ValidateDashboardCommand{
|
|
Dashboard: "{\"schemaVersion\": 1}",
|
|
}
|
|
|
|
role := org.RoleAdmin
|
|
postValidateScenario(t, "When calling POST on", "/api/dashboards/validate", "/api/dashboards/validate", cmd, role, func(sc *scenarioContext) {
|
|
callPostDashboard(sc)
|
|
|
|
result := sc.ToJSON()
|
|
assert.Equal(t, 412, sc.resp.Code)
|
|
assert.False(t, result.Get("isValid").MustBool())
|
|
assert.Equal(t, "invalid schema version", result.Get("message").MustString())
|
|
}, &sqlmock)
|
|
})
|
|
|
|
t.Run("When a valid dashboard is posted", func(t *testing.T) {
|
|
devenvDashboard, readErr := os.ReadFile("../../devenv/dev-dashboards/home.json")
|
|
assert.Empty(t, readErr)
|
|
|
|
cmd := models.ValidateDashboardCommand{
|
|
Dashboard: string(devenvDashboard),
|
|
}
|
|
|
|
role := org.RoleAdmin
|
|
postValidateScenario(t, "When calling POST on", "/api/dashboards/validate", "/api/dashboards/validate", cmd, role, func(sc *scenarioContext) {
|
|
callPostDashboard(sc)
|
|
|
|
result := sc.ToJSON()
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
assert.True(t, result.Get("isValid").MustBool())
|
|
}, &sqlmock)
|
|
})
|
|
})
|
|
|
|
t.Run("Given two dashboards being compared", func(t *testing.T) {
|
|
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
|
fakeDashboardVersionService.ExpectedDashboardVersions = []*dashver.DashboardVersionDTO{
|
|
{
|
|
DashboardID: 1,
|
|
Version: 1,
|
|
Data: simplejson.NewFromAny(map[string]interface{}{
|
|
"title": "Dash1",
|
|
}),
|
|
},
|
|
{
|
|
DashboardID: 2,
|
|
Version: 2,
|
|
Data: simplejson.NewFromAny(map[string]interface{}{
|
|
"title": "Dash2",
|
|
}),
|
|
},
|
|
}
|
|
sqlmock := mockstore.SQLStoreMock{}
|
|
setUp := func() {
|
|
teamSvc := &teamtest.FakeService{}
|
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Return(nil)
|
|
dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardQuery)
|
|
q.Result = &models.Dashboard{
|
|
OrgId: q.OrgId,
|
|
Id: q.Id,
|
|
}
|
|
}).Return(nil)
|
|
guardian.InitLegacyGuardian(&sqlmock, dashSvc, teamSvc)
|
|
}
|
|
|
|
cmd := dtos.CalculateDiffOptions{
|
|
Base: dtos.CalculateDiffTarget{
|
|
DashboardId: 1,
|
|
Version: 1,
|
|
},
|
|
New: dtos.CalculateDiffTarget{
|
|
DashboardId: 2,
|
|
Version: 2,
|
|
},
|
|
DiffType: "basic",
|
|
}
|
|
|
|
t.Run("when user does not have permission", func(t *testing.T) {
|
|
role := org.RoleViewer
|
|
postDiffScenario(t, "When calling POST on", "/api/dashboards/calculate-diff", "/api/dashboards/calculate-diff", cmd, role, func(sc *scenarioContext) {
|
|
setUp()
|
|
|
|
callPostDashboard(sc)
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
}, &sqlmock, fakeDashboardVersionService)
|
|
})
|
|
|
|
t.Run("when user does have permission", func(t *testing.T) {
|
|
role := org.RoleAdmin
|
|
postDiffScenario(t, "When calling POST on", "/api/dashboards/calculate-diff", "/api/dashboards/calculate-diff", cmd, role, func(sc *scenarioContext) {
|
|
// This test shouldn't hit GetDashboardACLInfoList, so no setup needed
|
|
sc.dashboardVersionService = fakeDashboardVersionService
|
|
callPostDashboard(sc)
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, &sqlmock, fakeDashboardVersionService)
|
|
})
|
|
})
|
|
|
|
t.Run("Given dashboard in folder being restored should restore to folder", func(t *testing.T) {
|
|
const folderID int64 = 1
|
|
fakeDash := models.NewDashboard("Child dash")
|
|
fakeDash.Id = 2
|
|
fakeDash.FolderId = folderID
|
|
fakeDash.HasACL = false
|
|
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardQuery)
|
|
q.Result = fakeDash
|
|
}).Return(nil)
|
|
dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")).Run(func(args mock.Arguments) {
|
|
cmd := args.Get(1).(*dashboards.SaveDashboardDTO)
|
|
cmd.Dashboard = &models.Dashboard{
|
|
Id: 2, Uid: "uid", Title: "Dash", Slug: "dash", Version: 1,
|
|
}
|
|
}).Return(nil, nil)
|
|
|
|
cmd := dtos.RestoreDashboardVersionCommand{
|
|
Version: 1,
|
|
}
|
|
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
|
fakeDashboardVersionService.ExpectedDashboardVersions = []*dashver.DashboardVersionDTO{
|
|
{
|
|
DashboardID: 2,
|
|
Version: 1,
|
|
Data: fakeDash.Data,
|
|
}}
|
|
mockSQLStore := mockstore.NewSQLStoreMock()
|
|
restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore",
|
|
"/api/dashboards/id/:dashboardId/restore", dashboardService, fakeDashboardVersionService, cmd, func(sc *scenarioContext) {
|
|
sc.dashboardVersionService = fakeDashboardVersionService
|
|
|
|
callRestoreDashboardVersion(sc)
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
|
|
t.Run("Given dashboard in general folder being restored should restore to general folder", func(t *testing.T) {
|
|
fakeDash := models.NewDashboard("Child dash")
|
|
fakeDash.Id = 2
|
|
fakeDash.HasACL = false
|
|
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardQuery)
|
|
q.Result = fakeDash
|
|
}).Return(nil)
|
|
dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")).Run(func(args mock.Arguments) {
|
|
cmd := args.Get(1).(*dashboards.SaveDashboardDTO)
|
|
cmd.Dashboard = &models.Dashboard{
|
|
Id: 2, Uid: "uid", Title: "Dash", Slug: "dash", Version: 1,
|
|
}
|
|
}).Return(nil, nil)
|
|
|
|
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
|
fakeDashboardVersionService.ExpectedDashboardVersions = []*dashver.DashboardVersionDTO{
|
|
{
|
|
DashboardID: 2,
|
|
Version: 1,
|
|
Data: fakeDash.Data,
|
|
}}
|
|
|
|
cmd := dtos.RestoreDashboardVersionCommand{
|
|
Version: 1,
|
|
}
|
|
mockSQLStore := mockstore.NewSQLStoreMock()
|
|
restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore",
|
|
"/api/dashboards/id/:dashboardId/restore", dashboardService, fakeDashboardVersionService, cmd, func(sc *scenarioContext) {
|
|
callRestoreDashboardVersion(sc)
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
}, mockSQLStore)
|
|
})
|
|
|
|
t.Run("Given provisioned dashboard", func(t *testing.T) {
|
|
mockSQLStore := mockstore.NewSQLStoreMock()
|
|
dashboardStore := dashboards.NewFakeDashboardStore(t)
|
|
dashboardStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&models.DashboardProvisioning{ExternalId: "/dashboard1.json"}, nil).Once()
|
|
|
|
teamService := &teamtest.FakeService{}
|
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
|
|
|
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`))
|
|
require.NoError(t, err)
|
|
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardQuery)
|
|
q.Result = &models.Dashboard{Id: 1, Data: dataValue}
|
|
}).Return(nil)
|
|
dashboardService.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
|
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
|
q.Result = []*models.DashboardACLInfoDTO{{OrgId: testOrgID, DashboardId: 1, UserId: testUserID, Permission: models.PERMISSION_EDIT}}
|
|
}).Return(nil)
|
|
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
|
|
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", org.RoleEditor, func(sc *scenarioContext) {
|
|
fakeProvisioningService := provisioning.NewProvisioningServiceMock(context.Background())
|
|
fakeProvisioningService.GetDashboardProvisionerResolvedPathFunc = func(name string) string {
|
|
return "/tmp/grafana/dashboards"
|
|
}
|
|
|
|
dash := getDashboardShouldReturn200WithConfig(t, sc, fakeProvisioningService, dashboardStore, dashboardService)
|
|
|
|
assert.Equal(t, "../../../dashboard1.json", dash.Meta.ProvisionedExternalId, mockSQLStore)
|
|
}, mockSQLStore)
|
|
|
|
loggedInUserScenarioWithRole(t, "When allowUiUpdates is true and calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", org.RoleEditor, func(sc *scenarioContext) {
|
|
fakeProvisioningService := provisioning.NewProvisioningServiceMock(context.Background())
|
|
fakeProvisioningService.GetDashboardProvisionerResolvedPathFunc = func(name string) string {
|
|
return "/tmp/grafana/dashboards"
|
|
}
|
|
|
|
fakeProvisioningService.GetAllowUIUpdatesFromConfigFunc = func(name string) bool {
|
|
return true
|
|
}
|
|
|
|
hs := &HTTPServer{
|
|
Cfg: setting.NewCfg(),
|
|
ProvisioningService: fakeProvisioningService,
|
|
LibraryPanelService: &mockLibraryPanelService{},
|
|
LibraryElementService: &mockLibraryElementService{},
|
|
dashboardProvisioningService: mockDashboardProvisioningService{},
|
|
SQLStore: mockSQLStore,
|
|
AccessControl: accesscontrolmock.New(),
|
|
DashboardService: dashboardService,
|
|
Features: featuremgmt.WithFeatures(),
|
|
Kinds: corekind.NewBase(nil),
|
|
}
|
|
hs.callGetDashboard(sc)
|
|
|
|
assert.Equal(t, 200, sc.resp.Code)
|
|
|
|
dash := dtos.DashboardFullWithMeta{}
|
|
err := json.NewDecoder(sc.resp.Body).Decode(&dash)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, false, dash.Meta.Provisioned)
|
|
}, mockSQLStore)
|
|
})
|
|
}
|
|
|
|
func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, provisioningService provisioning.ProvisioningService, dashboardStore dashboards.Store, dashboardService dashboards.DashboardService) dtos.DashboardFullWithMeta {
|
|
t.Helper()
|
|
|
|
if provisioningService == nil {
|
|
provisioningService = provisioning.NewProvisioningServiceMock(context.Background())
|
|
}
|
|
|
|
var err error
|
|
if dashboardStore == nil {
|
|
sql := db.InitTestDB(t)
|
|
quotaService := quotatest.New(false, nil)
|
|
dashboardStore, err = database.ProvideDashboardStore(sql, sql.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql, sql.Cfg), quotaService)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
libraryPanelsService := mockLibraryPanelService{}
|
|
libraryElementsService := mockLibraryElementService{}
|
|
cfg := setting.NewCfg()
|
|
ac := accesscontrolmock.New()
|
|
folderPermissions := accesscontrolmock.NewMockedPermissionsService()
|
|
dashboardPermissions := accesscontrolmock.NewMockedPermissionsService()
|
|
features := featuremgmt.WithFeatures()
|
|
|
|
if dashboardService == nil {
|
|
dashboardService = service.ProvideDashboardService(
|
|
cfg, dashboardStore, nil, features,
|
|
folderPermissions, dashboardPermissions, ac,
|
|
)
|
|
}
|
|
|
|
hs := &HTTPServer{
|
|
Cfg: cfg,
|
|
LibraryPanelService: &libraryPanelsService,
|
|
LibraryElementService: &libraryElementsService,
|
|
SQLStore: sc.sqlStore,
|
|
ProvisioningService: provisioningService,
|
|
AccessControl: accesscontrolmock.New(),
|
|
dashboardProvisioningService: service.ProvideDashboardService(
|
|
cfg, dashboardStore, nil, features,
|
|
folderPermissions, dashboardPermissions, ac,
|
|
),
|
|
DashboardService: dashboardService,
|
|
Features: featuremgmt.WithFeatures(),
|
|
Kinds: corekind.NewBase(nil),
|
|
}
|
|
|
|
hs.callGetDashboard(sc)
|
|
|
|
require.Equal(sc.t, 200, sc.resp.Code)
|
|
|
|
dash := dtos.DashboardFullWithMeta{}
|
|
err = json.NewDecoder(sc.resp.Body).Decode(&dash)
|
|
require.NoError(sc.t, err)
|
|
|
|
return dash
|
|
}
|
|
|
|
func (hs *HTTPServer) callGetDashboard(sc *scenarioContext) {
|
|
sc.handlerFunc = hs.GetDashboard
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
}
|
|
|
|
func (hs *HTTPServer) callGetDashboardVersion(sc *scenarioContext) {
|
|
sc.handlerFunc = hs.GetDashboardVersion
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
}
|
|
|
|
func (hs *HTTPServer) callGetDashboardVersions(sc *scenarioContext) {
|
|
sc.handlerFunc = hs.GetDashboardVersions
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
}
|
|
|
|
func (hs *HTTPServer) callDeleteDashboardByUID(t *testing.T,
|
|
sc *scenarioContext, mockDashboard *dashboards.FakeDashboardService) {
|
|
hs.DashboardService = mockDashboard
|
|
sc.handlerFunc = hs.DeleteDashboardByUID
|
|
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
|
}
|
|
|
|
func callPostDashboard(sc *scenarioContext) {
|
|
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
|
}
|
|
|
|
func callRestoreDashboardVersion(sc *scenarioContext) {
|
|
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
|
}
|
|
|
|
func callPostDashboardShouldReturnSuccess(sc *scenarioContext) {
|
|
callPostDashboard(sc)
|
|
|
|
assert.Equal(sc.t, 200, sc.resp.Code)
|
|
}
|
|
|
|
func postDashboardScenario(t *testing.T, desc string, url string, routePattern string, cmd models.SaveDashboardCommand, dashboardService dashboards.DashboardService, folderService folder.Service, fn scenarioFunc) {
|
|
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
|
cfg := setting.NewCfg()
|
|
hs := HTTPServer{
|
|
Cfg: cfg,
|
|
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
|
|
Live: newTestLive(t, db.InitTestDB(t)),
|
|
QuotaService: quotatest.New(false, nil),
|
|
pluginStore: &plugins.FakePluginStore{},
|
|
LibraryPanelService: &mockLibraryPanelService{},
|
|
LibraryElementService: &mockLibraryElementService{},
|
|
DashboardService: dashboardService,
|
|
folderService: folderService,
|
|
Features: featuremgmt.WithFeatures(),
|
|
Kinds: corekind.NewBase(nil),
|
|
accesscontrolService: actest.FakeService{},
|
|
}
|
|
|
|
sc := setupScenarioContext(t, url)
|
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
|
c.Req.Body = mockRequestBody(cmd)
|
|
c.Req.Header.Add("Content-Type", "application/json")
|
|
sc.context = c
|
|
sc.context.SignedInUser = &user.SignedInUser{OrgID: cmd.OrgId, UserID: cmd.UserId}
|
|
|
|
return hs.PostDashboard(c)
|
|
})
|
|
|
|
sc.m.Post(routePattern, sc.defaultHandler)
|
|
|
|
fn(sc)
|
|
})
|
|
}
|
|
|
|
func postValidateScenario(t *testing.T, desc string, url string, routePattern string, cmd models.ValidateDashboardCommand,
|
|
role org.RoleType, fn scenarioFunc, sqlmock db.DB) {
|
|
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
|
cfg := setting.NewCfg()
|
|
hs := HTTPServer{
|
|
Cfg: cfg,
|
|
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
|
|
Live: newTestLive(t, db.InitTestDB(t)),
|
|
QuotaService: quotatest.New(false, nil),
|
|
LibraryPanelService: &mockLibraryPanelService{},
|
|
LibraryElementService: &mockLibraryElementService{},
|
|
SQLStore: sqlmock,
|
|
Features: featuremgmt.WithFeatures(),
|
|
Kinds: corekind.NewBase(nil),
|
|
}
|
|
|
|
sc := setupScenarioContext(t, url)
|
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
|
c.Req.Body = mockRequestBody(cmd)
|
|
c.Req.Header.Add("Content-Type", "application/json")
|
|
sc.context = c
|
|
sc.context.SignedInUser = &user.SignedInUser{
|
|
OrgID: testOrgID,
|
|
UserID: testUserID,
|
|
}
|
|
sc.context.OrgRole = role
|
|
|
|
return hs.ValidateDashboard(c)
|
|
})
|
|
|
|
sc.m.Post(routePattern, sc.defaultHandler)
|
|
|
|
fn(sc)
|
|
})
|
|
}
|
|
|
|
func postDiffScenario(t *testing.T, desc string, url string, routePattern string, cmd dtos.CalculateDiffOptions,
|
|
role org.RoleType, fn scenarioFunc, sqlmock db.DB, fakeDashboardVersionService *dashvertest.FakeDashboardVersionService) {
|
|
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
|
cfg := setting.NewCfg()
|
|
|
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
hs := HTTPServer{
|
|
Cfg: cfg,
|
|
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
|
|
Live: newTestLive(t, db.InitTestDB(t)),
|
|
QuotaService: quotatest.New(false, nil),
|
|
LibraryPanelService: &mockLibraryPanelService{},
|
|
LibraryElementService: &mockLibraryElementService{},
|
|
SQLStore: sqlmock,
|
|
dashboardVersionService: fakeDashboardVersionService,
|
|
Features: featuremgmt.WithFeatures(),
|
|
Kinds: corekind.NewBase(nil),
|
|
DashboardService: dashSvc,
|
|
}
|
|
|
|
sc := setupScenarioContext(t, url)
|
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
|
c.Req.Body = mockRequestBody(cmd)
|
|
c.Req.Header.Add("Content-Type", "application/json")
|
|
sc.context = c
|
|
sc.context.SignedInUser = &user.SignedInUser{
|
|
OrgID: testOrgID,
|
|
UserID: testUserID,
|
|
}
|
|
sc.context.OrgRole = role
|
|
|
|
return hs.CalculateDashboardDiff(c)
|
|
})
|
|
|
|
sc.m.Post(routePattern, sc.defaultHandler)
|
|
|
|
fn(sc)
|
|
})
|
|
}
|
|
|
|
func restoreDashboardVersionScenario(t *testing.T, desc string, url string, routePattern string,
|
|
mock *dashboards.FakeDashboardService, fakeDashboardVersionService *dashvertest.FakeDashboardVersionService,
|
|
cmd dtos.RestoreDashboardVersionCommand, fn scenarioFunc, sqlStore db.DB) {
|
|
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
|
cfg := setting.NewCfg()
|
|
hs := HTTPServer{
|
|
Cfg: cfg,
|
|
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
|
|
Live: newTestLive(t, db.InitTestDB(t)),
|
|
QuotaService: quotatest.New(false, nil),
|
|
LibraryPanelService: &mockLibraryPanelService{},
|
|
LibraryElementService: &mockLibraryElementService{},
|
|
DashboardService: mock,
|
|
SQLStore: sqlStore,
|
|
Features: featuremgmt.WithFeatures(),
|
|
dashboardVersionService: fakeDashboardVersionService,
|
|
Kinds: corekind.NewBase(nil),
|
|
accesscontrolService: actest.FakeService{},
|
|
}
|
|
|
|
sc := setupScenarioContext(t, url)
|
|
sc.sqlStore = sqlStore
|
|
sc.dashboardVersionService = fakeDashboardVersionService
|
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
|
c.Req.Body = mockRequestBody(cmd)
|
|
c.Req.Header.Add("Content-Type", "application/json")
|
|
sc.context = c
|
|
sc.context.SignedInUser = &user.SignedInUser{
|
|
OrgID: testOrgID,
|
|
UserID: testUserID,
|
|
}
|
|
sc.context.OrgRole = org.RoleAdmin
|
|
|
|
return hs.RestoreDashboardVersion(c)
|
|
})
|
|
|
|
sc.m.Post(routePattern, sc.defaultHandler)
|
|
|
|
fn(sc)
|
|
})
|
|
}
|
|
|
|
func (sc *scenarioContext) ToJSON() *simplejson.Json {
|
|
result := simplejson.New()
|
|
err := json.NewDecoder(sc.resp.Body).Decode(result)
|
|
require.NoError(sc.t, err)
|
|
return result
|
|
}
|
|
|
|
type mockDashboardProvisioningService struct {
|
|
dashboards.DashboardProvisioningService
|
|
}
|
|
|
|
func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboardID(ctx context.Context, dashboardID int64) (
|
|
*models.DashboardProvisioning, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
type mockLibraryPanelService struct {
|
|
}
|
|
|
|
func (m *mockLibraryPanelService) ConnectLibraryPanelsForDashboard(c context.Context, signedInUser *user.SignedInUser, dash *models.Dashboard) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockLibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser *user.SignedInUser, libraryPanels *simplejson.Json, panels []interface{}, folderID int64) error {
|
|
return nil
|
|
}
|
|
|
|
type mockLibraryElementService struct {
|
|
}
|
|
|
|
func (l *mockLibraryElementService) CreateElement(c context.Context, signedInUser *user.SignedInUser, cmd libraryelements.CreateLibraryElementCommand) (libraryelements.LibraryElementDTO, error) {
|
|
return libraryelements.LibraryElementDTO{}, nil
|
|
}
|
|
|
|
// GetElement gets an element from a UID.
|
|
func (l *mockLibraryElementService) GetElement(c context.Context, signedInUser *user.SignedInUser, UID string) (libraryelements.LibraryElementDTO, error) {
|
|
return libraryelements.LibraryElementDTO{}, nil
|
|
}
|
|
|
|
// GetElementsForDashboard gets all connected elements for a specific dashboard.
|
|
func (l *mockLibraryElementService) GetElementsForDashboard(c context.Context, dashboardID int64) (map[string]libraryelements.LibraryElementDTO, error) {
|
|
return map[string]libraryelements.LibraryElementDTO{}, nil
|
|
}
|
|
|
|
// ConnectElementsToDashboard connects elements to a specific dashboard.
|
|
func (l *mockLibraryElementService) ConnectElementsToDashboard(c context.Context, signedInUser *user.SignedInUser, 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 *user.SignedInUser, folderUID string) error {
|
|
return nil
|
|
}
|