mirror of
https://github.com/grafana/grafana.git
synced 2024-11-26 02:40:26 -06:00
Search: Allowed actions (#52529)
This commit is contained in:
parent
ce04a01ae9
commit
bcfa4fabf6
257
pkg/services/searchV2/allowed_actions.go
Normal file
257
pkg/services/searchV2/allowed_actions.go
Normal file
@ -0,0 +1,257 @@
|
||||
package searchV2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
)
|
||||
|
||||
func (s *StandardSearchService) addAllowedActionsField(ctx context.Context, orgId int64, user *models.SignedInUser, response *backend.DataResponse) error {
|
||||
references, err := getEntityReferences(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allAllowedActions, err := s.createAllowedActions(ctx, orgId, user, references)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(response.Frames) == 0 {
|
||||
return errors.New("empty response")
|
||||
}
|
||||
|
||||
frame := response.Frames[0]
|
||||
|
||||
allowedActionsField := data.NewFieldFromFieldType(data.FieldTypeJSON, len(allAllowedActions))
|
||||
allowedActionsField.Name = "allowed_actions"
|
||||
frame.Fields = append(frame.Fields, allowedActionsField)
|
||||
|
||||
for i, actions := range allAllowedActions {
|
||||
js, _ := json.Marshal(actions)
|
||||
jsb := json.RawMessage(js)
|
||||
allowedActionsField.Set(i, jsb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type allowedActions struct {
|
||||
EntityKind entityKind `json:"kind"`
|
||||
UID string `json:"uid"`
|
||||
Actions []string `json:"actions"`
|
||||
}
|
||||
|
||||
func (s *StandardSearchService) createAllowedActions(ctx context.Context, orgId int64, user *models.SignedInUser, references []entityReferences) ([][]allowedActions, error) {
|
||||
uidsPerKind := make(map[entityKind][]string)
|
||||
for _, refs := range references {
|
||||
if _, ok := uidsPerKind[refs.entityKind]; !ok {
|
||||
uidsPerKind[refs.entityKind] = []string{}
|
||||
}
|
||||
|
||||
uidsPerKind[refs.entityKind] = append(uidsPerKind[refs.entityKind], refs.uid)
|
||||
|
||||
if len(refs.dsUids) > 0 {
|
||||
if _, ok := uidsPerKind[entityKindDatasource]; !ok {
|
||||
uidsPerKind[entityKindDatasource] = []string{}
|
||||
}
|
||||
|
||||
uidsPerKind[entityKindDatasource] = append(uidsPerKind[entityKindDatasource], refs.dsUids...)
|
||||
}
|
||||
}
|
||||
|
||||
allowedActionsByUid := make(map[entityKind]map[string][]string)
|
||||
|
||||
for entKind, uids := range uidsPerKind {
|
||||
if entKind == entityKindPanel {
|
||||
emptyAllowedActions := make(map[string][]string)
|
||||
for _, uid := range uids {
|
||||
emptyAllowedActions[uid] = []string{}
|
||||
}
|
||||
allowedActionsByUid[entityKindPanel] = emptyAllowedActions
|
||||
}
|
||||
|
||||
var prefix string
|
||||
switch entKind {
|
||||
case entityKindFolder:
|
||||
prefix = dashboards.ScopeFoldersPrefix
|
||||
case entityKindDatasource:
|
||||
prefix = datasources.ScopePrefix
|
||||
case entityKindDashboard:
|
||||
prefix = dashboards.ScopeDashboardsPrefix
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
allowedActionsByUid[entKind] = s.getAllowedActionsByUid(ctx, user, orgId, prefix, uids)
|
||||
}
|
||||
|
||||
dsActionsByUid, ok := allowedActionsByUid[entityKindDatasource]
|
||||
if !ok {
|
||||
dsActionsByUid = make(map[string][]string)
|
||||
}
|
||||
|
||||
var out [][]allowedActions
|
||||
for _, ref := range references {
|
||||
var actions []allowedActions
|
||||
|
||||
selfActions := make([]string, 0)
|
||||
if selfKindActions, ok := allowedActionsByUid[ref.entityKind]; ok {
|
||||
if self, ok := selfKindActions[ref.uid]; ok && len(self) > 0 {
|
||||
selfActions = self
|
||||
}
|
||||
}
|
||||
|
||||
actions = append(actions, allowedActions{
|
||||
EntityKind: ref.entityKind,
|
||||
UID: ref.uid,
|
||||
Actions: selfActions,
|
||||
})
|
||||
|
||||
for _, dsUid := range ref.dsUids {
|
||||
dsActions := make([]string, 0)
|
||||
if dsAct, ok := dsActionsByUid[dsUid]; ok {
|
||||
dsActions = dsAct
|
||||
}
|
||||
|
||||
actions = append(actions, allowedActions{
|
||||
EntityKind: entityKindDatasource,
|
||||
UID: dsUid,
|
||||
Actions: dsActions,
|
||||
})
|
||||
}
|
||||
|
||||
out = append(out, actions)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *StandardSearchService) getAllowedActionsByUid(ctx context.Context, user *models.SignedInUser,
|
||||
orgID int64, prefix string, resourceIDs []string) map[string][]string {
|
||||
if s.ac.IsDisabled() {
|
||||
return map[string][]string{}
|
||||
}
|
||||
|
||||
if user.Permissions == nil {
|
||||
return map[string][]string{}
|
||||
}
|
||||
|
||||
permissions, ok := user.Permissions[orgID]
|
||||
if !ok {
|
||||
return map[string][]string{}
|
||||
}
|
||||
|
||||
uidsAsMap := make(map[string]bool)
|
||||
for _, uid := range resourceIDs {
|
||||
uidsAsMap[uid] = true
|
||||
}
|
||||
|
||||
out := make(map[string][]string)
|
||||
resp := accesscontrol.GetResourcesMetadata(ctx, permissions, prefix, uidsAsMap)
|
||||
for uid, meta := range resp {
|
||||
var actions []string
|
||||
for action := range meta {
|
||||
actions = append(actions, action)
|
||||
}
|
||||
sort.Strings(actions)
|
||||
out[uid] = actions
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type entityReferences struct {
|
||||
entityKind entityKind
|
||||
uid string
|
||||
dsUids []string
|
||||
}
|
||||
|
||||
func getEntityReferences(resp *backend.DataResponse) ([]entityReferences, error) {
|
||||
if resp == nil {
|
||||
return nil, errors.New("nil response")
|
||||
}
|
||||
|
||||
if resp.Error != nil {
|
||||
return nil, resp.Error
|
||||
}
|
||||
|
||||
if len(resp.Frames) == 0 {
|
||||
return nil, errors.New("empty response")
|
||||
}
|
||||
|
||||
frame := resp.Frames[0]
|
||||
|
||||
kindField, idx := frame.FieldByName("kind")
|
||||
if idx == -1 {
|
||||
return nil, errors.New("no kind field")
|
||||
}
|
||||
|
||||
dsUidField, idx := frame.FieldByName("ds_uid")
|
||||
if idx == -1 {
|
||||
return nil, errors.New("no ds_uid field")
|
||||
}
|
||||
uidField, idx := frame.FieldByName("uid")
|
||||
if idx == -1 {
|
||||
return nil, errors.New("no dash_uid field")
|
||||
}
|
||||
|
||||
if dsUidField.Len() != uidField.Len() {
|
||||
return nil, errors.New("mismatched lengths")
|
||||
}
|
||||
|
||||
var out []entityReferences
|
||||
for i := 0; i < dsUidField.Len(); i++ {
|
||||
kind, ok := kindField.At(i).(string)
|
||||
if !ok || kind == "" {
|
||||
return nil, errors.New("invalid value in kind field")
|
||||
}
|
||||
|
||||
uid, ok := uidField.At(i).(string)
|
||||
if !ok || uid == "" {
|
||||
return nil, errors.New("invalid value in uid field")
|
||||
}
|
||||
|
||||
if entityKind(kind) != entityKindDashboard {
|
||||
out = append(out, entityReferences{
|
||||
entityKind: entityKind(kind),
|
||||
uid: uid,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
uidField, ok := uidField.At(i).(string)
|
||||
if !ok || uidField == "" {
|
||||
return nil, errors.New("invalid value in dash_uid field")
|
||||
}
|
||||
|
||||
rawDsUids, ok := dsUidField.At(i).(*json.RawMessage)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid value in ds_uid field")
|
||||
}
|
||||
|
||||
var uids []string
|
||||
if rawDsUids != nil {
|
||||
jsonValue, err := rawDsUids.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(jsonValue, &uids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
out = append(out, entityReferences{entityKind: entityKindDashboard, uid: uid, dsUids: uids})
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
119
pkg/services/searchV2/allowed_actions_test.go
Normal file
119
pkg/services/searchV2/allowed_actions_test.go
Normal file
@ -0,0 +1,119 @@
|
||||
package searchV2
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/experimental"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed testdata/search_response_frame.json
|
||||
exampleListFrameJSON string
|
||||
|
||||
orgId = int64(1)
|
||||
permissionsWithScopeAll = map[string][]string{
|
||||
datasources.ActionIDRead: {datasources.ScopeAll},
|
||||
datasources.ActionDelete: {datasources.ScopeAll},
|
||||
ac.ActionDatasourcesExplore: {datasources.ScopeAll},
|
||||
datasources.ActionQuery: {datasources.ScopeAll},
|
||||
datasources.ActionRead: {datasources.ScopeAll},
|
||||
datasources.ActionWrite: {datasources.ScopeAll},
|
||||
datasources.ActionPermissionsRead: {datasources.ScopeAll},
|
||||
datasources.ActionPermissionsWrite: {datasources.ScopeAll},
|
||||
|
||||
dashboards.ActionFoldersCreate: {dashboards.ScopeFoldersAll},
|
||||
dashboards.ActionFoldersRead: {dashboards.ScopeFoldersAll},
|
||||
dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersAll},
|
||||
dashboards.ActionFoldersDelete: {dashboards.ScopeFoldersAll},
|
||||
dashboards.ActionFoldersPermissionsRead: {dashboards.ScopeFoldersAll},
|
||||
dashboards.ActionFoldersPermissionsWrite: {dashboards.ScopeFoldersAll},
|
||||
|
||||
dashboards.ActionDashboardsCreate: {dashboards.ScopeDashboardsAll},
|
||||
dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsAll},
|
||||
dashboards.ActionDashboardsWrite: {dashboards.ScopeDashboardsAll},
|
||||
dashboards.ActionDashboardsDelete: {dashboards.ScopeDashboardsAll},
|
||||
dashboards.ActionDashboardsPermissionsRead: {dashboards.ScopeDashboardsAll},
|
||||
dashboards.ActionDashboardsPermissionsWrite: {dashboards.ScopeDashboardsAll},
|
||||
}
|
||||
permissionsWithUidScopes = map[string][]string{
|
||||
datasources.ActionIDRead: {},
|
||||
datasources.ActionDelete: {},
|
||||
ac.ActionDatasourcesExplore: {},
|
||||
datasources.ActionQuery: {},
|
||||
datasources.ActionRead: {
|
||||
datasources.ScopeProvider.GetResourceScopeUID("datasource-2"),
|
||||
datasources.ScopeProvider.GetResourceScopeUID("datasource-3"),
|
||||
},
|
||||
datasources.ActionWrite: {},
|
||||
datasources.ActionPermissionsRead: {},
|
||||
datasources.ActionPermissionsWrite: {},
|
||||
|
||||
dashboards.ActionFoldersCreate: {},
|
||||
dashboards.ActionFoldersRead: {
|
||||
dashboards.ScopeFoldersProvider.GetResourceScopeUID("ujaM1h6nz"),
|
||||
},
|
||||
dashboards.ActionFoldersWrite: {},
|
||||
dashboards.ActionFoldersDelete: {},
|
||||
dashboards.ActionFoldersPermissionsRead: {},
|
||||
dashboards.ActionFoldersPermissionsWrite: {},
|
||||
|
||||
dashboards.ActionDashboardsCreate: {},
|
||||
dashboards.ActionDashboardsRead: {},
|
||||
dashboards.ActionDashboardsWrite: {
|
||||
dashboards.ScopeDashboardsProvider.GetResourceScopeUID("7MeksYbmk"),
|
||||
},
|
||||
dashboards.ActionDashboardsDelete: {},
|
||||
dashboards.ActionDashboardsPermissionsRead: {},
|
||||
dashboards.ActionDashboardsPermissionsWrite: {},
|
||||
}
|
||||
)
|
||||
|
||||
func service(t *testing.T) *StandardSearchService {
|
||||
service, ok := ProvideService(nil, nil, nil, accesscontrolmock.New()).(*StandardSearchService)
|
||||
require.True(t, ok)
|
||||
return service
|
||||
}
|
||||
|
||||
func TestAllowedActionsForPermissionsWithScopeAll(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
permissions map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "scope_all",
|
||||
permissions: permissionsWithScopeAll,
|
||||
},
|
||||
{
|
||||
name: "scope_uids",
|
||||
permissions: permissionsWithUidScopes,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
frame := &data.Frame{}
|
||||
err := frame.UnmarshalJSON([]byte(exampleListFrameJSON))
|
||||
require.NoError(t, err)
|
||||
|
||||
err = service(t).addAllowedActionsField(context.Background(), orgId, &models.SignedInUser{
|
||||
Permissions: map[int64]map[string][]string{
|
||||
orgId: tt.permissions,
|
||||
},
|
||||
}, &backend.DataResponse{
|
||||
Frames: []*data.Frame{frame},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
experimental.CheckGoldenJSONFrame(t, "testdata", fmt.Sprintf("allowed_actions_%s.golden", tt.name), frame, true)
|
||||
}
|
||||
}
|
@ -18,9 +18,10 @@ type PermissionFilter struct {
|
||||
type entityKind string
|
||||
|
||||
const (
|
||||
entityKindPanel entityKind = "panel"
|
||||
entityKindDashboard entityKind = "dashboard"
|
||||
entityKindFolder entityKind = "folder"
|
||||
entityKindPanel entityKind = "panel"
|
||||
entityKindDashboard entityKind = "dashboard"
|
||||
entityKindFolder entityKind = "folder"
|
||||
entityKindDatasource entityKind = "datasource"
|
||||
)
|
||||
|
||||
func (r entityKind) IsValid() bool {
|
||||
|
@ -175,5 +175,13 @@ func (s *StandardSearchService) DoDashboardQuery(ctx context.Context, user *back
|
||||
return rsp
|
||||
}
|
||||
|
||||
return doSearchQuery(ctx, s.logger, index, filter, q, s.extender.GetQueryExtender(q), s.cfg.AppSubURL)
|
||||
response := doSearchQuery(ctx, s.logger, index, filter, q, s.extender.GetQueryExtender(q), s.cfg.AppSubURL)
|
||||
|
||||
if q.WithAllowedActions {
|
||||
if err := s.addAllowedActionsField(ctx, orgID, signedInUser, response); err != nil {
|
||||
s.logger.Error("error when adding the allowedActions field", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
321
pkg/services/searchV2/testdata/allowed_actions_scope_all.golden.jsonc
vendored
Normal file
321
pkg/services/searchV2/testdata/allowed_actions_scope_all.golden.jsonc
vendored
Normal file
@ -0,0 +1,321 @@
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
// Frame[0] {
|
||||
// "type": "search-results",
|
||||
// "custom": {
|
||||
// "count": 106,
|
||||
// "locationInfo": {
|
||||
// "yboVMzb7z": {
|
||||
// "kind": "folder",
|
||||
// "name": "gdev dashboards",
|
||||
// "url": "/dashboards/f/yboVMzb7z/gdev-dashboards"
|
||||
// }
|
||||
// },
|
||||
// "sortBy": "name_sort"
|
||||
// }
|
||||
// }
|
||||
// Name: Query results
|
||||
// Dimensions: 9 Fields by 4 Rows
|
||||
// +----------------+----------------+-------------------------+------------------+--------------------------------------+--------------------------+---------------------------+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
// | Name: kind | Name: uid | Name: name | Name: panel_type | Name: url | Name: tags | Name: ds_uid | Name: location | Name: allowed_actions |
|
||||
// | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: |
|
||||
// | Type: []string | Type: []string | Type: []string | Type: []string | Type: []string | Type: []*json.RawMessage | Type: []*json.RawMessage | Type: []string | Type: []json.RawMessage |
|
||||
// +----------------+----------------+-------------------------+------------------+--------------------------------------+--------------------------+---------------------------+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
// | folder | ujaM1h6nz | abc2 | | /dashboards/f/ujaM1h6nz/abc2 | null | null | | [{"kind":"folder","uid":"ujaM1h6nz","actions":["folders.permissions:read","folders.permissions:write","folders:create","folders:delete","folders:read","folders:write"]}] |
|
||||
// | dashboard | 7MeksYbmk | Alerting with TestData | | /d/7MeksYbmk/alerting-with-testdata | [ | [ | yboVMzb7z | [{"kind":"dashboard","uid":"7MeksYbmk","actions":["dashboards.permissions:read","dashboards.permissions:write","dashboards:create","dashboards:delete","dashboards:read","dashboards:write"]},{"kind":"datasource","uid":"datasource-1","actions":["datasources.id:read","datasources.permissions:read","datasources.permissions:write","datasources:delete","datasources:explore","datasources:query","datasources:read","datasources:write"]}] |
|
||||
// | | | | | | "gdev", | "datasource-1" | | |
|
||||
// | | | | | | "alerting" | ] | | |
|
||||
// | | | | | | ] | | | |
|
||||
// | dashboard | vmie2cmWz | Bar Gauge Demo | | /d/vmie2cmWz/bar-gauge-demo | [ | [ | yboVMzb7z | [{"kind":"dashboard","uid":"vmie2cmWz","actions":["dashboards.permissions:read","dashboards.permissions:write","dashboards:create","dashboards:delete","dashboards:read","dashboards:write"]},{"kind":"datasource","uid":"datasource-2","actions":["datasources.id:read","datasources.permissions:read","datasources.permissions:write","datasources:delete","datasources:explore","datasources:query","datasources:read","datasources:write"]},{"kind":"datasource","uid":"datasource-3","actions":["datasources.id:read","datasources.permissions:read","datasources.permissions:write","datasources:delete","datasources:explore","datasources:query","datasources:read","datasources:write"]},{"kind":"datasource","uid":"datasource-4","actions":["datasources.id:read","datasources.permissions:read","datasources.permissions:write","datasources:delete","datasources:explore","datasources:query","datasources:read","datasources:write"]}] |
|
||||
// | | | | | | "gdev", | "datasource-2", | | |
|
||||
// | | | | | | "demo" | "datasource-3", | | |
|
||||
// | | | | | | ] | "datasource-4" | | |
|
||||
// | | | | | | | ] | | |
|
||||
// | dashboard | xMsQdBfWz | Bar Gauge Demo Unfilled | | /d/xMsQdBfWz/bar-gauge-demo-unfilled | [ | [] | yboVMzb7z | [{"kind":"dashboard","uid":"xMsQdBfWz","actions":["dashboards.permissions:read","dashboards.permissions:write","dashboards:create","dashboards:delete","dashboards:read","dashboards:write"]}] |
|
||||
// | | | | | | "gdev", | | | |
|
||||
// | | | | | | "demo" | | | |
|
||||
// | | | | | | ] | | | |
|
||||
// +----------------+----------------+-------------------------+------------------+--------------------------------------+--------------------------+---------------------------+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
//
|
||||
//
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
{
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "Query results",
|
||||
"refId": "Search",
|
||||
"meta": {
|
||||
"type": "search-results",
|
||||
"custom": {
|
||||
"count": 106,
|
||||
"locationInfo": {
|
||||
"yboVMzb7z": {
|
||||
"kind": "folder",
|
||||
"name": "gdev dashboards",
|
||||
"url": "/dashboards/f/yboVMzb7z/gdev-dashboards"
|
||||
}
|
||||
},
|
||||
"sortBy": "name_sort"
|
||||
}
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "kind",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "uid",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "panel_type",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
},
|
||||
"config": {
|
||||
"links": [
|
||||
{
|
||||
"title": "link",
|
||||
"url": "${__value.text}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ds_uid",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "location",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "allowed_actions",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"folder",
|
||||
"dashboard",
|
||||
"dashboard",
|
||||
"dashboard"
|
||||
],
|
||||
[
|
||||
"ujaM1h6nz",
|
||||
"7MeksYbmk",
|
||||
"vmie2cmWz",
|
||||
"xMsQdBfWz"
|
||||
],
|
||||
[
|
||||
"abc2",
|
||||
"Alerting with TestData",
|
||||
"Bar Gauge Demo",
|
||||
"Bar Gauge Demo Unfilled"
|
||||
],
|
||||
[
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
],
|
||||
[
|
||||
"/dashboards/f/ujaM1h6nz/abc2",
|
||||
"/d/7MeksYbmk/alerting-with-testdata",
|
||||
"/d/vmie2cmWz/bar-gauge-demo",
|
||||
"/d/xMsQdBfWz/bar-gauge-demo-unfilled"
|
||||
],
|
||||
[
|
||||
null,
|
||||
[
|
||||
"gdev",
|
||||
"alerting"
|
||||
],
|
||||
[
|
||||
"gdev",
|
||||
"demo"
|
||||
],
|
||||
[
|
||||
"gdev",
|
||||
"demo"
|
||||
]
|
||||
],
|
||||
[
|
||||
null,
|
||||
[
|
||||
"datasource-1"
|
||||
],
|
||||
[
|
||||
"datasource-2",
|
||||
"datasource-3",
|
||||
"datasource-4"
|
||||
],
|
||||
[]
|
||||
],
|
||||
[
|
||||
"",
|
||||
"yboVMzb7z",
|
||||
"yboVMzb7z",
|
||||
"yboVMzb7z"
|
||||
],
|
||||
[
|
||||
[
|
||||
{
|
||||
"kind": "folder",
|
||||
"uid": "ujaM1h6nz",
|
||||
"actions": [
|
||||
"folders.permissions:read",
|
||||
"folders.permissions:write",
|
||||
"folders:create",
|
||||
"folders:delete",
|
||||
"folders:read",
|
||||
"folders:write"
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"kind": "dashboard",
|
||||
"uid": "7MeksYbmk",
|
||||
"actions": [
|
||||
"dashboards.permissions:read",
|
||||
"dashboards.permissions:write",
|
||||
"dashboards:create",
|
||||
"dashboards:delete",
|
||||
"dashboards:read",
|
||||
"dashboards:write"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "datasource",
|
||||
"uid": "datasource-1",
|
||||
"actions": [
|
||||
"datasources.id:read",
|
||||
"datasources.permissions:read",
|
||||
"datasources.permissions:write",
|
||||
"datasources:delete",
|
||||
"datasources:explore",
|
||||
"datasources:query",
|
||||
"datasources:read",
|
||||
"datasources:write"
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"kind": "dashboard",
|
||||
"uid": "vmie2cmWz",
|
||||
"actions": [
|
||||
"dashboards.permissions:read",
|
||||
"dashboards.permissions:write",
|
||||
"dashboards:create",
|
||||
"dashboards:delete",
|
||||
"dashboards:read",
|
||||
"dashboards:write"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "datasource",
|
||||
"uid": "datasource-2",
|
||||
"actions": [
|
||||
"datasources.id:read",
|
||||
"datasources.permissions:read",
|
||||
"datasources.permissions:write",
|
||||
"datasources:delete",
|
||||
"datasources:explore",
|
||||
"datasources:query",
|
||||
"datasources:read",
|
||||
"datasources:write"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "datasource",
|
||||
"uid": "datasource-3",
|
||||
"actions": [
|
||||
"datasources.id:read",
|
||||
"datasources.permissions:read",
|
||||
"datasources.permissions:write",
|
||||
"datasources:delete",
|
||||
"datasources:explore",
|
||||
"datasources:query",
|
||||
"datasources:read",
|
||||
"datasources:write"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "datasource",
|
||||
"uid": "datasource-4",
|
||||
"actions": [
|
||||
"datasources.id:read",
|
||||
"datasources.permissions:read",
|
||||
"datasources.permissions:write",
|
||||
"datasources:delete",
|
||||
"datasources:explore",
|
||||
"datasources:query",
|
||||
"datasources:read",
|
||||
"datasources:write"
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"kind": "dashboard",
|
||||
"uid": "xMsQdBfWz",
|
||||
"actions": [
|
||||
"dashboards.permissions:read",
|
||||
"dashboards.permissions:write",
|
||||
"dashboards:create",
|
||||
"dashboards:delete",
|
||||
"dashboards:read",
|
||||
"dashboards:write"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
265
pkg/services/searchV2/testdata/allowed_actions_scope_uids.golden.jsonc
vendored
Normal file
265
pkg/services/searchV2/testdata/allowed_actions_scope_uids.golden.jsonc
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
// Frame[0] {
|
||||
// "type": "search-results",
|
||||
// "custom": {
|
||||
// "count": 106,
|
||||
// "locationInfo": {
|
||||
// "yboVMzb7z": {
|
||||
// "kind": "folder",
|
||||
// "name": "gdev dashboards",
|
||||
// "url": "/dashboards/f/yboVMzb7z/gdev-dashboards"
|
||||
// }
|
||||
// },
|
||||
// "sortBy": "name_sort"
|
||||
// }
|
||||
// }
|
||||
// Name: Query results
|
||||
// Dimensions: 9 Fields by 4 Rows
|
||||
// +----------------+----------------+-------------------------+------------------+--------------------------------------+--------------------------+---------------------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
// | Name: kind | Name: uid | Name: name | Name: panel_type | Name: url | Name: tags | Name: ds_uid | Name: location | Name: allowed_actions |
|
||||
// | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: |
|
||||
// | Type: []string | Type: []string | Type: []string | Type: []string | Type: []string | Type: []*json.RawMessage | Type: []*json.RawMessage | Type: []string | Type: []json.RawMessage |
|
||||
// +----------------+----------------+-------------------------+------------------+--------------------------------------+--------------------------+---------------------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
// | folder | ujaM1h6nz | abc2 | | /dashboards/f/ujaM1h6nz/abc2 | null | null | | [{"kind":"folder","uid":"ujaM1h6nz","actions":["folders:read"]}] |
|
||||
// | dashboard | 7MeksYbmk | Alerting with TestData | | /d/7MeksYbmk/alerting-with-testdata | [ | [ | yboVMzb7z | [{"kind":"dashboard","uid":"7MeksYbmk","actions":["dashboards:write"]},{"kind":"datasource","uid":"datasource-1","actions":[]}] |
|
||||
// | | | | | | "gdev", | "datasource-1" | | |
|
||||
// | | | | | | "alerting" | ] | | |
|
||||
// | | | | | | ] | | | |
|
||||
// | dashboard | vmie2cmWz | Bar Gauge Demo | | /d/vmie2cmWz/bar-gauge-demo | [ | [ | yboVMzb7z | [{"kind":"dashboard","uid":"vmie2cmWz","actions":[]},{"kind":"datasource","uid":"datasource-2","actions":["datasources:read"]},{"kind":"datasource","uid":"datasource-3","actions":["datasources:read"]},{"kind":"datasource","uid":"datasource-4","actions":[]}] |
|
||||
// | | | | | | "gdev", | "datasource-2", | | |
|
||||
// | | | | | | "demo" | "datasource-3", | | |
|
||||
// | | | | | | ] | "datasource-4" | | |
|
||||
// | | | | | | | ] | | |
|
||||
// | dashboard | xMsQdBfWz | Bar Gauge Demo Unfilled | | /d/xMsQdBfWz/bar-gauge-demo-unfilled | [ | [] | yboVMzb7z | [{"kind":"dashboard","uid":"xMsQdBfWz","actions":[]}] |
|
||||
// | | | | | | "gdev", | | | |
|
||||
// | | | | | | "demo" | | | |
|
||||
// | | | | | | ] | | | |
|
||||
// +----------------+----------------+-------------------------+------------------+--------------------------------------+--------------------------+---------------------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
//
|
||||
//
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
{
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "Query results",
|
||||
"refId": "Search",
|
||||
"meta": {
|
||||
"type": "search-results",
|
||||
"custom": {
|
||||
"count": 106,
|
||||
"locationInfo": {
|
||||
"yboVMzb7z": {
|
||||
"kind": "folder",
|
||||
"name": "gdev dashboards",
|
||||
"url": "/dashboards/f/yboVMzb7z/gdev-dashboards"
|
||||
}
|
||||
},
|
||||
"sortBy": "name_sort"
|
||||
}
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "kind",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "uid",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "panel_type",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
},
|
||||
"config": {
|
||||
"links": [
|
||||
{
|
||||
"title": "link",
|
||||
"url": "${__value.text}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ds_uid",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "location",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "allowed_actions",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"folder",
|
||||
"dashboard",
|
||||
"dashboard",
|
||||
"dashboard"
|
||||
],
|
||||
[
|
||||
"ujaM1h6nz",
|
||||
"7MeksYbmk",
|
||||
"vmie2cmWz",
|
||||
"xMsQdBfWz"
|
||||
],
|
||||
[
|
||||
"abc2",
|
||||
"Alerting with TestData",
|
||||
"Bar Gauge Demo",
|
||||
"Bar Gauge Demo Unfilled"
|
||||
],
|
||||
[
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
],
|
||||
[
|
||||
"/dashboards/f/ujaM1h6nz/abc2",
|
||||
"/d/7MeksYbmk/alerting-with-testdata",
|
||||
"/d/vmie2cmWz/bar-gauge-demo",
|
||||
"/d/xMsQdBfWz/bar-gauge-demo-unfilled"
|
||||
],
|
||||
[
|
||||
null,
|
||||
[
|
||||
"gdev",
|
||||
"alerting"
|
||||
],
|
||||
[
|
||||
"gdev",
|
||||
"demo"
|
||||
],
|
||||
[
|
||||
"gdev",
|
||||
"demo"
|
||||
]
|
||||
],
|
||||
[
|
||||
null,
|
||||
[
|
||||
"datasource-1"
|
||||
],
|
||||
[
|
||||
"datasource-2",
|
||||
"datasource-3",
|
||||
"datasource-4"
|
||||
],
|
||||
[]
|
||||
],
|
||||
[
|
||||
"",
|
||||
"yboVMzb7z",
|
||||
"yboVMzb7z",
|
||||
"yboVMzb7z"
|
||||
],
|
||||
[
|
||||
[
|
||||
{
|
||||
"kind": "folder",
|
||||
"uid": "ujaM1h6nz",
|
||||
"actions": [
|
||||
"folders:read"
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"kind": "dashboard",
|
||||
"uid": "7MeksYbmk",
|
||||
"actions": [
|
||||
"dashboards:write"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "datasource",
|
||||
"uid": "datasource-1",
|
||||
"actions": []
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"kind": "dashboard",
|
||||
"uid": "vmie2cmWz",
|
||||
"actions": []
|
||||
},
|
||||
{
|
||||
"kind": "datasource",
|
||||
"uid": "datasource-2",
|
||||
"actions": [
|
||||
"datasources:read"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "datasource",
|
||||
"uid": "datasource-3",
|
||||
"actions": [
|
||||
"datasources:read"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "datasource",
|
||||
"uid": "datasource-4",
|
||||
"actions": []
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"kind": "dashboard",
|
||||
"uid": "xMsQdBfWz",
|
||||
"actions": []
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
155
pkg/services/searchV2/testdata/search_response_frame.json
vendored
Normal file
155
pkg/services/searchV2/testdata/search_response_frame.json
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
{
|
||||
"schema": {
|
||||
"name": "Query results",
|
||||
"refId": "Search",
|
||||
"meta": {
|
||||
"type": "search-results",
|
||||
"custom": {
|
||||
"count": 106,
|
||||
"locationInfo": {
|
||||
"yboVMzb7z": {
|
||||
"name": "gdev dashboards",
|
||||
"kind": "folder",
|
||||
"url": "/dashboards/f/yboVMzb7z/gdev-dashboards"
|
||||
}
|
||||
},
|
||||
"sortBy": "name_sort"
|
||||
}
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "kind",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "uid",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "panel_type",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
},
|
||||
"config": {
|
||||
"links": [
|
||||
{
|
||||
"title": "link",
|
||||
"url": "${__value.text}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ds_uid",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "location",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"folder",
|
||||
"dashboard",
|
||||
"dashboard",
|
||||
"dashboard"
|
||||
],
|
||||
[
|
||||
"ujaM1h6nz",
|
||||
"7MeksYbmk",
|
||||
"vmie2cmWz",
|
||||
"xMsQdBfWz"
|
||||
],
|
||||
[
|
||||
"abc2",
|
||||
"Alerting with TestData",
|
||||
"Bar Gauge Demo",
|
||||
"Bar Gauge Demo Unfilled"
|
||||
],
|
||||
[
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
],
|
||||
[
|
||||
"/dashboards/f/ujaM1h6nz/abc2",
|
||||
"/d/7MeksYbmk/alerting-with-testdata",
|
||||
"/d/vmie2cmWz/bar-gauge-demo",
|
||||
"/d/xMsQdBfWz/bar-gauge-demo-unfilled"
|
||||
],
|
||||
[
|
||||
null,
|
||||
[
|
||||
"gdev",
|
||||
"alerting"
|
||||
],
|
||||
[
|
||||
"gdev",
|
||||
"demo"
|
||||
],
|
||||
[
|
||||
"gdev",
|
||||
"demo"
|
||||
]
|
||||
],
|
||||
[
|
||||
null,
|
||||
[
|
||||
"datasource-1"
|
||||
],
|
||||
[
|
||||
"datasource-2",
|
||||
"datasource-3",
|
||||
"datasource-4"
|
||||
],
|
||||
[]
|
||||
],
|
||||
[
|
||||
"",
|
||||
"yboVMzb7z",
|
||||
"yboVMzb7z",
|
||||
"yboVMzb7z"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
@ -14,21 +14,21 @@ type FacetField struct {
|
||||
}
|
||||
|
||||
type DashboardQuery struct {
|
||||
Query string `json:"query"`
|
||||
Location string `json:"location,omitempty"` // parent folder ID
|
||||
Sort string `json:"sort,omitempty"` // field ASC/DESC
|
||||
Datasource string `json:"ds_uid,omitempty"` // "datasource" collides with the JSON value at the same leel :()
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Kind []string `json:"kind,omitempty"`
|
||||
PanelType string `json:"panel_type,omitempty"`
|
||||
UIDs []string `json:"uid,omitempty"`
|
||||
Explain bool `json:"explain,omitempty"` // adds details on why document matched
|
||||
Facet []FacetField `json:"facet,omitempty"`
|
||||
SkipLocation bool `json:"skipLocation,omitempty"`
|
||||
AccessInfo bool `json:"accessInfo,omitempty"` // adds field for access control
|
||||
HasPreview string `json:"hasPreview,omitempty"` // the light|dark theme
|
||||
Limit int `json:"limit,omitempty"` // explicit page size
|
||||
From int `json:"from,omitempty"` // for paging
|
||||
Query string `json:"query"`
|
||||
Location string `json:"location,omitempty"` // parent folder ID
|
||||
Sort string `json:"sort,omitempty"` // field ASC/DESC
|
||||
Datasource string `json:"ds_uid,omitempty"` // "datasource" collides with the JSON value at the same leel :()
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Kind []string `json:"kind,omitempty"`
|
||||
PanelType string `json:"panel_type,omitempty"`
|
||||
UIDs []string `json:"uid,omitempty"`
|
||||
Explain bool `json:"explain,omitempty"` // adds details on why document matched
|
||||
WithAllowedActions bool `json:"withAllowedActions,omitempty"` // adds allowed actions per entity
|
||||
Facet []FacetField `json:"facet,omitempty"`
|
||||
SkipLocation bool `json:"skipLocation,omitempty"`
|
||||
HasPreview string `json:"hasPreview,omitempty"` // the light|dark theme
|
||||
Limit int `json:"limit,omitempty"` // explicit page size
|
||||
From int `json:"from,omitempty"` // for paging
|
||||
}
|
||||
|
||||
type SearchService interface {
|
||||
|
@ -14,6 +14,7 @@ export interface Props {
|
||||
const tabs = [
|
||||
{ label: 'Score', value: 'score' },
|
||||
{ label: 'Fields', value: 'fields' },
|
||||
{ label: 'Allowed actions', value: 'allowed_actions' },
|
||||
];
|
||||
|
||||
export function ExplainScorePopup({ name, explain, frame, row }: Props) {
|
||||
@ -51,6 +52,21 @@ export function ExplainScorePopup({ name, explain, frame, row }: Props) {
|
||||
<DataHoverView data={frame} rowIndex={row} />
|
||||
</div>
|
||||
)}
|
||||
{activeTab === tabs[2].value && (
|
||||
<CodeEditor
|
||||
width="100%"
|
||||
height="70vh"
|
||||
language="json"
|
||||
showLineNumbers={false}
|
||||
showMiniMap={false}
|
||||
value={(() => {
|
||||
const allowedActions = frame.fields.find((f) => f.name === 'allowed_actions')?.values?.get(row);
|
||||
const dsUids = frame.fields.find((f) => f.name === 'ds_uid')?.values?.get(row);
|
||||
return JSON.stringify({ dsUids: dsUids ?? [], allowedActions: allowedActions ?? [] }, null, 2);
|
||||
})()}
|
||||
readOnly={false}
|
||||
/>
|
||||
)}
|
||||
</TabContent>
|
||||
</Modal>
|
||||
);
|
||||
|
@ -65,6 +65,7 @@ export const SearchView = ({
|
||||
location: folderDTO?.uid, // This will scope all results to the prefix
|
||||
sort: query.sort?.value,
|
||||
explain: query.explain,
|
||||
withAllowedActions: query.explain, // allowedActions are currently not used for anything on the UI and added only in `explain` mode
|
||||
};
|
||||
|
||||
// Only dashboards have additional properties
|
||||
|
@ -18,6 +18,7 @@ export interface SearchQuery {
|
||||
id?: number[];
|
||||
facet?: FacetField[];
|
||||
explain?: boolean;
|
||||
withAllowedActions?: boolean;
|
||||
accessInfo?: boolean;
|
||||
hasPreview?: string; // theme
|
||||
limit?: number;
|
||||
|
Loading…
Reference in New Issue
Block a user