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:
Kristin Laemmert
2022-06-30 09:31:54 -04:00
committed by GitHub
parent a1fb73c503
commit 9de00c8eb2
46 changed files with 489 additions and 466 deletions
+4 -4
View File
@@ -7,15 +7,15 @@ import (
"net/http"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/util"
)
// ToDashboardErrorResponse returns a different response status according to the dashboard error type
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 body := dashboardErr.Body(); body != nil {
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)
}
if errors.Is(err, models.ErrFolderNotFound) {
if errors.Is(err, dashboards.ErrFolderNotFound) {
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)
}
var pluginErr models.UpdatePluginDashboardError
var pluginErr dashboards.UpdatePluginDashboardError
if ok := errors.As(err, &pluginErr); ok {
message := fmt.Sprintf("The dashboard belongs to plugin %s.", pluginErr.PluginId)
// look up plugin name
+14 -14
View File
@@ -4,40 +4,40 @@ import (
"errors"
"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"
)
// ToFolderErrorResponse returns a different response status according to the folder error type
func ToFolderErrorResponse(err error) response.Response {
var dashboardErr models.DashboardErr
var dashboardErr dashboards.DashboardErr
if ok := errors.As(err, &dashboardErr); ok {
return response.Error(dashboardErr.StatusCode, err.Error(), err)
}
if errors.Is(err, models.ErrFolderTitleEmpty) ||
errors.Is(err, models.ErrDashboardTypeMismatch) ||
errors.Is(err, models.ErrDashboardInvalidUid) ||
errors.Is(err, models.ErrDashboardUidTooLong) ||
errors.Is(err, models.ErrFolderContainsAlertRules) {
if errors.Is(err, dashboards.ErrFolderTitleEmpty) ||
errors.Is(err, dashboards.ErrDashboardTypeMismatch) ||
errors.Is(err, dashboards.ErrDashboardInvalidUid) ||
errors.Is(err, dashboards.ErrDashboardUidTooLong) ||
errors.Is(err, dashboards.ErrFolderContainsAlertRules) {
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)
}
if errors.Is(err, models.ErrFolderNotFound) {
return response.JSON(404, util.DynMap{"status": "not-found", "message": models.ErrFolderNotFound.Error()})
if errors.Is(err, dashboards.ErrFolderNotFound) {
return response.JSON(404, util.DynMap{"status": "not-found", "message": dashboards.ErrFolderNotFound.Error()})
}
if errors.Is(err, models.ErrFolderSameNameExists) ||
errors.Is(err, models.ErrFolderWithSameUIDExists) {
if errors.Is(err, dashboards.ErrFolderSameNameExists) ||
errors.Is(err, dashboards.ErrFolderWithSameUIDExists) {
return response.Error(409, err.Error(), nil)
}
if errors.Is(err, models.ErrFolderVersionMismatch) {
return response.JSON(412, util.DynMap{"status": "version-mismatch", "message": models.ErrFolderVersionMismatch.Error()})
if errors.Is(err, dashboards.ErrFolderVersionMismatch) {
return response.JSON(412, util.DynMap{"status": "version-mismatch", "message": dashboards.ErrFolderVersionMismatch.Error()})
}
return response.Error(500, "Folder API error", err)
+5 -5
View File
@@ -145,7 +145,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
if dash.FolderId > 0 {
query := models.GetDashboardQuery{Id: dash.FolderId, OrgId: c.OrgId}
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(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)
if err != nil {
var dashboardErr models.DashboardErr
var dashboardErr dashboards.DashboardErr
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)
}
}
@@ -333,7 +333,7 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
if cmd.FolderUid != "" {
folder, err := hs.folderService.GetFolderByUID(ctx, c.SignedInUser, c.OrgId, cmd.FolderUid)
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(500, "Error while checking folder ID", err)
@@ -362,7 +362,7 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
provisioningData = data
} else if 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)
}
provisioningData = data
+1 -1
View File
@@ -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
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 {
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), dashboardErr)
+3 -3
View File
@@ -87,7 +87,7 @@ func TestAPIGetPublicDashboard(t *testing.T) {
AccessToken: accessToken,
ExpectedHttpResponse: http.StatusNotFound,
publicDashboardResult: nil,
publicDashboardErr: models.ErrPublicDashboardNotFound,
publicDashboardErr: dashboards.ErrPublicDashboardNotFound,
},
}
@@ -153,7 +153,7 @@ func TestAPIGetPublicDashboardConfig(t *testing.T) {
DashboardUid: "77777",
ExpectedHttpResponse: http.StatusNotFound,
PublicDashboardResult: nil,
PublicDashboardError: models.ErrDashboardNotFound,
PublicDashboardError: dashboards.ErrDashboardNotFound,
},
{
Name: "returns 500 when internal server error",
@@ -218,7 +218,7 @@ func TestApiSavePublicDashboardConfig(t *testing.T) {
Name: "returns 404 when dashboard not found",
ExpectedHttpResponse: http.StatusNotFound,
publicDashboardConfig: &models.PublicDashboard{},
saveDashboardError: models.ErrDashboardNotFound,
saveDashboardError: dashboards.ErrDashboardNotFound,
},
}
+3 -2
View File
@@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/metrics"
"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/guardian"
"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)
canEdit, err := guardian.CanEdit()
// 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)
}
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)
}
+18 -18
View File
@@ -9,7 +9,6 @@ import (
"net/http"
"testing"
"github.com/grafana/grafana/pkg/framework/coremodel/registry"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
@@ -18,6 +17,7 @@ import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/framework/coremodel/registry"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
@@ -679,24 +679,24 @@ func TestDashboardAPIEndpoint(t *testing.T) {
SaveError error
ExpectedStatusCode int
}{
{SaveError: models.ErrDashboardNotFound, ExpectedStatusCode: 404},
{SaveError: models.ErrFolderNotFound, ExpectedStatusCode: 400},
{SaveError: models.ErrDashboardWithSameUIDExists, ExpectedStatusCode: 400},
{SaveError: models.ErrDashboardWithSameNameInFolderExists, ExpectedStatusCode: 412},
{SaveError: models.ErrDashboardVersionMismatch, ExpectedStatusCode: 412},
{SaveError: models.ErrDashboardTitleEmpty, ExpectedStatusCode: 400},
{SaveError: models.ErrDashboardFolderCannotHaveParent, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardNotFound, ExpectedStatusCode: 404},
{SaveError: dashboards.ErrFolderNotFound, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardWithSameUIDExists, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardWithSameNameInFolderExists, ExpectedStatusCode: 412},
{SaveError: dashboards.ErrDashboardVersionMismatch, ExpectedStatusCode: 412},
{SaveError: dashboards.ErrDashboardTitleEmpty, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardFolderCannotHaveParent, ExpectedStatusCode: 400},
{SaveError: alerting.ValidationError{Reason: "Mu"}, ExpectedStatusCode: 422},
{SaveError: models.ErrDashboardFailedGenerateUniqueUid, ExpectedStatusCode: 500},
{SaveError: models.ErrDashboardTypeMismatch, ExpectedStatusCode: 400},
{SaveError: models.ErrDashboardFolderWithSameNameAsDashboard, ExpectedStatusCode: 400},
{SaveError: models.ErrDashboardWithSameNameAsFolder, ExpectedStatusCode: 400},
{SaveError: models.ErrDashboardFolderNameExists, ExpectedStatusCode: 400},
{SaveError: models.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403},
{SaveError: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{SaveError: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{SaveError: models.ErrDashboardCannotSaveProvisionedDashboard, ExpectedStatusCode: 400},
{SaveError: models.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412},
{SaveError: dashboards.ErrDashboardFailedGenerateUniqueUid, ExpectedStatusCode: 500},
{SaveError: dashboards.ErrDashboardTypeMismatch, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardFolderWithSameNameAsDashboard, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardWithSameNameAsFolder, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardFolderNameExists, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403},
{SaveError: dashboards.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{SaveError: dashboards.ErrDashboardCannotSaveProvisionedDashboard, ExpectedStatusCode: 400},
{SaveError: dashboards.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412},
}
cmd := models.SaveDashboardCommand{
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"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/services/guardian"
"github.com/grafana/grafana/pkg/util"
"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)
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
return apierrors.ToFolderErrorResponse(models.ErrFolderAccessDenied)
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
}
acl, err := g.GetAcl()
@@ -78,7 +79,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
}
if !canAdmin {
return apierrors.ToFolderErrorResponse(models.ErrFolderAccessDenied)
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
}
var items []*models.DashboardAcl
+2 -2
View File
@@ -49,7 +49,7 @@ func TestFolderPermissionAPIEndpoint(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()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
callGetFolderPermissions(sc, hs)
@@ -81,7 +81,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
})
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()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
+22 -21
View File
@@ -7,6 +7,10 @@ import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
@@ -19,9 +23,6 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/setting"
"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) {
@@ -55,15 +56,15 @@ func TestFoldersAPIEndpoint(t *testing.T) {
Error error
ExpectedStatusCode int
}{
{Error: models.ErrFolderWithSameUIDExists, ExpectedStatusCode: 409},
{Error: models.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
{Error: models.ErrFolderSameNameExists, ExpectedStatusCode: 409},
{Error: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{Error: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{Error: models.ErrFolderAccessDenied, ExpectedStatusCode: 403},
{Error: models.ErrFolderNotFound, ExpectedStatusCode: 404},
{Error: models.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
{Error: models.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
{Error: dashboards.ErrFolderWithSameUIDExists, ExpectedStatusCode: 409},
{Error: dashboards.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
{Error: dashboards.ErrFolderSameNameExists, ExpectedStatusCode: 409},
{Error: dashboards.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{Error: dashboards.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{Error: dashboards.ErrFolderAccessDenied, ExpectedStatusCode: 403},
{Error: dashboards.ErrFolderNotFound, ExpectedStatusCode: 404},
{Error: dashboards.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
{Error: dashboards.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
}
cmd := models.CreateFolderCommand{
@@ -110,15 +111,15 @@ func TestFoldersAPIEndpoint(t *testing.T) {
Error error
ExpectedStatusCode int
}{
{Error: models.ErrFolderWithSameUIDExists, ExpectedStatusCode: 409},
{Error: models.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
{Error: models.ErrFolderSameNameExists, ExpectedStatusCode: 409},
{Error: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{Error: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{Error: models.ErrFolderAccessDenied, ExpectedStatusCode: 403},
{Error: models.ErrFolderNotFound, ExpectedStatusCode: 404},
{Error: models.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
{Error: models.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
{Error: dashboards.ErrFolderWithSameUIDExists, ExpectedStatusCode: 409},
{Error: dashboards.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
{Error: dashboards.ErrFolderSameNameExists, ExpectedStatusCode: 409},
{Error: dashboards.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{Error: dashboards.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{Error: dashboards.ErrFolderAccessDenied, ExpectedStatusCode: 403},
{Error: dashboards.ErrFolderNotFound, ExpectedStatusCode: 404},
{Error: dashboards.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
{Error: dashboards.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
}
cmd := models.UpdateFolderCommand{