LibraryPanel: Cleanup service calls (#113277)
* cleanup * library panel via search * test cleanup * merge main * add FindDashboards mock * no matching dashbaords should return empty * do not alllow name and libraryPanel query
This commit is contained in:
+3
@@ -244,6 +244,7 @@ const injectedRtkApi = api
|
||||
folder: queryArg.folder,
|
||||
facet: queryArg.facet,
|
||||
tags: queryArg.tags,
|
||||
libraryPanel: queryArg.libraryPanel,
|
||||
sort: queryArg.sort,
|
||||
limit: queryArg.limit,
|
||||
explain: queryArg.explain,
|
||||
@@ -608,6 +609,8 @@ export type GetSearchApiArg = {
|
||||
facet?: string[];
|
||||
/** tag query filter */
|
||||
tags?: string[];
|
||||
/** find dashboards that reference a given libraryPanel */
|
||||
libraryPanel?: string;
|
||||
/** sortable field */
|
||||
sort?: string;
|
||||
/** number of results to return */
|
||||
|
||||
@@ -438,7 +438,6 @@ func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.S
|
||||
}
|
||||
|
||||
ctx = c.Req.Context()
|
||||
var err error
|
||||
|
||||
var userID int64
|
||||
if id, err := identity.UserIdentifier(c.GetID()); err == nil {
|
||||
@@ -518,12 +517,6 @@ func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.S
|
||||
return apierrors.ToDashboardErrorResponse(ctx, hs.pluginStore, saveErr)
|
||||
}
|
||||
|
||||
// connect library panels for this dashboard after the dashboard is stored and has an ID
|
||||
err = hs.LibraryPanelService.ConnectLibraryPanelsForDashboard(ctx, c.SignedInUser, dashboard)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Error while connecting library panels", err)
|
||||
}
|
||||
|
||||
c.TimeRequest(metrics.MApiDashboardSave)
|
||||
return response.JSON(http.StatusOK, util.DynMap{
|
||||
"status": "success",
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
||||
@@ -39,7 +38,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
libraryelementsfake "github.com/grafana/grafana/pkg/services/libraryelements/fake"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
"github.com/grafana/grafana/pkg/services/licensing/licensingtest"
|
||||
"github.com/grafana/grafana/pkg/services/live"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
@@ -265,7 +263,6 @@ func TestHTTPServer_DeleteDashboardByUID_AccessControl(t *testing.T) {
|
||||
hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures())
|
||||
hs.starService = startest.NewStarServiceFake()
|
||||
|
||||
hs.LibraryPanelService = &mockLibraryPanelService{}
|
||||
hs.LibraryElementService = &libraryelementsfake.LibraryElementService{}
|
||||
|
||||
middleware := publicdashboards.NewFakePublicDashboardMiddleware(t)
|
||||
@@ -791,7 +788,6 @@ func TestIntegrationDashboardAPIEndpoint(t *testing.T) {
|
||||
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
|
||||
Live: newTestLive(t, db.InitTestDB(t)),
|
||||
QuotaService: quotatest.New(false, nil),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||
DashboardService: dashboardService,
|
||||
SQLStore: dbtest.NewFakeDB(),
|
||||
@@ -853,7 +849,6 @@ func TestIntegrationDashboardAPIEndpoint(t *testing.T) {
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
ProvisioningService: fakeProvisioningService,
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||
dashboardProvisioningService: dashboardProvisioningService,
|
||||
SQLStore: mockSQLStore,
|
||||
@@ -887,7 +882,6 @@ func TestIntegrationDashboardAPIEndpoint(t *testing.T) {
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
ProvisioningService: fakeProvisioningService,
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||
dashboardProvisioningService: dashboardProvisioningService,
|
||||
SQLStore: mockSQLStore,
|
||||
@@ -928,7 +922,6 @@ func TestIntegrationDashboardAPIEndpoint(t *testing.T) {
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", org.RoleEditor, func(sc *scenarioContext) {
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||
SQLStore: mockSQLStore,
|
||||
AccessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
@@ -1087,7 +1080,6 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
||||
Live: newTestLive(t, db.InitTestDB(t)),
|
||||
QuotaService: quotatest.New(false, nil),
|
||||
pluginStore: &pluginstore.FakePluginStore{},
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||
DashboardService: dashboardService,
|
||||
folderService: folderService,
|
||||
@@ -1127,7 +1119,6 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
||||
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
|
||||
Live: newTestLive(t, db.InitTestDB(t)),
|
||||
QuotaService: quotatest.New(false, nil),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||
DashboardService: mock,
|
||||
SQLStore: sqlStore,
|
||||
@@ -1177,15 +1168,3 @@ func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboard
|
||||
) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type mockLibraryPanelService struct{}
|
||||
|
||||
var _ librarypanels.Service = (*mockLibraryPanelService)(nil)
|
||||
|
||||
func (m *mockLibraryPanelService) ConnectLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockLibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -164,7 +164,6 @@ type HTTPServer struct {
|
||||
LoggerMiddleware loggermw.Logger
|
||||
SQLStore db.DB
|
||||
AlertNG *ngalert.AlertNG
|
||||
LibraryPanelService librarypanels.Service
|
||||
LibraryElementService libraryelements.Service
|
||||
SocialService social.Service
|
||||
Listener net.Listener
|
||||
@@ -319,7 +318,6 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
ContextHandler: contextHandler,
|
||||
LoggerMiddleware: loggerMiddleware,
|
||||
AlertNG: alertNG,
|
||||
LibraryPanelService: libraryPanelService,
|
||||
LibraryElementService: libraryElementService,
|
||||
QuotaService: quotaService,
|
||||
tracer: tracer,
|
||||
|
||||
@@ -229,12 +229,32 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resourcepb.Reso
|
||||
return nil, fmt.Errorf("only one repo name is supported")
|
||||
}
|
||||
query.ManagerIdentity = vals[0]
|
||||
|
||||
case unisearch.DASHBOARD_LIBRARY_PANEL_REFERENCE:
|
||||
if len(vals) != 1 {
|
||||
return nil, fmt.Errorf("only one library panel uid is supported")
|
||||
}
|
||||
|
||||
return c.getLibraryPanelConnections(ctx, user, vals[0], req.Options.Key.Namespace)
|
||||
// Make sure the query does not include incompatible combinations
|
||||
for _, f := range req.Options.Fields {
|
||||
switch f.Key {
|
||||
case resource.SEARCH_FIELD_NAME:
|
||||
return nil, fmt.Errorf("libraryPanel query must not include explicit names")
|
||||
}
|
||||
}
|
||||
|
||||
query.DashboardUIDs, err = c.getLibraryPanelConnections(ctx, user, vals[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(query.DashboardUIDs) == 0 {
|
||||
// Empty results
|
||||
return &resourcepb.ResourceSearchResponse{
|
||||
TotalHits: 0,
|
||||
Results: &resourcepb.ResourceTable{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
case resource.SEARCH_FIELD_TITLE_PHRASE:
|
||||
if len(vals) != 1 {
|
||||
return nil, fmt.Errorf("only one title supported")
|
||||
@@ -362,32 +382,18 @@ func getResourceKey(item *dashboards.DashboardSearchProjection, namespace string
|
||||
}
|
||||
}
|
||||
|
||||
// retrieves all the dashboards that are connected to the given library panel
|
||||
func (c *DashboardSearchClient) getLibraryPanelConnections(ctx context.Context, user identity.Requester, libraryElementUID, namespace string) (*resourcepb.ResourceSearchResponse, error) {
|
||||
// retrieves all dashboard UIDs connected to a given library panel
|
||||
func (c *DashboardSearchClient) getLibraryPanelConnections(ctx context.Context, user identity.Requester, libraryElementUID string) ([]string, error) {
|
||||
connections, err := c.dashboardStore.GetDashboardsByLibraryPanelUID(ctx, libraryElementUID, user.GetOrgID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := c.getColumns("", &dashboards.FindPersistedDashboardsQuery{})
|
||||
list := &resourcepb.ResourceSearchResponse{
|
||||
Results: &resourcepb.ResourceTable{
|
||||
Columns: columns,
|
||||
},
|
||||
uids := make([]string, len(connections))
|
||||
for i, dashboard := range connections {
|
||||
uids[i] = dashboard.UID
|
||||
}
|
||||
|
||||
for _, dashboard := range connections {
|
||||
cells := c.createCommonCells("", dashboard.FolderUID, dashboard.ID, nil) // nolint:staticcheck
|
||||
list.Results.Rows = append(list.Results.Rows, &resourcepb.ResourceTableRow{
|
||||
Key: getResourceKey(&dashboards.DashboardSearchProjection{
|
||||
UID: dashboard.UID,
|
||||
}, namespace),
|
||||
Cells: cells,
|
||||
})
|
||||
}
|
||||
|
||||
list.TotalHits = int64(len(list.Results.Rows))
|
||||
return list, nil
|
||||
return uids, nil
|
||||
}
|
||||
|
||||
func (c *DashboardSearchClient) GetStats(ctx context.Context, req *resourcepb.ResourceStatsRequest, _ ...grpc.CallOption) (*resourcepb.ResourceStatsResponse, error) {
|
||||
|
||||
@@ -581,6 +581,11 @@ func TestDashboardSearchClient_Search(t *testing.T) {
|
||||
{UID: "dashboard2", FolderUID: "folder2", ID: 2},
|
||||
}, nil).Once()
|
||||
|
||||
mockStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{
|
||||
{UID: "dashboard1", FolderUID: "folder1", ID: 1},
|
||||
{UID: "dashboard2", FolderUID: "folder2", ID: 2},
|
||||
}, nil).Once()
|
||||
|
||||
req := &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: dashboardKey,
|
||||
|
||||
@@ -121,6 +121,15 @@ func (s *SearchHandler) GetAPIRoutes(defs map[string]common.OpenAPIDefinition) *
|
||||
Schema: spec.ArrayProperty(spec.StringProperty()),
|
||||
},
|
||||
},
|
||||
{
|
||||
ParameterProps: spec3.ParameterProps{
|
||||
Name: "libraryPanel",
|
||||
In: "query",
|
||||
Description: "find dashboards that reference a given libraryPanel",
|
||||
Required: false,
|
||||
Schema: spec.StringProperty(),
|
||||
},
|
||||
},
|
||||
{
|
||||
ParameterProps: spec3.ParameterProps{
|
||||
Name: "sort",
|
||||
@@ -363,6 +372,15 @@ func (s *SearchHandler) DoSearch(w http.ResponseWriter, r *http.Request) {
|
||||
}}
|
||||
}
|
||||
|
||||
// The libraryPanel filter
|
||||
if libraryPanel, ok := queryParams["libraryPanel"]; ok {
|
||||
searchRequest.Options.Fields = []*resourcepb.Requirement{{
|
||||
Key: search.DASHBOARD_LIBRARY_PANEL_REFERENCE,
|
||||
Operator: "=",
|
||||
Values: libraryPanel,
|
||||
}}
|
||||
}
|
||||
|
||||
// The names filter
|
||||
names := queryParams["name"]
|
||||
|
||||
|
||||
@@ -630,6 +630,7 @@ func (d *dashboardStore) deleteDashboard(cmd *dashboards.DeleteDashboardCommand,
|
||||
{SQL: "DELETE FROM dashboard_version WHERE dashboard_id = ?", args: []any{dashboard.ID}},
|
||||
{SQL: "DELETE FROM dashboard_provisioning WHERE dashboard_id = ?", args: []any{dashboard.ID}},
|
||||
{SQL: "DELETE FROM dashboard_acl WHERE dashboard_id = ?", args: []any{dashboard.ID}},
|
||||
{SQL: "DELETE FROM library_element_connection WHERE connection_id = ?", args: []any{dashboard.ID}},
|
||||
}
|
||||
|
||||
if dashboard.IsFolder {
|
||||
|
||||
@@ -1812,6 +1812,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "libraryPanel",
|
||||
"in": "query",
|
||||
"description": "find dashboards that reference a given libraryPanel",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sort",
|
||||
"in": "query",
|
||||
|
||||
Reference in New Issue
Block a user