mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Refactor usage of legacy data contracts (#41218)
Refactor usage of legacy data contracts. Moves legacy data contracts to pkg/tsdb/legacydata package. Refactor pkg/expr to be a proper service/dependency that can be provided to wire to remove some unneeded dependencies to SSE in ngalert and other places. Refactor pkg/expr to not use the legacydata,RequestHandler and use backend.QueryDataHandler instead.
This commit is contained in:
committed by
GitHub
parent
d6ed5d295e
commit
baab021fec
@@ -17,6 +17,7 @@ import (
|
|||||||
httpstatic "github.com/grafana/grafana/pkg/api/static"
|
httpstatic "github.com/grafana/grafana/pkg/api/static"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||||
@@ -54,7 +55,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/updatechecker"
|
"github.com/grafana/grafana/pkg/services/updatechecker"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
@@ -97,7 +98,7 @@ type HTTPServer struct {
|
|||||||
LivePushGateway *pushhttp.Gateway
|
LivePushGateway *pushhttp.Gateway
|
||||||
ContextHandler *contexthandler.ContextHandler
|
ContextHandler *contexthandler.ContextHandler
|
||||||
SQLStore *sqlstore.SQLStore
|
SQLStore *sqlstore.SQLStore
|
||||||
DataService *tsdb.Service
|
legacyDataRequestHandler legacydata.RequestHandler
|
||||||
AlertEngine *alerting.AlertEngine
|
AlertEngine *alerting.AlertEngine
|
||||||
LoadSchemaService *schemaloader.SchemaLoaderService
|
LoadSchemaService *schemaloader.SchemaLoaderService
|
||||||
AlertNG *ngalert.AlertNG
|
AlertNG *ngalert.AlertNG
|
||||||
@@ -115,6 +116,7 @@ type HTTPServer struct {
|
|||||||
internalMetricsSvc *metrics.InternalMetricsService
|
internalMetricsSvc *metrics.InternalMetricsService
|
||||||
updateChecker *updatechecker.Service
|
updateChecker *updatechecker.Service
|
||||||
searchUsersService searchusers.Service
|
searchUsersService searchusers.Service
|
||||||
|
expressionService *expr.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerOptions struct {
|
type ServerOptions struct {
|
||||||
@@ -124,7 +126,7 @@ type ServerOptions struct {
|
|||||||
func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routing.RouteRegister, bus bus.Bus,
|
func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routing.RouteRegister, bus bus.Bus,
|
||||||
renderService rendering.Service, licensing models.Licensing, hooksService *hooks.HooksService,
|
renderService rendering.Service, licensing models.Licensing, hooksService *hooks.HooksService,
|
||||||
cacheService *localcache.CacheService, sqlStore *sqlstore.SQLStore,
|
cacheService *localcache.CacheService, sqlStore *sqlstore.SQLStore,
|
||||||
dataService *tsdb.Service, alertEngine *alerting.AlertEngine,
|
legacyDataRequestHandler legacydata.RequestHandler, alertEngine *alerting.AlertEngine,
|
||||||
pluginRequestValidator models.PluginRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver,
|
pluginRequestValidator models.PluginRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver,
|
||||||
pluginDashboardManager plugins.PluginDashboardManager, pluginStore plugins.Store, pluginClient plugins.Client,
|
pluginDashboardManager plugins.PluginDashboardManager, pluginStore plugins.Store, pluginClient plugins.Client,
|
||||||
pluginErrorResolver plugins.ErrorResolver, settingsProvider setting.Provider,
|
pluginErrorResolver plugins.ErrorResolver, settingsProvider setting.Provider,
|
||||||
@@ -141,7 +143,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
internalMetricsSvc *metrics.InternalMetricsService, quotaService *quota.QuotaService,
|
internalMetricsSvc *metrics.InternalMetricsService, quotaService *quota.QuotaService,
|
||||||
socialService social.Service, oauthTokenService oauthtoken.OAuthTokenService,
|
socialService social.Service, oauthTokenService oauthtoken.OAuthTokenService,
|
||||||
encryptionService encryption.Service, updateChecker *updatechecker.Service, searchUsersService searchusers.Service,
|
encryptionService encryption.Service, updateChecker *updatechecker.Service, searchUsersService searchusers.Service,
|
||||||
dataSourcesService *datasources.Service, secretsService secrets.Service) (*HTTPServer, error) {
|
dataSourcesService *datasources.Service, secretsService secrets.Service, expressionService *expr.Service) (*HTTPServer, error) {
|
||||||
web.Env = cfg.Env
|
web.Env = cfg.Env
|
||||||
m := web.New()
|
m := web.New()
|
||||||
|
|
||||||
@@ -154,7 +156,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
HooksService: hooksService,
|
HooksService: hooksService,
|
||||||
CacheService: cacheService,
|
CacheService: cacheService,
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
DataService: dataService,
|
legacyDataRequestHandler: legacyDataRequestHandler,
|
||||||
AlertEngine: alertEngine,
|
AlertEngine: alertEngine,
|
||||||
PluginRequestValidator: pluginRequestValidator,
|
PluginRequestValidator: pluginRequestValidator,
|
||||||
pluginClient: pluginClient,
|
pluginClient: pluginClient,
|
||||||
@@ -195,6 +197,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
SecretsService: secretsService,
|
SecretsService: secretsService,
|
||||||
DataSourcesService: dataSourcesService,
|
DataSourcesService: dataSourcesService,
|
||||||
searchUsersService: searchUsersService,
|
searchUsersService: searchUsersService,
|
||||||
|
expressionService: expressionService,
|
||||||
}
|
}
|
||||||
if hs.Listener != nil {
|
if hs.Listener != nil {
|
||||||
hs.log.Debug("Using provided listener")
|
hs.log.Debug("Using provided listener")
|
||||||
|
|||||||
@@ -7,16 +7,15 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/expr"
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/adapters"
|
"github.com/grafana/grafana/pkg/plugins/adapters"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QueryMetricsV2 returns query metrics.
|
// QueryMetricsV2 returns query metrics.
|
||||||
@@ -26,12 +25,12 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
|
|||||||
return response.Error(http.StatusBadRequest, "No queries found in query", nil)
|
return response.Error(http.StatusBadRequest, "No queries found in query", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
timeRange := plugins.NewDataTimeRange(reqDTO.From, reqDTO.To)
|
timeRange := legacydata.NewDataTimeRange(reqDTO.From, reqDTO.To)
|
||||||
request := plugins.DataQuery{
|
request := legacydata.DataQuery{
|
||||||
TimeRange: &timeRange,
|
TimeRange: &timeRange,
|
||||||
Debug: reqDTO.Debug,
|
Debug: reqDTO.Debug,
|
||||||
User: c.SignedInUser,
|
User: c.SignedInUser,
|
||||||
Queries: make([]plugins.DataSubQuery, 0, len(reqDTO.Queries)),
|
Queries: make([]legacydata.DataSubQuery, 0, len(reqDTO.Queries)),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the queries
|
// Parse the queries
|
||||||
@@ -53,7 +52,7 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
|
|||||||
|
|
||||||
hs.log.Debug("Processing metrics query", "query", query)
|
hs.log.Debug("Processing metrics query", "query", query)
|
||||||
|
|
||||||
request.Queries = append(request.Queries, plugins.DataSubQuery{
|
request.Queries = append(request.Queries, legacydata.DataSubQuery{
|
||||||
RefID: query.Get("refId").MustString("A"),
|
RefID: query.Get("refId").MustString("A"),
|
||||||
MaxDataPoints: query.Get("maxDataPoints").MustInt64(100),
|
MaxDataPoints: query.Get("maxDataPoints").MustInt64(100),
|
||||||
IntervalMS: query.Get("intervalMs").MustInt64(1000),
|
IntervalMS: query.Get("intervalMs").MustInt64(1000),
|
||||||
@@ -64,11 +63,7 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hasExpression {
|
if hasExpression {
|
||||||
exprService := expr.Service{
|
qdr, err := hs.expressionService.WrapTransformData(c.Req.Context(), request)
|
||||||
Cfg: hs.Cfg,
|
|
||||||
DataService: hs.DataService,
|
|
||||||
}
|
|
||||||
qdr, err := exprService.WrapTransformData(c.Req.Context(), request)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "expression request error", err)
|
return response.Error(500, "expression request error", err)
|
||||||
}
|
}
|
||||||
@@ -187,15 +182,15 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque
|
|||||||
return response.Error(http.StatusForbidden, "Access denied", err)
|
return response.Error(http.StatusForbidden, "Access denied", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
timeRange := plugins.NewDataTimeRange(reqDto.From, reqDto.To)
|
timeRange := legacydata.NewDataTimeRange(reqDto.From, reqDto.To)
|
||||||
request := plugins.DataQuery{
|
request := legacydata.DataQuery{
|
||||||
TimeRange: &timeRange,
|
TimeRange: &timeRange,
|
||||||
Debug: reqDto.Debug,
|
Debug: reqDto.Debug,
|
||||||
User: c.SignedInUser,
|
User: c.SignedInUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, query := range reqDto.Queries {
|
for _, query := range reqDto.Queries {
|
||||||
request.Queries = append(request.Queries, plugins.DataSubQuery{
|
request.Queries = append(request.Queries, legacydata.DataSubQuery{
|
||||||
RefID: query.Get("refId").MustString("A"),
|
RefID: query.Get("refId").MustString("A"),
|
||||||
MaxDataPoints: query.Get("maxDataPoints").MustInt64(100),
|
MaxDataPoints: query.Get("maxDataPoints").MustInt64(100),
|
||||||
IntervalMS: query.Get("intervalMs").MustInt64(1000),
|
IntervalMS: query.Get("intervalMs").MustInt64(1000),
|
||||||
@@ -204,7 +199,7 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := hs.DataService.HandleRequest(c.Req.Context(), ds, request)
|
resp, err := hs.legacyDataRequestHandler.HandleRequest(c.Req.Context(), ds, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Metric request error", err)
|
return response.Error(http.StatusInternalServerError, "Metric request error", err)
|
||||||
}
|
}
|
||||||
@@ -221,9 +216,7 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque
|
|||||||
return response.JSON(statusCode, &resp)
|
return response.JSON(statusCode, &resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck // plugins.DataQueryResponse deprecated
|
func (hs *HTTPServer) createRequest(ctx context.Context, ds *models.DataSource, query legacydata.DataQuery) (*backend.QueryDataRequest, error) {
|
||||||
func (hs *HTTPServer) createRequest(ctx context.Context, ds *models.DataSource,
|
|
||||||
query plugins.DataQuery) (*backend.QueryDataRequest, error) {
|
|
||||||
instanceSettings, err := adapters.ModelToInstanceSettings(ds, hs.decryptSecureJsonDataFn())
|
instanceSettings, err := adapters.ModelToInstanceSettings(ds, hs.decryptSecureJsonDataFn())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/services/encryption"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,15 +36,24 @@ func IsDataSource(uid string) bool {
|
|||||||
|
|
||||||
// Service is service representation for expression handling.
|
// Service is service representation for expression handling.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Cfg *setting.Cfg
|
cfg *setting.Cfg
|
||||||
DataService plugins.DataRequestHandler
|
dataService backend.QueryDataHandler
|
||||||
|
encryptionService encryption.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideService(cfg *setting.Cfg, pluginClient plugins.Client, encryptionService encryption.Service) *Service {
|
||||||
|
return &Service{
|
||||||
|
cfg: cfg,
|
||||||
|
dataService: pluginClient,
|
||||||
|
encryptionService: encryptionService,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) isDisabled() bool {
|
func (s *Service) isDisabled() bool {
|
||||||
if s.Cfg == nil {
|
if s.cfg == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return !s.Cfg.ExpressionsEnabled
|
return !s.cfg.ExpressionsEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildPipeline builds a pipeline from a request.
|
// BuildPipeline builds a pipeline from a request.
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/services/encryption/ossencryption"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint:staticcheck // plugins.DataPlugin deprecated
|
|
||||||
func TestService(t *testing.T) {
|
func TestService(t *testing.T) {
|
||||||
dsDF := data.NewFrame("test",
|
dsDF := data.NewFrame("test",
|
||||||
data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
|
data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
|
||||||
@@ -25,9 +26,13 @@ func TestService(t *testing.T) {
|
|||||||
me := &mockEndpoint{
|
me := &mockEndpoint{
|
||||||
Frames: []*data.Frame{dsDF},
|
Frames: []*data.Frame{dsDF},
|
||||||
}
|
}
|
||||||
s := Service{DataService: me}
|
s := Service{
|
||||||
bus.AddHandler("test", func(query *models.GetDataSourceQuery) error {
|
cfg: setting.NewCfg(),
|
||||||
query.Result = &models.DataSource{Id: 1, OrgId: 1, Type: "test"}
|
dataService: me,
|
||||||
|
encryptionService: ossencryption.ProvideService(),
|
||||||
|
}
|
||||||
|
bus.AddHandlerCtx("test", func(_ context.Context, query *models.GetDataSourceQuery) error {
|
||||||
|
query.Result = &models.DataSource{Id: 1, OrgId: 1, Type: "test", JsonData: simplejson.New()}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -88,18 +93,10 @@ type mockEndpoint struct {
|
|||||||
Frames data.Frames
|
Frames data.Frames
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck // plugins.DataQueryResponse deprecated
|
func (me *mockEndpoint) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||||
func (me *mockEndpoint) DataQuery(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (plugins.DataResponse, error) {
|
resp := backend.NewQueryDataResponse()
|
||||||
return plugins.DataResponse{
|
resp.Responses["A"] = backend.DataResponse{
|
||||||
Results: map[string]plugins.DataQueryResult{
|
Frames: me.Frames,
|
||||||
"A": {
|
}
|
||||||
Dataframes: plugins.NewDecodedDataFrames(me.Frames),
|
return resp, nil
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:staticcheck // plugins.DataQueryResponse deprecated
|
|
||||||
func (me *mockEndpoint) HandleRequest(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (plugins.DataResponse, error) {
|
|
||||||
return me.DataQuery(ctx, ds, query)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins/adapters"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
@@ -33,7 +33,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WrapTransformData creates and executes transform requests
|
// WrapTransformData creates and executes transform requests
|
||||||
func (s *Service) WrapTransformData(ctx context.Context, query plugins.DataQuery) (*backend.QueryDataResponse, error) {
|
func (s *Service) WrapTransformData(ctx context.Context, query legacydata.DataQuery) (*backend.QueryDataResponse, error) {
|
||||||
req := Request{
|
req := Request{
|
||||||
OrgId: query.User.OrgId,
|
OrgId: query.User.OrgId,
|
||||||
Queries: []Query{},
|
Queries: []Query{},
|
||||||
@@ -206,38 +206,23 @@ func (s *Service) queryData(ctx context.Context, req *backend.QueryDataRequest)
|
|||||||
return nil, fmt.Errorf("could not find datasource: %w", err)
|
return nil, fmt.Errorf("could not find datasource: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert plugin-model (datasource) queries to tsdb queries
|
dsInstanceSettings, err := adapters.ModelToInstanceSettings(getDsInfo.Result, s.decryptSecureJsonDataFn(ctx))
|
||||||
queries := make([]plugins.DataSubQuery, len(req.Queries))
|
|
||||||
for i, query := range req.Queries {
|
|
||||||
sj, err := simplejson.NewJson(query.JSON)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
queries[i] = plugins.DataSubQuery{
|
|
||||||
RefID: query.RefID,
|
|
||||||
IntervalMS: query.Interval.Milliseconds(),
|
|
||||||
MaxDataPoints: query.MaxDataPoints,
|
|
||||||
QueryType: query.QueryType,
|
|
||||||
DataSource: getDsInfo.Result,
|
|
||||||
Model: sj,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now take Time Range from first query.
|
|
||||||
timeRange := plugins.NewDataTimeRange(strconv.FormatInt(req.Queries[0].TimeRange.From.Unix()*1000, 10),
|
|
||||||
strconv.FormatInt(req.Queries[0].TimeRange.To.Unix()*1000, 10))
|
|
||||||
|
|
||||||
tQ := plugins.DataQuery{
|
|
||||||
TimeRange: &timeRange,
|
|
||||||
Queries: queries,
|
|
||||||
Headers: req.Headers,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the converted queries
|
|
||||||
tsdbRes, err := s.DataService.HandleRequest(ctx, getDsInfo.Result, tQ)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errutil.Wrap("failed to convert datasource instance settings", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tsdbRes.ToBackendDataResponse()
|
req.PluginContext.DataSourceInstanceSettings = dsInstanceSettings
|
||||||
|
req.PluginContext.PluginID = getDsInfo.Result.Type
|
||||||
|
|
||||||
|
return s.dataService.QueryData(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) decryptSecureJsonDataFn(ctx context.Context) func(map[string][]byte) map[string]string {
|
||||||
|
return func(m map[string][]byte) map[string]string {
|
||||||
|
decryptedJsonData, err := s.encryptionService.DecryptJsonData(ctx, m, s.cfg.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Failed to decrypt secure json data", "error", err)
|
||||||
|
}
|
||||||
|
return decryptedJsonData
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DataRequestHandler is a data request handler interface.
|
|
||||||
type DataRequestHandler interface {
|
|
||||||
// HandleRequest handles a data request.
|
|
||||||
HandleRequest(context.Context, *models.DataSource, DataQuery) (DataResponse, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store is the storage for plugins.
|
// Store is the storage for plugins.
|
||||||
type Store interface {
|
type Store interface {
|
||||||
// Plugin finds a plugin by its ID.
|
// Plugin finds a plugin by its ID.
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api"
|
"github.com/grafana/grafana/pkg/api"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider"
|
"github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider"
|
||||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||||
@@ -57,7 +58,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/updatechecker"
|
"github.com/grafana/grafana/pkg/services/updatechecker"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
|
"github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
||||||
@@ -65,6 +65,8 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/graphite"
|
"github.com/grafana/grafana/pkg/tsdb/graphite"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/influxdb"
|
"github.com/grafana/grafana/pkg/tsdb/influxdb"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
|
legacydataservice "github.com/grafana/grafana/pkg/tsdb/legacydata/service"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/loki"
|
"github.com/grafana/grafana/pkg/tsdb/loki"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/mssql"
|
"github.com/grafana/grafana/pkg/tsdb/mssql"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/mysql"
|
"github.com/grafana/grafana/pkg/tsdb/mysql"
|
||||||
@@ -76,8 +78,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var wireBasicSet = wire.NewSet(
|
var wireBasicSet = wire.NewSet(
|
||||||
tsdb.NewService,
|
legacydataservice.ProvideService,
|
||||||
wire.Bind(new(plugins.DataRequestHandler), new(*tsdb.Service)),
|
wire.Bind(new(legacydata.RequestHandler), new(*legacydataservice.Service)),
|
||||||
alerting.ProvideAlertEngine,
|
alerting.ProvideAlertEngine,
|
||||||
wire.Bind(new(alerting.UsageStatsQuerier), new(*alerting.AlertEngine)),
|
wire.Bind(new(alerting.UsageStatsQuerier), new(*alerting.AlertEngine)),
|
||||||
setting.NewCfgFromArgs,
|
setting.NewCfgFromArgs,
|
||||||
@@ -164,6 +166,7 @@ var wireBasicSet = wire.NewSet(
|
|||||||
datasources.ProvideService,
|
datasources.ProvideService,
|
||||||
pluginsettings.ProvideService,
|
pluginsettings.ProvideService,
|
||||||
alerting.ProvideService,
|
alerting.ProvideService,
|
||||||
|
expr.ProvideService,
|
||||||
)
|
)
|
||||||
|
|
||||||
var wireSet = wire.NewSet(
|
var wireSet = wire.NewSet(
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/interval"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata/interval"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/prometheus"
|
"github.com/grafana/grafana/pkg/tsdb/prometheus"
|
||||||
|
|
||||||
gocontext "context"
|
gocontext "context"
|
||||||
@@ -47,8 +47,8 @@ type AlertQuery struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Eval evaluates the `QueryCondition`.
|
// Eval evaluates the `QueryCondition`.
|
||||||
func (c *QueryCondition) Eval(context *alerting.EvalContext, requestHandler plugins.DataRequestHandler) (*alerting.ConditionResult, error) {
|
func (c *QueryCondition) Eval(context *alerting.EvalContext, requestHandler legacydata.RequestHandler) (*alerting.ConditionResult, error) {
|
||||||
timeRange := plugins.NewDataTimeRange(c.Query.From, c.Query.To)
|
timeRange := legacydata.NewDataTimeRange(c.Query.From, c.Query.To)
|
||||||
|
|
||||||
seriesList, err := c.executeQuery(context, timeRange, requestHandler)
|
seriesList, err := c.executeQuery(context, timeRange, requestHandler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -109,7 +109,7 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext, requestHandler plug
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateInterval(timeRange plugins.DataTimeRange, model *simplejson.Json, dsInfo *models.DataSource) (time.Duration, error) {
|
func calculateInterval(timeRange legacydata.DataTimeRange, model *simplejson.Json, dsInfo *models.DataSource) (time.Duration, error) {
|
||||||
// if there is no min-interval specified in the datasource or in the dashboard-panel,
|
// if there is no min-interval specified in the datasource or in the dashboard-panel,
|
||||||
// the value of 1ms is used (this is how it is done in the dashboard-interval-calculation too,
|
// the value of 1ms is used (this is how it is done in the dashboard-interval-calculation too,
|
||||||
// see https://github.com/grafana/grafana/blob/9a0040c0aeaae8357c650cec2ee644a571dddf3d/packages/grafana-data/src/datetime/rangeutil.ts#L264)
|
// see https://github.com/grafana/grafana/blob/9a0040c0aeaae8357c650cec2ee644a571dddf3d/packages/grafana-data/src/datetime/rangeutil.ts#L264)
|
||||||
@@ -133,8 +133,8 @@ func calculateInterval(timeRange plugins.DataTimeRange, model *simplejson.Json,
|
|||||||
return interval.Value, nil
|
return interval.Value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange plugins.DataTimeRange,
|
func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange legacydata.DataTimeRange,
|
||||||
requestHandler plugins.DataRequestHandler) (plugins.DataTimeSeriesSlice, error) {
|
requestHandler legacydata.RequestHandler) (legacydata.DataTimeSeriesSlice, error) {
|
||||||
getDsInfo := &models.GetDataSourceQuery{
|
getDsInfo := &models.GetDataSourceQuery{
|
||||||
Id: c.Query.DatasourceID,
|
Id: c.Query.DatasourceID,
|
||||||
OrgId: context.Rule.OrgID,
|
OrgId: context.Rule.OrgID,
|
||||||
@@ -153,7 +153,7 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange p
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("interval calculation failed: %w", err)
|
return nil, fmt.Errorf("interval calculation failed: %w", err)
|
||||||
}
|
}
|
||||||
result := make(plugins.DataTimeSeriesSlice, 0)
|
result := make(legacydata.DataTimeSeriesSlice, 0)
|
||||||
|
|
||||||
if context.IsDebug {
|
if context.IsDebug {
|
||||||
data := simplejson.New()
|
data := simplejson.New()
|
||||||
@@ -247,18 +247,18 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange p
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QueryCondition) getRequestForAlertRule(datasource *models.DataSource, timeRange plugins.DataTimeRange,
|
func (c *QueryCondition) getRequestForAlertRule(datasource *models.DataSource, timeRange legacydata.DataTimeRange,
|
||||||
debug bool) (plugins.DataQuery, error) {
|
debug bool) (legacydata.DataQuery, error) {
|
||||||
queryModel := c.Query.Model
|
queryModel := c.Query.Model
|
||||||
|
|
||||||
calculatedInterval, err := calculateInterval(timeRange, queryModel, datasource)
|
calculatedInterval, err := calculateInterval(timeRange, queryModel, datasource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plugins.DataQuery{}, err
|
return legacydata.DataQuery{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req := plugins.DataQuery{
|
req := legacydata.DataQuery{
|
||||||
TimeRange: &timeRange,
|
TimeRange: &timeRange,
|
||||||
Queries: []plugins.DataSubQuery{
|
Queries: []legacydata.DataSubQuery{
|
||||||
{
|
{
|
||||||
RefID: "A",
|
RefID: "A",
|
||||||
Model: queryModel,
|
Model: queryModel,
|
||||||
@@ -340,21 +340,21 @@ func validateToValue(to string) error {
|
|||||||
|
|
||||||
// FrameToSeriesSlice converts a frame that is a valid time series as per data.TimeSeriesSchema()
|
// FrameToSeriesSlice converts a frame that is a valid time series as per data.TimeSeriesSchema()
|
||||||
// to a DataTimeSeriesSlice.
|
// to a DataTimeSeriesSlice.
|
||||||
func FrameToSeriesSlice(frame *data.Frame) (plugins.DataTimeSeriesSlice, error) {
|
func FrameToSeriesSlice(frame *data.Frame) (legacydata.DataTimeSeriesSlice, error) {
|
||||||
tsSchema := frame.TimeSeriesSchema()
|
tsSchema := frame.TimeSeriesSchema()
|
||||||
if tsSchema.Type == data.TimeSeriesTypeNot {
|
if tsSchema.Type == data.TimeSeriesTypeNot {
|
||||||
// If no fields, or only a time field, create an empty plugins.DataTimeSeriesSlice with a single
|
// If no fields, or only a time field, create an empty plugins.DataTimeSeriesSlice with a single
|
||||||
// time series in order to trigger "no data" in alerting.
|
// time series in order to trigger "no data" in alerting.
|
||||||
if frame.Rows() == 0 || (len(frame.Fields) == 1 && frame.Fields[0].Type().Time()) {
|
if frame.Rows() == 0 || (len(frame.Fields) == 1 && frame.Fields[0].Type().Time()) {
|
||||||
return plugins.DataTimeSeriesSlice{{
|
return legacydata.DataTimeSeriesSlice{{
|
||||||
Name: frame.Name,
|
Name: frame.Name,
|
||||||
Points: make(plugins.DataTimeSeriesPoints, 0),
|
Points: make(legacydata.DataTimeSeriesPoints, 0),
|
||||||
}}, nil
|
}}, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("input frame is not recognized as a time series")
|
return nil, fmt.Errorf("input frame is not recognized as a time series")
|
||||||
}
|
}
|
||||||
seriesCount := len(tsSchema.ValueIndices)
|
seriesCount := len(tsSchema.ValueIndices)
|
||||||
seriesSlice := make(plugins.DataTimeSeriesSlice, 0, seriesCount)
|
seriesSlice := make(legacydata.DataTimeSeriesSlice, 0, seriesCount)
|
||||||
timeField := frame.Fields[tsSchema.TimeIndex]
|
timeField := frame.Fields[tsSchema.TimeIndex]
|
||||||
timeNullFloatSlice := make([]null.Float, timeField.Len())
|
timeNullFloatSlice := make([]null.Float, timeField.Len())
|
||||||
|
|
||||||
@@ -368,8 +368,8 @@ func FrameToSeriesSlice(frame *data.Frame) (plugins.DataTimeSeriesSlice, error)
|
|||||||
|
|
||||||
for _, fieldIdx := range tsSchema.ValueIndices { // create a TimeSeries for each value Field
|
for _, fieldIdx := range tsSchema.ValueIndices { // create a TimeSeries for each value Field
|
||||||
field := frame.Fields[fieldIdx]
|
field := frame.Fields[fieldIdx]
|
||||||
ts := plugins.DataTimeSeries{
|
ts := legacydata.DataTimeSeries{
|
||||||
Points: make(plugins.DataTimeSeriesPoints, field.Len()),
|
Points: make(legacydata.DataTimeSeriesPoints, field.Len()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(field.Labels) > 0 {
|
if len(field.Labels) > 0 {
|
||||||
@@ -395,7 +395,7 @@ func FrameToSeriesSlice(frame *data.Frame) (plugins.DataTimeSeriesSlice, error)
|
|||||||
return nil, errutil.Wrapf(err,
|
return nil, errutil.Wrapf(err,
|
||||||
"failed to convert frame to DataTimeSeriesSlice, can not convert value %v to float", field.At(rowIdx))
|
"failed to convert frame to DataTimeSeriesSlice, can not convert value %v to float", field.At(rowIdx))
|
||||||
}
|
}
|
||||||
ts.Points[rowIdx] = plugins.DataTimePoint{
|
ts.Points[rowIdx] = legacydata.DataTimePoint{
|
||||||
null.FloatFrom(val),
|
null.FloatFrom(val),
|
||||||
timeNullFloatSlice[rowIdx],
|
timeNullFloatSlice[rowIdx],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/interval"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -27,11 +26,11 @@ func TestQueryInterval(t *testing.T) {
|
|||||||
|
|
||||||
timeRange := "5m"
|
timeRange := "5m"
|
||||||
|
|
||||||
verifier := func(query plugins.DataSubQuery) {
|
verifier := func(query legacydata.DataSubQuery) {
|
||||||
// 5minutes timerange = 300000milliseconds; default-resolution is 1500pixels,
|
// 5minutes timerange = 300000milliseconds; default-resolution is 1500pixels,
|
||||||
// so we should have 300000/1500 = 200milliseconds here
|
// so we should have 300000/1500 = 200milliseconds here
|
||||||
require.Equal(t, int64(200), query.IntervalMS)
|
require.Equal(t, int64(200), query.IntervalMS)
|
||||||
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
|
require.Equal(t, intervalv2.DefaultRes, query.MaxDataPoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
||||||
@@ -45,9 +44,9 @@ func TestQueryInterval(t *testing.T) {
|
|||||||
|
|
||||||
timeRange := "5m"
|
timeRange := "5m"
|
||||||
|
|
||||||
verifier := func(query plugins.DataSubQuery) {
|
verifier := func(query legacydata.DataSubQuery) {
|
||||||
require.Equal(t, int64(123000), query.IntervalMS)
|
require.Equal(t, int64(123000), query.IntervalMS)
|
||||||
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
|
require.Equal(t, intervalv2.DefaultRes, query.MaxDataPoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
||||||
@@ -64,9 +63,9 @@ func TestQueryInterval(t *testing.T) {
|
|||||||
|
|
||||||
timeRange := "5m"
|
timeRange := "5m"
|
||||||
|
|
||||||
verifier := func(query plugins.DataSubQuery) {
|
verifier := func(query legacydata.DataSubQuery) {
|
||||||
require.Equal(t, int64(71000), query.IntervalMS)
|
require.Equal(t, int64(71000), query.IntervalMS)
|
||||||
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
|
require.Equal(t, intervalv2.DefaultRes, query.MaxDataPoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
||||||
@@ -83,11 +82,11 @@ func TestQueryInterval(t *testing.T) {
|
|||||||
|
|
||||||
timeRange := "5m"
|
timeRange := "5m"
|
||||||
|
|
||||||
verifier := func(query plugins.DataSubQuery) {
|
verifier := func(query legacydata.DataSubQuery) {
|
||||||
// when both panel-min-interval and datasource-min-interval exists,
|
// when both panel-min-interval and datasource-min-interval exists,
|
||||||
// panel-min-interval is used
|
// panel-min-interval is used
|
||||||
require.Equal(t, int64(19000), query.IntervalMS)
|
require.Equal(t, int64(19000), query.IntervalMS)
|
||||||
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
|
require.Equal(t, intervalv2.DefaultRes, query.MaxDataPoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
||||||
@@ -102,11 +101,11 @@ func TestQueryInterval(t *testing.T) {
|
|||||||
|
|
||||||
timeRange := "1s"
|
timeRange := "1s"
|
||||||
|
|
||||||
verifier := func(query plugins.DataSubQuery) {
|
verifier := func(query legacydata.DataSubQuery) {
|
||||||
// no min-interval exists, the default-min-interval will be used,
|
// no min-interval exists, the default-min-interval will be used,
|
||||||
// and for such a short time-range this will cause the value to be 1millisecond.
|
// and for such a short time-range this will cause the value to be 1millisecond.
|
||||||
require.Equal(t, int64(1), query.IntervalMS)
|
require.Equal(t, int64(1), query.IntervalMS)
|
||||||
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
|
require.Equal(t, intervalv2.DefaultRes, query.MaxDataPoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
|
||||||
@@ -119,24 +118,24 @@ type queryIntervalTestContext struct {
|
|||||||
condition *QueryCondition
|
condition *QueryCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
type queryIntervalVerifier func(query plugins.DataSubQuery)
|
type queryIntervalVerifier func(query legacydata.DataSubQuery)
|
||||||
|
|
||||||
type fakeIntervalTestReqHandler struct {
|
type fakeIntervalTestReqHandler struct {
|
||||||
//nolint: staticcheck // plugins.DataResponse deprecated
|
//nolint: staticcheck // legacydata.DataResponse deprecated
|
||||||
response plugins.DataResponse
|
response legacydata.DataResponse
|
||||||
verifier queryIntervalVerifier
|
verifier queryIntervalVerifier
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataResponse deprecated
|
//nolint: staticcheck // legacydata.DataResponse deprecated
|
||||||
func (rh fakeIntervalTestReqHandler) HandleRequest(ctx context.Context, dsInfo *models.DataSource, query plugins.DataQuery) (
|
func (rh fakeIntervalTestReqHandler) HandleRequest(ctx context.Context, dsInfo *models.DataSource, query legacydata.DataQuery) (
|
||||||
plugins.DataResponse, error) {
|
legacydata.DataResponse, error) {
|
||||||
q := query.Queries[0]
|
q := query.Queries[0]
|
||||||
rh.verifier(q)
|
rh.verifier(q)
|
||||||
return rh.response, nil
|
return rh.response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataResponse deprecated
|
//nolint: staticcheck // legacydata.DataResponse deprecated
|
||||||
func applyScenario(t *testing.T, timeRange string, dataSourceJsonData *simplejson.Json, queryModel string, verifier func(query plugins.DataSubQuery)) {
|
func applyScenario(t *testing.T, timeRange string, dataSourceJsonData *simplejson.Json, queryModel string, verifier func(query legacydata.DataSubQuery)) {
|
||||||
t.Run("desc", func(t *testing.T) {
|
t.Run("desc", func(t *testing.T) {
|
||||||
bus.AddHandlerCtx("test", func(ctx context.Context, query *models.GetDataSourceQuery) error {
|
bus.AddHandlerCtx("test", func(ctx context.Context, query *models.GetDataSourceQuery) error {
|
||||||
query.Result = &models.DataSource{Id: 1, Type: "graphite", JsonData: dataSourceJsonData}
|
query.Result = &models.DataSource{Id: 1, Type: "graphite", JsonData: dataSourceJsonData}
|
||||||
@@ -167,11 +166,11 @@ func applyScenario(t *testing.T, timeRange string, dataSourceJsonData *simplejso
|
|||||||
|
|
||||||
ctx.condition = condition
|
ctx.condition = condition
|
||||||
|
|
||||||
qr := plugins.DataQueryResult{}
|
qr := legacydata.DataQueryResult{}
|
||||||
|
|
||||||
reqHandler := fakeIntervalTestReqHandler{
|
reqHandler := fakeIntervalTestReqHandler{
|
||||||
response: plugins.DataResponse{
|
response: legacydata.DataResponse{
|
||||||
Results: map[string]plugins.DataQueryResult{
|
Results: map[string]legacydata.DataQueryResult{
|
||||||
"A": qr,
|
"A": qr,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
@@ -15,18 +16,17 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/null"
|
"github.com/grafana/grafana/pkg/components/null"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/xorcare/pointer"
|
"github.com/xorcare/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTimeSeriesPointsFromArgs(values ...float64) plugins.DataTimeSeriesPoints {
|
func newTimeSeriesPointsFromArgs(values ...float64) legacydata.DataTimeSeriesPoints {
|
||||||
points := make(plugins.DataTimeSeriesPoints, 0)
|
points := make(legacydata.DataTimeSeriesPoints, 0)
|
||||||
|
|
||||||
for i := 0; i < len(values); i += 2 {
|
for i := 0; i < len(values); i += 2 {
|
||||||
points = append(points, plugins.DataTimePoint{null.FloatFrom(values[i]), null.FloatFrom(values[i+1])})
|
points = append(points, legacydata.DataTimePoint{null.FloatFrom(values[i]), null.FloatFrom(values[i+1])})
|
||||||
}
|
}
|
||||||
|
|
||||||
return points
|
return points
|
||||||
@@ -74,7 +74,7 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
t.Run("should fire when avg is above 100", func(t *testing.T) {
|
t.Run("should fire when avg is above 100", func(t *testing.T) {
|
||||||
ctx := setup()
|
ctx := setup()
|
||||||
points := newTimeSeriesPointsFromArgs(120, 0)
|
points := newTimeSeriesPointsFromArgs(120, 0)
|
||||||
ctx.series = plugins.DataTimeSeriesSlice{plugins.DataTimeSeries{Name: "test1", Points: points}}
|
ctx.series = legacydata.DataTimeSeriesSlice{legacydata.DataTimeSeries{Name: "test1", Points: points}}
|
||||||
cr, err := ctx.exec(t)
|
cr, err := ctx.exec(t)
|
||||||
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -96,7 +96,7 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
t.Run("Should not fire when avg is below 100", func(t *testing.T) {
|
t.Run("Should not fire when avg is below 100", func(t *testing.T) {
|
||||||
ctx := setup()
|
ctx := setup()
|
||||||
points := newTimeSeriesPointsFromArgs(90, 0)
|
points := newTimeSeriesPointsFromArgs(90, 0)
|
||||||
ctx.series = plugins.DataTimeSeriesSlice{plugins.DataTimeSeries{Name: "test1", Points: points}}
|
ctx.series = legacydata.DataTimeSeriesSlice{legacydata.DataTimeSeries{Name: "test1", Points: points}}
|
||||||
cr, err := ctx.exec(t)
|
cr, err := ctx.exec(t)
|
||||||
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -117,9 +117,9 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Should fire if only first series matches", func(t *testing.T) {
|
t.Run("Should fire if only first series matches", func(t *testing.T) {
|
||||||
ctx := setup()
|
ctx := setup()
|
||||||
ctx.series = plugins.DataTimeSeriesSlice{
|
ctx.series = legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs(120, 0)},
|
legacydata.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs(120, 0)},
|
||||||
plugins.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs(0, 0)},
|
legacydata.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs(0, 0)},
|
||||||
}
|
}
|
||||||
cr, err := ctx.exec(t)
|
cr, err := ctx.exec(t)
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
t.Run("No series", func(t *testing.T) {
|
t.Run("No series", func(t *testing.T) {
|
||||||
ctx := setup()
|
ctx := setup()
|
||||||
t.Run("Should set NoDataFound when condition is gt", func(t *testing.T) {
|
t.Run("Should set NoDataFound when condition is gt", func(t *testing.T) {
|
||||||
ctx.series = plugins.DataTimeSeriesSlice{}
|
ctx.series = legacydata.DataTimeSeriesSlice{}
|
||||||
cr, err := ctx.exec(t)
|
cr, err := ctx.exec(t)
|
||||||
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -140,7 +140,7 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Should be firing when condition is no_value", func(t *testing.T) {
|
t.Run("Should be firing when condition is no_value", func(t *testing.T) {
|
||||||
ctx.evaluator = `{"type": "no_value", "params": []}`
|
ctx.evaluator = `{"type": "no_value", "params": []}`
|
||||||
ctx.series = plugins.DataTimeSeriesSlice{}
|
ctx.series = legacydata.DataTimeSeriesSlice{}
|
||||||
cr, err := ctx.exec(t)
|
cr, err := ctx.exec(t)
|
||||||
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -152,8 +152,8 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
ctx := setup()
|
ctx := setup()
|
||||||
t.Run("Should set Firing if eval match", func(t *testing.T) {
|
t.Run("Should set Firing if eval match", func(t *testing.T) {
|
||||||
ctx.evaluator = `{"type": "no_value", "params": []}`
|
ctx.evaluator = `{"type": "no_value", "params": []}`
|
||||||
ctx.series = plugins.DataTimeSeriesSlice{
|
ctx.series = legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
|
legacydata.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
|
||||||
}
|
}
|
||||||
cr, err := ctx.exec(t)
|
cr, err := ctx.exec(t)
|
||||||
|
|
||||||
@@ -162,9 +162,9 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should set NoDataFound both series are empty", func(t *testing.T) {
|
t.Run("Should set NoDataFound both series are empty", func(t *testing.T) {
|
||||||
ctx.series = plugins.DataTimeSeriesSlice{
|
ctx.series = legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
|
legacydata.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
|
||||||
plugins.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs()},
|
legacydata.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs()},
|
||||||
}
|
}
|
||||||
cr, err := ctx.exec(t)
|
cr, err := ctx.exec(t)
|
||||||
|
|
||||||
@@ -173,9 +173,9 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should set NoDataFound both series contains null", func(t *testing.T) {
|
t.Run("Should set NoDataFound both series contains null", func(t *testing.T) {
|
||||||
ctx.series = plugins.DataTimeSeriesSlice{
|
ctx.series = legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{Name: "test1", Points: plugins.DataTimeSeriesPoints{plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}},
|
legacydata.DataTimeSeries{Name: "test1", Points: legacydata.DataTimeSeriesPoints{legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}},
|
||||||
plugins.DataTimeSeries{Name: "test2", Points: plugins.DataTimeSeriesPoints{plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}},
|
legacydata.DataTimeSeries{Name: "test2", Points: legacydata.DataTimeSeriesPoints{legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}},
|
||||||
}
|
}
|
||||||
cr, err := ctx.exec(t)
|
cr, err := ctx.exec(t)
|
||||||
|
|
||||||
@@ -184,9 +184,9 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should not set NoDataFound if one series is empty", func(t *testing.T) {
|
t.Run("Should not set NoDataFound if one series is empty", func(t *testing.T) {
|
||||||
ctx.series = plugins.DataTimeSeriesSlice{
|
ctx.series = legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
|
legacydata.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
|
||||||
plugins.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs(120, 0)},
|
legacydata.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs(120, 0)},
|
||||||
}
|
}
|
||||||
cr, err := ctx.exec(t)
|
cr, err := ctx.exec(t)
|
||||||
|
|
||||||
@@ -199,13 +199,13 @@ func TestQueryCondition(t *testing.T) {
|
|||||||
type queryConditionTestContext struct {
|
type queryConditionTestContext struct {
|
||||||
reducer string
|
reducer string
|
||||||
evaluator string
|
evaluator string
|
||||||
series plugins.DataTimeSeriesSlice
|
series legacydata.DataTimeSeriesSlice
|
||||||
frame *data.Frame
|
frame *data.Frame
|
||||||
result *alerting.EvalContext
|
result *alerting.EvalContext
|
||||||
condition *QueryCondition
|
condition *QueryCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
//nolint: staticcheck // legacydata.DataPlugin deprecated
|
||||||
func (ctx *queryConditionTestContext) exec(t *testing.T) (*alerting.ConditionResult, error) {
|
func (ctx *queryConditionTestContext) exec(t *testing.T) (*alerting.ConditionResult, error) {
|
||||||
jsonModel, err := simplejson.NewJson([]byte(`{
|
jsonModel, err := simplejson.NewJson([]byte(`{
|
||||||
"type": "query",
|
"type": "query",
|
||||||
@@ -224,18 +224,18 @@ func (ctx *queryConditionTestContext) exec(t *testing.T) (*alerting.ConditionRes
|
|||||||
|
|
||||||
ctx.condition = condition
|
ctx.condition = condition
|
||||||
|
|
||||||
qr := plugins.DataQueryResult{
|
qr := legacydata.DataQueryResult{
|
||||||
Series: ctx.series,
|
Series: ctx.series,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.frame != nil {
|
if ctx.frame != nil {
|
||||||
qr = plugins.DataQueryResult{
|
qr = legacydata.DataQueryResult{
|
||||||
Dataframes: plugins.NewDecodedDataFrames(data.Frames{ctx.frame}),
|
Dataframes: legacydata.NewDecodedDataFrames(data.Frames{ctx.frame}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reqHandler := fakeReqHandler{
|
reqHandler := fakeReqHandler{
|
||||||
response: plugins.DataResponse{
|
response: legacydata.DataResponse{
|
||||||
Results: map[string]plugins.DataQueryResult{
|
Results: map[string]legacydata.DataQueryResult{
|
||||||
"A": qr,
|
"A": qr,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -245,13 +245,13 @@ func (ctx *queryConditionTestContext) exec(t *testing.T) (*alerting.ConditionRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
type fakeReqHandler struct {
|
type fakeReqHandler struct {
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
//nolint: staticcheck // legacydata.DataPlugin deprecated
|
||||||
response plugins.DataResponse
|
response legacydata.DataResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
//nolint: staticcheck // legacydata.DataPlugin deprecated
|
||||||
func (rh fakeReqHandler) HandleRequest(context.Context, *models.DataSource, plugins.DataQuery) (
|
func (rh fakeReqHandler) HandleRequest(context.Context, *models.DataSource, legacydata.DataQuery) (
|
||||||
plugins.DataResponse, error) {
|
legacydata.DataResponse, error) {
|
||||||
return rh.response, nil
|
return rh.response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ func TestFrameToSeriesSlice(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
frame *data.Frame
|
frame *data.Frame
|
||||||
seriesSlice plugins.DataTimeSeriesSlice
|
seriesSlice legacydata.DataTimeSeriesSlice
|
||||||
Err require.ErrorAssertionFunc
|
Err require.ErrorAssertionFunc
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -278,21 +278,21 @@ func TestFrameToSeriesSlice(t *testing.T) {
|
|||||||
4.0,
|
4.0,
|
||||||
})),
|
})),
|
||||||
|
|
||||||
seriesSlice: plugins.DataTimeSeriesSlice{
|
seriesSlice: legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{
|
legacydata.DataTimeSeries{
|
||||||
Name: "Values Int64s {Animal Factor=cat}",
|
Name: "Values Int64s {Animal Factor=cat}",
|
||||||
Tags: map[string]string{"Animal Factor": "cat"},
|
Tags: map[string]string{"Animal Factor": "cat"},
|
||||||
Points: plugins.DataTimeSeriesPoints{
|
Points: legacydata.DataTimeSeriesPoints{
|
||||||
plugins.DataTimePoint{null.FloatFrom(math.NaN()), null.FloatFrom(1577934240000)},
|
legacydata.DataTimePoint{null.FloatFrom(math.NaN()), null.FloatFrom(1577934240000)},
|
||||||
plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(1577934270000)},
|
legacydata.DataTimePoint{null.FloatFrom(3), null.FloatFrom(1577934270000)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins.DataTimeSeries{
|
legacydata.DataTimeSeries{
|
||||||
Name: "Values Floats {Animal Factor=sloth}",
|
Name: "Values Floats {Animal Factor=sloth}",
|
||||||
Tags: map[string]string{"Animal Factor": "sloth"},
|
Tags: map[string]string{"Animal Factor": "sloth"},
|
||||||
Points: plugins.DataTimeSeriesPoints{
|
Points: legacydata.DataTimeSeriesPoints{
|
||||||
plugins.DataTimePoint{null.FloatFrom(2), null.FloatFrom(1577934240000)},
|
legacydata.DataTimePoint{null.FloatFrom(2), null.FloatFrom(1577934240000)},
|
||||||
plugins.DataTimePoint{null.FloatFrom(4), null.FloatFrom(1577934270000)},
|
legacydata.DataTimePoint{null.FloatFrom(4), null.FloatFrom(1577934270000)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -305,16 +305,16 @@ func TestFrameToSeriesSlice(t *testing.T) {
|
|||||||
data.NewField(`Values Int64s`, data.Labels{"Animal Factor": "cat"}, []*int64{}),
|
data.NewField(`Values Int64s`, data.Labels{"Animal Factor": "cat"}, []*int64{}),
|
||||||
data.NewField(`Values Floats`, data.Labels{"Animal Factor": "sloth"}, []float64{})),
|
data.NewField(`Values Floats`, data.Labels{"Animal Factor": "sloth"}, []float64{})),
|
||||||
|
|
||||||
seriesSlice: plugins.DataTimeSeriesSlice{
|
seriesSlice: legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{
|
legacydata.DataTimeSeries{
|
||||||
Name: "Values Int64s {Animal Factor=cat}",
|
Name: "Values Int64s {Animal Factor=cat}",
|
||||||
Tags: map[string]string{"Animal Factor": "cat"},
|
Tags: map[string]string{"Animal Factor": "cat"},
|
||||||
Points: plugins.DataTimeSeriesPoints{},
|
Points: legacydata.DataTimeSeriesPoints{},
|
||||||
},
|
},
|
||||||
plugins.DataTimeSeries{
|
legacydata.DataTimeSeries{
|
||||||
Name: "Values Floats {Animal Factor=sloth}",
|
Name: "Values Floats {Animal Factor=sloth}",
|
||||||
Tags: map[string]string{"Animal Factor": "sloth"},
|
Tags: map[string]string{"Animal Factor": "sloth"},
|
||||||
Points: plugins.DataTimeSeriesPoints{},
|
Points: legacydata.DataTimeSeriesPoints{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Err: require.NoError,
|
Err: require.NoError,
|
||||||
@@ -325,10 +325,10 @@ func TestFrameToSeriesSlice(t *testing.T) {
|
|||||||
data.NewField("Time", data.Labels{}, []time.Time{}),
|
data.NewField("Time", data.Labels{}, []time.Time{}),
|
||||||
data.NewField(`Values`, data.Labels{}, []float64{})),
|
data.NewField(`Values`, data.Labels{}, []float64{})),
|
||||||
|
|
||||||
seriesSlice: plugins.DataTimeSeriesSlice{
|
seriesSlice: legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{
|
legacydata.DataTimeSeries{
|
||||||
Name: "Values",
|
Name: "Values",
|
||||||
Points: plugins.DataTimeSeriesPoints{},
|
Points: legacydata.DataTimeSeriesPoints{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Err: require.NoError,
|
Err: require.NoError,
|
||||||
@@ -341,10 +341,10 @@ func TestFrameToSeriesSlice(t *testing.T) {
|
|||||||
DisplayNameFromDS: "sloth",
|
DisplayNameFromDS: "sloth",
|
||||||
})),
|
})),
|
||||||
|
|
||||||
seriesSlice: plugins.DataTimeSeriesSlice{
|
seriesSlice: legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{
|
legacydata.DataTimeSeries{
|
||||||
Name: "sloth",
|
Name: "sloth",
|
||||||
Points: plugins.DataTimeSeriesPoints{},
|
Points: legacydata.DataTimeSeriesPoints{},
|
||||||
Tags: map[string]string{"Rating": "10"},
|
Tags: map[string]string{"Rating": "10"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -359,10 +359,10 @@ func TestFrameToSeriesSlice(t *testing.T) {
|
|||||||
DisplayNameFromDS: "sloth #2",
|
DisplayNameFromDS: "sloth #2",
|
||||||
})),
|
})),
|
||||||
|
|
||||||
seriesSlice: plugins.DataTimeSeriesSlice{
|
seriesSlice: legacydata.DataTimeSeriesSlice{
|
||||||
plugins.DataTimeSeries{
|
legacydata.DataTimeSeries{
|
||||||
Name: "sloth #1",
|
Name: "sloth #1",
|
||||||
Points: plugins.DataTimeSeriesPoints{},
|
Points: legacydata.DataTimeSeriesPoints{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Err: require.NoError,
|
Err: require.NoError,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/null"
|
"github.com/grafana/grafana/pkg/components/null"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// queryReducer reduces a timeseries to a nullable float
|
// queryReducer reduces a timeseries to a nullable float
|
||||||
@@ -18,8 +18,7 @@ type queryReducer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//nolint: gocyclo
|
//nolint: gocyclo
|
||||||
//nolint: staticcheck // plugins.DataTimeSeries deprecated
|
func (s *queryReducer) Reduce(series legacydata.DataTimeSeries) null.Float {
|
||||||
func (s *queryReducer) Reduce(series plugins.DataTimeSeries) null.Float {
|
|
||||||
if len(series.Points) == 0 {
|
if len(series.Points) == 0 {
|
||||||
return null.FloatFromPtr(nil)
|
return null.FloatFromPtr(nil)
|
||||||
}
|
}
|
||||||
@@ -127,8 +126,7 @@ func newSimpleReducer(t string) *queryReducer {
|
|||||||
return &queryReducer{Type: t}
|
return &queryReducer{Type: t}
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.* deprecated
|
func calculateDiff(series legacydata.DataTimeSeries, allNull bool, value float64, fn func(float64, float64) float64) (bool, float64) {
|
||||||
func calculateDiff(series plugins.DataTimeSeries, allNull bool, value float64, fn func(float64, float64) float64) (bool, float64) {
|
|
||||||
var (
|
var (
|
||||||
points = series.Points
|
points = series.Points
|
||||||
first float64
|
first float64
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/null"
|
"github.com/grafana/grafana/pkg/components/null"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,16 +52,16 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("median should ignore null values", func(t *testing.T) {
|
t.Run("median should ignore null values", func(t *testing.T) {
|
||||||
reducer := newSimpleReducer("median")
|
reducer := newSimpleReducer("median")
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(3)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(3)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(float64(1)), null.FloatFrom(4)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFrom(float64(1)), null.FloatFrom(4)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(float64(2)), null.FloatFrom(5)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFrom(float64(2)), null.FloatFrom(5)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(float64(3)), null.FloatFrom(6)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFrom(float64(3)), null.FloatFrom(6)})
|
||||||
|
|
||||||
result := reducer.Reduce(series)
|
result := reducer.Reduce(series)
|
||||||
require.Equal(t, true, result.Valid)
|
require.Equal(t, true, result.Valid)
|
||||||
@@ -75,25 +75,25 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("avg with only nulls", func(t *testing.T) {
|
t.Run("avg with only nulls", func(t *testing.T) {
|
||||||
reducer := newSimpleReducer("avg")
|
reducer := newSimpleReducer("avg")
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
||||||
require.Equal(t, false, reducer.Reduce(series).Valid)
|
require.Equal(t, false, reducer.Reduce(series).Valid)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("count_non_null", func(t *testing.T) {
|
t.Run("count_non_null", func(t *testing.T) {
|
||||||
t.Run("with null values and real values", func(t *testing.T) {
|
t.Run("with null values and real values", func(t *testing.T) {
|
||||||
reducer := newSimpleReducer("count_non_null")
|
reducer := newSimpleReducer("count_non_null")
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(3)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFrom(3), null.FloatFrom(3)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(4)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFrom(3), null.FloatFrom(4)})
|
||||||
|
|
||||||
require.Equal(t, true, reducer.Reduce(series).Valid)
|
require.Equal(t, true, reducer.Reduce(series).Valid)
|
||||||
require.Equal(t, 2.0, reducer.Reduce(series).Float64)
|
require.Equal(t, 2.0, reducer.Reduce(series).Float64)
|
||||||
@@ -101,12 +101,12 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("with null values", func(t *testing.T) {
|
t.Run("with null values", func(t *testing.T) {
|
||||||
reducer := newSimpleReducer("count_non_null")
|
reducer := newSimpleReducer("count_non_null")
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
||||||
|
|
||||||
require.Equal(t, false, reducer.Reduce(series).Valid)
|
require.Equal(t, false, reducer.Reduce(series).Valid)
|
||||||
})
|
})
|
||||||
@@ -114,14 +114,14 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("avg of number values and null values should ignore nulls", func(t *testing.T) {
|
t.Run("avg of number values and null values should ignore nulls", func(t *testing.T) {
|
||||||
reducer := newSimpleReducer("avg")
|
reducer := newSimpleReducer("avg")
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(1)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFrom(3), null.FloatFrom(1)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(3)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(3)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(4)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFrom(3), null.FloatFrom(4)})
|
||||||
|
|
||||||
require.Equal(t, float64(3), reducer.Reduce(series).Float64)
|
require.Equal(t, float64(3), reducer.Reduce(series).Float64)
|
||||||
})
|
})
|
||||||
@@ -179,12 +179,12 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("diff with only nulls", func(t *testing.T) {
|
t.Run("diff with only nulls", func(t *testing.T) {
|
||||||
reducer := newSimpleReducer("diff")
|
reducer := newSimpleReducer("diff")
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
||||||
|
|
||||||
require.Equal(t, false, reducer.Reduce(series).Valid)
|
require.Equal(t, false, reducer.Reduce(series).Valid)
|
||||||
})
|
})
|
||||||
@@ -242,12 +242,12 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("diff_abs with only nulls", func(t *testing.T) {
|
t.Run("diff_abs with only nulls", func(t *testing.T) {
|
||||||
reducer := newSimpleReducer("diff_abs")
|
reducer := newSimpleReducer("diff_abs")
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
||||||
|
|
||||||
require.Equal(t, false, reducer.Reduce(series).Valid)
|
require.Equal(t, false, reducer.Reduce(series).Valid)
|
||||||
})
|
})
|
||||||
@@ -305,12 +305,12 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("percent_diff with only nulls", func(t *testing.T) {
|
t.Run("percent_diff with only nulls", func(t *testing.T) {
|
||||||
reducer := newSimpleReducer("percent_diff")
|
reducer := newSimpleReducer("percent_diff")
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
||||||
|
|
||||||
require.Equal(t, false, reducer.Reduce(series).Valid)
|
require.Equal(t, false, reducer.Reduce(series).Valid)
|
||||||
})
|
})
|
||||||
@@ -368,12 +368,12 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("percent_diff_abs with only nulls", func(t *testing.T) {
|
t.Run("percent_diff_abs with only nulls", func(t *testing.T) {
|
||||||
reducer := newSimpleReducer("percent_diff_abs")
|
reducer := newSimpleReducer("percent_diff_abs")
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
|
||||||
|
|
||||||
require.Equal(t, false, reducer.Reduce(series).Valid)
|
require.Equal(t, false, reducer.Reduce(series).Valid)
|
||||||
})
|
})
|
||||||
@@ -396,12 +396,12 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
|
|
||||||
func testReducer(reducerType string, datapoints ...float64) float64 {
|
func testReducer(reducerType string, datapoints ...float64) float64 {
|
||||||
reducer := newSimpleReducer(reducerType)
|
reducer := newSimpleReducer(reducerType)
|
||||||
series := plugins.DataTimeSeries{
|
series := legacydata.DataTimeSeries{
|
||||||
Name: "test time series",
|
Name: "test time series",
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx := range datapoints {
|
for idx := range datapoints {
|
||||||
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(datapoints[idx]), null.FloatFrom(1234134)})
|
series.Points = append(series.Points, legacydata.DataTimePoint{null.FloatFrom(datapoints[idx]), null.FloatFrom(1234134)})
|
||||||
}
|
}
|
||||||
|
|
||||||
return reducer.Reduce(series).Float64
|
return reducer.Reduce(series).Float64
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/services/encryption"
|
"github.com/grafana/grafana/pkg/services/encryption"
|
||||||
"github.com/grafana/grafana/pkg/services/rendering"
|
"github.com/grafana/grafana/pkg/services/rendering"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/opentracing/opentracing-go"
|
"github.com/opentracing/opentracing-go"
|
||||||
"github.com/opentracing/opentracing-go/ext"
|
"github.com/opentracing/opentracing-go/ext"
|
||||||
tlog "github.com/opentracing/opentracing-go/log"
|
tlog "github.com/opentracing/opentracing-go/log"
|
||||||
@@ -28,7 +28,7 @@ type AlertEngine struct {
|
|||||||
RenderService rendering.Service
|
RenderService rendering.Service
|
||||||
Bus bus.Bus
|
Bus bus.Bus
|
||||||
RequestValidator models.PluginRequestValidator
|
RequestValidator models.PluginRequestValidator
|
||||||
DataService plugins.DataRequestHandler
|
DataService legacydata.RequestHandler
|
||||||
Cfg *setting.Cfg
|
Cfg *setting.Cfg
|
||||||
|
|
||||||
execQueue chan *Job
|
execQueue chan *Job
|
||||||
@@ -48,7 +48,7 @@ func (e *AlertEngine) IsDisabled() bool {
|
|||||||
|
|
||||||
// ProvideAlertEngine returns a new AlertEngine.
|
// ProvideAlertEngine returns a new AlertEngine.
|
||||||
func ProvideAlertEngine(renderer rendering.Service, bus bus.Bus, requestValidator models.PluginRequestValidator,
|
func ProvideAlertEngine(renderer rendering.Service, bus bus.Bus, requestValidator models.PluginRequestValidator,
|
||||||
dataService plugins.DataRequestHandler, usageStatsService usagestats.Service, encryptionService encryption.Service,
|
dataService legacydata.RequestHandler, usageStatsService usagestats.Service, encryptionService encryption.Service,
|
||||||
cfg *setting.Cfg) *AlertEngine {
|
cfg *setting.Cfg) *AlertEngine {
|
||||||
e := &AlertEngine{
|
e := &AlertEngine{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
|
|||||||
@@ -7,18 +7,18 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultEvalHandler is responsible for evaluating the alert rule.
|
// DefaultEvalHandler is responsible for evaluating the alert rule.
|
||||||
type DefaultEvalHandler struct {
|
type DefaultEvalHandler struct {
|
||||||
log log.Logger
|
log log.Logger
|
||||||
alertJobTimeout time.Duration
|
alertJobTimeout time.Duration
|
||||||
requestHandler plugins.DataRequestHandler
|
requestHandler legacydata.RequestHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvalHandler is the `DefaultEvalHandler` constructor.
|
// NewEvalHandler is the `DefaultEvalHandler` constructor.
|
||||||
func NewEvalHandler(requestHandler plugins.DataRequestHandler) *DefaultEvalHandler {
|
func NewEvalHandler(requestHandler legacydata.RequestHandler) *DefaultEvalHandler {
|
||||||
return &DefaultEvalHandler{
|
return &DefaultEvalHandler{
|
||||||
log: log.New("alerting.evalHandler"),
|
log: log.New("alerting.evalHandler"),
|
||||||
alertJobTimeout: time.Second * 5,
|
alertJobTimeout: time.Second * 5,
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -17,7 +17,7 @@ type conditionStub struct {
|
|||||||
noData bool
|
noData bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conditionStub) Eval(context *EvalContext, reqHandler plugins.DataRequestHandler) (*ConditionResult, error) {
|
func (c *conditionStub) Eval(context *EvalContext, reqHandler legacydata.RequestHandler) (*ConditionResult, error) {
|
||||||
return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator, NoDataFound: c.noData}, nil
|
return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator, NoDataFound: c.noData}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type evalHandler interface {
|
type evalHandler interface {
|
||||||
@@ -60,5 +60,5 @@ type ConditionResult struct {
|
|||||||
|
|
||||||
// Condition is responsible for evaluating an alert condition.
|
// Condition is responsible for evaluating an alert condition.
|
||||||
type Condition interface {
|
type Condition interface {
|
||||||
Eval(result *EvalContext, requestHandler plugins.DataRequestHandler) (*ConditionResult, error)
|
Eval(result *EvalContext, requestHandler legacydata.RequestHandler) (*ConditionResult, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FakeCondition struct{}
|
type FakeCondition struct{}
|
||||||
|
|
||||||
func (f *FakeCondition) Eval(context *EvalContext, reqHandler plugins.DataRequestHandler) (*ConditionResult, error) {
|
func (f *FakeCondition) Eval(context *EvalContext, reqHandler legacydata.RequestHandler) (*ConditionResult, error) {
|
||||||
return &ConditionResult{}, nil
|
return &ConditionResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
@@ -18,7 +19,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/quota"
|
"github.com/grafana/grafana/pkg/services/quota"
|
||||||
"github.com/grafana/grafana/pkg/services/secrets"
|
"github.com/grafana/grafana/pkg/services/secrets"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// timeNow makes it possible to test usage of time
|
// timeNow makes it possible to test usage of time
|
||||||
@@ -54,7 +54,7 @@ type API struct {
|
|||||||
Cfg *setting.Cfg
|
Cfg *setting.Cfg
|
||||||
DatasourceCache datasources.CacheService
|
DatasourceCache datasources.CacheService
|
||||||
RouteRegister routing.RouteRegister
|
RouteRegister routing.RouteRegister
|
||||||
DataService *tsdb.Service
|
ExpressionService *expr.Service
|
||||||
QuotaService *quota.QuotaService
|
QuotaService *quota.QuotaService
|
||||||
Schedule schedule.ScheduleService
|
Schedule schedule.ScheduleService
|
||||||
RuleStore store.RuleStore
|
RuleStore store.RuleStore
|
||||||
@@ -93,11 +93,11 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
|
|||||||
RulerSrv{DatasourceCache: api.DatasourceCache, QuotaService: api.QuotaService, manager: api.StateManager, store: api.RuleStore, log: logger},
|
RulerSrv{DatasourceCache: api.DatasourceCache, QuotaService: api.QuotaService, manager: api.StateManager, store: api.RuleStore, log: logger},
|
||||||
), m)
|
), m)
|
||||||
api.RegisterTestingApiEndpoints(TestingApiSrv{
|
api.RegisterTestingApiEndpoints(TestingApiSrv{
|
||||||
AlertingProxy: proxy,
|
AlertingProxy: proxy,
|
||||||
Cfg: api.Cfg,
|
Cfg: api.Cfg,
|
||||||
DataService: api.DataService,
|
ExpressionService: api.ExpressionService,
|
||||||
DatasourceCache: api.DatasourceCache,
|
DatasourceCache: api.DatasourceCache,
|
||||||
log: logger,
|
log: logger,
|
||||||
}, m)
|
}, m)
|
||||||
api.RegisterConfigurationApiEndpoints(AdminSrv{
|
api.RegisterConfigurationApiEndpoints(AdminSrv{
|
||||||
store: api.AdminConfigStore,
|
store: api.AdminConfigStore,
|
||||||
|
|||||||
@@ -8,22 +8,22 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestingApiSrv struct {
|
type TestingApiSrv struct {
|
||||||
*AlertingProxy
|
*AlertingProxy
|
||||||
Cfg *setting.Cfg
|
Cfg *setting.Cfg
|
||||||
DataService *tsdb.Service
|
ExpressionService *expr.Service
|
||||||
DatasourceCache datasources.CacheService
|
DatasourceCache datasources.CacheService
|
||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv TestingApiSrv) RouteTestRuleConfig(c *models.ReqContext, body apimodels.TestRulePayload) response.Response {
|
func (srv TestingApiSrv) RouteTestRuleConfig(c *models.ReqContext, body apimodels.TestRulePayload) response.Response {
|
||||||
@@ -32,7 +32,7 @@ func (srv TestingApiSrv) RouteTestRuleConfig(c *models.ReqContext, body apimodel
|
|||||||
if body.Type() != apimodels.GrafanaBackend || body.GrafanaManagedCondition == nil {
|
if body.Type() != apimodels.GrafanaBackend || body.GrafanaManagedCondition == nil {
|
||||||
return ErrResp(http.StatusBadRequest, errors.New("unexpected payload"), "")
|
return ErrResp(http.StatusBadRequest, errors.New("unexpected payload"), "")
|
||||||
}
|
}
|
||||||
return conditionEval(c, *body.GrafanaManagedCondition, srv.DatasourceCache, srv.DataService, srv.Cfg, srv.log)
|
return conditionEval(c, *body.GrafanaManagedCondition, srv.DatasourceCache, srv.ExpressionService, srv.Cfg, srv.log)
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.Type() != apimodels.LoTexRulerBackend {
|
if body.Type() != apimodels.LoTexRulerBackend {
|
||||||
@@ -86,7 +86,7 @@ func (srv TestingApiSrv) RouteEvalQueries(c *models.ReqContext, cmd apimodels.Ev
|
|||||||
}
|
}
|
||||||
|
|
||||||
evaluator := eval.Evaluator{Cfg: srv.Cfg, Log: srv.log}
|
evaluator := eval.Evaluator{Cfg: srv.Cfg, Log: srv.log}
|
||||||
evalResults, err := evaluator.QueriesAndExpressionsEval(c.SignedInUser.OrgId, cmd.Data, now, srv.DataService)
|
evalResults, err := evaluator.QueriesAndExpressionsEval(c.SignedInUser.OrgId, cmd.Data, now, srv.ExpressionService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrResp(http.StatusBadRequest, err, "Failed to evaluate queries and expressions")
|
return ErrResp(http.StatusBadRequest, err, "Failed to evaluate queries and expressions")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
||||||
@@ -21,7 +22,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -223,7 +223,7 @@ func validateQueriesAndExpressions(data []ngmodels.AlertQuery, user *models.Sign
|
|||||||
return refIDs, nil
|
return refIDs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func conditionEval(c *models.ReqContext, cmd ngmodels.EvalAlertConditionCommand, datasourceCache datasources.CacheService, dataService *tsdb.Service, cfg *setting.Cfg, log log.Logger) response.Response {
|
func conditionEval(c *models.ReqContext, cmd ngmodels.EvalAlertConditionCommand, datasourceCache datasources.CacheService, expressionService *expr.Service, cfg *setting.Cfg, log log.Logger) response.Response {
|
||||||
evalCond := ngmodels.Condition{
|
evalCond := ngmodels.Condition{
|
||||||
Condition: cmd.Condition,
|
Condition: cmd.Condition,
|
||||||
OrgID: c.SignedInUser.OrgId,
|
OrgID: c.SignedInUser.OrgId,
|
||||||
@@ -239,7 +239,7 @@ func conditionEval(c *models.ReqContext, cmd ngmodels.EvalAlertConditionCommand,
|
|||||||
}
|
}
|
||||||
|
|
||||||
evaluator := eval.Evaluator{Cfg: cfg, Log: log}
|
evaluator := eval.Evaluator{Cfg: cfg, Log: log}
|
||||||
evalResults, err := evaluator.ConditionEval(&evalCond, now, dataService)
|
evalResults, err := evaluator.ConditionEval(&evalCond, now, expressionService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrResp(http.StatusBadRequest, err, "Failed to evaluate conditions")
|
return ErrResp(http.StatusBadRequest, err, "Failed to evaluate conditions")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
@@ -165,10 +164,10 @@ type NumberValueCapture struct {
|
|||||||
Value *float64
|
Value *float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeCondition(ctx AlertExecCtx, c *models.Condition, now time.Time, dataService *tsdb.Service) ExecutionResults {
|
func executeCondition(ctx AlertExecCtx, c *models.Condition, now time.Time, exprService *expr.Service) ExecutionResults {
|
||||||
result := ExecutionResults{}
|
result := ExecutionResults{}
|
||||||
|
|
||||||
execResp, err := executeQueriesAndExpressions(ctx, c.Data, now, dataService)
|
execResp, err := executeQueriesAndExpressions(ctx, c.Data, now, exprService)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ExecutionResults{Error: err}
|
return ExecutionResults{Error: err}
|
||||||
@@ -232,7 +231,7 @@ func executeCondition(ctx AlertExecCtx, c *models.Condition, now time.Time, data
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeQueriesAndExpressions(ctx AlertExecCtx, data []models.AlertQuery, now time.Time, dataService *tsdb.Service) (resp *backend.QueryDataResponse, err error) {
|
func executeQueriesAndExpressions(ctx AlertExecCtx, data []models.AlertQuery, now time.Time, exprService *expr.Service) (resp *backend.QueryDataResponse, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
ctx.Log.Error("alert rule panic", "error", e, "stack", string(debug.Stack()))
|
ctx.Log.Error("alert rule panic", "error", e, "stack", string(debug.Stack()))
|
||||||
@@ -250,10 +249,6 @@ func executeQueriesAndExpressions(ctx AlertExecCtx, data []models.AlertQuery, no
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
exprService := expr.Service{
|
|
||||||
Cfg: &setting.Cfg{ExpressionsEnabled: ctx.ExpressionsEnabled},
|
|
||||||
DataService: dataService,
|
|
||||||
}
|
|
||||||
return exprService.TransformData(ctx.Ctx, queryDataReq)
|
return exprService.TransformData(ctx.Ctx, queryDataReq)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,26 +426,26 @@ func (evalResults Results) AsDataFrame() data.Frame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ConditionEval executes conditions and evaluates the result.
|
// ConditionEval executes conditions and evaluates the result.
|
||||||
func (e *Evaluator) ConditionEval(condition *models.Condition, now time.Time, dataService *tsdb.Service) (Results, error) {
|
func (e *Evaluator) ConditionEval(condition *models.Condition, now time.Time, expressionService *expr.Service) (Results, error) {
|
||||||
alertCtx, cancelFn := context.WithTimeout(context.Background(), e.Cfg.UnifiedAlerting.EvaluationTimeout)
|
alertCtx, cancelFn := context.WithTimeout(context.Background(), e.Cfg.UnifiedAlerting.EvaluationTimeout)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
|
|
||||||
alertExecCtx := AlertExecCtx{OrgID: condition.OrgID, Ctx: alertCtx, ExpressionsEnabled: e.Cfg.ExpressionsEnabled, Log: e.Log}
|
alertExecCtx := AlertExecCtx{OrgID: condition.OrgID, Ctx: alertCtx, ExpressionsEnabled: e.Cfg.ExpressionsEnabled, Log: e.Log}
|
||||||
|
|
||||||
execResult := executeCondition(alertExecCtx, condition, now, dataService)
|
execResult := executeCondition(alertExecCtx, condition, now, expressionService)
|
||||||
|
|
||||||
evalResults := evaluateExecutionResult(execResult, now)
|
evalResults := evaluateExecutionResult(execResult, now)
|
||||||
return evalResults, nil
|
return evalResults, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueriesAndExpressionsEval executes queries and expressions and returns the result.
|
// QueriesAndExpressionsEval executes queries and expressions and returns the result.
|
||||||
func (e *Evaluator) QueriesAndExpressionsEval(orgID int64, data []models.AlertQuery, now time.Time, dataService *tsdb.Service) (*backend.QueryDataResponse, error) {
|
func (e *Evaluator) QueriesAndExpressionsEval(orgID int64, data []models.AlertQuery, now time.Time, expressionService *expr.Service) (*backend.QueryDataResponse, error) {
|
||||||
alertCtx, cancelFn := context.WithTimeout(context.Background(), e.Cfg.UnifiedAlerting.EvaluationTimeout)
|
alertCtx, cancelFn := context.WithTimeout(context.Background(), e.Cfg.UnifiedAlerting.EvaluationTimeout)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
|
|
||||||
alertExecCtx := AlertExecCtx{OrgID: orgID, Ctx: alertCtx, ExpressionsEnabled: e.Cfg.ExpressionsEnabled, Log: e.Log}
|
alertExecCtx := AlertExecCtx{OrgID: orgID, Ctx: alertCtx, ExpressionsEnabled: e.Cfg.ExpressionsEnabled, Log: e.Log}
|
||||||
|
|
||||||
execResult, err := executeQueriesAndExpressions(alertExecCtx, data, now, dataService)
|
execResult, err := executeQueriesAndExpressions(alertExecCtx, data, now, expressionService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute conditions: %w", err)
|
return nil, fmt.Errorf("failed to execute conditions: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/benbjohnson/clock"
|
"github.com/benbjohnson/clock"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
||||||
@@ -22,7 +23,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/secrets"
|
"github.com/grafana/grafana/pkg/services/secrets"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,20 +38,20 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, dataSourceCache datasources.CacheService, routeRegister routing.RouteRegister,
|
func ProvideService(cfg *setting.Cfg, dataSourceCache datasources.CacheService, routeRegister routing.RouteRegister,
|
||||||
sqlStore *sqlstore.SQLStore, kvStore kvstore.KVStore, dataService *tsdb.Service, dataProxy *datasourceproxy.DataSourceProxyService,
|
sqlStore *sqlstore.SQLStore, kvStore kvstore.KVStore, expressionService *expr.Service, dataProxy *datasourceproxy.DataSourceProxyService,
|
||||||
quotaService *quota.QuotaService, secretsService secrets.Service, m *metrics.NGAlert) (*AlertNG, error) {
|
quotaService *quota.QuotaService, secretsService secrets.Service, m *metrics.NGAlert) (*AlertNG, error) {
|
||||||
ng := &AlertNG{
|
ng := &AlertNG{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
DataSourceCache: dataSourceCache,
|
DataSourceCache: dataSourceCache,
|
||||||
RouteRegister: routeRegister,
|
RouteRegister: routeRegister,
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
KVStore: kvStore,
|
KVStore: kvStore,
|
||||||
DataService: dataService,
|
ExpressionService: expressionService,
|
||||||
DataProxy: dataProxy,
|
DataProxy: dataProxy,
|
||||||
QuotaService: quotaService,
|
QuotaService: quotaService,
|
||||||
SecretsService: secretsService,
|
SecretsService: secretsService,
|
||||||
Metrics: m,
|
Metrics: m,
|
||||||
Log: log.New("ngalert"),
|
Log: log.New("ngalert"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if ng.IsDisabled() {
|
if ng.IsDisabled() {
|
||||||
@@ -67,19 +67,19 @@ func ProvideService(cfg *setting.Cfg, dataSourceCache datasources.CacheService,
|
|||||||
|
|
||||||
// AlertNG is the service for evaluating the condition of an alert definition.
|
// AlertNG is the service for evaluating the condition of an alert definition.
|
||||||
type AlertNG struct {
|
type AlertNG struct {
|
||||||
Cfg *setting.Cfg
|
Cfg *setting.Cfg
|
||||||
DataSourceCache datasources.CacheService
|
DataSourceCache datasources.CacheService
|
||||||
RouteRegister routing.RouteRegister
|
RouteRegister routing.RouteRegister
|
||||||
SQLStore *sqlstore.SQLStore
|
SQLStore *sqlstore.SQLStore
|
||||||
KVStore kvstore.KVStore
|
KVStore kvstore.KVStore
|
||||||
DataService *tsdb.Service
|
ExpressionService *expr.Service
|
||||||
DataProxy *datasourceproxy.DataSourceProxyService
|
DataProxy *datasourceproxy.DataSourceProxyService
|
||||||
QuotaService *quota.QuotaService
|
QuotaService *quota.QuotaService
|
||||||
SecretsService secrets.Service
|
SecretsService secrets.Service
|
||||||
Metrics *metrics.NGAlert
|
Metrics *metrics.NGAlert
|
||||||
Log log.Logger
|
Log log.Logger
|
||||||
schedule schedule.ScheduleService
|
schedule schedule.ScheduleService
|
||||||
stateManager *state.Manager
|
stateManager *state.Manager
|
||||||
|
|
||||||
// Alerting notification services
|
// Alerting notification services
|
||||||
MultiOrgAlertmanager *notifier.MultiOrgAlertmanager
|
MultiOrgAlertmanager *notifier.MultiOrgAlertmanager
|
||||||
@@ -136,7 +136,7 @@ func (ng *AlertNG) init() error {
|
|||||||
appUrl = nil
|
appUrl = nil
|
||||||
}
|
}
|
||||||
stateManager := state.NewManager(ng.Log, ng.Metrics.GetStateMetrics(), appUrl, store, store)
|
stateManager := state.NewManager(ng.Log, ng.Metrics.GetStateMetrics(), appUrl, store, store)
|
||||||
scheduler := schedule.NewScheduler(schedCfg, ng.DataService, appUrl, stateManager)
|
scheduler := schedule.NewScheduler(schedCfg, ng.ExpressionService, appUrl, stateManager)
|
||||||
|
|
||||||
ng.stateManager = stateManager
|
ng.stateManager = stateManager
|
||||||
ng.schedule = scheduler
|
ng.schedule = scheduler
|
||||||
@@ -145,7 +145,7 @@ func (ng *AlertNG) init() error {
|
|||||||
Cfg: ng.Cfg,
|
Cfg: ng.Cfg,
|
||||||
DatasourceCache: ng.DataSourceCache,
|
DatasourceCache: ng.DataSourceCache,
|
||||||
RouteRegister: ng.RouteRegister,
|
RouteRegister: ng.RouteRegister,
|
||||||
DataService: ng.DataService,
|
ExpressionService: ng.ExpressionService,
|
||||||
Schedule: ng.schedule,
|
Schedule: ng.schedule,
|
||||||
DataProxy: ng.DataProxy,
|
DataProxy: ng.DataProxy,
|
||||||
QuotaService: ng.QuotaService,
|
QuotaService: ng.QuotaService,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
@@ -17,7 +18,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/ngalert/sender"
|
"github.com/grafana/grafana/pkg/services/ngalert/sender"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
|
||||||
|
|
||||||
"github.com/benbjohnson/clock"
|
"github.com/benbjohnson/clock"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -64,11 +64,11 @@ type schedule struct {
|
|||||||
|
|
||||||
evaluator eval.Evaluator
|
evaluator eval.Evaluator
|
||||||
|
|
||||||
ruleStore store.RuleStore
|
ruleStore store.RuleStore
|
||||||
instanceStore store.InstanceStore
|
instanceStore store.InstanceStore
|
||||||
adminConfigStore store.AdminConfigurationStore
|
adminConfigStore store.AdminConfigurationStore
|
||||||
orgStore store.OrgStore
|
orgStore store.OrgStore
|
||||||
dataService *tsdb.Service
|
expressionService *expr.Service
|
||||||
|
|
||||||
stateManager *state.Manager
|
stateManager *state.Manager
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ type SchedulerCfg struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewScheduler returns a new schedule.
|
// NewScheduler returns a new schedule.
|
||||||
func NewScheduler(cfg SchedulerCfg, dataService *tsdb.Service, appURL *url.URL, stateManager *state.Manager) *schedule {
|
func NewScheduler(cfg SchedulerCfg, expressionService *expr.Service, appURL *url.URL, stateManager *state.Manager) *schedule {
|
||||||
ticker := alerting.NewTicker(cfg.C.Now(), time.Second*0, cfg.C, int64(cfg.BaseInterval.Seconds()))
|
ticker := alerting.NewTicker(cfg.C.Now(), time.Second*0, cfg.C, int64(cfg.BaseInterval.Seconds()))
|
||||||
|
|
||||||
sch := schedule{
|
sch := schedule{
|
||||||
@@ -123,7 +123,7 @@ func NewScheduler(cfg SchedulerCfg, dataService *tsdb.Service, appURL *url.URL,
|
|||||||
ruleStore: cfg.RuleStore,
|
ruleStore: cfg.RuleStore,
|
||||||
instanceStore: cfg.InstanceStore,
|
instanceStore: cfg.InstanceStore,
|
||||||
orgStore: cfg.OrgStore,
|
orgStore: cfg.OrgStore,
|
||||||
dataService: dataService,
|
expressionService: expressionService,
|
||||||
adminConfigStore: cfg.AdminConfigStore,
|
adminConfigStore: cfg.AdminConfigStore,
|
||||||
multiOrgNotifier: cfg.MultiOrgNotifier,
|
multiOrgNotifier: cfg.MultiOrgNotifier,
|
||||||
metrics: cfg.Metrics,
|
metrics: cfg.Metrics,
|
||||||
@@ -449,7 +449,7 @@ func (sch *schedule) ruleRoutine(grafanaCtx context.Context, key models.AlertRul
|
|||||||
OrgID: alertRule.OrgID,
|
OrgID: alertRule.OrgID,
|
||||||
Data: alertRule.Data,
|
Data: alertRule.Data,
|
||||||
}
|
}
|
||||||
results, err := sch.evaluator.ConditionEval(&condition, ctx.now, sch.dataService)
|
results, err := sch.evaluator.ConditionEval(&condition, ctx.now, sch.expressionService)
|
||||||
dur := sch.clock.Now().Sub(start)
|
dur := sch.clock.Now().Sub(start)
|
||||||
evalTotal.Inc()
|
evalTotal.Inc()
|
||||||
evalDuration.Observe(dur.Seconds())
|
evalDuration.Observe(dur.Seconds())
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/prometheus/common/model"
|
"github.com/prometheus/common/model"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
@@ -604,7 +605,7 @@ func setupScheduler(t *testing.T, rs store.RuleStore, is store.InstanceStore, ac
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
}
|
}
|
||||||
return NewScheduler(schedCfg, nil, appUrl, st), mockedClock
|
return NewScheduler(schedCfg, expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, nil, nil), appUrl, st), mockedClock
|
||||||
}
|
}
|
||||||
|
|
||||||
// createTestAlertRule creates a dummy alert definition to be used by the tests.
|
// createTestAlertRule creates a dummy alert definition to be used by the tests.
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ type Cfg struct {
|
|||||||
EnforceDomain bool
|
EnforceDomain bool
|
||||||
|
|
||||||
// Security settings
|
// Security settings
|
||||||
|
SecretKey string
|
||||||
EmailCodeValidMinutes int
|
EmailCodeValidMinutes int
|
||||||
|
|
||||||
// build
|
// build
|
||||||
@@ -1139,6 +1140,7 @@ func (cfg *Cfg) SectionWithEnvOverrides(s string) *DynamicSection {
|
|||||||
func readSecuritySettings(iniFile *ini.File, cfg *Cfg) error {
|
func readSecuritySettings(iniFile *ini.File, cfg *Cfg) error {
|
||||||
security := iniFile.Section("security")
|
security := iniFile.Section("security")
|
||||||
SecretKey = valueAsString(security, "secret_key", "")
|
SecretKey = valueAsString(security, "secret_key", "")
|
||||||
|
cfg.SecretKey = SecretKey
|
||||||
DisableGravatar = security.Key("disable_gravatar").MustBool(true)
|
DisableGravatar = security.Key("disable_gravatar").MustBool(true)
|
||||||
cfg.DisableBruteForceLoginProtection = security.Key("disable_brute_force_login_protection").MustBool(false)
|
cfg.DisableBruteForceLoginProtection = security.Key("disable_brute_force_login_protection").MustBool(false)
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -25,13 +25,13 @@ func TestBuildingAzureResourceGraphQueries(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
queryModel []backend.DataQuery
|
queryModel []backend.DataQuery
|
||||||
timeRange plugins.DataTimeRange
|
timeRange legacydata.DataTimeRange
|
||||||
azureResourceGraphQueries []*AzureResourceGraphQuery
|
azureResourceGraphQueries []*AzureResourceGraphQuery
|
||||||
Err require.ErrorAssertionFunc
|
Err require.ErrorAssertionFunc
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Query with macros should be interpolated",
|
name: "Query with macros should be interpolated",
|
||||||
timeRange: plugins.DataTimeRange{
|
timeRange: legacydata.DataTimeRange{
|
||||||
From: fmt.Sprintf("%v", fromStart.Unix()*1000),
|
From: fmt.Sprintf("%v", fromStart.Unix()*1000),
|
||||||
To: fmt.Sprintf("%v", fromStart.Add(34*time.Minute).Unix()*1000),
|
To: fmt.Sprintf("%v", fromStart.Add(34*time.Minute).Unix()*1000),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/interval"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata/interval"
|
||||||
)
|
)
|
||||||
|
|
||||||
const rsIdentifier = `__(timeFilter|timeFrom|timeTo|interval|contains|escapeMulti)`
|
const rsIdentifier = `__(timeFilter|timeFrom|timeTo|interval|contains|escapeMulti)`
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/interval"
|
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TimeGrain handles conversions between
|
// TimeGrain handles conversions between
|
||||||
@@ -19,7 +19,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (tg *TimeGrain) createISO8601DurationFromIntervalMS(it int64) (string, error) {
|
func (tg *TimeGrain) createISO8601DurationFromIntervalMS(it int64) (string, error) {
|
||||||
formatted := interval.FormatDuration(time.Duration(it) * time.Millisecond)
|
formatted := intervalv2.FormatDuration(time.Duration(it) * time.Millisecond)
|
||||||
|
|
||||||
if strings.Contains(formatted, "ms") {
|
if strings.Contains(formatted, "ms") {
|
||||||
return "PT1M", nil
|
return "PT1M", nil
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ func (r *logQueryRunner) publishResults(orgID int64, channelName string) error {
|
|||||||
|
|
||||||
// executeLiveLogQuery executes a CloudWatch Logs query with live updates over WebSocket.
|
// executeLiveLogQuery executes a CloudWatch Logs query with live updates over WebSocket.
|
||||||
// A WebSocket channel is created, which goroutines send responses over.
|
// A WebSocket channel is created, which goroutines send responses over.
|
||||||
//nolint: staticcheck // plugins.DataResponse deprecated
|
|
||||||
func (e *cloudWatchExecutor) executeLiveLogQuery(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
func (e *cloudWatchExecutor) executeLiveLogQuery(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||||
responseChannelName := uuid.New().String()
|
responseChannelName := uuid.New().String()
|
||||||
responseChannel := make(chan *backend.QueryDataResponse)
|
responseChannel := make(chan *backend.QueryDataResponse)
|
||||||
@@ -133,7 +132,6 @@ func (e *cloudWatchExecutor) executeLiveLogQuery(ctx context.Context, req *backe
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataResponse deprecated
|
|
||||||
func (e *cloudWatchExecutor) sendLiveQueriesToChannel(req *backend.QueryDataRequest, responseChannel chan *backend.QueryDataResponse) {
|
func (e *cloudWatchExecutor) sendLiveQueriesToChannel(req *backend.QueryDataRequest, responseChannel chan *backend.QueryDataResponse) {
|
||||||
defer close(responseChannel)
|
defer close(responseChannel)
|
||||||
|
|
||||||
@@ -211,7 +209,6 @@ func (e *cloudWatchExecutor) fetchConcurrentQueriesQuota(region string, pluginCt
|
|||||||
return defaultConcurrentQueries
|
return defaultConcurrentQueries
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataResponse deprecated
|
|
||||||
func (e *cloudWatchExecutor) startLiveQuery(ctx context.Context, responseChannel chan *backend.QueryDataResponse, query backend.DataQuery, timeRange backend.TimeRange, pluginCtx backend.PluginContext) error {
|
func (e *cloudWatchExecutor) startLiveQuery(ctx context.Context, responseChannel chan *backend.QueryDataResponse, query backend.DataQuery, timeRange backend.TimeRange, pluginCtx backend.PluginContext) error {
|
||||||
model, err := simplejson.NewJson(query.JSON)
|
model, err := simplejson.NewJson(query.JSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
func ProvideLogsService() *LogsService {
|
func ProvideLogsService() *LogsService {
|
||||||
return &LogsService{
|
return &LogsService{
|
||||||
// nolint:staticcheck // plugins.DataQueryResponse deprecated
|
|
||||||
responseChannels: make(map[string]chan *backend.QueryDataResponse),
|
responseChannels: make(map[string]chan *backend.QueryDataResponse),
|
||||||
queues: make(map[string](chan bool)),
|
queues: make(map[string](chan bool)),
|
||||||
}
|
}
|
||||||
@@ -17,14 +16,12 @@ func ProvideLogsService() *LogsService {
|
|||||||
|
|
||||||
// LogsService provides methods for querying CloudWatch Logs.
|
// LogsService provides methods for querying CloudWatch Logs.
|
||||||
type LogsService struct {
|
type LogsService struct {
|
||||||
channelMu sync.Mutex
|
channelMu sync.Mutex
|
||||||
// nolint:staticcheck // plugins.DataQueryResult deprecated
|
|
||||||
responseChannels map[string]chan *backend.QueryDataResponse
|
responseChannels map[string]chan *backend.QueryDataResponse
|
||||||
queues map[string](chan bool)
|
queues map[string](chan bool)
|
||||||
queueLock sync.Mutex
|
queueLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck // plugins.DataQueryResult deprecated
|
|
||||||
func (s *LogsService) AddResponseChannel(name string, channel chan *backend.QueryDataResponse) error {
|
func (s *LogsService) AddResponseChannel(name string, channel chan *backend.QueryDataResponse) error {
|
||||||
s.channelMu.Lock()
|
s.channelMu.Lock()
|
||||||
defer s.channelMu.Unlock()
|
defer s.channelMu.Unlock()
|
||||||
@@ -37,7 +34,6 @@ func (s *LogsService) AddResponseChannel(name string, channel chan *backend.Quer
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck // plugins.DataQueryResult deprecated
|
|
||||||
func (s *LogsService) GetResponseChannel(name string) (chan *backend.QueryDataResponse, error) {
|
func (s *LogsService) GetResponseChannel(name string) (chan *backend.QueryDataResponse, error) {
|
||||||
s.channelMu.Lock()
|
s.channelMu.Lock()
|
||||||
defer s.channelMu.Unlock()
|
defer s.channelMu.Unlock()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -48,7 +48,7 @@ func TestRequestParser(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
timeRange := tsdb.NewTimeRange("now-1h", "now-2h")
|
timeRange := legacydata.NewDataTimeRange("now-1h", "now-2h")
|
||||||
from, err := timeRange.ParseFrom()
|
from, err := timeRange.ParseFrom()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
to, err := timeRange.ParseTo()
|
to, err := timeRange.ParseTo()
|
||||||
@@ -138,7 +138,7 @@ func TestRequestParser(t *testing.T) {
|
|||||||
"hide": false,
|
"hide": false,
|
||||||
})
|
})
|
||||||
query.Set("period", "900")
|
query.Set("period", "900")
|
||||||
timeRange := tsdb.NewTimeRange("now-1h", "now-2h")
|
timeRange := legacydata.NewDataTimeRange("now-1h", "now-2h")
|
||||||
from, err := timeRange.ParseFrom()
|
from, err := timeRange.ParseFrom()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
to, err := timeRange.ParseTo()
|
to, err := timeRange.ParseTo()
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
package tsdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/adapters"
|
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
|
||||||
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
|
||||||
)
|
|
||||||
|
|
||||||
// nolint:staticcheck // plugins.DataQuery deprecated
|
|
||||||
func dataPluginQueryAdapter(pluginID string, handler backend.QueryDataHandler, oAuthService oauthtoken.OAuthTokenService,
|
|
||||||
dsService *datasources.Service) plugins.DataPluginFunc {
|
|
||||||
return func(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (plugins.DataResponse, error) {
|
|
||||||
jsonDataBytes, err := ds.JsonData.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
return plugins.DataResponse{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceSettings := &backend.DataSourceInstanceSettings{
|
|
||||||
ID: ds.Id,
|
|
||||||
Name: ds.Name,
|
|
||||||
URL: ds.Url,
|
|
||||||
Database: ds.Database,
|
|
||||||
User: ds.User,
|
|
||||||
BasicAuthEnabled: ds.BasicAuth,
|
|
||||||
BasicAuthUser: ds.BasicAuthUser,
|
|
||||||
JSONData: jsonDataBytes,
|
|
||||||
DecryptedSecureJSONData: dsService.DecryptedValues(ds),
|
|
||||||
Updated: ds.Updated,
|
|
||||||
UID: ds.Uid,
|
|
||||||
}
|
|
||||||
|
|
||||||
if query.Headers == nil {
|
|
||||||
query.Headers = make(map[string]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if oAuthService.IsOAuthPassThruEnabled(ds) {
|
|
||||||
if token := oAuthService.GetCurrentOAuthToken(ctx, query.User); token != nil {
|
|
||||||
delete(query.Headers, "Authorization")
|
|
||||||
query.Headers["Authorization"] = fmt.Sprintf("%s %s", token.Type(), token.AccessToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &backend.QueryDataRequest{
|
|
||||||
PluginContext: backend.PluginContext{
|
|
||||||
OrgID: ds.OrgId,
|
|
||||||
PluginID: pluginID,
|
|
||||||
User: adapters.BackendUserFromSignedInUser(query.User),
|
|
||||||
DataSourceInstanceSettings: instanceSettings,
|
|
||||||
},
|
|
||||||
Queries: []backend.DataQuery{},
|
|
||||||
Headers: query.Headers,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, q := range query.Queries {
|
|
||||||
modelJSON, err := q.Model.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
return plugins.DataResponse{}, err
|
|
||||||
}
|
|
||||||
req.Queries = append(req.Queries, backend.DataQuery{
|
|
||||||
RefID: q.RefID,
|
|
||||||
Interval: time.Duration(q.IntervalMS) * time.Millisecond,
|
|
||||||
MaxDataPoints: q.MaxDataPoints,
|
|
||||||
TimeRange: backend.TimeRange{
|
|
||||||
From: query.TimeRange.GetFromAsTimeUTC(),
|
|
||||||
To: query.TimeRange.GetToAsTimeUTC(),
|
|
||||||
},
|
|
||||||
QueryType: q.QueryType,
|
|
||||||
JSON: modelJSON,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := handler.QueryData(ctx, req)
|
|
||||||
if err != nil {
|
|
||||||
return plugins.DataResponse{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tR := plugins.DataResponse{
|
|
||||||
Results: make(map[string]plugins.DataQueryResult, len(resp.Responses)),
|
|
||||||
}
|
|
||||||
|
|
||||||
for refID, r := range resp.Responses {
|
|
||||||
qr := plugins.DataQueryResult{
|
|
||||||
RefID: refID,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range r.Frames {
|
|
||||||
if f.RefID == "" {
|
|
||||||
f.RefID = refID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qr.Dataframes = plugins.NewDecodedDataFrames(r.Frames)
|
|
||||||
|
|
||||||
if r.Error != nil {
|
|
||||||
qr.Error = r.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
tR.Results[refID] = qr
|
|
||||||
}
|
|
||||||
|
|
||||||
return tR, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -42,7 +42,6 @@ var newResponseParser = func(responses []*es.SearchResponse, targets []*Query, d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck
|
|
||||||
func (rp *responseParser) getTimeSeries() (*backend.QueryDataResponse, error) {
|
func (rp *responseParser) getTimeSeries() (*backend.QueryDataResponse, error) {
|
||||||
result := backend.QueryDataResponse{
|
result := backend.QueryDataResponse{
|
||||||
Responses: backend.Responses{},
|
Responses: backend.Responses{},
|
||||||
@@ -93,7 +92,6 @@ func (rp *responseParser) getTimeSeries() (*backend.QueryDataResponse, error) {
|
|||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck
|
|
||||||
func (rp *responseParser) processBuckets(aggs map[string]interface{}, target *Query,
|
func (rp *responseParser) processBuckets(aggs map[string]interface{}, target *Query,
|
||||||
queryResult *backend.DataResponse, props map[string]string, depth int) error {
|
queryResult *backend.DataResponse, props map[string]string, depth int) error {
|
||||||
var err error
|
var err error
|
||||||
@@ -172,7 +170,7 @@ func (rp *responseParser) processBuckets(aggs map[string]interface{}, target *Qu
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck,gocyclo
|
// nolint:gocyclo
|
||||||
func (rp *responseParser) processMetrics(esAgg *simplejson.Json, target *Query, query *backend.DataResponse,
|
func (rp *responseParser) processMetrics(esAgg *simplejson.Json, target *Query, query *backend.DataResponse,
|
||||||
props map[string]string) error {
|
props map[string]string) error {
|
||||||
frames := data.Frames{}
|
frames := data.Frames{}
|
||||||
@@ -365,7 +363,6 @@ func (rp *responseParser) processMetrics(esAgg *simplejson.Json, target *Query,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck
|
|
||||||
func (rp *responseParser) processAggregationDocs(esAgg *simplejson.Json, aggDef *BucketAgg, target *Query,
|
func (rp *responseParser) processAggregationDocs(esAgg *simplejson.Json, aggDef *BucketAgg, target *Query,
|
||||||
queryResult *backend.DataResponse, props map[string]string) error {
|
queryResult *backend.DataResponse, props map[string]string) error {
|
||||||
propKeys := make([]string, 0)
|
propKeys := make([]string, 0)
|
||||||
@@ -517,7 +514,6 @@ func extractDataField(name string, v interface{}) *data.Field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck
|
|
||||||
func (rp *responseParser) trimDatapoints(queryResult backend.DataResponse, target *Query) {
|
func (rp *responseParser) trimDatapoints(queryResult backend.DataResponse, target *Query) {
|
||||||
var histogram *BucketAgg
|
var histogram *BucketAgg
|
||||||
for _, bucketAgg := range target.BucketAggs {
|
for _, bucketAgg := range target.BucketAggs {
|
||||||
@@ -551,7 +547,6 @@ func (rp *responseParser) trimDatapoints(queryResult backend.DataResponse, targe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck
|
|
||||||
func (rp *responseParser) nameFields(queryResult backend.DataResponse, target *Query) {
|
func (rp *responseParser) nameFields(queryResult backend.DataResponse, target *Query) {
|
||||||
set := make(map[string]struct{})
|
set := make(map[string]struct{})
|
||||||
frames := queryResult.Frames
|
frames := queryResult.Frames
|
||||||
@@ -575,7 +570,6 @@ func (rp *responseParser) nameFields(queryResult backend.DataResponse, target *Q
|
|||||||
|
|
||||||
var aliasPatternRegex = regexp.MustCompile(`\{\{([\s\S]+?)\}\}`)
|
var aliasPatternRegex = regexp.MustCompile(`\{\{([\s\S]+?)\}\}`)
|
||||||
|
|
||||||
// nolint:staticcheck
|
|
||||||
func (rp *responseParser) getFieldName(dataField data.Field, target *Query, metricTypeCount int) string {
|
func (rp *responseParser) getFieldName(dataField data.Field, target *Query, metricTypeCount int) string {
|
||||||
metricType := dataField.Labels["metric"]
|
metricType := dataField.Labels["metric"]
|
||||||
metricName := rp.getMetricName(metricType)
|
metricName := rp.getMetricName(metricType)
|
||||||
@@ -708,7 +702,6 @@ func findAgg(target *Query, aggID string) (*BucketAgg, error) {
|
|||||||
return nil, errors.New("can't found aggDef, aggID:" + aggID)
|
return nil, errors.New("can't found aggDef, aggID:" + aggID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck
|
|
||||||
func getErrorFromElasticResponse(response *es.SearchResponse) string {
|
func getErrorFromElasticResponse(response *es.SearchResponse) string {
|
||||||
var errorString string
|
var errorString string
|
||||||
json := simplejson.NewFromAny(response.Error)
|
json := simplejson.NewFromAny(response.Error)
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ var newTimeSeriesQuery = func(client es.Client, dataQuery []backend.DataQuery,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck
|
|
||||||
func (e *timeSeriesQuery) execute() (*backend.QueryDataResponse, error) {
|
func (e *timeSeriesQuery) execute() (*backend.QueryDataResponse, error) {
|
||||||
tsQueryParser := newTimeSeriesQueryParser()
|
tsQueryParser := newTimeSeriesQueryParser()
|
||||||
queries, err := tsQueryParser.parse(e.dataQueries)
|
queries, err := tsQueryParser.parse(e.dataQueries)
|
||||||
@@ -63,7 +62,6 @@ func (e *timeSeriesQuery) execute() (*backend.QueryDataResponse, error) {
|
|||||||
return rp.getTimeSeries()
|
return rp.getTimeSeries()
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck
|
|
||||||
func (e *timeSeriesQuery) processQuery(q *Query, ms *es.MultiSearchRequestBuilder, from, to string,
|
func (e *timeSeriesQuery) processQuery(q *Query, ms *es.MultiSearchRequestBuilder, from, to string,
|
||||||
result backend.QueryDataResponse) error {
|
result backend.QueryDataResponse) error {
|
||||||
minInterval, err := e.client.GetMinInterval(q.Interval)
|
minInterval, err := e.client.GetMinInterval(q.Interval)
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ func TestExecuteTimeSeriesQuery(t *testing.T) {
|
|||||||
"id": "2",
|
"id": "2",
|
||||||
"type": "date_histogram",
|
"type": "date_histogram",
|
||||||
"field": "@timestamp",
|
"field": "@timestamp",
|
||||||
"settings": {
|
"settings": {
|
||||||
"timeZone": "utc"
|
"timeZone": "utc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,7 +435,7 @@ func TestExecuteTimeSeriesQuery(t *testing.T) {
|
|||||||
"id": "2",
|
"id": "2",
|
||||||
"type": "date_histogram",
|
"type": "date_histogram",
|
||||||
"field": "@timestamp",
|
"field": "@timestamp",
|
||||||
"settings": {
|
"settings": {
|
||||||
"timeZone": "America/Los_Angeles"
|
"timeZone": "America/Los_Angeles"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1181,7 +1181,6 @@ func newDataQuery(body string) (backend.QueryDataRequest, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:staticcheck // plugins.DataQueryResult deprecated
|
|
||||||
func executeTsdbQuery(c es.Client, body string, from, to time.Time, minInterval time.Duration) (
|
func executeTsdbQuery(c es.Client, body string, from, to time.Time, minInterval time.Duration) (
|
||||||
*backend.QueryDataResponse, error) {
|
*backend.QueryDataResponse, error) {
|
||||||
timeRange := backend.TimeRange{
|
timeRange := backend.TimeRange{
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/opentracing/opentracing-go"
|
"github.com/opentracing/opentracing-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -297,7 +298,7 @@ func epochMStoGraphiteTime(tr backend.TimeRange) (string, string) {
|
|||||||
/**
|
/**
|
||||||
* Graphite should always return timestamp as a number but values might be nil when data is missing
|
* Graphite should always return timestamp as a number but values might be nil when data is missing
|
||||||
*/
|
*/
|
||||||
func parseDataTimePoint(dataTimePoint plugins.DataTimePoint) (time.Time, *float64, error) {
|
func parseDataTimePoint(dataTimePoint legacydata.DataTimePoint) (time.Time, *float64, error) {
|
||||||
if !dataTimePoint[1].Valid {
|
if !dataTimePoint[1].Valid {
|
||||||
return time.Time{}, nil, errors.New("failed to parse data point timestamp")
|
return time.Time{}, nil, errors.New("failed to parse data point timestamp")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package graphite
|
package graphite
|
||||||
|
|
||||||
import "github.com/grafana/grafana/pkg/plugins"
|
import "github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
|
|
||||||
type TargetResponseDTO struct {
|
type TargetResponseDTO struct {
|
||||||
Target string `json:"target"`
|
Target string `json:"target"`
|
||||||
DataPoints plugins.DataTimeSeriesPoints `json:"datapoints"`
|
DataPoints legacydata.DataTimeSeriesPoints `json:"datapoints"`
|
||||||
// Graphite <=1.1.7 may return some tags as numbers requiring extra conversion. See https://github.com/grafana/grafana/issues/37614
|
// Graphite <=1.1.7 may return some tags as numbers requiring extra conversion. See https://github.com/grafana/grafana/issues/37614
|
||||||
Tags map[string]interface{} `json:"tags"`
|
Tags map[string]interface{} `json:"tags"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/interval"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
defaultRes int64 = 1500
|
DefaultRes int64 = 1500
|
||||||
defaultMinInterval = time.Millisecond * 1
|
defaultMinInterval = time.Millisecond * 1
|
||||||
year = time.Hour * 24 * 365
|
year = time.Hour * 24 * 365
|
||||||
day = time.Hour * 24
|
day = time.Hour * 24
|
||||||
@@ -59,18 +58,18 @@ func (ic *intervalCalculator) Calculate(timerange backend.TimeRange, minInterval
|
|||||||
from := timerange.From.UnixNano()
|
from := timerange.From.UnixNano()
|
||||||
resolution := maxDataPoints
|
resolution := maxDataPoints
|
||||||
if resolution == 0 {
|
if resolution == 0 {
|
||||||
resolution = defaultRes
|
resolution = DefaultRes
|
||||||
}
|
}
|
||||||
|
|
||||||
calculatedInterval := time.Duration((to - from) / resolution)
|
calculatedInterval := time.Duration((to - from) / resolution)
|
||||||
|
|
||||||
if calculatedInterval < minInterval {
|
if calculatedInterval < minInterval {
|
||||||
return Interval{Text: interval.FormatDuration(minInterval), Value: minInterval}
|
return Interval{Text: FormatDuration(minInterval), Value: minInterval}
|
||||||
}
|
}
|
||||||
|
|
||||||
rounded := roundInterval(calculatedInterval)
|
rounded := roundInterval(calculatedInterval)
|
||||||
|
|
||||||
return Interval{Text: interval.FormatDuration(rounded), Value: rounded}
|
return Interval{Text: FormatDuration(rounded), Value: rounded}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *intervalCalculator) CalculateSafeInterval(timerange backend.TimeRange, safeRes int64) Interval {
|
func (ic *intervalCalculator) CalculateSafeInterval(timerange backend.TimeRange, safeRes int64) Interval {
|
||||||
@@ -79,7 +78,7 @@ func (ic *intervalCalculator) CalculateSafeInterval(timerange backend.TimeRange,
|
|||||||
safeInterval := time.Duration((to - from) / safeRes)
|
safeInterval := time.Duration((to - from) / safeRes)
|
||||||
|
|
||||||
rounded := roundInterval(safeInterval)
|
rounded := roundInterval(safeInterval)
|
||||||
return Interval{Text: interval.FormatDuration(rounded), Value: rounded}
|
return Interval{Text: FormatDuration(rounded), Value: rounded}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIntervalFrom returns the minimum interval.
|
// GetIntervalFrom returns the minimum interval.
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
package plugins
|
package legacydata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana/pkg/components/null"
|
"github.com/grafana/grafana/pkg/components/null"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/timberio/go-datemath"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RequestHandler is a data request handler interface.
|
||||||
|
// Deprecated: use backend.QueryDataHandler instead.
|
||||||
|
type RequestHandler interface {
|
||||||
|
// HandleRequest handles a data request.
|
||||||
|
HandleRequest(context.Context, *models.DataSource, DataQuery) (DataResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
// DataSubQuery represents a data sub-query. New work should use the plugin SDK.
|
// DataSubQuery represents a data sub-query. New work should use the plugin SDK.
|
||||||
type DataSubQuery struct {
|
type DataSubQuery struct {
|
||||||
RefID string `json:"refId"`
|
RefID string `json:"refId"`
|
||||||
@@ -35,12 +39,6 @@ type DataQuery struct {
|
|||||||
User *models.SignedInUser
|
User *models.SignedInUser
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataTimeRange struct {
|
|
||||||
From string
|
|
||||||
To string
|
|
||||||
Now time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type DataTable struct {
|
type DataTable struct {
|
||||||
Columns []DataTableColumn `json:"columns"`
|
Columns []DataTableColumn `json:"columns"`
|
||||||
Rows []DataRowValues `json:"rows"`
|
Rows []DataRowValues `json:"rows"`
|
||||||
@@ -230,131 +228,3 @@ func (r DataResponse) ToBackendDataResponse() (*backend.QueryDataResponse, error
|
|||||||
}
|
}
|
||||||
return qdr, nil
|
return qdr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: use the plugin SDK
|
|
||||||
type DataPlugin interface {
|
|
||||||
DataQuery(ctx context.Context, ds *models.DataSource, query DataQuery) (DataResponse, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type DataPluginFunc func(ctx context.Context, ds *models.DataSource, query DataQuery) (DataResponse, error)
|
|
||||||
|
|
||||||
func (f DataPluginFunc) DataQuery(ctx context.Context, ds *models.DataSource, query DataQuery) (DataResponse, error) {
|
|
||||||
return f(ctx, ds, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDataTimeRange(from, to string) DataTimeRange {
|
|
||||||
return DataTimeRange{
|
|
||||||
From: from,
|
|
||||||
To: to,
|
|
||||||
Now: time.Now(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *DataTimeRange) GetFromAsMsEpoch() int64 {
|
|
||||||
return tr.MustGetFrom().UnixNano() / int64(time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *DataTimeRange) GetFromAsSecondsEpoch() int64 {
|
|
||||||
return tr.GetFromAsMsEpoch() / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *DataTimeRange) GetFromAsTimeUTC() time.Time {
|
|
||||||
return tr.MustGetFrom().UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *DataTimeRange) GetToAsMsEpoch() int64 {
|
|
||||||
return tr.MustGetTo().UnixNano() / int64(time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *DataTimeRange) GetToAsSecondsEpoch() int64 {
|
|
||||||
return tr.GetToAsMsEpoch() / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *DataTimeRange) GetToAsTimeUTC() time.Time {
|
|
||||||
return tr.MustGetTo().UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *DataTimeRange) MustGetFrom() time.Time {
|
|
||||||
res, err := tr.ParseFrom()
|
|
||||||
if err != nil {
|
|
||||||
return time.Unix(0, 0)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *DataTimeRange) MustGetTo() time.Time {
|
|
||||||
res, err := tr.ParseTo()
|
|
||||||
if err != nil {
|
|
||||||
return time.Unix(0, 0)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr DataTimeRange) ParseFrom() (time.Time, error) {
|
|
||||||
return parseTimeRange(tr.From, tr.Now, false, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr DataTimeRange) ParseTo() (time.Time, error) {
|
|
||||||
return parseTimeRange(tr.To, tr.Now, true, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr DataTimeRange) ParseFromWithLocation(location *time.Location) (time.Time, error) {
|
|
||||||
return parseTimeRange(tr.From, tr.Now, false, location)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr DataTimeRange) ParseToWithLocation(location *time.Location) (time.Time, error) {
|
|
||||||
return parseTimeRange(tr.To, tr.Now, true, location)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseTimeRange(s string, now time.Time, withRoundUp bool, location *time.Location) (time.Time, error) {
|
|
||||||
if val, err := strconv.ParseInt(s, 10, 64); err == nil {
|
|
||||||
seconds := val / 1000
|
|
||||||
nano := (val - seconds*1000) * 1000000
|
|
||||||
return time.Unix(seconds, nano), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
diff, err := time.ParseDuration("-" + s)
|
|
||||||
if err != nil {
|
|
||||||
options := []func(*datemath.Options){
|
|
||||||
datemath.WithNow(now),
|
|
||||||
datemath.WithRoundUp(withRoundUp),
|
|
||||||
}
|
|
||||||
if location != nil {
|
|
||||||
options = append(options, datemath.WithLocation(location))
|
|
||||||
}
|
|
||||||
|
|
||||||
return datemath.ParseAndEvaluate(s, options...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return now.Add(diff), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SeriesToFrame converts a DataTimeSeries to an SDK frame.
|
|
||||||
func SeriesToFrame(series DataTimeSeries) (*data.Frame, error) {
|
|
||||||
timeVec := make([]*time.Time, len(series.Points))
|
|
||||||
floatVec := make([]*float64, len(series.Points))
|
|
||||||
for idx, point := range series.Points {
|
|
||||||
timeVec[idx], floatVec[idx] = convertDataTimePoint(point)
|
|
||||||
}
|
|
||||||
frame := data.NewFrame(series.Name,
|
|
||||||
data.NewField("time", nil, timeVec),
|
|
||||||
data.NewField("value", data.Labels(series.Tags), floatVec),
|
|
||||||
)
|
|
||||||
|
|
||||||
return frame, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// convertDataTimePoint converts a DataTimePoint into two values appropriate
|
|
||||||
// for Series values.
|
|
||||||
func convertDataTimePoint(point DataTimePoint) (t *time.Time, f *float64) {
|
|
||||||
timeIdx, valueIdx := 1, 0
|
|
||||||
if point[timeIdx].Valid { // Assuming valid is null?
|
|
||||||
tI := int64(point[timeIdx].Float64)
|
|
||||||
uT := time.Unix(tI/int64(1e+3), (tI%int64(1e+3))*int64(1e+6)) // time.Time from millisecond unix ts
|
|
||||||
t = &uT
|
|
||||||
}
|
|
||||||
if point[valueIdx].Valid {
|
|
||||||
f = &point[valueIdx].Float64
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package plugins
|
package legacydata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
3
pkg/tsdb/legacydata/doc.go
Normal file
3
pkg/tsdb/legacydata/doc.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Package legacydata contains old/legacy interfaces/contracts that uses a data format of series/tables.
|
||||||
|
// Deprecated: use github.com/grafana/grafana-plugin-sdk-go/backend instead.
|
||||||
|
package legacydata
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -29,8 +29,8 @@ type intervalCalculator struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Calculator interface {
|
type Calculator interface {
|
||||||
Calculate(timeRange plugins.DataTimeRange, interval time.Duration) Interval
|
Calculate(timeRange legacydata.DataTimeRange, interval time.Duration) Interval
|
||||||
CalculateSafeInterval(timeRange plugins.DataTimeRange, resolution int64) Interval
|
CalculateSafeInterval(timeRange legacydata.DataTimeRange, resolution int64) Interval
|
||||||
}
|
}
|
||||||
|
|
||||||
type CalculatorOptions struct {
|
type CalculatorOptions struct {
|
||||||
@@ -55,7 +55,7 @@ func (i *Interval) Milliseconds() int64 {
|
|||||||
return i.Value.Nanoseconds() / int64(time.Millisecond)
|
return i.Value.Nanoseconds() / int64(time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *intervalCalculator) Calculate(timerange plugins.DataTimeRange, minInterval time.Duration) Interval {
|
func (ic *intervalCalculator) Calculate(timerange legacydata.DataTimeRange, minInterval time.Duration) Interval {
|
||||||
to := timerange.MustGetTo().UnixNano()
|
to := timerange.MustGetTo().UnixNano()
|
||||||
from := timerange.MustGetFrom().UnixNano()
|
from := timerange.MustGetFrom().UnixNano()
|
||||||
calculatedInterval := time.Duration((to - from) / DefaultRes)
|
calculatedInterval := time.Duration((to - from) / DefaultRes)
|
||||||
@@ -68,7 +68,7 @@ func (ic *intervalCalculator) Calculate(timerange plugins.DataTimeRange, minInte
|
|||||||
return Interval{Text: FormatDuration(rounded), Value: rounded}
|
return Interval{Text: FormatDuration(rounded), Value: rounded}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *intervalCalculator) CalculateSafeInterval(timerange plugins.DataTimeRange, safeRes int64) Interval {
|
func (ic *intervalCalculator) CalculateSafeInterval(timerange legacydata.DataTimeRange, safeRes int64) Interval {
|
||||||
to := timerange.MustGetTo().UnixNano()
|
to := timerange.MustGetTo().UnixNano()
|
||||||
from := timerange.MustGetFrom().UnixNano()
|
from := timerange.MustGetFrom().UnixNano()
|
||||||
safeInterval := time.Duration((to - from) / safeRes)
|
safeInterval := time.Duration((to - from) / safeRes)
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -16,13 +16,13 @@ func TestIntervalCalculator_Calculate(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
timeRange plugins.DataTimeRange
|
timeRange legacydata.DataTimeRange
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{"from 5m to now", plugins.NewDataTimeRange("5m", "now"), "200ms"},
|
{"from 5m to now", legacydata.NewDataTimeRange("5m", "now"), "200ms"},
|
||||||
{"from 15m to now", plugins.NewDataTimeRange("15m", "now"), "500ms"},
|
{"from 15m to now", legacydata.NewDataTimeRange("15m", "now"), "500ms"},
|
||||||
{"from 30m to now", plugins.NewDataTimeRange("30m", "now"), "1s"},
|
{"from 30m to now", legacydata.NewDataTimeRange("30m", "now"), "1s"},
|
||||||
{"from 1h to now", plugins.NewDataTimeRange("1h", "now"), "2s"},
|
{"from 1h to now", legacydata.NewDataTimeRange("1h", "now"), "2s"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@@ -38,14 +38,14 @@ func TestIntervalCalculator_CalculateSafeInterval(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
timeRange plugins.DataTimeRange
|
timeRange legacydata.DataTimeRange
|
||||||
safeResolution int64
|
safeResolution int64
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{"from 5m to now", plugins.NewDataTimeRange("5m", "now"), 11000, "20ms"},
|
{"from 5m to now", legacydata.NewDataTimeRange("5m", "now"), 11000, "20ms"},
|
||||||
{"from 15m to now", plugins.NewDataTimeRange("15m", "now"), 11000, "100ms"},
|
{"from 15m to now", legacydata.NewDataTimeRange("15m", "now"), 11000, "100ms"},
|
||||||
{"from 30m to now", plugins.NewDataTimeRange("30m", "now"), 11000, "200ms"},
|
{"from 30m to now", legacydata.NewDataTimeRange("30m", "now"), 11000, "200ms"},
|
||||||
{"from 24h to now", plugins.NewDataTimeRange("24h", "now"), 11000, "10s"},
|
{"from 24h to now", legacydata.NewDataTimeRange("24h", "now"), 11000, "10s"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
128
pkg/tsdb/legacydata/service/service.go
Normal file
128
pkg/tsdb/legacydata/service/service.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/adapters"
|
||||||
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
|
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
|
)
|
||||||
|
|
||||||
|
var oAuthIsOAuthPassThruEnabledFunc = func(oAuthTokenService oauthtoken.OAuthTokenService, ds *models.DataSource) bool {
|
||||||
|
return oAuthTokenService.IsOAuthPassThruEnabled(ds)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
pluginsClient plugins.Client
|
||||||
|
oAuthTokenService oauthtoken.OAuthTokenService
|
||||||
|
dataSourcesService *datasources.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideService(pluginsClient plugins.Client, oAuthTokenService oauthtoken.OAuthTokenService, dataSourcesService *datasources.Service) *Service {
|
||||||
|
return &Service{
|
||||||
|
pluginsClient: pluginsClient,
|
||||||
|
oAuthTokenService: oAuthTokenService,
|
||||||
|
dataSourcesService: dataSourcesService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint: staticcheck // legacydata.DataResponse deprecated
|
||||||
|
func (h *Service) HandleRequest(ctx context.Context, ds *models.DataSource, query legacydata.DataQuery) (legacydata.DataResponse, error) {
|
||||||
|
jsonDataBytes, err := ds.JsonData.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return legacydata.DataResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceSettings := &backend.DataSourceInstanceSettings{
|
||||||
|
ID: ds.Id,
|
||||||
|
Name: ds.Name,
|
||||||
|
URL: ds.Url,
|
||||||
|
Database: ds.Database,
|
||||||
|
User: ds.User,
|
||||||
|
BasicAuthEnabled: ds.BasicAuth,
|
||||||
|
BasicAuthUser: ds.BasicAuthUser,
|
||||||
|
JSONData: jsonDataBytes,
|
||||||
|
DecryptedSecureJSONData: h.dataSourcesService.DecryptedValues(ds),
|
||||||
|
Updated: ds.Updated,
|
||||||
|
UID: ds.Uid,
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.Headers == nil {
|
||||||
|
query.Headers = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if oAuthIsOAuthPassThruEnabledFunc(h.oAuthTokenService, ds) {
|
||||||
|
if token := h.oAuthTokenService.GetCurrentOAuthToken(ctx, query.User); token != nil {
|
||||||
|
delete(query.Headers, "Authorization")
|
||||||
|
query.Headers["Authorization"] = fmt.Sprintf("%s %s", token.Type(), token.AccessToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &backend.QueryDataRequest{
|
||||||
|
PluginContext: backend.PluginContext{
|
||||||
|
OrgID: ds.OrgId,
|
||||||
|
PluginID: ds.Type,
|
||||||
|
User: adapters.BackendUserFromSignedInUser(query.User),
|
||||||
|
DataSourceInstanceSettings: instanceSettings,
|
||||||
|
},
|
||||||
|
Queries: []backend.DataQuery{},
|
||||||
|
Headers: query.Headers,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, q := range query.Queries {
|
||||||
|
modelJSON, err := q.Model.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return legacydata.DataResponse{}, err
|
||||||
|
}
|
||||||
|
req.Queries = append(req.Queries, backend.DataQuery{
|
||||||
|
RefID: q.RefID,
|
||||||
|
Interval: time.Duration(q.IntervalMS) * time.Millisecond,
|
||||||
|
MaxDataPoints: q.MaxDataPoints,
|
||||||
|
TimeRange: backend.TimeRange{
|
||||||
|
From: query.TimeRange.GetFromAsTimeUTC(),
|
||||||
|
To: query.TimeRange.GetToAsTimeUTC(),
|
||||||
|
},
|
||||||
|
QueryType: q.QueryType,
|
||||||
|
JSON: modelJSON,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := h.pluginsClient.QueryData(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return legacydata.DataResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tR := legacydata.DataResponse{
|
||||||
|
Results: make(map[string]legacydata.DataQueryResult, len(resp.Responses)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for refID, r := range resp.Responses {
|
||||||
|
qr := legacydata.DataQueryResult{
|
||||||
|
RefID: refID,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range r.Frames {
|
||||||
|
if f.RefID == "" {
|
||||||
|
f.RefID = refID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qr.Dataframes = legacydata.NewDecodedDataFrames(r.Frames)
|
||||||
|
|
||||||
|
if r.Error != nil {
|
||||||
|
qr.Error = r.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
tR.Results[refID] = qr
|
||||||
|
}
|
||||||
|
|
||||||
|
return tR, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ legacydata.RequestHandler = &Service{}
|
||||||
67
pkg/tsdb/legacydata/service/service_test.go
Normal file
67
pkg/tsdb/legacydata/service/service_test.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
|
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
||||||
|
"github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||||
|
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleRequest(t *testing.T) {
|
||||||
|
t.Run("Should invoke plugin manager QueryData when handling request for query", func(t *testing.T) {
|
||||||
|
origOAuthIsOAuthPassThruEnabledFunc := oAuthIsOAuthPassThruEnabledFunc
|
||||||
|
oAuthIsOAuthPassThruEnabledFunc = func(oAuthTokenService oauthtoken.OAuthTokenService, ds *models.DataSource) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
oAuthIsOAuthPassThruEnabledFunc = origOAuthIsOAuthPassThruEnabledFunc
|
||||||
|
})
|
||||||
|
|
||||||
|
client := &fakePluginsClient{}
|
||||||
|
var actualReq *backend.QueryDataRequest
|
||||||
|
client.QueryDataHandlerFunc = func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||||
|
actualReq = req
|
||||||
|
return backend.NewQueryDataResponse(), nil
|
||||||
|
}
|
||||||
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
|
dsService := datasources.ProvideService(bus.New(), nil, secretsService)
|
||||||
|
s := ProvideService(client, nil, dsService)
|
||||||
|
|
||||||
|
ds := &models.DataSource{Id: 12, Type: "unregisteredType", JsonData: simplejson.New()}
|
||||||
|
req := legacydata.DataQuery{
|
||||||
|
TimeRange: &legacydata.DataTimeRange{},
|
||||||
|
Queries: []legacydata.DataSubQuery{
|
||||||
|
{RefID: "A", DataSource: &models.DataSource{Id: 1, Type: "test"}, Model: simplejson.New()},
|
||||||
|
{RefID: "B", DataSource: &models.DataSource{Id: 1, Type: "test"}, Model: simplejson.New()},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res, err := s.HandleRequest(context.Background(), ds, req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, actualReq)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type fakePluginsClient struct {
|
||||||
|
plugins.Client
|
||||||
|
backend.QueryDataHandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *fakePluginsClient) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||||
|
if m.QueryDataHandlerFunc != nil {
|
||||||
|
return m.QueryDataHandlerFunc.QueryData(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
119
pkg/tsdb/legacydata/time_range.go
Normal file
119
pkg/tsdb/legacydata/time_range.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package legacydata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/timberio/go-datemath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DataTimeRange struct {
|
||||||
|
From string
|
||||||
|
To string
|
||||||
|
Now time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDataTimeRange(from, to string) DataTimeRange {
|
||||||
|
return DataTimeRange{
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
Now: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *DataTimeRange) GetFromAsMsEpoch() int64 {
|
||||||
|
return tr.MustGetFrom().UnixNano() / int64(time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *DataTimeRange) GetFromAsSecondsEpoch() int64 {
|
||||||
|
return tr.GetFromAsMsEpoch() / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *DataTimeRange) GetFromAsTimeUTC() time.Time {
|
||||||
|
return tr.MustGetFrom().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *DataTimeRange) GetToAsMsEpoch() int64 {
|
||||||
|
return tr.MustGetTo().UnixNano() / int64(time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *DataTimeRange) GetToAsSecondsEpoch() int64 {
|
||||||
|
return tr.GetToAsMsEpoch() / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *DataTimeRange) GetToAsTimeUTC() time.Time {
|
||||||
|
return tr.MustGetTo().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *DataTimeRange) MustGetFrom() time.Time {
|
||||||
|
res, err := tr.ParseFrom()
|
||||||
|
if err != nil {
|
||||||
|
return time.Unix(0, 0)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *DataTimeRange) MustGetTo() time.Time {
|
||||||
|
res, err := tr.ParseTo()
|
||||||
|
if err != nil {
|
||||||
|
return time.Unix(0, 0)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr DataTimeRange) ParseFrom() (time.Time, error) {
|
||||||
|
return parseTimeRange(tr.From, tr.Now, false, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr DataTimeRange) ParseTo() (time.Time, error) {
|
||||||
|
return parseTimeRange(tr.To, tr.Now, true, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr DataTimeRange) ParseFromWithLocation(location *time.Location) (time.Time, error) {
|
||||||
|
return parseTimeRange(tr.From, tr.Now, false, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr DataTimeRange) ParseToWithLocation(location *time.Location) (time.Time, error) {
|
||||||
|
return parseTimeRange(tr.To, tr.Now, true, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr DataTimeRange) ParseFromWithWeekStart(location *time.Location, weekstart time.Weekday) (time.Time, error) {
|
||||||
|
return parseTimeRangeWithWeekStart(tr.From, tr.Now, false, location, weekstart)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *DataTimeRange) ParseToWithWeekStart(location *time.Location, weekstart time.Weekday) (time.Time, error) {
|
||||||
|
return parseTimeRangeWithWeekStart(tr.To, tr.Now, true, location, weekstart)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTimeRange(s string, now time.Time, withRoundUp bool, location *time.Location) (time.Time, error) {
|
||||||
|
return parseTimeRangeWithWeekStart(s, now, withRoundUp, location, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTimeRangeWithWeekStart(s string, now time.Time, withRoundUp bool, location *time.Location, weekstart time.Weekday) (time.Time, error) {
|
||||||
|
if val, err := strconv.ParseInt(s, 10, 64); err == nil {
|
||||||
|
seconds := val / 1000
|
||||||
|
nano := (val - seconds*1000) * 1000000
|
||||||
|
return time.Unix(seconds, nano), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
diff, err := time.ParseDuration("-" + s)
|
||||||
|
if err != nil {
|
||||||
|
options := []func(*datemath.Options){
|
||||||
|
datemath.WithNow(now),
|
||||||
|
datemath.WithRoundUp(withRoundUp),
|
||||||
|
}
|
||||||
|
if location != nil {
|
||||||
|
options = append(options, datemath.WithLocation(location))
|
||||||
|
}
|
||||||
|
if weekstart != -1 {
|
||||||
|
if weekstart > now.Weekday() {
|
||||||
|
weekstart = weekstart - 7
|
||||||
|
}
|
||||||
|
options = append(options, datemath.WithStartOfWeek(weekstart))
|
||||||
|
}
|
||||||
|
|
||||||
|
return datemath.ParseAndEvaluate(s, options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return now.Add(diff), nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package tsdb
|
package legacydata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -13,10 +13,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
t.Run("Can parse 5m, now", func(t *testing.T) {
|
t.Run("Can parse 5m, now", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "5m",
|
From: "5m",
|
||||||
To: "now",
|
To: "now",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("5m ago ", func(t *testing.T) {
|
t.Run("5m ago ", func(t *testing.T) {
|
||||||
@@ -37,10 +37,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Can parse 5h, now-10m", func(t *testing.T) {
|
t.Run("Can parse 5h, now-10m", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "5h",
|
From: "5h",
|
||||||
To: "now-10m",
|
To: "now-10m",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("5h ago ", func(t *testing.T) {
|
t.Run("5h ago ", func(t *testing.T) {
|
||||||
@@ -66,10 +66,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
now, err := time.Parse(time.RFC3339Nano, "2020-03-26T15:12:56.000Z")
|
now, err := time.Parse(time.RFC3339Nano, "2020-03-26T15:12:56.000Z")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
t.Run("Can parse now-1M/M, now-1M/M", func(t *testing.T) {
|
t.Run("Can parse now-1M/M, now-1M/M", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "now-1M/M",
|
From: "now-1M/M",
|
||||||
To: "now-1M/M",
|
To: "now-1M/M",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("from now-1M/M ", func(t *testing.T) {
|
t.Run("from now-1M/M ", func(t *testing.T) {
|
||||||
@@ -92,10 +92,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Can parse now-3d, now+3w", func(t *testing.T) {
|
t.Run("Can parse now-3d, now+3w", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "now-3d",
|
From: "now-3d",
|
||||||
To: "now+3w",
|
To: "now+3w",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("now-3d ", func(t *testing.T) {
|
t.Run("now-3d ", func(t *testing.T) {
|
||||||
@@ -118,10 +118,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Can parse 1960-02-01T07:00:00.000Z, 1965-02-03T08:00:00.000Z", func(t *testing.T) {
|
t.Run("Can parse 1960-02-01T07:00:00.000Z, 1965-02-03T08:00:00.000Z", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "1960-02-01T07:00:00.000Z",
|
From: "1960-02-01T07:00:00.000Z",
|
||||||
To: "1965-02-03T08:00:00.000Z",
|
To: "1965-02-03T08:00:00.000Z",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("1960-02-01T07:00:00.000Z ", func(t *testing.T) {
|
t.Run("1960-02-01T07:00:00.000Z ", func(t *testing.T) {
|
||||||
@@ -146,7 +146,7 @@ func TestTimeRange(t *testing.T) {
|
|||||||
t.Run("Can parse negative unix epochs", func(t *testing.T) {
|
t.Run("Can parse negative unix epochs", func(t *testing.T) {
|
||||||
from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC)
|
from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC)
|
||||||
to := time.Date(1965, 2, 3, 8, 0, 0, 0, time.UTC)
|
to := time.Date(1965, 2, 3, 8, 0, 0, 0, time.UTC)
|
||||||
tr := NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10))
|
tr := NewDataTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10))
|
||||||
|
|
||||||
res, err := tr.ParseFrom()
|
res, err := tr.ParseFrom()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -159,10 +159,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("can parse unix epochs", func(t *testing.T) {
|
t.Run("can parse unix epochs", func(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "1474973725473",
|
From: "1474973725473",
|
||||||
To: "1474975757930",
|
To: "1474975757930",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := tr.ParseFrom()
|
res, err := tr.ParseFrom()
|
||||||
@@ -176,10 +176,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Cannot parse asdf", func(t *testing.T) {
|
t.Run("Cannot parse asdf", func(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "asdf",
|
From: "asdf",
|
||||||
To: "asdf",
|
To: "asdf",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tr.ParseFrom()
|
_, err = tr.ParseFrom()
|
||||||
@@ -193,10 +193,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
t.Run("Can parse now-1M/M, now-1M/M with America/Chicago timezone", func(t *testing.T) {
|
t.Run("Can parse now-1M/M, now-1M/M with America/Chicago timezone", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "now-1M/M",
|
From: "now-1M/M",
|
||||||
To: "now-1M/M",
|
To: "now-1M/M",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
location, err := time.LoadLocation("America/Chicago")
|
location, err := time.LoadLocation("America/Chicago")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -221,10 +221,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Can parse now-3h, now+2h with America/Chicago timezone", func(t *testing.T) {
|
t.Run("Can parse now-3h, now+2h with America/Chicago timezone", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "now-3h",
|
From: "now-3h",
|
||||||
To: "now+2h",
|
To: "now+2h",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
location, err := time.LoadLocation("America/Chicago")
|
location, err := time.LoadLocation("America/Chicago")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -249,10 +249,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Can parse now-1w/w, now-1w/w without timezone and week start on Monday", func(t *testing.T) {
|
t.Run("Can parse now-1w/w, now-1w/w without timezone and week start on Monday", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "now-1w/w",
|
From: "now-1w/w",
|
||||||
To: "now-1w/w",
|
To: "now-1w/w",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
weekstart := time.Monday
|
weekstart := time.Monday
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -277,10 +277,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Can parse now-1w/w, now-1w/w with America/Chicago timezone and week start on Monday", func(t *testing.T) {
|
t.Run("Can parse now-1w/w, now-1w/w with America/Chicago timezone and week start on Monday", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "now-1w/w",
|
From: "now-1w/w",
|
||||||
To: "now-1w/w",
|
To: "now-1w/w",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
weekstart := time.Monday
|
weekstart := time.Monday
|
||||||
location, err := time.LoadLocation("America/Chicago")
|
location, err := time.LoadLocation("America/Chicago")
|
||||||
@@ -306,10 +306,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Can parse now-1w/w, now-1w/w with America/Chicago timezone and week start on Sunday", func(t *testing.T) {
|
t.Run("Can parse now-1w/w, now-1w/w with America/Chicago timezone and week start on Sunday", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "now-1w/w",
|
From: "now-1w/w",
|
||||||
To: "now-1w/w",
|
To: "now-1w/w",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
weekstart := time.Sunday
|
weekstart := time.Sunday
|
||||||
location, err := time.LoadLocation("America/Chicago")
|
location, err := time.LoadLocation("America/Chicago")
|
||||||
@@ -335,10 +335,10 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Can parse now-1w/w, now-1w/w with America/Chicago timezone and week start on Saturday", func(t *testing.T) {
|
t.Run("Can parse now-1w/w, now-1w/w with America/Chicago timezone and week start on Saturday", func(t *testing.T) {
|
||||||
tr := TimeRange{
|
tr := DataTimeRange{
|
||||||
From: "now-1w/w",
|
From: "now-1w/w",
|
||||||
To: "now-1w/w",
|
To: "now-1w/w",
|
||||||
now: now,
|
Now: now,
|
||||||
}
|
}
|
||||||
weekstart := time.Saturday
|
weekstart := time.Saturday
|
||||||
location, err := time.LoadLocation("America/Chicago")
|
location, err := time.LoadLocation("America/Chicago")
|
||||||
37
pkg/tsdb/legacydata/utils.go
Normal file
37
pkg/tsdb/legacydata/utils.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package legacydata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SeriesToFrame converts a DataTimeSeries to an SDK frame.
|
||||||
|
func SeriesToFrame(series DataTimeSeries) (*data.Frame, error) {
|
||||||
|
timeVec := make([]*time.Time, len(series.Points))
|
||||||
|
floatVec := make([]*float64, len(series.Points))
|
||||||
|
for idx, point := range series.Points {
|
||||||
|
timeVec[idx], floatVec[idx] = convertDataTimePoint(point)
|
||||||
|
}
|
||||||
|
frame := data.NewFrame(series.Name,
|
||||||
|
data.NewField("time", nil, timeVec),
|
||||||
|
data.NewField("value", data.Labels(series.Tags), floatVec),
|
||||||
|
)
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertDataTimePoint converts a DataTimePoint into two values appropriate
|
||||||
|
// for Series values.
|
||||||
|
func convertDataTimePoint(point DataTimePoint) (t *time.Time, f *float64) {
|
||||||
|
timeIdx, valueIdx := 1, 0
|
||||||
|
if point[timeIdx].Valid { // Assuming valid is null?
|
||||||
|
tI := int64(point[timeIdx].Float64)
|
||||||
|
uT := time.Unix(tI/int64(1e+3), (tI%int64(1e+3))*int64(1e+6)) // time.Time from millisecond unix ts
|
||||||
|
t = &uT
|
||||||
|
}
|
||||||
|
if point[valueIdx].Valid {
|
||||||
|
f = &point[valueIdx].Float64
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package tsdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
|
||||||
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
_ "github.com/grafana/grafana/pkg/tsdb/postgres"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewService returns a new Service.
|
|
||||||
func NewService(
|
|
||||||
cfg *setting.Cfg, pluginsClient plugins.Client, oauthTokenService *oauthtoken.Service,
|
|
||||||
dataSourcesService *datasources.Service) *Service {
|
|
||||||
return newService(cfg, pluginsClient, oauthTokenService, dataSourcesService)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newService(cfg *setting.Cfg, pluginsClient plugins.Client, oauthTokenService oauthtoken.OAuthTokenService,
|
|
||||||
dataSourcesService *datasources.Service) *Service {
|
|
||||||
return &Service{
|
|
||||||
Cfg: cfg,
|
|
||||||
pluginsClient: pluginsClient,
|
|
||||||
OAuthTokenService: oauthTokenService,
|
|
||||||
DataSourcesService: dataSourcesService,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service handles data requests to data sources.
|
|
||||||
type Service struct {
|
|
||||||
Cfg *setting.Cfg
|
|
||||||
pluginsClient plugins.Client
|
|
||||||
OAuthTokenService oauthtoken.OAuthTokenService
|
|
||||||
DataSourcesService *datasources.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
|
||||||
func (s *Service) HandleRequest(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (plugins.DataResponse, error) {
|
|
||||||
return dataPluginQueryAdapter(ds.Type, s.pluginsClient, s.OAuthTokenService, s.DataSourcesService).DataQuery(ctx, ds, query)
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
package tsdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
|
||||||
"github.com/grafana/grafana/pkg/services/secrets/fakes"
|
|
||||||
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHandleRequest(t *testing.T) {
|
|
||||||
t.Run("Should invoke plugin manager QueryData when handling request for query", func(t *testing.T) {
|
|
||||||
svc, _, pm := createService(t)
|
|
||||||
backendPluginManagerCalled := false
|
|
||||||
pm.QueryDataHandlerFunc = func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
|
||||||
backendPluginManagerCalled = true
|
|
||||||
return backend.NewQueryDataResponse(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ds := &models.DataSource{Id: 12, Type: "unregisteredType", JsonData: simplejson.New()}
|
|
||||||
req := plugins.DataQuery{
|
|
||||||
TimeRange: &plugins.DataTimeRange{},
|
|
||||||
Queries: []plugins.DataSubQuery{
|
|
||||||
{RefID: "A", DataSource: &models.DataSource{Id: 1, Type: "test"}, Model: simplejson.New()},
|
|
||||||
{RefID: "B", DataSource: &models.DataSource{Id: 1, Type: "test"}, Model: simplejson.New()},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, err := svc.HandleRequest(context.Background(), ds, req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, backendPluginManagerCalled)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
|
||||||
type resultsFn func(context plugins.DataQuery) plugins.DataQueryResult
|
|
||||||
|
|
||||||
type fakeExecutor struct {
|
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
|
||||||
results map[string]plugins.DataQueryResult
|
|
||||||
resultsFn map[string]resultsFn
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
|
||||||
func (e *fakeExecutor) DataQuery(ctx context.Context, dsInfo *models.DataSource, context plugins.DataQuery) (
|
|
||||||
plugins.DataResponse, error) {
|
|
||||||
result := plugins.DataResponse{Results: make(map[string]plugins.DataQueryResult)}
|
|
||||||
for _, query := range context.Queries {
|
|
||||||
if results, has := e.results[query.RefID]; has {
|
|
||||||
result.Results[query.RefID] = results
|
|
||||||
}
|
|
||||||
if testFunc, has := e.resultsFn[query.RefID]; has {
|
|
||||||
result.Results[query.RefID] = testFunc(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *fakeExecutor) Return(refID string, series plugins.DataTimeSeriesSlice) {
|
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
|
||||||
e.results[refID] = plugins.DataQueryResult{
|
|
||||||
RefID: refID, Series: series,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *fakeExecutor) HandleQuery(refId string, fn resultsFn) {
|
|
||||||
e.resultsFn[refId] = fn
|
|
||||||
}
|
|
||||||
|
|
||||||
type fakePluginsClient struct {
|
|
||||||
plugins.Client
|
|
||||||
backend.QueryDataHandlerFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *fakePluginsClient) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
|
||||||
if m.QueryDataHandlerFunc != nil {
|
|
||||||
return m.QueryDataHandlerFunc.QueryData(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type fakeOAuthTokenService struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *fakeOAuthTokenService) GetCurrentOAuthToken(context.Context, *models.SignedInUser) *oauth2.Token {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *fakeOAuthTokenService) IsOAuthPassThruEnabled(*models.DataSource) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func createService(t *testing.T) (*Service, *fakeExecutor, *fakePluginsClient) {
|
|
||||||
fakePluginsClient := &fakePluginsClient{}
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
|
||||||
dsService := datasources.ProvideService(bus.New(), nil, secretsService)
|
|
||||||
|
|
||||||
s := newService(
|
|
||||||
setting.NewCfg(),
|
|
||||||
fakePluginsClient,
|
|
||||||
&fakeOAuthTokenService{},
|
|
||||||
dsService,
|
|
||||||
)
|
|
||||||
e := &fakeExecutor{
|
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
|
||||||
results: make(map[string]plugins.DataQueryResult),
|
|
||||||
resultsFn: make(map[string]resultsFn),
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, e, fakePluginsClient
|
|
||||||
}
|
|
||||||
@@ -382,7 +382,6 @@ var Interpolate = func(query backend.DataQuery, timeRange backend.TimeRange, tim
|
|||||||
return sql, nil
|
return sql, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
|
||||||
func (e *DataSourceHandler) newProcessCfg(query backend.DataQuery, queryContext context.Context,
|
func (e *DataSourceHandler) newProcessCfg(query backend.DataQuery, queryContext context.Context,
|
||||||
rows *core.Rows, interpolatedQuery string) (*dataQueryModel, error) {
|
rows *core.Rows, interpolatedQuery string) (*dataQueryModel, error) {
|
||||||
columnNames, err := rows.Columns()
|
columnNames, err := rows.Columns()
|
||||||
@@ -425,7 +424,6 @@ func (e *DataSourceHandler) newProcessCfg(query backend.DataQuery, queryContext
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
|
||||||
|
|
||||||
qm.TimeRange.From = query.TimeRange.From.UTC()
|
qm.TimeRange.From = query.TimeRange.From.UTC()
|
||||||
qm.TimeRange.To = query.TimeRange.To.UTC()
|
qm.TimeRange.To = query.TimeRange.To.UTC()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
|
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/xorcare/pointer"
|
"github.com/xorcare/pointer"
|
||||||
@@ -420,7 +420,7 @@ type testQueryResultTransformer struct {
|
|||||||
transformQueryErrorWasCalled bool
|
transformQueryErrorWasCalled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testQueryResultTransformer) TransformQueryResult(columnTypes []*sql.ColumnType, rows *core.Rows) (plugins.DataRowValues, error) {
|
func (t *testQueryResultTransformer) TransformQueryResult(columnTypes []*sql.ColumnType, rows *core.Rows) (legacydata.DataRowValues, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -19,7 +19,7 @@ func TestTestdataScenarios(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("random walk ", func(t *testing.T) {
|
t.Run("random walk ", func(t *testing.T) {
|
||||||
t.Run("Should start at the requested value", func(t *testing.T) {
|
t.Run("Should start at the requested value", func(t *testing.T) {
|
||||||
timeRange := plugins.DataTimeRange{From: "5m", To: "now", Now: time.Now()}
|
timeRange := legacydata.DataTimeRange{From: "5m", To: "now", Now: time.Now()}
|
||||||
|
|
||||||
model := simplejson.New()
|
model := simplejson.New()
|
||||||
model.Set("startValue", 1.234)
|
model.Set("startValue", 1.234)
|
||||||
@@ -63,7 +63,7 @@ func TestTestdataScenarios(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("random walk table", func(t *testing.T) {
|
t.Run("random walk table", func(t *testing.T) {
|
||||||
t.Run("Should return a table that looks like value/min/max", func(t *testing.T) {
|
t.Run("Should return a table that looks like value/min/max", func(t *testing.T) {
|
||||||
timeRange := plugins.DataTimeRange{From: "5m", To: "now", Now: time.Now()}
|
timeRange := legacydata.DataTimeRange{From: "5m", To: "now", Now: time.Now()}
|
||||||
|
|
||||||
model := simplejson.New()
|
model := simplejson.New()
|
||||||
modelBytes, err := model.MarshalJSON()
|
modelBytes, err := model.MarshalJSON()
|
||||||
@@ -117,7 +117,7 @@ func TestTestdataScenarios(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should return a table with some nil values", func(t *testing.T) {
|
t.Run("Should return a table with some nil values", func(t *testing.T) {
|
||||||
timeRange := plugins.DataTimeRange{From: "5m", To: "now", Now: time.Now()}
|
timeRange := legacydata.DataTimeRange{From: "5m", To: "now", Now: time.Now()}
|
||||||
|
|
||||||
model := simplejson.New()
|
model := simplejson.New()
|
||||||
model.Set("withNil", true)
|
model.Set("withNil", true)
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
package tsdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/timberio/go-datemath"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewTimeRange(from, to string) *TimeRange {
|
|
||||||
return &TimeRange{
|
|
||||||
From: from,
|
|
||||||
To: to,
|
|
||||||
now: time.Now(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFakeTimeRange(from, to string, now time.Time) *TimeRange {
|
|
||||||
return &TimeRange{
|
|
||||||
From: from,
|
|
||||||
To: to,
|
|
||||||
now: now,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TimeRange struct {
|
|
||||||
From string
|
|
||||||
To string
|
|
||||||
now time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) GetFromAsMsEpoch() int64 {
|
|
||||||
return tr.MustGetFrom().UnixNano() / int64(time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) GetFromAsSecondsEpoch() int64 {
|
|
||||||
return tr.GetFromAsMsEpoch() / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) GetFromAsTimeUTC() time.Time {
|
|
||||||
return tr.MustGetFrom().UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) GetToAsMsEpoch() int64 {
|
|
||||||
return tr.MustGetTo().UnixNano() / int64(time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) GetToAsSecondsEpoch() int64 {
|
|
||||||
return tr.GetToAsMsEpoch() / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) GetToAsTimeUTC() time.Time {
|
|
||||||
return tr.MustGetTo().UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) MustGetFrom() time.Time {
|
|
||||||
res, err := tr.ParseFrom()
|
|
||||||
if err != nil {
|
|
||||||
return time.Unix(0, 0)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) MustGetTo() time.Time {
|
|
||||||
res, err := tr.ParseTo()
|
|
||||||
if err != nil {
|
|
||||||
return time.Unix(0, 0)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryParseUnixMsEpoch(val string) (time.Time, bool) {
|
|
||||||
if val, err := strconv.ParseInt(val, 10, 64); err == nil {
|
|
||||||
seconds := val / 1000
|
|
||||||
nano := (val - seconds*1000) * 1000000
|
|
||||||
return time.Unix(seconds, nano), true
|
|
||||||
}
|
|
||||||
return time.Time{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) ParseFrom() (time.Time, error) {
|
|
||||||
return parse(tr.From, tr.now, false, nil, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) ParseTo() (time.Time, error) {
|
|
||||||
return parse(tr.To, tr.now, true, nil, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) ParseFromWithLocation(location *time.Location) (time.Time, error) {
|
|
||||||
return parse(tr.From, tr.now, false, location, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) ParseToWithLocation(location *time.Location) (time.Time, error) {
|
|
||||||
return parse(tr.To, tr.now, true, location, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) ParseFromWithWeekStart(location *time.Location, weekstart time.Weekday) (time.Time, error) {
|
|
||||||
return parse(tr.From, tr.now, false, location, weekstart)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TimeRange) ParseToWithWeekStart(location *time.Location, weekstart time.Weekday) (time.Time, error) {
|
|
||||||
return parse(tr.To, tr.now, true, location, weekstart)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(s string, now time.Time, withRoundUp bool, location *time.Location, weekstart time.Weekday) (time.Time, error) {
|
|
||||||
if res, ok := tryParseUnixMsEpoch(s); ok {
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
diff, err := time.ParseDuration("-" + s)
|
|
||||||
if err != nil {
|
|
||||||
options := []func(*datemath.Options){
|
|
||||||
datemath.WithNow(now),
|
|
||||||
datemath.WithRoundUp(withRoundUp),
|
|
||||||
}
|
|
||||||
if location != nil {
|
|
||||||
options = append(options, datemath.WithLocation(location))
|
|
||||||
}
|
|
||||||
if weekstart != -1 {
|
|
||||||
if weekstart > now.Weekday() {
|
|
||||||
weekstart = weekstart - 7
|
|
||||||
}
|
|
||||||
options = append(options, datemath.WithStartOfWeek(weekstart))
|
|
||||||
}
|
|
||||||
|
|
||||||
return datemath.ParseAndEvaluate(s, options...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return now.Add(diff), nil
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user