2015-01-08 02:00:00 -06:00
|
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
2020-04-20 09:20:45 -05:00
|
|
|
|
"net/http"
|
2016-03-14 05:59:51 -05:00
|
|
|
|
"strconv"
|
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
|
"github.com/grafana/grafana/pkg/api/response"
|
2019-02-23 16:35:26 -06:00
|
|
|
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
2023-01-27 01:50:36 -06:00
|
|
|
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
2023-11-22 07:20:22 -06:00
|
|
|
|
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
2015-06-05 01:15:38 -05:00
|
|
|
|
"github.com/grafana/grafana/pkg/services/search"
|
2023-01-30 08:17:53 -06:00
|
|
|
|
"github.com/grafana/grafana/pkg/services/search/model"
|
2022-05-17 08:51:44 -05:00
|
|
|
|
"github.com/grafana/grafana/pkg/util"
|
2015-01-08 02:00:00 -06:00
|
|
|
|
)
|
|
|
|
|
|
2022-07-27 08:54:37 -05:00
|
|
|
|
// swagger:route GET /search search search
|
|
|
|
|
//
|
|
|
|
|
// Responses:
|
|
|
|
|
// 200: searchResponse
|
|
|
|
|
// 401: unauthorisedError
|
|
|
|
|
// 422: unprocessableEntityError
|
|
|
|
|
// 500: internalServerError
|
2023-01-27 01:50:36 -06:00
|
|
|
|
func (hs *HTTPServer) Search(c *contextmodel.ReqContext) response.Response {
|
2015-02-07 09:12:29 -06:00
|
|
|
|
query := c.Query("query")
|
2015-06-02 03:24:20 -05:00
|
|
|
|
tags := c.QueryStrings("tag")
|
2015-02-04 04:35:59 -06:00
|
|
|
|
starred := c.Query("starred")
|
2019-04-17 06:07:50 -05:00
|
|
|
|
limit := c.QueryInt64("limit")
|
|
|
|
|
page := c.QueryInt64("page")
|
2017-05-24 11:28:13 -05:00
|
|
|
|
dashboardType := c.Query("type")
|
2020-04-20 09:20:45 -05:00
|
|
|
|
sort := c.Query("sort")
|
2023-11-22 07:20:22 -06:00
|
|
|
|
permission := dashboardaccess.PERMISSION_VIEW
|
2015-02-05 04:10:56 -06:00
|
|
|
|
|
2019-04-17 06:07:50 -05:00
|
|
|
|
if limit > 5000 {
|
2021-01-15 07:43:20 -06:00
|
|
|
|
return response.Error(422, "Limit is above maximum allowed (5000), use page parameter to access hits beyond limit", nil)
|
2015-02-05 04:10:56 -06:00
|
|
|
|
}
|
2015-02-04 04:35:59 -06:00
|
|
|
|
|
2018-02-08 10:11:01 -06:00
|
|
|
|
if c.Query("permission") == "Edit" {
|
2023-11-22 07:20:22 -06:00
|
|
|
|
permission = dashboardaccess.PERMISSION_EDIT
|
2018-02-08 10:11:01 -06:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-22 06:37:35 -05:00
|
|
|
|
dbIDs := make([]int64, 0)
|
2016-03-05 05:26:21 -06:00
|
|
|
|
for _, id := range c.QueryStrings("dashboardIds") {
|
2018-03-22 06:37:35 -05:00
|
|
|
|
dashboardID, err := strconv.ParseInt(id, 10, 64)
|
2016-03-05 05:26:21 -06:00
|
|
|
|
if err == nil {
|
2018-03-22 06:37:35 -05:00
|
|
|
|
dbIDs = append(dbIDs, dashboardID)
|
2016-03-05 05:26:21 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-26 21:28:14 -05:00
|
|
|
|
dbUIDs := c.QueryStrings("dashboardUIDs")
|
|
|
|
|
if len(dbUIDs) == 0 {
|
|
|
|
|
// To keep it for now backward compatible for grafana 9
|
|
|
|
|
dbUIDs = c.QueryStrings("dashboardUID")
|
|
|
|
|
}
|
2022-06-02 14:56:01 -05:00
|
|
|
|
|
2018-03-22 06:37:35 -05:00
|
|
|
|
folderIDs := make([]int64, 0)
|
2017-11-20 05:47:03 -06:00
|
|
|
|
for _, id := range c.QueryStrings("folderIds") {
|
2018-03-22 06:37:35 -05:00
|
|
|
|
folderID, err := strconv.ParseInt(id, 10, 64)
|
2017-11-20 05:47:03 -06:00
|
|
|
|
if err == nil {
|
2018-03-22 06:37:35 -05:00
|
|
|
|
folderIDs = append(folderIDs, folderID)
|
2024-01-24 05:39:11 -06:00
|
|
|
|
metrics.MFolderIDsAPICount.WithLabelValues(metrics.Search).Inc()
|
2017-11-20 05:47:03 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-04 04:43:47 -05:00
|
|
|
|
folderUIDs := c.QueryStrings("folderUIDs")
|
|
|
|
|
|
|
|
|
|
bothDashboardIds := len(dbIDs) > 0 && len(dbUIDs) > 0
|
|
|
|
|
bothFolderIds := len(folderIDs) > 0 && len(folderUIDs) > 0
|
|
|
|
|
|
|
|
|
|
if bothDashboardIds || bothFolderIds {
|
2022-06-02 14:56:01 -05:00
|
|
|
|
return response.Error(400, "search supports UIDs or IDs, not both", nil)
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 03:45:53 -05:00
|
|
|
|
searchQuery := search.Query{
|
2022-06-02 14:56:01 -05:00
|
|
|
|
Title: query,
|
|
|
|
|
Tags: tags,
|
|
|
|
|
SignedInUser: c.SignedInUser,
|
|
|
|
|
Limit: limit,
|
|
|
|
|
Page: page,
|
|
|
|
|
IsStarred: starred == "true",
|
2023-10-06 04:34:36 -05:00
|
|
|
|
OrgId: c.SignedInUser.GetOrgID(),
|
2022-06-02 14:56:01 -05:00
|
|
|
|
DashboardIds: dbIDs,
|
|
|
|
|
DashboardUIDs: dbUIDs,
|
|
|
|
|
Type: dashboardType,
|
2023-11-07 08:51:44 -06:00
|
|
|
|
FolderIds: folderIDs, // nolint:staticcheck
|
2023-08-04 04:43:47 -05:00
|
|
|
|
FolderUIDs: folderUIDs,
|
2022-06-02 14:56:01 -05:00
|
|
|
|
Permission: permission,
|
|
|
|
|
Sort: sort,
|
2015-01-08 02:00:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2023-03-30 04:28:12 -05:00
|
|
|
|
hits, err := hs.SearchService.SearchHandler(c.Req.Context(), &searchQuery)
|
2015-05-13 03:45:53 -05:00
|
|
|
|
if err != nil {
|
2021-01-15 07:43:20 -06:00
|
|
|
|
return response.Error(500, "Search failed", err)
|
2015-01-08 02:00:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-17 08:51:44 -05:00
|
|
|
|
defer c.TimeRequest(metrics.MApiDashboardSearch)
|
|
|
|
|
|
2023-04-21 09:05:11 -05:00
|
|
|
|
return response.JSON(http.StatusOK, hits)
|
2015-01-08 02:00:00 -06:00
|
|
|
|
}
|
2020-04-20 09:20:45 -05:00
|
|
|
|
|
2022-07-27 08:54:37 -05:00
|
|
|
|
// swagger:route GET /search/sorting search listSortOptions
|
|
|
|
|
//
|
2022-09-12 02:40:35 -05:00
|
|
|
|
// List search sorting options.
|
2022-07-27 08:54:37 -05:00
|
|
|
|
//
|
|
|
|
|
// Responses:
|
|
|
|
|
// 200: listSortOptionsResponse
|
|
|
|
|
// 401: unauthorisedError
|
2023-01-27 01:50:36 -06:00
|
|
|
|
func (hs *HTTPServer) ListSortOptions(c *contextmodel.ReqContext) response.Response {
|
2020-04-20 09:20:45 -05:00
|
|
|
|
opts := hs.SearchService.SortOptions()
|
|
|
|
|
|
|
|
|
|
res := []util.DynMap{}
|
|
|
|
|
for _, o := range opts {
|
|
|
|
|
res = append(res, util.DynMap{
|
|
|
|
|
"name": o.Name,
|
|
|
|
|
"displayName": o.DisplayName,
|
|
|
|
|
"description": o.Description,
|
2021-02-11 01:49:16 -06:00
|
|
|
|
"meta": o.MetaName,
|
2020-04-20 09:20:45 -05:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
|
return response.JSON(http.StatusOK, util.DynMap{
|
2020-04-20 09:20:45 -05:00
|
|
|
|
"sortOptions": res,
|
|
|
|
|
})
|
|
|
|
|
}
|
2022-07-27 08:54:37 -05:00
|
|
|
|
|
|
|
|
|
// swagger:parameters search
|
|
|
|
|
type SearchParams struct {
|
|
|
|
|
// Search Query
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
Query string `json:"query"`
|
|
|
|
|
// List of tags to search for
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
// type: array
|
|
|
|
|
// collectionFormat: multi
|
|
|
|
|
Tag []string `json:"tag"`
|
|
|
|
|
// Type to search for, dash-folder or dash-db
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
// Description:
|
|
|
|
|
// * `dash-folder` - Search for folder
|
|
|
|
|
// * `dash-db` - Seatch for dashboard
|
|
|
|
|
// Enum: dash-folder,dash-db
|
|
|
|
|
Type string `json:"type"`
|
|
|
|
|
// List of dashboard id’s to search for
|
2023-08-04 04:43:47 -05:00
|
|
|
|
// This is deprecated: users should use the `dashboardUIDs` query parameter instead
|
2022-07-27 08:54:37 -05:00
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
2023-08-04 04:43:47 -05:00
|
|
|
|
// deprecated: true
|
2022-07-27 08:54:37 -05:00
|
|
|
|
DashboardIds []int64 `json:"dashboardIds"`
|
|
|
|
|
// List of dashboard uid’s to search for
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
DashboardUIDs []string `json:"dashboardUIDs"`
|
|
|
|
|
// List of folder id’s to search in for dashboards
|
2023-08-04 04:43:47 -05:00
|
|
|
|
// If it's `0` then it will query for the top level folders
|
|
|
|
|
// This is deprecated: users should use the `folderUIDs` query parameter instead
|
2022-07-27 08:54:37 -05:00
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
2023-08-04 04:43:47 -05:00
|
|
|
|
// deprecated: true
|
2023-11-16 08:57:04 -06:00
|
|
|
|
//
|
|
|
|
|
// Deprecated: use FolderUIDs instead
|
2022-07-27 08:54:37 -05:00
|
|
|
|
FolderIds []int64 `json:"folderIds"`
|
2023-08-04 04:43:47 -05:00
|
|
|
|
// List of folder UID’s to search in for dashboards
|
|
|
|
|
// If it's an empty string then it will query for the top level folders
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
FolderUIDs []string `json:"folderUIDs"`
|
2022-07-27 08:54:37 -05:00
|
|
|
|
// Flag indicating if only starred Dashboards should be returned
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
Starred bool `json:"starred"`
|
|
|
|
|
// Limit the number of returned results (max 5000)
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
Limit int64 `json:"limit"`
|
|
|
|
|
// Use this parameter to access hits beyond limit. Numbering starts at 1. limit param acts as page size. Only available in Grafana v6.2+.
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
Page int64 `json:"page"`
|
|
|
|
|
// Set to `Edit` to return dashboards/folders that the user can edit
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
// default:View
|
|
|
|
|
// Enum: Edit,View
|
|
|
|
|
Permission string `json:"permission"`
|
|
|
|
|
// Sort method; for listing all the possible sort methods use the search sorting endpoint.
|
|
|
|
|
// in:query
|
|
|
|
|
// required: false
|
|
|
|
|
// default: alpha-asc
|
|
|
|
|
// Enum: alpha-asc,alpha-desc
|
|
|
|
|
Sort string `json:"sort"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// swagger:response searchResponse
|
|
|
|
|
type SearchResponse struct {
|
|
|
|
|
// in: body
|
2023-01-30 08:17:53 -06:00
|
|
|
|
Body model.HitList `json:"body"`
|
2022-07-27 08:54:37 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// swagger:response listSortOptionsResponse
|
|
|
|
|
type ListSortOptionsResponse struct {
|
|
|
|
|
// in: body
|
|
|
|
|
Body struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
DisplayName string `json:"displayName"`
|
|
|
|
|
Description string `json:"description"`
|
|
|
|
|
Meta string `json:"meta"`
|
|
|
|
|
} `json:"body"`
|
|
|
|
|
}
|