Merge remote-tracking branch 'grafana/master' into alpha-text2

* grafana/master:
  Explore: Enable click on name label
  Bumping grafana ui version (#15669)
  Need this to be available for plugins
  docs: 6.0 whats new
  Updated latest.json with 6.0
  Update CHANGELOG.md
  docs: grafana 6.0 has been released.
  moves social package to /login
  moves tracing packge into /infra
  Update CHANGELOG.md
  changelog: adds notes for #14509 and #15179
  graph: fixes click after scroll in series override menu
  moves metric package to /infra
  stackdriver: change reducer mapping for distribution metrics
  stackdriver: fix for float64 bounds for distribution metrics
  Fixed value dropdown not updating when it's current value updates, fixes #15566
This commit is contained in:
ryan 2019-02-28 10:07:53 -08:00
commit d3cbc5638c
63 changed files with 685 additions and 250 deletions

View File

@ -1,4 +1,10 @@
# Unreleased # 6.0.0 stable (2019-02-25)
### Bug Fixes
* **Stackdriver**: fix for float64 bounds for distribution metrics [#14509](https://github.com/grafana/grafana/issues/14509)
* **Stackdriver**: no reducers available for distribution type [#15179](https://github.com/grafana/grafana/issues/15179)
* **Dashboard**: fixes click after scroll in series override menu [#15621](https://github.com/grafana/grafana/issues/15621)
* **MySQL**: fix mysql query using _interval_ms variable throws error [#14507](https://github.com/grafana/grafana/issues/14507)
# 6.0.0-beta3 (2019-02-19) # 6.0.0-beta3 (2019-02-19)

View File

@ -14,8 +14,6 @@ weight = -11
This update to Grafana introduces a new way of exploring your data, support for log data and tons of other features. This update to Grafana introduces a new way of exploring your data, support for log data and tons of other features.
Grafana v6.0 is out in **Beta**, [Download Now!](https://grafana.com/grafana/download/beta)
The main highlights are: The main highlights are:
- [Explore]({{< relref "#explore" >}}) - A new query focused workflow for ad-hoc data exploration and troubleshooting. - [Explore]({{< relref "#explore" >}}) - A new query focused workflow for ad-hoc data exploration and troubleshooting.
@ -107,9 +105,9 @@ continue to refine and start using in other panels.
### React Panels & Query Editors ### React Panels & Query Editors
A major part of all the work that has gone into Grafana v6.0 has been on the migration to React. This investment A major part of all the work that has gone into Grafana v6.0 has been on the migration to React. This investment
is part of the future proofing of Grafana and it's code base and ecosystem. Starting in v6.0 **Panels** and **Data is part of the future proofing of Grafana's code base and ecosystem. Starting in v6.0 **Panels** and **Data
source** plugins can be written in React using our published `@grafana/ui` sdk library. More information on this source** plugins can be written in React using our published `@grafana/ui` sdk library. More information on this
will be shared closer to or just after release. will be shared soon.
{{< docs-imagebox img="/img/docs/v60/react_panels.png" max-width="600px" caption="React Panel" >}} {{< docs-imagebox img="/img/docs/v60/react_panels.png" max-width="600px" caption="React Panel" >}}
<br /> <br />
@ -122,7 +120,7 @@ To get started read the guide: [Using Google Stackdriver in Grafana](/features/d
## Azure Monitor Datasource ## Azure Monitor Datasource
One of the goals of the Grafana v6.0 release is to add support for the three major clouds. Amazon Cloudwatch has been a core datasource for years and Google Stackdriver is also now supported. We developed an external plugin for Azure Monitor last year and for this release the [plugin](https://grafana.com/plugins/grafana-azure-monitor-datasource) is being moved into Grafana to be one of the built-in datasources. For users of the external plugin, Grafana will automatically start using the built-in version. As a core datasource, the Azure Monitor datasource will get alerting support for the official 6.0 release. One of the goals of the Grafana v6.0 release is to add support for the three major clouds. Amazon Cloudwatch has been a core datasource for years and Google Stackdriver is also now supported. We developed an external plugin for Azure Monitor last year and for this release the [plugin](https://grafana.com/plugins/grafana-azure-monitor-datasource) is being moved into Grafana to be one of the built-in datasources. For users of the external plugin, Grafana will automatically start using the built-in version. As a core datasource, the Azure Monitor datasource is able to get alerting support, in the 6.0 release alerting is supported for the Azure Monitor service, with the rest to follow.
The Azure Monitor datasource integrates four Azure services with Grafana - Azure Monitor, Azure Log Analytics, Azure Application Insights and Azure Application Insights Analytics. The Azure Monitor datasource integrates four Azure services with Grafana - Azure Monitor, Azure Log Analytics, Azure Application Insights and Azure Application Insights Analytics.

View File

@ -1,4 +1,4 @@
{ {
"stable": "5.4.3", "stable": "6.0.0",
"testing": "5.4.3" "testing": "6.0.0"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@grafana/ui", "name": "@grafana/ui",
"version": "6.0.0-alpha.0", "version": "6.0.1-alpha.0",
"description": "Grafana Components Library", "description": "Grafana Components Library",
"keywords": [ "keywords": [
"typescript", "typescript",

View File

@ -2,3 +2,4 @@ export * from './processTimeSeries';
export * from './valueFormats/valueFormats'; export * from './valueFormats/valueFormats';
export * from './colors'; export * from './colors';
export * from './namedColorsPalette'; export * from './namedColorsPalette';
export { getMappedValue } from './valueMappings';

View File

@ -3,7 +3,7 @@ package api
import ( import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )

View File

@ -13,8 +13,8 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/dashdiffs" "github.com/grafana/grafana/pkg/components/dashdiffs"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"

View File

@ -10,7 +10,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"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/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"

View File

@ -2,7 +2,7 @@ package api
import ( import (
"github.com/grafana/grafana/pkg/api/pluginproxy" "github.com/grafana/grafana/pkg/api/pluginproxy"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
) )

View File

@ -7,9 +7,9 @@ import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"

View File

@ -16,12 +16,12 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/login/social"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/social"
) )
var ( var (

View File

@ -3,7 +3,7 @@ package api
import ( import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"

View File

@ -6,7 +6,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/events" "github.com/grafana/grafana/pkg/events"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"

View File

@ -4,7 +4,7 @@ import (
"strconv" "strconv"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search" "github.com/grafana/grafana/pkg/services/search"
) )

View File

@ -4,7 +4,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/events" "github.com/grafana/grafana/pkg/events"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"

View File

@ -14,8 +14,8 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/extensions" "github.com/grafana/grafana/pkg/extensions"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
_ "github.com/grafana/grafana/pkg/services/alerting/conditions" _ "github.com/grafana/grafana/pkg/services/alerting/conditions"
_ "github.com/grafana/grafana/pkg/services/alerting/notifiers" _ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"

View File

@ -16,9 +16,9 @@ import (
"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/login" "github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/social"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@ -28,8 +28,9 @@ import (
// self registering services // self registering services
_ "github.com/grafana/grafana/pkg/extensions" _ "github.com/grafana/grafana/pkg/extensions"
_ "github.com/grafana/grafana/pkg/infra/metrics"
_ "github.com/grafana/grafana/pkg/infra/serverlock" _ "github.com/grafana/grafana/pkg/infra/serverlock"
_ "github.com/grafana/grafana/pkg/metrics" _ "github.com/grafana/grafana/pkg/infra/tracing"
_ "github.com/grafana/grafana/pkg/plugins" _ "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/auth" _ "github.com/grafana/grafana/pkg/services/auth"
@ -39,7 +40,6 @@ import (
_ "github.com/grafana/grafana/pkg/services/rendering" _ "github.com/grafana/grafana/pkg/services/rendering"
_ "github.com/grafana/grafana/pkg/services/search" _ "github.com/grafana/grafana/pkg/services/search"
_ "github.com/grafana/grafana/pkg/services/sqlstore" _ "github.com/grafana/grafana/pkg/services/sqlstore"
_ "github.com/grafana/grafana/pkg/tracing"
) )
func NewGrafanaServer() *GrafanaServerImpl { func NewGrafanaServer() *GrafanaServerImpl {

View File

@ -3,8 +3,8 @@ package metrics
import ( import (
"context" "context"
"github.com/grafana/grafana/pkg/infra/metrics/graphitebridge"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics/graphitebridge"
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )

View File

@ -5,7 +5,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/grafana/grafana/pkg/metrics/graphitebridge" "github.com/grafana/grafana/pkg/infra/metrics/graphitebridge"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )

View File

@ -5,8 +5,8 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/social"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"

View File

@ -9,7 +9,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
"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/setting" "github.com/grafana/grafana/pkg/setting"

View File

@ -6,7 +6,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
) )

View File

@ -5,8 +5,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
) )
type DefaultEvalHandler struct { type DefaultEvalHandler struct {

View File

@ -7,8 +7,8 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/imguploader" "github.com/grafana/grafana/pkg/components/imguploader"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
"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"

View File

@ -5,8 +5,8 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
) )

View File

@ -5,8 +5,8 @@ import (
"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/infra/metrics"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/annotations" "github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/services/rendering"

View File

@ -5,7 +5,7 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search" "github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"

View File

@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/securejsondata" "github.com/grafana/grafana/pkg/components/securejsondata"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
) )

View File

@ -24,7 +24,7 @@ import (
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
"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/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
) )
type CloudWatchExecutor struct { type CloudWatchExecutor struct {

View File

@ -17,7 +17,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/tsdb" "github.com/grafana/grafana/pkg/tsdb"
) )

View File

@ -336,6 +336,8 @@ func (e *StackdriverExecutor) unmarshalResponse(res *http.Response) (Stackdriver
return StackdriverResponse{}, err return StackdriverResponse{}, err
} }
// slog.Info("stackdriver", "response", string(body))
if res.StatusCode/100 != 2 { if res.StatusCode/100 != 2 {
slog.Error("Request failed", "status", res.Status, "body", string(body)) slog.Error("Request failed", "status", res.Status, "body", string(body))
return StackdriverResponse{}, fmt.Errorf(string(body)) return StackdriverResponse{}, fmt.Errorf(string(body))
@ -559,7 +561,7 @@ func calcBucketBound(bucketOptions StackdriverBucketOptions, n int) string {
} else if bucketOptions.ExponentialBuckets != nil { } else if bucketOptions.ExponentialBuckets != nil {
bucketBound = strconv.FormatInt(int64(bucketOptions.ExponentialBuckets.Scale*math.Pow(bucketOptions.ExponentialBuckets.GrowthFactor, float64(n-1))), 10) bucketBound = strconv.FormatInt(int64(bucketOptions.ExponentialBuckets.Scale*math.Pow(bucketOptions.ExponentialBuckets.GrowthFactor, float64(n-1))), 10)
} else if bucketOptions.ExplicitBuckets != nil { } else if bucketOptions.ExplicitBuckets != nil {
bucketBound = strconv.FormatInt(bucketOptions.ExplicitBuckets.Bounds[(n-1)], 10) bucketBound = fmt.Sprintf("%g", bucketOptions.ExplicitBuckets.Bounds[n])
} }
return bucketBound return bucketBound
} }

View File

@ -344,8 +344,8 @@ func TestStackdriver(t *testing.T) {
}) })
}) })
Convey("when data from query is distribution", func() { Convey("when data from query is distribution with exponential bounds", func() {
data, err := loadTestFile("./test-data/3-series-response-distribution.json") data, err := loadTestFile("./test-data/3-series-response-distribution-exponential.json")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(data.TimeSeries), ShouldEqual, 1) So(len(data.TimeSeries), ShouldEqual, 1)
@ -370,6 +370,14 @@ func TestStackdriver(t *testing.T) {
So(res.Series[0].Points[2][1].Float64, ShouldEqual, 1536669060000) So(res.Series[0].Points[2][1].Float64, ShouldEqual, 1536669060000)
}) })
Convey("bucket bounds should be correct", func() {
So(res.Series[0].Name, ShouldEqual, "0")
So(res.Series[1].Name, ShouldEqual, "1")
So(res.Series[2].Name, ShouldEqual, "2")
So(res.Series[3].Name, ShouldEqual, "4")
So(res.Series[4].Name, ShouldEqual, "8")
})
Convey("value should be correct", func() { Convey("value should be correct", func() {
So(res.Series[8].Points[0][0].Float64, ShouldEqual, 1) So(res.Series[8].Points[0][0].Float64, ShouldEqual, 1)
So(res.Series[9].Points[0][0].Float64, ShouldEqual, 1) So(res.Series[9].Points[0][0].Float64, ShouldEqual, 1)
@ -383,6 +391,45 @@ func TestStackdriver(t *testing.T) {
}) })
}) })
Convey("when data from query is distribution with explicit bounds", func() {
data, err := loadTestFile("./test-data/4-series-response-distribution-explicit.json")
So(err, ShouldBeNil)
So(len(data.TimeSeries), ShouldEqual, 1)
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &StackdriverQuery{AliasBy: "{{bucket}}"}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
So(len(res.Series), ShouldEqual, 33)
for i := 0; i < 33; i++ {
if i == 0 {
So(res.Series[i].Name, ShouldEqual, "0")
}
So(len(res.Series[i].Points), ShouldEqual, 2)
}
Convey("timestamps should be in ascending order", func() {
So(res.Series[0].Points[0][1].Float64, ShouldEqual, 1550859086000)
So(res.Series[0].Points[1][1].Float64, ShouldEqual, 1550859146000)
})
Convey("bucket bounds should be correct", func() {
So(res.Series[0].Name, ShouldEqual, "0")
So(res.Series[1].Name, ShouldEqual, "0.01")
So(res.Series[2].Name, ShouldEqual, "0.05")
So(res.Series[3].Name, ShouldEqual, "0.1")
})
Convey("value should be correct", func() {
So(res.Series[8].Points[0][0].Float64, ShouldEqual, 381)
So(res.Series[9].Points[0][0].Float64, ShouldEqual, 212)
So(res.Series[10].Points[0][0].Float64, ShouldEqual, 56)
So(res.Series[8].Points[1][0].Float64, ShouldEqual, 375)
So(res.Series[9].Points[1][0].Float64, ShouldEqual, 213)
So(res.Series[10].Points[1][0].Float64, ShouldEqual, 56)
})
})
}) })
Convey("when interpolating filter wildcards", func() { Convey("when interpolating filter wildcards", func() {

View File

@ -0,0 +1,209 @@
{
"timeSeries": [
{
"metric": {
"type": "custom.googleapis.com\/opencensus\/grpc.io\/client\/roundtrip_latency"
},
"resource": {
"type": "global",
"labels": {
"project_id": "grafana-demo"
}
},
"metricKind": "DELTA",
"valueType": "DISTRIBUTION",
"points": [
{
"interval": {
"startTime": "2019-02-22T18:11:26Z",
"endTime": "2019-02-22T18:12:26Z"
},
"value": {
"distributionValue": {
"count": "1878",
"mean": 17.813718392255,
"sumOfSquaredDeviation": 7141630.651914,
"bucketOptions": {
"explicitBuckets": {
"bounds": [
0,
0.01,
0.05,
0.1,
0.3,
0.6,
0.8,
1,
2,
3,
4,
5,
6,
8,
10,
13,
16,
20,
25,
30,
40,
50,
65,
80,
100,
130,
160,
200,
250,
300,
400,
500,
650,
800,
1000,
2000,
5000,
10000,
20000,
50000,
100000
]
}
},
"bucketCounts": [
"0",
"0",
"0",
"0",
"8",
"403",
"297",
"184",
"375",
"213",
"56",
"31",
"15",
"13",
"4",
"1",
"5",
"2",
"8",
"13",
"26",
"13",
"45",
"48",
"61",
"10",
"3",
"6",
"7",
"4",
"7",
"12",
"8"
]
}
}
},
{
"interval": {
"startTime": "2019-02-22T18:10:26Z",
"endTime": "2019-02-22T18:11:26Z"
},
"value": {
"distributionValue": {
"count": "1887",
"mean": 17.654277577766,
"sumOfSquaredDeviation": 7082587.2133073,
"bucketOptions": {
"explicitBuckets": {
"bounds": [
0,
0.01,
0.05,
0.1,
0.3,
0.6,
0.8,
1,
2,
3,
4,
5,
6,
8,
10,
13,
16,
20,
25,
30,
40,
50,
65,
80,
100,
130,
160,
200,
250,
300,
400,
500,
650,
800,
1000,
2000,
5000,
10000,
20000,
50000,
100000
]
}
},
"bucketCounts": [
"0",
"0",
"0",
"0",
"8",
"404",
"298",
"187",
"381",
"212",
"56",
"31",
"15",
"14",
"4",
"1",
"4",
"2",
"9",
"13",
"24",
"13",
"46",
"46",
"61",
"11",
"3",
"6",
"7",
"5",
"7",
"11",
"8"
]
}
}
}
]
}
]
}

View File

@ -26,7 +26,7 @@ type StackdriverBucketOptions struct {
Scale float64 `json:"scale"` Scale float64 `json:"scale"`
} `json:"exponentialBuckets"` } `json:"exponentialBuckets"`
ExplicitBuckets *struct { ExplicitBuckets *struct {
Bounds []int64 `json:"bounds"` Bounds []float64 `json:"bounds"`
} `json:"explicitBuckets"` } `json:"explicitBuckets"`
} }

View File

@ -128,7 +128,7 @@ export function dropdownTypeahead2($compile) {
'<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>'; '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
const buttonTemplate = const buttonTemplate =
'<a class="gf-form-input dropdown-toggle"' + '<a class="{{buttonTemplateClass}} dropdown-toggle"' +
' tabindex="1" gf-dropdown="menuItems" data-toggle="dropdown"' + ' tabindex="1" gf-dropdown="menuItems" data-toggle="dropdown"' +
' ><i class="fa fa-plus"></i></a>'; ' ><i class="fa fa-plus"></i></a>';
@ -137,9 +137,15 @@ export function dropdownTypeahead2($compile) {
menuItems: '=dropdownTypeahead2', menuItems: '=dropdownTypeahead2',
dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect', dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',
model: '=ngModel', model: '=ngModel',
buttonTemplateClass: '@',
}, },
link: ($scope, elem, attrs) => { link: ($scope, elem, attrs) => {
const $input = $(inputTemplate); const $input = $(inputTemplate);
if (!$scope.buttonTemplateClass) {
$scope.buttonTemplateClass = 'gf-form-input';
}
const $button = $(buttonTemplate); const $button = $(buttonTemplate);
const timeoutId = { const timeoutId = {
blur: null, blur: null,

View File

@ -240,7 +240,7 @@ export class ValueSelectDropdownCtrl {
/** @ngInject */ /** @ngInject */
export function valueSelectDropdown($compile, $window, $timeout, $rootScope) { export function valueSelectDropdown($compile, $window, $timeout, $rootScope) {
return { return {
scope: { variable: '=', onUpdated: '&' }, scope: { dashboard: '=', variable: '=', onUpdated: '&' },
templateUrl: 'public/app/partials/valueSelectDropdown.html', templateUrl: 'public/app/partials/valueSelectDropdown.html',
controller: 'ValueSelectDropdownCtrl', controller: 'ValueSelectDropdownCtrl',
controllerAs: 'vm', controllerAs: 'vm',
@ -288,13 +288,13 @@ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) {
} }
}); });
const cleanUp = $rootScope.$on('template-variable-value-updated', () => { scope.vm.dashboard.on(
'template-variable-value-updated',
() => {
scope.vm.updateLinkText(); scope.vm.updateLinkText();
}); },
scope
scope.$on('$destroy', () => { );
cleanUp();
});
scope.vm.init(); scope.vm.init();
}, },

View File

@ -4,7 +4,7 @@
<label class="gf-form-label template-variable" ng-hide="variable.hide === 1"> <label class="gf-form-label template-variable" ng-hide="variable.hide === 1">
{{variable.label || variable.name}} {{variable.label || variable.name}}
</label> </label>
<value-select-dropdown ng-if="variable.type !== 'adhoc' && variable.type !== 'textbox'" variable="variable" on-updated="ctrl.variableUpdated(variable)"></value-select-dropdown> <value-select-dropdown ng-if="variable.type !== 'adhoc' && variable.type !== 'textbox'" dashboard="ctrl.dashboard" variable="variable" on-updated="ctrl.variableUpdated(variable)"></value-select-dropdown>
<input type="text" ng-if="variable.type === 'textbox'" ng-model="variable.query" class="gf-form-input width-12" ng-blur="variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ng-keydown="$event.keyCode === 13 && variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ></input> <input type="text" ng-if="variable.type === 'textbox'" ng-model="variable.query" class="gf-form-input width-12" ng-blur="variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ng-keydown="$event.keyCode === 13 && variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ></input>
</div> </div>
<ad-hoc-filters ng-if="variable.type === 'adhoc'" variable="variable" dashboard="ctrl.dashboard"></ad-hoc-filters> <ad-hoc-filters ng-if="variable.type === 'adhoc'" variable="variable" dashboard="ctrl.dashboard"></ad-hoc-filters>

View File

@ -1,19 +1,38 @@
<query-editor-row query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true"> <query-editor-row query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true">
<div ng-if="ctrl.target.rawQuery"> <div ng-if="ctrl.target.rawQuery">
<div class="gf-form"> <div class="gf-form">
<textarea rows="3" class="gf-form-input" ng-model="ctrl.target.query" spellcheck="false" placeholder="InfluxDB Query" ng-model-onblur ng-change="ctrl.refresh()"></textarea> <textarea
rows="3"
class="gf-form-input"
ng-model="ctrl.target.query"
spellcheck="false"
placeholder="InfluxDB Query"
ng-model-onblur
ng-change="ctrl.refresh()"
></textarea>
</div> </div>
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword">FORMAT AS</label> <label class="gf-form-label query-keyword">FORMAT AS</label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select> <select
class="gf-form-input gf-size-auto"
ng-model="ctrl.target.resultFormat"
ng-options="f.value as f.text for f in ctrl.resultFormats"
ng-change="ctrl.refresh()"
></select>
</div> </div>
</div> </div>
<div class="gf-form max-width-25" ng-hide="ctrl.target.resultFormat === 'table'"> <div class="gf-form max-width-25" ng-hide="ctrl.target.resultFormat === 'table'">
<label class="gf-form-label query-keyword">ALIAS BY</label> <label class="gf-form-label query-keyword">ALIAS BY</label>
<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()"> <input
type="text"
class="gf-form-input"
ng-model="ctrl.target.alias"
spellcheck="false"
placeholder="Naming pattern"
ng-blur="ctrl.refresh()"
/>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div> <div class="gf-form-label gf-form-label--grow"></div>
@ -22,13 +41,20 @@
</div> </div>
<div ng-if="!ctrl.target.rawQuery"> <div ng-if="!ctrl.target.rawQuery">
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-7">FROM</label> <label class="gf-form-label query-keyword width-7">FROM</label>
<metric-segment segment="ctrl.policySegment" get-options="ctrl.getPolicySegments()" on-change="ctrl.policyChanged()"></metric-segment> <metric-segment
<metric-segment segment="ctrl.measurementSegment" get-options="ctrl.getMeasurements($query)" on-change="ctrl.measurementChanged()"></metric-segment> segment="ctrl.policySegment"
get-options="ctrl.getPolicySegments()"
on-change="ctrl.policyChanged()"
></metric-segment>
<metric-segment
segment="ctrl.measurementSegment"
get-options="ctrl.getMeasurements($query)"
on-change="ctrl.measurementChanged()"
></metric-segment>
</div> </div>
<div class="gf-form"> <div class="gf-form">
@ -36,7 +62,11 @@
</div> </div>
<div class="gf-form" ng-repeat="segment in ctrl.tagSegments"> <div class="gf-form" ng-repeat="segment in ctrl.tagSegments">
<metric-segment segment="segment" get-options="ctrl.getTagsOrValues(segment, $index)" on-change="ctrl.tagSegmentUpdated(segment, $index)"></metric-segment> <metric-segment
segment="segment"
get-options="ctrl.getTagsOrValues(segment, $index)"
on-change="ctrl.tagSegmentUpdated(segment, $index)"
></metric-segment>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -46,20 +76,25 @@
<div class="gf-form-inline" ng-repeat="selectParts in ctrl.queryModel.selectModels"> <div class="gf-form-inline" ng-repeat="selectParts in ctrl.queryModel.selectModels">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-7"> <label class="gf-form-label query-keyword width-7"> <span ng-show="$index === 0">SELECT</span>&nbsp; </label>
<span ng-show="$index === 0">SELECT</span>&nbsp;
</label>
</div> </div>
<div class="gf-form" ng-repeat="part in selectParts"> <div class="gf-form" ng-repeat="part in selectParts">
<query-part-editor class="gf-form-label query-part" part="part" handle-event="ctrl.handleSelectPartEvent(selectParts, part, $event)"> <query-part-editor
class="gf-form-label query-part"
part="part"
handle-event="ctrl.handleSelectPartEvent(selectParts, part, $event)"
>
</query-part-editor> </query-part-editor>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="dropdown" <label
dropdown-typeahead="ctrl.selectMenu" class="dropdown"
dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"> dropdown-typeahead2="ctrl.selectMenu"
dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"
button-template-class="gf-form-label query-part"
>
</label> </label>
</div> </div>
@ -74,14 +109,21 @@
<span>GROUP BY</span> <span>GROUP BY</span>
</label> </label>
<query-part-editor ng-repeat="part in ctrl.queryModel.groupByParts" <query-part-editor
part="part" class="gf-form-label query-part" ng-repeat="part in ctrl.queryModel.groupByParts"
handle-event="ctrl.handleGroupByPartEvent(part, $index, $event)"> part="part"
class="gf-form-label query-part"
handle-event="ctrl.handleGroupByPartEvent(part, $index, $event)"
>
</query-part-editor> </query-part-editor>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<metric-segment segment="ctrl.groupBySegment" get-options="ctrl.getGroupByOptions()" on-change="ctrl.groupByAction(part, $index)"></metric-segment> <metric-segment
segment="ctrl.groupBySegment"
get-options="ctrl.getGroupByOptions()"
on-change="ctrl.groupByAction(part, $index)"
></metric-segment>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -92,7 +134,9 @@
<div class="gf-form-inline" ng-if="ctrl.target.orderByTime === 'DESC'"> <div class="gf-form-inline" ng-if="ctrl.target.orderByTime === 'DESC'">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-7">ORDER BY</label> <label class="gf-form-label query-keyword width-7">ORDER BY</label>
<label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span class="query-keyword">DESC</span> <i class="fa fa-remove"></i></label> <label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()"
>time <span class="query-keyword">DESC</span> <i class="fa fa-remove"></i
></label>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div> <div class="gf-form-label gf-form-label--grow"></div>
@ -102,7 +146,14 @@
<div class="gf-form-inline" ng-if="ctrl.target.limit"> <div class="gf-form-inline" ng-if="ctrl.target.limit">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-7">LIMIT</label> <label class="gf-form-label query-keyword width-7">LIMIT</label>
<input type="text" class="gf-form-input width-9" ng-model="ctrl.target.limit" spellcheck='false' placeholder="No Limit" ng-blur="ctrl.refresh()"> <input
type="text"
class="gf-form-input width-9"
ng-model="ctrl.target.limit"
spellcheck="false"
placeholder="No Limit"
ng-blur="ctrl.refresh()"
/>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div> <div class="gf-form-label gf-form-label--grow"></div>
@ -112,7 +163,14 @@
<div class="gf-form-inline" ng-if="ctrl.target.slimit"> <div class="gf-form-inline" ng-if="ctrl.target.slimit">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-7">SLIMIT</label> <label class="gf-form-label query-keyword width-7">SLIMIT</label>
<input type="text" class="gf-form-input width-9" ng-model="ctrl.target.slimit" spellcheck='false' placeholder="No Limit" ng-blur="ctrl.refresh()"> <input
type="text"
class="gf-form-input width-9"
ng-model="ctrl.target.slimit"
spellcheck="false"
placeholder="No Limit"
ng-blur="ctrl.refresh()"
/>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div> <div class="gf-form-label gf-form-label--grow"></div>
@ -122,7 +180,14 @@
<div class="gf-form-inline" ng-if="ctrl.target.tz"> <div class="gf-form-inline" ng-if="ctrl.target.tz">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-7">tz</label> <label class="gf-form-label query-keyword width-7">tz</label>
<input type="text" class="gf-form-input width-9" ng-model="ctrl.target.tz" spellcheck='false' placeholder="No Timezone" ng-blur="ctrl.refresh()"> <input
type="text"
class="gf-form-input width-9"
ng-model="ctrl.target.tz"
spellcheck="false"
placeholder="No Timezone"
ng-blur="ctrl.refresh()"
/>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div> <div class="gf-form-label gf-form-label--grow"></div>
@ -133,7 +198,12 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-7">FORMAT AS</label> <label class="gf-form-label query-keyword width-7">FORMAT AS</label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select> <select
class="gf-form-input gf-size-auto"
ng-model="ctrl.target.resultFormat"
ng-options="f.value as f.text for f in ctrl.resultFormats"
ng-change="ctrl.refresh()"
></select>
</div> </div>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -144,12 +214,18 @@
<div class="gf-form-inline" ng-hide="ctrl.target.resultFormat === 'table'"> <div class="gf-form-inline" ng-hide="ctrl.target.resultFormat === 'table'">
<div class="gf-form max-width-30"> <div class="gf-form max-width-30">
<label class="gf-form-label query-keyword width-7">ALIAS BY</label> <label class="gf-form-label query-keyword width-7">ALIAS BY</label>
<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()"> <input
type="text"
class="gf-form-input"
ng-model="ctrl.target.alias"
spellcheck="false"
placeholder="Naming pattern"
ng-blur="ctrl.refresh()"
/>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div> <div class="gf-form-label gf-form-label--grow"></div>
</div> </div>
</div> </div>
</div> </div>
</query-editor-row> </query-editor-row>

View File

@ -45,8 +45,10 @@
<div class="gf-form"> <div class="gf-form">
<label class="dropdown" <label class="dropdown"
dropdown-typeahead="ctrl.selectMenu" dropdown-typeahead2="ctrl.selectMenu"
dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"> dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"
button-template-class="gf-form-label query-part"
>
</label> </label>
</div> </div>

View File

@ -45,8 +45,10 @@
<div class="gf-form"> <div class="gf-form">
<label class="dropdown" <label class="dropdown"
dropdown-typeahead="ctrl.selectMenu" dropdown-typeahead2="ctrl.selectMenu"
dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"> dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"
button-template-class="gf-form-label query-part"
>
</label> </label>
</div> </div>

View File

@ -100,7 +100,7 @@ export class ResultTransformer {
table.columns.push({ text: 'Time', type: 'time' }); table.columns.push({ text: 'Time', type: 'time' });
_.each(sortedLabels, (label, labelIndex) => { _.each(sortedLabels, (label, labelIndex) => {
metricLabels[label] = labelIndex + 1; metricLabels[label] = labelIndex + 1;
table.columns.push({ text: label, filterable: !label.startsWith('__') }); table.columns.push({ text: label, filterable: true });
}); });
const valueText = resultCount > 1 || valueWithRefId ? `Value #${refId}` : 'Value'; const valueText = resultCount > 1 || valueWithRefId ? `Value #${refId}` : 'Value';
table.columns.push({ text: valueText }); table.columns.push({ text: valueText });

View File

@ -66,11 +66,12 @@ describe('Prometheus Result Transformer', () => {
]); ]);
expect(table.columns).toMatchObject([ expect(table.columns).toMatchObject([
{ text: 'Time', type: 'time' }, { text: 'Time', type: 'time' },
{ text: '__name__' }, { text: '__name__', filterable: true },
{ text: 'instance' }, { text: 'instance', filterable: true },
{ text: 'job' }, { text: 'job' },
{ text: 'Value' }, { text: 'Value' },
]); ]);
expect(table.columns[4].filterable).toBeUndefined();
}); });
it('should column title include refId if response count is more than 2', () => { it('should column title include refId if response count is more than 2', () => {

View File

@ -49,7 +49,8 @@ describe('Aggregations', () => {
}); });
it('', () => { it('', () => {
const options = wrapper.state().aggOptions[0].options; const options = wrapper.state().aggOptions[0].options;
expect(options.length).toEqual(5);
expect(options.length).toEqual(10);
expect(options.map(o => o.value)).toEqual(expect.arrayContaining(['REDUCE_NONE'])); expect(options.map(o => o.value)).toEqual(expect.arrayContaining(['REDUCE_NONE']));
}); });
}); });

