mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 01:15:46 +08:00
Merge branch 'master' into develop
This commit is contained in:
16
CHANGELOG.md
16
CHANGELOG.md
@ -7,9 +7,20 @@
|
|||||||
- UX changes to nav & side menu
|
- UX changes to nav & side menu
|
||||||
- New dashboard grid layout system
|
- New dashboard grid layout system
|
||||||
|
|
||||||
# 4.6.0 (unreleased)
|
# 4.6.0-beta2 (2017-10-17)
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
* **ColorPicker**: Fix for color picker not showing [#9549](https://github.com/grafana/grafana/issues/9549)
|
||||||
|
* **Alerting**: Fix for broken test rule button in alert tab [#9539](https://github.com/grafana/grafana/issues/9539)
|
||||||
|
* **Cloudwatch**: Provide error message when failing to add cloudwatch datasource [#9534](https://github.com/grafana/grafana/pull/9534), thx [@mtanda](https://github.com/mtanda)
|
||||||
|
* **Cloudwatch**: Fix unused period parameter [#9536](https://github.com/grafana/grafana/pull/9536), thx [@mtanda](https://github.com/mtanda)
|
||||||
|
* **CSV Export**: Fix for broken CSV export [#9525](https://github.com/grafana/grafana/issues/9525)
|
||||||
|
* **Text panel**: Fixes issue with break lines in Firefox [#9491](https://github.com/grafana/grafana/issues/9491)
|
||||||
|
|
||||||
|
# 4.6.0-beta1 (2017-10-13)
|
||||||
|
|
||||||
## New Features
|
## New Features
|
||||||
|
* **Annotations**: Add support for creating annotations from graph panel [#8197](https://github.com/grafana/grafana/pull/8197)
|
||||||
* **GCS**: Adds support for Google Cloud Storage [#8370](https://github.com/grafana/grafana/issues/8370) thx [@chuhlomin](https://github.com/chuhlomin)
|
* **GCS**: Adds support for Google Cloud Storage [#8370](https://github.com/grafana/grafana/issues/8370) thx [@chuhlomin](https://github.com/chuhlomin)
|
||||||
* **Prometheus**: Adds /metrics endpoint for exposing Grafana metrics. [#9187](https://github.com/grafana/grafana/pull/9187)
|
* **Prometheus**: Adds /metrics endpoint for exposing Grafana metrics. [#9187](https://github.com/grafana/grafana/pull/9187)
|
||||||
* **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk)
|
* **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk)
|
||||||
@ -36,10 +47,11 @@
|
|||||||
* **Table**: Add support for displaying the timestamp with milliseconds [#9429](https://github.com/grafana/grafana/pull/9429), thx [@s1061123](https://github.com/s1061123)
|
* **Table**: Add support for displaying the timestamp with milliseconds [#9429](https://github.com/grafana/grafana/pull/9429), thx [@s1061123](https://github.com/s1061123)
|
||||||
* **Hipchat**: Add metrics, message and image to hipchat notifications [#9110](https://github.com/grafana/grafana/issues/9110), thx [@eloo](https://github.com/eloo)
|
* **Hipchat**: Add metrics, message and image to hipchat notifications [#9110](https://github.com/grafana/grafana/issues/9110), thx [@eloo](https://github.com/eloo)
|
||||||
* **Kafka**: Add support for sending alert notifications to kafka [#7104](https://github.com/grafana/grafana/issues/7104), thx [@utkarshcmu](https://github.com/utkarshcmu)
|
* **Kafka**: Add support for sending alert notifications to kafka [#7104](https://github.com/grafana/grafana/issues/7104), thx [@utkarshcmu](https://github.com/utkarshcmu)
|
||||||
|
* **Alerting**: add count_non_null as series reducer [#9516](https://github.com/grafana/grafana/issues/9516)
|
||||||
|
|
||||||
## Tech
|
## Tech
|
||||||
* **Go**: Grafana is now built using golang 1.9
|
* **Go**: Grafana is now built using golang 1.9
|
||||||
* **Webpack**: Changed from systemjs to webpack (see readme or building from source guide for new build instructions). Systemjs is still used to load plugins but now plugins can only import a limited set of dependencies. See [PLUGIN_DEV.md](https://github.com/grafana/grafana/blob/master/PLUGIN_DEV.md) for more details on how this can effect some plugins.
|
* **Webpack**: Changed from systemjs to webpack (see readme or building from source guide for new build instructions). Systemjs is still used to load plugins but now plugins can only import a limited set of dependencies. See [PLUGIN_DEV.md](https://github.com/grafana/grafana/blob/master/PLUGIN_DEV.md) for more details on how this can effect some plugins.
|
||||||
|
|
||||||
# 4.5.2 (2017-09-22)
|
# 4.5.2 (2017-09-22)
|
||||||
|
|
||||||
|
@ -23,6 +23,6 @@ If you think we missed exposing a crucial lib or Grafana component let us know b
|
|||||||
|
|
||||||
### Deprecated components
|
### Deprecated components
|
||||||
|
|
||||||
The angular directive `<spectrum-picker>` is no deprecated (will still work for a version more) but we recommend plugin authors
|
The angular directive `<spectrum-picker>` is now deprecated (will still work for a version more) but we recommend plugin authors
|
||||||
to upgrade to new `<color-picker color="ctrl.color" onChange="ctrl.onSparklineColorChange"></color-picker>`
|
to upgrade to new `<color-picker color="ctrl.color" onChange="ctrl.onSparklineColorChange"></color-picker>`
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ Name | Description
|
|||||||
------------ | -------------
|
------------ | -------------
|
||||||
*Name* | The data source name. This is how you refer to the data source in panels & queries.
|
*Name* | The data source name. This is how you refer to the data source in panels & queries.
|
||||||
*Default* | Default data source means that it will be pre-selected for new panels.
|
*Default* | Default data source means that it will be pre-selected for new panels.
|
||||||
*Credentials* profile name | Specify the name of the profile to use (if you use `~/aws/credentials` file), leave blank for default.
|
*Credentials* profile name | Specify the name of the profile to use (if you use `~/.aws/credentials` file), leave blank for default.
|
||||||
*Default Region* | Used in query editor to set region (can be changed on per query basis)
|
*Default Region* | Used in query editor to set region (can be changed on per query basis)
|
||||||
*Custom Metrics namespace* | Specify the CloudWatch namespace of Custom metrics
|
*Custom Metrics namespace* | Specify the CloudWatch namespace of Custom metrics
|
||||||
*Assume Role Arn* | Specify the ARN of the role to assume
|
*Assume Role Arn* | Specify the ARN of the role to assume
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"company": "Grafana Labs"
|
"company": "Grafana Labs"
|
||||||
},
|
},
|
||||||
"name": "grafana",
|
"name": "grafana",
|
||||||
"version": "4.6.0-beta1",
|
"version": "4.7.0-pre1",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "http://github.com/grafana/grafana.git"
|
"url": "http://github.com/grafana/grafana.git"
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#! /usr/bin/env bash
|
#! /usr/bin/env bash
|
||||||
deb_ver=4.5.0-beta1
|
deb_ver=4.6.0-beta1
|
||||||
rpm_ver=4.5.0-beta1
|
rpm_ver=4.6.0-beta1
|
||||||
|
|
||||||
# wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${deb_ver}_amd64.deb
|
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${deb_ver}_amd64.deb
|
||||||
|
|
||||||
# package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
|
package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
|
||||||
package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
|
package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
|
||||||
package_cloud push grafana/testing/debian/stretch grafana_${deb_ver}_amd64.deb
|
package_cloud push grafana/testing/debian/stretch grafana_${deb_ver}_amd64.deb
|
||||||
|
|
||||||
|
@ -141,6 +141,16 @@ func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) null.Float {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "count_non_null":
|
||||||
|
for _, v := range series.Points {
|
||||||
|
if v[0].Valid {
|
||||||
|
value++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value > 0 {
|
||||||
|
allNull = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if allNull {
|
if allNull {
|
||||||
|
@ -67,6 +67,35 @@ func TestSimpleReducer(t *testing.T) {
|
|||||||
So(reducer.Reduce(series).Valid, ShouldEqual, false)
|
So(reducer.Reduce(series).Valid, ShouldEqual, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("count_non_null", func() {
|
||||||
|
Convey("with null values and real values", func() {
|
||||||
|
reducer := NewSimpleReducer("count_non_null")
|
||||||
|
series := &tsdb.TimeSeries{
|
||||||
|
Name: "test time serie",
|
||||||
|
}
|
||||||
|
|
||||||
|
series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
|
||||||
|
series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))
|
||||||
|
series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 3))
|
||||||
|
series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 4))
|
||||||
|
|
||||||
|
So(reducer.Reduce(series).Valid, ShouldEqual, true)
|
||||||
|
So(reducer.Reduce(series).Float64, ShouldEqual, 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("with null values", func() {
|
||||||
|
reducer := NewSimpleReducer("count_non_null")
|
||||||
|
series := &tsdb.TimeSeries{
|
||||||
|
Name: "test time serie",
|
||||||
|
}
|
||||||
|
|
||||||
|
series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
|
||||||
|
series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))
|
||||||
|
|
||||||
|
So(reducer.Reduce(series).Valid, ShouldEqual, false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Convey("avg of number values and null values should ignore nulls", func() {
|
Convey("avg of number values and null values should ignore nulls", func() {
|
||||||
reducer := NewSimpleReducer("avg")
|
reducer := NewSimpleReducer("avg")
|
||||||
series := &tsdb.TimeSeries{
|
series := &tsdb.TimeSeries{
|
||||||
|
@ -43,7 +43,7 @@ func (r *SqlAnnotationRepo) ensureTagsExist(sess *DBSession, tags []*models.Tag)
|
|||||||
var existingTag models.Tag
|
var existingTag models.Tag
|
||||||
|
|
||||||
// check if it exists
|
// check if it exists
|
||||||
if exists, err := sess.Table("tag").Where("key=? AND value=?", tag.Key, tag.Value).Get(&existingTag); err != nil {
|
if exists, err := sess.Table("tag").Where("`key`=? AND `value`=?", tag.Key, tag.Value).Get(&existingTag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if exists {
|
} else if exists {
|
||||||
tag.Id = existingTag.Id
|
tag.Id = existingTag.Id
|
||||||
|
@ -38,7 +38,7 @@ var customMetricsDimensionsMap map[string]map[string]map[string]*CustomMetricsCa
|
|||||||
func init() {
|
func init() {
|
||||||
metricsMap = map[string][]string{
|
metricsMap = map[string][]string{
|
||||||
"AWS/ApiGateway": {"4XXError", "5XXError", "CacheHitCount", "CacheMissCount", "Count", "IntegrationLatency", "Latency"},
|
"AWS/ApiGateway": {"4XXError", "5XXError", "CacheHitCount", "CacheMissCount", "Count", "IntegrationLatency", "Latency"},
|
||||||
"AWS/ApplicationELB": {"ActiveConnectionCount", "ClientTLSNegotiationErrorCount", "HealthyHostCount", "HTTPCode_ELB_4XX_Count", "HTTPCode_ELB_5XX_Count", "HTTPCode_Target_2XX_Count", "HTTPCode_Target_3XX_Count", "HTTPCode_Target_4XX_Count", "HTTPCode_Target_5XX_Count", "IPv6ProcessedBytes", "IPv6RequestCount", "NewConnectionCount", "ProcessedBytes", "RejectedConnectionCount", "RequestCount", "TargetConnectionErrorCount", "TargetResponseTime", "TargetTLSNegotiationErrorCount", "UnHealthyHostCount"},
|
"AWS/ApplicationELB": {"ActiveConnectionCount", "ClientTLSNegotiationErrorCount", "HealthyHostCount", "HTTPCode_ELB_4XX_Count", "HTTPCode_ELB_5XX_Count", "HTTPCode_Target_2XX_Count", "HTTPCode_Target_3XX_Count", "HTTPCode_Target_4XX_Count", "HTTPCode_Target_5XX_Count", "IPv6ProcessedBytes", "IPv6RequestCount", "NewConnectionCount", "ProcessedBytes", "RejectedConnectionCount", "RequestCount", "RequestCountPerTarget", "TargetConnectionErrorCount", "TargetResponseTime", "TargetTLSNegotiationErrorCount", "UnHealthyHostCount"},
|
||||||
"AWS/AutoScaling": {"GroupMinSize", "GroupMaxSize", "GroupDesiredCapacity", "GroupInServiceInstances", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"},
|
"AWS/AutoScaling": {"GroupMinSize", "GroupMaxSize", "GroupDesiredCapacity", "GroupInServiceInstances", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"},
|
||||||
"AWS/Billing": {"EstimatedCharges"},
|
"AWS/Billing": {"EstimatedCharges"},
|
||||||
"AWS/CloudFront": {"Requests", "BytesDownloaded", "BytesUploaded", "TotalErrorRate", "4xxErrorRate", "5xxErrorRate"},
|
"AWS/CloudFront": {"Requests", "BytesDownloaded", "BytesUploaded", "TotalErrorRate", "4xxErrorRate", "5xxErrorRate"},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import coreModule from '../core_module';
|
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||||
|
|
||||||
export interface IProps {
|
export interface IProps {
|
||||||
password: string;
|
password: string;
|
||||||
@ -33,7 +33,5 @@ export class PasswordStrength extends React.Component<IProps, any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.directive('passwordStrength', function(reactDirective) {
|
react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
|
||||||
return reactDirective(PasswordStrength, ['password']);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import coreModule from 'app/core/core_module';
|
|
||||||
import { sortedColors } from 'app/core/utils/colors';
|
import { sortedColors } from 'app/core/utils/colors';
|
||||||
|
|
||||||
export interface IProps {
|
export interface IProps {
|
||||||
@ -23,12 +22,15 @@ export class GfColorPalette extends React.Component<IProps, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const colorPaletteItems = this.paletteColors.map((paletteColor) => {
|
const colorPaletteItems = this.paletteColors.map(paletteColor => {
|
||||||
const cssClass = paletteColor.toLowerCase() === this.props.color.toLowerCase() ? 'fa-circle-o' : 'fa-circle';
|
const cssClass = paletteColor.toLowerCase() === this.props.color.toLowerCase() ? 'fa-circle-o' : 'fa-circle';
|
||||||
return (
|
return (
|
||||||
<i key={paletteColor} className={"pointer fa " + cssClass}
|
<i
|
||||||
style={{'color': paletteColor}}
|
key={paletteColor}
|
||||||
onClick={this.onColorSelect(paletteColor)}>
|
className={'pointer fa ' + cssClass}
|
||||||
|
style={{ color: paletteColor }}
|
||||||
|
onClick={this.onColorSelect(paletteColor)}>
|
||||||
|
|
||||||
</i>
|
</i>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -40,6 +42,3 @@ export class GfColorPalette extends React.Component<IProps, any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.directive('gfColorPalette', function (reactDirective) {
|
|
||||||
return reactDirective(GfColorPalette, ['color', 'onColorSelect']);
|
|
||||||
});
|
|
||||||
|
@ -2,8 +2,8 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import Drop from 'tether-drop';
|
import Drop from 'tether-drop';
|
||||||
import coreModule from 'app/core/core_module';
|
|
||||||
import { ColorPickerPopover } from './ColorPickerPopover';
|
import { ColorPickerPopover } from './ColorPickerPopover';
|
||||||
|
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||||
|
|
||||||
export interface IProps {
|
export interface IProps {
|
||||||
color: string;
|
color: string;
|
||||||
@ -27,9 +27,7 @@ export class ColorPicker extends React.Component<IProps, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openColorPicker() {
|
openColorPicker() {
|
||||||
const dropContent = (
|
const dropContent = <ColorPickerPopover color={this.props.color} onColorSelect={this.onColorSelect} />;
|
||||||
<ColorPickerPopover color={this.props.color} onColorSelect={this.onColorSelect} />
|
|
||||||
);
|
|
||||||
|
|
||||||
let dropContentElem = document.createElement('div');
|
let dropContentElem = document.createElement('div');
|
||||||
ReactDOM.render(dropContent, dropContentElem);
|
ReactDOM.render(dropContent, dropContentElem);
|
||||||
@ -38,12 +36,12 @@ export class ColorPicker extends React.Component<IProps, any> {
|
|||||||
target: this.pickerElem[0],
|
target: this.pickerElem[0],
|
||||||
content: dropContentElem,
|
content: dropContentElem,
|
||||||
position: 'top center',
|
position: 'top center',
|
||||||
classes: 'drop-popover drop-popover--form',
|
classes: 'drop-popover',
|
||||||
openOn: 'hover',
|
openOn: 'click',
|
||||||
hoverCloseDelay: 200,
|
hoverCloseDelay: 200,
|
||||||
tetherOptions: {
|
tetherOptions: {
|
||||||
constraints: [{ to: 'scrollParent', attachment: "none both" }]
|
constraints: [{ to: 'scrollParent', attachment: 'none both' }],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
drop.on('close', this.closeColorPicker);
|
drop.on('close', this.closeColorPicker);
|
||||||
@ -68,17 +66,14 @@ export class ColorPicker extends React.Component<IProps, any> {
|
|||||||
return (
|
return (
|
||||||
<div className="sp-replacer sp-light" onClick={this.openColorPicker} ref={this.setPickerElem}>
|
<div className="sp-replacer sp-light" onClick={this.openColorPicker} ref={this.setPickerElem}>
|
||||||
<div className="sp-preview">
|
<div className="sp-preview">
|
||||||
<div className="sp-preview-inner" style={{backgroundColor: this.props.color}}>
|
<div className="sp-preview-inner" style={{ backgroundColor: this.props.color }} />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.directive('colorPicker', function (reactDirective) {
|
react2AngularDirective('colorPicker', ColorPicker, [
|
||||||
return reactDirective(ColorPicker, [
|
'color',
|
||||||
'color',
|
['onChange', { watchDepth: 'reference', wrapApply: true }],
|
||||||
['onChange', { watchDepth: 'reference', wrapApply: true }]
|
]);
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import coreModule from 'app/core/core_module';
|
|
||||||
import { GfColorPalette } from './ColorPalette';
|
import { GfColorPalette } from './ColorPalette';
|
||||||
import { GfSpectrumPicker } from './SpectrumPicker';
|
import { GfSpectrumPicker } from './SpectrumPicker';
|
||||||
|
|
||||||
@ -115,7 +114,3 @@ export class ColorPickerPopover extends React.Component<IProps, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.directive('gfColorPickerPopover', function (reactDirective) {
|
|
||||||
return reactDirective(ColorPickerPopover, ['color', 'onColorSelect']);
|
|
||||||
});
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import coreModule from 'app/core/core_module';
|
import { ColorPickerPopover } from './ColorPickerPopover';
|
||||||
import {ColorPickerPopover} from './ColorPickerPopover';
|
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||||
|
|
||||||
export interface IProps {
|
export interface IProps {
|
||||||
series: any;
|
series: any;
|
||||||
@ -50,6 +50,4 @@ export class SeriesColorPicker extends React.Component<IProps, any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.directive('seriesColorPicker', function(reactDirective) {
|
react2AngularDirective('seriesColorPicker', SeriesColorPicker, ['series', 'onColorChange', 'onToggleAxis']);
|
||||||
return reactDirective(SeriesColorPicker, ['series', 'onColorChange', 'onToggleAxis']);
|
|
||||||
});
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import coreModule from 'app/core/core_module';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import 'vendor/spectrum';
|
import 'vendor/spectrum';
|
||||||
@ -71,6 +70,3 @@ export class GfSpectrumPicker extends React.Component<IProps, any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.directive('gfSpectrumPicker', function (reactDirective) {
|
|
||||||
return reactDirective(GfSpectrumPicker, ['color', 'options', 'onColorSelect']);
|
|
||||||
});
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import coreModule from '../../core_module';
|
import coreModule from '../../core_module';
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
export function spectrumPicker() {
|
export function spectrumPicker() {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import {saveAs} from 'file-saver';
|
||||||
declare var window: any;
|
|
||||||
|
|
||||||
const DEFAULT_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ';
|
const DEFAULT_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ';
|
||||||
|
|
||||||
@ -69,5 +68,5 @@ export function exportTableDataToCsv(table, excel = false) {
|
|||||||
|
|
||||||
export function saveSaveBlob(payload, fname) {
|
export function saveSaveBlob(payload, fname) {
|
||||||
var blob = new Blob([payload], { type: "text/csv;charset=utf-8" });
|
var blob = new Blob([payload], { type: "text/csv;charset=utf-8" });
|
||||||
window.saveAs(blob, fname);
|
saveAs(blob, fname);
|
||||||
}
|
}
|
||||||
|
10
public/app/core/utils/react2angular.ts
Normal file
10
public/app/core/utils/react2angular.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import coreModule from 'app/core/core_module';
|
||||||
|
|
||||||
|
export function react2AngularDirective(name: string, component: any, options: any) {
|
||||||
|
|
||||||
|
coreModule.directive(name, ['reactDirective', reactDirective => {
|
||||||
|
return reactDirective(component, options);
|
||||||
|
}]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -51,6 +51,7 @@ var reducerTypes = [
|
|||||||
{text: 'median()', value: 'median'},
|
{text: 'median()', value: 'median'},
|
||||||
{text: 'diff()', value: 'diff'},
|
{text: 'diff()', value: 'diff'},
|
||||||
{text: 'percent_diff()', value: 'percent_diff'},
|
{text: 'percent_diff()', value: 'percent_diff'},
|
||||||
|
{text: 'count_non_null()', value: 'count_non_null'},
|
||||||
];
|
];
|
||||||
|
|
||||||
var noDataModes = [
|
var noDataModes = [
|
||||||
|
@ -383,6 +383,7 @@ export class AlertTabCtrl {
|
|||||||
|
|
||||||
test() {
|
test() {
|
||||||
this.testing = true;
|
this.testing = true;
|
||||||
|
this.testResult = false;
|
||||||
|
|
||||||
var payload = {
|
var payload = {
|
||||||
dashboard: this.dashboardSrv.getCurrent().getSaveModelClone(),
|
dashboard: this.dashboardSrv.getCurrent().getSaveModelClone(),
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
<span class="gf-form-label query-keyword width-5" ng-if="$index===0">WHEN</span>
|
<span class="gf-form-label query-keyword width-5" ng-if="$index===0">WHEN</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<query-part-editor class="gf-form-label query-part width-6" part="conditionModel.reducerPart" handle-event="ctrl.handleReducerPartEvent(conditionModel, $event)">
|
<query-part-editor class="gf-form-label query-part width-9" part="conditionModel.reducerPart" handle-event="ctrl.handleReducerPartEvent(conditionModel, $event)">
|
||||||
</query-part-editor>
|
</query-part-editor>
|
||||||
<span class="gf-form-label query-keyword">OF</span>
|
<span class="gf-form-label query-keyword">OF</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,6 +12,9 @@ import {coreModule, appEvents, contextSrv} from 'app/core/core';
|
|||||||
import {Observable} from 'rxjs/Observable';
|
import {Observable} from 'rxjs/Observable';
|
||||||
import {Subject} from 'rxjs/Subject';
|
import {Subject} from 'rxjs/Subject';
|
||||||
import * as datemath from 'app/core/utils/datemath';
|
import * as datemath from 'app/core/utils/datemath';
|
||||||
|
import * as fileExport from 'app/core/utils/file_export';
|
||||||
|
import * as flatten from 'app/core/utils/flatten';
|
||||||
|
import * as ticks from 'app/core/utils/ticks';
|
||||||
import builtInPlugins from './buit_in_plugins';
|
import builtInPlugins from './buit_in_plugins';
|
||||||
import d3 from 'vendor/d3/d3';
|
import d3 from 'vendor/d3/d3';
|
||||||
|
|
||||||
@ -54,19 +57,24 @@ exposeToPlugin('rxjs/Observable', Observable);
|
|||||||
exposeToPlugin('d3', d3);
|
exposeToPlugin('d3', d3);
|
||||||
|
|
||||||
exposeToPlugin('app/plugins/sdk', sdk);
|
exposeToPlugin('app/plugins/sdk', sdk);
|
||||||
|
|
||||||
exposeToPlugin('app/core/utils/datemath', datemath);
|
exposeToPlugin('app/core/utils/datemath', datemath);
|
||||||
|
exposeToPlugin('app/core/utils/file_export', fileExport);
|
||||||
|
exposeToPlugin('app/core/utils/flatten', flatten);
|
||||||
exposeToPlugin('app/core/utils/kbn', kbn);
|
exposeToPlugin('app/core/utils/kbn', kbn);
|
||||||
|
exposeToPlugin('app/core/utils/ticks', ticks);
|
||||||
|
|
||||||
exposeToPlugin('app/core/config', config);
|
exposeToPlugin('app/core/config', config);
|
||||||
exposeToPlugin('app/core/time_series', TimeSeries);
|
exposeToPlugin('app/core/time_series', TimeSeries);
|
||||||
exposeToPlugin('app/core/time_series2', TimeSeries);
|
exposeToPlugin('app/core/time_series2', TimeSeries);
|
||||||
exposeToPlugin('app/core/table_model', TableModel);
|
exposeToPlugin('app/core/table_model', TableModel);
|
||||||
exposeToPlugin('app/core/app_events', appEvents);
|
exposeToPlugin('app/core/app_events', appEvents);
|
||||||
exposeToPlugin('app/core/core_module', coreModule);
|
exposeToPlugin('app/core/core_module', coreModule);
|
||||||
exposeToPlugin('app/core/core_module', coreModule);
|
|
||||||
exposeToPlugin('app/core/core', {
|
exposeToPlugin('app/core/core', {
|
||||||
coreModule: coreModule,
|
coreModule: coreModule,
|
||||||
appEvents: appEvents,
|
appEvents: appEvents,
|
||||||
contextSrv: contextSrv,
|
contextSrv: contextSrv,
|
||||||
|
__esModule: true
|
||||||
});
|
});
|
||||||
|
|
||||||
import 'vendor/flot/jquery.flot';
|
import 'vendor/flot/jquery.flot';
|
||||||
@ -79,7 +87,11 @@ import 'vendor/flot/jquery.flot.fillbelow';
|
|||||||
import 'vendor/flot/jquery.flot.crosshair';
|
import 'vendor/flot/jquery.flot.crosshair';
|
||||||
import 'vendor/flot/jquery.flot.dashes';
|
import 'vendor/flot/jquery.flot.dashes';
|
||||||
|
|
||||||
for (let flotDep of ['jquery.flot', 'jquery.flot.pie', 'jquery.flot.time']) {
|
const flotDeps = [
|
||||||
|
'jquery.flot', 'jquery.flot.pie', 'jquery.flot.time', 'jquery.flot.fillbelow', 'jquery.flot.crosshair',
|
||||||
|
'jquery.flot.stack', 'jquery.flot.selection', 'jquery.flot.stackpercent', 'jquery.flot.events'
|
||||||
|
];
|
||||||
|
for (let flotDep of flotDeps) {
|
||||||
exposeToPlugin(flotDep, {fakeDep: 1});
|
exposeToPlugin(flotDep, {fakeDep: 1});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<form name="loginForm" class="login-form gf-form-group" ng-hide="disableLoginForm">
|
<form name="loginForm" class="login-form gf-form-group" ng-hide="disableLoginForm">
|
||||||
<div class="gf-form" ng-if="loginMode">
|
<div class="gf-form" ng-if="loginMode">
|
||||||
<span class="gf-form-label width-7">User</span>
|
<span class="gf-form-label width-7">User</span>
|
||||||
<input type="text" name="username" class="gf-form-input max-width-14" required ng-model='formModel.user' placeholder={{loginHint}}>
|
<input type="text" name="username" class="gf-form-input max-width-14" required ng-model='formModel.user' placeholder={{loginHint}} autofocus>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form" ng-if="loginMode">
|
<div class="gf-form" ng-if="loginMode">
|
||||||
<span class="gf-form-label width-7">Password</span>
|
<span class="gf-form-label width-7">Password</span>
|
||||||
|
@ -41,7 +41,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) {
|
|||||||
item.namespace = templateSrv.replace(item.namespace, options.scopedVars);
|
item.namespace = templateSrv.replace(item.namespace, options.scopedVars);
|
||||||
item.metricName = templateSrv.replace(item.metricName, options.scopedVars);
|
item.metricName = templateSrv.replace(item.metricName, options.scopedVars);
|
||||||
item.dimensions = self.convertDimensionFormat(item.dimensions, options.scopeVars);
|
item.dimensions = self.convertDimensionFormat(item.dimensions, options.scopeVars);
|
||||||
item.period = self.getPeriod(item, options);
|
item.period = String(self.getPeriod(item, options)); // use string format for period in graph query, and alerting
|
||||||
|
|
||||||
return _.extend({
|
return _.extend({
|
||||||
refId: item.refId,
|
refId: item.refId,
|
||||||
@ -318,6 +318,8 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) {
|
|||||||
|
|
||||||
return this.getDimensionValues(region, namespace, metricName, 'ServiceName', dimensions).then(function () {
|
return this.getDimensionValues(region, namespace, metricName, 'ServiceName', dimensions).then(function () {
|
||||||
return { status: 'success', message: 'Data source is working' };
|
return { status: 'success', message: 'Data source is working' };
|
||||||
|
}, function (err) {
|
||||||
|
return { status: 'error', message: err.message };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ describe('CloudWatchDatasource', function() {
|
|||||||
InstanceId: 'i-12345678'
|
InstanceId: 'i-12345678'
|
||||||
},
|
},
|
||||||
statistics: ['Average'],
|
statistics: ['Average'],
|
||||||
period: 300
|
period: '300'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@ -109,7 +109,7 @@ describe('CloudWatchDatasource', function() {
|
|||||||
|
|
||||||
ctx.ds.query(query).then(function() {
|
ctx.ds.query(query).then(function() {
|
||||||
var params = requestParams.queries[0];
|
var params = requestParams.queries[0];
|
||||||
expect(params.period).to.be(600);
|
expect(params.period).to.be('600');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
ctx.$rootScope.$apply();
|
ctx.$rootScope.$apply();
|
||||||
|
@ -32,8 +32,8 @@ $white: #fff;
|
|||||||
// -------------------------
|
// -------------------------
|
||||||
$blue: #2AB2E4;
|
$blue: #2AB2E4;
|
||||||
$blue-dark: #3CAAD6;
|
$blue-dark: #3CAAD6;
|
||||||
$green: #28B62C;
|
$green: #3aa655;
|
||||||
$red: #FF4136;
|
$red: #d44939;
|
||||||
$yellow: #FF851B;
|
$yellow: #FF851B;
|
||||||
$orange: #Ff7941;
|
$orange: #Ff7941;
|
||||||
$pink: #E671B8;
|
$pink: #E671B8;
|
||||||
|
@ -125,6 +125,7 @@ $gf-form-margin: 3px;
|
|||||||
// text areas should be scrollable
|
// text areas should be scrollable
|
||||||
@at-root textarea#{&} {
|
@at-root textarea#{&} {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unstyle the caret on `<select>`s in IE10+.
|
// Unstyle the caret on `<select>`s in IE10+.
|
||||||
|
@ -211,15 +211,6 @@
|
|||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
line-height: initial;
|
line-height: initial;
|
||||||
}
|
}
|
||||||
.close {
|
|
||||||
margin-right: 5px;
|
|
||||||
color: $link-color;
|
|
||||||
opacity: 0.7;
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
.editor-row {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotation-tags {
|
.annotation-tags {
|
||||||
|
Reference in New Issue
Block a user