mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into develop
This commit is contained in:
commit
b44daaabf2
@ -1,10 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
test -z "$(gofmt -s -l . | grep -v vendor/src/ | tee /dev/stderr)"
|
|
||||||
if [ $? -gt 0 ]; then
|
|
||||||
echo "Some files aren't formatted, please run 'go fmt ./pkg/...' to format your source code before committing"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
grunt test
|
|
@ -12,15 +12,16 @@
|
|||||||
## New Features
|
## New Features
|
||||||
* **Data Source Proxy**: Add support for whitelisting specified cookies that will be passed through to the data source when proxying data source requests [#5457](https://github.com/grafana/grafana/issues/5457), thanks [@robingustafsson](https://github.com/robingustafsson)
|
* **Data Source Proxy**: Add support for whitelisting specified cookies that will be passed through to the data source when proxying data source requests [#5457](https://github.com/grafana/grafana/issues/5457), thanks [@robingustafsson](https://github.com/robingustafsson)
|
||||||
|
|
||||||
* **Postgres**: modify group by time macro so it can be used in select clause [#9527](https://github.com/grafana/grafana/pull/9527), thanks [@svenklemm](https://github.com/svenklemm)
|
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
* **Sensu**: Send alert message to sensu output [#9551](https://github.com/grafana/grafana/issues/9551), thx [@cjchand](https://github.com/cjchand)
|
* **Sensu**: Send alert message to sensu output [#9551](https://github.com/grafana/grafana/issues/9551), thx [@cjchand](https://github.com/cjchand)
|
||||||
|
|
||||||
# 4.6.0-beta3 (unreleased)
|
# 4.6.0-beta3 (2017-10-23)
|
||||||
|
|
||||||
|
## Fixes
|
||||||
* **Prometheus**: Fix for browser crash for short time ranges. [#9575](https://github.com/grafana/grafana/issues/9575)
|
* **Prometheus**: Fix for browser crash for short time ranges. [#9575](https://github.com/grafana/grafana/issues/9575)
|
||||||
* **Heatmap**: Fix for y-axis not showing. [#9576](https://github.com/grafana/grafana/issues/9576)
|
* **Heatmap**: Fix for y-axis not showing. [#9576](https://github.com/grafana/grafana/issues/9576)
|
||||||
* **Save to file**: Fix for save to file in export modal. [#9586](https://github.com/grafana/grafana/issues/9586)
|
* **Save to file**: Fix for save to file in export modal. [#9586](https://github.com/grafana/grafana/issues/9586)
|
||||||
|
* **Postgres**: modify group by time macro so it can be used in select clause [#9527](https://github.com/grafana/grafana/pull/9527), thanks [@svenklemm](https://github.com/svenklemm)
|
||||||
|
|
||||||
# 4.6.0-beta2 (2017-10-17)
|
# 4.6.0-beta2 (2017-10-17)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
machine:
|
machine:
|
||||||
node:
|
node:
|
||||||
version: 6.9.2
|
version: 6.11.4
|
||||||
python:
|
python:
|
||||||
version: 2.7.3
|
version: 2.7.3
|
||||||
services:
|
services:
|
||||||
@ -30,10 +30,10 @@ dependencies:
|
|||||||
- sudo apt-get update; sudo apt-get install rpm; sudo apt-get install expect
|
- sudo apt-get update; sudo apt-get install rpm; sudo apt-get install expect
|
||||||
- ./scripts/build/build_container.sh
|
- ./scripts/build/build_container.sh
|
||||||
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
- bash scripts/circle-test.sh
|
- bash scripts/circle-test-frontend.sh
|
||||||
|
- bash scripts/circle-test-backend.sh
|
||||||
|
|
||||||
deployment:
|
deployment:
|
||||||
gh_branch:
|
gh_branch:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
+++
|
+++
|
||||||
title = "Data Sources"
|
title = "Data Sources"
|
||||||
type = "docs"
|
type = "docs"
|
||||||
|
aliases = ["/datasources/overview/"]
|
||||||
[menu.docs]
|
[menu.docs]
|
||||||
name = "Data Sources"
|
name = "Data Sources"
|
||||||
identifier = "datasources"
|
identifier = "datasources"
|
||||||
|
@ -68,6 +68,7 @@ This makes exploring and filtering Prometheus data much easier.
|
|||||||
* **Opsgenie**: Use their latest API instead of old version [#9399](https://github.com/grafana/grafana/pull/9399), thx [@cglrkn](https://github.com/cglrkn)
|
* **Opsgenie**: Use their latest API instead of old version [#9399](https://github.com/grafana/grafana/pull/9399), thx [@cglrkn](https://github.com/cglrkn)
|
||||||
* **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)
|
||||||
|
* **Postgres**: modify group by time macro so it can be used in select clause [#9527](https://github.com/grafana/grafana/pull/9527), thanks [@svenklemm](https://github.com/svenklemm)
|
||||||
|
|
||||||
### Tech
|
### Tech
|
||||||
* **Go**: Grafana is now built using golang 1.9
|
* **Go**: Grafana is now built using golang 1.9
|
||||||
|
4
docs/yarn.lock
Normal file
4
docs/yarn.lock
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
@ -4,7 +4,7 @@ module.exports = {
|
|||||||
"transform": {
|
"transform": {
|
||||||
"^.+\\.tsx?$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
|
"^.+\\.tsx?$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
|
||||||
},
|
},
|
||||||
"moduleDirectories": ["<rootDir>node_modules", "<rootDir>/public"],
|
"moduleDirectories": ["<rootDir>/node_modules", "<rootDir>/public"],
|
||||||
"roots": [
|
"roots": [
|
||||||
"<rootDir>/public"
|
"<rootDir>/public"
|
||||||
],
|
],
|
||||||
|
21
package.json
21
package.json
@ -95,14 +95,15 @@
|
|||||||
"zone.js": "^0.7.2"
|
"zone.js": "^0.7.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "./node_modules/.bin/webpack --progress --colors --config scripts/webpack/webpack.dev.js",
|
"dev": "node ./node_modules/.bin/webpack --progress --colors --config scripts/webpack/webpack.dev.js",
|
||||||
"watch": "./node_modules/.bin/webpack --progress --colors --watch --config scripts/webpack/webpack.dev.js",
|
"watch": "node ./node_modules/.bin/webpack --progress --colors --watch --config scripts/webpack/webpack.dev.js",
|
||||||
"build": "./node_modules/.bin/grunt build",
|
"build": "node ./node_modules/.bin/grunt build",
|
||||||
"test": "./node_modules/.bin/grunt test",
|
"test": "node ./node_modules/.bin/grunt test",
|
||||||
"test-ci": "./node_modules/.bin/grunt test --coverage=true",
|
"test:coverage": "node ./node_modules/.bin/grunt test --coverage=true",
|
||||||
"lint": "./node_modules/.bin/tslint -c tslint.json --project tsconfig.json --type-check",
|
"lint": "node ./node_modules/.bin/tslint -c tslint.json --project tsconfig.json --type-check",
|
||||||
"karma": "./node_modules/grunt-cli/bin/grunt karma:dev",
|
"karma": "node ./node_modules/grunt-cli/bin/grunt karma:dev",
|
||||||
"jest": "./node_modules/jest-cli/bin/jest --notify --watch"
|
"jest": "node ./node_modules/jest-cli/bin/jest.js --notify --watch",
|
||||||
|
"precommit": "node ./node_modules/grunt-cli/bin/grunt precommit"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -133,6 +134,8 @@
|
|||||||
"rxjs": "^5.4.3",
|
"rxjs": "^5.4.3",
|
||||||
"tether": "^1.4.0",
|
"tether": "^1.4.0",
|
||||||
"tether-drop": "https://github.com/torkelo/drop",
|
"tether-drop": "https://github.com/torkelo/drop",
|
||||||
"tinycolor2": "^1.4.1"
|
"tinycolor2": "^1.4.1",
|
||||||
|
"d3": "^4.11.0",
|
||||||
|
"d3-scale-chromatic": "^1.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,9 +188,8 @@ func (hs *HttpServer) metricsEndpoint(ctx *macaron.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{
|
promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}).
|
||||||
DisableCompression: true,
|
ServeHTTP(ctx.Resp, ctx.Req.Request)
|
||||||
}).ServeHTTP(ctx.Resp, ctx.Req.Request)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hs *HttpServer) healthHandler(ctx *macaron.Context) {
|
func (hs *HttpServer) healthHandler(ctx *macaron.Context) {
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/go-stack/stack"
|
"github.com/go-stack/stack"
|
||||||
"github.com/inconshreveable/log15"
|
"github.com/inconshreveable/log15"
|
||||||
"github.com/inconshreveable/log15/term"
|
isatty "github.com/mattn/go-isatty"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
@ -157,7 +157,7 @@ func getFilters(filterStrArray []string) map[string]log15.Lvl {
|
|||||||
func getLogFormat(format string) log15.Format {
|
func getLogFormat(format string) log15.Format {
|
||||||
switch format {
|
switch format {
|
||||||
case "console":
|
case "console":
|
||||||
if term.IsTty(os.Stdout.Fd()) {
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
return log15.TerminalFormat()
|
return log15.TerminalFormat()
|
||||||
}
|
}
|
||||||
return log15.LogfmtFormat()
|
return log15.LogfmtFormat()
|
||||||
|
@ -21,6 +21,10 @@ func Gziper() macaron.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(requestPath, "/metrics") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Invoke(macaronGziper)
|
ctx.Invoke(macaronGziper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func getPluginLogoUrl(pluginType, path, baseUrl string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fp *FrontendPluginBase) setPathsBasedOnApp(app *AppPlugin) {
|
func (fp *FrontendPluginBase) setPathsBasedOnApp(app *AppPlugin) {
|
||||||
appSubPath := strings.Replace(fp.PluginDir, app.PluginDir, "", 1)
|
appSubPath := strings.Replace(strings.Replace(fp.PluginDir, app.PluginDir, "", 1), "\\", "/", 1)
|
||||||
fp.IncludedInAppId = app.Id
|
fp.IncludedInAppId = app.Id
|
||||||
fp.BaseUrl = app.BaseUrl
|
fp.BaseUrl = app.BaseUrl
|
||||||
|
|
||||||
|
34
pkg/plugins/frontend_plugin_test.go
Normal file
34
pkg/plugins/frontend_plugin_test.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFrontendPlugin(t *testing.T) {
|
||||||
|
|
||||||
|
Convey("When setting paths based on App on Windows", t, func() {
|
||||||
|
setting.StaticRootPath = "c:\\grafana\\public"
|
||||||
|
|
||||||
|
fp := &FrontendPluginBase{
|
||||||
|
PluginBase: PluginBase{
|
||||||
|
PluginDir: "c:\\grafana\\public\\app\\plugins\\app\\testdata\\datasource",
|
||||||
|
BaseUrl: "fpbase",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app := &AppPlugin{
|
||||||
|
FrontendPluginBase: FrontendPluginBase{
|
||||||
|
PluginBase: PluginBase{
|
||||||
|
PluginDir: "c:\\grafana\\public\\app\\plugins\\app\\testdata",
|
||||||
|
Id: "testdata",
|
||||||
|
BaseUrl: "public/app/plugins/app/testdata",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fp.setPathsBasedOnApp(app)
|
||||||
|
|
||||||
|
So(fp.Module, ShouldEqual, "app/plugins/app/testdata/datasource/module")
|
||||||
|
})
|
||||||
|
}
|
@ -27,7 +27,7 @@ _.move = function (array, fromIndex, toIndex) {
|
|||||||
return array;
|
return array;
|
||||||
};
|
};
|
||||||
|
|
||||||
import {coreModule} from './core/core';
|
import {coreModule, registerAngularDirectives} from './core/core';
|
||||||
|
|
||||||
export class GrafanaApp {
|
export class GrafanaApp {
|
||||||
registerFunctions: any;
|
registerFunctions: any;
|
||||||
@ -109,6 +109,9 @@ export class GrafanaApp {
|
|||||||
// makes it possible to add dynamic stuff
|
// makes it possible to add dynamic stuff
|
||||||
this.useModule(coreModule);
|
this.useModule(coreModule);
|
||||||
|
|
||||||
|
// register react angular wrappers
|
||||||
|
registerAngularDirectives();
|
||||||
|
|
||||||
var preBootRequires = [System.import('app/features/all')];
|
var preBootRequires = [System.import('app/features/all')];
|
||||||
|
|
||||||
Promise.all(preBootRequires).then(() => {
|
Promise.all(preBootRequires).then(() => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||||
import { PasswordStrength } from './ui/PasswordStrength';
|
import { PasswordStrength } from './components/PasswordStrength';
|
||||||
|
|
||||||
export function registerAngularDirectives() {
|
export function registerAngularDirectives() {
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ export interface IProps {
|
|||||||
onColorSelect: (c: string) => void;
|
onColorSelect: (c: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GfColorPalette extends React.Component<IProps, any> {
|
export class ColorPalette extends React.Component<IProps, any> {
|
||||||
paletteColors: string[];
|
paletteColors: string[];
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import { GfColorPalette } from './ColorPalette';
|
import { ColorPalette } from './ColorPalette';
|
||||||
import { GfSpectrumPicker } from './SpectrumPicker';
|
import { SpectrumPicker } from './SpectrumPicker';
|
||||||
|
|
||||||
const DEFAULT_COLOR = '#000000';
|
const DEFAULT_COLOR = '#000000';
|
||||||
|
|
||||||
@ -82,12 +82,12 @@ export class ColorPickerPopover extends React.Component<IProps, any> {
|
|||||||
render() {
|
render() {
|
||||||
const paletteTab = (
|
const paletteTab = (
|
||||||
<div id="palette">
|
<div id="palette">
|
||||||
<GfColorPalette color={this.state.color} onColorSelect={this.sampleColorSelected.bind(this)} />
|
<ColorPalette color={this.state.color} onColorSelect={this.sampleColorSelected.bind(this)} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
const spectrumTab = (
|
const spectrumTab = (
|
||||||
<div id="spectrum">
|
<div id="spectrum">
|
||||||
<GfSpectrumPicker color={this.state.color} onColorSelect={this.spectrumColorSelected.bind(this)} options={{}} />
|
<SpectrumPicker color={this.state.color} onColorSelect={this.spectrumColorSelected.bind(this)} options={{}} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
const currentTab = this.state.tab === 'palette' ? paletteTab : spectrumTab;
|
const currentTab = this.state.tab === 'palette' ? paletteTab : spectrumTab;
|
||||||
|
@ -9,7 +9,7 @@ export interface IProps {
|
|||||||
onColorSelect: (c: string) => void;
|
onColorSelect: (c: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GfSpectrumPicker extends React.Component<IProps, any> {
|
export class SpectrumPicker extends React.Component<IProps, any> {
|
||||||
elem: any;
|
elem: any;
|
||||||
isMoving: boolean;
|
isMoving: boolean;
|
||||||
|
|
||||||
|
@ -53,10 +53,9 @@ import {orgSwitcher} from './components/org_switcher';
|
|||||||
import {profiler} from './profiler';
|
import {profiler} from './profiler';
|
||||||
import {registerAngularDirectives} from './angular_wrappers';
|
import {registerAngularDirectives} from './angular_wrappers';
|
||||||
|
|
||||||
registerAngularDirectives();
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
profiler,
|
profiler,
|
||||||
|
registerAngularDirectives,
|
||||||
arrayJoin,
|
arrayJoin,
|
||||||
coreModule,
|
coreModule,
|
||||||
grafanaAppDirective,
|
grafanaAppDirective,
|
||||||
|
10
public/app/core/specs/ColorPalette.jest.tsx
Normal file
10
public/app/core/specs/ColorPalette.jest.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
import { ColorPalette } from '../components/colorpicker/ColorPalette';
|
||||||
|
|
||||||
|
describe('CollorPalette', () => {
|
||||||
|
it('renders correctly', () => {
|
||||||
|
const tree = renderer.create(<ColorPalette color="#EAB839" onColorSelect={jest.fn()} />).toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
|
|
||||||
import {PasswordStrength} from '../ui/PasswordStrength';
|
import {PasswordStrength} from '../components/PasswordStrength';
|
||||||
|
|
||||||
describe('PasswordStrength', () => {
|
describe('PasswordStrength', () => {
|
||||||
|
|
||||||
|
628
public/app/core/specs/__snapshots__/ColorPalette.jest.tsx.snap
Normal file
628
public/app/core/specs/__snapshots__/ColorPalette.jest.tsx.snap
Normal file
@ -0,0 +1,628 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`CollorPalette renders correctly 1`] = `
|
||||||
|
<div
|
||||||
|
className="graph-legend-popover"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
className="m-b-0"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#890f02",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#58140c",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#99440a",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#c15c17",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#967302",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#cca300",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#3f6833",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#2f575e",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#64b0c8",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#052b51",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#0a50a1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#584477",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#3f2b5b",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#511749",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#e24d42",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#bf1b00",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#ef843c",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#f4d598",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#e5ac0e",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#9ac48a",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#508642",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#6ed0e0",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#65c5db",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#0a437c",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#447ebc",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#614d93",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#d683ce",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#6d1f62",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#ea6460",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#e0752d",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#f9934e",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#fceaca",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle-o"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#eab839",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#b7dbab",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#629e51",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#70dbed",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#82b5d8",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#1f78c1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#aea2e0",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#705da0",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#e5a8e2",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#962d82",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#f29191",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#fce2de",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#f9ba8f",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#f9e2d2",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#f2c96d",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#e0f9d7",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#7eb26d",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#cffaff",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#badff4",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#5195ce",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#dedaf7",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#806eb7",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#f9d9f9",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
<i
|
||||||
|
className="pointer fa fa-circle"
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"color": "#ba43a9",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</i>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -1,5 +1,3 @@
|
|||||||
///<reference path="../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
@ -23,6 +21,7 @@ export default class TimeSeries {
|
|||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
alias: string;
|
alias: string;
|
||||||
|
aliasEscaped: string;
|
||||||
color: string;
|
color: string;
|
||||||
valueFormater: any;
|
valueFormater: any;
|
||||||
stats: any;
|
stats: any;
|
||||||
@ -52,6 +51,7 @@ export default class TimeSeries {
|
|||||||
this.label = opts.alias;
|
this.label = opts.alias;
|
||||||
this.id = opts.alias;
|
this.id = opts.alias;
|
||||||
this.alias = opts.alias;
|
this.alias = opts.alias;
|
||||||
|
this.aliasEscaped = _.escape(opts.alias);
|
||||||
this.color = opts.color;
|
this.color = opts.color;
|
||||||
this.valueFormater = kbn.valueFormats.none;
|
this.valueFormater = kbn.valueFormats.none;
|
||||||
this.stats = {};
|
this.stats = {};
|
||||||
|
@ -3,6 +3,7 @@ import './editor_ctrl';
|
|||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
|
import {makeRegions, dedupAnnotations} from './events_processing';
|
||||||
|
|
||||||
export class AnnotationsSrv {
|
export class AnnotationsSrv {
|
||||||
globalAnnotationsPromise: any;
|
globalAnnotationsPromise: any;
|
||||||
@ -161,83 +162,4 @@ export class AnnotationsSrv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This function converts annotation events into set
|
|
||||||
* of single events and regions (event consist of two)
|
|
||||||
* @param annotations
|
|
||||||
* @param options
|
|
||||||
*/
|
|
||||||
function makeRegions(annotations, options) {
|
|
||||||
let [regionEvents, singleEvents] = _.partition(annotations, 'regionId');
|
|
||||||
let regions = getRegions(regionEvents, options.range);
|
|
||||||
annotations = _.concat(regions, singleEvents);
|
|
||||||
return annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRegions(events, range) {
|
|
||||||
let region_events = _.filter(events, event => {
|
|
||||||
return event.regionId;
|
|
||||||
});
|
|
||||||
let regions = _.groupBy(region_events, 'regionId');
|
|
||||||
regions = _.compact(
|
|
||||||
_.map(regions, region_events => {
|
|
||||||
let region_obj = _.head(region_events);
|
|
||||||
if (region_events && region_events.length > 1) {
|
|
||||||
region_obj.timeEnd = region_events[1].time;
|
|
||||||
region_obj.isRegion = true;
|
|
||||||
return region_obj;
|
|
||||||
} else {
|
|
||||||
if (region_events && region_events.length) {
|
|
||||||
// Don't change proper region object
|
|
||||||
if (!region_obj.time || !region_obj.timeEnd) {
|
|
||||||
// This is cut region
|
|
||||||
if (isStartOfRegion(region_obj)) {
|
|
||||||
region_obj.timeEnd = range.to.valueOf() - 1;
|
|
||||||
} else {
|
|
||||||
// Start time = null
|
|
||||||
region_obj.timeEnd = region_obj.time;
|
|
||||||
region_obj.time = range.from.valueOf() + 1;
|
|
||||||
}
|
|
||||||
region_obj.isRegion = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return region_obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
return regions;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isStartOfRegion(event): boolean {
|
|
||||||
return event.id && event.id === event.regionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function dedupAnnotations(annotations) {
|
|
||||||
let dedup = [];
|
|
||||||
|
|
||||||
// Split events by annotationId property existance
|
|
||||||
let events = _.partition(annotations, 'id');
|
|
||||||
|
|
||||||
let eventsById = _.groupBy(events[0], 'id');
|
|
||||||
dedup = _.map(eventsById, eventGroup => {
|
|
||||||
if (eventGroup.length > 1 && !_.every(eventGroup, isPanelAlert)) {
|
|
||||||
// Get first non-panel alert
|
|
||||||
return _.find(eventGroup, event => {
|
|
||||||
return event.eventType !== 'panel-alert';
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return _.head(eventGroup);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dedup = _.concat(dedup, events[1]);
|
|
||||||
return dedup;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPanelAlert(event) {
|
|
||||||
return event.eventType === 'panel-alert';
|
|
||||||
}
|
|
||||||
|
|
||||||
coreModule.service('annotationsSrv', AnnotationsSrv);
|
coreModule.service('annotationsSrv', AnnotationsSrv);
|
||||||
|
80
public/app/features/annotations/events_processing.ts
Normal file
80
public/app/features/annotations/events_processing.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function converts annotation events into set
|
||||||
|
* of single events and regions (event consist of two)
|
||||||
|
* @param annotations
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
export function makeRegions(annotations, options) {
|
||||||
|
let [regionEvents, singleEvents] = _.partition(annotations, 'regionId');
|
||||||
|
let regions = getRegions(regionEvents, options.range);
|
||||||
|
annotations = _.concat(regions, singleEvents);
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRegions(events, range) {
|
||||||
|
let region_events = _.filter(events, event => {
|
||||||
|
return event.regionId;
|
||||||
|
});
|
||||||
|
let regions = _.groupBy(region_events, 'regionId');
|
||||||
|
regions = _.compact(
|
||||||
|
_.map(regions, region_events => {
|
||||||
|
let region_obj = _.head(region_events);
|
||||||
|
if (region_events && region_events.length > 1) {
|
||||||
|
region_obj.timeEnd = region_events[1].time;
|
||||||
|
region_obj.isRegion = true;
|
||||||
|
return region_obj;
|
||||||
|
} else {
|
||||||
|
if (region_events && region_events.length) {
|
||||||
|
// Don't change proper region object
|
||||||
|
if (!region_obj.time || !region_obj.timeEnd) {
|
||||||
|
// This is cut region
|
||||||
|
if (isStartOfRegion(region_obj)) {
|
||||||
|
region_obj.timeEnd = range.to.valueOf() - 1;
|
||||||
|
} else {
|
||||||
|
// Start time = null
|
||||||
|
region_obj.timeEnd = region_obj.time;
|
||||||
|
region_obj.time = range.from.valueOf() + 1;
|
||||||
|
}
|
||||||
|
region_obj.isRegion = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return region_obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStartOfRegion(event): boolean {
|
||||||
|
return event.id && event.id === event.regionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dedupAnnotations(annotations) {
|
||||||
|
let dedup = [];
|
||||||
|
|
||||||
|
// Split events by annotationId property existance
|
||||||
|
let events = _.partition(annotations, 'id');
|
||||||
|
|
||||||
|
let eventsById = _.groupBy(events[0], 'id');
|
||||||
|
dedup = _.map(eventsById, eventGroup => {
|
||||||
|
if (eventGroup.length > 1 && !_.every(eventGroup, isPanelAlert)) {
|
||||||
|
// Get first non-panel alert
|
||||||
|
return _.find(eventGroup, event => {
|
||||||
|
return event.eventType !== 'panel-alert';
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return _.head(eventGroup);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dedup = _.concat(dedup, events[1]);
|
||||||
|
return dedup;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPanelAlert(event) {
|
||||||
|
return event.eventType === 'panel-alert';
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
import {makeRegions, dedupAnnotations} from '../events_processing';
|
||||||
|
|
||||||
|
describe('Annotations', () => {
|
||||||
|
|
||||||
|
describe('Annotations regions', () => {
|
||||||
|
let testAnnotations: any[];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testAnnotations = [
|
||||||
|
{id: 1, time: 1},
|
||||||
|
{id: 2, time: 2},
|
||||||
|
{id: 3, time: 3, regionId: 3},
|
||||||
|
{id: 4, time: 5, regionId: 3},
|
||||||
|
{id: 5, time: 4, regionId: 5},
|
||||||
|
{id: 6, time: 8, regionId: 5}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert single region events to regions', () => {
|
||||||
|
const range = {from: 0, to: 10};
|
||||||
|
const expectedAnnotations = [
|
||||||
|
{id: 3, regionId: 3, isRegion: true, time: 3, timeEnd: 5},
|
||||||
|
{id: 5, regionId: 5, isRegion: true, time: 4, timeEnd: 8},
|
||||||
|
{id: 1, time: 1},
|
||||||
|
{id: 2, time: 2}
|
||||||
|
];
|
||||||
|
|
||||||
|
let regions = makeRegions(testAnnotations, {range: range});
|
||||||
|
expect(regions).toEqual(expectedAnnotations);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cut regions to current time range', () => {
|
||||||
|
const range = {from: 0, to: 8};
|
||||||
|
testAnnotations = [
|
||||||
|
{id: 5, time: 4, regionId: 5}
|
||||||
|
];
|
||||||
|
const expectedAnnotations = [
|
||||||
|
{id: 5, regionId: 5, isRegion: true, time: 4, timeEnd: 7}
|
||||||
|
];
|
||||||
|
|
||||||
|
let regions = makeRegions(testAnnotations, {range: range});
|
||||||
|
expect(regions).toEqual(expectedAnnotations);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Annotations deduplication', () => {
|
||||||
|
it('should remove duplicated annotations', () => {
|
||||||
|
const testAnnotations = [
|
||||||
|
{id: 1, time: 1},
|
||||||
|
{id: 2, time: 2},
|
||||||
|
{id: 2, time: 2},
|
||||||
|
{id: 5, time: 5},
|
||||||
|
{id: 5, time: 5}
|
||||||
|
];
|
||||||
|
const expectedAnnotations = [
|
||||||
|
{id: 1, time: 1},
|
||||||
|
{id: 2, time: 2},
|
||||||
|
{id: 5, time: 5}
|
||||||
|
];
|
||||||
|
|
||||||
|
let deduplicated = dedupAnnotations(testAnnotations);
|
||||||
|
expect(deduplicated).toEqual(expectedAnnotations);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should leave non "panel-alert" event if present', () => {
|
||||||
|
const testAnnotations = [
|
||||||
|
{id: 1, time: 1},
|
||||||
|
{id: 2, time: 2},
|
||||||
|
{id: 2, time: 2, eventType: 'panel-alert'},
|
||||||
|
{id: 5, time: 5},
|
||||||
|
{id: 5, time: 5}
|
||||||
|
];
|
||||||
|
const expectedAnnotations = [
|
||||||
|
{id: 1, time: 1},
|
||||||
|
{id: 2, time: 2},
|
||||||
|
{id: 5, time: 5}
|
||||||
|
];
|
||||||
|
|
||||||
|
let deduplicated = dedupAnnotations(testAnnotations);
|
||||||
|
expect(deduplicated).toEqual(expectedAnnotations);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -7,6 +7,7 @@ import {coreModule, JsonExplorer} from 'app/core/core';
|
|||||||
const template = `
|
const template = `
|
||||||
<div class="query-troubleshooter" ng-if="ctrl.isOpen">
|
<div class="query-troubleshooter" ng-if="ctrl.isOpen">
|
||||||
<div class="query-troubleshooter__header">
|
<div class="query-troubleshooter__header">
|
||||||
|
<a class="pointer" ng-click="ctrl.toggleMocking()">Mock Response</a>
|
||||||
<a class="pointer" ng-click="ctrl.toggleExpand()" ng-hide="ctrl.allNodesExpanded">
|
<a class="pointer" ng-click="ctrl.toggleExpand()" ng-hide="ctrl.allNodesExpanded">
|
||||||
<i class="fa fa-plus-square-o"></i> Expand All
|
<i class="fa fa-plus-square-o"></i> Expand All
|
||||||
</a>
|
</a>
|
||||||
@ -15,10 +16,15 @@ const template = `
|
|||||||
</a>
|
</a>
|
||||||
<a class="pointer" clipboard-button="ctrl.getClipboardText()"><i class="fa fa-clipboard"></i> Copy to Clipboard</a>
|
<a class="pointer" clipboard-button="ctrl.getClipboardText()"><i class="fa fa-clipboard"></i> Copy to Clipboard</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="query-troubleshooter__body">
|
<div class="query-troubleshooter__body" ng-hide="ctrl.isMocking">
|
||||||
<i class="fa fa-spinner fa-spin" ng-show="ctrl.isLoading"></i>
|
<i class="fa fa-spinner fa-spin" ng-show="ctrl.isLoading"></i>
|
||||||
<div class="query-troubleshooter-json"></div>
|
<div class="query-troubleshooter-json"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="query-troubleshooter__body" ng-show="ctrl.isMocking">
|
||||||
|
<div class="gf-form p-l-1 gf-form--v-stretch">
|
||||||
|
<textarea class="gf-form-input" style="width: 95%" rows="10" ng-model="ctrl.mockedResponse" placeholder="JSON"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -32,6 +38,8 @@ export class QueryTroubleshooterCtrl {
|
|||||||
onRequestResponseEventListener: any;
|
onRequestResponseEventListener: any;
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
allNodesExpanded: boolean;
|
allNodesExpanded: boolean;
|
||||||
|
isMocking: boolean;
|
||||||
|
mockedResponse: string;
|
||||||
jsonExplorer: JsonExplorer;
|
jsonExplorer: JsonExplorer;
|
||||||
|
|
||||||
/** @ngInject **/
|
/** @ngInject **/
|
||||||
@ -51,6 +59,10 @@ export class QueryTroubleshooterCtrl {
|
|||||||
appEvents.off('ds-request-error', this.onRequestErrorEventListener);
|
appEvents.off('ds-request-error', this.onRequestErrorEventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleMocking() {
|
||||||
|
this.isMocking = !this.isMocking;
|
||||||
|
}
|
||||||
|
|
||||||
onRequestError(err) {
|
onRequestError(err) {
|
||||||
// ignore if closed
|
// ignore if closed
|
||||||
if (!this.isOpen) {
|
if (!this.isOpen) {
|
||||||
@ -76,12 +88,29 @@ export class QueryTroubleshooterCtrl {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleMocking(data) {
|
||||||
|
var mockedData;
|
||||||
|
try {
|
||||||
|
mockedData = JSON.parse(this.mockedResponse);
|
||||||
|
} catch (err) {
|
||||||
|
appEvents.emit('alert-error', ['Failed to parse mocked response']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.data = mockedData;
|
||||||
|
}
|
||||||
|
|
||||||
onRequestResponse(data) {
|
onRequestResponse(data) {
|
||||||
// ignore if closed
|
// ignore if closed
|
||||||
if (!this.isOpen) {
|
if (!this.isOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isMocking) {
|
||||||
|
this.handleMocking(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
data = _.cloneDeep(data);
|
data = _.cloneDeep(data);
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import * as flatten from 'app/core/utils/flatten';
|
|||||||
import * as ticks from 'app/core/utils/ticks';
|
import * as ticks from 'app/core/utils/ticks';
|
||||||
import {impressions} from 'app/features/dashboard/impression_store';
|
import {impressions} from 'app/features/dashboard/impression_store';
|
||||||
import builtInPlugins from './built_in_plugins';
|
import builtInPlugins from './built_in_plugins';
|
||||||
import d3 from 'vendor/d3/d3';
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
// rxjs
|
// rxjs
|
||||||
import {Observable} from 'rxjs/Observable';
|
import {Observable} from 'rxjs/Observable';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import {describe, it, expect} from 'test/lib/common';
|
|
||||||
|
|
||||||
import {containsVariable, assignModelProperties} from '../variable';
|
import {containsVariable, assignModelProperties} from '../variable';
|
||||||
|
|
||||||
describe('containsVariable', function() {
|
describe('containsVariable', function() {
|
||||||
@ -8,37 +6,37 @@ describe('containsVariable', function() {
|
|||||||
|
|
||||||
it('should find it with $var syntax', function() {
|
it('should find it with $var syntax', function() {
|
||||||
var contains = containsVariable('this.$test.filters', 'test');
|
var contains = containsVariable('this.$test.filters', 'test');
|
||||||
expect(contains).to.be(true);
|
expect(contains).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not find it if only part matches with $var syntax', function() {
|
it('should not find it if only part matches with $var syntax', function() {
|
||||||
var contains = containsVariable('this.$serverDomain.filters', 'server');
|
var contains = containsVariable('this.$serverDomain.filters', 'server');
|
||||||
expect(contains).to.be(false);
|
expect(contains).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find it if it ends with variable and passing multiple test strings', function() {
|
it('should find it if it ends with variable and passing multiple test strings', function() {
|
||||||
var contains = containsVariable('show field keys from $pgmetric', 'test string2', 'pgmetric');
|
var contains = containsVariable('show field keys from $pgmetric', 'test string2', 'pgmetric');
|
||||||
expect(contains).to.be(true);
|
expect(contains).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find it with [[var]] syntax', function() {
|
it('should find it with [[var]] syntax', function() {
|
||||||
var contains = containsVariable('this.[[test]].filters', 'test');
|
var contains = containsVariable('this.[[test]].filters', 'test');
|
||||||
expect(contains).to.be(true);
|
expect(contains).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find it when part of segment', function() {
|
it('should find it when part of segment', function() {
|
||||||
var contains = containsVariable('metrics.$env.$group-*', 'group');
|
var contains = containsVariable('metrics.$env.$group-*', 'group');
|
||||||
expect(contains).to.be(true);
|
expect(contains).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find it its the only thing', function() {
|
it('should find it its the only thing', function() {
|
||||||
var contains = containsVariable('$env', 'env');
|
var contains = containsVariable('$env', 'env');
|
||||||
expect(contains).to.be(true);
|
expect(contains).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to pass in multiple test strings', function() {
|
it('should be able to pass in multiple test strings', function() {
|
||||||
var contains = containsVariable('asd','asd2.$env', 'env');
|
var contains = containsVariable('asd','asd2.$env', 'env');
|
||||||
expect(contains).to.be(true);
|
expect(contains).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -50,14 +48,14 @@ describe('assignModelProperties', function() {
|
|||||||
it('only set properties defined in defaults', function() {
|
it('only set properties defined in defaults', function() {
|
||||||
var target: any = {test: 'asd'};
|
var target: any = {test: 'asd'};
|
||||||
assignModelProperties(target, {propA: 1, propB: 2}, {propB: 0});
|
assignModelProperties(target, {propA: 1, propB: 2}, {propB: 0});
|
||||||
expect(target.propB).to.be(2);
|
expect(target.propB).toBe(2);
|
||||||
expect(target.test).to.be('asd');
|
expect(target.test).toBe('asd');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('use default value if not found on source', function() {
|
it('use default value if not found on source', function() {
|
||||||
var target: any = {test: 'asd'};
|
var target: any = {test: 'asd'};
|
||||||
assignModelProperties(target, {propA: 1, propB: 2}, {propC: 10});
|
assignModelProperties(target, {propA: 1, propB: 2}, {propC: 10});
|
||||||
expect(target.propC).to.be(10);
|
expect(target.propC).toBe(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
@ -1,8 +1,6 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import TimeSeries from 'app/core/time_series2';
|
import TimeSeries from 'app/core/time_series2';
|
||||||
import {colors} from 'app/core/core';
|
import colors from 'app/core/utils/colors';
|
||||||
|
|
||||||
export class DataProcessor {
|
export class DataProcessor {
|
||||||
|
|
||||||
|
@ -381,7 +381,7 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) {
|
|||||||
var haveSortOrder = sortOrder !== null || sortOrder !== undefined;
|
var haveSortOrder = sortOrder !== null || sortOrder !== undefined;
|
||||||
|
|
||||||
if (panel.stack && haveSortBy && haveSortOrder) {
|
if (panel.stack && haveSortBy && haveSortOrder) {
|
||||||
var desc = desc = panel.legend.sortDesc === true ? 1 : -1;
|
var desc = desc = panel.legend.sortDesc === true ? -1 : 1;
|
||||||
series.sort((x, y) => {
|
series.sort((x, y) => {
|
||||||
if (x.stats[sortBy] > y.stats[sortBy]) {
|
if (x.stats[sortBy] > y.stats[sortBy]) {
|
||||||
return 1 * desc;
|
return 1 * desc;
|
||||||
|
@ -127,7 +127,7 @@ function ($, core) {
|
|||||||
value: value,
|
value: value,
|
||||||
hoverIndex: hoverIndex,
|
hoverIndex: hoverIndex,
|
||||||
color: series.color,
|
color: series.color,
|
||||||
label: series.label,
|
label: series.aliasEscaped,
|
||||||
time: pointTime,
|
time: pointTime,
|
||||||
distance: hoverDistance,
|
distance: hoverDistance,
|
||||||
index: i
|
index: i
|
||||||
@ -264,7 +264,7 @@ function ($, core) {
|
|||||||
else if (item) {
|
else if (item) {
|
||||||
series = seriesList[item.seriesIndex];
|
series = seriesList[item.seriesIndex];
|
||||||
group = '<div class="graph-tooltip-list-item"><div class="graph-tooltip-series-name">';
|
group = '<div class="graph-tooltip-list-item"><div class="graph-tooltip-series-name">';
|
||||||
group += '<i class="fa fa-minus" style="color:' + item.series.color +';"></i> ' + series.label + ':</div>';
|
group += '<i class="fa fa-minus" style="color:' + item.series.color +';"></i> ' + series.aliasEscaped + ':</div>';
|
||||||
|
|
||||||
if (panel.stack && panel.tooltip.value_type === 'individual') {
|
if (panel.stack && panel.tooltip.value_type === 'individual') {
|
||||||
value = item.datapoint[1] - item.datapoint[2];
|
value = item.datapoint[1] - item.datapoint[2];
|
||||||
|
@ -169,7 +169,7 @@ function (angular, _, $) {
|
|||||||
html += '<i class="fa fa-minus pointer" style="color:' + series.color + '"></i>';
|
html += '<i class="fa fa-minus pointer" style="color:' + series.color + '"></i>';
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
html += '<a class="graph-legend-alias pointer" title="' + _.escape(series.label) + '">' + _.escape(series.label) + '</a>';
|
html += '<a class="graph-legend-alias pointer" title="' + series.aliasEscaped + '">' + series.aliasEscaped + '</a>';
|
||||||
|
|
||||||
if (panel.legend.values) {
|
if (panel.legend.values) {
|
||||||
var avg = series.formatValue(series.stats.avg);
|
var avg = series.formatValue(series.stats.avg);
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
///<reference path="../../../../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import {describe, beforeEach, it, expect} from '../../../../../test/lib/common';
|
|
||||||
|
|
||||||
import {DataProcessor} from '../data_processor';
|
import {DataProcessor} from '../data_processor';
|
||||||
|
|
||||||
describe('Graph DataProcessor', function() {
|
describe('Graph DataProcessor', function() {
|
||||||
@ -29,7 +25,7 @@ describe('Graph DataProcessor', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should automatically set xaxis mode to field', () => {
|
it('Should automatically set xaxis mode to field', () => {
|
||||||
expect(panel.xaxis.mode).to.be('field');
|
expect(panel.xaxis.mode).toBe('field');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -48,16 +44,16 @@ describe('Graph DataProcessor', function() {
|
|||||||
|
|
||||||
it('Should return all field names', () => {
|
it('Should return all field names', () => {
|
||||||
var fields = processor.getDataFieldNames(dataList, false);
|
var fields = processor.getDataFieldNames(dataList, false);
|
||||||
expect(fields).to.contain('hostname');
|
expect(fields).toContain('hostname');
|
||||||
expect(fields).to.contain('valueField');
|
expect(fields).toContain('valueField');
|
||||||
expect(fields).to.contain('nested.prop1');
|
expect(fields).toContain('nested.prop1');
|
||||||
expect(fields).to.contain('nested.value2');
|
expect(fields).toContain('nested.value2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should return all number fields', () => {
|
it('Should return all number fields', () => {
|
||||||
var fields = processor.getDataFieldNames(dataList, true);
|
var fields = processor.getDataFieldNames(dataList, true);
|
||||||
expect(fields).to.contain('valueField');
|
expect(fields).toContain('valueField');
|
||||||
expect(fields).to.contain('nested.value2');
|
expect(fields).toContain('nested.value2');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -75,7 +75,7 @@ describe('grafanaGraph', function() {
|
|||||||
alias: 'series1'
|
alias: 'series1'
|
||||||
}));
|
}));
|
||||||
ctx.data.push(new TimeSeries({
|
ctx.data.push(new TimeSeries({
|
||||||
datapoints: [[1,10],[2,20]],
|
datapoints: [[10,1],[20,2]],
|
||||||
alias: 'series2'
|
alias: 'series2'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -112,10 +112,10 @@ describe('grafanaGraph', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
graphScenario('sort series as legend', (ctx) => {
|
graphScenario('sorting stacked series as legend. disabled', (ctx) => {
|
||||||
describe("with sort as legend undefined", () => {
|
|
||||||
ctx.setup((ctrl) => {
|
ctx.setup((ctrl) => {
|
||||||
ctrl.panel.legend.sort = undefined;
|
ctrl.panel.legend.sort = undefined;
|
||||||
|
ctrl.panel.stack = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not modify order of time series", () => {
|
it("should not modify order of time series", () => {
|
||||||
@ -124,22 +124,24 @@ describe('grafanaGraph', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("with sort as legend set to min. descending order", () => {
|
graphScenario("sorting stacked series as legend. min descending order", (ctx) => {
|
||||||
ctx.setup((ctrl) => {
|
ctx.setup(ctrl => {
|
||||||
ctrl.panel.legend.sort = 'min';
|
ctrl.panel.legend.sort = 'min';
|
||||||
ctrl.panel.legend.sortDesc = true;
|
ctrl.panel.legend.sortDesc = true;
|
||||||
|
ctrl.panel.stack = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("highest value should be first", () => {
|
it("highest value should be first", () => {
|
||||||
expect(ctx.plotData[1].alias).to.be('series2');
|
expect(ctx.plotData[0].alias).to.be('series2');
|
||||||
expect(ctx.plotData[0].alias).to.be('series1');
|
expect(ctx.plotData[1].alias).to.be('series1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("with sort as legend set to min. ascending order", () => {
|
graphScenario("sorting stacked series as legend. min ascending order", (ctx) => {
|
||||||
ctx.setup((ctrl) => {
|
ctx.setup((ctrl, data) => {
|
||||||
ctrl.panel.legend.sort = 'min';
|
ctrl.panel.legend.sort = 'min';
|
||||||
ctrl.panel.legend.sortDesc = true;
|
ctrl.panel.legend.sortDesc = false;
|
||||||
|
ctrl.panel.stack = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("lowest value should be first", () => {
|
it("lowest value should be first", () => {
|
||||||
@ -148,16 +150,29 @@ describe('grafanaGraph', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("with sort as legend set to current. ascending order", () => {
|
graphScenario("sorting stacked series as legend. stacking disabled", (ctx) => {
|
||||||
|
ctx.setup((ctrl) => {
|
||||||
|
ctrl.panel.legend.sort = 'min';
|
||||||
|
ctrl.panel.legend.sortDesc = true;
|
||||||
|
ctrl.panel.stack = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("highest value should be first", () => {
|
||||||
|
expect(ctx.plotData[0].alias).to.be('series1');
|
||||||
|
expect(ctx.plotData[1].alias).to.be('series2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
graphScenario("sorting stacked series as legend. current descending order", (ctx) => {
|
||||||
ctx.setup((ctrl) => {
|
ctx.setup((ctrl) => {
|
||||||
ctrl.panel.legend.sort = 'current';
|
ctrl.panel.legend.sort = 'current';
|
||||||
ctrl.panel.legend.sortDesc = false;
|
ctrl.panel.legend.sortDesc = true;
|
||||||
|
ctrl.panel.stack = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("highest last value should be first", () => {
|
it("highest last value should be first", () => {
|
||||||
expect(ctx.plotData[1].alias).to.be('series2');
|
expect(ctx.plotData[0].alias).to.be('series2');
|
||||||
expect(ctx.plotData[0].alias).to.be('series1');
|
expect(ctx.plotData[1].alias).to.be('series1');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -300,7 +315,7 @@ describe('grafanaGraph', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should set barWidth', function() {
|
it('should set barWidth', function() {
|
||||||
expect(ctx.plotOptions.series.bars.barWidth).to.be(10/1.5);
|
expect(ctx.plotOptions.series.bars.barWidth).to.be(1/1.5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -383,146 +398,4 @@ describe('grafanaGraph', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
// graphScenario('when using flexible Y-Min and Y-Max settings', function(ctx) {
|
|
||||||
// describe('and Y-Min is <100 and Y-Max is >200 and values within range', function() {
|
|
||||||
// ctx.setup(function(ctrl, data) {
|
|
||||||
// ctrl.panel.yaxes[0].min = '<100';
|
|
||||||
// ctrl.panel.yaxes[0].max = '>200';
|
|
||||||
// data[0] = new TimeSeries({
|
|
||||||
// datapoints: [[120,10],[160,20]],
|
|
||||||
// alias: 'series1',
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should set min to 100 and max to 200', function() {
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// describe('and Y-Min is <100 and Y-Max is >200 and values outside range', function() {
|
|
||||||
// ctx.setup(function(ctrl, data) {
|
|
||||||
// ctrl.panel.yaxes[0].min = '<100';
|
|
||||||
// ctrl.panel.yaxes[0].max = '>200';
|
|
||||||
// data[0] = new TimeSeries({
|
|
||||||
// datapoints: [[99,10],[201,20]],
|
|
||||||
// alias: 'series1',
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should set min to auto and max to auto', function() {
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(null);
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(null);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// describe('and Y-Min is =10.5 and Y-Max is =10.5', function() {
|
|
||||||
// ctx.setup(function(ctrl, data) {
|
|
||||||
// ctrl.panel.yaxes[0].min = '=10.5';
|
|
||||||
// ctrl.panel.yaxes[0].max = '=10.5';
|
|
||||||
// data[0] = new TimeSeries({
|
|
||||||
// datapoints: [[100,10],[120,20], [110,30]],
|
|
||||||
// alias: 'series1',
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should set min to last value + 10.5 and max to last value + 10.5', function() {
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(99.5);
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(120.5);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// describe('and Y-Min is ~10.5 and Y-Max is ~10.5', function() {
|
|
||||||
// ctx.setup(function(ctrl, data) {
|
|
||||||
// ctrl.panel.yaxes[0].min = '~10.5';
|
|
||||||
// ctrl.panel.yaxes[0].max = '~10.5';
|
|
||||||
// data[0] = new TimeSeries({
|
|
||||||
// datapoints: [[102,10],[104,20], [110,30]], //Also checks precision
|
|
||||||
// alias: 'series1',
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should set min to average value + 10.5 and max to average value + 10.5', function() {
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(94.8);
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(115.8);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// graphScenario('when using regular Y-Min and Y-Max settings', function(ctx) {
|
|
||||||
// describe('and Y-Min is 100 and Y-Max is 200', function() {
|
|
||||||
// ctx.setup(function(ctrl, data) {
|
|
||||||
// ctrl.panel.yaxes[0].min = '100';
|
|
||||||
// ctrl.panel.yaxes[0].max = '200';
|
|
||||||
// data[0] = new TimeSeries({
|
|
||||||
// datapoints: [[120,10],[160,20]],
|
|
||||||
// alias: 'series1',
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should set min to 100 and max to 200', function() {
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// describe('and Y-Min is 0 and Y-Max is 0', function() {
|
|
||||||
// ctx.setup(function(ctrl, data) {
|
|
||||||
// ctrl.panel.yaxes[0].min = '0';
|
|
||||||
// ctrl.panel.yaxes[0].max = '0';
|
|
||||||
// data[0] = new TimeSeries({
|
|
||||||
// datapoints: [[120,10],[160,20]],
|
|
||||||
// alias: 'series1',
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should set min to 0 and max to 0', function() {
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(0);
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(0);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// describe('and negative values used', function() {
|
|
||||||
// ctx.setup(function(ctrl, data) {
|
|
||||||
// ctrl.panel.yaxes[0].min = '-10';
|
|
||||||
// ctrl.panel.yaxes[0].max = '-13.14';
|
|
||||||
// data[0] = new TimeSeries({
|
|
||||||
// datapoints: [[120,10],[160,20]],
|
|
||||||
// alias: 'series1',
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should set min and max to negative', function() {
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(-10);
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(-13.14);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// graphScenario('when using Y-Min and Y-Max settings stored as number', function(ctx) {
|
|
||||||
// describe('and Y-Min is 0 and Y-Max is 100', function() {
|
|
||||||
// ctx.setup(function(ctrl, data) {
|
|
||||||
// ctrl.panel.yaxes[0].min = 0;
|
|
||||||
// ctrl.panel.yaxes[0].max = 100;
|
|
||||||
// data[0] = new TimeSeries({
|
|
||||||
// datapoints: [[120,10],[160,20]],
|
|
||||||
// alias: 'series1',
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should set min to 0 and max to 100', function() {
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(0);
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(100);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// describe('and Y-Min is -100 and Y-Max is -10.5', function() {
|
|
||||||
// ctx.setup(function(ctrl, data) {
|
|
||||||
// ctrl.panel.yaxes[0].min = -100;
|
|
||||||
// ctrl.panel.yaxes[0].max = -10.5;
|
|
||||||
// data[0] = new TimeSeries({
|
|
||||||
// datapoints: [[120,10],[160,20]],
|
|
||||||
// alias: 'series1',
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should set min to -100 and max to -10.5', function() {
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].min).to.be(-100);
|
|
||||||
// expect(ctx.plotOptions.yaxes[0].max).to.be(-10.5);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
///<reference path="../../../../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import { describe, beforeEach, it, expect } from '../../../../../test/lib/common';
|
|
||||||
|
|
||||||
import { convertValuesToHistogram, getSeriesValues } from '../histogram';
|
import { convertValuesToHistogram, getSeriesValues } from '../histogram';
|
||||||
|
|
||||||
describe('Graph Histogam Converter', function () {
|
describe('Graph Histogam Converter', function () {
|
||||||
@ -21,7 +17,7 @@ describe('Graph Histogam Converter', function () {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let histogram = convertValuesToHistogram(values, bucketSize);
|
let histogram = convertValuesToHistogram(values, bucketSize);
|
||||||
expect(histogram).to.eql(expected);
|
expect(histogram).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should not add empty buckets', () => {
|
it('Should not add empty buckets', () => {
|
||||||
@ -31,7 +27,7 @@ describe('Graph Histogam Converter', function () {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let histogram = convertValuesToHistogram(values, bucketSize);
|
let histogram = convertValuesToHistogram(values, bucketSize);
|
||||||
expect(histogram).to.eql(expected);
|
expect(histogram).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -50,7 +46,7 @@ describe('Graph Histogam Converter', function () {
|
|||||||
let expected = [1, 2, 10, 11, 17, 20, 29];
|
let expected = [1, 2, 10, 11, 17, 20, 29];
|
||||||
|
|
||||||
let values = getSeriesValues(data);
|
let values = getSeriesValues(data);
|
||||||
expect(values).to.eql(expected);
|
expect(values).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should skip null values', () => {
|
it('Should skip null values', () => {
|
||||||
@ -59,7 +55,7 @@ describe('Graph Histogam Converter', function () {
|
|||||||
let expected = [1, 2, 10, 11, 17, 20, 29];
|
let expected = [1, 2, 10, 11, 17, 20, 29];
|
||||||
|
|
||||||
let values = getSeriesValues(data);
|
let values = getSeriesValues(data);
|
||||||
expect(values).to.eql(expected);
|
expect(values).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -1,9 +1,10 @@
|
|||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import d3 from 'vendor/d3/d3';
|
import * as d3 from 'd3';
|
||||||
import {contextSrv} from 'app/core/core';
|
import {contextSrv} from 'app/core/core';
|
||||||
import {tickStep} from 'app/core/utils/ticks';
|
import {tickStep} from 'app/core/utils/ticks';
|
||||||
|
import {getColorScale, getOpacityScale} from './color_scale';
|
||||||
|
|
||||||
let module = angular.module('grafana.directives');
|
let module = angular.module('grafana.directives');
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ module.directive('colorLegend', function() {
|
|||||||
|
|
||||||
if (panel.color.mode === 'spectrum') {
|
if (panel.color.mode === 'spectrum') {
|
||||||
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
||||||
let colorScale = getColorScale(colorScheme, legendWidth);
|
let colorScale = getColorScale(colorScheme, contextSrv.user.lightTheme, legendWidth);
|
||||||
drawSimpleColorLegend(elem, colorScale);
|
drawSimpleColorLegend(elem, colorScale);
|
||||||
} else if (panel.color.mode === 'opacity') {
|
} else if (panel.color.mode === 'opacity') {
|
||||||
let colorOptions = panel.color;
|
let colorOptions = panel.color;
|
||||||
@ -93,7 +94,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal
|
|||||||
let widthFactor = legendWidth / (rangeTo - rangeFrom);
|
let widthFactor = legendWidth / (rangeTo - rangeFrom);
|
||||||
let valuesRange = d3.range(rangeFrom, rangeTo, rangeStep);
|
let valuesRange = d3.range(rangeFrom, rangeTo, rangeStep);
|
||||||
|
|
||||||
let colorScale = getColorScale(colorScheme, maxValue, minValue);
|
let colorScale = getColorScale(colorScheme, contextSrv.user.lightTheme, maxValue, minValue);
|
||||||
legend.selectAll(".heatmap-color-legend-rect")
|
legend.selectAll(".heatmap-color-legend-rect")
|
||||||
.data(valuesRange)
|
.data(valuesRange)
|
||||||
.enter().append("rect")
|
.enter().append("rect")
|
||||||
@ -115,7 +116,10 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue
|
|||||||
let legendWidth = Math.floor(legendElem.outerWidth()) - 30;
|
let legendWidth = Math.floor(legendElem.outerWidth()) - 30;
|
||||||
let legendHeight = legendElem.attr("height");
|
let legendHeight = legendElem.attr("height");
|
||||||
|
|
||||||
let rangeStep = 10;
|
let rangeStep = 1;
|
||||||
|
if (rangeTo - rangeFrom > legendWidth) {
|
||||||
|
rangeStep = Math.floor((rangeTo - rangeFrom) / legendWidth);
|
||||||
|
}
|
||||||
let widthFactor = legendWidth / (rangeTo - rangeFrom);
|
let widthFactor = legendWidth / (rangeTo - rangeFrom);
|
||||||
let valuesRange = d3.range(rangeFrom, rangeTo, rangeStep);
|
let valuesRange = d3.range(rangeFrom, rangeTo, rangeStep);
|
||||||
|
|
||||||
@ -228,31 +232,6 @@ function clearLegend(elem) {
|
|||||||
legendElem.empty();
|
legendElem.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getColorScale(colorScheme, maxValue, minValue = 0) {
|
|
||||||
let colorInterpolator = d3[colorScheme.value];
|
|
||||||
let colorScaleInverted = colorScheme.invert === 'always' ||
|
|
||||||
(colorScheme.invert === 'dark' && !contextSrv.user.lightTheme);
|
|
||||||
|
|
||||||
let start = colorScaleInverted ? maxValue : minValue;
|
|
||||||
let end = colorScaleInverted ? minValue : maxValue;
|
|
||||||
|
|
||||||
return d3.scaleSequential(colorInterpolator).domain([start, end]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOpacityScale(options, maxValue, minValue = 0) {
|
|
||||||
let legendOpacityScale;
|
|
||||||
if (options.colorScale === 'linear') {
|
|
||||||
legendOpacityScale = d3.scaleLinear()
|
|
||||||
.domain([minValue, maxValue])
|
|
||||||
.range([0, 1]);
|
|
||||||
} else if (options.colorScale === 'sqrt') {
|
|
||||||
legendOpacityScale = d3.scalePow().exponent(options.exponent)
|
|
||||||
.domain([minValue, maxValue])
|
|
||||||
.range([0, 1]);
|
|
||||||
}
|
|
||||||
return legendOpacityScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSvgElemX(elem) {
|
function getSvgElemX(elem) {
|
||||||
let svgElem = elem.get(0);
|
let svgElem = elem.get(0);
|
||||||
if (svgElem && svgElem.x && svgElem.x.baseVal) {
|
if (svgElem && svgElem.x && svgElem.x.baseVal) {
|
||||||
|
27
public/app/plugins/panel/heatmap/color_scale.ts
Normal file
27
public/app/plugins/panel/heatmap/color_scale.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import * as d3 from 'd3';
|
||||||
|
import * as d3ScaleChromatic from 'd3-scale-chromatic';
|
||||||
|
|
||||||
|
export function getColorScale(colorScheme: any, lightTheme: boolean, maxValue: number, minValue = 0): (d: any) => any {
|
||||||
|
let colorInterpolator = d3ScaleChromatic[colorScheme.value];
|
||||||
|
let colorScaleInverted = colorScheme.invert === 'always' ||
|
||||||
|
(colorScheme.invert === 'dark' && !lightTheme);
|
||||||
|
|
||||||
|
let start = colorScaleInverted ? maxValue : minValue;
|
||||||
|
let end = colorScaleInverted ? minValue : maxValue;
|
||||||
|
|
||||||
|
return d3.scaleSequential(colorInterpolator).domain([start, end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOpacityScale(options, maxValue, minValue = 0) {
|
||||||
|
let legendOpacityScale;
|
||||||
|
if (options.colorScale === 'linear') {
|
||||||
|
legendOpacityScale = d3.scaleLinear()
|
||||||
|
.domain([minValue, maxValue])
|
||||||
|
.range([0, 1]);
|
||||||
|
} else if (options.colorScale === 'sqrt') {
|
||||||
|
legendOpacityScale = d3.scalePow().exponent(options.exponent)
|
||||||
|
.domain([minValue, maxValue])
|
||||||
|
.range([0, 1]);
|
||||||
|
}
|
||||||
|
return legendOpacityScale;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import d3 from 'vendor/d3/d3';
|
import * as d3 from 'd3';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import * as d3 from 'd3';
|
||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
import {appEvents, contextSrv} from 'app/core/core';
|
import {appEvents, contextSrv} from 'app/core/core';
|
||||||
import {tickStep, getScaledDecimals, getFlotTickSize} from 'app/core/utils/ticks';
|
import {tickStep, getScaledDecimals, getFlotTickSize} from 'app/core/utils/ticks';
|
||||||
import d3 from 'vendor/d3/d3';
|
|
||||||
import {HeatmapTooltip} from './heatmap_tooltip';
|
import {HeatmapTooltip} from './heatmap_tooltip';
|
||||||
import {mergeZeroBuckets} from './heatmap_data_converter';
|
import {mergeZeroBuckets} from './heatmap_data_converter';
|
||||||
|
import {getColorScale, getOpacityScale} from './color_scale';
|
||||||
|
|
||||||
let MIN_CARD_SIZE = 1,
|
let MIN_CARD_SIZE = 1,
|
||||||
CARD_PADDING = 1,
|
CARD_PADDING = 1,
|
||||||
@ -386,8 +387,9 @@ export default function link(scope, elem, attrs, ctrl) {
|
|||||||
let maxValue = panel.color.max || maxValueAuto;
|
let maxValue = panel.color.max || maxValueAuto;
|
||||||
let minValue = panel.color.min || 0;
|
let minValue = panel.color.min || 0;
|
||||||
|
|
||||||
colorScale = getColorScale(maxValue, minValue);
|
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
||||||
setOpacityScale(maxValue);
|
colorScale = getColorScale(colorScheme, contextSrv.user.lightTheme, maxValue, minValue);
|
||||||
|
opacityScale = getOpacityScale(panel.color, maxValue);
|
||||||
setCardSize();
|
setCardSize();
|
||||||
|
|
||||||
let cards = heatmap.selectAll(".heatmap-card").data(cardsData);
|
let cards = heatmap.selectAll(".heatmap-card").data(cardsData);
|
||||||
@ -422,8 +424,8 @@ export default function link(scope, elem, attrs, ctrl) {
|
|||||||
let strokeColor = d3.color(color).brighter(4);
|
let strokeColor = d3.color(color).brighter(4);
|
||||||
let current_card = d3.select(event.target);
|
let current_card = d3.select(event.target);
|
||||||
tooltip.originalFillColor = color;
|
tooltip.originalFillColor = color;
|
||||||
current_card.style("fill", highlightColor)
|
current_card.style("fill", highlightColor.toString())
|
||||||
.style("stroke", strokeColor)
|
.style("stroke", strokeColor.toString())
|
||||||
.style("stroke-width", 1);
|
.style("stroke-width", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,30 +435,6 @@ export default function link(scope, elem, attrs, ctrl) {
|
|||||||
.style("stroke-width", 0);
|
.style("stroke-width", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getColorScale(maxValue, minValue = 0) {
|
|
||||||
let colorScheme = _.find(ctrl.colorSchemes, {value: panel.color.colorScheme});
|
|
||||||
let colorInterpolator = d3[colorScheme.value];
|
|
||||||
let colorScaleInverted = colorScheme.invert === 'always' ||
|
|
||||||
(colorScheme.invert === 'dark' && !contextSrv.user.lightTheme);
|
|
||||||
|
|
||||||
let start = colorScaleInverted ? maxValue : minValue;
|
|
||||||
let end = colorScaleInverted ? minValue : maxValue;
|
|
||||||
|
|
||||||
return d3.scaleSequential(colorInterpolator).domain([start, end]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setOpacityScale(maxValue) {
|
|
||||||
if (panel.color.colorScale === 'linear') {
|
|
||||||
opacityScale = d3.scaleLinear()
|
|
||||||
.domain([0, maxValue])
|
|
||||||
.range([0, 1]);
|
|
||||||
} else if (panel.color.colorScale === 'sqrt') {
|
|
||||||
opacityScale = d3.scalePow().exponent(panel.color.exponent)
|
|
||||||
.domain([0, maxValue])
|
|
||||||
.range([0, 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCardSize() {
|
function setCardSize() {
|
||||||
let xGridSize = Math.floor(xScale(data.xBucketSize) - xScale(0));
|
let xGridSize = Math.floor(xScale(data.xBucketSize) - xScale(0));
|
||||||
let yGridSize = Math.floor(yScale(yScale.invert(0) - data.yBucketSize));
|
let yGridSize = Math.floor(yScale(yScale.invert(0) - data.yBucketSize));
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
System.config({
|
|
||||||
defaultJSExtenions: true,
|
|
||||||
baseURL: 'public',
|
|
||||||
paths: {
|
|
||||||
'virtual-scroll': 'vendor/npm/virtual-scroll/src/index.js',
|
|
||||||
'mousetrap': 'vendor/npm/mousetrap/mousetrap.js',
|
|
||||||
'remarkable': 'vendor/npm/remarkable/dist/remarkable.js',
|
|
||||||
'tether': 'vendor/npm/tether/dist/js/tether.js',
|
|
||||||
'eventemitter3': 'vendor/npm/eventemitter3/index.js',
|
|
||||||
'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',
|
|
||||||
'moment': 'vendor/moment.js',
|
|
||||||
"jquery": "vendor/jquery/dist/jquery.js",
|
|
||||||
'lodash-src': 'vendor/lodash/dist/lodash.js',
|
|
||||||
"lodash": 'app/core/lodash_extended.js',
|
|
||||||
"angular": "vendor/angular/angular.js",
|
|
||||||
"bootstrap": "vendor/bootstrap/bootstrap.js",
|
|
||||||
'angular-route': 'vendor/angular-route/angular-route.js',
|
|
||||||
'angular-sanitize': 'vendor/angular-sanitize/angular-sanitize.js',
|
|
||||||
"angular-ui": "vendor/angular-ui/ui-bootstrap-tpls.js",
|
|
||||||
"angular-strap": "vendor/angular-other/angular-strap.js",
|
|
||||||
"angular-dragdrop": "vendor/angular-native-dragdrop/draganddrop.js",
|
|
||||||
"angular-bindonce": "vendor/angular-bindonce/bindonce.js",
|
|
||||||
"spectrum": "vendor/spectrum.js",
|
|
||||||
"bootstrap-tagsinput": "vendor/tagsinput/bootstrap-tagsinput.js",
|
|
||||||
"jquery.flot": "vendor/flot/jquery.flot",
|
|
||||||
"jquery.flot.pie": "vendor/flot/jquery.flot.pie",
|
|
||||||
"jquery.flot.selection": "vendor/flot/jquery.flot.selection",
|
|
||||||
"jquery.flot.stack": "vendor/flot/jquery.flot.stack",
|
|
||||||
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent",
|
|
||||||
"jquery.flot.time": "vendor/flot/jquery.flot.time",
|
|
||||||
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair",
|
|
||||||
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow",
|
|
||||||
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge",
|
|
||||||
"d3": "vendor/d3/d3.js",
|
|
||||||
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes",
|
|
||||||
"twemoji": "vendor/npm/twemoji/2/twemoji.amd.js",
|
|
||||||
"ace": "vendor/npm/ace-builds/src-noconflict/ace",
|
|
||||||
},
|
|
||||||
|
|
||||||
packages: {
|
|
||||||
app: {
|
|
||||||
defaultExtension: 'js',
|
|
||||||
},
|
|
||||||
vendor: {
|
|
||||||
defaultExtension: 'js',
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
defaultExtension: 'js',
|
|
||||||
},
|
|
||||||
test: {
|
|
||||||
defaultExtension: 'js',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
map: {
|
|
||||||
text: 'vendor/plugin-text/text.js',
|
|
||||||
css: 'app/core/utils/css_loader.js'
|
|
||||||
},
|
|
||||||
|
|
||||||
meta: {
|
|
||||||
'vendor/npm/virtual-scroll/src/indx.js': {
|
|
||||||
format: 'cjs',
|
|
||||||
exports: 'VirtualScroll',
|
|
||||||
},
|
|
||||||
'vendor/angular/angular.js': {
|
|
||||||
format: 'global',
|
|
||||||
deps: ['jquery'],
|
|
||||||
exports: 'angular',
|
|
||||||
},
|
|
||||||
'vendor/npm/eventemitter3/index.js': {
|
|
||||||
format: 'cjs',
|
|
||||||
exports: 'EventEmitter'
|
|
||||||
},
|
|
||||||
'vendor/npm/mousetrap/mousetrap.js': {
|
|
||||||
format: 'global',
|
|
||||||
exports: 'Mousetrap'
|
|
||||||
},
|
|
||||||
'vendor/npm/ace-builds/src-noconflict/ace.js': {
|
|
||||||
format: 'global',
|
|
||||||
exports: 'ace'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
27
public/vendor/d3/LICENSE
vendored
27
public/vendor/d3/LICENSE
vendored
@ -1,27 +0,0 @@
|
|||||||
Copyright 2010-2016 Mike Bostock
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the author nor the names of contributors may be used to
|
|
||||||
endorse or promote products derived from this software without specific prior
|
|
||||||
written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
57
public/vendor/d3/README.md
vendored
57
public/vendor/d3/README.md
vendored
@ -1,57 +0,0 @@
|
|||||||
# D3: Data-Driven Documents
|
|
||||||
|
|
||||||
<a href="https://d3js.org"><img src="https://d3js.org/logo.svg" align="left" hspace="10" vspace="6"></a>
|
|
||||||
|
|
||||||
**D3** (or **D3.js**) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
* [API Reference](https://github.com/d3/d3/blob/master/API.md)
|
|
||||||
* [Release Notes](https://github.com/d3/d3/releases)
|
|
||||||
* [Gallery](https://github.com/d3/d3/wiki/Gallery)
|
|
||||||
* [Examples](http://bl.ocks.org/mbostock)
|
|
||||||
* [Wiki](https://github.com/d3/d3/wiki)
|
|
||||||
|
|
||||||
## Installing
|
|
||||||
|
|
||||||
If you use npm, `npm install d3`. Otherwise, download the [latest release](https://github.com/d3/d3/releases/latest). The released bundle supports anonymous AMD, CommonJS, and vanilla environments. You can load directly from [d3js.org](https://d3js.org), [CDNJS](https://cdnjs.com/libraries/d3), or [unpkg](https://unpkg.com/d3/). For example:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script src="https://d3js.org/d3.v4.js"></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
For the minified version:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use the standalone D3 microlibraries. For example, [d3-selection](https://github.com/d3/d3-selection):
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script src="https://d3js.org/d3-selection.v1.js"></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
D3 is written using [ES2015 modules](http://www.2ality.com/2014/09/es6-modules-final.html). Create a [custom bundle using Rollup](http://bl.ocks.org/mbostock/bb09af4c39c79cffcde4), Webpack, or your preferred bundler. To import D3 into an ES2015 application, either import specific symbols from specific D3 modules:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import {scaleLinear} from "d3-scale";
|
|
||||||
```
|
|
||||||
|
|
||||||
Or import everything into a namespace (here, `d3`):
|
|
||||||
|
|
||||||
```js
|
|
||||||
import * as d3 from "d3";
|
|
||||||
```
|
|
||||||
|
|
||||||
In Node:
|
|
||||||
|
|
||||||
```js
|
|
||||||
var d3 = require("d3");
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also require individual modules and combine them into a `d3` object using [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign):
|
|
||||||
|
|
||||||
```js
|
|
||||||
var d3 = Object.assign({}, require("d3-format"), require("d3-geo"), require("d3-geo-projection"));
|
|
||||||
```
|
|
2
public/vendor/d3/d3-scale-chromatic.min.js
vendored
2
public/vendor/d3/d3-scale-chromatic.min.js
vendored
File diff suppressed because one or more lines are too long
3
public/vendor/d3/d3.js
vendored
3
public/vendor/d3/d3.js
vendored
@ -1,3 +0,0 @@
|
|||||||
// Import main D3.js module and combine it with another
|
|
||||||
var d3 = Object.assign({}, require('./d3.v4.min.js'), require('./d3-scale-chromatic.min.js'));
|
|
||||||
module.exports = d3;
|
|
8
public/vendor/d3/d3.v4.min.js
vendored
8
public/vendor/d3/d3.v4.min.js
vendored
File diff suppressed because one or more lines are too long
@ -12,37 +12,23 @@ function exit_if_fail {
|
|||||||
|
|
||||||
cd /home/ubuntu/.go_workspace/src/github.com/grafana/grafana
|
cd /home/ubuntu/.go_workspace/src/github.com/grafana/grafana
|
||||||
|
|
||||||
rm -rf node_modules
|
|
||||||
npm install -g yarn --quiet
|
|
||||||
yarn install --pure-lockfile --no-progress
|
|
||||||
|
|
||||||
exit_if_fail npm run test-ci
|
|
||||||
exit_if_fail npm run build
|
|
||||||
|
|
||||||
# publish code coverage
|
|
||||||
echo "Publishing javascript code coverage"
|
|
||||||
bash <(curl -s https://codecov.io/bash) -cF javascript
|
|
||||||
rm -rf coverage
|
|
||||||
# npm install -g codecov
|
|
||||||
# codecov
|
|
||||||
# cat ./coverage/lcov.info | node ./node_modules/coveralls/bin/coveralls.js
|
|
||||||
|
|
||||||
echo "running go fmt"
|
echo "running go fmt"
|
||||||
exit_if_fail test -z "$(gofmt -s -l ./pkg | tee /dev/stderr)"
|
exit_if_fail test -z "$(gofmt -s -l ./pkg | tee /dev/stderr)"
|
||||||
|
|
||||||
echo "running go vet"
|
echo "running go vet"
|
||||||
exit_if_fail test -z "$(go vet ./pkg/... | tee /dev/stderr)"
|
exit_if_fail test -z "$(go vet ./pkg/... | tee /dev/stderr)"
|
||||||
|
|
||||||
echo "building binaries"
|
cd ~/dev/go/src/github.com/grafana/grafana
|
||||||
exit_if_fail go run build.go build
|
echo "building backend with install to cache pkgs"
|
||||||
|
exit_if_fail time go install ./pkg/cmd/grafana-server
|
||||||
|
|
||||||
echo "running go test"
|
echo "running go test"
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
echo "" > coverage.txt
|
echo "" > coverage.txt
|
||||||
|
|
||||||
for d in $(go list ./pkg/...); do
|
time for d in $(go list ./pkg/...); do
|
||||||
exit_if_fail go test -race -coverprofile=profile.out -covermode=atomic $d
|
exit_if_fail go test -coverprofile=profile.out -covermode=atomic $d
|
||||||
if [ -f profile.out ]; then
|
if [ -f profile.out ]; then
|
||||||
cat profile.out >> coverage.txt
|
cat profile.out >> coverage.txt
|
||||||
rm profile.out
|
rm profile.out
|
25
scripts/circle-test-frontend.sh
Executable file
25
scripts/circle-test-frontend.sh
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
function exit_if_fail {
|
||||||
|
command=$@
|
||||||
|
echo "Executing '$command'"
|
||||||
|
eval $command
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "'$command' returned $rc."
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cd /home/ubuntu/.go_workspace/src/github.com/grafana/grafana
|
||||||
|
|
||||||
|
rm -rf node_modules
|
||||||
|
npm install -g yarn --quiet
|
||||||
|
yarn install --pure-lockfile --no-progress
|
||||||
|
|
||||||
|
exit_if_fail npm run test:coverage
|
||||||
|
exit_if_fail npm run build
|
||||||
|
|
||||||
|
# publish code coverage
|
||||||
|
echo "Publishing javascript code coverage"
|
||||||
|
bash <(curl -s https://codecov.io/bash) -cF javascript
|
||||||
|
rm -rf coverage
|
@ -7,7 +7,7 @@ module.exports = function(grunt) {
|
|||||||
'clean:release',
|
'clean:release',
|
||||||
'clean:build',
|
'clean:build',
|
||||||
'phantomjs',
|
'phantomjs',
|
||||||
'webpack:prod',
|
'exec:webpack',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,12 @@ module.exports = function(grunt) {
|
|||||||
'no-only-tests'
|
'no-only-tests'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
grunt.registerTask('precommit', [
|
||||||
|
'sasslint',
|
||||||
|
'exec:tslint',
|
||||||
|
'no-only-tests'
|
||||||
|
]);
|
||||||
|
|
||||||
grunt.registerTask('no-only-tests', function() {
|
grunt.registerTask('no-only-tests', function() {
|
||||||
var files = grunt.file.expand('public/**/*_specs\.ts', 'public/**/*_specs\.js');
|
var files = grunt.file.expand('public/**/*_specs\.ts', 'public/**/*_specs\.js');
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
module.exports = function(config, grunt) {
|
module.exports = function(config, grunt) {
|
||||||
'use strict'
|
'use strict';
|
||||||
|
|
||||||
var coverage = '';
|
var coverage = '';
|
||||||
if (config.coverage) {
|
if (config.coverage) {
|
||||||
coverage = '--coverage';
|
coverage = '--coverage --maxWorkers 2';
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tslint : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json",
|
tslint: 'node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json',
|
||||||
jest : "node ./node_modules/jest-cli/bin/jest.js " + coverage,
|
jest: 'node ./node_modules/jest-cli/bin/jest.js ' + coverage,
|
||||||
|
webpack: 'node ./node_modules/webpack/bin/webpack.js --config scripts/webpack/webpack.prod.js',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,7 @@ module.exports = function() {
|
|||||||
'use strict';
|
'use strict';
|
||||||
return {
|
return {
|
||||||
options: {
|
options: {
|
||||||
stats: !process.env.NODE_ENV || process.env.NODE_ENV === 'development'
|
stats: false,
|
||||||
},
|
},
|
||||||
dev: dev,
|
dev: dev,
|
||||||
prod: prod
|
prod: prod
|
||||||
|
@ -27,7 +27,10 @@ module.exports = merge(common, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
devServer: {
|
devServer: {
|
||||||
stats: 'errors-only',
|
noInfo: true,
|
||||||
|
stats: {
|
||||||
|
chunks: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
|
39
vendor/github.com/inconshreveable/log15/CONTRIBUTORS
generated
vendored
39
vendor/github.com/inconshreveable/log15/CONTRIBUTORS
generated
vendored
@ -1,11 +1,28 @@
|
|||||||
Contributors to log15:
|
Aaron L <aaron@bettercoder.net>
|
||||||
|
Alan Shreve <alan@inconshreveable.com>
|
||||||
- Aaron L
|
Andy Walker <walkeraj@gmail.com>
|
||||||
- Alan Shreve
|
Andy Watson <andrewmoorewatson@gmail.com>
|
||||||
- Chris Hines
|
Carl Veazey <Carl_Veazey@cable.comcast.com>
|
||||||
- Ciaran Downey
|
Chris Hines <github@cs-guy.com>
|
||||||
- Dmitry Chestnykh
|
Christoph Hack <christoph@tux21b.org>
|
||||||
- Evan Shaw
|
Ciaran Downey <me@ciarand.me>
|
||||||
- Péter Szilágyi
|
Dmitry Chestnykh <dmitry@codingrobots.com>
|
||||||
- Trevor Gattis
|
Evan Shaw <edsrzf@gmail.com>
|
||||||
- Vincent Vanackere
|
Gonzalo Serrano <boikot@gmail.com>
|
||||||
|
Jeremy <jrbudnack@starkandwayne.com>
|
||||||
|
Jonathan Rudenberg <jonathan@titanous.com>
|
||||||
|
Kang Seong-Min <kang.seongmin@gmail.com>
|
||||||
|
Kevin Burke <kev@inburke.com>
|
||||||
|
Marc Abramowitz <marc@marc-abramowitz.com>
|
||||||
|
Nathan Baulch <nathan.baulch@gmail.com>
|
||||||
|
NotZippy <notzippy@gmail.com>
|
||||||
|
Péter Szilágyi <peterke@gmail.com>
|
||||||
|
Robert Egorov <robert.egorov@gmail.com>
|
||||||
|
Robert Starbuck <robstarbuck@gmail.com>
|
||||||
|
Robert Zaremba <robert.zaremba@scale-it.pl>
|
||||||
|
Sean Chittenden <sean@chittenden.org>
|
||||||
|
Spencer Nelson <s@spenczar.com>
|
||||||
|
Tomasz Grodzki <tg@users.noreply.github.com>
|
||||||
|
Trevor Gattis <github@trevorgattis.com>
|
||||||
|
Vincent Vanackere <vincent.vanackere@gmail.com>
|
||||||
|
Will McGovern <will@brkt.com>
|
||||||
|
7
vendor/github.com/inconshreveable/log15/README.md
generated
vendored
7
vendor/github.com/inconshreveable/log15/README.md
generated
vendored
@ -73,5 +73,12 @@ srvlog := log.New(log.Ctx{"module": "app/server"})
|
|||||||
srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate})
|
srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Regenerating the CONTRIBUTORS file
|
||||||
|
|
||||||
|
```
|
||||||
|
go get -u github.com/kevinburke/write_mailmap
|
||||||
|
write_mailmap > CONTRIBUTORS
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Apache
|
Apache
|
||||||
|
2
vendor/github.com/inconshreveable/log15/doc.go
generated
vendored
2
vendor/github.com/inconshreveable/log15/doc.go
generated
vendored
@ -23,7 +23,7 @@ To get started, you'll want to import the library:
|
|||||||
Now you're ready to start logging:
|
Now you're ready to start logging:
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Info("Program starting", "args", os.Args())
|
log.Info("Program starting", "args", os.Args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
5
vendor/github.com/inconshreveable/log15/format.go
generated
vendored
5
vendor/github.com/inconshreveable/log15/format.go
generated
vendored
@ -18,6 +18,7 @@ const (
|
|||||||
termMsgJust = 40
|
termMsgJust = 40
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Format is the interface implemented by StreamHandler formatters.
|
||||||
type Format interface {
|
type Format interface {
|
||||||
Format(r *Record) []byte
|
Format(r *Record) []byte
|
||||||
}
|
}
|
||||||
@ -147,7 +148,7 @@ func JsonFormatEx(pretty, lineSeparated bool) Format {
|
|||||||
if !ok {
|
if !ok {
|
||||||
props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i])
|
props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i])
|
||||||
}
|
}
|
||||||
props[k] = formatJsonValue(r.Ctx[i+1])
|
props[k] = formatJSONValue(r.Ctx[i+1])
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := jsonMarshal(props)
|
b, err := jsonMarshal(props)
|
||||||
@ -192,7 +193,7 @@ func formatShared(value interface{}) (result interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatJsonValue(value interface{}) interface{} {
|
func formatJSONValue(value interface{}) interface{} {
|
||||||
value = formatShared(value)
|
value = formatShared(value)
|
||||||
switch value.(type) {
|
switch value.(type) {
|
||||||
case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
|
case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
|
||||||
|
17
vendor/github.com/inconshreveable/log15/handler.go
generated
vendored
17
vendor/github.com/inconshreveable/log15/handler.go
generated
vendored
@ -11,8 +11,8 @@ import (
|
|||||||
"github.com/go-stack/stack"
|
"github.com/go-stack/stack"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Logger prints its log records by writing to a Handler.
|
// Handler interface defines where and how log records are written.
|
||||||
// The Handler interface defines where and how log records are written.
|
// A logger prints its log records by writing to a Handler.
|
||||||
// Handlers are composable, providing you great flexibility in combining
|
// Handlers are composable, providing you great flexibility in combining
|
||||||
// them to achieve the logging structure that suits your applications.
|
// them to achieve the logging structure that suits your applications.
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
@ -188,7 +188,7 @@ func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
|
|||||||
}, h)
|
}, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A MultiHandler dispatches any write to each of its handlers.
|
// MultiHandler dispatches any write to each of its handlers.
|
||||||
// This is useful for writing different types of log information
|
// This is useful for writing different types of log information
|
||||||
// to different locations. For example, to log to a file and
|
// to different locations. For example, to log to a file and
|
||||||
// standard error:
|
// standard error:
|
||||||
@ -207,7 +207,7 @@ func MultiHandler(hs ...Handler) Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// A FailoverHandler writes all log records to the first handler
|
// FailoverHandler writes all log records to the first handler
|
||||||
// specified, but will failover and write to the second handler if
|
// specified, but will failover and write to the second handler if
|
||||||
// the first handler has failed, and so on for all handlers specified.
|
// the first handler has failed, and so on for all handlers specified.
|
||||||
// For example you might want to log to a network socket, but failover
|
// For example you might want to log to a network socket, but failover
|
||||||
@ -229,11 +229,9 @@ func FailoverHandler(hs ...Handler) Handler {
|
|||||||
err = h.Log(r)
|
err = h.Log(r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
}
|
||||||
r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
|
r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -315,13 +313,12 @@ func evaluateLazy(lz Lazy) (interface{}, error) {
|
|||||||
results := value.Call([]reflect.Value{})
|
results := value.Call([]reflect.Value{})
|
||||||
if len(results) == 1 {
|
if len(results) == 1 {
|
||||||
return results[0].Interface(), nil
|
return results[0].Interface(), nil
|
||||||
} else {
|
}
|
||||||
values := make([]interface{}, len(results))
|
values := make([]interface{}, len(results))
|
||||||
for i, v := range results {
|
for i, v := range results {
|
||||||
values[i] = v.Interface()
|
values[i] = v.Interface()
|
||||||
}
|
}
|
||||||
return values, nil
|
return values, nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscardHandler reports success for all writes but does nothing.
|
// DiscardHandler reports success for all writes but does nothing.
|
||||||
@ -333,7 +330,7 @@ func DiscardHandler() Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Must object provides the following Handler creation functions
|
// Must object provides the following Handler creation functions
|
||||||
// which instead of returning an error parameter only return a Handler
|
// which instead of returning an error parameter only return a Handler
|
||||||
// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
|
// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
|
||||||
var Must muster
|
var Must muster
|
||||||
|
5
vendor/github.com/inconshreveable/log15/logger.go
generated
vendored
5
vendor/github.com/inconshreveable/log15/logger.go
generated
vendored
@ -12,8 +12,10 @@ const lvlKey = "lvl"
|
|||||||
const msgKey = "msg"
|
const msgKey = "msg"
|
||||||
const errorKey = "LOG15_ERROR"
|
const errorKey = "LOG15_ERROR"
|
||||||
|
|
||||||
|
// Lvl is a type for predefined log levels.
|
||||||
type Lvl int
|
type Lvl int
|
||||||
|
|
||||||
|
// List of predefined log Levels
|
||||||
const (
|
const (
|
||||||
LvlCrit Lvl = iota
|
LvlCrit Lvl = iota
|
||||||
LvlError
|
LvlError
|
||||||
@ -40,7 +42,7 @@ func (l Lvl) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the appropriate Lvl from a string name.
|
// LvlFromString returns the appropriate Lvl from a string name.
|
||||||
// Useful for parsing command line args and configuration files.
|
// Useful for parsing command line args and configuration files.
|
||||||
func LvlFromString(lvlString string) (Lvl, error) {
|
func LvlFromString(lvlString string) (Lvl, error) {
|
||||||
switch lvlString {
|
switch lvlString {
|
||||||
@ -69,6 +71,7 @@ type Record struct {
|
|||||||
KeyNames RecordKeyNames
|
KeyNames RecordKeyNames
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordKeyNames are the predefined names of the log props used by the Logger interface.
|
||||||
type RecordKeyNames struct {
|
type RecordKeyNames struct {
|
||||||
Time string
|
Time string
|
||||||
Msg string
|
Msg string
|
||||||
|
7
vendor/github.com/inconshreveable/log15/root.go
generated
vendored
7
vendor/github.com/inconshreveable/log15/root.go
generated
vendored
@ -3,10 +3,11 @@ package log15
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/inconshreveable/log15/term"
|
|
||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
|
isatty "github.com/mattn/go-isatty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Predefined handlers
|
||||||
var (
|
var (
|
||||||
root *logger
|
root *logger
|
||||||
StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat())
|
StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat())
|
||||||
@ -14,11 +15,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if term.IsTty(os.Stdout.Fd()) {
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
StdoutHandler = StreamHandler(colorable.NewColorableStdout(), TerminalFormat())
|
StdoutHandler = StreamHandler(colorable.NewColorableStdout(), TerminalFormat())
|
||||||
}
|
}
|
||||||
|
|
||||||
if term.IsTty(os.Stderr.Fd()) {
|
if isatty.IsTerminal(os.Stderr.Fd()) {
|
||||||
StderrHandler = StreamHandler(colorable.NewColorableStderr(), TerminalFormat())
|
StderrHandler = StreamHandler(colorable.NewColorableStderr(), TerminalFormat())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
vendor/github.com/mattn/go-isatty/README.md
generated
vendored
17
vendor/github.com/mattn/go-isatty/README.md
generated
vendored
@ -1,5 +1,10 @@
|
|||||||
# go-isatty
|
# go-isatty
|
||||||
|
|
||||||
|
[](http://godoc.org/github.com/mattn/go-isatty)
|
||||||
|
[](https://travis-ci.org/mattn/go-isatty)
|
||||||
|
[](https://coveralls.io/github/mattn/go-isatty?branch=master)
|
||||||
|
[](https://goreportcard.com/report/mattn/go-isatty)
|
||||||
|
|
||||||
isatty for golang
|
isatty for golang
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@ -16,6 +21,8 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
fmt.Println("Is Terminal")
|
fmt.Println("Is Terminal")
|
||||||
|
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
||||||
|
fmt.Println("Is Cygwin/MSYS2 Terminal")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Is Not Terminal")
|
fmt.Println("Is Not Terminal")
|
||||||
}
|
}
|
||||||
@ -28,10 +35,16 @@ func main() {
|
|||||||
$ go get github.com/mattn/go-isatty
|
$ go get github.com/mattn/go-isatty
|
||||||
```
|
```
|
||||||
|
|
||||||
# License
|
## License
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
|
||||||
# Author
|
## Author
|
||||||
|
|
||||||
Yasuhiro Matsumoto (a.k.a mattn)
|
Yasuhiro Matsumoto (a.k.a mattn)
|
||||||
|
|
||||||
|
## Thanks
|
||||||
|
|
||||||
|
* k-takata: base idea for IsCygwinTerminal
|
||||||
|
|
||||||
|
https://github.com/k-takata/go-iscygpty
|
||||||
|
6
vendor/github.com/mattn/go-isatty/isatty_appengine.go
generated
vendored
6
vendor/github.com/mattn/go-isatty/isatty_appengine.go
generated
vendored
@ -7,3 +7,9 @@ package isatty
|
|||||||
func IsTerminal(fd uintptr) bool {
|
func IsTerminal(fd uintptr) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
2
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
2
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
@ -1,4 +1,4 @@
|
|||||||
// +build darwin freebsd openbsd netbsd
|
// +build darwin freebsd openbsd netbsd dragonfly
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
2
vendor/github.com/mattn/go-isatty/isatty_linux.go
generated
vendored
2
vendor/github.com/mattn/go-isatty/isatty_linux.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
// +build linux
|
// +build linux
|
||||||
// +build !appengine
|
// +build !appengine,!ppc64,!ppc64le
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
|
19
vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go
generated
vendored
Normal file
19
vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// +build linux
|
||||||
|
// +build ppc64 ppc64le
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
syscall "golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TCGETS
|
||||||
|
|
||||||
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var termios syscall.Termios
|
||||||
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
|
return err == 0
|
||||||
|
}
|
10
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
Normal file
10
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// +build !windows
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
79
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
79
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
@ -4,12 +4,30 @@
|
|||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
const (
|
||||||
var procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
fileNameInfo uintptr = 2
|
||||||
|
fileTypePipe = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||||
|
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
|
||||||
|
procGetFileType = kernel32.NewProc("GetFileType")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Check if GetFileInformationByHandleEx is available.
|
||||||
|
if procGetFileInformationByHandleEx.Find() != nil {
|
||||||
|
procGetFileInformationByHandleEx = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IsTerminal return true if the file descriptor is terminal.
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
func IsTerminal(fd uintptr) bool {
|
func IsTerminal(fd uintptr) bool {
|
||||||
@ -17,3 +35,60 @@ func IsTerminal(fd uintptr) bool {
|
|||||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
|
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
|
||||||
return r != 0 && e == 0
|
return r != 0 && e == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check pipe name is used for cygwin/msys2 pty.
|
||||||
|
// Cygwin/MSYS2 PTY has a name like:
|
||||||
|
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
|
||||||
|
func isCygwinPipeName(name string) bool {
|
||||||
|
token := strings.Split(name, "-")
|
||||||
|
if len(token) < 5 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[0] != `\msys` && token[0] != `\cygwin` {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(token[2], "pty") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[3] != `from` && token[3] != `to` {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[4] != "master" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
if procGetFileInformationByHandleEx == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cygwin/msys's pty is a pipe.
|
||||||
|
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
|
||||||
|
if ft != fileTypePipe || e != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf [2 + syscall.MAX_PATH]uint16
|
||||||
|
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
|
||||||
|
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
|
||||||
|
uintptr(len(buf)*2), 0, 0)
|
||||||
|
if r == 0 || e != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
l := *(*uint32)(unsafe.Pointer(&buf))
|
||||||
|
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
|
||||||
|
}
|
||||||
|
16
vendor/vendor.json
vendored
16
vendor/vendor.json
vendored
@ -443,10 +443,10 @@
|
|||||||
"revisionTime": "2016-12-15T22:53:35Z"
|
"revisionTime": "2016-12-15T22:53:35Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "mrmfY0cVu7jvgoIuTRaR8yVVh/M=",
|
"checksumSHA1": "W7WyVSrJNaQNQt2R9O4DxrK58cs=",
|
||||||
"path": "github.com/inconshreveable/log15",
|
"path": "github.com/inconshreveable/log15",
|
||||||
"revision": "39bacc234bf1afd0b68573e95b45871f67ba2cd4",
|
"revision": "0decfc6c20d9ca0ad143b0e89dcaa20f810b4fb3",
|
||||||
"revisionTime": "2017-02-16T22:56:31Z"
|
"revisionTime": "2016-11-12T20:41:34Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "oVIIInZXKkcRozJfuH2vWJsAS7s=",
|
"checksumSHA1": "oVIIInZXKkcRozJfuH2vWJsAS7s=",
|
||||||
@ -468,25 +468,29 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "jaCQF1par6Jl8g+V2Cgp0n/0wSc=",
|
"checksumSHA1": "jaCQF1par6Jl8g+V2Cgp0n/0wSc=",
|
||||||
"origin": "github.com/grafana/grafana/vendor/github.com/lib/pq/hstore",
|
|
||||||
"path": "github.com/lib/pq/hstore",
|
"path": "github.com/lib/pq/hstore",
|
||||||
"revision": "23da1db4f16d9658a86ae9b717c245fc078f10f1",
|
"revision": "23da1db4f16d9658a86ae9b717c245fc078f10f1",
|
||||||
"revisionTime": "2017-09-18T17:50:43Z"
|
"revisionTime": "2017-09-18T17:50:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "mJHrY33tDs2MRhHt+XunkRF/5ek=",
|
"checksumSHA1": "mJHrY33tDs2MRhHt+XunkRF/5ek=",
|
||||||
"origin": "github.com/grafana/grafana/vendor/github.com/lib/pq/listen_example",
|
|
||||||
"path": "github.com/lib/pq/listen_example",
|
"path": "github.com/lib/pq/listen_example",
|
||||||
"revision": "23da1db4f16d9658a86ae9b717c245fc078f10f1",
|
"revision": "23da1db4f16d9658a86ae9b717c245fc078f10f1",
|
||||||
"revisionTime": "2017-09-18T17:50:43Z"
|
"revisionTime": "2017-09-18T17:50:43Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "AU3fA8Sm33Vj9PBoRPSeYfxLRuE=",
|
"checksumSHA1": "AU3fA8Sm33Vj9PBoRPSeYfxLRuE=",
|
||||||
"origin": "github.com/grafana/grafana/vendor/github.com/lib/pq/oid",
|
|
||||||
"path": "github.com/lib/pq/oid",
|
"path": "github.com/lib/pq/oid",
|
||||||
"revision": "23da1db4f16d9658a86ae9b717c245fc078f10f1",
|
"revision": "23da1db4f16d9658a86ae9b717c245fc078f10f1",
|
||||||
"revisionTime": "2017-09-18T17:50:43Z"
|
"revisionTime": "2017-09-18T17:50:43Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "y/A5iuvwjytQE2CqVuphQRXR2nI=",
|
||||||
|
"origin": "github.com/grafana/grafana/vendor/github.com/mattn/go-isatty",
|
||||||
|
"path": "github.com/mattn/go-isatty",
|
||||||
|
"revision": "a5cdd64afdee435007ee3e9f6ed4684af949d568",
|
||||||
|
"revisionTime": "2017-09-25T05:49:04Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "bKMZjd2wPw13VwoE7mBeSv5djFA=",
|
"checksumSHA1": "bKMZjd2wPw13VwoE7mBeSv5djFA=",
|
||||||
"path": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
"path": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||||
|
Loading…
Reference in New Issue
Block a user