View File

@ -73,7 +73,7 @@ export class Aggregations extends React.Component<Props, State> {
value={crossSeriesReducer} value={crossSeriesReducer}
variables={templateSrv.variables} variables={templateSrv.variables}
options={aggOptions} options={aggOptions}
placeholder="Select Aggregation" placeholder="Select Reducer"
className="width-15" className="width-15"
/> />
</div> </div>

View File

@ -185,7 +185,7 @@ export class Metrics extends React.Component<Props, State> {
}, },
]} ]}
placeholder="Select Metric" placeholder="Select Metric"
className="width-15" className="width-26"
/> />
</div> </div>
<div className="gf-form gf-form--grow"> <div className="gf-form gf-form--grow">

View File

@ -28,7 +28,7 @@ Array [
<div <div
className="css-0 gf-form-select-box__placeholder" className="css-0 gf-form-select-box__placeholder"
> >
Select Aggregation Select Reducer
</div> </div>
<div <div
className="css-0" className="css-0"

View File

@ -72,7 +72,7 @@ Array [
Metric Metric
</span> </span>
<div <div
className="css-0 gf-form-input gf-form-input--form-dropdown width-15" className="css-0 gf-form-input gf-form-input--form-dropdown width-26"
onKeyDown={[Function]} onKeyDown={[Function]}
> >
<div <div
@ -196,7 +196,7 @@ Array [
<div <div
className="css-0 gf-form-select-box__placeholder" className="css-0 gf-form-select-box__placeholder"
> >
Select Aggregation Select Reducer
</div> </div>
<div <div
className="css-0" className="css-0"

View File

@ -189,7 +189,7 @@ export const aggOptions = [
ValueTypes.BOOL, ValueTypes.BOOL,
ValueTypes.STRING, ValueTypes.STRING,
], ],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA], metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
}, },
{ {
text: 'count true', text: 'count true',
@ -207,25 +207,25 @@ export const aggOptions = [
text: '99th percentile', text: '99th percentile',
value: 'REDUCE_PERCENTILE_99', value: 'REDUCE_PERCENTILE_99',
valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION], valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA], metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
}, },
{ {
text: '95th percentile', text: '95th percentile',
value: 'REDUCE_PERCENTILE_95', value: 'REDUCE_PERCENTILE_95',
valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION], valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA], metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
}, },
{ {
text: '50th percentile', text: '50th percentile',
value: 'REDUCE_PERCENTILE_50', value: 'REDUCE_PERCENTILE_50',
valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION], valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA], metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
}, },
{ {
text: '5th percentile', text: '5th percentile',
value: 'REDUCE_PERCENTILE_05', value: 'REDUCE_PERCENTILE_05',
valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION], valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA], metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
}, },
]; ];

