178 lines
5.3 KiB
Go
178 lines
5.3 KiB
Go
package dashboardsearch
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
|
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
|
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
|
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
|
)
|
|
|
|
var (
|
|
// These fields exist at the top-level of DashboardHit
|
|
standardFields = 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_DESCRIPTION: "",
|
|
resource.SEARCH_FIELD_MANAGER_ID: "",
|
|
resource.SEARCH_FIELD_MANAGER_KIND: "",
|
|
}
|
|
|
|
IncludeFields = []string{
|
|
resource.SEARCH_FIELD_TITLE,
|
|
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,
|
|
resource.SEARCH_FIELD_UPDATED_BY,
|
|
resource.SEARCH_FIELD_MANAGER_KIND,
|
|
resource.SEARCH_FIELD_MANAGER_ID,
|
|
resource.SEARCH_FIELD_SOURCE_PATH,
|
|
resource.SEARCH_FIELD_SOURCE_CHECKSUM,
|
|
resource.SEARCH_FIELD_SOURCE_TIME,
|
|
// below is needed to determine whether a provisioned dashboard exists or not
|
|
resource.SEARCH_FIELD_LEGACY_ID,
|
|
resource.SEARCH_FIELD_LABELS + "." + resource.SEARCH_FIELD_LEGACY_ID,
|
|
}
|
|
)
|
|
|
|
// nolint:gocyclo
|
|
func ParseResults(result *resourcepb.ResourceSearchResponse, offset int64) (v0alpha1.SearchResults, error) {
|
|
if result == nil {
|
|
return v0alpha1.SearchResults{}, nil
|
|
} else if result.Error != nil {
|
|
return v0alpha1.SearchResults{}, fmt.Errorf("%d error searching: %s: %s", result.Error.Code, result.Error.Message, result.Error.Details)
|
|
} else if result.Results == nil {
|
|
return v0alpha1.SearchResults{}, nil
|
|
}
|
|
|
|
titleIDX := -1
|
|
folderIDX := -1
|
|
tagsIDX := -1
|
|
descriptionIDX := -1
|
|
scoreIDX := -1
|
|
explainIDX := -1
|
|
managerKindIDX := -1
|
|
managerIdIDX := -1
|
|
|
|
for i, v := range result.Results.Columns {
|
|
switch v.Name {
|
|
case resource.SEARCH_FIELD_EXPLAIN:
|
|
explainIDX = i
|
|
case resource.SEARCH_FIELD_SCORE:
|
|
scoreIDX = i
|
|
case resource.SEARCH_FIELD_TITLE:
|
|
titleIDX = i
|
|
case resource.SEARCH_FIELD_FOLDER:
|
|
folderIDX = i
|
|
case resource.SEARCH_FIELD_TAGS:
|
|
tagsIDX = i
|
|
case resource.SEARCH_FIELD_MANAGER_ID:
|
|
managerIdIDX = i
|
|
case resource.SEARCH_FIELD_MANAGER_KIND:
|
|
managerKindIDX = i
|
|
case resource.SEARCH_FIELD_DESCRIPTION:
|
|
descriptionIDX = i
|
|
}
|
|
}
|
|
|
|
sr := v0alpha1.SearchResults{
|
|
Offset: offset,
|
|
TotalHits: result.TotalHits,
|
|
QueryCost: result.QueryCost,
|
|
MaxScore: result.MaxScore,
|
|
Hits: make([]v0alpha1.DashboardHit, len(result.Results.Rows)),
|
|
}
|
|
|
|
for i, row := range result.Results.Rows {
|
|
if len(row.Cells) != len(result.Results.Columns) {
|
|
// there should never be mismatch len between # Columns and # Cells in a row. This indicates a bug in our
|
|
// code
|
|
return v0alpha1.SearchResults{}, fmt.Errorf("error parsing Search Response: mismatch number of columns and cells")
|
|
}
|
|
|
|
// Dynamically defined fields
|
|
fields := &common.Unstructured{}
|
|
for colIndex, col := range result.Results.Columns {
|
|
if _, ok := standardFields[col.Name]; !ok {
|
|
val, err := resource.DecodeCell(col, colIndex, row.Cells[colIndex])
|
|
if err != nil {
|
|
return v0alpha1.SearchResults{}, err
|
|
}
|
|
// Some of the dashboard fields come in as int32, but we need to convert them to int64 or else fields.Set() will panic
|
|
int32Val, ok := val.(int32)
|
|
if ok {
|
|
val = int64(int32Val)
|
|
}
|
|
fields.Set(col.Name, val)
|
|
}
|
|
}
|
|
|
|
hit := &v0alpha1.DashboardHit{
|
|
Resource: row.Key.Resource, // folders | dashboards
|
|
Name: row.Key.Name, // The Grafana UID
|
|
Field: fields,
|
|
}
|
|
if titleIDX >= 0 && row.Cells[titleIDX] != nil {
|
|
hit.Title = string(row.Cells[titleIDX])
|
|
} else {
|
|
hit.Title = "(no title)"
|
|
}
|
|
|
|
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 managerIdIDX >= 0 && row.Cells[managerIdIDX] != nil {
|
|
hit.ManagedBy.ID = string(row.Cells[managerIdIDX])
|
|
}
|
|
if managerKindIDX >= 0 && row.Cells[managerKindIDX] != nil {
|
|
hit.ManagedBy.Kind = utils.ManagerKind(row.Cells[managerKindIDX])
|
|
}
|
|
if tagsIDX >= 0 && row.Cells[tagsIDX] != nil {
|
|
_ = json.Unmarshal(row.Cells[tagsIDX], &hit.Tags)
|
|
}
|
|
if explainIDX >= 0 && row.Cells[explainIDX] != nil {
|
|
_ = json.Unmarshal(row.Cells[explainIDX], &hit.Explain)
|
|
}
|
|
if scoreIDX >= 0 && row.Cells[scoreIDX] != nil {
|
|
_, _ = binary.Decode(row.Cells[scoreIDX], binary.BigEndian, &hit.Score)
|
|
}
|
|
|
|
sr.Hits[i] = *hit
|
|
}
|
|
|
|
// Add facet results
|
|
if result.Facet != nil {
|
|
sr.Facets = make(map[string]v0alpha1.FacetResult)
|
|
for k, v := range result.Facet {
|
|
sr.Facets[k] = v0alpha1.FacetResult{
|
|
Field: v.Field,
|
|
Total: v.Total,
|
|
Missing: v.Missing,
|
|
Terms: make([]v0alpha1.TermFacet, len(v.Terms)),
|
|
}
|
|
for j, t := range v.Terms {
|
|
sr.Facets[k].Terms[j] = v0alpha1.TermFacet{
|
|
Term: t.Term,
|
|
Count: t.Count,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return sr, nil
|
|
}
|