diff --git a/go.mod b/go.mod index 3f8a52d8ee99c..adcc462e1c614 100644 --- a/go.mod +++ b/go.mod @@ -287,6 +287,8 @@ require ( k8s.io/klog/v2 v2.90.1 // @grafana/grafana-app-platform-squad ) +require github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible + require ( cloud.google.com/go v0.110.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect @@ -444,7 +446,7 @@ require ( github.com/go-logr/logr v1.2.4 // @grafana/grafana-app-platform-squad github.com/go-logr/stdr v1.2.2 // indirect github.com/google/go-github v17.0.0+incompatible // @grafana/grafana-app-platform-squad - github.com/grafadruid/go-druid v0.0.6 + github.com/grafadruid/go-druid v0.0.7-0.20230607082331-983aa577d90a github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hmarr/codeowners v1.1.2 // @grafana/grafana-as-code github.com/imdario/mergo v0.3.13 // indirect diff --git a/go.sum b/go.sum index 716b9b327def6..69e62652f5364 100644 --- a/go.sum +++ b/go.sum @@ -625,6 +625,7 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -1758,8 +1759,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotestyourself/gotestyourself v1.3.0/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= -github.com/grafadruid/go-druid v0.0.6 h1:Nt9jQrhrtHi1BJICN9aDJgYDmBmc10pJYpQiuwAsxa4= -github.com/grafadruid/go-druid v0.0.6/go.mod h1:KY3a6MrVMKkXgMTwBS9Nrhm1E8OWyR4gd0WzUi8d/zM= +github.com/grafadruid/go-druid v0.0.7-0.20230607082331-983aa577d90a h1:+2Q5MlRJc7nxT5H4aPzWK9YyC6uO3bhiw2yElOz4Nfo= +github.com/grafadruid/go-druid v0.0.7-0.20230607082331-983aa577d90a/go.mod h1:KY3a6MrVMKkXgMTwBS9Nrhm1E8OWyR4gd0WzUi8d/zM= github.com/grafana/alerting v0.0.0-20230606080147-55b8d71c7890 h1:ubNIgVGX4PQ9YI1nWnt2mky3il8clWSjdo3NFSD26DQ= github.com/grafana/alerting v0.0.0-20230606080147-55b8d71c7890/go.mod h1:zEflOvMVchYhRbFb5ziXVR/JG67FOLBzQTjhHh9xaI4= github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= diff --git a/pkg/tsdb/druid/druid.go b/pkg/tsdb/druid/druid.go index 44344952809b0..538cf6657b93c 100644 --- a/pkg/tsdb/druid/druid.go +++ b/pkg/tsdb/druid/druid.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/Knetic/govaluate" "github.com/bitly/go-simplejson" "github.com/grafadruid/go-druid" druidquerybuilder "github.com/grafadruid/go-druid/builder" @@ -183,7 +184,10 @@ func (ds *Service) CallResource(ctx context.Context, req *backend.CallResourceRe default: body = "Path not supported" } - resp := &backend.CallResourceResponse{Status: code} + resp := &backend.CallResourceResponse{ + Headers: map[string][]string{}, + Status: code, + } resp.Body, err = json.Marshal(body) sender.Send(resp) return nil @@ -440,6 +444,9 @@ func (ds *Service) prepareQuery(qry []byte, s *druidInstanceSettings) (druidquer defaultQueryContext, ds.prepareQueryContext(queryContextParameters.([]interface{}))) } + if g, ok := q.Builder["granularity"].(map[string]any); ok { + q.Builder["granularity"] = resolveGranularity(g) + } jsonQuery, err := json.Marshal(q.Builder) if err != nil { return nil, nil, err @@ -449,6 +456,28 @@ func (ds *Service) prepareQuery(qry []byte, s *druidInstanceSettings) (druidquer return query, mergeSettings(s.defaultQuerySettings, q.Settings), err } +func resolveGranularity(m map[string]any) map[string]any { + // granularity is optional, so return early if not set and is of wrong type + if m == nil || m["type"] != "duration" { + return m + } + expr, ok := m["duration"].(string) + if !ok { + return m + } + + eval, err := govaluate.NewEvaluableExpression(expr) + if err != nil { + return m + } + result, err := eval.Evaluate(nil) + if err != nil { + return m + } + m["duration"] = result + return m +} + func (ds *Service) prepareQueryContext(parameters []interface{}) map[string]interface{} { ctx := make(map[string]interface{}) for _, parameter := range parameters { diff --git a/pkg/tsdb/druid/druid_test.go b/pkg/tsdb/druid/druid_test.go new file mode 100644 index 0000000000000..2b1b41a9956b0 --- /dev/null +++ b/pkg/tsdb/druid/druid_test.go @@ -0,0 +1,21 @@ +package druid + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResolveGranularity(t *testing.T) { + granularity := map[string]any{ + "type": "duration", + "duration": "10 * 10", + "origin": "2012-01-01T00:30:00Z", + } + gran := resolveGranularity(granularity) + assert.Equal(t, gran, map[string]any{ + "type": "duration", + "duration": 100, + "origin": "2012-01-01T00:30:00Z", + }) +}