mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -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:
@@ -85,12 +85,13 @@ Returns a list of queries in the query history that matches the search criteria.
|
|||||||
## Delete query from Query history by UID
|
## Delete query from Query history by UID
|
||||||
|
|
||||||
`DELETE /api/query-history/:uid`
|
`DELETE /api/query-history/:uid`
|
||||||
Deletes the query in query history that matches the specified uid. It requires that the user is logged in and that Query history feature is enabled in config file.
|
|
||||||
Deletes the query in query history that matches the specified uid. It requires that the user is logged in and that Query history feature is enabled in config file.
|
Deletes the query in query history that matches the specified uid. It requires that the user is logged in and that Query history feature is enabled in config file.
|
||||||
|
|
||||||
**Example Request**:
|
**Example Request**:
|
||||||
|
|
||||||
```http
|
```http
|
||||||
|
DELETE /api/query-history/P8zM2I1nz HTTP/1.1
|
||||||
Accept: application/json
|
Accept: application/json
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/middleware"
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"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 {
|
func (s *QueryHistoryService) searchHandler(c *models.ReqContext) response.Response {
|
||||||
|
timeRange := legacydata.NewDataTimeRange(c.Query("from"), c.Query("to"))
|
||||||
|
|
||||||
query := SearchInQueryHistoryQuery{
|
query := SearchInQueryHistoryQuery{
|
||||||
DatasourceUIDs: c.QueryStrings("datasourceUid"),
|
DatasourceUIDs: c.QueryStrings("datasourceUid"),
|
||||||
SearchString: c.Query("searchString"),
|
SearchString: c.Query("searchString"),
|
||||||
@@ -46,6 +49,8 @@ func (s *QueryHistoryService) searchHandler(c *models.ReqContext) response.Respo
|
|||||||
Sort: c.Query("sort"),
|
Sort: c.Query("sort"),
|
||||||
Page: c.QueryInt("page"),
|
Page: c.QueryInt("page"),
|
||||||
Limit: c.QueryInt("limit"),
|
Limit: c.QueryInt("limit"),
|
||||||
|
From: timeRange.GetFromAsSecondsEpoch(),
|
||||||
|
To: timeRange.GetToAsSecondsEpoch(),
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := s.SearchInQueryHistory(c.Req.Context(), c.SignedInUser, query)
|
result, err := s.SearchInQueryHistory(c.Req.Context(), c.SignedInUser, query)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package queryhistory
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -47,8 +46,8 @@ func (s QueryHistoryService) searchQueries(ctx context.Context, user *models.Sig
|
|||||||
var dtos []QueryHistoryDTO
|
var dtos []QueryHistoryDTO
|
||||||
var allQueries []interface{}
|
var allQueries []interface{}
|
||||||
|
|
||||||
if len(query.DatasourceUIDs) == 0 {
|
if query.To <= 0 {
|
||||||
return QueryHistorySearchResult{}, errors.New("no selected data source for query history search")
|
query.To = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.Page <= 0 {
|
if query.Page <= 0 {
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ type SearchInQueryHistoryQuery struct {
|
|||||||
Sort string `json:"sort"`
|
Sort string `json:"sort"`
|
||||||
Page int `json:"page"`
|
Page int `json:"page"`
|
||||||
Limit int `json:"limit"`
|
Limit int `json:"limit"`
|
||||||
|
From int64 `json:"from"`
|
||||||
|
To int64 `json:"to"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PatchQueryCommentInQueryHistoryCommand struct {
|
type PatchQueryCommentInQueryHistoryCommand struct {
|
||||||
|
|||||||
@@ -5,18 +5,14 @@ package queryhistory
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetQueriesFromQueryHistory(t *testing.T) {
|
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",
|
testScenario(t, "When users tries to get query in empty query history, it should return empty result",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
sc.reqContext.Req.Form.Add("datasourceUid", "test")
|
sc.reqContext.Req.Form.Add("datasourceUid", "test")
|
||||||
@@ -39,6 +35,16 @@ func TestGetQueriesFromQueryHistory(t *testing.T) {
|
|||||||
require.Equal(t, 1, response.Result.TotalCount)
|
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",
|
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries with datasourceUid, it should return correct queries",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
sc.reqContext.Req.Form.Add("datasourceUid", sc.initialResult.Result.DatasourceUID)
|
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)
|
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) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID1)
|
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID1)
|
||||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID2)
|
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, 2, response.Result.TotalCount)
|
||||||
require.Equal(t, true, response.Result.QueryHistory[0].Starred)
|
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) {
|
func writeFiltersSQL(query SearchInQueryHistoryQuery, user *models.SignedInUser, sqlStore *sqlstore.SQLStore, builder *sqlstore.SQLBuilder) {
|
||||||
params := []interface{}{user.OrgId, user.UserId, "%" + query.SearchString + "%", "%" + query.SearchString + "%"}
|
params := []interface{}{user.OrgId, user.UserId, query.From, query.To, "%" + query.SearchString + "%", "%" + query.SearchString + "%"}
|
||||||
for _, uid := range query.DatasourceUIDs {
|
|
||||||
params = append(params, uid)
|
|
||||||
}
|
|
||||||
var sql bytes.Buffer
|
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...)
|
builder.Write(sql.String(), params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user