Dashboard Library: Display datasource plugin dashboards in empty page (#111279)
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport/utils"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
@@ -23,15 +24,17 @@ type ImportDashboardAPI struct {
|
||||
quotaService QuotaService
|
||||
pluginStore pluginstore.Store
|
||||
ac accesscontrol.AccessControl
|
||||
features featuremgmt.FeatureToggles
|
||||
}
|
||||
|
||||
func New(dashboardImportService dashboardimport.Service, quotaService QuotaService,
|
||||
pluginStore pluginstore.Store, ac accesscontrol.AccessControl) *ImportDashboardAPI {
|
||||
pluginStore pluginstore.Store, ac accesscontrol.AccessControl, features featuremgmt.FeatureToggles) *ImportDashboardAPI {
|
||||
return &ImportDashboardAPI{
|
||||
dashboardImportService: dashboardImportService,
|
||||
quotaService: quotaService,
|
||||
pluginStore: pluginStore,
|
||||
ac: ac,
|
||||
features: features,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,9 +46,48 @@ func (api *ImportDashboardAPI) RegisterAPIEndpoints(routeRegister routing.RouteR
|
||||
authorize(accesscontrol.EvalPermission(dashboards.ActionDashboardsCreate)),
|
||||
routing.Wrap(api.ImportDashboard),
|
||||
)
|
||||
if api.features.IsEnabledGlobally(featuremgmt.FlagDashboardLibrary) {
|
||||
route.Post(
|
||||
"/interpolate",
|
||||
authorize(accesscontrol.EvalPermission(dashboards.ActionDashboardsCreate)),
|
||||
routing.Wrap(api.InterpolateDashboard),
|
||||
)
|
||||
}
|
||||
}, middleware.ReqSignedIn)
|
||||
}
|
||||
|
||||
// swagger:route POST /dashboards/interpolate dashboards interpolateDashboard
|
||||
//
|
||||
// Interpolate dashboard. This is an experimental endpoint under dashboardLibrary FF and is subject to change.
|
||||
//
|
||||
// Responses:
|
||||
// 200: interpolateDashboardResponse
|
||||
// 400: badRequestError
|
||||
// 401: unauthorisedError
|
||||
// 422: unprocessableEntityError
|
||||
// 500: internalServerError
|
||||
func (api *ImportDashboardAPI) InterpolateDashboard(c *contextmodel.ReqContext) response.Response {
|
||||
req := dashboardimport.ImportDashboardRequest{}
|
||||
if err := web.Bind(c.Req, &req); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
if req.PluginId == "" {
|
||||
return response.Error(http.StatusUnprocessableEntity, "pluginId must be set", nil)
|
||||
}
|
||||
|
||||
resp, err := api.dashboardImportService.InterpolateDashboard(c.Req.Context(), &req)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "failed to interpolate dashboard", err)
|
||||
}
|
||||
|
||||
resp.Del("__elements")
|
||||
resp.Del("__inputs")
|
||||
resp.Del("__requires")
|
||||
|
||||
return response.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// swagger:route POST /dashboards/import dashboards importDashboard
|
||||
//
|
||||
// Import dashboard.
|
||||
@@ -110,3 +152,9 @@ type ImportDashboardResponse struct {
|
||||
// in: body
|
||||
Body dashboardimport.ImportDashboardResponse `json:"body"`
|
||||
}
|
||||
|
||||
// swagger:response interpolateDashboardResponse
|
||||
type InterpolateDashboardResponse struct {
|
||||
// in: body
|
||||
Body interface{} `json:"body"`
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
@@ -30,7 +31,7 @@ func TestImportDashboardAPI(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), nil, actest.FakeAccessControl{ExpectedEvaluate: true})
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), nil, actest.FakeAccessControl{ExpectedEvaluate: true}, featuremgmt.WithFeatures())
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
|
||||
s := webtest.NewServer(t, routeRegister)
|
||||
@@ -107,7 +108,7 @@ func TestImportDashboardAPI(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), nil, actest.FakeAccessControl{ExpectedEvaluate: true})
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), nil, actest.FakeAccessControl{ExpectedEvaluate: true}, featuremgmt.WithFeatures())
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
|
||||
s := webtest.NewServer(t, routeRegister)
|
||||
@@ -135,7 +136,7 @@ func TestImportDashboardAPI(t *testing.T) {
|
||||
|
||||
t.Run("Quota reached", func(t *testing.T) {
|
||||
service := &serviceMock{}
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaReached), nil, actest.FakeAccessControl{ExpectedEvaluate: true})
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaReached), nil, actest.FakeAccessControl{ExpectedEvaluate: true}, featuremgmt.WithFeatures())
|
||||
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
|
||||
@@ -159,8 +160,74 @@ func TestImportDashboardAPI(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestInterpolateDashboardFeatureFlag(t *testing.T) {
|
||||
t.Run("Feature flag disabled - interpolate endpoint should return 404", func(t *testing.T) {
|
||||
service := &serviceMock{}
|
||||
// Create features with dashboardLibrary disabled
|
||||
features := featuremgmt.WithFeatures()
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), nil, actest.FakeAccessControl{ExpectedEvaluate: true}, features)
|
||||
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
|
||||
s := webtest.NewServer(t, routeRegister)
|
||||
|
||||
cmd := &dashboardimport.ImportDashboardRequest{
|
||||
Dashboard: simplejson.New(),
|
||||
}
|
||||
jsonBytes, err := json.Marshal(cmd)
|
||||
require.NoError(t, err)
|
||||
req := s.NewPostRequest("/api/dashboards/interpolate", bytes.NewReader(jsonBytes))
|
||||
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||
UserID: 1,
|
||||
Permissions: map[int64]map[string][]string{
|
||||
1: {dashboards.ActionDashboardsCreate: {}},
|
||||
},
|
||||
})
|
||||
resp, err := s.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.Equal(t, http.StatusNotFound, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("Feature flag enabled - interpolate endpoint should work", func(t *testing.T) {
|
||||
interpolateDashboardServiceCalled := false
|
||||
service := &serviceMock{
|
||||
interpolateDashboardFunc: func(ctx context.Context, req *dashboardimport.ImportDashboardRequest) (*simplejson.Json, error) {
|
||||
interpolateDashboardServiceCalled = true
|
||||
return simplejson.New(), nil
|
||||
},
|
||||
}
|
||||
// Create features with dashboardLibrary enabled
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagDashboardLibrary)
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), nil, actest.FakeAccessControl{ExpectedEvaluate: true}, features)
|
||||
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
|
||||
s := webtest.NewServer(t, routeRegister)
|
||||
|
||||
cmd := &dashboardimport.ImportDashboardRequest{
|
||||
PluginId: "test-plugin",
|
||||
}
|
||||
jsonBytes, err := json.Marshal(cmd)
|
||||
require.NoError(t, err)
|
||||
req := s.NewPostRequest("/api/dashboards/interpolate", bytes.NewReader(jsonBytes))
|
||||
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||
UserID: 1,
|
||||
Permissions: map[int64]map[string][]string{
|
||||
1: {dashboards.ActionDashboardsCreate: {}},
|
||||
},
|
||||
})
|
||||
resp, err := s.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
require.True(t, interpolateDashboardServiceCalled)
|
||||
})
|
||||
}
|
||||
|
||||
type serviceMock struct {
|
||||
importDashboardFunc func(ctx context.Context, req *dashboardimport.ImportDashboardRequest) (*dashboardimport.ImportDashboardResponse, error)
|
||||
importDashboardFunc func(ctx context.Context, req *dashboardimport.ImportDashboardRequest) (*dashboardimport.ImportDashboardResponse, error)
|
||||
interpolateDashboardFunc func(ctx context.Context, req *dashboardimport.ImportDashboardRequest) (*simplejson.Json, error)
|
||||
}
|
||||
|
||||
func (s *serviceMock) ImportDashboard(ctx context.Context, req *dashboardimport.ImportDashboardRequest) (*dashboardimport.ImportDashboardResponse, error) {
|
||||
@@ -171,6 +238,14 @@ func (s *serviceMock) ImportDashboard(ctx context.Context, req *dashboardimport.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *serviceMock) InterpolateDashboard(ctx context.Context, req *dashboardimport.ImportDashboardRequest) (*simplejson.Json, error) {
|
||||
if s.interpolateDashboardFunc != nil {
|
||||
return s.interpolateDashboardFunc(ctx, req)
|
||||
}
|
||||
|
||||
return simplejson.New(), nil
|
||||
}
|
||||
|
||||
func quotaReached(c *contextmodel.ReqContext, target quota.TargetSrv) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user