mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
chore/backend: move dashboard errors to dashboard service (#51593)
* chore/backend: move dashboard errors to dashboard service Dashboard-related models are slowly moving out of the models package and into dashboard services. This commit moves dashboard-related errors; the rest will come in later commits. There are no logical code changes, this is only a structural (package) move. * lint lint lint
This commit is contained in:
parent
a1fb73c503
commit
9de00c8eb2
@ -7,15 +7,15 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToDashboardErrorResponse returns a different response status according to the dashboard error type
|
// ToDashboardErrorResponse returns a different response status according to the dashboard error type
|
||||||
func ToDashboardErrorResponse(ctx context.Context, pluginStore plugins.Store, err error) response.Response {
|
func ToDashboardErrorResponse(ctx context.Context, pluginStore plugins.Store, err error) response.Response {
|
||||||
var dashboardErr models.DashboardErr
|
var dashboardErr dashboards.DashboardErr
|
||||||
if ok := errors.As(err, &dashboardErr); ok {
|
if ok := errors.As(err, &dashboardErr); ok {
|
||||||
if body := dashboardErr.Body(); body != nil {
|
if body := dashboardErr.Body(); body != nil {
|
||||||
return response.JSON(dashboardErr.StatusCode, body)
|
return response.JSON(dashboardErr.StatusCode, body)
|
||||||
@ -26,7 +26,7 @@ func ToDashboardErrorResponse(ctx context.Context, pluginStore plugins.Store, er
|
|||||||
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), nil)
|
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrFolderNotFound) {
|
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||||
return response.Error(http.StatusBadRequest, err.Error(), nil)
|
return response.Error(http.StatusBadRequest, err.Error(), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ func ToDashboardErrorResponse(ctx context.Context, pluginStore plugins.Store, er
|
|||||||
return response.Error(http.StatusUnprocessableEntity, validationErr.Error(), err)
|
return response.Error(http.StatusUnprocessableEntity, validationErr.Error(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pluginErr models.UpdatePluginDashboardError
|
var pluginErr dashboards.UpdatePluginDashboardError
|
||||||
if ok := errors.As(err, &pluginErr); ok {
|
if ok := errors.As(err, &pluginErr); ok {
|
||||||
message := fmt.Sprintf("The dashboard belongs to plugin %s.", pluginErr.PluginId)
|
message := fmt.Sprintf("The dashboard belongs to plugin %s.", pluginErr.PluginId)
|
||||||
// look up plugin name
|
// look up plugin name
|
||||||
|
@ -4,40 +4,40 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToFolderErrorResponse returns a different response status according to the folder error type
|
// ToFolderErrorResponse returns a different response status according to the folder error type
|
||||||
func ToFolderErrorResponse(err error) response.Response {
|
func ToFolderErrorResponse(err error) response.Response {
|
||||||
var dashboardErr models.DashboardErr
|
var dashboardErr dashboards.DashboardErr
|
||||||
if ok := errors.As(err, &dashboardErr); ok {
|
if ok := errors.As(err, &dashboardErr); ok {
|
||||||
return response.Error(dashboardErr.StatusCode, err.Error(), err)
|
return response.Error(dashboardErr.StatusCode, err.Error(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrFolderTitleEmpty) ||
|
if errors.Is(err, dashboards.ErrFolderTitleEmpty) ||
|
||||||
errors.Is(err, models.ErrDashboardTypeMismatch) ||
|
errors.Is(err, dashboards.ErrDashboardTypeMismatch) ||
|
||||||
errors.Is(err, models.ErrDashboardInvalidUid) ||
|
errors.Is(err, dashboards.ErrDashboardInvalidUid) ||
|
||||||
errors.Is(err, models.ErrDashboardUidTooLong) ||
|
errors.Is(err, dashboards.ErrDashboardUidTooLong) ||
|
||||||
errors.Is(err, models.ErrFolderContainsAlertRules) {
|
errors.Is(err, dashboards.ErrFolderContainsAlertRules) {
|
||||||
return response.Error(400, err.Error(), nil)
|
return response.Error(400, err.Error(), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrFolderAccessDenied) {
|
if errors.Is(err, dashboards.ErrFolderAccessDenied) {
|
||||||
return response.Error(403, "Access denied", err)
|
return response.Error(403, "Access denied", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrFolderNotFound) {
|
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||||
return response.JSON(404, util.DynMap{"status": "not-found", "message": models.ErrFolderNotFound.Error()})
|
return response.JSON(404, util.DynMap{"status": "not-found", "message": dashboards.ErrFolderNotFound.Error()})
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrFolderSameNameExists) ||
|
if errors.Is(err, dashboards.ErrFolderSameNameExists) ||
|
||||||
errors.Is(err, models.ErrFolderWithSameUIDExists) {
|
errors.Is(err, dashboards.ErrFolderWithSameUIDExists) {
|
||||||
return response.Error(409, err.Error(), nil)
|
return response.Error(409, err.Error(), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrFolderVersionMismatch) {
|
if errors.Is(err, dashboards.ErrFolderVersionMismatch) {
|
||||||
return response.JSON(412, util.DynMap{"status": "version-mismatch", "message": models.ErrFolderVersionMismatch.Error()})
|
return response.JSON(412, util.DynMap{"status": "version-mismatch", "message": dashboards.ErrFolderVersionMismatch.Error()})
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Error(500, "Folder API error", err)
|
return response.Error(500, "Folder API error", err)
|
||||||
|
@ -145,7 +145,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
|
|||||||
if dash.FolderId > 0 {
|
if dash.FolderId > 0 {
|
||||||
query := models.GetDashboardQuery{Id: dash.FolderId, OrgId: c.OrgId}
|
query := models.GetDashboardQuery{Id: dash.FolderId, OrgId: c.OrgId}
|
||||||
if err := hs.dashboardService.GetDashboard(c.Req.Context(), &query); err != nil {
|
if err := hs.dashboardService.GetDashboard(c.Req.Context(), &query); err != nil {
|
||||||
if errors.Is(err, models.ErrFolderNotFound) {
|
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||||
return response.Error(404, "Folder not found", err)
|
return response.Error(404, "Folder not found", err)
|
||||||
}
|
}
|
||||||
return response.Error(500, "Dashboard folder could not be read", err)
|
return response.Error(500, "Dashboard folder could not be read", err)
|
||||||
@ -264,9 +264,9 @@ func (hs *HTTPServer) deleteDashboard(c *models.ReqContext) response.Response {
|
|||||||
|
|
||||||
err = hs.dashboardService.DeleteDashboard(c.Req.Context(), dash.Id, c.OrgId)
|
err = hs.dashboardService.DeleteDashboard(c.Req.Context(), dash.Id, c.OrgId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var dashboardErr models.DashboardErr
|
var dashboardErr dashboards.DashboardErr
|
||||||
if ok := errors.As(err, &dashboardErr); ok {
|
if ok := errors.As(err, &dashboardErr); ok {
|
||||||
if errors.Is(err, models.ErrDashboardCannotDeleteProvisionedDashboard) {
|
if errors.Is(err, dashboards.ErrDashboardCannotDeleteProvisionedDashboard) {
|
||||||
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
|
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -333,7 +333,7 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
|||||||
if cmd.FolderUid != "" {
|
if cmd.FolderUid != "" {
|
||||||
folder, err := hs.folderService.GetFolderByUID(ctx, c.SignedInUser, c.OrgId, cmd.FolderUid)
|
folder, err := hs.folderService.GetFolderByUID(ctx, c.SignedInUser, c.OrgId, cmd.FolderUid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, models.ErrFolderNotFound) {
|
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||||
return response.Error(400, "Folder not found", err)
|
return response.Error(400, "Folder not found", err)
|
||||||
}
|
}
|
||||||
return response.Error(500, "Error while checking folder ID", err)
|
return response.Error(500, "Error while checking folder ID", err)
|
||||||
@ -362,7 +362,7 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
|||||||
provisioningData = data
|
provisioningData = data
|
||||||
} else if dash.Uid != "" {
|
} else if dash.Uid != "" {
|
||||||
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardUID(dash.OrgId, dash.Uid)
|
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardUID(dash.OrgId, dash.Uid)
|
||||||
if err != nil && !errors.Is(err, models.ErrProvisionedDashboardNotFound) && !errors.Is(err, models.ErrDashboardNotFound) {
|
if err != nil && !errors.Is(err, dashboards.ErrProvisionedDashboardNotFound) && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||||
return response.Error(500, "Error while checking if dashboard is provisioned", err)
|
return response.Error(500, "Error while checking if dashboard is provisioned", err)
|
||||||
}
|
}
|
||||||
provisioningData = data
|
provisioningData = data
|
||||||
|
@ -131,7 +131,7 @@ func (hs *HTTPServer) QueryPublicDashboard(c *models.ReqContext) response.Respon
|
|||||||
|
|
||||||
// util to help us unpack a dashboard err or use default http code and message
|
// util to help us unpack a dashboard err or use default http code and message
|
||||||
func handleDashboardErr(defaultCode int, defaultMsg string, err error) response.Response {
|
func handleDashboardErr(defaultCode int, defaultMsg string, err error) response.Response {
|
||||||
var dashboardErr models.DashboardErr
|
var dashboardErr dashboards.DashboardErr
|
||||||
|
|
||||||
if ok := errors.As(err, &dashboardErr); ok {
|
if ok := errors.As(err, &dashboardErr); ok {
|
||||||
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), dashboardErr)
|
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), dashboardErr)
|
||||||
|
@ -87,7 +87,7 @@ func TestAPIGetPublicDashboard(t *testing.T) {
|
|||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
ExpectedHttpResponse: http.StatusNotFound,
|
ExpectedHttpResponse: http.StatusNotFound,
|
||||||
publicDashboardResult: nil,
|
publicDashboardResult: nil,
|
||||||
publicDashboardErr: models.ErrPublicDashboardNotFound,
|
publicDashboardErr: dashboards.ErrPublicDashboardNotFound,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ func TestAPIGetPublicDashboardConfig(t *testing.T) {
|
|||||||
DashboardUid: "77777",
|
DashboardUid: "77777",
|
||||||
ExpectedHttpResponse: http.StatusNotFound,
|
ExpectedHttpResponse: http.StatusNotFound,
|
||||||
PublicDashboardResult: nil,
|
PublicDashboardResult: nil,
|
||||||
PublicDashboardError: models.ErrDashboardNotFound,
|
PublicDashboardError: dashboards.ErrDashboardNotFound,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "returns 500 when internal server error",
|
Name: "returns 500 when internal server error",
|
||||||
@ -218,7 +218,7 @@ func TestApiSavePublicDashboardConfig(t *testing.T) {
|
|||||||
Name: "returns 404 when dashboard not found",
|
Name: "returns 404 when dashboard not found",
|
||||||
ExpectedHttpResponse: http.StatusNotFound,
|
ExpectedHttpResponse: http.StatusNotFound,
|
||||||
publicDashboardConfig: &models.PublicDashboard{},
|
publicDashboardConfig: &models.PublicDashboard{},
|
||||||
saveDashboardError: models.ErrDashboardNotFound,
|
saveDashboardError: dashboards.ErrDashboardNotFound,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@ -272,11 +273,11 @@ func (hs *HTTPServer) DeleteDashboardSnapshot(c *models.ReqContext) response.Res
|
|||||||
guardian := guardian.New(c.Req.Context(), dashboardID, c.OrgId, c.SignedInUser)
|
guardian := guardian.New(c.Req.Context(), dashboardID, c.OrgId, c.SignedInUser)
|
||||||
canEdit, err := guardian.CanEdit()
|
canEdit, err := guardian.CanEdit()
|
||||||
// check for permissions only if the dahboard is found
|
// check for permissions only if the dahboard is found
|
||||||
if err != nil && !errors.Is(err, models.ErrDashboardNotFound) {
|
if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||||
return response.Error(500, "Error while checking permissions for snapshot", err)
|
return response.Error(500, "Error while checking permissions for snapshot", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !canEdit && query.Result.UserId != c.SignedInUser.UserId && !errors.Is(err, models.ErrDashboardNotFound) {
|
if !canEdit && query.Result.UserId != c.SignedInUser.UserId && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||||
return response.Error(403, "Access denied to this snapshot", nil)
|
return response.Error(403, "Access denied to this snapshot", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/framework/coremodel/registry"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -18,6 +17,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
"github.com/grafana/grafana/pkg/framework/coremodel/registry"
|
||||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
@ -679,24 +679,24 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
SaveError error
|
SaveError error
|
||||||
ExpectedStatusCode int
|
ExpectedStatusCode int
|
||||||
}{
|
}{
|
||||||
{SaveError: models.ErrDashboardNotFound, ExpectedStatusCode: 404},
|
{SaveError: dashboards.ErrDashboardNotFound, ExpectedStatusCode: 404},
|
||||||
{SaveError: models.ErrFolderNotFound, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrFolderNotFound, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.ErrDashboardWithSameUIDExists, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardWithSameUIDExists, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.ErrDashboardWithSameNameInFolderExists, ExpectedStatusCode: 412},
|
{SaveError: dashboards.ErrDashboardWithSameNameInFolderExists, ExpectedStatusCode: 412},
|
||||||
{SaveError: models.ErrDashboardVersionMismatch, ExpectedStatusCode: 412},
|
{SaveError: dashboards.ErrDashboardVersionMismatch, ExpectedStatusCode: 412},
|
||||||
{SaveError: models.ErrDashboardTitleEmpty, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardTitleEmpty, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.ErrDashboardFolderCannotHaveParent, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardFolderCannotHaveParent, ExpectedStatusCode: 400},
|
||||||
{SaveError: alerting.ValidationError{Reason: "Mu"}, ExpectedStatusCode: 422},
|
{SaveError: alerting.ValidationError{Reason: "Mu"}, ExpectedStatusCode: 422},
|
||||||
{SaveError: models.ErrDashboardFailedGenerateUniqueUid, ExpectedStatusCode: 500},
|
{SaveError: dashboards.ErrDashboardFailedGenerateUniqueUid, ExpectedStatusCode: 500},
|
||||||
{SaveError: models.ErrDashboardTypeMismatch, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardTypeMismatch, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.ErrDashboardFolderWithSameNameAsDashboard, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardFolderWithSameNameAsDashboard, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.ErrDashboardWithSameNameAsFolder, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardWithSameNameAsFolder, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.ErrDashboardFolderNameExists, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardFolderNameExists, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403},
|
{SaveError: dashboards.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403},
|
||||||
{SaveError: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.ErrDashboardCannotSaveProvisionedDashboard, ExpectedStatusCode: 400},
|
{SaveError: dashboards.ErrDashboardCannotSaveProvisionedDashboard, ExpectedStatusCode: 400},
|
||||||
{SaveError: models.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412},
|
{SaveError: dashboards.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
@ -24,7 +25,7 @@ func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Res
|
|||||||
g := guardian.New(c.Req.Context(), folder.Id, c.OrgId, c.SignedInUser)
|
g := guardian.New(c.Req.Context(), folder.Id, c.OrgId, c.SignedInUser)
|
||||||
|
|
||||||
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
|
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
|
||||||
return apierrors.ToFolderErrorResponse(models.ErrFolderAccessDenied)
|
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
|
||||||
}
|
}
|
||||||
|
|
||||||
acl, err := g.GetAcl()
|
acl, err := g.GetAcl()
|
||||||
@ -78,7 +79,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !canAdmin {
|
if !canAdmin {
|
||||||
return apierrors.ToFolderErrorResponse(models.ErrFolderAccessDenied)
|
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
|
||||||
}
|
}
|
||||||
|
|
||||||
var items []*models.DashboardAcl
|
var items []*models.DashboardAcl
|
||||||
|
@ -49,7 +49,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Given folder not exists", func(t *testing.T) {
|
t.Run("Given folder not exists", func(t *testing.T) {
|
||||||
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, models.ErrFolderNotFound).Twice()
|
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, dashboards.ErrFolderNotFound).Twice()
|
||||||
mockSQLStore := mockstore.NewSQLStoreMock()
|
mockSQLStore := mockstore.NewSQLStoreMock()
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||||
callGetFolderPermissions(sc, hs)
|
callGetFolderPermissions(sc, hs)
|
||||||
@ -81,7 +81,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
|
||||||
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, models.ErrFolderAccessDenied).Twice()
|
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, dashboards.ErrFolderAccessDenied).Twice()
|
||||||
mockSQLStore := mockstore.NewSQLStoreMock()
|
mockSQLStore := mockstore.NewSQLStoreMock()
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||||
|
@ -7,6 +7,10 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"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/dtos"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
@ -19,9 +23,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/web/webtest"
|
"github.com/grafana/grafana/pkg/web/webtest"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFoldersAPIEndpoint(t *testing.T) {
|
func TestFoldersAPIEndpoint(t *testing.T) {
|
||||||
@ -55,15 +56,15 @@ func TestFoldersAPIEndpoint(t *testing.T) {
|
|||||||
Error error
|
Error error
|
||||||
ExpectedStatusCode int
|
ExpectedStatusCode int
|
||||||
}{
|
}{
|
||||||
{Error: models.ErrFolderWithSameUIDExists, ExpectedStatusCode: 409},
|
{Error: dashboards.ErrFolderWithSameUIDExists, ExpectedStatusCode: 409},
|
||||||
{Error: models.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
|
{Error: dashboards.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
|
||||||
{Error: models.ErrFolderSameNameExists, ExpectedStatusCode: 409},
|
{Error: dashboards.ErrFolderSameNameExists, ExpectedStatusCode: 409},
|
||||||
{Error: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
|
{Error: dashboards.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
|
||||||
{Error: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
|
{Error: dashboards.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
|
||||||
{Error: models.ErrFolderAccessDenied, ExpectedStatusCode: 403},
|
{Error: dashboards.ErrFolderAccessDenied, ExpectedStatusCode: 403},
|
||||||
{Error: models.ErrFolderNotFound, ExpectedStatusCode: 404},
|
{Error: dashboards.ErrFolderNotFound, ExpectedStatusCode: 404},
|
||||||
{Error: models.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
|
{Error: dashboards.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
|
||||||
{Error: models.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
|
{Error: dashboards.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := models.CreateFolderCommand{
|
cmd := models.CreateFolderCommand{
|
||||||
@ -110,15 +111,15 @@ func TestFoldersAPIEndpoint(t *testing.T) {
|
|||||||
Error error
|
Error error
|
||||||
ExpectedStatusCode int
|
ExpectedStatusCode int
|
||||||
}{
|
}{
|
||||||
{Error: models.ErrFolderWithSameUIDExists, ExpectedStatusCode: 409},
|
{Error: dashboards.ErrFolderWithSameUIDExists, ExpectedStatusCode: 409},
|
||||||
{Error: models.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
|
{Error: dashboards.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
|
||||||
{Error: models.ErrFolderSameNameExists, ExpectedStatusCode: 409},
|
{Error: dashboards.ErrFolderSameNameExists, ExpectedStatusCode: 409},
|
||||||
{Error: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
|
{Error: dashboards.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
|
||||||
{Error: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
|
{Error: dashboards.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
|
||||||
{Error: models.ErrFolderAccessDenied, ExpectedStatusCode: 403},
|
{Error: dashboards.ErrFolderAccessDenied, ExpectedStatusCode: 403},
|
||||||
{Error: models.ErrFolderNotFound, ExpectedStatusCode: 404},
|
{Error: dashboards.ErrFolderNotFound, ExpectedStatusCode: 404},
|
||||||
{Error: models.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
|
{Error: dashboards.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
|
||||||
{Error: models.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
|
{Error: dashboards.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := models.UpdateFolderCommand{
|
cmd := models.UpdateFolderCommand{
|
||||||
|
@ -10,173 +10,10 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const RootFolderName = "General"
|
const RootFolderName = "General"
|
||||||
|
|
||||||
// Typed errors
|
|
||||||
var (
|
|
||||||
ErrDashboardNotFound = DashboardErr{
|
|
||||||
Reason: "Dashboard not found",
|
|
||||||
StatusCode: 404,
|
|
||||||
Status: "not-found",
|
|
||||||
}
|
|
||||||
ErrDashboardCorrupt = DashboardErr{
|
|
||||||
Reason: "Dashboard data is missing or corrupt",
|
|
||||||
StatusCode: 500,
|
|
||||||
Status: "not-found",
|
|
||||||
}
|
|
||||||
ErrDashboardPanelNotFound = DashboardErr{
|
|
||||||
Reason: "Dashboard panel not found",
|
|
||||||
StatusCode: 404,
|
|
||||||
Status: "not-found",
|
|
||||||
}
|
|
||||||
ErrDashboardFolderNotFound = DashboardErr{
|
|
||||||
Reason: "Folder not found",
|
|
||||||
StatusCode: 404,
|
|
||||||
}
|
|
||||||
ErrDashboardSnapshotNotFound = DashboardErr{
|
|
||||||
Reason: "Dashboard snapshot not found",
|
|
||||||
StatusCode: 404,
|
|
||||||
}
|
|
||||||
ErrDashboardWithSameUIDExists = DashboardErr{
|
|
||||||
Reason: "A dashboard with the same uid already exists",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardWithSameNameInFolderExists = DashboardErr{
|
|
||||||
Reason: "A dashboard with the same name in the folder already exists",
|
|
||||||
StatusCode: 412,
|
|
||||||
Status: "name-exists",
|
|
||||||
}
|
|
||||||
ErrDashboardVersionMismatch = DashboardErr{
|
|
||||||
Reason: "The dashboard has been changed by someone else",
|
|
||||||
StatusCode: 412,
|
|
||||||
Status: "version-mismatch",
|
|
||||||
}
|
|
||||||
ErrDashboardTitleEmpty = DashboardErr{
|
|
||||||
Reason: "Dashboard title cannot be empty",
|
|
||||||
StatusCode: 400,
|
|
||||||
Status: "empty-name",
|
|
||||||
}
|
|
||||||
ErrDashboardFolderCannotHaveParent = DashboardErr{
|
|
||||||
Reason: "A Dashboard Folder cannot be added to another folder",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardsWithSameSlugExists = DashboardErr{
|
|
||||||
Reason: "Multiple dashboards with the same slug exists",
|
|
||||||
StatusCode: 412,
|
|
||||||
}
|
|
||||||
ErrDashboardFailedGenerateUniqueUid = DashboardErr{
|
|
||||||
Reason: "Failed to generate unique dashboard id",
|
|
||||||
StatusCode: 500,
|
|
||||||
}
|
|
||||||
ErrDashboardTypeMismatch = DashboardErr{
|
|
||||||
Reason: "Dashboard cannot be changed to a folder",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardFolderWithSameNameAsDashboard = DashboardErr{
|
|
||||||
Reason: "Folder name cannot be the same as one of its dashboards",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardWithSameNameAsFolder = DashboardErr{
|
|
||||||
Reason: "Dashboard name cannot be the same as folder",
|
|
||||||
StatusCode: 400,
|
|
||||||
Status: "name-match",
|
|
||||||
}
|
|
||||||
ErrDashboardFolderNameExists = DashboardErr{
|
|
||||||
Reason: "A folder with that name already exists",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardUpdateAccessDenied = DashboardErr{
|
|
||||||
Reason: "Access denied to save dashboard",
|
|
||||||
StatusCode: 403,
|
|
||||||
}
|
|
||||||
ErrDashboardInvalidUid = DashboardErr{
|
|
||||||
Reason: "uid contains illegal characters",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardUidTooLong = DashboardErr{
|
|
||||||
Reason: "uid too long, max 40 characters",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardCannotSaveProvisionedDashboard = DashboardErr{
|
|
||||||
Reason: "Cannot save provisioned dashboard",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardRefreshIntervalTooShort = DashboardErr{
|
|
||||||
Reason: "Dashboard refresh interval is too low",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardCannotDeleteProvisionedDashboard = DashboardErr{
|
|
||||||
Reason: "provisioned dashboard cannot be deleted",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardIdentifierNotSet = DashboardErr{
|
|
||||||
Reason: "Unique identifier needed to be able to get a dashboard",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardIdentifierInvalid = DashboardErr{
|
|
||||||
Reason: "Dashboard ID not a number",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardPanelIdentifierInvalid = DashboardErr{
|
|
||||||
Reason: "Dashboard panel ID not a number",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrDashboardOrPanelIdentifierNotSet = DashboardErr{
|
|
||||||
Reason: "Unique identifier needed to be able to get a dashboard panel",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
ErrProvisionedDashboardNotFound = DashboardErr{
|
|
||||||
Reason: "Dashboard is not provisioned",
|
|
||||||
StatusCode: 404,
|
|
||||||
Status: "not-found",
|
|
||||||
}
|
|
||||||
ErrDashboardThumbnailNotFound = DashboardErr{
|
|
||||||
Reason: "Dashboard thumbnail not found",
|
|
||||||
StatusCode: 404,
|
|
||||||
Status: "not-found",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// DashboardErr represents a dashboard error.
|
|
||||||
type DashboardErr struct {
|
|
||||||
StatusCode int
|
|
||||||
Status string
|
|
||||||
Reason string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal returns whether equal to another DashboardErr.
|
|
||||||
func (e DashboardErr) Equal(o DashboardErr) bool {
|
|
||||||
return o.StatusCode == e.StatusCode && o.Status == e.Status && o.Reason == e.Reason
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the error message.
|
|
||||||
func (e DashboardErr) Error() string {
|
|
||||||
if e.Reason != "" {
|
|
||||||
return e.Reason
|
|
||||||
}
|
|
||||||
return "Dashboard Error"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Body returns the error's response body, if applicable.
|
|
||||||
func (e DashboardErr) Body() util.DynMap {
|
|
||||||
if e.Status == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return util.DynMap{"status": e.Status, "message": e.Error()}
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdatePluginDashboardError struct {
|
|
||||||
PluginId string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d UpdatePluginDashboardError) Error() string {
|
|
||||||
return "Dashboard belongs to plugin"
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DashTypeDB = "db"
|
DashTypeDB = "db"
|
||||||
DashTypeSnapshot = "snapshot"
|
DashTypeSnapshot = "snapshot"
|
||||||
|
@ -6,31 +6,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
ErrPublicDashboardFailedGenerateUniqueUid = DashboardErr{
|
|
||||||
Reason: "Failed to generate unique public dashboard id",
|
|
||||||
StatusCode: 500,
|
|
||||||
}
|
|
||||||
ErrPublicDashboardFailedGenerateAccesstoken = DashboardErr{
|
|
||||||
Reason: "Failed to public dashboard access token",
|
|
||||||
StatusCode: 500,
|
|
||||||
}
|
|
||||||
ErrPublicDashboardNotFound = DashboardErr{
|
|
||||||
Reason: "Public dashboard not found",
|
|
||||||
StatusCode: 404,
|
|
||||||
Status: "not-found",
|
|
||||||
}
|
|
||||||
ErrPublicDashboardPanelNotFound = DashboardErr{
|
|
||||||
Reason: "Panel not found in dashboard",
|
|
||||||
StatusCode: 404,
|
|
||||||
Status: "not-found",
|
|
||||||
}
|
|
||||||
ErrPublicDashboardIdentifierNotSet = DashboardErr{
|
|
||||||
Reason: "No Uid for public dashboard specified",
|
|
||||||
StatusCode: 400,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type PublicDashboard struct {
|
type PublicDashboard struct {
|
||||||
Uid string `json:"uid" xorm:"pk uid"`
|
Uid string `json:"uid" xorm:"pk uid"`
|
||||||
DashboardUid string `json:"dashboardUid" xorm:"dashboard_uid"`
|
DashboardUid string `json:"dashboardUid" xorm:"dashboard_uid"`
|
||||||
|
@ -1,24 +1,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Typed errors
|
|
||||||
var (
|
|
||||||
ErrFolderNotFound = errors.New("folder not found")
|
|
||||||
ErrFolderVersionMismatch = errors.New("the folder has been changed by someone else")
|
|
||||||
ErrFolderTitleEmpty = errors.New("folder title cannot be empty")
|
|
||||||
ErrFolderWithSameUIDExists = errors.New("a folder/dashboard with the same uid already exists")
|
|
||||||
ErrFolderInvalidUID = errors.New("invalid uid for folder provided")
|
|
||||||
ErrFolderSameNameExists = errors.New("a folder or dashboard in the general folder with the same name already exists")
|
|
||||||
ErrFolderFailedGenerateUniqueUid = errors.New("failed to generate unique folder ID")
|
|
||||||
ErrFolderAccessDenied = errors.New("access denied to folder")
|
|
||||||
ErrFolderContainsAlertRules = errors.New("folder contains alert rules")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Folder struct {
|
type Folder struct {
|
||||||
Id int64
|
Id int64
|
||||||
Uid string
|
Uid string
|
||||||
|
@ -64,12 +64,12 @@ func TestNewFolderNameScopeResolver(t *testing.T) {
|
|||||||
_, resolver := NewFolderNameScopeResolver(dashboardStore)
|
_, resolver := NewFolderNameScopeResolver(dashboardStore)
|
||||||
|
|
||||||
orgId := rand.Int63()
|
orgId := rand.Int63()
|
||||||
dashboardStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(nil, models.ErrDashboardNotFound).Once()
|
dashboardStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once()
|
||||||
|
|
||||||
scope := "folders:name:" + util.GenerateShortUID()
|
scope := "folders:name:" + util.GenerateShortUID()
|
||||||
|
|
||||||
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
|
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
|
||||||
require.ErrorIs(t, err, models.ErrDashboardNotFound)
|
require.ErrorIs(t, err, ErrDashboardNotFound)
|
||||||
require.Nil(t, resolvedScopes)
|
require.Nil(t, resolvedScopes)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -136,11 +136,11 @@ func TestNewFolderIDScopeResolver(t *testing.T) {
|
|||||||
_, resolver := NewFolderIDScopeResolver(dashboardStore)
|
_, resolver := NewFolderIDScopeResolver(dashboardStore)
|
||||||
|
|
||||||
orgId := rand.Int63()
|
orgId := rand.Int63()
|
||||||
dashboardStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(nil, models.ErrDashboardNotFound).Once()
|
dashboardStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(nil, ErrDashboardNotFound).Once()
|
||||||
|
|
||||||
scope := "folders:id:10"
|
scope := "folders:id:10"
|
||||||
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
|
resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope)
|
||||||
require.ErrorIs(t, err, models.ErrDashboardNotFound)
|
require.ErrorIs(t, err, ErrDashboardNotFound)
|
||||||
require.Nil(t, resolvedScopes)
|
require.Nil(t, resolvedScopes)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ func (d *DashboardStore) ValidateDashboardBeforeSave(dashboard *models.Dashboard
|
|||||||
|
|
||||||
func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*models.Folder, error) {
|
func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*models.Folder, error) {
|
||||||
if title == "" {
|
if title == "" {
|
||||||
return nil, models.ErrFolderTitleEmpty
|
return nil, dashboards.ErrFolderTitleEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
// there is a unique constraint on org_id, folder_id, title
|
// there is a unique constraint on org_id, folder_id, title
|
||||||
@ -72,7 +72,7 @@ func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, titl
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !has {
|
if !has {
|
||||||
return models.ErrFolderNotFound
|
return dashboards.ErrFolderNotFound
|
||||||
}
|
}
|
||||||
dashboard.SetId(dashboard.Id)
|
dashboard.SetId(dashboard.Id)
|
||||||
dashboard.SetUid(dashboard.Uid)
|
dashboard.SetUid(dashboard.Uid)
|
||||||
@ -89,7 +89,7 @@ func (d *DashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int6
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !has {
|
if !has {
|
||||||
return models.ErrFolderNotFound
|
return dashboards.ErrFolderNotFound
|
||||||
}
|
}
|
||||||
dashboard.SetId(dashboard.Id)
|
dashboard.SetId(dashboard.Id)
|
||||||
dashboard.SetUid(dashboard.Uid)
|
dashboard.SetUid(dashboard.Uid)
|
||||||
@ -103,7 +103,7 @@ func (d *DashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int6
|
|||||||
|
|
||||||
func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*models.Folder, error) {
|
func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*models.Folder, error) {
|
||||||
if uid == "" {
|
if uid == "" {
|
||||||
return nil, models.ErrDashboardIdentifierNotSet
|
return nil, dashboards.ErrDashboardIdentifierNotSet
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard := models.Dashboard{OrgId: orgID, FolderId: 0, Uid: uid}
|
dashboard := models.Dashboard{OrgId: orgID, FolderId: 0, Uid: uid}
|
||||||
@ -113,7 +113,7 @@ func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid st
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !has {
|
if !has {
|
||||||
return models.ErrFolderNotFound
|
return dashboards.ErrFolderNotFound
|
||||||
}
|
}
|
||||||
dashboard.SetId(dashboard.Id)
|
dashboard.SetId(dashboard.Id)
|
||||||
dashboard.SetUid(dashboard.Uid)
|
dashboard.SetUid(dashboard.Uid)
|
||||||
@ -147,15 +147,14 @@ func (d *DashboardStore) GetProvisionedDataByDashboardUID(orgID int64, dashboard
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
return models.
|
return dashboards.ErrDashboardNotFound
|
||||||
ErrDashboardNotFound
|
|
||||||
}
|
}
|
||||||
exists, err = sess.Where("dashboard_id = ?", dashboard.Id).Get(&provisionedDashboard)
|
exists, err = sess.Where("dashboard_id = ?", dashboard.Id).Get(&provisionedDashboard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
return models.ErrProvisionedDashboardNotFound
|
return dashboards.ErrProvisionedDashboardNotFound
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -267,7 +266,7 @@ func (d *DashboardStore) DeleteOrphanedProvisionedDashboards(ctx context.Context
|
|||||||
|
|
||||||
for _, deleteDashCommand := range result {
|
for _, deleteDashCommand := range result {
|
||||||
err := d.DeleteDashboard(ctx, &models.DeleteDashboardCommand{Id: deleteDashCommand.DashboardId})
|
err := d.DeleteDashboard(ctx, &models.DeleteDashboardCommand{Id: deleteDashCommand.DashboardId})
|
||||||
if err != nil && !errors.Is(err, models.ErrDashboardNotFound) {
|
if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,7 +288,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *sqlstore.DBSession, dash *mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !dashWithIdExists {
|
if !dashWithIdExists {
|
||||||
return false, models.ErrDashboardNotFound
|
return false, dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if dash.Uid == "" {
|
if dash.Uid == "" {
|
||||||
@ -317,7 +316,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *sqlstore.DBSession, dash *mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !folderExists {
|
if !folderExists {
|
||||||
return false, models.ErrDashboardFolderNotFound
|
return false, dashboards.ErrDashboardFolderNotFound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,7 +325,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *sqlstore.DBSession, dash *mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
if dashWithIdExists && dashWithUidExists && existingById.Id != existingByUid.Id {
|
if dashWithIdExists && dashWithUidExists && existingById.Id != existingByUid.Id {
|
||||||
return false, models.ErrDashboardWithSameUIDExists
|
return false, dashboards.ErrDashboardWithSameUIDExists
|
||||||
}
|
}
|
||||||
|
|
||||||
existing := existingById
|
existing := existingById
|
||||||
@ -339,7 +338,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *sqlstore.DBSession, dash *mode
|
|||||||
|
|
||||||
if (existing.IsFolder && !dash.IsFolder) ||
|
if (existing.IsFolder && !dash.IsFolder) ||
|
||||||
(!existing.IsFolder && dash.IsFolder) {
|
(!existing.IsFolder && dash.IsFolder) {
|
||||||
return isParentFolderChanged, models.ErrDashboardTypeMismatch
|
return isParentFolderChanged, dashboards.ErrDashboardTypeMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dash.IsFolder && dash.FolderId != existing.FolderId {
|
if !dash.IsFolder && dash.FolderId != existing.FolderId {
|
||||||
@ -351,13 +350,13 @@ func getExistingDashboardByIdOrUidForUpdate(sess *sqlstore.DBSession, dash *mode
|
|||||||
if overwrite {
|
if overwrite {
|
||||||
dash.SetVersion(existing.Version)
|
dash.SetVersion(existing.Version)
|
||||||
} else {
|
} else {
|
||||||
return isParentFolderChanged, models.ErrDashboardVersionMismatch
|
return isParentFolderChanged, dashboards.ErrDashboardVersionMismatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not allow plugin dashboard updates without overwrite flag
|
// do not allow plugin dashboard updates without overwrite flag
|
||||||
if existing.PluginId != "" && !overwrite {
|
if existing.PluginId != "" && !overwrite {
|
||||||
return isParentFolderChanged, models.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
return isParentFolderChanged, dashboards.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isParentFolderChanged, nil
|
return isParentFolderChanged, nil
|
||||||
@ -374,11 +373,11 @@ func getExistingDashboardByTitleAndFolder(sess *sqlstore.DBSession, dash *models
|
|||||||
|
|
||||||
if exists && dash.Id != existing.Id {
|
if exists && dash.Id != existing.Id {
|
||||||
if existing.IsFolder && !dash.IsFolder {
|
if existing.IsFolder && !dash.IsFolder {
|
||||||
return isParentFolderChanged, models.ErrDashboardWithSameNameAsFolder
|
return isParentFolderChanged, dashboards.ErrDashboardWithSameNameAsFolder
|
||||||
}
|
}
|
||||||
|
|
||||||
if !existing.IsFolder && dash.IsFolder {
|
if !existing.IsFolder && dash.IsFolder {
|
||||||
return isParentFolderChanged, models.ErrDashboardFolderWithSameNameAsDashboard
|
return isParentFolderChanged, dashboards.ErrDashboardFolderWithSameNameAsDashboard
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dash.IsFolder && (dash.FolderId != existing.FolderId || dash.Id == 0) {
|
if !dash.IsFolder && (dash.FolderId != existing.FolderId || dash.Id == 0) {
|
||||||
@ -390,7 +389,7 @@ func getExistingDashboardByTitleAndFolder(sess *sqlstore.DBSession, dash *models
|
|||||||
dash.SetUid(existing.Uid)
|
dash.SetUid(existing.Uid)
|
||||||
dash.SetVersion(existing.Version)
|
dash.SetVersion(existing.Version)
|
||||||
} else {
|
} else {
|
||||||
return isParentFolderChanged, models.ErrDashboardWithSameNameInFolderExists
|
return isParentFolderChanged, dashboards.ErrDashboardWithSameNameInFolderExists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +412,7 @@ func saveDashboard(sess *sqlstore.DBSession, cmd *models.SaveDashboardCommand) e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !dashWithIdExists {
|
if !dashWithIdExists {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for is someone else has written in between
|
// check for is someone else has written in between
|
||||||
@ -421,13 +420,13 @@ func saveDashboard(sess *sqlstore.DBSession, cmd *models.SaveDashboardCommand) e
|
|||||||
if cmd.Overwrite {
|
if cmd.Overwrite {
|
||||||
dash.SetVersion(existing.Version)
|
dash.SetVersion(existing.Version)
|
||||||
} else {
|
} else {
|
||||||
return models.ErrDashboardVersionMismatch
|
return dashboards.ErrDashboardVersionMismatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not allow plugin dashboard updates without overwrite flag
|
// do not allow plugin dashboard updates without overwrite flag
|
||||||
if existing.PluginId != "" && !cmd.Overwrite {
|
if existing.PluginId != "" && !cmd.Overwrite {
|
||||||
return models.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
return dashboards.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,7 +469,7 @@ func saveDashboard(sess *sqlstore.DBSession, cmd *models.SaveDashboardCommand) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
if affectedRows == 0 {
|
if affectedRows == 0 {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
dashVersion := &dashver.DashboardVersion{
|
dashVersion := &dashver.DashboardVersion{
|
||||||
@ -488,7 +487,7 @@ func saveDashboard(sess *sqlstore.DBSession, cmd *models.SaveDashboardCommand) e
|
|||||||
if affectedRows, err = sess.Insert(dashVersion); err != nil {
|
if affectedRows, err = sess.Insert(dashVersion); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if affectedRows == 0 {
|
} else if affectedRows == 0 {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete existing tags
|
// delete existing tags
|
||||||
@ -525,7 +524,7 @@ func generateNewDashboardUid(sess *sqlstore.DBSession, orgId int64) (string, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", models.ErrDashboardFailedGenerateUniqueUid
|
return "", dashboards.ErrDashboardFailedGenerateUniqueUid
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveProvisionedData(sess *sqlstore.DBSession, provisioning *models.DashboardProvisioning, dashboard *models.Dashboard) error {
|
func saveProvisionedData(sess *sqlstore.DBSession, provisioning *models.DashboardProvisioning, dashboard *models.Dashboard) error {
|
||||||
@ -709,7 +708,7 @@ func (d *DashboardStore) deleteDashboard(cmd *models.DeleteDashboardCommand, ses
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
deletes := []string{
|
deletes := []string{
|
||||||
@ -779,7 +778,7 @@ func (d *DashboardStore) deleteDashboard(cmd *models.DeleteDashboardCommand, ses
|
|||||||
}
|
}
|
||||||
if exists {
|
if exists {
|
||||||
if !cmd.ForceDeleteFolderRules {
|
if !cmd.ForceDeleteFolderRules {
|
||||||
return fmt.Errorf("folder cannot be deleted: %w", models.ErrFolderContainsAlertRules)
|
return fmt.Errorf("folder cannot be deleted: %w", dashboards.ErrFolderContainsAlertRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all rules under this folder.
|
// Delete all rules under this folder.
|
||||||
@ -836,7 +835,7 @@ func (d *DashboardStore) deleteAlertDefinition(dashboardId int64, sess *sqlstore
|
|||||||
func (d *DashboardStore) GetDashboard(ctx context.Context, query *models.GetDashboardQuery) (*models.Dashboard, error) {
|
func (d *DashboardStore) GetDashboard(ctx context.Context, query *models.GetDashboardQuery) (*models.Dashboard, error) {
|
||||||
err := d.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
err := d.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
if query.Id == 0 && len(query.Slug) == 0 && len(query.Uid) == 0 {
|
if query.Id == 0 && len(query.Slug) == 0 && len(query.Uid) == 0 {
|
||||||
return models.ErrDashboardIdentifierNotSet
|
return dashboards.ErrDashboardIdentifierNotSet
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard := models.Dashboard{Slug: query.Slug, OrgId: query.OrgId, Id: query.Id, Uid: query.Uid}
|
dashboard := models.Dashboard{Slug: query.Slug, OrgId: query.OrgId, Id: query.Id, Uid: query.Uid}
|
||||||
@ -845,7 +844,7 @@ func (d *DashboardStore) GetDashboard(ctx context.Context, query *models.GetDash
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard.SetId(dashboard.Id)
|
dashboard.SetId(dashboard.Id)
|
||||||
@ -865,7 +864,7 @@ func (d *DashboardStore) GetDashboardUIDById(ctx context.Context, query *models.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !exists {
|
} else if !exists {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
query.Result = us
|
query.Result = us
|
||||||
return nil
|
return nil
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
@ -11,7 +12,7 @@ import (
|
|||||||
// retrieves public dashboard configuration
|
// retrieves public dashboard configuration
|
||||||
func (d *DashboardStore) GetPublicDashboard(ctx context.Context, accessToken string) (*models.PublicDashboard, *models.Dashboard, error) {
|
func (d *DashboardStore) GetPublicDashboard(ctx context.Context, accessToken string) (*models.PublicDashboard, *models.Dashboard, error) {
|
||||||
if accessToken == "" {
|
if accessToken == "" {
|
||||||
return nil, nil, models.ErrPublicDashboardIdentifierNotSet
|
return nil, nil, dashboards.ErrPublicDashboardIdentifierNotSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// get public dashboard
|
// get public dashboard
|
||||||
@ -22,7 +23,7 @@ func (d *DashboardStore) GetPublicDashboard(ctx context.Context, accessToken str
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !has {
|
if !has {
|
||||||
return models.ErrPublicDashboardNotFound
|
return dashboards.ErrPublicDashboardNotFound
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -39,7 +40,7 @@ func (d *DashboardStore) GetPublicDashboard(ctx context.Context, accessToken str
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !has {
|
if !has {
|
||||||
return models.ErrPublicDashboardNotFound
|
return dashboards.ErrPublicDashboardNotFound
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -69,7 +70,7 @@ func (d *DashboardStore) GenerateNewPublicDashboardUid(ctx context.Context) (str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return models.ErrPublicDashboardFailedGenerateUniqueUid
|
return dashboards.ErrPublicDashboardFailedGenerateUniqueUid
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -82,7 +83,7 @@ func (d *DashboardStore) GenerateNewPublicDashboardUid(ctx context.Context) (str
|
|||||||
// retrieves public dashboard configuration
|
// retrieves public dashboard configuration
|
||||||
func (d *DashboardStore) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*models.PublicDashboard, error) {
|
func (d *DashboardStore) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*models.PublicDashboard, error) {
|
||||||
if dashboardUid == "" {
|
if dashboardUid == "" {
|
||||||
return nil, models.ErrDashboardIdentifierNotSet
|
return nil, dashboards.ErrDashboardIdentifierNotSet
|
||||||
}
|
}
|
||||||
|
|
||||||
pdRes := &models.PublicDashboard{OrgId: orgId, DashboardUid: dashboardUid}
|
pdRes := &models.PublicDashboard{OrgId: orgId, DashboardUid: dashboardUid}
|
||||||
|
@ -5,13 +5,15 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is what the db sets empty time settings to
|
// This is what the db sets empty time settings to
|
||||||
@ -58,13 +60,13 @@ func TestIntegrationGetPublicDashboard(t *testing.T) {
|
|||||||
t.Run("returns ErrPublicDashboardNotFound with empty uid", func(t *testing.T) {
|
t.Run("returns ErrPublicDashboardNotFound with empty uid", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
_, _, err := dashboardStore.GetPublicDashboard(context.Background(), "")
|
_, _, err := dashboardStore.GetPublicDashboard(context.Background(), "")
|
||||||
require.Error(t, models.ErrPublicDashboardIdentifierNotSet, err)
|
require.Error(t, dashboards.ErrPublicDashboardIdentifierNotSet, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("returns ErrPublicDashboardNotFound when PublicDashboard not found", func(t *testing.T) {
|
t.Run("returns ErrPublicDashboardNotFound when PublicDashboard not found", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
_, _, err := dashboardStore.GetPublicDashboard(context.Background(), "zzzzzz")
|
_, _, err := dashboardStore.GetPublicDashboard(context.Background(), "zzzzzz")
|
||||||
require.Error(t, models.ErrPublicDashboardNotFound, err)
|
require.Error(t, dashboards.ErrPublicDashboardNotFound, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("returns ErrDashboardNotFound when Dashboard not found", func(t *testing.T) {
|
t.Run("returns ErrDashboardNotFound when Dashboard not found", func(t *testing.T) {
|
||||||
@ -83,7 +85,7 @@ func TestIntegrationGetPublicDashboard(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, _, err = dashboardStore.GetPublicDashboard(context.Background(), "abc1234")
|
_, _, err = dashboardStore.GetPublicDashboard(context.Background(), "abc1234")
|
||||||
require.Error(t, models.ErrDashboardNotFound, err)
|
require.Error(t, dashboards.ErrDashboardNotFound, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +111,7 @@ func TestIntegrationGetPublicDashboardConfig(t *testing.T) {
|
|||||||
t.Run("returns dashboard errDashboardIdentifierNotSet", func(t *testing.T) {
|
t.Run("returns dashboard errDashboardIdentifierNotSet", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
_, err := dashboardStore.GetPublicDashboardConfig(context.Background(), savedDashboard.OrgId, "")
|
_, err := dashboardStore.GetPublicDashboardConfig(context.Background(), savedDashboard.OrgId, "")
|
||||||
require.Error(t, models.ErrDashboardIdentifierNotSet, err)
|
require.Error(t, dashboards.ErrDashboardIdentifierNotSet, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("returns isPublic along with public dashboard when exists", func(t *testing.T) {
|
t.Run("returns isPublic along with public dashboard when exists", func(t *testing.T) {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
)
|
)
|
||||||
@ -492,12 +493,12 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
|||||||
t.Run("should not find dashboard", func(t *testing.T) {
|
t.Run("should not find dashboard", func(t *testing.T) {
|
||||||
d, err := dashboardStore.GetFolderByUID(context.Background(), orgId, dash.Uid)
|
d, err := dashboardStore.GetFolderByUID(context.Background(), orgId, dash.Uid)
|
||||||
require.Nil(t, d)
|
require.Nil(t, d)
|
||||||
require.ErrorIs(t, err, models.ErrFolderNotFound)
|
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
|
||||||
})
|
})
|
||||||
t.Run("should search in organization", func(t *testing.T) {
|
t.Run("should search in organization", func(t *testing.T) {
|
||||||
d, err := dashboardStore.GetFolderByUID(context.Background(), orgId+1, folder.Uid)
|
d, err := dashboardStore.GetFolderByUID(context.Background(), orgId+1, folder.Uid)
|
||||||
require.Nil(t, d)
|
require.Nil(t, d)
|
||||||
require.ErrorIs(t, err, models.ErrFolderNotFound)
|
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -516,12 +517,12 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
|||||||
t.Run("should not find dashboard", func(t *testing.T) {
|
t.Run("should not find dashboard", func(t *testing.T) {
|
||||||
d, err := dashboardStore.GetFolderByID(context.Background(), orgId, dash.Id)
|
d, err := dashboardStore.GetFolderByID(context.Background(), orgId, dash.Id)
|
||||||
require.Nil(t, d)
|
require.Nil(t, d)
|
||||||
require.ErrorIs(t, err, models.ErrFolderNotFound)
|
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
|
||||||
})
|
})
|
||||||
t.Run("should search in organization", func(t *testing.T) {
|
t.Run("should search in organization", func(t *testing.T) {
|
||||||
d, err := dashboardStore.GetFolderByID(context.Background(), orgId+1, folder.Id)
|
d, err := dashboardStore.GetFolderByID(context.Background(), orgId+1, folder.Id)
|
||||||
require.Nil(t, d)
|
require.Nil(t, d)
|
||||||
require.ErrorIs(t, err, models.ErrFolderNotFound)
|
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -124,7 +124,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err := dashboardStore.GetDashboard(context.Background(), &query)
|
_, err := dashboardStore.GetDashboard(context.Background(), &query)
|
||||||
require.Equal(t, err, models.ErrDashboardIdentifierNotSet)
|
require.Equal(t, err, dashboards.ErrDashboardIdentifierNotSet)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should be able to get dashboards by IDs & UIDs", func(t *testing.T) {
|
t.Run("Should be able to get dashboards by IDs & UIDs", func(t *testing.T) {
|
||||||
@ -227,7 +227,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||||||
setup()
|
setup()
|
||||||
deleteCmd := &models.DeleteDashboardCommand{Id: savedFolder.Id, ForceDeleteFolderRules: false}
|
deleteCmd := &models.DeleteDashboardCommand{Id: savedFolder.Id, ForceDeleteFolderRules: false}
|
||||||
err := dashboardStore.DeleteDashboard(context.Background(), deleteCmd)
|
err := dashboardStore.DeleteDashboard(context.Background(), deleteCmd)
|
||||||
require.True(t, errors.Is(err, models.ErrFolderContainsAlertRules))
|
require.True(t, errors.Is(err, dashboards.ErrFolderContainsAlertRules))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should be able to delete a dashboard folder and its children if force delete rules is enabled", func(t *testing.T) {
|
t.Run("Should be able to delete a dashboard folder and its children if force delete rules is enabled", func(t *testing.T) {
|
||||||
@ -274,7 +274,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err := dashboardStore.SaveDashboard(cmd)
|
_, err := dashboardStore.SaveDashboard(cmd)
|
||||||
require.Equal(t, err, models.ErrDashboardNotFound)
|
require.Equal(t, err, dashboards.ErrDashboardNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should not return error if no dashboard is found for update when dashboard id is zero", func(t *testing.T) {
|
t.Run("Should not return error if no dashboard is found for update when dashboard id is zero", func(t *testing.T) {
|
||||||
|
197
pkg/services/dashboards/errors.go
Normal file
197
pkg/services/dashboards/errors.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
package dashboards
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Typed errors
|
||||||
|
var (
|
||||||
|
ErrDashboardNotFound = DashboardErr{
|
||||||
|
Reason: "Dashboard not found",
|
||||||
|
StatusCode: 404,
|
||||||
|
Status: "not-found",
|
||||||
|
}
|
||||||
|
ErrDashboardCorrupt = DashboardErr{
|
||||||
|
Reason: "Dashboard data is missing or corrupt",
|
||||||
|
StatusCode: 500,
|
||||||
|
Status: "not-found",
|
||||||
|
}
|
||||||
|
ErrDashboardPanelNotFound = DashboardErr{
|
||||||
|
Reason: "Dashboard panel not found",
|
||||||
|
StatusCode: 404,
|
||||||
|
Status: "not-found",
|
||||||
|
}
|
||||||
|
ErrDashboardFolderNotFound = DashboardErr{
|
||||||
|
Reason: "Folder not found",
|
||||||
|
StatusCode: 404,
|
||||||
|
}
|
||||||
|
ErrDashboardWithSameUIDExists = DashboardErr{
|
||||||
|
Reason: "A dashboard with the same uid already exists",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardWithSameNameInFolderExists = DashboardErr{
|
||||||
|
Reason: "A dashboard with the same name in the folder already exists",
|
||||||
|
StatusCode: 412,
|
||||||
|
Status: "name-exists",
|
||||||
|
}
|
||||||
|
ErrDashboardVersionMismatch = DashboardErr{
|
||||||
|
Reason: "The dashboard has been changed by someone else",
|
||||||
|
StatusCode: 412,
|
||||||
|
Status: "version-mismatch",
|
||||||
|
}
|
||||||
|
ErrDashboardTitleEmpty = DashboardErr{
|
||||||
|
Reason: "Dashboard title cannot be empty",
|
||||||
|
StatusCode: 400,
|
||||||
|
Status: "empty-name",
|
||||||
|
}
|
||||||
|
ErrDashboardFolderCannotHaveParent = DashboardErr{
|
||||||
|
Reason: "A Dashboard Folder cannot be added to another folder",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardsWithSameSlugExists = DashboardErr{
|
||||||
|
Reason: "Multiple dashboards with the same slug exists",
|
||||||
|
StatusCode: 412,
|
||||||
|
}
|
||||||
|
ErrDashboardFailedGenerateUniqueUid = DashboardErr{
|
||||||
|
Reason: "Failed to generate unique dashboard id",
|
||||||
|
StatusCode: 500,
|
||||||
|
}
|
||||||
|
ErrDashboardTypeMismatch = DashboardErr{
|
||||||
|
Reason: "Dashboard cannot be changed to a folder",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardFolderWithSameNameAsDashboard = DashboardErr{
|
||||||
|
Reason: "Folder name cannot be the same as one of its dashboards",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardWithSameNameAsFolder = DashboardErr{
|
||||||
|
Reason: "Dashboard name cannot be the same as folder",
|
||||||
|
StatusCode: 400,
|
||||||
|
Status: "name-match",
|
||||||
|
}
|
||||||
|
ErrDashboardFolderNameExists = DashboardErr{
|
||||||
|
Reason: "A folder with that name already exists",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardUpdateAccessDenied = DashboardErr{
|
||||||
|
Reason: "Access denied to save dashboard",
|
||||||
|
StatusCode: 403,
|
||||||
|
}
|
||||||
|
ErrDashboardInvalidUid = DashboardErr{
|
||||||
|
Reason: "uid contains illegal characters",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardUidTooLong = DashboardErr{
|
||||||
|
Reason: "uid too long, max 40 characters",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardCannotSaveProvisionedDashboard = DashboardErr{
|
||||||
|
Reason: "Cannot save provisioned dashboard",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardRefreshIntervalTooShort = DashboardErr{
|
||||||
|
Reason: "Dashboard refresh interval is too low",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardCannotDeleteProvisionedDashboard = DashboardErr{
|
||||||
|
Reason: "provisioned dashboard cannot be deleted",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardIdentifierNotSet = DashboardErr{
|
||||||
|
Reason: "Unique identifier needed to be able to get a dashboard",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardIdentifierInvalid = DashboardErr{
|
||||||
|
Reason: "Dashboard ID not a number",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardPanelIdentifierInvalid = DashboardErr{
|
||||||
|
Reason: "Dashboard panel ID not a number",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrDashboardOrPanelIdentifierNotSet = DashboardErr{
|
||||||
|
Reason: "Unique identifier needed to be able to get a dashboard panel",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
ErrProvisionedDashboardNotFound = DashboardErr{
|
||||||
|
Reason: "Dashboard is not provisioned",
|
||||||
|
StatusCode: 404,
|
||||||
|
Status: "not-found",
|
||||||
|
}
|
||||||
|
ErrDashboardThumbnailNotFound = DashboardErr{
|
||||||
|
Reason: "Dashboard thumbnail not found",
|
||||||
|
StatusCode: 404,
|
||||||
|
Status: "not-found",
|
||||||
|
}
|
||||||
|
ErrPublicDashboardFailedGenerateUniqueUid = DashboardErr{
|
||||||
|
Reason: "Failed to generate unique public dashboard id",
|
||||||
|
StatusCode: 500,
|
||||||
|
}
|
||||||
|
ErrPublicDashboardFailedGenerateAccesstoken = DashboardErr{
|
||||||
|
Reason: "Failed to public dashboard access token",
|
||||||
|
StatusCode: 500,
|
||||||
|
}
|
||||||
|
ErrPublicDashboardNotFound = DashboardErr{
|
||||||
|
Reason: "Public dashboard not found",
|
||||||
|
StatusCode: 404,
|
||||||
|
Status: "not-found",
|
||||||
|
}
|
||||||
|
ErrPublicDashboardPanelNotFound = DashboardErr{
|
||||||
|
Reason: "Panel not found in dashboard",
|
||||||
|
StatusCode: 404,
|
||||||
|
Status: "not-found",
|
||||||
|
}
|
||||||
|
ErrPublicDashboardIdentifierNotSet = DashboardErr{
|
||||||
|
Reason: "No Uid for public dashboard specified",
|
||||||
|
StatusCode: 400,
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrFolderNotFound = errors.New("folder not found")
|
||||||
|
ErrFolderVersionMismatch = errors.New("the folder has been changed by someone else")
|
||||||
|
ErrFolderTitleEmpty = errors.New("folder title cannot be empty")
|
||||||
|
ErrFolderWithSameUIDExists = errors.New("a folder/dashboard with the same uid already exists")
|
||||||
|
ErrFolderInvalidUID = errors.New("invalid uid for folder provided")
|
||||||
|
ErrFolderSameNameExists = errors.New("a folder or dashboard in the general folder with the same name already exists")
|
||||||
|
ErrFolderFailedGenerateUniqueUid = errors.New("failed to generate unique folder ID")
|
||||||
|
ErrFolderAccessDenied = errors.New("access denied to folder")
|
||||||
|
ErrFolderContainsAlertRules = errors.New("folder contains alert rules")
|
||||||
|
)
|
||||||
|
|
||||||
|
// DashboardErr represents a dashboard error.
|
||||||
|
type DashboardErr struct {
|
||||||
|
StatusCode int
|
||||||
|
Status string
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns whether equal to another DashboardErr.
|
||||||
|
func (e DashboardErr) Equal(o DashboardErr) bool {
|
||||||
|
return o.StatusCode == e.StatusCode && o.Status == e.Status && o.Reason == e.Reason
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the error message.
|
||||||
|
func (e DashboardErr) Error() string {
|
||||||
|
if e.Reason != "" {
|
||||||
|
return e.Reason
|
||||||
|
}
|
||||||
|
return "Dashboard Error"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body returns the error's response body, if applicable.
|
||||||
|
func (e DashboardErr) Body() util.DynMap {
|
||||||
|
if e.Status == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.DynMap{"status": e.Status, "message": e.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdatePluginDashboardError struct {
|
||||||
|
PluginId string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d UpdatePluginDashboardError) Error() string {
|
||||||
|
return "Dashboard belongs to plugin"
|
||||||
|
}
|
@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
@ -21,11 +22,11 @@ func (dr *DashboardServiceImpl) GetPublicDashboard(ctx context.Context, accessTo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pubdash == nil || d == nil {
|
if pubdash == nil || d == nil {
|
||||||
return nil, models.ErrPublicDashboardNotFound
|
return nil, dashboards.ErrPublicDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pubdash.IsEnabled {
|
if !pubdash.IsEnabled {
|
||||||
return nil, models.ErrPublicDashboardNotFound
|
return nil, dashboards.ErrPublicDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
ts := pubdash.BuildTimeSettings(d)
|
ts := pubdash.BuildTimeSettings(d)
|
||||||
@ -49,7 +50,7 @@ func (dr *DashboardServiceImpl) GetPublicDashboardConfig(ctx context.Context, or
|
|||||||
// to the database. It handles validations for sharing config and persistence
|
// to the database. It handles validations for sharing config and persistence
|
||||||
func (dr *DashboardServiceImpl) SavePublicDashboardConfig(ctx context.Context, dto *dashboards.SavePublicDashboardConfigDTO) (*models.PublicDashboard, error) {
|
func (dr *DashboardServiceImpl) SavePublicDashboardConfig(ctx context.Context, dto *dashboards.SavePublicDashboardConfigDTO) (*models.PublicDashboard, error) {
|
||||||
if len(dto.DashboardUid) == 0 {
|
if len(dto.DashboardUid) == 0 {
|
||||||
return nil, models.ErrDashboardIdentifierNotSet
|
return nil, dashboards.ErrDashboardIdentifierNotSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// set default value for time settings
|
// set default value for time settings
|
||||||
@ -125,13 +126,13 @@ func (dr *DashboardServiceImpl) updatePublicDashboardConfig(ctx context.Context,
|
|||||||
// dashboard and returns a metrics request to be sent to query backend
|
// dashboard and returns a metrics request to be sent to query backend
|
||||||
func (dr *DashboardServiceImpl) BuildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *models.PublicDashboard, panelId int64) (dtos.MetricRequest, error) {
|
func (dr *DashboardServiceImpl) BuildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *models.PublicDashboard, panelId int64) (dtos.MetricRequest, error) {
|
||||||
if !publicDashboard.IsEnabled {
|
if !publicDashboard.IsEnabled {
|
||||||
return dtos.MetricRequest{}, models.ErrPublicDashboardNotFound
|
return dtos.MetricRequest{}, dashboards.ErrPublicDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
queriesByPanel := models.GetQueriesFromDashboard(dashboard.Data)
|
queriesByPanel := models.GetQueriesFromDashboard(dashboard.Data)
|
||||||
|
|
||||||
if _, ok := queriesByPanel[panelId]; !ok {
|
if _, ok := queriesByPanel[panelId]; !ok {
|
||||||
return dtos.MetricRequest{}, models.ErrPublicDashboardPanelNotFound
|
return dtos.MetricRequest{}, dashboards.ErrPublicDashboardPanelNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
ts := publicDashboard.BuildTimeSettings(dashboard)
|
ts := publicDashboard.BuildTimeSettings(dashboard)
|
||||||
|
@ -6,15 +6,16 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var timeSettings, _ = simplejson.NewJson([]byte(`{"from": "now-12", "to": "now"}`))
|
var timeSettings, _ = simplejson.NewJson([]byte(`{"from": "now-12", "to": "now"}`))
|
||||||
@ -66,21 +67,21 @@ func TestGetPublicDashboard(t *testing.T) {
|
|||||||
d: &models.Dashboard{Uid: "mydashboard"},
|
d: &models.Dashboard{Uid: "mydashboard"},
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
ErrResp: models.ErrPublicDashboardNotFound,
|
ErrResp: dashboards.ErrPublicDashboardNotFound,
|
||||||
DashResp: nil,
|
DashResp: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "returns ErrPublicDashboardNotFound if PublicDashboard missing",
|
Name: "returns ErrPublicDashboardNotFound if PublicDashboard missing",
|
||||||
AccessToken: "abc123",
|
AccessToken: "abc123",
|
||||||
StoreResp: &storeResp{pd: nil, d: nil, err: nil},
|
StoreResp: &storeResp{pd: nil, d: nil, err: nil},
|
||||||
ErrResp: models.ErrPublicDashboardNotFound,
|
ErrResp: dashboards.ErrPublicDashboardNotFound,
|
||||||
DashResp: nil,
|
DashResp: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "returns ErrPublicDashboardNotFound if Dashboard missing",
|
Name: "returns ErrPublicDashboardNotFound if Dashboard missing",
|
||||||
AccessToken: "abc123",
|
AccessToken: "abc123",
|
||||||
StoreResp: &storeResp{pd: nil, d: nil, err: nil},
|
StoreResp: &storeResp{pd: nil, d: nil, err: nil},
|
||||||
ErrResp: models.ErrPublicDashboardNotFound,
|
ErrResp: dashboards.ErrPublicDashboardNotFound,
|
||||||
DashResp: nil,
|
DashResp: nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -83,21 +83,21 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
|||||||
dash.SetUid(strings.TrimSpace(dash.Uid))
|
dash.SetUid(strings.TrimSpace(dash.Uid))
|
||||||
|
|
||||||
if dash.Title == "" {
|
if dash.Title == "" {
|
||||||
return nil, models.ErrDashboardTitleEmpty
|
return nil, dashboards.ErrDashboardTitleEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
if dash.IsFolder && dash.FolderId > 0 {
|
if dash.IsFolder && dash.FolderId > 0 {
|
||||||
return nil, models.ErrDashboardFolderCannotHaveParent
|
return nil, dashboards.ErrDashboardFolderCannotHaveParent
|
||||||
}
|
}
|
||||||
|
|
||||||
if dash.IsFolder && strings.EqualFold(dash.Title, models.RootFolderName) {
|
if dash.IsFolder && strings.EqualFold(dash.Title, models.RootFolderName) {
|
||||||
return nil, models.ErrDashboardFolderNameExists
|
return nil, dashboards.ErrDashboardFolderNameExists
|
||||||
}
|
}
|
||||||
|
|
||||||
if !util.IsValidShortUID(dash.Uid) {
|
if !util.IsValidShortUID(dash.Uid) {
|
||||||
return nil, models.ErrDashboardInvalidUid
|
return nil, dashboards.ErrDashboardInvalidUid
|
||||||
} else if util.IsShortUIDTooLong(dash.Uid) {
|
} else if util.IsShortUIDTooLong(dash.Uid) {
|
||||||
return nil, models.ErrDashboardUidTooLong
|
return nil, dashboards.ErrDashboardUidTooLong
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateDashboardRefreshInterval(dash); err != nil {
|
if err := validateDashboardRefreshInterval(dash); err != nil {
|
||||||
@ -123,7 +123,7 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil, models.ErrDashboardUpdateAccessDenied
|
return nil, dashboards.ErrDashboardUpdateAccessDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
|||||||
}
|
}
|
||||||
|
|
||||||
if provisionedData != nil {
|
if provisionedData != nil {
|
||||||
return nil, models.ErrDashboardCannotSaveProvisionedDashboard
|
return nil, dashboards.ErrDashboardCannotSaveProvisionedDashboard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,14 +144,14 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil, models.ErrDashboardUpdateAccessDenied
|
return nil, dashboards.ErrDashboardUpdateAccessDenied
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if canSave, err := guard.CanSave(); err != nil || !canSave {
|
if canSave, err := guard.CanSave(); err != nil || !canSave {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil, models.ErrDashboardUpdateAccessDenied
|
return nil, dashboards.ErrDashboardUpdateAccessDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +202,7 @@ func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if d < minRefreshInterval {
|
if d < minRefreshInterval {
|
||||||
return models.ErrDashboardRefreshIntervalTooShort
|
return dashboards.ErrDashboardRefreshIntervalTooShort
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -414,7 +414,7 @@ func (dr *DashboardServiceImpl) deleteDashboard(ctx context.Context, dashboardId
|
|||||||
}
|
}
|
||||||
|
|
||||||
if provisionedData != nil {
|
if provisionedData != nil {
|
||||||
return models.ErrDashboardCannotDeleteProvisionedDashboard
|
return dashboards.ErrDashboardCannotDeleteProvisionedDashboard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd := &models.DeleteDashboardCommand{OrgId: orgId, Id: dashboardId}
|
cmd := &models.DeleteDashboardCommand{OrgId: orgId, Id: dashboardId}
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
dashbboardservice "github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
@ -39,7 +39,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardNotFound, err)
|
assert.Equal(t, dashboards.ErrDashboardNotFound, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Given other organization
|
// Given other organization
|
||||||
@ -59,7 +59,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardNotFound, err)
|
assert.Equal(t, dashboards.ErrDashboardNotFound, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When creating a dashboard with same uid as dashboard in organization A, it should create a new dashboard in org B",
|
permissionScenario(t, "When creating a dashboard with same uid as dashboard in organization A, it should create a new dashboard in org B",
|
||||||
@ -101,7 +101,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sqlStore)
|
err := callSaveWithError(cmd, sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -121,7 +121,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -141,7 +141,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -162,7 +162,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -183,7 +183,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -204,7 +204,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -225,7 +225,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -246,7 +246,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -267,7 +267,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -288,7 +288,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
@ -429,7 +429,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardFolderNotFound, err)
|
assert.Equal(t, dashboards.ErrDashboardFolderNotFound, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When updating an existing dashboard by id without current version", canSave,
|
permissionScenario(t, "When updating an existing dashboard by id without current version", canSave,
|
||||||
@ -445,7 +445,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardVersionMismatch, err)
|
assert.Equal(t, dashboards.ErrDashboardVersionMismatch, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When updating an existing dashboard by id with current version", canSave,
|
permissionScenario(t, "When updating an existing dashboard by id with current version", canSave,
|
||||||
@ -485,7 +485,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardVersionMismatch, err)
|
assert.Equal(t, dashboards.ErrDashboardVersionMismatch, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When updating an existing dashboard by uid with current version", canSave,
|
permissionScenario(t, "When updating an existing dashboard by uid with current version", canSave,
|
||||||
@ -524,7 +524,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardWithSameNameInFolderExists, err)
|
assert.Equal(t, dashboards.ErrDashboardWithSameNameInFolderExists, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When creating a dashboard with same name as dashboard in General folder",
|
permissionScenario(t, "When creating a dashboard with same name as dashboard in General folder",
|
||||||
@ -540,7 +540,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardWithSameNameInFolderExists, err)
|
assert.Equal(t, dashboards.ErrDashboardWithSameNameInFolderExists, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When creating a folder with same name as existing folder", canSave,
|
permissionScenario(t, "When creating a folder with same name as existing folder", canSave,
|
||||||
@ -556,7 +556,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardWithSameNameInFolderExists, err)
|
assert.Equal(t, dashboards.ErrDashboardWithSameNameInFolderExists, err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -644,7 +644,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardWithSameUIDExists, err)
|
assert.Equal(t, dashboards.ErrDashboardWithSameUIDExists, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When creating a dashboard with same name as dashboard in other folder", canSave,
|
permissionScenario(t, "When creating a dashboard with same name as dashboard in other folder", canSave,
|
||||||
@ -708,7 +708,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When updating existing dashboard to a folder using id", canSave,
|
permissionScenario(t, "When updating existing dashboard to a folder using id", canSave,
|
||||||
@ -724,7 +724,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When updating existing folder to a dashboard using uid", canSave,
|
permissionScenario(t, "When updating existing folder to a dashboard using uid", canSave,
|
||||||
@ -740,7 +740,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When updating existing dashboard to a folder using uid", canSave,
|
permissionScenario(t, "When updating existing dashboard to a folder using uid", canSave,
|
||||||
@ -756,7 +756,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When updating existing folder to a dashboard using title", canSave,
|
permissionScenario(t, "When updating existing folder to a dashboard using title", canSave,
|
||||||
@ -771,7 +771,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardWithSameNameAsFolder, err)
|
assert.Equal(t, dashboards.ErrDashboardWithSameNameAsFolder, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario(t, "When updating existing dashboard to a folder using title", canSave,
|
permissionScenario(t, "When updating existing dashboard to a folder using title", canSave,
|
||||||
@ -786,7 +786,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := callSaveWithError(cmd, sc.sqlStore)
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
assert.Equal(t, models.ErrDashboardFolderWithSameNameAsDashboard, err)
|
assert.Equal(t, dashboards.ErrDashboardFolderWithSameNameAsDashboard, err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -796,7 +796,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
type permissionScenarioContext struct {
|
type permissionScenarioContext struct {
|
||||||
dashboardGuardianMock *guardian.FakeDashboardGuardian
|
dashboardGuardianMock *guardian.FakeDashboardGuardian
|
||||||
sqlStore *sqlstore.SQLStore
|
sqlStore *sqlstore.SQLStore
|
||||||
dashboardStore dashbboardservice.Store
|
dashboardStore dashboards.Store
|
||||||
savedFolder *models.Dashboard
|
savedFolder *models.Dashboard
|
||||||
savedDashInFolder *models.Dashboard
|
savedDashInFolder *models.Dashboard
|
||||||
otherSavedFolder *models.Dashboard
|
otherSavedFolder *models.Dashboard
|
||||||
@ -913,7 +913,7 @@ func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlSto
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
dto := dashbboardservice.SaveDashboardDTO{
|
dto := dashboards.SaveDashboardDTO{
|
||||||
OrgId: orgID,
|
OrgId: orgID,
|
||||||
Dashboard: cmd.GetDashboardModel(),
|
Dashboard: cmd.GetDashboardModel(),
|
||||||
User: &models.SignedInUser{
|
User: &models.SignedInUser{
|
||||||
@ -950,7 +950,7 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore *sqlstore.
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
dto := dashbboardservice.SaveDashboardDTO{
|
dto := dashboards.SaveDashboardDTO{
|
||||||
OrgId: orgID,
|
OrgId: orgID,
|
||||||
Dashboard: cmd.GetDashboardModel(),
|
Dashboard: cmd.GetDashboardModel(),
|
||||||
User: &models.SignedInUser{
|
User: &models.SignedInUser{
|
||||||
@ -975,10 +975,10 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore *sqlstore.
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func toSaveDashboardDto(cmd models.SaveDashboardCommand) dashbboardservice.SaveDashboardDTO {
|
func toSaveDashboardDto(cmd models.SaveDashboardCommand) dashboards.SaveDashboardDTO {
|
||||||
dash := (&cmd).GetDashboardModel()
|
dash := (&cmd).GetDashboardModel()
|
||||||
|
|
||||||
return dashbboardservice.SaveDashboardDTO{
|
return dashboards.SaveDashboardDTO{
|
||||||
Dashboard: dash,
|
Dashboard: dash,
|
||||||
Message: cmd.Message,
|
Message: cmd.Message,
|
||||||
OrgId: cmd.OrgId,
|
OrgId: cmd.OrgId,
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
m "github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
@ -21,7 +21,7 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
}
|
}
|
||||||
t.Run("Dashboard service tests", func(t *testing.T) {
|
t.Run("Dashboard service tests", func(t *testing.T) {
|
||||||
fakeStore := m.FakeDashboardStore{}
|
fakeStore := dashboards.FakeDashboardStore{}
|
||||||
defer fakeStore.AssertExpectations(t)
|
defer fakeStore.AssertExpectations(t)
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
log: log.New("test.logger"),
|
log: log.New("test.logger"),
|
||||||
@ -34,7 +34,7 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
||||||
|
|
||||||
t.Run("Save dashboard validation", func(t *testing.T) {
|
t.Run("Save dashboard validation", func(t *testing.T) {
|
||||||
dto := &m.SaveDashboardDTO{}
|
dto := &dashboards.SaveDashboardDTO{}
|
||||||
|
|
||||||
t.Run("When saving a dashboard with empty title it should return error", func(t *testing.T) {
|
t.Run("When saving a dashboard with empty title it should return error", func(t *testing.T) {
|
||||||
titles := []string{"", " ", " \t "}
|
titles := []string{"", " ", " \t "}
|
||||||
@ -42,7 +42,7 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
for _, title := range titles {
|
for _, title := range titles {
|
||||||
dto.Dashboard = models.NewDashboard(title)
|
dto.Dashboard = models.NewDashboard(title)
|
||||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
||||||
require.Equal(t, err, models.ErrDashboardTitleEmpty)
|
require.Equal(t, err, dashboards.ErrDashboardTitleEmpty)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -50,13 +50,13 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
dto.Dashboard = models.NewDashboardFolder("Folder")
|
dto.Dashboard = models.NewDashboardFolder("Folder")
|
||||||
dto.Dashboard.FolderId = 1
|
dto.Dashboard.FolderId = 1
|
||||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
||||||
require.Equal(t, err, models.ErrDashboardFolderCannotHaveParent)
|
require.Equal(t, err, dashboards.ErrDashboardFolderCannotHaveParent)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should return validation error if folder is named General", func(t *testing.T) {
|
t.Run("Should return validation error if folder is named General", func(t *testing.T) {
|
||||||
dto.Dashboard = models.NewDashboardFolder("General")
|
dto.Dashboard = models.NewDashboardFolder("General")
|
||||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
||||||
require.Equal(t, err, models.ErrDashboardFolderNameExists)
|
require.Equal(t, err, dashboards.ErrDashboardFolderNameExists)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When saving a dashboard should validate uid", func(t *testing.T) {
|
t.Run("When saving a dashboard should validate uid", func(t *testing.T) {
|
||||||
@ -68,9 +68,9 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
{Uid: " ", Error: nil},
|
{Uid: " ", Error: nil},
|
||||||
{Uid: " \t ", Error: nil},
|
{Uid: " \t ", Error: nil},
|
||||||
{Uid: "asdf90_-", Error: nil},
|
{Uid: "asdf90_-", Error: nil},
|
||||||
{Uid: "asdf/90", Error: models.ErrDashboardInvalidUid},
|
{Uid: "asdf/90", Error: dashboards.ErrDashboardInvalidUid},
|
||||||
{Uid: " asdfghjklqwertyuiopzxcvbnmasdfghjklqwer ", Error: nil},
|
{Uid: " asdfghjklqwertyuiopzxcvbnmasdfghjklqwer ", Error: nil},
|
||||||
{Uid: "asdfghjklqwertyuiopzxcvbnmasdfghjklqwertyuiopzxcvbnmasdfghjklqwertyuiopzxcvbnm", Error: models.ErrDashboardUidTooLong},
|
{Uid: "asdfghjklqwertyuiopzxcvbnmasdfghjklqwertyuiopzxcvbnmasdfghjklqwertyuiopzxcvbnm", Error: dashboards.ErrDashboardUidTooLong},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@ -94,7 +94,7 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
dto.Dashboard.SetId(3)
|
dto.Dashboard.SetId(3)
|
||||||
dto.User = &models.SignedInUser{UserId: 1}
|
dto.User = &models.SignedInUser{UserId: 1}
|
||||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
||||||
require.Equal(t, err, models.ErrDashboardCannotSaveProvisionedDashboard)
|
require.Equal(t, err, dashboards.ErrDashboardCannotSaveProvisionedDashboard)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should not return validation error if dashboard is provisioned but UI updates allowed", func(t *testing.T) {
|
t.Run("Should not return validation error if dashboard is provisioned but UI updates allowed", func(t *testing.T) {
|
||||||
@ -123,7 +123,7 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Save provisioned dashboard validation", func(t *testing.T) {
|
t.Run("Save provisioned dashboard validation", func(t *testing.T) {
|
||||||
dto := &m.SaveDashboardDTO{}
|
dto := &dashboards.SaveDashboardDTO{}
|
||||||
|
|
||||||
t.Run("Should not return validation error if dashboard is provisioned", func(t *testing.T) {
|
t.Run("Should not return validation error if dashboard is provisioned", func(t *testing.T) {
|
||||||
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything).Return(true, nil).Once()
|
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything).Return(true, nil).Once()
|
||||||
@ -157,7 +157,7 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Import dashboard validation", func(t *testing.T) {
|
t.Run("Import dashboard validation", func(t *testing.T) {
|
||||||
dto := &m.SaveDashboardDTO{}
|
dto := &dashboards.SaveDashboardDTO{}
|
||||||
|
|
||||||
t.Run("Should return validation error if dashboard is provisioned", func(t *testing.T) {
|
t.Run("Should return validation error if dashboard is provisioned", func(t *testing.T) {
|
||||||
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything).Return(true, nil).Once()
|
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything).Return(true, nil).Once()
|
||||||
@ -167,7 +167,7 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
dto.Dashboard.SetId(3)
|
dto.Dashboard.SetId(3)
|
||||||
dto.User = &models.SignedInUser{UserId: 1}
|
dto.User = &models.SignedInUser{UserId: 1}
|
||||||
_, err := service.ImportDashboard(context.Background(), dto)
|
_, err := service.ImportDashboard(context.Background(), dto)
|
||||||
require.Equal(t, err, models.ErrDashboardCannotSaveProvisionedDashboard)
|
require.Equal(t, err, dashboards.ErrDashboardCannotSaveProvisionedDashboard)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ func TestIntegrationDashboardService(t *testing.T) {
|
|||||||
t.Run("DeleteDashboard should fail to delete it when provisioning information is missing", func(t *testing.T) {
|
t.Run("DeleteDashboard should fail to delete it when provisioning information is missing", func(t *testing.T) {
|
||||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything).Return(&models.DashboardProvisioning{}, nil).Once()
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything).Return(&models.DashboardProvisioning{}, nil).Once()
|
||||||
err := service.DeleteDashboard(context.Background(), 1, 1)
|
err := service.DeleteDashboard(context.Background(), 1, 1)
|
||||||
require.Equal(t, err, models.ErrDashboardCannotDeleteProvisionedDashboard)
|
require.Equal(t, err, dashboards.ErrDashboardCannotDeleteProvisionedDashboard)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ func (f *FolderServiceImpl) GetFolderByID(ctx context.Context, user *models.Sign
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
return nil, models.ErrFolderAccessDenied
|
return nil, dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
return dashFolder, nil
|
return dashFolder, nil
|
||||||
@ -111,7 +111,7 @@ func (f *FolderServiceImpl) GetFolderByUID(ctx context.Context, user *models.Sig
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
return nil, models.ErrFolderAccessDenied
|
return nil, dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
return dashFolder, nil
|
return dashFolder, nil
|
||||||
@ -128,7 +128,7 @@ func (f *FolderServiceImpl) GetFolderByTitle(ctx context.Context, user *models.S
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
return nil, models.ErrFolderAccessDenied
|
return nil, dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
return dashFolder, nil
|
return dashFolder, nil
|
||||||
@ -140,7 +140,7 @@ func (f *FolderServiceImpl) CreateFolder(ctx context.Context, user *models.Signe
|
|||||||
|
|
||||||
trimmedUID := strings.TrimSpace(uid)
|
trimmedUID := strings.TrimSpace(uid)
|
||||||
if trimmedUID == accesscontrol.GeneralFolderUID {
|
if trimmedUID == accesscontrol.GeneralFolderUID {
|
||||||
return nil, models.ErrFolderInvalidUID
|
return nil, dashboards.ErrFolderInvalidUID
|
||||||
}
|
}
|
||||||
|
|
||||||
dashFolder.SetUid(trimmedUID)
|
dashFolder.SetUid(trimmedUID)
|
||||||
@ -201,7 +201,7 @@ func (f *FolderServiceImpl) UpdateFolder(ctx context.Context, user *models.Signe
|
|||||||
dashFolder := query.Result
|
dashFolder := query.Result
|
||||||
|
|
||||||
if !dashFolder.IsFolder {
|
if !dashFolder.IsFolder {
|
||||||
return models.ErrFolderNotFound
|
return dashboards.ErrFolderNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.UpdateDashboardModel(dashFolder, orgID, user.UserId)
|
cmd.UpdateDashboardModel(dashFolder, orgID, user.UserId)
|
||||||
@ -254,7 +254,7 @@ func (f *FolderServiceImpl) DeleteFolder(ctx context.Context, user *models.Signe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
return nil, models.ErrFolderAccessDenied
|
return nil, dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteCmd := models.DeleteDashboardCommand{OrgId: orgID, Id: dashFolder.Id, ForceDeleteFolderRules: forceDeleteRules}
|
deleteCmd := models.DeleteDashboardCommand{OrgId: orgID, Id: dashFolder.Id, ForceDeleteFolderRules: forceDeleteRules}
|
||||||
@ -271,32 +271,32 @@ func (f *FolderServiceImpl) MakeUserAdmin(ctx context.Context, orgID int64, user
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toFolderError(err error) error {
|
func toFolderError(err error) error {
|
||||||
if errors.Is(err, models.ErrDashboardTitleEmpty) {
|
if errors.Is(err, dashboards.ErrDashboardTitleEmpty) {
|
||||||
return models.ErrFolderTitleEmpty
|
return dashboards.ErrFolderTitleEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrDashboardUpdateAccessDenied) {
|
if errors.Is(err, dashboards.ErrDashboardUpdateAccessDenied) {
|
||||||
return models.ErrFolderAccessDenied
|
return dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrDashboardWithSameNameInFolderExists) {
|
if errors.Is(err, dashboards.ErrDashboardWithSameNameInFolderExists) {
|
||||||
return models.ErrFolderSameNameExists
|
return dashboards.ErrFolderSameNameExists
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrDashboardWithSameUIDExists) {
|
if errors.Is(err, dashboards.ErrDashboardWithSameUIDExists) {
|
||||||
return models.ErrFolderWithSameUIDExists
|
return dashboards.ErrFolderWithSameUIDExists
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrDashboardVersionMismatch) {
|
if errors.Is(err, dashboards.ErrDashboardVersionMismatch) {
|
||||||
return models.ErrFolderVersionMismatch
|
return dashboards.ErrFolderVersionMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrDashboardNotFound) {
|
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||||
return models.ErrFolderNotFound
|
return dashboards.ErrFolderNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.Is(err, models.ErrDashboardFailedGenerateUniqueUid) {
|
if errors.Is(err, dashboards.ErrDashboardFailedGenerateUniqueUid) {
|
||||||
err = models.ErrFolderFailedGenerateUniqueUid
|
err = dashboards.ErrFolderFailedGenerateUniqueUid
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -77,7 +77,7 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("When get folder by id should return access denied error", func(t *testing.T) {
|
t.Run("When get folder by id should return access denied error", func(t *testing.T) {
|
||||||
_, err := service.GetFolderByID(context.Background(), user, folderId, orgID)
|
_, err := service.GetFolderByID(context.Background(), user, folderId, orgID)
|
||||||
require.Equal(t, err, models.ErrFolderAccessDenied)
|
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When get folder by id, with id = 0 should return default folder", func(t *testing.T) {
|
t.Run("When get folder by id, with id = 0 should return default folder", func(t *testing.T) {
|
||||||
@ -88,13 +88,13 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("When get folder by uid should return access denied error", func(t *testing.T) {
|
t.Run("When get folder by uid should return access denied error", func(t *testing.T) {
|
||||||
_, err := service.GetFolderByUID(context.Background(), user, orgID, folderUID)
|
_, err := service.GetFolderByUID(context.Background(), user, orgID, folderUID)
|
||||||
require.Equal(t, err, models.ErrFolderAccessDenied)
|
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When creating folder should return access denied error", func(t *testing.T) {
|
t.Run("When creating folder should return access denied error", func(t *testing.T) {
|
||||||
store.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything).Return(true, nil).Times(2)
|
store.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything).Return(true, nil).Times(2)
|
||||||
_, err := service.CreateFolder(context.Background(), user, orgID, folder.Title, folderUID)
|
_, err := service.CreateFolder(context.Background(), user, orgID, folder.Title, folderUID)
|
||||||
require.Equal(t, err, models.ErrFolderAccessDenied)
|
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When updating folder should return access denied error", func(t *testing.T) {
|
t.Run("When updating folder should return access denied error", func(t *testing.T) {
|
||||||
@ -107,13 +107,13 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
Uid: folderUID,
|
Uid: folderUID,
|
||||||
Title: "Folder-TEST",
|
Title: "Folder-TEST",
|
||||||
})
|
})
|
||||||
require.Equal(t, err, models.ErrFolderAccessDenied)
|
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When deleting folder by uid should return access denied error", func(t *testing.T) {
|
t.Run("When deleting folder by uid should return access denied error", func(t *testing.T) {
|
||||||
_, err := service.DeleteFolder(context.Background(), user, orgID, folderUID, false)
|
_, err := service.DeleteFolder(context.Background(), user, orgID, folderUID, false)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, err, models.ErrFolderAccessDenied)
|
require.Equal(t, err, dashboards.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
@ -144,7 +144,7 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
dash.Id = rand.Int63()
|
dash.Id = rand.Int63()
|
||||||
|
|
||||||
_, err := service.CreateFolder(context.Background(), user, orgID, dash.Title, "general")
|
_, err := service.CreateFolder(context.Background(), user, orgID, dash.Title, "general")
|
||||||
require.ErrorIs(t, err, models.ErrFolderInvalidUID)
|
require.ErrorIs(t, err, dashboards.ErrFolderInvalidUID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When updating folder should not return access denied error", func(t *testing.T) {
|
t.Run("When updating folder should not return access denied error", func(t *testing.T) {
|
||||||
@ -238,14 +238,14 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
ActualError error
|
ActualError error
|
||||||
ExpectedError error
|
ExpectedError error
|
||||||
}{
|
}{
|
||||||
{ActualError: models.ErrDashboardTitleEmpty, ExpectedError: models.ErrFolderTitleEmpty},
|
{ActualError: dashboards.ErrDashboardTitleEmpty, ExpectedError: dashboards.ErrFolderTitleEmpty},
|
||||||
{ActualError: models.ErrDashboardUpdateAccessDenied, ExpectedError: models.ErrFolderAccessDenied},
|
{ActualError: dashboards.ErrDashboardUpdateAccessDenied, ExpectedError: dashboards.ErrFolderAccessDenied},
|
||||||
{ActualError: models.ErrDashboardWithSameNameInFolderExists, ExpectedError: models.ErrFolderSameNameExists},
|
{ActualError: dashboards.ErrDashboardWithSameNameInFolderExists, ExpectedError: dashboards.ErrFolderSameNameExists},
|
||||||
{ActualError: models.ErrDashboardWithSameUIDExists, ExpectedError: models.ErrFolderWithSameUIDExists},
|
{ActualError: dashboards.ErrDashboardWithSameUIDExists, ExpectedError: dashboards.ErrFolderWithSameUIDExists},
|
||||||
{ActualError: models.ErrDashboardVersionMismatch, ExpectedError: models.ErrFolderVersionMismatch},
|
{ActualError: dashboards.ErrDashboardVersionMismatch, ExpectedError: dashboards.ErrFolderVersionMismatch},
|
||||||
{ActualError: models.ErrDashboardNotFound, ExpectedError: models.ErrFolderNotFound},
|
{ActualError: dashboards.ErrDashboardNotFound, ExpectedError: dashboards.ErrFolderNotFound},
|
||||||
{ActualError: models.ErrDashboardFailedGenerateUniqueUid, ExpectedError: models.ErrFolderFailedGenerateUniqueUid},
|
{ActualError: dashboards.ErrDashboardFailedGenerateUniqueUid, ExpectedError: dashboards.ErrFolderFailedGenerateUniqueUid},
|
||||||
{ActualError: models.ErrDashboardInvalidUid, ExpectedError: models.ErrDashboardInvalidUid},
|
{ActualError: dashboards.ErrDashboardInvalidUid, ExpectedError: dashboards.ErrDashboardInvalidUid},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
@ -91,7 +91,7 @@ func (d *DashboardSnapshotStore) GetDashboardSnapshot(ctx context.Context, query
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return models.ErrDashboardSnapshotNotFound
|
return dashboardsnapshots.ErrDashboardSnapshotNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
query.Result = &snapshot
|
query.Result = &snapshot
|
||||||
|
8
pkg/services/dashboardsnapshots/errors.go
Normal file
8
pkg/services/dashboardsnapshots/errors.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package dashboardsnapshots
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
|
||||||
|
var ErrDashboardSnapshotNotFound = dashboards.DashboardErr{
|
||||||
|
Reason: "Dashboard snapshot not found",
|
||||||
|
StatusCode: 404,
|
||||||
|
}
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@ -134,7 +135,7 @@ func getDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, dashboard *models.D
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard.SetId(dashboard.Id)
|
dashboard.SetId(dashboard.Id)
|
||||||
@ -188,7 +189,7 @@ func insertTestDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, title string
|
|||||||
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if affectedRows == 0 {
|
} else if affectedRows == 0 {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -250,7 +251,7 @@ func updateTestDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, dashboard *m
|
|||||||
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if affectedRows == 0 {
|
} else if affectedRows == 0 {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/middleware"
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -173,11 +174,11 @@ func toLibraryElementError(err error, message string) response.Response {
|
|||||||
if errors.Is(err, errLibraryElementVersionMismatch) {
|
if errors.Is(err, errLibraryElementVersionMismatch) {
|
||||||
return response.Error(412, errLibraryElementVersionMismatch.Error(), err)
|
return response.Error(412, errLibraryElementVersionMismatch.Error(), err)
|
||||||
}
|
}
|
||||||
if errors.Is(err, models.ErrFolderNotFound) {
|
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||||
return response.Error(404, models.ErrFolderNotFound.Error(), err)
|
return response.Error(404, dashboards.ErrFolderNotFound.Error(), err)
|
||||||
}
|
}
|
||||||
if errors.Is(err, models.ErrFolderAccessDenied) {
|
if errors.Is(err, dashboards.ErrFolderAccessDenied) {
|
||||||
return response.Error(403, models.ErrFolderAccessDenied.Error(), err)
|
return response.Error(403, dashboards.ErrFolderAccessDenied.Error(), err)
|
||||||
}
|
}
|
||||||
if errors.Is(err, errLibraryElementHasConnections) {
|
if errors.Is(err, errLibraryElementHasConnections) {
|
||||||
return response.Error(403, errLibraryElementHasConnections.Error(), err)
|
return response.Error(403, errLibraryElementHasConnections.Error(), err)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/search"
|
"github.com/grafana/grafana/pkg/services/search"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
@ -707,7 +708,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c context.Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(folderUIDs) == 0 {
|
if len(folderUIDs) == 0 {
|
||||||
return models.ErrFolderNotFound
|
return dashboards.ErrFolderNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(folderUIDs) != 1 {
|
if len(folderUIDs) != 1 {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ func (l *LibraryElementService) requireEditPermissionsOnFolder(ctx context.Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isGeneralFolder(folderID) && user.HasRole(models.ROLE_VIEWER) {
|
if isGeneralFolder(folderID) && user.HasRole(models.ROLE_VIEWER) {
|
||||||
return models.ErrFolderAccessDenied
|
return dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
folder, err := l.folderService.GetFolderByID(ctx, user, folderID, user.OrgId)
|
folder, err := l.folderService.GetFolderByID(ctx, user, folderID, user.OrgId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -43,7 +44,7 @@ func (l *LibraryElementService) requireEditPermissionsOnFolder(ctx context.Conte
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !canEdit {
|
if !canEdit {
|
||||||
return models.ErrFolderAccessDenied
|
return dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -66,7 +67,7 @@ func (l *LibraryElementService) requireViewPermissionsOnFolder(ctx context.Conte
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !canView {
|
if !canView {
|
||||||
return models.ErrFolderAccessDenied
|
return dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -80,7 +81,7 @@ func (l *LibraryElementService) requireEditPermissionsOnDashboard(ctx context.Co
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !canEdit {
|
if !canEdit {
|
||||||
return models.ErrDashboardUpdateAccessDenied
|
return dashboards.ErrDashboardUpdateAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -77,7 +77,7 @@ func TestDeleteLibraryPanelsInFolder(t *testing.T) {
|
|||||||
scenarioWithPanel(t, "When an admin tries to delete a folder uid that doesn't exist, it should fail",
|
scenarioWithPanel(t, "When an admin tries to delete a folder uid that doesn't exist, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
err := sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.Uid+"xxxx")
|
err := sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.Uid+"xxxx")
|
||||||
require.EqualError(t, err, models.ErrFolderNotFound.Error())
|
require.EqualError(t, err, dashboards.ErrFolderNotFound.Error())
|
||||||
})
|
})
|
||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too",
|
scenarioWithPanel(t, "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too",
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
@ -495,7 +496,7 @@ func toNamespaceErrorResponse(err error) response.Response {
|
|||||||
if errors.Is(err, ngmodels.ErrCannotEditNamespace) {
|
if errors.Is(err, ngmodels.ErrCannotEditNamespace) {
|
||||||
return ErrResp(http.StatusForbidden, err, err.Error())
|
return ErrResp(http.StatusForbidden, err, err.Error())
|
||||||
}
|
}
|
||||||
if errors.Is(err, models.ErrDashboardIdentifierNotSet) {
|
if errors.Is(err, dashboards.ErrDashboardIdentifierNotSet) {
|
||||||
return ErrResp(http.StatusBadRequest, err, err.Error())
|
return ErrResp(http.StatusBadRequest, err, err.Error())
|
||||||
}
|
}
|
||||||
return apierrors.ToFolderErrorResponse(err)
|
return apierrors.ToFolderErrorResponse(err)
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/imguploader"
|
"github.com/grafana/grafana/pkg/components/imguploader"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
@ -111,7 +110,7 @@ func (s *ScreenshotImageService) NewImage(ctx context.Context, r *ngmodels.Alert
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: Check for screenshot upload failures. These images should still be
|
// TODO: Check for screenshot upload failures. These images should still be
|
||||||
// stored because we have a local disk path that could be useful.
|
// stored because we have a local disk path that could be useful.
|
||||||
if errors.Is(err, models.ErrDashboardNotFound) {
|
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||||
return nil, ErrNoDashboard
|
return nil, ErrNoDashboard
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -302,12 +302,12 @@ func (fr *FileReader) getOrCreateFolderID(ctx context.Context, cfg *config, serv
|
|||||||
cmd := &models.GetDashboardQuery{Slug: models.SlugifyTitle(folderName), OrgId: cfg.OrgID}
|
cmd := &models.GetDashboardQuery{Slug: models.SlugifyTitle(folderName), OrgId: cfg.OrgID}
|
||||||
err := fr.dashboardStore.GetDashboard(ctx, cmd)
|
err := fr.dashboardStore.GetDashboard(ctx, cmd)
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, models.ErrDashboardNotFound) {
|
if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// dashboard folder not found. create one.
|
// dashboard folder not found. create one.
|
||||||
if errors.Is(err, models.ErrDashboardNotFound) {
|
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||||
dash := &dashboards.SaveDashboardDTO{}
|
dash := &dashboards.SaveDashboardDTO{}
|
||||||
dash.Dashboard = models.NewDashboardFolder(folderName)
|
dash.Dashboard = models.NewDashboardFolder(folderName)
|
||||||
dash.Dashboard.IsFolder = true
|
dash.Dashboard.IsFolder = true
|
||||||
@ -315,7 +315,7 @@ func (fr *FileReader) getOrCreateFolderID(ctx context.Context, cfg *config, serv
|
|||||||
dash.OrgId = cfg.OrgID
|
dash.OrgId = cfg.OrgID
|
||||||
// set dashboard folderUid if given
|
// set dashboard folderUid if given
|
||||||
if cfg.FolderUID == accesscontrol.GeneralFolderUID {
|
if cfg.FolderUID == accesscontrol.GeneralFolderUID {
|
||||||
return 0, models.ErrFolderInvalidUID
|
return 0, dashboards.ErrFolderInvalidUID
|
||||||
}
|
}
|
||||||
dash.Dashboard.SetUid(cfg.FolderUID)
|
dash.Dashboard.SetUid(cfg.FolderUID)
|
||||||
dbDash, err := service.SaveFolderForProvisionedDashboards(ctx, dash)
|
dbDash, err := service.SaveFolderForProvisionedDashboards(ctx, dash)
|
||||||
|
@ -8,13 +8,14 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -405,7 +406,7 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = r.getOrCreateFolderID(context.Background(), cfg, fakeService, cfg.Folder)
|
_, err = r.getOrCreateFolderID(context.Background(), cfg, fakeService, cfg.Folder)
|
||||||
require.ErrorIs(t, err, models.ErrFolderInvalidUID)
|
require.ErrorIs(t, err, dashboards.ErrFolderInvalidUID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Walking the folder with dashboards", func(t *testing.T) {
|
t.Run("Walking the folder with dashboards", func(t *testing.T) {
|
||||||
@ -513,5 +514,5 @@ func (ffi FakeFileInfo) Sys() interface{} {
|
|||||||
type fakeDashboardStore struct{}
|
type fakeDashboardStore struct{}
|
||||||
|
|
||||||
func (fds *fakeDashboardStore) GetDashboard(_ context.Context, _ *models.GetDashboardQuery) error {
|
func (fds *fakeDashboardStore) GetDashboard(_ context.Context, _ *models.GetDashboardQuery) error {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ func createDashboardJSON(data *simplejson.Json, lastModified time.Time, cfg *con
|
|||||||
dash.Dashboard.FolderId = folderID
|
dash.Dashboard.FolderId = folderID
|
||||||
|
|
||||||
if dash.Dashboard.Title == "" {
|
if dash.Dashboard.Title == "" {
|
||||||
return nil, models.ErrDashboardTitleEmpty
|
return nil, dashboards.ErrDashboardTitleEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
return dash, nil
|
return dash, nil
|
||||||
|
@ -79,7 +79,7 @@ func TestBrowserScreenshotService(t *testing.T) {
|
|||||||
s := NewBrowserScreenshotService(&d, r)
|
s := NewBrowserScreenshotService(&d, r)
|
||||||
|
|
||||||
// a non-existent dashboard should return error
|
// a non-existent dashboard should return error
|
||||||
d.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Return(models.ErrDashboardNotFound).Once()
|
d.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Return(dashboards.ErrDashboardNotFound).Once()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
opts := ScreenshotOptions{}
|
opts := ScreenshotOptions{}
|
||||||
screenshot, err := s.Take(ctx, opts)
|
screenshot, err := s.Take(ctx, opts)
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ss *SQLStore) GetThumbnail(ctx context.Context, query *models.GetDashboardThumbnailCommand) (*models.DashboardThumbnail, error) {
|
func (ss *SQLStore) GetThumbnail(ctx context.Context, query *models.GetDashboardThumbnailCommand) (*models.DashboardThumbnail, error) {
|
||||||
@ -25,7 +26,7 @@ func (ss *SQLStore) SaveThumbnail(ctx context.Context, cmd *models.SaveDashboard
|
|||||||
err := ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
err := ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||||
existing, err := findThumbnailByMeta(sess, cmd.DashboardThumbnailMeta)
|
existing, err := findThumbnailByMeta(sess, cmd.DashboardThumbnailMeta)
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, models.ErrDashboardThumbnailNotFound) {
|
if err != nil && !errors.Is(err, dashboards.ErrDashboardThumbnailNotFound) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ func findThumbnailByMeta(sess *DBSession, meta models.DashboardThumbnailMeta) (*
|
|||||||
exists, err := sess.Get(result)
|
exists, err := sess.Get(result)
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, models.ErrDashboardThumbnailNotFound
|
return nil, dashboards.ErrDashboardThumbnailNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -174,7 +175,7 @@ func findDashboardIdByThumbMeta(sess *DBSession, meta models.DashboardThumbnailM
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, models.ErrDashboardNotFound
|
return nil, dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, err
|
return result, err
|
||||||
|
@ -5,11 +5,13 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var theme = models.ThemeDark
|
var theme = models.ThemeDark
|
||||||
@ -313,7 +315,7 @@ func updateTestDashboard(t *testing.T, sqlStore *SQLStore, dashboard *models.Das
|
|||||||
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if affectedRows == 0 {
|
} else if affectedRows == 0 {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@ -151,7 +152,7 @@ func (m *folderHelper) generateNewDashboardUid(orgId int64) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", models.ErrDashboardFailedGenerateUniqueUid
|
return "", dashboards.ErrDashboardFailedGenerateUniqueUid
|
||||||
}
|
}
|
||||||
|
|
||||||
// based on SQLStore.UpdateDashboardACL()
|
// based on SQLStore.UpdateDashboardACL()
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@ -461,7 +462,7 @@ func insertTestDashboard(t *testing.T, sqlStore *SQLStore, title string, orgId i
|
|||||||
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
if affectedRows, err := sess.Insert(dashVersion); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if affectedRows == 0 {
|
} else if affectedRows == 0 {
|
||||||
return models.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -222,7 +222,7 @@ func (hs *thumbService) GetImage(c *models.ReqContext) {
|
|||||||
Kind: models.ThumbnailKindDefault,
|
Kind: models.ThumbnailKindDefault,
|
||||||
})
|
})
|
||||||
|
|
||||||
if errors.Is(err, models.ErrDashboardThumbnailNotFound) {
|
if errors.Is(err, dashboards.ErrDashboardThumbnailNotFound) {
|
||||||
c.Resp.WriteHeader(404)
|
c.Resp.WriteHeader(404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -12,15 +12,17 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDashboardQuota(t *testing.T) {
|
func TestDashboardQuota(t *testing.T) {
|
||||||
@ -173,19 +175,19 @@ providers:
|
|||||||
desc: "when updating provisioned dashboard using ID it should fail",
|
desc: "when updating provisioned dashboard using ID it should fail",
|
||||||
dashboardData: fmt.Sprintf(`{"title":"just testing", "id": %d, "version": 1}`, dashboardID),
|
dashboardData: fmt.Sprintf(`{"title":"just testing", "id": %d, "version": 1}`, dashboardID),
|
||||||
expStatus: http.StatusBadRequest,
|
expStatus: http.StatusBadRequest,
|
||||||
expErrReason: models.ErrDashboardCannotSaveProvisionedDashboard.Reason,
|
expErrReason: dashboards.ErrDashboardCannotSaveProvisionedDashboard.Reason,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "when updating provisioned dashboard using UID it should fail",
|
desc: "when updating provisioned dashboard using UID it should fail",
|
||||||
dashboardData: fmt.Sprintf(`{"title":"just testing", "uid": %q, "version": 1}`, dashboardUID),
|
dashboardData: fmt.Sprintf(`{"title":"just testing", "uid": %q, "version": 1}`, dashboardUID),
|
||||||
expStatus: http.StatusBadRequest,
|
expStatus: http.StatusBadRequest,
|
||||||
expErrReason: models.ErrDashboardCannotSaveProvisionedDashboard.Reason,
|
expErrReason: dashboards.ErrDashboardCannotSaveProvisionedDashboard.Reason,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "when updating dashboard using unknown ID, it should fail",
|
desc: "when updating dashboard using unknown ID, it should fail",
|
||||||
dashboardData: `{"title":"just testing", "id": 42, "version": 1}`,
|
dashboardData: `{"title":"just testing", "id": 42, "version": 1}`,
|
||||||
expStatus: http.StatusNotFound,
|
expStatus: http.StatusNotFound,
|
||||||
expErrReason: models.ErrDashboardNotFound.Reason,
|
expErrReason: dashboards.ErrDashboardNotFound.Reason,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "when updating dashboard using unknown UID, it should succeed",
|
desc: "when updating dashboard using unknown UID, it should succeed",
|
||||||
@ -247,7 +249,7 @@ providers:
|
|||||||
dashboardErr := &errorResponseBody{}
|
dashboardErr := &errorResponseBody{}
|
||||||
err = json.Unmarshal(b, dashboardErr)
|
err = json.Unmarshal(b, dashboardErr)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, models.ErrDashboardCannotDeleteProvisionedDashboard.Reason, dashboardErr.Message)
|
assert.Equal(t, dashboards.ErrDashboardCannotDeleteProvisionedDashboard.Reason, dashboardErr.Message)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user