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)

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.
Grafana v6.0 is out in **Beta**, [Download Now!](https://grafana.com/grafana/download/beta)
The main highlights are:
- [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
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
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" >}}
<br />
@ -122,7 +120,7 @@ To get started read the guide: [Using Google Stackdriver in Grafana](/features/d
## 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.

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"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"
"github.com/grafana/grafana/pkg/util"
)

View File

@ -13,8 +13,8 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/dashdiffs"
"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/metrics"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"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/bus"
"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"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/setting"

View File

@ -2,7 +2,7 @@ package api
import (
"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"
"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/bus"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"

View File

@ -16,12 +16,12 @@ import (
"golang.org/x/oauth2"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/log"
"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"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/social"
)
var (

View File

@ -3,7 +3,7 @@ package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"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"
"github.com/grafana/grafana/pkg/setting"
"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/bus"
"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"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"

View File

@ -4,7 +4,7 @@ import (
"strconv"
"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"
"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/bus"
"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"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"

View File

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

View File

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

View File

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

View File

@ -5,8 +5,8 @@ import (
"time"
"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/social"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/registry"

View File

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

View File

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

View File

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

View File

@ -7,8 +7,8 @@ import (
"github.com/grafana/grafana/pkg/bus"
"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/metrics"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/setting"

View File

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

View File

@ -5,7 +5,7 @@ import (
"time"
"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"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/util"

View File

@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"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"
)

View File

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

View File

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

View File

@ -344,8 +344,8 @@ func TestStackdriver(t *testing.T) {
})
})
Convey("when data from query is distribution", func() {
data, err := loadTestFile("./test-data/3-series-response-distribution.json")
Convey("when data from query is distribution with exponential bounds", func() {
data, err := loadTestFile("./test-data/3-series-response-distribution-exponential.json")
So(err, ShouldBeNil)
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)
})
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() {
So(res.Series[8].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() {

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"`
} `json:"exponentialBuckets"`
ExplicitBuckets *struct {
Bounds []int64 `json:"bounds"`
Bounds []float64 `json:"bounds"`
} `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>';
const buttonTemplate =
'<a class="gf-form-input dropdown-toggle"' +
'<a class="{{buttonTemplateClass}} dropdown-toggle"' +
' tabindex="1" gf-dropdown="menuItems" data-toggle="dropdown"' +
' ><i class="fa fa-plus"></i></a>';
@ -137,9 +137,15 @@ export function dropdownTypeahead2($compile) {
menuItems: '=dropdownTypeahead2',
dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',
model: '=ngModel',
buttonTemplateClass: '@',
},
link: ($scope, elem, attrs) => {
const $input = $(inputTemplate);
if (!$scope.buttonTemplateClass) {
$scope.buttonTemplateClass = 'gf-form-input';
}
const $button = $(buttonTemplate);
const timeoutId = {
blur: null,

View File

@ -240,7 +240,7 @@ export class ValueSelectDropdownCtrl {
/** @ngInject */
export function valueSelectDropdown($compile, $window, $timeout, $rootScope) {
return {
scope: { variable: '=', onUpdated: '&' },
scope: { dashboard: '=', variable: '=', onUpdated: '&' },
templateUrl: 'public/app/partials/valueSelectDropdown.html',
controller: 'ValueSelectDropdownCtrl',
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.$on('$destroy', () => {
cleanUp();
});
},
scope
);
scope.vm.init();
},

View File

@ -4,7 +4,7 @@
<label class="gf-form-label template-variable" ng-hide="variable.hide === 1">
{{variable.label || variable.name}}
</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>
</div>
<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">
<div ng-if="ctrl.target.rawQuery">
<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 class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword">FORMAT AS</label>
<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 class="gf-form max-width-25" ng-hide="ctrl.target.resultFormat === 'table'">
<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 class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
@ -22,13 +41,20 @@
</div>
<div ng-if="!ctrl.target.rawQuery">
<div class="gf-form-inline">
<div class="gf-form">
<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 segment="ctrl.measurementSegment" get-options="ctrl.getMeasurements($query)" on-change="ctrl.measurementChanged()"></metric-segment>
<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 class="gf-form">
@ -36,7 +62,11 @@
</div>
<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 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">
<label class="gf-form-label query-keyword width-7">
<span ng-show="$index === 0">SELECT</span>&nbsp;
</label>
<label class="gf-form-label query-keyword width-7"> <span ng-show="$index === 0">SELECT</span>&nbsp; </label>
</div>
<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>
</div>
<div class="gf-form">
<label class="dropdown"
dropdown-typeahead="ctrl.selectMenu"
dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)">
<label
class="dropdown"
dropdown-typeahead2="ctrl.selectMenu"
dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"
button-template-class="gf-form-label query-part"
>
</label>
</div>
@ -74,14 +109,21 @@
<span>GROUP BY</span>
</label>
<query-part-editor ng-repeat="part in ctrl.queryModel.groupByParts"
part="part" class="gf-form-label query-part"
handle-event="ctrl.handleGroupByPartEvent(part, $index, $event)">
<query-part-editor
ng-repeat="part in ctrl.queryModel.groupByParts"
part="part"
class="gf-form-label query-part"
handle-event="ctrl.handleGroupByPartEvent(part, $index, $event)"
>
</query-part-editor>
</div>
<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 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">
<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 class="gf-form gf-form--grow">
<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">
<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 class="gf-form gf-form--grow">
<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">
<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 class="gf-form gf-form--grow">
<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">
<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 class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
@ -133,7 +198,12 @@
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">FORMAT AS</label>
<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 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 max-width-30">
<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 class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
</div>
</query-editor-row>

View File

@ -45,8 +45,10 @@
<div class="gf-form">
<label class="dropdown"
dropdown-typeahead="ctrl.selectMenu"
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>
</div>

View File

@ -45,8 +45,10 @@
<div class="gf-form">
<label class="dropdown"
dropdown-typeahead="ctrl.selectMenu"
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>
</div>

View File

@ -100,7 +100,7 @@ export class ResultTransformer {
table.columns.push({ text: 'Time', type: 'time' });
_.each(sortedLabels, (label, labelIndex) => {
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';
table.columns.push({ text: valueText });

View File

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

View File

@ -49,7 +49,8 @@ describe('Aggregations', () => {
});
it('', () => {
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']));
});
});

View File

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

View File

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

View File

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

View File

@ -72,7 +72,7 @@ Array [
Metric
</span>
<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]}
>
<div
@ -196,7 +196,7 @@ Array [
<div
className="css-0 gf-form-select-box__placeholder"
>
Select Aggregation
Select Reducer
</div>
<div
className="css-0"

View File

@ -189,7 +189,7 @@ export const aggOptions = [
ValueTypes.BOOL,
ValueTypes.STRING,
],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
},
{
text: 'count true',
@ -207,25 +207,25 @@ export const aggOptions = [
text: '99th percentile',
value: 'REDUCE_PERCENTILE_99',
valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
},
{
text: '95th percentile',
value: 'REDUCE_PERCENTILE_95',
valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
},
{
text: '50th percentile',
value: 'REDUCE_PERCENTILE_50',
valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA],
metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
},
{
text: '5th percentile',
value: 'REDUCE_PERCENTILE_05',
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 = {
text: name,
propertyName: propertyName,
index: $scope.overrideMenu.lenght,
index: $scope.overrideMenu.length,
values: values,
submenu: _.map(values, value => {
return { text: String(value), value: value };

View File

@ -1,31 +1,72 @@
<div class="editor-row">
<div class="section gf-form-group">
<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 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>
<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
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 class="section gf-form-group">
<h5 class="section-heading">Mode Options</h5>
<div class="gf-form">
<label class="gf-form-label width-8">Fill</label>
<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 class="gf-form">
<label class="gf-form-label width-8">Line Width</label>
<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>
<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>
<div class="gf-form" ng-if="ctrl.panel.points">
<label class="gf-form-label width-8">Point Radius</label>
<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>
@ -34,33 +75,66 @@
<div class="gf-form">
<label class="gf-form-label width-9">Mode</label>
<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 class="gf-form">
<label class="gf-form-label width-9">Sort order</label>
<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 class="gf-form" ng-show="ctrl.panel.stack">
<label class="gf-form-label width-9">Stacked value</label>
<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 class="section gf-form-group">
<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 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>
<div class="gf-form">
<label class="gf-form-label width-7">Null value</label>
<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>
@ -72,7 +146,15 @@
<label class="gf-form-label">alias or regex</label>
</div>
<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 class="gf-form" ng-repeat="option in currentOverrides">
<label class="gf-form-label">
@ -80,14 +162,17 @@
<span ng-show="option.propertyName === 'color'">
Color: <i class="fa fa-circle" ng-style="{color:option.value}"></i>
</span>
<span ng-show="option.propertyName !== 'color'">
{{option.name}}: {{option.value}}
</span>
<span ng-show="option.propertyName !== 'color'"> {{ option.name }}: {{ option.value }} </span>
</label>
</div>
<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>
</div>
@ -107,4 +192,3 @@
</button>
</div>
</div>