mirror of https://github.com/grafana/grafana.git
Dashboard search: Return description in search results (#110857)
* DashList: Add description * Support unified storage * Support unified storage[2] * Exclude description from field * Cleanup * add description * Revert dashlist changes * Update cue * Fix test --------- Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
parent
fad8891b1a
commit
571b3226ba
|
|
@ -61,18 +61,20 @@ type DashboardHit struct {
|
|||
Resource string `json:"resource"` // dashboards | folders
|
||||
// The k8s "name" (eg, grafana UID)
|
||||
Name string `json:"name"`
|
||||
// The display nam
|
||||
// The display name
|
||||
Title string `json:"title"`
|
||||
// Dashboard description
|
||||
Description string `json:"description,omitempty"`
|
||||
// Filter tags
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
// The k8s name (eg, grafana UID) for the parent folder
|
||||
Folder string `json:"folder,omitempty"`
|
||||
// Stick untyped extra fields in this object (including the sort value)
|
||||
Field *common.Unstructured `json:"field,omitempty"`
|
||||
Field *common.Unstructured `json:"field,omitzero,omitempty"`
|
||||
// When using "real" search, this is the score
|
||||
Score float64 `json:"score,omitempty"`
|
||||
// Explain the score (if possible)
|
||||
Explain *common.Unstructured `json:"explain,omitempty"`
|
||||
Explain *common.Unstructured `json:"explain,omitzero,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
|
|
|||
|
|
@ -302,12 +302,19 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardHit(ref common.ReferenceCallbac
|
|||
},
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The display nam",
|
||||
Description: "The display name",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"description": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Dashboard description",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"tags": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Filter tags",
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ func (s *SearchHandler) DoSearch(w http.ResponseWriter, r *http.Request) {
|
|||
Page: int64(page), // for modes 0-2 (legacy)
|
||||
Explain: queryParams.Has("explain") && queryParams.Get("explain") != "false",
|
||||
}
|
||||
fields := []string{"title", "folder", "tags"}
|
||||
fields := []string{"title", "folder", "tags", "description"}
|
||||
if queryParams.Has("field") {
|
||||
// add fields to search and exclude duplicates
|
||||
for _, f := range queryParams["field"] {
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ func TestSearchHandler(t *testing.T) {
|
|||
if mockClient.LastSearchRequest == nil {
|
||||
t.Fatalf("expected Search to be called, but it was not")
|
||||
}
|
||||
expectedFields := []string{"title", "folder", "tags", "field1", "field2", "field3"}
|
||||
expectedFields := []string{"title", "folder", "tags", "description", "field1", "field2", "field3"}
|
||||
if fmt.Sprintf("%v", mockClient.LastSearchRequest.Fields) != fmt.Sprintf("%v", expectedFields) {
|
||||
t.Errorf("expected fields %v, got %v", expectedFields, mockClient.LastSearchRequest.Fields)
|
||||
}
|
||||
|
|
@ -242,7 +242,7 @@ func TestSearchHandler(t *testing.T) {
|
|||
if mockClient.LastSearchRequest == nil {
|
||||
t.Fatalf("expected Search to be called, but it was not")
|
||||
}
|
||||
expectedFields := []string{"title", "folder", "tags", "field1"}
|
||||
expectedFields := []string{"title", "folder", "tags", "description", "field1"}
|
||||
if fmt.Sprintf("%v", mockClient.LastSearchRequest.Fields) != fmt.Sprintf("%v", expectedFields) {
|
||||
t.Errorf("expected fields %v, got %v", expectedFields, mockClient.LastSearchRequest.Fields)
|
||||
}
|
||||
|
|
@ -271,7 +271,7 @@ func TestSearchHandler(t *testing.T) {
|
|||
if mockClient.LastSearchRequest == nil {
|
||||
t.Fatalf("expected Search to be called, but it was not")
|
||||
}
|
||||
expectedFields := []string{"title", "folder", "tags"}
|
||||
expectedFields := []string{"title", "folder", "tags", "description"}
|
||||
if fmt.Sprintf("%v", mockClient.LastSearchRequest.Fields) != fmt.Sprintf("%v", expectedFields) {
|
||||
t.Errorf("expected fields %v, got %v", expectedFields, mockClient.LastSearchRequest.Fields)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1310,6 +1310,7 @@ func makeQueryResult(query *dashboards.FindPersistedDashboardsQuery, res []dashb
|
|||
FolderUID: item.FolderUID,
|
||||
FolderTitle: item.FolderTitle,
|
||||
Tags: item.Tags,
|
||||
Description: item.Description,
|
||||
}
|
||||
|
||||
if item.FolderUID != "" {
|
||||
|
|
|
|||
|
|
@ -318,13 +318,14 @@ type SaveDashboardDTO struct {
|
|||
}
|
||||
|
||||
type DashboardSearchProjection struct {
|
||||
ID int64 `xorm:"id"`
|
||||
UID string `xorm:"uid"`
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
Title string
|
||||
Slug string
|
||||
Term string
|
||||
IsFolder bool
|
||||
ID int64 `xorm:"id"`
|
||||
UID string `xorm:"uid"`
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
Title string
|
||||
Slug string
|
||||
Term string
|
||||
Description string
|
||||
IsFolder bool
|
||||
// Deprecated: use FolderUID instead
|
||||
FolderID int64 `xorm:"folder_id"`
|
||||
FolderUID string `xorm:"folder_uid"`
|
||||
|
|
|
|||
|
|
@ -1468,6 +1468,7 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *dashb
|
|||
OrgID: query.OrgId,
|
||||
Title: hit.Title,
|
||||
Slug: slugify.Slugify(hit.Title),
|
||||
Description: hit.Description,
|
||||
IsFolder: false,
|
||||
FolderUID: hit.Folder,
|
||||
FolderTitle: folderTitle,
|
||||
|
|
@ -1569,6 +1570,7 @@ func makeQueryResult(query *dashboards.FindPersistedDashboardsQuery, res []dashb
|
|||
FolderUID: item.FolderUID,
|
||||
FolderTitle: item.FolderTitle,
|
||||
Tags: []string{},
|
||||
Description: item.Description,
|
||||
}
|
||||
|
||||
if item.Tags != nil {
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@ import (
|
|||
|
||||
var (
|
||||
excludedFields = map[string]string{
|
||||
resource.SEARCH_FIELD_EXPLAIN: "",
|
||||
resource.SEARCH_FIELD_SCORE: "",
|
||||
resource.SEARCH_FIELD_TITLE: "",
|
||||
resource.SEARCH_FIELD_FOLDER: "",
|
||||
resource.SEARCH_FIELD_TAGS: "",
|
||||
resource.SEARCH_FIELD_EXPLAIN: "",
|
||||
resource.SEARCH_FIELD_SCORE: "",
|
||||
resource.SEARCH_FIELD_TITLE: "",
|
||||
resource.SEARCH_FIELD_FOLDER: "",
|
||||
resource.SEARCH_FIELD_TAGS: "",
|
||||
resource.SEARCH_FIELD_DESCRIPTION: "",
|
||||
}
|
||||
|
||||
IncludeFields = []string{
|
||||
|
|
@ -26,6 +27,7 @@ var (
|
|||
resource.SEARCH_FIELD_TAGS,
|
||||
resource.SEARCH_FIELD_LABELS,
|
||||
resource.SEARCH_FIELD_FOLDER,
|
||||
resource.SEARCH_FIELD_DESCRIPTION,
|
||||
resource.SEARCH_FIELD_CREATED,
|
||||
resource.SEARCH_FIELD_CREATED_BY,
|
||||
resource.SEARCH_FIELD_UPDATED,
|
||||
|
|
@ -38,6 +40,7 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// nolint:gocyclo
|
||||
func ParseResults(result *resourcepb.ResourceSearchResponse, offset int64) (v0alpha1.SearchResults, error) {
|
||||
if result == nil {
|
||||
return v0alpha1.SearchResults{}, nil
|
||||
|
|
@ -50,6 +53,7 @@ func ParseResults(result *resourcepb.ResourceSearchResponse, offset int64) (v0al
|
|||
titleIDX := -1
|
||||
folderIDX := -1
|
||||
tagsIDX := -1
|
||||
descriptionIDX := -1
|
||||
scoreIDX := -1
|
||||
explainIDX := -1
|
||||
|
||||
|
|
@ -65,6 +69,8 @@ func ParseResults(result *resourcepb.ResourceSearchResponse, offset int64) (v0al
|
|||
folderIDX = i
|
||||
case resource.SEARCH_FIELD_TAGS:
|
||||
tagsIDX = i
|
||||
case resource.SEARCH_FIELD_DESCRIPTION:
|
||||
descriptionIDX = i
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +119,9 @@ func ParseResults(result *resourcepb.ResourceSearchResponse, offset int64) (v0al
|
|||
if folderIDX >= 0 && row.Cells[folderIDX] != nil {
|
||||
hit.Folder = string(row.Cells[folderIDX])
|
||||
}
|
||||
if descriptionIDX >= 0 && row.Cells[descriptionIDX] != nil {
|
||||
hit.Description = string(row.Cells[descriptionIDX])
|
||||
}
|
||||
if tagsIDX >= 0 && row.Cells[tagsIDX] != nil {
|
||||
_ = json.Unmarshal(row.Cells[tagsIDX], &hit.Tags)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ func TestParseResults(t *testing.T) {
|
|||
Name: search.DASHBOARD_LINK_COUNT,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT32,
|
||||
},
|
||||
{
|
||||
Name: "description",
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
},
|
||||
},
|
||||
Rows: []*resourcepb.ResourceTableRow{
|
||||
{
|
||||
|
|
@ -44,6 +48,7 @@ func TestParseResults(t *testing.T) {
|
|||
[]byte("folder1"),
|
||||
[]byte("100"),
|
||||
[]byte("25"),
|
||||
[]byte("description"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -51,8 +56,10 @@ func TestParseResults(t *testing.T) {
|
|||
TotalHits: 1,
|
||||
}
|
||||
|
||||
_, err := ParseResults(resSearchResp, 0)
|
||||
results, err := ParseResults(resSearchResp, 0)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results.Hits, 1)
|
||||
require.Equal(t, "description", results.Hits[0].Description)
|
||||
})
|
||||
|
||||
t.Run("should return error when trying to parse results with mismatch length between Columns and row Cells", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -224,14 +224,15 @@ func (s *Service) searchFoldersFromApiServer(ctx context.Context, query folder.S
|
|||
for i, item := range parsedResults.Hits {
|
||||
slug := slugify.Slugify(item.Title)
|
||||
hitList[i] = &model.Hit{
|
||||
ID: item.Field.GetNestedInt64(resource.SEARCH_FIELD_LEGACY_ID),
|
||||
UID: item.Name,
|
||||
OrgID: query.OrgID,
|
||||
Title: item.Title,
|
||||
URI: "db/" + slug,
|
||||
URL: dashboards.GetFolderURL(item.Name, slug),
|
||||
Type: model.DashHitFolder,
|
||||
FolderUID: item.Folder,
|
||||
ID: item.Field.GetNestedInt64(resource.SEARCH_FIELD_LEGACY_ID),
|
||||
UID: item.Name,
|
||||
OrgID: query.OrgID,
|
||||
Title: item.Title,
|
||||
URI: "db/" + slug,
|
||||
URL: dashboards.GetFolderURL(item.Name, slug),
|
||||
Type: model.DashHitFolder,
|
||||
FolderUID: item.Folder,
|
||||
Description: item.Description,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ type Hit struct {
|
|||
Type HitType `json:"type"`
|
||||
Tags []string `json:"tags"`
|
||||
IsStarred bool `json:"isStarred"`
|
||||
Description string `json:"description,omitempty"`
|
||||
FolderID int64 `json:"folderId,omitempty"` // Deprecated: use FolderUID instead
|
||||
FolderUID string `json:"folderUid,omitempty"`
|
||||
FolderTitle string `json:"folderTitle,omitempty"`
|
||||
|
|
|
|||
|
|
@ -5138,6 +5138,9 @@
|
|||
"Hit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"folderId": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
|
|
|
|||
|
|
@ -17024,6 +17024,9 @@
|
|||
"Hit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"folderId": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
|
|
|
|||
|
|
@ -6551,6 +6551,9 @@
|
|||
},
|
||||
"Hit": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"folderId": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
|
|
|
|||
Loading…
Reference in New Issue