Plugins: Refactor GetPluginDashboards/LoadPluginDashboard (#46316)
Refactors GetPluginDashboards/LoadPluginDashboard by moving database interaction from plugin management to the plugindashboards service. Fixes #44553 Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
co-authored by
Will Browne
parent
d076cabb60
commit
6c7d326499
@@ -40,6 +40,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -446,3 +447,31 @@ func mockRequestBody(v interface{}) io.ReadCloser {
|
||||
b, _ := json.Marshal(v)
|
||||
return io.NopCloser(bytes.NewReader(b))
|
||||
}
|
||||
|
||||
// APITestServerOption option func for customizing HTTPServer configuration
|
||||
// when setting up an API test server via SetupAPITestServer.
|
||||
type APITestServerOption func(hs *HTTPServer)
|
||||
|
||||
// SetupAPITestServer sets up a webtest.Server ready for testing all
|
||||
// routes registered via HTTPServer.registerRoutes().
|
||||
// Optionally customize HTTPServer configuration by providing APITestServerOption
|
||||
// option(s).
|
||||
func SetupAPITestServer(t *testing.T, opts ...APITestServerOption) *webtest.Server {
|
||||
t.Helper()
|
||||
|
||||
hs := &HTTPServer{
|
||||
RouteRegister: routing.NewRouteRegister(),
|
||||
Cfg: setting.NewCfg(),
|
||||
AccessControl: accesscontrolmock.New().WithDisabled(),
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
searchUsersService: &searchusers.OSSService{},
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(hs)
|
||||
}
|
||||
|
||||
hs.registerRoutes()
|
||||
s := webtest.NewServer(t, hs.RouteRegister)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert"
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsettings/service"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/query"
|
||||
@@ -100,7 +101,7 @@ type HTTPServer struct {
|
||||
PluginRequestValidator models.PluginRequestValidator
|
||||
pluginClient plugins.Client
|
||||
pluginStore plugins.Store
|
||||
pluginDashboardManager plugins.PluginDashboardManager
|
||||
pluginDashboardService plugindashboards.Service
|
||||
pluginStaticRouteResolver plugins.StaticRouteResolver
|
||||
pluginErrorResolver plugins.ErrorResolver
|
||||
SearchService search.Service
|
||||
@@ -152,7 +153,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
renderService rendering.Service, licensing models.Licensing, hooksService *hooks.HooksService,
|
||||
cacheService *localcache.CacheService, sqlStore *sqlstore.SQLStore, alertEngine *alerting.AlertEngine,
|
||||
pluginRequestValidator models.PluginRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver,
|
||||
pluginDashboardManager plugins.PluginDashboardManager, pluginStore plugins.Store, pluginClient plugins.Client,
|
||||
pluginDashboardService plugindashboards.Service, pluginStore plugins.Store, pluginClient plugins.Client,
|
||||
pluginErrorResolver plugins.ErrorResolver, settingsProvider setting.Provider,
|
||||
dataSourceCache datasources.CacheService, userTokenService models.UserTokenService,
|
||||
cleanUpService *cleanup.CleanUpService, shortURLService shorturls.Service, queryHistoryService queryhistory.Service,
|
||||
@@ -191,7 +192,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
pluginClient: pluginClient,
|
||||
pluginStore: pluginStore,
|
||||
pluginStaticRouteResolver: pluginStaticRouteResolver,
|
||||
pluginDashboardManager: pluginDashboardManager,
|
||||
pluginDashboardService: pluginDashboardService,
|
||||
pluginErrorResolver: pluginErrorResolver,
|
||||
grafanaUpdateChecker: grafanaUpdateChecker,
|
||||
pluginsUpdateChecker: pluginsUpdateChecker,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"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/plugindashboards"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
// GetPluginDashboards get plugin dashboards.
|
||||
//
|
||||
// /api/plugins/:pluginId/dashboards
|
||||
func (hs *HTTPServer) GetPluginDashboards(c *models.ReqContext) response.Response {
|
||||
pluginID := web.Params(c.Req)[":pluginId"]
|
||||
|
||||
listReq := &plugindashboards.ListPluginDashboardsRequest{
|
||||
OrgID: c.OrgId,
|
||||
PluginID: pluginID,
|
||||
}
|
||||
list, err := hs.pluginDashboardService.ListPluginDashboards(c.Req.Context(), listReq)
|
||||
if err != nil {
|
||||
var notFound plugins.NotFoundError
|
||||
if errors.As(err, ¬Found) {
|
||||
return response.Error(http.StatusNotFound, notFound.Error(), nil)
|
||||
}
|
||||
|
||||
return response.Error(http.StatusInternalServerError, "Failed to get plugin dashboards", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, list.Items)
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetPluginDashboards(t *testing.T) {
|
||||
const existingPluginID = "existing-plugin"
|
||||
pluginDashboardService := &pluginDashboardServiceMock{
|
||||
pluginDashboards: map[string][]*plugindashboards.PluginDashboard{
|
||||
existingPluginID: {
|
||||
{
|
||||
PluginId: existingPluginID,
|
||||
UID: "a",
|
||||
Title: "A",
|
||||
},
|
||||
{
|
||||
PluginId: existingPluginID,
|
||||
UID: "b",
|
||||
Title: "B",
|
||||
},
|
||||
},
|
||||
},
|
||||
unexpectedErrors: map[string]error{
|
||||
"boom": fmt.Errorf("BOOM"),
|
||||
},
|
||||
}
|
||||
|
||||
s := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.pluginDashboardService = pluginDashboardService
|
||||
})
|
||||
|
||||
t.Run("Not signed in should return 404 Not Found", func(t *testing.T) {
|
||||
req := s.NewGetRequest("/api/plugins/test/dashboards")
|
||||
resp, err := s.Send(req)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("Signed in and not org admin should return 403 Forbidden", func(t *testing.T) {
|
||||
user := &models.SignedInUser{
|
||||
UserId: 1,
|
||||
OrgRole: models.ROLE_EDITOR,
|
||||
}
|
||||
|
||||
resp, err := sendGetPluginDashboardsRequestForSignedInUser(t, s, existingPluginID, user)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.Equal(t, http.StatusForbidden, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("Signed in and org admin", func(t *testing.T) {
|
||||
user := &models.SignedInUser{
|
||||
UserId: 1,
|
||||
OrgId: 1,
|
||||
OrgRole: models.ROLE_ADMIN,
|
||||
}
|
||||
|
||||
t.Run("When plugin doesn't exist should return 404 Not Found", func(t *testing.T) {
|
||||
resp, err := sendGetPluginDashboardsRequestForSignedInUser(t, s, "not-exists", user)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.Equal(t, http.StatusNotFound, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("When result is unexpected error should return 500 Internal Server Error", func(t *testing.T) {
|
||||
resp, err := sendGetPluginDashboardsRequestForSignedInUser(t, s, "boom", user)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("When plugin exists should return 200 OK with expected payload", func(t *testing.T) {
|
||||
resp, err := sendGetPluginDashboardsRequestForSignedInUser(t, s, existingPluginID, user)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
bytes, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
var listResp []*plugindashboards.PluginDashboard
|
||||
err = json.Unmarshal(bytes, &listResp)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, listResp)
|
||||
require.Len(t, listResp, 2)
|
||||
require.Equal(t, pluginDashboardService.pluginDashboards[existingPluginID], listResp)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func sendGetPluginDashboardsRequestForSignedInUser(t *testing.T, s *webtest.Server, pluginID string, user *models.SignedInUser) (*http.Response, error) {
|
||||
t.Helper()
|
||||
|
||||
req := s.NewGetRequest(fmt.Sprintf("/api/plugins/%s/dashboards", pluginID))
|
||||
webtest.RequestWithSignedInUser(req, user)
|
||||
return s.Send(req)
|
||||
}
|
||||
|
||||
type pluginDashboardServiceMock struct {
|
||||
plugindashboards.Service
|
||||
pluginDashboards map[string][]*plugindashboards.PluginDashboard
|
||||
unexpectedErrors map[string]error
|
||||
}
|
||||
|
||||
func (m *pluginDashboardServiceMock) ListPluginDashboards(ctx context.Context, req *plugindashboards.ListPluginDashboardsRequest) (*plugindashboards.ListPluginDashboardsResponse, error) {
|
||||
if pluginDashboards, exists := m.pluginDashboards[req.PluginID]; exists {
|
||||
return &plugindashboards.ListPluginDashboardsResponse{
|
||||
Items: pluginDashboards,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if err, exists := m.unexpectedErrors[req.PluginID]; exists {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, plugins.NotFoundError{PluginID: req.PluginID}
|
||||
}
|
||||
@@ -182,22 +182,6 @@ func (hs *HTTPServer) UpdatePluginSetting(c *models.ReqContext) response.Respons
|
||||
return response.Success("Plugin settings updated")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) GetPluginDashboards(c *models.ReqContext) response.Response {
|
||||
pluginID := web.Params(c.Req)[":pluginId"]
|
||||
|
||||
list, err := hs.pluginDashboardManager.GetPluginDashboards(c.Req.Context(), c.OrgId, pluginID)
|
||||
if err != nil {
|
||||
var notFound plugins.NotFoundError
|
||||
if errors.As(err, ¬Found) {
|
||||
return response.Error(404, notFound.Error(), nil)
|
||||
}
|
||||
|
||||
return response.Error(500, "Failed to get plugin dashboards", err)
|
||||
}
|
||||
|
||||
return response.JSON(200, list)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) GetPluginMarkdown(c *models.ReqContext) response.Response {
|
||||
pluginID := web.Params(c.Req)[":pluginId"]
|
||||
name := web.Params(c.Req)[":name"]
|
||||
|
||||
Reference in New Issue
Block a user