mirror of
https://github.com/grafana/grafana.git
synced 2024-11-25 18:30:41 -06:00
Query history: Add from and to parameters (#48212)
* Query history: Add from and to parameters * Update docs/sources/http_api/query_history.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update docs/sources/http_api/query_history.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update docs/sources/http_api/query_history.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Implement Grafana relative time range Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>
This commit is contained in:
parent
17eca4505c
commit
b92fe0e0f5
@ -85,12 +85,13 @@ Returns a list of queries in the query history that matches the search criteria.
|
||||
|
||||
Query parameters:
|
||||
|
||||
- **datasourceUid** - Filter the query history for selected data sources. You must specify at least one data source UID. To perform an "AND" filtering with multiple data sources, specify the data source parameter using the following format: `datasourceUid=uid1&datasourceUid=uid2`.
|
||||
- **datasourceUid** - Filter the query history for the selected data source. To perform an "AND" filtering with multiple data sources, specify the data source parameter using the following format: `datasourceUid=uid1&datasourceUid=uid2`.
|
||||
- **searchString** – Filter the query history based on the content.
|
||||
- **sort** - Specify the sorting order. Sorting can be `time-asc` or `time-desc`. The default is `time-desc`.
|
||||
- **onlyStarred** - Search for queries that are starred. Defaults to `false`.
|
||||
- **page** - Search supports pagination. Specify which page number to return. Use the limit parameter to specify the number of queries per page.
|
||||
- **limit** - Limits the number of returned query history items per page. The default is 100 queries per page.
|
||||
- **from/to** - Specifies time range for the query history search. The time can be either epoch timestamps in milliseconds or relative using Grafana time units. For example, now-5m.
|
||||
|
||||
**Example request for query history search**:
|
||||
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
@ -39,6 +40,8 @@ func (s *QueryHistoryService) createHandler(c *models.ReqContext) response.Respo
|
||||
}
|
||||
|
||||
func (s *QueryHistoryService) searchHandler(c *models.ReqContext) response.Response {
|
||||
timeRange := legacydata.NewDataTimeRange(c.Query("from"), c.Query("to"))
|
||||
|
||||
query := SearchInQueryHistoryQuery{
|
||||
DatasourceUIDs: c.QueryStrings("datasourceUid"),
|
||||
SearchString: c.Query("searchString"),
|
||||
@ -46,6 +49,8 @@ func (s *QueryHistoryService) searchHandler(c *models.ReqContext) response.Respo
|
||||
Sort: c.Query("sort"),
|
||||
Page: c.QueryInt("page"),
|
||||
Limit: c.QueryInt("limit"),
|
||||
From: timeRange.GetFromAsSecondsEpoch(),
|
||||
To: timeRange.GetToAsSecondsEpoch(),
|
||||
}
|
||||
|
||||
result, err := s.SearchInQueryHistory(c.Req.Context(), c.SignedInUser, query)
|
||||
|
@ -2,7 +2,6 @@ package queryhistory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -47,8 +46,8 @@ func (s QueryHistoryService) searchQueries(ctx context.Context, user *models.Sig
|
||||
var dtos []QueryHistoryDTO
|
||||
var allQueries []interface{}
|
||||
|
||||
if len(query.DatasourceUIDs) == 0 {
|
||||
return QueryHistorySearchResult{}, errors.New("no selected data source for query history search")
|
||||
if query.To <= 0 {
|
||||
query.To = time.Now().Unix()
|
||||
}
|
||||
|
||||
if query.Page <= 0 {
|
||||
|
@ -41,6 +41,8 @@ type SearchInQueryHistoryQuery struct {
|
||||
Sort string `json:"sort"`
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
From int64 `json:"from"`
|
||||
To int64 `json:"to"`
|
||||
}
|
||||
|
||||
type PatchQueryCommentInQueryHistoryCommand struct {
|
||||
|
@ -5,18 +5,14 @@ package queryhistory
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetQueriesFromQueryHistory(t *testing.T) {
|
||||
testScenario(t, "When users tries to get query without datasourceUid, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
require.Equal(t, 500, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, "When users tries to get query in empty query history, it should return empty result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", "test")
|
||||
@ -39,6 +35,16 @@ func TestGetQueriesFromQueryHistory(t *testing.T) {
|
||||
require.Equal(t, 1, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries without datasourceUid, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 3, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries with datasourceUid, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", sc.initialResult.Result.DatasourceUID)
|
||||
@ -77,7 +83,7 @@ func TestGetQueriesFromQueryHistory(t *testing.T) {
|
||||
require.Equal(t, 0, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries with multiple datasourceUid, it should return correct queries",
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries with multiple datasourceUid, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID1)
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID2)
|
||||
@ -114,4 +120,146 @@ func TestGetQueriesFromQueryHistory(t *testing.T) {
|
||||
require.Equal(t, 2, response.Result.TotalCount)
|
||||
require.Equal(t, true, response.Result.QueryHistory[0].Starred)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using from filter, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("from", strconv.FormatInt(time.Now().UnixMilli()-60*1000, 10))
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 3, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using relative from filter, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("from", "now-1m")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 3, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using from filter, it should return no queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("from", strconv.FormatInt(time.Now().UnixMilli()+60*1000, 10))
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 0, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using from filter, it should return no queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("from", "now+1m")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 0, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using to filter, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("to", strconv.FormatInt(time.Now().UnixMilli(), 10))
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 3, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using to filter, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("to", "now")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 3, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using to filter, it should return no queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("to", strconv.FormatInt(time.Now().UnixMilli()-60*1000, 10))
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 0, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using to filter, it should return no queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("to", "now-1m")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 0, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using from and to filter, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("to", strconv.FormatInt(time.Now().UnixMilli(), 10))
|
||||
sc.reqContext.Req.Form.Add("from", strconv.FormatInt(time.Now().UnixMilli()-60*1000, 10))
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 3, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using from and to filter with other filters, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("to", strconv.FormatInt(time.Now().UnixMilli(), 10))
|
||||
sc.reqContext.Req.Form.Add("from", strconv.FormatInt(time.Now().UnixMilli()-60*1000, 10))
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID1)
|
||||
sc.reqContext.Req.Form.Add("searchString", "2")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 2, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using from and to filter with other filters, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("to", "now")
|
||||
sc.reqContext.Req.Form.Add("from", "now-1m")
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID1)
|
||||
sc.reqContext.Req.Form.Add("searchString", "2")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 2, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries using from and to filter with other filters, it should return no query",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("to", strconv.FormatInt(time.Now().UnixMilli()-60, 10))
|
||||
sc.reqContext.Req.Form.Add("from", strconv.FormatInt(time.Now().UnixMilli()+60, 10))
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID1)
|
||||
sc.reqContext.Req.Form.Add("searchString", "2")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 0, response.Result.TotalCount)
|
||||
})
|
||||
}
|
||||
|
@ -23,12 +23,16 @@ func writeStarredSQL(query SearchInQueryHistoryQuery, sqlStore *sqlstore.SQLStor
|
||||
}
|
||||
|
||||
func writeFiltersSQL(query SearchInQueryHistoryQuery, user *models.SignedInUser, sqlStore *sqlstore.SQLStore, builder *sqlstore.SQLBuilder) {
|
||||
params := []interface{}{user.OrgId, user.UserId, "%" + query.SearchString + "%", "%" + query.SearchString + "%"}
|
||||
for _, uid := range query.DatasourceUIDs {
|
||||
params = append(params, uid)
|
||||
}
|
||||
params := []interface{}{user.OrgId, user.UserId, query.From, query.To, "%" + query.SearchString + "%", "%" + query.SearchString + "%"}
|
||||
var sql bytes.Buffer
|
||||
sql.WriteString(" WHERE query_history.org_id = ? AND query_history.created_by = ? AND (query_history.queries " + sqlStore.Dialect.LikeStr() + " ? OR query_history.comment " + sqlStore.Dialect.LikeStr() + " ?) AND query_history.datasource_uid IN (? " + strings.Repeat(",?", len(query.DatasourceUIDs)-1) + ") ")
|
||||
sql.WriteString(" WHERE query_history.org_id = ? AND query_history.created_by = ? AND query_history.created_at >= ? AND query_history.created_at <= ? AND (query_history.queries " + sqlStore.Dialect.LikeStr() + " ? OR query_history.comment " + sqlStore.Dialect.LikeStr() + " ?) ")
|
||||
|
||||
if len(query.DatasourceUIDs) > 0 {
|
||||
for _, uid := range query.DatasourceUIDs {
|
||||
params = append(params, uid)
|
||||
}
|
||||
sql.WriteString(" AND query_history.datasource_uid IN (? " + strings.Repeat(",?", len(query.DatasourceUIDs)-1) + ") ")
|
||||
}
|
||||
builder.Write(sql.String(), params...)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user