View File

@ -11,7 +11,7 @@ export function SeriesOverridesCtrl($scope, $element, popoverSrv) {
const option = { const option = {
text: name, text: name,
propertyName: propertyName, propertyName: propertyName,
index: $scope.overrideMenu.lenght, index: $scope.overrideMenu.length,
values: values, values: values,
submenu: _.map(values, value => { submenu: _.map(values, value => {
return { text: String(value), value: value }; return { text: String(value), value: value };

View File

@ -1,31 +1,72 @@
<div class="editor-row"> <div class="editor-row">
<div class="section gf-form-group"> <div class="section gf-form-group">
<h5 class="section-heading">Draw Modes</h5> <h5 class="section-heading">Draw Modes</h5>
<gf-form-switch class="gf-form" label="Bars" label-class="width-5" checked="ctrl.panel.bars" on-change="ctrl.render()"></gf-form-switch> <gf-form-switch
<gf-form-switch class="gf-form" label="Lines" label-class="width-5" checked="ctrl.panel.lines" on-change="ctrl.render()"></gf-form-switch> class="gf-form"
<gf-form-switch class="gf-form" label="Points" label-class="width-5" checked="ctrl.panel.points" on-change="ctrl.render()"></gf-form-switch> label="Bars"
label-class="width-5"
checked="ctrl.panel.bars"
on-change="ctrl.render()"
></gf-form-switch>
<gf-form-switch
class="gf-form"
label="Lines"
label-class="width-5"
checked="ctrl.panel.lines"
on-change="ctrl.render()"
></gf-form-switch>
<gf-form-switch
class="gf-form"
label="Points"
label-class="width-5"
checked="ctrl.panel.points"
on-change="ctrl.render()"
></gf-form-switch>
</div> </div>
<div class="section gf-form-group"> <div class="section gf-form-group">
<h5 class="section-heading">Mode Options</h5> <h5 class="section-heading">Mode Options</h5>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-8">Fill</label> <label class="gf-form-label width-8">Fill</label>
<div class="gf-form-select-wrapper max-width-5"> <div class="gf-form-select-wrapper max-width-5">
<select class="gf-form-input" ng-model="ctrl.panel.fill" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()" ng-disabled="!ctrl.panel.lines"></select> <select
class="gf-form-input"
ng-model="ctrl.panel.fill"
ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"
ng-change="ctrl.render()"
ng-disabled="!ctrl.panel.lines"
></select>
</div> </div>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-8">Line Width</label> <label class="gf-form-label width-8">Line Width</label>
<div class="gf-form-select-wrapper max-width-5"> <div class="gf-form-select-wrapper max-width-5">
<select class="gf-form-input" ng-model="ctrl.panel.linewidth" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()" ng-disabled="!ctrl.panel.lines"></select> <select
class="gf-form-input"
ng-model="ctrl.panel.linewidth"
ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"
ng-change="ctrl.render()"
ng-disabled="!ctrl.panel.lines"
></select>
</div> </div>
</div> </div>
<gf-form-switch ng-disabled="!ctrl.panel.lines" class="gf-form" label="Staircase" label-class="width-8" checked="ctrl.panel.steppedLine" on-change="ctrl.render()"> <gf-form-switch
ng-disabled="!ctrl.panel.lines"
class="gf-form"
label="Staircase"
label-class="width-8"
checked="ctrl.panel.steppedLine"
on-change="ctrl.render()"
>
</gf-form-switch> </gf-form-switch>
<div class="gf-form" ng-if="ctrl.panel.points"> <div class="gf-form" ng-if="ctrl.panel.points">
<label class="gf-form-label width-8">Point Radius</label> <label class="gf-form-label width-8">Point Radius</label>
<div class="gf-form-select-wrapper max-width-5"> <div class="gf-form-select-wrapper max-width-5">
<select class="gf-form-input" ng-model="ctrl.panel.pointradius" ng-options="f for f in [0.5,1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()"></select> <select
class="gf-form-input"
ng-model="ctrl.panel.pointradius"
ng-options="f for f in [0.5,1,2,3,4,5,6,7,8,9,10]"
ng-change="ctrl.render()"
></select>
</div> </div>
</div> </div>
</div> </div>
@ -34,33 +75,66 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-9">Mode</label> <label class="gf-form-label width-9">Mode</label>
<div class="gf-form-select-wrapper max-width-8"> <div class="gf-form-select-wrapper max-width-8">
<select class="gf-form-input" ng-model="ctrl.panel.tooltip.shared" ng-options="f.value as f.text for f in [{text: 'All series', value: true}, {text: 'Single', value: false}]" ng-change="ctrl.render()"></select> <select
class="gf-form-input"
ng-model="ctrl.panel.tooltip.shared"
ng-options="f.value as f.text for f in [{text: 'All series', value: true}, {text: 'Single', value: false}]"
ng-change="ctrl.render()"
></select>
</div> </div>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-9">Sort order</label> <label class="gf-form-label width-9">Sort order</label>
<div class="gf-form-select-wrapper max-width-8"> <div class="gf-form-select-wrapper max-width-8">
<select class="gf-form-input" ng-model="ctrl.panel.tooltip.sort" ng-options="f.value as f.text for f in [{text: 'None', value: 0}, {text: 'Increasing', value: 1}, {text: 'Decreasing', value: 2}]" ng-change="ctrl.render()"></select> <select
class="gf-form-input"
ng-model="ctrl.panel.tooltip.sort"
ng-options="f.value as f.text for f in [{text: 'None', value: 0}, {text: 'Increasing', value: 1}, {text: 'Decreasing', value: 2}]"
ng-change="ctrl.render()"
></select>
</div> </div>
</div> </div>
<div class="gf-form" ng-show="ctrl.panel.stack"> <div class="gf-form" ng-show="ctrl.panel.stack">
<label class="gf-form-label width-9">Stacked value</label> <label class="gf-form-label width-9">Stacked value</label>
<div class="gf-form-select-wrapper max-width-8"> <div class="gf-form-select-wrapper max-width-8">
<select class="gf-form-input" ng-model="ctrl.panel.tooltip.value_type" ng-options="f for f in ['cumulative','individual']" ng-change="ctrl.render()"></select> <select
class="gf-form-input"
ng-model="ctrl.panel.tooltip.value_type"
ng-options="f for f in ['cumulative','individual']"
ng-change="ctrl.render()"
></select>
</div> </div>
</div> </div>
</div> </div>
<div class="section gf-form-group"> <div class="section gf-form-group">
<h5 class="section-heading">Stacking & Null value</h5> <h5 class="section-heading">Stacking & Null value</h5>
<gf-form-switch class="gf-form" label="Stack" label-class="width-7" checked="ctrl.panel.stack" on-change="ctrl.refresh()"> <gf-form-switch
class="gf-form"
label="Stack"
label-class="width-7"
checked="ctrl.panel.stack"
on-change="ctrl.refresh()"
>
</gf-form-switch> </gf-form-switch>
<gf-form-switch class="gf-form" ng-show="ctrl.panel.stack" label="Percent" label-class="width-7" checked="ctrl.panel.percentage" on-change="ctrl.render()"> <gf-form-switch
class="gf-form"
ng-show="ctrl.panel.stack"
label="Percent"
label-class="width-7"
checked="ctrl.panel.percentage"
on-change="ctrl.render()"
>
</gf-form-switch> </gf-form-switch>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-7">Null value</label> <label class="gf-form-label width-7">Null value</label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input max-width-9" ng-model="ctrl.panel.nullPointMode" ng-options="f for f in ['connected', 'null', 'null as zero']" ng-change="ctrl.render()"></select> <select
class="gf-form-input max-width-9"
ng-model="ctrl.panel.nullPointMode"
ng-options="f for f in ['connected', 'null', 'null as zero']"
ng-change="ctrl.render()"
></select>
</div> </div>
</div> </div>
</div> </div>
@ -72,7 +146,15 @@
<label class="gf-form-label">alias or regex</label> <label class="gf-form-label">alias or regex</label>
</div> </div>
<div class="gf-form width-15"> <div class="gf-form width-15">
<input type="text" ng-model="override.alias" bs-typeahead="getSeriesNames" ng-blur="ctrl.render()" data-min-length=0 data-items=100 class="gf-form-input width-15"> <input
type="text"
ng-model="override.alias"
bs-typeahead="getSeriesNames"
ng-blur="ctrl.render()"
data-min-length="0"
data-items="100"
class="gf-form-input width-15"
/>
</div> </div>
<div class="gf-form" ng-repeat="option in currentOverrides"> <div class="gf-form" ng-repeat="option in currentOverrides">
<label class="gf-form-label"> <label class="gf-form-label">
@ -80,14 +162,17 @@
<span ng-show="option.propertyName === 'color'"> <span ng-show="option.propertyName === 'color'">
Color: <i class="fa fa-circle" ng-style="{color:option.value}"></i> Color: <i class="fa fa-circle" ng-style="{color:option.value}"></i>
</span> </span>
<span ng-show="option.propertyName !== 'color'"> <span ng-show="option.propertyName !== 'color'"> {{ option.name }}: {{ option.value }} </span>
{{option.name}}: {{option.value}}
</span>
</label> </label>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<span class="dropdown" dropdown-typeahead="overrideMenu" dropdown-typeahead-on-select="setOverride($item, $subItem)"> <span
class="dropdown"
dropdown-typeahead2="overrideMenu"
dropdown-typeahead-on-select="setOverride($item, $subItem)"
button-template-class="gf-form-label"
>
</span> </span>
</div> </div>
@ -107,4 +192,3 @@
</button> </button>
</div> </div>
</div> </div>