diff --git a/.gitignore b/.gitignore index 7c956af621f..ddabf2b680c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ awsconfig /tmp vendor/phantomjs/phantomjs vendor/phantomjs/phantomjs.exe +profile.out +coverage.txt docs/AWS_S3_BUCKET docs/GIT_BRANCH diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c61d812773..14858af10f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ - UX changes to nav & side menu - New dashboard grid layout system +# 4.7.0 (unreleased) + +## 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) + +* **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 +* **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) +* **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) +* **Save to file**: Fix for save to file in export modal. [#9586](https://github.com/grafana/grafana/issues/9586) + # 4.6.0-beta2 (2017-10-17) ## Fixes @@ -15,7 +30,9 @@ * **Cloudwatch**: Provide error message when failing to add cloudwatch datasource [#9534](https://github.com/grafana/grafana/pull/9534), thx [@mtanda](https://github.com/mtanda) * **Cloudwatch**: Fix unused period parameter [#9536](https://github.com/grafana/grafana/pull/9536), thx [@mtanda](https://github.com/mtanda) * **CSV Export**: Fix for broken CSV export [#9525](https://github.com/grafana/grafana/issues/9525) -* **Text panel**: Fixes issue with break lines in Firefox [#9491](https://github.com/grafana/grafana/issues/9491) +* **Text panel**: Fix for issue with break lines in Firefox [#9491](https://github.com/grafana/grafana/issues/9491) +* **Annotations**: Fix for issue saving annotation event in MySQL DB [#9550](https://github.com/grafana/grafana/issues/9550), thanks [@krise3k](https://github.com/krise3k) + # 4.6.0-beta1 (2017-10-13) diff --git a/Gruntfile.js b/Gruntfile.js index 335d8691677..a0607ef49dc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -22,9 +22,10 @@ module.exports = function (grunt) { } } + config.coverage = grunt.option('coverage'); config.phjs = grunt.option('phjsToRelease'); - config.pkg.version = grunt.option('pkgVer') || config.pkg.version; + console.log('Version', config.pkg.version); // load plugins diff --git a/README.md b/README.md index 28f8bfc1ba2..aefc0c0802b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[Grafana](https://grafana.com) [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) [![Go Report Card](https://goreportcard.com/badge/github.com/grafana/grafana)](https://goreportcard.com/report/github.com/grafana/grafana) +[Grafana](https://grafana.com) [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) [![Go Report Card](https://goreportcard.com/badge/github.com/grafana/grafana)](https://goreportcard.com/report/github.com/grafana/grafana) [![codecov](https://codecov.io/gh/grafana/grafana/branch/master/graph/badge.svg)](https://codecov.io/gh/grafana/grafana) ================ [Website](https://grafana.com) | [Twitter](https://twitter.com/grafana) | @@ -81,6 +81,20 @@ You only need to add the options you want to override. Config files are applied In your custom.ini uncomment (remove the leading `;`) sign. And set `app_mode = development`. +### Running tests + +- You can run backend Golang tests using "go test ./pkg/...". +- Execute all frontend tests with "npm run test" + +Writing & watching frontend tests (we have two test runners) + +- jest for all new tests that do not require browser context (React+more) + - Start watcher: `npm run jest` + - Jest will run all test files that end with the name ".jest.ts" +- karma + mocha is used for testing angularjs components. We do want to migrate these test to jest over time (if possible). + - Start watcher: `npm run karma` + - Karma+Mocha runs all files that end with the name "_specs.ts". + ## Contribute If you have any idea for an improvement or found a bug do not hesitate to open an issue. @@ -89,8 +103,8 @@ the kickass metrics & devops dashboard we all dream about! ## Plugin development -Checkout the [Plugin Development Guide](http://docs.grafana.org/plugins/developing/development/) and checkout the [PLUGIN_DEV.md](https://github.com/grafana/grafana/blob/master/PLUGIN_DEV.md) file for changes in Grafana that relate to -plugin development. +Checkout the [Plugin Development Guide](http://docs.grafana.org/plugins/developing/development/) and checkout the [PLUGIN_DEV.md](https://github.com/grafana/grafana/blob/master/PLUGIN_DEV.md) file for changes in Grafana that relate to +plugin development. ## License diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 2f170ba40be..a4ef8277cd1 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -48,7 +48,7 @@ Macro example | Description *$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn > to_timestamp(1494410783) AND dateColumn < to_timestamp(1494497183)* *$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *to_timestamp(1494410783)* *$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *to_timestamp(1494497183)* -*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *(extract(epoch from "dateColumn")/extract(epoch from '5m'::interval))::int* +*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *(extract(epoch from "dateColumn")/extract(epoch from '5m'::interval))::int*extract(epoch from '5m'::interval)* *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183* *$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783* *$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183* @@ -94,26 +94,26 @@ Example with `metric` column ```sql SELECT - min(time_date_time) as time, + $__timeGroup(time_date_time,'5m') as time, min(value_double), 'min' as metric FROM test_data WHERE $__timeFilter(time_date_time) -GROUP BY metric1, (extract(epoch from time_date_time)/extract(epoch from $__interval::interval))::int -ORDER BY time asc +GROUP BY time +ORDER BY time ``` Example with multiple columns: ```sql SELECT - min(time_date_time) as time, + $__timeGroup(time_date_time,'5m') as time, min(value_double) as min_value, max(value_double) as max_value FROM test_data WHERE $__timeFilter(time_date_time) -GROUP BY metric1, (extract(epoch from time_date_time)/extract(epoch from $__interval::interval))::int -ORDER BY time asc +GROUP BY time +ORDER BY time ``` ## Templating diff --git a/docs/sources/http_api/annotations.md b/docs/sources/http_api/annotations.md index 2f148e9aded..7aab127cb0c 100644 --- a/docs/sources/http_api/annotations.md +++ b/docs/sources/http_api/annotations.md @@ -120,6 +120,37 @@ Content-Type: application/json {"message":"Annotation added"} ``` +## Create Annotation in Graphite format + +Creates an annotation by using Graphite-compatible event format. The `when` and `data` fields are optional. If `when` is not specified then the current time will be used as annotation's timestamp. The `tags` field can also be in prior to Graphite `0.10.0` +format (string with multiple tags being separated by a space). + +`POST /api/annotations/graphite` + +**Example Request**: + +```json +POST /api/annotations/graphite HTTP/1.1 +Accept: application/json +Content-Type: application/json + +{ + "what": "Event - deploy", + "tags": ["deploy", "production"], + "when": 1467844481, + "data": "deploy of master branch happened at Wed Jul 6 22:34:41 UTC 2016" +} +``` + +**Example Response**: + +```json +HTTP/1.1 200 +Content-Type: application/json + +{"message":"Graphite annotation added"} +``` + ## Update Annotation `PUT /api/annotations/:id` diff --git a/docs/sources/project/building_from_source.md b/docs/sources/project/building_from_source.md index 1e4f0421a0c..e4ccedb7299 100644 --- a/docs/sources/project/building_from_source.md +++ b/docs/sources/project/building_from_source.md @@ -84,6 +84,20 @@ bra run You'll also need to run `npm run watch` to watch for changes to the front-end (typescript, html, sass) +### Running tests + +- You can run backend Golang tests using "go test ./pkg/...". +- Execute all frontend tests with "npm run test" + +Writing & watching frontend tests (we have two test runners) + +- jest for all new tests that do not require browser context (React+more) + - Start watcher: `npm run jest` + - Jest will run all test files that end with the name ".jest.ts" +- karma + mocha is used for testing angularjs components. We do want to migrate these test to jest over time (if possible). + - Start watcher: `npm run karma` + - Karma+Mocha runs all files that end with the name "_specs.ts". + ## Creating optimized release packages This step builds linux packages and requires that fpm is installed. Install fpm via `gem install fpm`. diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000000..52b1c4aa97c --- /dev/null +++ b/jest.config.js @@ -0,0 +1,23 @@ + +module.exports = { + verbose: false, + "transform": { + "^.+\\.tsx?$": "/node_modules/ts-jest/preprocessor.js" + }, + "moduleDirectories": ["node_modules", "/public"], + "roots": [ + "/public" + ], + "testRegex": "(\\.|/)(jest)\\.(jsx?|tsx?)$", + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "jsx", + "json" + ], + "setupFiles": [ + "./public/test/jest-shim.ts", + "./public/test/jest-setup.ts" + ] +}; diff --git a/package.json b/package.json index c5bb731cca7..5a9a631266e 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "devDependencies": { "@types/d3": "^4.10.1", "@types/enzyme": "^2.8.9", + "@types/jest": "^21.1.4", "@types/node": "^8.0.31", "@types/react": "^16.0.5", "@types/react-dom": "^15.5.4", @@ -22,8 +23,8 @@ "babel-loader": "^7.1.2", "babel-preset-es2015": "^6.24.1", "css-loader": "^0.28.7", - "enzyme": "^3.0.0", - "enzyme-adapter-react-16": "^1.0.0", + "enzyme": "^3.1.0", + "enzyme-adapter-react-16": "^1.0.1", "es6-promise": "^3.0.2", "es6-shim": "^0.35.3", "expect.js": "~0.2.0", @@ -53,11 +54,11 @@ "html-loader": "^0.5.1", "html-webpack-plugin": "^2.30.1", "husky": "^0.14.3", + "jest": "^21.2.1", "jshint-stylish": "~2.2.1", "json-loader": "^0.5.7", "karma": "1.7.0", "karma-chrome-launcher": "~2.2.0", - "karma-coverage": "1.1.1", "karma-expect": "~1.1.3", "karma-mocha": "~1.3.0", "karma-phantomjs-launcher": "1.0.4", @@ -82,6 +83,7 @@ "sinon": "1.17.6", "systemjs": "0.20.19", "systemjs-plugin-css": "^0.1.36", + "ts-jest": "^21.1.3", "ts-loader": "^2.3.7", "tslint": "^5.7.0", "tslint-loader": "^3.5.3", @@ -97,8 +99,10 @@ "watch": "./node_modules/.bin/webpack --progress --colors --watch --config scripts/webpack/webpack.dev.js", "build": "./node_modules/.bin/grunt build", "test": "./node_modules/.bin/grunt test", + "test-ci": "./node_modules/.bin/grunt test --coverage=true", "lint": "./node_modules/.bin/tslint -c tslint.json --project tsconfig.json --type-check", - "watch-test": "./node_modules/grunt-cli/bin/grunt karma:dev" + "karma": "./node_modules/grunt-cli/bin/grunt karma:dev", + "jest": "./node_modules/jest-cli/bin/jest --notify --watch" }, "license": "Apache-2.0", "dependencies": { diff --git a/packaging/deb/init.d/grafana-server b/packaging/deb/init.d/grafana-server index d01778560f7..85b0e412d35 100755 --- a/packaging/deb/init.d/grafana-server +++ b/packaging/deb/init.d/grafana-server @@ -91,7 +91,7 @@ case "$1" in then sleep 1 - # check if pid file has been written two + # check if pid file has been written to if ! [[ -s $PID_FILE ]]; then log_end_msg 1 exit 1 diff --git a/packaging/rpm/init.d/grafana-server b/packaging/rpm/init.d/grafana-server index a9e2988bdb7..dc63e1ef4c6 100755 --- a/packaging/rpm/init.d/grafana-server +++ b/packaging/rpm/init.d/grafana-server @@ -96,7 +96,7 @@ case "$1" in if [ $return -eq 0 ] then sleep 1 - # check if pid file has been written two + # check if pid file has been written to if ! [[ -s $PID_FILE ]]; then echo "FAILED" exit 1 diff --git a/pkg/api/annotations.go b/pkg/api/annotations.go index be069f2b07e..e6454e9cf86 100644 --- a/pkg/api/annotations.go +++ b/pkg/api/annotations.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "strings" "time" @@ -41,9 +40,22 @@ func GetAnnotations(c *middleware.Context) Response { return Json(200, items) } +type CreateAnnotationError struct { + message string +} + +func (e *CreateAnnotationError) Error() string { + return e.message +} + func PostAnnotation(c *middleware.Context, cmd dtos.PostAnnotationsCmd) Response { repo := annotations.GetRepository() + if cmd.Text == "" { + err := &CreateAnnotationError{"text field should not be empty"} + return ApiError(500, "Failed to save annotation", err) + } + item := annotations.Item{ OrgId: c.OrgId, UserId: c.UserId, @@ -55,6 +67,10 @@ func PostAnnotation(c *middleware.Context, cmd dtos.PostAnnotationsCmd) Response Tags: cmd.Tags, } + if item.Epoch == 0 { + item.Epoch = time.Now().Unix() + } + if err := repo.Save(&item); err != nil { return ApiError(500, "Failed to save annotation", err) } @@ -82,21 +98,22 @@ func PostAnnotation(c *middleware.Context, cmd dtos.PostAnnotationsCmd) Response return ApiSuccess("Annotation added") } -type GraphiteAnnotationError struct { - message string -} - -func (e *GraphiteAnnotationError) Error() string { - return e.message -} - func formatGraphiteAnnotation(what string, data string) string { - return fmt.Sprintf("%s\n%s", what, data) + text := what + if data != "" { + text = text + "\n" + data + } + return text } func PostGraphiteAnnotation(c *middleware.Context, cmd dtos.PostGraphiteAnnotationsCmd) Response { repo := annotations.GetRepository() + if cmd.What == "" { + err := &CreateAnnotationError{"what field should not be empty"} + return ApiError(500, "Failed to save Graphite annotation", err) + } + if cmd.When == 0 { cmd.When = time.Now().Unix() } @@ -106,18 +123,22 @@ func PostGraphiteAnnotation(c *middleware.Context, cmd dtos.PostGraphiteAnnotati var tagsArray []string switch tags := cmd.Tags.(type) { case string: - tagsArray = strings.Split(tags, " ") + if tags != "" { + tagsArray = strings.Split(tags, " ") + } else { + tagsArray = []string{} + } case []interface{}: for _, t := range tags { if tagStr, ok := t.(string); ok { tagsArray = append(tagsArray, tagStr) } else { - err := &GraphiteAnnotationError{"tag should be a string"} + err := &CreateAnnotationError{"tag should be a string"} return ApiError(500, "Failed to save Graphite annotation", err) } } default: - err := &GraphiteAnnotationError{"unsupported tags format"} + err := &CreateAnnotationError{"unsupported tags format"} return ApiError(500, "Failed to save Graphite annotation", err) } @@ -133,7 +154,7 @@ func PostGraphiteAnnotation(c *middleware.Context, cmd dtos.PostGraphiteAnnotati return ApiError(500, "Failed to save Graphite annotation", err) } - return ApiSuccess("Graphite Annotation added") + return ApiSuccess("Graphite annotation added") } func UpdateAnnotation(c *middleware.Context, cmd dtos.UpdateAnnotationsCmd) Response { diff --git a/pkg/models/datasource.go b/pkg/models/datasource.go index 069900cc091..ee365582734 100644 --- a/pkg/models/datasource.go +++ b/pkg/models/datasource.go @@ -18,6 +18,7 @@ const ( DS_KAIROSDB = "kairosdb" DS_PROMETHEUS = "prometheus" DS_POSTGRES = "postgres" + DS_MYSQL = "mysql" DS_ACCESS_DIRECT = "direct" DS_ACCESS_PROXY = "proxy" ) @@ -64,6 +65,7 @@ var knownDatasourcePlugins map[string]bool = map[string]bool{ DS_PROMETHEUS: true, DS_OPENTSDB: true, DS_POSTGRES: true, + DS_MYSQL: true, "opennms": true, "druid": true, "dalmatinerdb": true, diff --git a/pkg/services/alerting/notifiers/sensu.go b/pkg/services/alerting/notifiers/sensu.go index a4c41e14bae..9f77801d458 100644 --- a/pkg/services/alerting/notifiers/sensu.go +++ b/pkg/services/alerting/notifiers/sensu.go @@ -112,7 +112,7 @@ func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error { } if evalContext.Rule.Message != "" { - bodyJSON.Set("message", evalContext.Rule.Message) + bodyJSON.Set("output", evalContext.Rule.Message) } body, _ := bodyJSON.MarshalJSON() diff --git a/pkg/tsdb/postgres/macros.go b/pkg/tsdb/postgres/macros.go index 3ca31591cb0..21400b03dfd 100644 --- a/pkg/tsdb/postgres/macros.go +++ b/pkg/tsdb/postgres/macros.go @@ -74,7 +74,7 @@ func (m *PostgresMacroEngine) evaluateMacro(name string, args []string) (string, if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= to_timestamp(%d) AND %s <= to_timestamp(%d)", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("extract(epoch from %s) BETWEEN %d AND %d", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__timeFrom": return fmt.Sprintf("to_timestamp(%d)", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil case "__timeTo": @@ -83,7 +83,7 @@ func (m *PostgresMacroEngine) evaluateMacro(name string, args []string) (string, if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) } - return fmt.Sprintf("(extract(epoch from \"%s\")/extract(epoch from %s::interval))::int", args[0], args[1]), nil + return fmt.Sprintf("(extract(epoch from \"%s\")/extract(epoch from %s::interval))::int*extract(epoch from %s::interval)", args[0], args[1], args[1]), nil case "__unixEpochFilter": if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) diff --git a/pkg/tsdb/postgres/macros_test.go b/pkg/tsdb/postgres/macros_test.go index f181780e6ea..ba991e6f2d5 100644 --- a/pkg/tsdb/postgres/macros_test.go +++ b/pkg/tsdb/postgres/macros_test.go @@ -30,7 +30,7 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, "WHERE time_column >= to_timestamp(18446744066914186738) AND time_column <= to_timestamp(18446744066914187038)") + So(sql, ShouldEqual, "WHERE extract(epoch from time_column) BETWEEN 18446744066914186738 AND 18446744066914187038") }) Convey("interpolate __timeFrom function", func() { @@ -45,7 +45,7 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(timeRange, "GROUP BY $__timeGroup(time_column,'5m')") So(err, ShouldBeNil) - So(sql, ShouldEqual, "GROUP BY (extract(epoch from \"time_column\")/extract(epoch from '5m'::interval))::int") + So(sql, ShouldEqual, "GROUP BY (extract(epoch from \"time_column\")/extract(epoch from '5m'::interval))::int*extract(epoch from '5m'::interval)") }) Convey("interpolate __timeTo function", func() { diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts new file mode 100644 index 00000000000..543b3a93d31 --- /dev/null +++ b/public/app/core/angular_wrappers.ts @@ -0,0 +1,8 @@ +import { react2AngularDirective } from 'app/core/utils/react2angular'; +import { PasswordStrength } from './ui/PasswordStrength'; + +export function registerAngularDirectives() { + + react2AngularDirective('passwordStrength', PasswordStrength, ['password']); + +} diff --git a/public/app/core/core.ts b/public/app/core/core.ts index ed845599991..39f13d30153 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -43,7 +43,6 @@ import {assignModelProperties} from './utils/model_utils'; import {contextSrv} from './services/context_srv'; import {KeybindingSrv} from './services/keybindingSrv'; import {helpModal} from './components/help/help'; -import {PasswordStrength} from './components/PasswordStrength'; import {JsonExplorer} from './components/json_explorer/json_explorer'; import {NavModelSrv, NavModel} from './nav_model_srv'; import {userPicker} from './components/user_picker'; @@ -52,6 +51,9 @@ import {geminiScrollbar} from './components/scroll/scroll'; import {gfPageDirective} from './components/gf_page'; import {orgSwitcher} from './components/org_switcher'; import {profiler} from './profiler'; +import {registerAngularDirectives} from './angular_wrappers'; + +registerAngularDirectives(); export { profiler, @@ -82,6 +84,5 @@ export { userGroupPicker, geminiScrollbar, gfPageDirective, - orgSwitcher, - PasswordStrength, + orgSwitcher }; diff --git a/public/app/core/directives/misc.js b/public/app/core/directives/misc.js index c43d1dd614c..f8c18ca847c 100644 --- a/public/app/core/directives/misc.js +++ b/public/app/core/directives/misc.js @@ -7,6 +7,8 @@ define([ function (angular, require, coreModule, kbn) { 'use strict'; + kbn = kbn.default; + coreModule.default.directive('tip', function($compile) { return { restrict: 'E', diff --git a/public/app/core/directives/ng_model_on_blur.js b/public/app/core/directives/ng_model_on_blur.js index 09c87f5fabe..b3c73e54dba 100644 --- a/public/app/core/directives/ng_model_on_blur.js +++ b/public/app/core/directives/ng_model_on_blur.js @@ -1,9 +1,8 @@ define([ '../core_module', - 'app/core/utils/kbn', 'app/core/utils/rangeutil', ], -function (coreModule, kbn, rangeUtil) { +function (coreModule, rangeUtil) { 'use strict'; coreModule.default.directive('ngModelOnblur', function() { diff --git a/public/app/core/specs/PasswordStrength_specs.tsx b/public/app/core/specs/PasswordStrength.jest.tsx similarity index 60% rename from public/app/core/specs/PasswordStrength_specs.tsx rename to public/app/core/specs/PasswordStrength.jest.tsx index 7a46d6b153f..b5f730e6bdd 100644 --- a/public/app/core/specs/PasswordStrength_specs.tsx +++ b/public/app/core/specs/PasswordStrength.jest.tsx @@ -1,24 +1,23 @@ import React from 'react'; -import {describe, it, expect} from 'test/lib/common'; import {shallow} from 'enzyme'; -import {PasswordStrength} from '../components/PasswordStrength'; +import {PasswordStrength} from '../ui/PasswordStrength'; describe('PasswordStrength', () => { it('should have class bad if length below 4', () => { const wrapper = shallow(); - expect(wrapper.find(".password-strength-bad")).to.have.length(1); + expect(wrapper.find(".password-strength-bad")).toHaveLength(1); }); it('should have class ok if length below 8', () => { const wrapper = shallow(); - expect(wrapper.find(".password-strength-ok")).to.have.length(1); + expect(wrapper.find(".password-strength-ok")).toHaveLength(1); }); it('should have class good if length above 8', () => { const wrapper = shallow(); - expect(wrapper.find(".password-strength-good")).to.have.length(1); + expect(wrapper.find(".password-strength-good")).toHaveLength(1); }); }); diff --git a/public/test/core/utils/datemath_specs.ts b/public/app/core/specs/datemath.jest.ts similarity index 71% rename from public/test/core/utils/datemath_specs.ts rename to public/app/core/specs/datemath.jest.ts index f00befa5457..0b752876882 100644 --- a/public/test/core/utils/datemath_specs.ts +++ b/public/app/core/specs/datemath.jest.ts @@ -1,4 +1,4 @@ -import {describe, beforeEach, afterEach, it, sinon, expect} from 'test/lib/common'; +import sinon from 'sinon'; import * as dateMath from 'app/core/utils/datemath'; import moment from 'moment'; @@ -13,25 +13,25 @@ describe("DateMath", () => { describe('errors', () => { it('should return undefined if passed something falsy', () => { - expect(dateMath.parse(false)).to.be(undefined); + expect(dateMath.parse(false)).toBe(undefined); }); it('should return undefined if I pass an operator besides [+-/]', () => { - expect(dateMath.parse('now&1d')).to.be(undefined); + expect(dateMath.parse('now&1d')).toBe(undefined); }); it('should return undefined if I pass a unit besides' + spans.toString(), () => { - expect(dateMath.parse('now+5f')).to.be(undefined); + expect(dateMath.parse('now+5f')).toBe(undefined); }); it('should return undefined if rounding unit is not 1', () => { - expect(dateMath.parse('now/2y')).to.be(undefined); - expect(dateMath.parse('now/0.5y')).to.be(undefined); + expect(dateMath.parse('now/2y')).toBe(undefined); + expect(dateMath.parse('now/0.5y')).toBe(undefined); }); it('should not go into an infinite loop when missing a unit', () => { - expect(dateMath.parse('now-0')).to.be(undefined); - expect(dateMath.parse('now-00')).to.be(undefined); + expect(dateMath.parse('now-0')).toBe(undefined); + expect(dateMath.parse('now-00')).toBe(undefined); }); }); @@ -43,7 +43,7 @@ describe("DateMath", () => { expected.setMilliseconds(0); var startOfDay = dateMath.parse('now/d', false).valueOf(); - expect(startOfDay).to.be(expected.getTime()); + expect(startOfDay).toBe(expected.getTime()); }); it("now/d on a utc dashboard should be start of the current day in UTC time", () => { @@ -51,7 +51,7 @@ describe("DateMath", () => { var expected = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0)); var startOfDay = dateMath.parse('now/d', false, 'utc').valueOf(); - expect(startOfDay).to.be(expected.getTime()); + expect(startOfDay).toBe(expected.getTime()); }); describe('subtraction', () => { @@ -69,11 +69,11 @@ describe("DateMath", () => { var thenEx = anchor + '||-5' + span; it('should return 5' + span + ' ago', () => { - expect(dateMath.parse(nowEx).format(format)).to.eql(now.subtract(5, span).format(format)); + expect(dateMath.parse(nowEx).format(format)).toEqual(now.subtract(5, span).format(format)); }); it('should return 5' + span + ' before ' + anchor, () => { - expect(dateMath.parse(thenEx).format(format)).to.eql(anchored.subtract(5, span).format(format)); + expect(dateMath.parse(thenEx).format(format)).toEqual(anchored.subtract(5, span).format(format)); }); }); @@ -94,11 +94,11 @@ describe("DateMath", () => { _.each(spans, (span) => { it('should round now to the beginning of the ' + span, function () { - expect(dateMath.parse('now/' + span).format(format)).to.eql(now.startOf(span).format(format)); + expect(dateMath.parse('now/' + span).format(format)).toEqual(now.startOf(span).format(format)); }); it('should round now to the end of the ' + span, function () { - expect(dateMath.parse('now/' + span, true).format(format)).to.eql(now.endOf(span).format(format)); + expect(dateMath.parse('now/' + span, true).format(format)).toEqual(now.endOf(span).format(format)); }); }); @@ -109,27 +109,27 @@ describe("DateMath", () => { describe('isValid', () => { it('should return false when invalid date text', () => { - expect(dateMath.isValid('asd')).to.be(false); + expect(dateMath.isValid('asd')).toBe(false); }); it('should return true when valid date text', () => { - expect(dateMath.isValid('now-1h')).to.be(true); + expect(dateMath.isValid('now-1h')).toBe(true); }); }); describe('relative time to date parsing', function() { it('should handle negative time', function() { var date = dateMath.parseDateMath('-2d', moment([2014, 1, 5])); - expect(date.valueOf()).to.equal(moment([2014, 1, 3]).valueOf()); + expect(date.valueOf()).toEqual(moment([2014, 1, 3]).valueOf()); }); it('should handle multiple math expressions', function() { var date = dateMath.parseDateMath('-2d-6h', moment([2014, 1, 5])); - expect(date.valueOf()).to.equal(moment([2014, 1, 2, 18]).valueOf()); + expect(date.valueOf()).toEqual(moment([2014, 1, 2, 18]).valueOf()); }); it('should return false when invalid expression', function() { var date = dateMath.parseDateMath('2', moment([2014, 1, 5])); - expect(date).to.equal(undefined); + expect(date).toEqual(undefined); }); }); diff --git a/public/test/core/utils/emitter_specs.ts b/public/app/core/specs/emitter.jest.ts similarity index 79% rename from public/test/core/utils/emitter_specs.ts rename to public/app/core/specs/emitter.jest.ts index 39559fdc32d..1de59852fcc 100644 --- a/public/test/core/utils/emitter_specs.ts +++ b/public/app/core/specs/emitter.jest.ts @@ -1,6 +1,4 @@ -import {describe, it, expect} from 'test/lib/common'; - -import {Emitter} from 'app/core/core'; +import {Emitter} from '../utils/emitter'; describe("Emitter", () => { @@ -20,8 +18,8 @@ describe("Emitter", () => { events.emit('test', null); - expect(sub1Called).to.be(true); - expect(sub2Called).to.be(true); + expect(sub1Called).toBe(true); + expect(sub2Called).toBe(true); }); it('when subscribing twice', () => { @@ -37,7 +35,7 @@ describe("Emitter", () => { events.emit('test', null); - expect(sub1Called).to.be(2); + expect(sub1Called).toBe(2); }); it('should handle errors', () => { @@ -57,8 +55,8 @@ describe("Emitter", () => { try { events.emit('test', null); } catch (_) { } try { events.emit('test', null); } catch (_) {} - expect(sub1Called).to.be(2); - expect(sub2Called).to.be(0); + expect(sub1Called).toBe(2); + expect(sub2Called).toBe(0); }); }); }); diff --git a/public/test/core/utils/flatten_specs.ts b/public/app/core/specs/flatten.jest.ts similarity index 57% rename from public/test/core/utils/flatten_specs.ts rename to public/app/core/specs/flatten.jest.ts index 5dc6c1535af..7fe07128d1e 100644 --- a/public/test/core/utils/flatten_specs.ts +++ b/public/app/core/specs/flatten.jest.ts @@ -1,5 +1,3 @@ -import {describe, it, expect} from 'test/lib/common'; - import flatten from 'app/core/utils/flatten'; describe("flatten", () => { @@ -15,9 +13,9 @@ describe("flatten", () => { } }, null); - expect(flattened['level1']).to.be('level1-value'); - expect(flattened['deeper.level2']).to.be('level2-value'); - expect(flattened['deeper.deeper.level3']).to.be('level3-value'); + expect(flattened['level1']).toBe('level1-value'); + expect(flattened['deeper.level2']).toBe('level2-value'); + expect(flattened['deeper.deeper.level3']).toBe('level3-value'); }); }); diff --git a/public/app/core/specs/kbn.jest.ts b/public/app/core/specs/kbn.jest.ts new file mode 100644 index 00000000000..efeaeaee8b2 --- /dev/null +++ b/public/app/core/specs/kbn.jest.ts @@ -0,0 +1,347 @@ +import kbn from '../utils/kbn'; +import * as dateMath from '../utils/datemath'; +import moment from 'moment'; + +describe('unit format menu', function() { + var menu = kbn.getUnitFormats(); + menu.map(function(submenu) { + + describe('submenu ' + submenu.text, function() { + + it('should have a title', function() { + expect(typeof submenu.text).toBe('string'); + }); + + it('should have a submenu', function() { + expect(Array.isArray(submenu.submenu)).toBe(true); + }); + + submenu.submenu.map(function(entry) { + describe('entry ' + entry.text, function() { + it('should have a title', function() { expect(typeof entry.text).toBe('string'); }); + it('should have a format', function() { expect(typeof entry.value).toBe('string'); }); + it('should have a valid format', function() { + expect(typeof kbn.valueFormats[entry.value]).toBe('function'); + }); + }); + }); + }); + }); +}); + +function describeValueFormat(desc, value, tickSize, tickDecimals, result) { + + describe('value format: ' + desc, function() { + it('should translate ' + value + ' as ' + result, function() { + var scaledDecimals = tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10); + var str = kbn.valueFormats[desc](value, tickDecimals, scaledDecimals); + expect(str).toBe(result); + }); + }); + +} + +describeValueFormat('ms', 0.0024, 0.0005, 4, '0.0024 ms'); +describeValueFormat('ms', 100, 1, 0, '100 ms'); +describeValueFormat('ms', 1250, 10, 0, '1.25 s'); +describeValueFormat('ms', 1250, 300, 0, '1.3 s'); +describeValueFormat('ms', 65150, 10000, 0, '1.1 min'); +describeValueFormat('ms', 6515000, 1500000, 0, '1.8 hour'); +describeValueFormat('ms', 651500000, 150000000, 0, '8 day'); + +describeValueFormat('none', 2.75e-10, 0, 10, '3e-10'); +describeValueFormat('none', 0, 0, 2, '0'); +describeValueFormat('dB', 10, 1000, 2, '10.00 dB'); + +describeValueFormat('percent', 0, 0, 0, '0%'); +describeValueFormat('percent', 53, 0, 1, '53.0%'); +describeValueFormat('percentunit', 0.0, 0, 0, '0%'); +describeValueFormat('percentunit', 0.278, 0, 1, '27.8%'); +describeValueFormat('percentunit', 1.0, 0, 0, '100%'); + +describeValueFormat('currencyUSD', 7.42, 10000, 2, '$7.42'); +describeValueFormat('currencyUSD', 1532.82, 1000, 1, '$1.53K'); +describeValueFormat('currencyUSD', 18520408.7, 10000000, 0, '$19M'); + +describeValueFormat('bytes', -1.57e+308, -1.57e+308, 2, 'NA'); + +describeValueFormat('ns', 25, 1, 0, '25 ns'); +describeValueFormat('ns', 2558, 50, 0, '2.56 µs'); + +describeValueFormat('ops', 123, 1, 0, '123 ops'); +describeValueFormat('rps', 456000, 1000, -1, '456K rps'); +describeValueFormat('rps', 123456789, 1000000, 2, '123.457M rps'); +describeValueFormat('wps', 789000000, 1000000, -1, '789M wps'); +describeValueFormat('iops', 11000000000, 1000000000, -1, '11B iops'); + +describeValueFormat('s', 1.23456789e-7, 1e-10, 8, '123.5 ns'); +describeValueFormat('s', 1.23456789e-4, 1e-7, 5, '123.5 µs'); +describeValueFormat('s', 1.23456789e-3, 1e-6, 4, '1.235 ms'); +describeValueFormat('s', 1.23456789e-2, 1e-5, 3, '12.35 ms'); +describeValueFormat('s', 1.23456789e-1, 1e-4, 2, '123.5 ms'); +describeValueFormat('s', 24, 1, 0, '24 s'); +describeValueFormat('s', 246, 1, 0, '4.1 min'); +describeValueFormat('s', 24567, 100, 0, '6.82 hour'); +describeValueFormat('s', 24567890, 10000, 0, '40.62 week'); +describeValueFormat('s', 24567890000, 1000000, 0, '778.53 year'); + +describeValueFormat('m', 24, 1, 0, '24 min'); +describeValueFormat('m', 246, 10, 0, '4.1 hour'); +describeValueFormat('m', 6545, 10, 0, '4.55 day'); +describeValueFormat('m', 24567, 100, 0, '2.44 week'); +describeValueFormat('m', 24567892, 10000, 0, '46.7 year'); + +describeValueFormat('h', 21, 1, 0, '21 hour'); +describeValueFormat('h', 145, 1, 0, '6.04 day'); +describeValueFormat('h', 1234, 100, 0, '7.3 week'); +describeValueFormat('h', 9458, 1000, 0, '1.08 year'); + +describeValueFormat('d', 3, 1, 0, '3 day'); +describeValueFormat('d', 245, 100, 0, '35 week'); +describeValueFormat('d', 2456, 10, 0, '6.73 year'); + +describe('date time formats', function() { + it('should format as iso date', function() { + var str = kbn.valueFormats.dateTimeAsIso(1505634997920, 1); + expect(str).toBe(moment(1505634997920).format('YYYY-MM-DD HH:mm:ss')); + }); + + it('should format as iso date and skip date when today', function() { + var now = moment(); + var str = kbn.valueFormats.dateTimeAsIso(now.valueOf(), 1); + expect(str).toBe(now.format("HH:mm:ss")); + }); + + it('should format as US date', function() { + var str = kbn.valueFormats.dateTimeAsUS(1505634997920, 1); + expect(str).toBe(moment(1505634997920).format('MM/DD/YYYY H:mm:ss a')); + }); + + it('should format as US date and skip date when today', function() { + var now = moment(); + var str = kbn.valueFormats.dateTimeAsUS(now.valueOf(), 1); + expect(str).toBe(now.format("h:mm:ss a")); + }); + + it('should format as from now with days', function() { + var daysAgo = moment().add(-7, 'd'); + var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1); + expect(str).toBe('7 days ago'); + }); + + it('should format as from now with minutes', function() { + var daysAgo = moment().add(-2, 'm'); + var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1); + expect(str).toBe('2 minutes ago'); + }); +}); + +describe('kbn.toFixed and negative decimals', function() { + it('should treat as zero decimals', function() { + var str = kbn.toFixed(186.123, -2); + expect(str).toBe('186'); + }); +}); + +describe('kbn ms format when scaled decimals is null do not use it', function() { + it('should use specified decimals', function() { + var str = kbn.valueFormats['ms'](10000086.123, 1, null); + expect(str).toBe('2.8 hour'); + }); +}); + +describe('kbn kbytes format when scaled decimals is null do not use it', function() { + it('should use specified decimals', function() { + var str = kbn.valueFormats['kbytes'](10000000, 3, null); + expect(str).toBe('9.537 GiB'); + }); +}); + +describe('kbn deckbytes format when scaled decimals is null do not use it', function() { + it('should use specified decimals', function() { + var str = kbn.valueFormats['deckbytes'](10000000, 3, null); + expect(str).toBe('10.000 GB'); + }); +}); + +describe('kbn roundValue', function() { + it('should should handle null value', function() { + var str = kbn.roundValue(null, 2); + expect(str).toBe(null); + }); + it('should round value', function() { + var str = kbn.roundValue(200.877, 2); + expect(str).toBe(200.88); + }); +}); + +describe('calculateInterval', function() { + it('1h 100 resultion', function() { + var range = { from: dateMath.parse('now-1h'), to: dateMath.parse('now') }; + var res = kbn.calculateInterval(range, 100, null); + expect(res.interval).toBe('30s'); + }); + + it('10m 1600 resolution', function() { + var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') }; + var res = kbn.calculateInterval(range, 1600, null); + expect(res.interval).toBe('500ms'); + expect(res.intervalMs).toBe(500); + }); + + it('fixed user min interval', function() { + var range = {from: dateMath.parse('now-10m'), to: dateMath.parse('now')}; + var res = kbn.calculateInterval(range, 1600, '10s'); + expect(res.interval).toBe('10s'); + expect(res.intervalMs).toBe(10000); + }); + + it('short time range and user low limit', function() { + var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') }; + var res = kbn.calculateInterval(range, 1600, '>10s'); + expect(res.interval).toBe('10s'); + }); + + it('large time range and user low limit', function() { + var range = {from: dateMath.parse('now-14d'), to: dateMath.parse('now')}; + var res = kbn.calculateInterval(range, 1000, '>10s'); + expect(res.interval).toBe('20m'); + }); + + it('10s 900 resolution and user low limit in ms', function() { + var range = { from: dateMath.parse('now-10s'), to: dateMath.parse('now') }; + var res = kbn.calculateInterval(range, 900, '>15ms'); + expect(res.interval).toBe('15ms'); + }); + + it('1d 1 resolution', function() { + var range = { from: dateMath.parse('now-1d'), to: dateMath.parse('now') }; + var res = kbn.calculateInterval(range, 1, null); + expect(res.interval).toBe('1d'); + expect(res.intervalMs).toBe(86400000); + }); + + it('86399s 1 resolution', function() { + var range = { from: dateMath.parse('now-86390s'), to: dateMath.parse('now') }; + var res = kbn.calculateInterval(range, 1, null); + expect(res.interval).toBe('12h'); + expect(res.intervalMs).toBe(43200000); + }); +}); + +describe('hex', function() { + it('positive integer', function() { + var str = kbn.valueFormats.hex(100, 0); + expect(str).toBe('64'); + }); + it('negative integer', function() { + var str = kbn.valueFormats.hex(-100, 0); + expect(str).toBe('-64'); + }); + it('null', function() { + var str = kbn.valueFormats.hex(null, 0); + expect(str).toBe(''); + }); + it('positive float', function() { + var str = kbn.valueFormats.hex(50.52, 1); + expect(str).toBe('32.8'); + }); + it('negative float', function() { + var str = kbn.valueFormats.hex(-50.333, 2); + expect(str).toBe('-32.547AE147AE14'); + }); +}); + +describe('hex 0x', function() { + it('positive integeter', function() { + var str = kbn.valueFormats.hex0x(7999,0); + expect(str).toBe('0x1F3F'); + }); + it('negative integer', function() { + var str = kbn.valueFormats.hex0x(-584,0); + expect(str).toBe('-0x248'); + }); + it('null', function() { + var str = kbn.valueFormats.hex0x(null, 0); + expect(str).toBe(''); + }); + it('positive float', function() { + var str = kbn.valueFormats.hex0x(74.443, 3); + expect(str).toBe('0x4A.716872B020C4'); + }); + it('negative float', function() { + var str = kbn.valueFormats.hex0x(-65.458, 1); + expect(str).toBe('-0x41.8'); + }); +}); + +describe('duration', function() { + it('null', function() { + var str = kbn.toDuration(null, 0, "millisecond"); + expect(str).toBe(''); + }); + it('0 milliseconds', function() { + var str = kbn.toDuration(0, 0, "millisecond"); + expect(str).toBe('0 milliseconds'); + }); + it('1 millisecond', function() { + var str = kbn.toDuration(1, 0, "millisecond"); + expect(str).toBe('1 millisecond'); + }); + it('-1 millisecond', function() { + var str = kbn.toDuration(-1, 0, "millisecond"); + expect(str).toBe('1 millisecond ago'); + }); + it('seconds', function() { + var str = kbn.toDuration(1, 0, "second"); + expect(str).toBe('1 second'); + }); + it('minutes', function() { + var str = kbn.toDuration(1, 0, "minute"); + expect(str).toBe('1 minute'); + }); + it('hours', function() { + var str = kbn.toDuration(1, 0, "hour"); + expect(str).toBe('1 hour'); + }); + it('days', function() { + var str = kbn.toDuration(1, 0, "day"); + expect(str).toBe('1 day'); + }); + it('weeks', function() { + var str = kbn.toDuration(1, 0, "week"); + expect(str).toBe('1 week'); + }); + it('months', function() { + var str = kbn.toDuration(1, 0, "month"); + expect(str).toBe('1 month'); + }); + it('years', function() { + var str = kbn.toDuration(1, 0, "year"); + expect(str).toBe('1 year'); + }); + it('decimal days', function() { + var str = kbn.toDuration(1.5, 2, "day"); + expect(str).toBe('1 day, 12 hours, 0 minutes'); + }); + it('decimal months', function() { + var str = kbn.toDuration(1.5, 3, "month"); + expect(str).toBe('1 month, 2 weeks, 1 day, 0 hours'); + }); + it('no decimals', function() { + var str = kbn.toDuration(38898367008, 0, "millisecond"); + expect(str).toBe('1 year'); + }); + it('1 decimal', function() { + var str = kbn.toDuration(38898367008, 1, "millisecond"); + expect(str).toBe('1 year, 2 months'); + }); + it('too many decimals', function() { + var str = kbn.toDuration(38898367008, 20, "millisecond"); + expect(str).toBe('1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds, 8 milliseconds'); + }); + it('floating point error', function() { + var str = kbn.toDuration(36993906007, 8, "millisecond"); + expect(str).toBe('1 year, 2 months, 0 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds'); + }); +}); diff --git a/public/test/core/utils/rangeutil_specs.ts b/public/app/core/specs/rangeutil.jest.ts similarity index 70% rename from public/test/core/utils/rangeutil_specs.ts rename to public/app/core/specs/rangeutil.jest.ts index f2ce34f0edd..eb9dcfc3ace 100644 --- a/public/test/core/utils/rangeutil_specs.ts +++ b/public/app/core/specs/rangeutil.jest.ts @@ -1,5 +1,3 @@ -import {describe, it, expect} from 'test/lib/common'; - import * as rangeUtil from 'app/core/utils/rangeutil'; import _ from 'lodash'; import moment from 'moment'; @@ -9,100 +7,100 @@ describe("rangeUtil", () => { describe("Can get range grouped list of ranges", () => { it('when custom settings should return default range list', () => { var groups = rangeUtil.getRelativeTimesList({time_options: []}, 'Last 5 minutes'); - expect(_.keys(groups).length).to.be(4); - expect(groups[3][0].active).to.be(true); + expect(_.keys(groups).length).toBe(4); + expect(groups[3][0].active).toBe(true); }); }); describe("Can get range text described", () => { it('should handle simple old expression with only amount and unit', () => { var info = rangeUtil.describeTextRange('5m'); - expect(info.display).to.be('Last 5 minutes'); + expect(info.display).toBe('Last 5 minutes'); }); it('should have singular when amount is 1', () => { var info = rangeUtil.describeTextRange('1h'); - expect(info.display).to.be('Last 1 hour'); + expect(info.display).toBe('Last 1 hour'); }); it('should handle non default amount', () => { var info = rangeUtil.describeTextRange('13h'); - expect(info.display).to.be('Last 13 hours'); - expect(info.from).to.be('now-13h'); + expect(info.display).toBe('Last 13 hours'); + expect(info.from).toBe('now-13h'); }); it('should handle non default future amount', () => { var info = rangeUtil.describeTextRange('+3h'); - expect(info.display).to.be('Next 3 hours'); - expect(info.from).to.be('now'); - expect(info.to).to.be('now+3h'); + expect(info.display).toBe('Next 3 hours'); + expect(info.from).toBe('now'); + expect(info.to).toBe('now+3h'); }); it('should handle now/d', () => { var info = rangeUtil.describeTextRange('now/d'); - expect(info.display).to.be('Today so far'); + expect(info.display).toBe('Today so far'); }); it('should handle now/w', () => { var info = rangeUtil.describeTextRange('now/w'); - expect(info.display).to.be('This week so far'); + expect(info.display).toBe('This week so far'); }); it('should handle now/M', () => { var info = rangeUtil.describeTextRange('now/M'); - expect(info.display).to.be('This month so far'); + expect(info.display).toBe('This month so far'); }); it('should handle now/y', () => { var info = rangeUtil.describeTextRange('now/y'); - expect(info.display).to.be('This year so far'); + expect(info.display).toBe('This year so far'); }); }); describe("Can get date range described", () => { it('Date range with simple ranges', () => { var text = rangeUtil.describeTimeRange({from: 'now-1h', to: 'now'}); - expect(text).to.be('Last 1 hour'); + expect(text).toBe('Last 1 hour'); }); it('Date range with rounding ranges', () => { var text = rangeUtil.describeTimeRange({from: 'now/d+6h', to: 'now'}); - expect(text).to.be('now/d+6h to now'); + expect(text).toBe('now/d+6h to now'); }); it('Date range with absolute to now', () => { var text = rangeUtil.describeTimeRange({from: moment([2014,10,10,2,3,4]), to: 'now'}); - expect(text).to.be('Nov 10, 2014 02:03:04 to a few seconds ago'); + expect(text).toBe('Nov 10, 2014 02:03:04 to a few seconds ago'); }); it('Date range with absolute to relative', () => { var text = rangeUtil.describeTimeRange({from: moment([2014,10,10,2,3,4]), to: 'now-1d'}); - expect(text).to.be('Nov 10, 2014 02:03:04 to a day ago'); + expect(text).toBe('Nov 10, 2014 02:03:04 to a day ago'); }); it('Date range with relative to absolute', () => { var text = rangeUtil.describeTimeRange({from: 'now-7d', to: moment([2014,10,10,2,3,4])}); - expect(text).to.be('7 days ago to Nov 10, 2014 02:03:04'); + expect(text).toBe('7 days ago to Nov 10, 2014 02:03:04'); }); it('Date range with non matching default ranges', () => { var text = rangeUtil.describeTimeRange({from: 'now-13h', to: 'now'}); - expect(text).to.be('Last 13 hours'); + expect(text).toBe('Last 13 hours'); }); it('Date range with from and to both are in now-* format', () => { var text = rangeUtil.describeTimeRange({from: 'now-6h', to: 'now-3h'}); - expect(text).to.be('now-6h to now-3h'); + expect(text).toBe('now-6h to now-3h'); }); it('Date range with from and to both are either in now-* or now/* format', () => { var text = rangeUtil.describeTimeRange({from: 'now/d+6h', to: 'now-3h'}); - expect(text).to.be('now/d+6h to now-3h'); + expect(text).toBe('now/d+6h to now-3h'); }); it('Date range with from and to both are either in now-* or now+* format', () => { var text = rangeUtil.describeTimeRange({from: 'now-6h', to: 'now+1h'}); - expect(text).to.be('now-6h to now+1h'); + expect(text).toBe('now-6h to now+1h'); }); }); diff --git a/public/test/core/table_model_specs.ts b/public/app/core/specs/table_model.jest.ts similarity index 65% rename from public/test/core/table_model_specs.ts rename to public/app/core/specs/table_model.jest.ts index 20a90498d50..05c9fbda039 100644 --- a/public/test/core/table_model_specs.ts +++ b/public/app/core/specs/table_model.jest.ts @@ -1,5 +1,3 @@ -import {describe, beforeEach, it, expect} from 'test/lib/common'; - import TableModel from 'app/core/table_model'; describe('when sorting table desc', () => { @@ -16,14 +14,14 @@ describe('when sorting table desc', () => { }); it('should sort by time', () => { - expect(table.rows[0][0]).to.be(105); - expect(table.rows[1][0]).to.be(103); - expect(table.rows[2][0]).to.be(100); + expect(table.rows[0][0]).toBe(105); + expect(table.rows[1][0]).toBe(103); + expect(table.rows[2][0]).toBe(100); }); it ('should mark column being sorted', () => { - expect(table.columns[0].sort).to.be(true); - expect(table.columns[0].desc).to.be(true); + expect(table.columns[0].sort).toBe(true); + expect(table.columns[0].desc).toBe(true); }); }); @@ -42,9 +40,9 @@ describe('when sorting table asc', () => { }); it('should sort by time', () => { - expect(table.rows[0][1]).to.be(10); - expect(table.rows[1][1]).to.be(11); - expect(table.rows[2][1]).to.be(15); + expect(table.rows[0][1]).toBe(10); + expect(table.rows[1][1]).toBe(11); + expect(table.rows[2][1]).toBe(15); }); }); diff --git a/public/app/core/specs/time_series_specs.ts b/public/app/core/specs/time_series.jest.ts similarity index 79% rename from public/app/core/specs/time_series_specs.ts rename to public/app/core/specs/time_series.jest.ts index ddf08a9f704..1a199daa59f 100644 --- a/public/app/core/specs/time_series_specs.ts +++ b/public/app/core/specs/time_series.jest.ts @@ -1,4 +1,3 @@ -import {describe, beforeEach, it, expect} from 'test/lib/common'; import TimeSeries from 'app/core/time_series2'; describe("TimeSeries", function() { @@ -19,14 +18,14 @@ describe("TimeSeries", function() { it('with connected style, should ignore nulls', function() { series = new TimeSeries(testData); points = series.getFlotPairs('connected', yAxisFormats); - expect(points.length).to.be(3); + expect(points.length).toBe(3); }); it('with null as zero style, should replace nulls with zero', function() { series = new TimeSeries(testData); points = series.getFlotPairs('null as zero', yAxisFormats); - expect(points.length).to.be(4); - expect(points[1][1]).to.be(0); + expect(points.length).toBe(4); + expect(points[1][1]).toBe(0); }); it('if last is null current should pick next to last', function() { @@ -34,7 +33,7 @@ describe("TimeSeries", function() { datapoints: [[10,1], [null, 2]] }); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.current).to.be(10); + expect(series.stats.current).toBe(10); }); it('max value should work for negative values', function() { @@ -42,13 +41,13 @@ describe("TimeSeries", function() { datapoints: [[-10,1], [-4, 2]] }); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.max).to.be(-4); + expect(series.stats.max).toBe(-4); }); it('average value should ignore nulls', function() { series = new TimeSeries(testData); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.avg).to.be(6.333333333333333); + expect(series.stats.avg).toBe(6.333333333333333); }); it('the delta value should account for nulls', function() { @@ -56,7 +55,7 @@ describe("TimeSeries", function() { datapoints: [[1,2],[3,3],[null,4],[10,5],[15,6]] }); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.delta).to.be(14); + expect(series.stats.delta).toBe(14); }); it('the delta value should account for nulls on first', function() { @@ -64,7 +63,7 @@ describe("TimeSeries", function() { datapoints: [[null,2],[1,3],[10,4],[15,5]] }); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.delta).to.be(14); + expect(series.stats.delta).toBe(14); }); it('the delta value should account for nulls on last', function() { @@ -72,7 +71,7 @@ describe("TimeSeries", function() { datapoints: [[1,2],[5,3],[10,4],[null,5]] }); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.delta).to.be(9); + expect(series.stats.delta).toBe(9); }); it('the delta value should account for resets', function() { @@ -80,7 +79,7 @@ describe("TimeSeries", function() { datapoints: [[1,2],[5,3],[10,4],[0,5],[10,6]] }); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.delta).to.be(19); + expect(series.stats.delta).toBe(19); }); it('the delta value should account for resets on last', function() { @@ -88,30 +87,30 @@ describe("TimeSeries", function() { datapoints: [[1,2],[2,3],[10,4],[8,5]] }); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.delta).to.be(17); + expect(series.stats.delta).toBe(17); }); it('the range value should be max - min', function() { series = new TimeSeries(testData); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.range).to.be(9); + expect(series.stats.range).toBe(9); }); it('first value should ingone nulls', function() { series = new TimeSeries(testData); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.first).to.be(1); + expect(series.stats.first).toBe(1); series = new TimeSeries({ datapoints: [[null,2],[1,3],[10,4],[8,5]] }); series.getFlotPairs('null', yAxisFormats); - expect(series.stats.first).to.be(1); + expect(series.stats.first).toBe(1); }); it('with null as zero style, average value should treat nulls as 0', function() { series = new TimeSeries(testData); series.getFlotPairs('null as zero', yAxisFormats); - expect(series.stats.avg).to.be(4.75); + expect(series.stats.avg).toBe(4.75); }); it('average value should be null if all values is null', function() { @@ -119,7 +118,7 @@ describe("TimeSeries", function() { datapoints: [[null,2],[null,3],[null,4],[null,5]] }); series.getFlotPairs('null'); - expect(series.stats.avg).to.be(null); + expect(series.stats.avg).toBe(null); }); }); @@ -130,7 +129,7 @@ describe("TimeSeries", function() { }); it('should set hasMsResolution to false', function() { - expect(series.hasMsResolution).to.be(false); + expect(series.hasMsResolution).toBe(false); }); }); @@ -140,7 +139,7 @@ describe("TimeSeries", function() { }); it('should show millisecond resolution tooltip', function() { - expect(series.hasMsResolution).to.be(true); + expect(series.hasMsResolution).toBe(true); }); }); @@ -150,7 +149,7 @@ describe("TimeSeries", function() { }); it('should not show millisecond resolution tooltip', function() { - expect(series.hasMsResolution).to.be(false); + expect(series.hasMsResolution).toBe(false); }); }); }); @@ -165,13 +164,13 @@ describe("TimeSeries", function() { it('missing datapoint with ms precision', function() { fakedata.datapoints[0] = [1337, 1234567890000]; series = new TimeSeries(fakedata); - expect(series.isMsResolutionNeeded()).to.be(false); + expect(series.isMsResolutionNeeded()).toBe(false); }); it('contains datapoint with ms precision', function() { fakedata.datapoints[0] = [1337, 1236547890001]; series = new TimeSeries(fakedata); - expect(series.isMsResolutionNeeded()).to.be(true); + expect(series.isMsResolutionNeeded()).toBe(true); }); }); @@ -188,8 +187,8 @@ describe("TimeSeries", function() { }); it('should set fill zero, and enable points', function() { - expect(series.lines.fill).to.be(0.001); - expect(series.points.show).to.be(true); + expect(series.lines.fill).toBe(0.001); + expect(series.points.show).toBe(true); }); }); @@ -200,8 +199,8 @@ describe("TimeSeries", function() { }); it('should disable lines, and enable bars', function() { - expect(series.lines.show).to.be(false); - expect(series.bars.show).to.be(true); + expect(series.lines.show).toBe(false); + expect(series.bars.show).toBe(true); }); }); @@ -212,8 +211,8 @@ describe("TimeSeries", function() { }); it('should disable stack, and set lineWidth', function() { - expect(series.stack).to.be(false); - expect(series.lines.lineWidth).to.be(5); + expect(series.stack).toBe(false); + expect(series.lines.lineWidth).toBe(5); }); }); @@ -224,9 +223,9 @@ describe("TimeSeries", function() { }); it('should enable dashes, set dashes lineWidth to 5 and lines lineWidth to 0', function() { - expect(series.dashes.show).to.be(true); - expect(series.dashes.lineWidth).to.be(5); - expect(series.lines.lineWidth).to.be(0); + expect(series.dashes.show).toBe(true); + expect(series.dashes.lineWidth).toBe(5); + expect(series.lines.lineWidth).toBe(0); }); }); @@ -237,7 +236,7 @@ describe("TimeSeries", function() { }); it('should disable line fill and add fillBelowTo', function() { - expect(series.fillBelowTo).to.be('min'); + expect(series.fillBelowTo).toBe('min'); }); }); @@ -248,8 +247,8 @@ describe("TimeSeries", function() { }); it('should set pointradius, and set steppedLine', function() { - expect(series.points.radius).to.be(5); - expect(series.lines.steps).to.be(true); + expect(series.points.radius).toBe(5); + expect(series.lines.steps).toBe(true); }); }); @@ -260,7 +259,7 @@ describe("TimeSeries", function() { }); it('should match second series', function() { - expect(series.lines.show).to.be(false); + expect(series.lines.show).toBe(false); }); }); @@ -271,11 +270,11 @@ describe("TimeSeries", function() { }); it('should set yaxis', function() { - expect(series.yaxis).to.be(2); + expect(series.yaxis).toBe(2); }); it('should set zindex', function() { - expect(series.zindex).to.be(2); + expect(series.zindex).toBe(2); }); }); @@ -288,11 +287,11 @@ describe("TimeSeries", function() { }); it('should format non-numeric values as empty string', function() { - expect(series.formatValue(null)).to.be(""); - expect(series.formatValue(undefined)).to.be(""); - expect(series.formatValue(NaN)).to.be(""); - expect(series.formatValue(Infinity)).to.be(""); - expect(series.formatValue(-Infinity)).to.be(""); + expect(series.formatValue(null)).toBe(""); + expect(series.formatValue(undefined)).toBe(""); + expect(series.formatValue(NaN)).toBe(""); + expect(series.formatValue(Infinity)).toBe(""); + expect(series.formatValue(-Infinity)).toBe(""); }); }); diff --git a/public/app/core/time_series.js b/public/app/core/time_series.js deleted file mode 100644 index 75feab32971..00000000000 --- a/public/app/core/time_series.js +++ /dev/null @@ -1,7 +0,0 @@ -define([ - './time_series2' -], function(timeSeries) { - 'use strict'; - // backward compatability hack; - return timeSeries.default; -}); diff --git a/public/app/core/components/PasswordStrength.tsx b/public/app/core/ui/PasswordStrength.tsx similarity index 83% rename from public/app/core/components/PasswordStrength.tsx rename to public/app/core/ui/PasswordStrength.tsx index 3f690f3d239..1fede3e5cd5 100644 --- a/public/app/core/components/PasswordStrength.tsx +++ b/public/app/core/ui/PasswordStrength.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { react2AngularDirective } from 'app/core/utils/react2angular'; export interface IProps { password: string; @@ -33,5 +32,4 @@ export class PasswordStrength extends React.Component { } } -react2AngularDirective('passwordStrength', PasswordStrength, ['password']); diff --git a/public/app/core/utils/kbn.js b/public/app/core/utils/kbn.js deleted file mode 100644 index 1daaa2f8ba3..00000000000 --- a/public/app/core/utils/kbn.js +++ /dev/null @@ -1,936 +0,0 @@ -define([ - 'jquery', - 'lodash', - 'moment' -], -function($, _, moment) { - 'use strict'; - - var kbn = {}; - kbn.valueFormats = {}; - - kbn.regexEscape = function(value) { - return value.replace(/[\\^$*+?.()|[\]{}\/]/g, '\\$&'); - }; - - ///// HELPER FUNCTIONS ///// - - kbn.round_interval = function(interval) { - switch (true) { - // 0.015s - case (interval < 15): - return 10; // 0.01s - // 0.035s - case (interval < 35): - return 20; // 0.02s - // 0.075s - case (interval < 75): - return 50; // 0.05s - // 0.15s - case (interval < 150): - return 100; // 0.1s - // 0.35s - case (interval < 350): - return 200; // 0.2s - // 0.75s - case (interval < 750): - return 500; // 0.5s - // 1.5s - case (interval < 1500): - return 1000; // 1s - // 3.5s - case (interval < 3500): - return 2000; // 2s - // 7.5s - case (interval < 7500): - return 5000; // 5s - // 12.5s - case (interval < 12500): - return 10000; // 10s - // 17.5s - case (interval < 17500): - return 15000; // 15s - // 25s - case (interval < 25000): - return 20000; // 20s - // 45s - case (interval < 45000): - return 30000; // 30s - // 1.5m - case (interval < 90000): - return 60000; // 1m - // 3.5m - case (interval < 210000): - return 120000; // 2m - // 7.5m - case (interval < 450000): - return 300000; // 5m - // 12.5m - case (interval < 750000): - return 600000; // 10m - // 12.5m - case (interval < 1050000): - return 900000; // 15m - // 25m - case (interval < 1500000): - return 1200000; // 20m - // 45m - case (interval < 2700000): - return 1800000; // 30m - // 1.5h - case (interval < 5400000): - return 3600000; // 1h - // 2.5h - case (interval < 9000000): - return 7200000; // 2h - // 4.5h - case (interval < 16200000): - return 10800000; // 3h - // 9h - case (interval < 32400000): - return 21600000; // 6h - // 1d - case (interval < 86400000): - return 43200000; // 12h - // 1w - case (interval < 604800000): - return 86400000; // 1d - // 3w - case (interval < 1814400000): - return 604800000; // 1w - // 6w - case (interval < 3628800000): - return 2592000000; // 30d - default: - return 31536000000; // 1y - } - }; - - kbn.secondsToHms = function(seconds) { - var numyears = Math.floor(seconds / 31536000); - if(numyears){ - return numyears + 'y'; - } - var numdays = Math.floor((seconds % 31536000) / 86400); - if(numdays){ - return numdays + 'd'; - } - var numhours = Math.floor(((seconds % 31536000) % 86400) / 3600); - if(numhours){ - return numhours + 'h'; - } - var numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60); - if(numminutes){ - return numminutes + 'm'; - } - var numseconds = Math.floor((((seconds % 31536000) % 86400) % 3600) % 60); - if(numseconds){ - return numseconds + 's'; - } - var nummilliseconds = Math.floor(seconds * 1000.0); - if(nummilliseconds){ - return nummilliseconds + 'ms'; - } - - return 'less than a millisecond'; //'just now' //or other string you like; - }; - - kbn.to_percent = function(number,outof) { - return Math.floor((number/outof)*10000)/100 + "%"; - }; - - kbn.addslashes = function(str) { - str = str.replace(/\\/g, '\\\\'); - str = str.replace(/\'/g, '\\\''); - str = str.replace(/\"/g, '\\"'); - str = str.replace(/\0/g, '\\0'); - return str; - }; - - kbn.interval_regex = /(\d+(?:\.\d+)?)(ms|[Mwdhmsy])/; - - // histogram & trends - kbn.intervals_in_seconds = { - y: 31536000, - M: 2592000, - w: 604800, - d: 86400, - h: 3600, - m: 60, - s: 1, - ms: 0.001 - }; - - kbn.calculateInterval = function(range, resolution, lowLimitInterval) { - var lowLimitMs = 1; // 1 millisecond default low limit - var intervalMs; - - if (lowLimitInterval) { - if (lowLimitInterval[0] === '>') { - lowLimitInterval = lowLimitInterval.slice(1); - } - lowLimitMs = kbn.interval_to_ms(lowLimitInterval); - } - - intervalMs = kbn.round_interval((range.to.valueOf() - range.from.valueOf()) / resolution); - if (lowLimitMs > intervalMs) { - intervalMs = lowLimitMs; - } - - return { - intervalMs: intervalMs, - interval: kbn.secondsToHms(intervalMs / 1000), - }; - }; - - kbn.describe_interval = function (string) { - var matches = string.match(kbn.interval_regex); - if (!matches || !_.has(kbn.intervals_in_seconds, matches[2])) { - throw new Error('Invalid interval string, expecting a number followed by one of "Mwdhmsy"'); - } else { - return { - sec: kbn.intervals_in_seconds[matches[2]], - type: matches[2], - count: parseInt(matches[1], 10) - }; - } - }; - - kbn.interval_to_ms = function(string) { - var info = kbn.describe_interval(string); - return info.sec * 1000 * info.count; - }; - - kbn.interval_to_seconds = function (string) { - var info = kbn.describe_interval(string); - return info.sec * info.count; - }; - - kbn.query_color_dot = function (color, diameter) { - return '
'; - }; - - kbn.slugifyForUrl = function(str) { - return str - .toLowerCase() - .replace(/[^\w ]+/g,'') - .replace(/ +/g,'-'); - }; - - kbn.stringToJsRegex = function(str) { - if (str[0] !== '/') { - return new RegExp('^' + str + '$'); - } - - var match = str.match(new RegExp('^/(.*?)/(g?i?m?y?)$')); - return new RegExp(match[1], match[2]); - }; - - kbn.toFixed = function(value, decimals) { - if (value === null) { - return ""; - } - - var factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1; - var formatted = String(Math.round(value * factor) / factor); - - // if exponent return directly - if (formatted.indexOf('e') !== -1 || value === 0) { - return formatted; - } - - // If tickDecimals was specified, ensure that we have exactly that - // much precision; otherwise default to the value's own precision. - if (decimals != null) { - var decimalPos = formatted.indexOf("."); - var precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1; - if (precision < decimals) { - return (precision ? formatted : formatted + ".") + (String(factor)).substr(1, decimals - precision); - } - } - - return formatted; - }; - - kbn.toFixedScaled = function(value, decimals, scaledDecimals, additionalDecimals, ext) { - if (scaledDecimals === null) { - return kbn.toFixed(value, decimals) + ext; - } else { - return kbn.toFixed(value, scaledDecimals + additionalDecimals) + ext; - } - }; - - kbn.roundValue = function (num, decimals) { - if (num === null) { return null; } - var n = Math.pow(10, decimals); - return Math.round((n * num).toFixed(decimals)) / n; - }; - - ///// FORMAT FUNCTION CONSTRUCTORS ///// - - kbn.formatBuilders = {}; - - // Formatter which always appends a fixed unit string to the value. No - // scaling of the value is performed. - kbn.formatBuilders.fixedUnit = function(unit) { - return function(size, decimals) { - if (size === null) { return ""; } - return kbn.toFixed(size, decimals) + ' ' + unit; - }; - }; - - // Formatter which scales the unit string geometrically according to the given - // numeric factor. Repeatedly scales the value down by the factor until it is - // less than the factor in magnitude, or the end of the array is reached. - kbn.formatBuilders.scaledUnits = function(factor, extArray) { - return function(size, decimals, scaledDecimals) { - if (size === null) { - return ""; - } - - var steps = 0; - var limit = extArray.length; - - while (Math.abs(size) >= factor) { - steps++; - size /= factor; - - if (steps >= limit) { return "NA"; } - } - - if (steps > 0 && scaledDecimals !== null) { - decimals = scaledDecimals + (3 * steps); - } - - return kbn.toFixed(size, decimals) + extArray[steps]; - }; - }; - - // Extension of the scaledUnits builder which uses SI decimal prefixes. If an - // offset is given, it adjusts the starting units at the given prefix; a value - // of 0 starts at no scale; -3 drops to nano, +2 starts at mega, etc. - kbn.formatBuilders.decimalSIPrefix = function(unit, offset) { - var prefixes = ['n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; - prefixes = prefixes.slice(3 + (offset || 0)); - var units = prefixes.map(function(p) { return ' ' + p + unit; }); - return kbn.formatBuilders.scaledUnits(1000, units); - }; - - // Extension of the scaledUnits builder which uses SI binary prefixes. If - // offset is given, it starts the units at the given prefix; otherwise, the - // offset defaults to zero and the initial unit is not prefixed. - kbn.formatBuilders.binarySIPrefix = function(unit, offset) { - var prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'].slice(offset); - var units = prefixes.map(function(p) { return ' ' + p + unit; }); - return kbn.formatBuilders.scaledUnits(1024, units); - }; - - // Currency formatter for prefixing a symbol onto a number. Supports scaling - // up to the trillions. - kbn.formatBuilders.currency = function(symbol) { - var units = ['', 'K', 'M', 'B', 'T']; - var scaler = kbn.formatBuilders.scaledUnits(1000, units); - return function(size, decimals, scaledDecimals) { - if (size === null) { return ""; } - var scaled = scaler(size, decimals, scaledDecimals); - return symbol + scaled; - }; - }; - - kbn.formatBuilders.simpleCountUnit = function(symbol) { - var units = ['', 'K', 'M', 'B', 'T']; - var scaler = kbn.formatBuilders.scaledUnits(1000, units); - return function(size, decimals, scaledDecimals) { - if (size === null) { return ""; } - var scaled = scaler(size, decimals, scaledDecimals); - return scaled + " " + symbol; - }; - }; - - ///// VALUE FORMATS ///// - - // Dimensionless Units - kbn.valueFormats.none = kbn.toFixed; - kbn.valueFormats.short = kbn.formatBuilders.scaledUnits(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Quadr', ' Quint', ' Sext', ' Sept']); - kbn.valueFormats.dB = kbn.formatBuilders.fixedUnit('dB'); - kbn.valueFormats.ppm = kbn.formatBuilders.fixedUnit('ppm'); - - kbn.valueFormats.percent = function(size, decimals) { - if (size === null) { return ""; } - return kbn.toFixed(size, decimals) + '%'; - }; - - kbn.valueFormats.percentunit = function(size, decimals) { - if (size === null) { return ""; } - return kbn.toFixed(100*size, decimals) + '%'; - }; - - /* Formats the value to hex. Uses float if specified decimals are not 0. - * There are two options, one with 0x, and one without */ - - kbn.valueFormats.hex = function(value, decimals) { - if (value == null) { return ""; } - return parseFloat(kbn.toFixed(value, decimals)).toString(16).toUpperCase(); - }; - - kbn.valueFormats.hex0x = function(value, decimals) { - if (value == null) { return ""; } - var hexString = kbn.valueFormats.hex(value, decimals); - if (hexString.substring(0,1) === "-") { - return "-0x" + hexString.substring(1); - } - return "0x" + hexString; - }; - - kbn.valueFormats.sci = function(value, decimals) { - return value.toExponential(decimals); - }; - - kbn.valueFormats.locale = function(value, decimals) { - return value.toLocaleString(undefined, {maximumFractionDigits: decimals}); - }; - - // Currencies - kbn.valueFormats.currencyUSD = kbn.formatBuilders.currency('$'); - kbn.valueFormats.currencyGBP = kbn.formatBuilders.currency('£'); - kbn.valueFormats.currencyEUR = kbn.formatBuilders.currency('€'); - kbn.valueFormats.currencyJPY = kbn.formatBuilders.currency('¥'); - kbn.valueFormats.currencyRUB = kbn.formatBuilders.currency('₽'); - kbn.valueFormats.currencyUAH = kbn.formatBuilders.currency('₴'); - kbn.valueFormats.currencyBRL = kbn.formatBuilders.currency('R$'); - kbn.valueFormats.currencyDKK = kbn.formatBuilders.currency('kr'); - kbn.valueFormats.currencyISK = kbn.formatBuilders.currency('kr'); - kbn.valueFormats.currencyNOK = kbn.formatBuilders.currency('kr'); - kbn.valueFormats.currencySEK = kbn.formatBuilders.currency('kr'); - - // Data (Binary) - kbn.valueFormats.bits = kbn.formatBuilders.binarySIPrefix('b'); - kbn.valueFormats.bytes = kbn.formatBuilders.binarySIPrefix('B'); - kbn.valueFormats.kbytes = kbn.formatBuilders.binarySIPrefix('B', 1); - kbn.valueFormats.mbytes = kbn.formatBuilders.binarySIPrefix('B', 2); - kbn.valueFormats.gbytes = kbn.formatBuilders.binarySIPrefix('B', 3); - - // Data (Decimal) - kbn.valueFormats.decbits = kbn.formatBuilders.decimalSIPrefix('b'); - kbn.valueFormats.decbytes = kbn.formatBuilders.decimalSIPrefix('B'); - kbn.valueFormats.deckbytes = kbn.formatBuilders.decimalSIPrefix('B', 1); - kbn.valueFormats.decmbytes = kbn.formatBuilders.decimalSIPrefix('B', 2); - kbn.valueFormats.decgbytes = kbn.formatBuilders.decimalSIPrefix('B', 3); - - // Data Rate - kbn.valueFormats.pps = kbn.formatBuilders.decimalSIPrefix('pps'); - kbn.valueFormats.bps = kbn.formatBuilders.decimalSIPrefix('bps'); - kbn.valueFormats.Bps = kbn.formatBuilders.decimalSIPrefix('Bps'); - kbn.valueFormats.KBs = kbn.formatBuilders.decimalSIPrefix('Bs', 1); - kbn.valueFormats.Kbits = kbn.formatBuilders.decimalSIPrefix('bps', 1); - kbn.valueFormats.MBs = kbn.formatBuilders.decimalSIPrefix('Bs', 2); - kbn.valueFormats.Mbits = kbn.formatBuilders.decimalSIPrefix('bps', 2); - kbn.valueFormats.GBs = kbn.formatBuilders.decimalSIPrefix('Bs', 3); - kbn.valueFormats.Gbits = kbn.formatBuilders.decimalSIPrefix('bps', 3); - - // Throughput - kbn.valueFormats.ops = kbn.formatBuilders.simpleCountUnit('ops'); - kbn.valueFormats.rps = kbn.formatBuilders.simpleCountUnit('rps'); - kbn.valueFormats.wps = kbn.formatBuilders.simpleCountUnit('wps'); - kbn.valueFormats.iops = kbn.formatBuilders.simpleCountUnit('iops'); - kbn.valueFormats.opm = kbn.formatBuilders.simpleCountUnit('opm'); - kbn.valueFormats.rpm = kbn.formatBuilders.simpleCountUnit('rpm'); - kbn.valueFormats.wpm = kbn.formatBuilders.simpleCountUnit('wpm'); - - // Energy - kbn.valueFormats.watt = kbn.formatBuilders.decimalSIPrefix('W'); - kbn.valueFormats.kwatt = kbn.formatBuilders.decimalSIPrefix('W', 1); - kbn.valueFormats.voltamp = kbn.formatBuilders.decimalSIPrefix('VA'); - kbn.valueFormats.kvoltamp = kbn.formatBuilders.decimalSIPrefix('VA', 1); - kbn.valueFormats.voltampreact = kbn.formatBuilders.decimalSIPrefix('var'); - kbn.valueFormats.kvoltampreact = kbn.formatBuilders.decimalSIPrefix('var', 1); - kbn.valueFormats.watth = kbn.formatBuilders.decimalSIPrefix('Wh'); - kbn.valueFormats.kwatth = kbn.formatBuilders.decimalSIPrefix('Wh', 1); - kbn.valueFormats.joule = kbn.formatBuilders.decimalSIPrefix('J'); - kbn.valueFormats.ev = kbn.formatBuilders.decimalSIPrefix('eV'); - kbn.valueFormats.amp = kbn.formatBuilders.decimalSIPrefix('A'); - kbn.valueFormats.kamp = kbn.formatBuilders.decimalSIPrefix('A', 1); - kbn.valueFormats.volt = kbn.formatBuilders.decimalSIPrefix('V'); - kbn.valueFormats.kvolt = kbn.formatBuilders.decimalSIPrefix('V', 1); - kbn.valueFormats.dBm = kbn.formatBuilders.decimalSIPrefix('dBm'); - - // Temperature - kbn.valueFormats.celsius = kbn.formatBuilders.fixedUnit('°C'); - kbn.valueFormats.farenheit = kbn.formatBuilders.fixedUnit('°F'); - kbn.valueFormats.kelvin = kbn.formatBuilders.fixedUnit('K'); - kbn.valueFormats.humidity = kbn.formatBuilders.fixedUnit('%H'); - - // Pressure - kbn.valueFormats.pressurebar = kbn.formatBuilders.decimalSIPrefix('bar'); - kbn.valueFormats.pressurembar = kbn.formatBuilders.decimalSIPrefix('bar', -1); - kbn.valueFormats.pressurekbar = kbn.formatBuilders.decimalSIPrefix('bar', 1); - kbn.valueFormats.pressurehpa = kbn.formatBuilders.fixedUnit('hPa'); - kbn.valueFormats.pressurehg = kbn.formatBuilders.fixedUnit('"Hg'); - kbn.valueFormats.pressurepsi = kbn.formatBuilders.scaledUnits(1000, [' psi', ' ksi', ' Mpsi']); - - // Force - kbn.valueFormats.forceNm = kbn.formatBuilders.decimalSIPrefix('Nm'); - kbn.valueFormats.forcekNm = kbn.formatBuilders.decimalSIPrefix('Nm', 1); - kbn.valueFormats.forceN = kbn.formatBuilders.decimalSIPrefix('N'); - kbn.valueFormats.forcekN = kbn.formatBuilders.decimalSIPrefix('N', 1); - - // Length - kbn.valueFormats.lengthm = kbn.formatBuilders.decimalSIPrefix('m'); - kbn.valueFormats.lengthmm = kbn.formatBuilders.decimalSIPrefix('m', -1); - kbn.valueFormats.lengthkm = kbn.formatBuilders.decimalSIPrefix('m', 1); - kbn.valueFormats.lengthmi = kbn.formatBuilders.fixedUnit('mi'); - - // Mass - kbn.valueFormats.massmg = kbn.formatBuilders.decimalSIPrefix('g', -1); - kbn.valueFormats.massg = kbn.formatBuilders.decimalSIPrefix('g'); - kbn.valueFormats.masskg = kbn.formatBuilders.decimalSIPrefix('g', 1); - kbn.valueFormats.masst = kbn.formatBuilders.fixedUnit('t'); - - // Velocity - kbn.valueFormats.velocityms = kbn.formatBuilders.fixedUnit('m/s'); - kbn.valueFormats.velocitykmh = kbn.formatBuilders.fixedUnit('km/h'); - kbn.valueFormats.velocitymph = kbn.formatBuilders.fixedUnit('mph'); - kbn.valueFormats.velocityknot = kbn.formatBuilders.fixedUnit('kn'); - - // Volume - kbn.valueFormats.litre = kbn.formatBuilders.decimalSIPrefix('L'); - kbn.valueFormats.mlitre = kbn.formatBuilders.decimalSIPrefix('L', -1); - kbn.valueFormats.m3 = kbn.formatBuilders.decimalSIPrefix('m3'); - kbn.valueFormats.dm3 = kbn.formatBuilders.decimalSIPrefix('dm3'); - kbn.valueFormats.gallons = kbn.formatBuilders.fixedUnit('gal'); - - // Flow - kbn.valueFormats.flowgpm = kbn.formatBuilders.fixedUnit('gpm'); - kbn.valueFormats.flowcms = kbn.formatBuilders.fixedUnit('cms'); - kbn.valueFormats.flowcfs = kbn.formatBuilders.fixedUnit('cfs'); - kbn.valueFormats.flowcfm = kbn.formatBuilders.fixedUnit('cfm'); - - // Time - kbn.valueFormats.hertz = kbn.formatBuilders.decimalSIPrefix('Hz'); - - kbn.valueFormats.ms = function(size, decimals, scaledDecimals) { - if (size === null) { return ""; } - - if (Math.abs(size) < 1000) { - return kbn.toFixed(size, decimals) + " ms"; - } - // Less than 1 min - else if (Math.abs(size) < 60000) { - return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, " s"); - } - // Less than 1 hour, devide in minutes - else if (Math.abs(size) < 3600000) { - return kbn.toFixedScaled(size / 60000, decimals, scaledDecimals, 5, " min"); - } - // Less than one day, devide in hours - else if (Math.abs(size) < 86400000) { - return kbn.toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, " hour"); - } - // Less than one year, devide in days - else if (Math.abs(size) < 31536000000) { - return kbn.toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, " day"); - } - - return kbn.toFixedScaled(size / 31536000000, decimals, scaledDecimals, 10, " year"); - }; - - kbn.valueFormats.s = function(size, decimals, scaledDecimals) { - if (size === null) { return ""; } - - // Less than 1 µs, devide in ns - if (Math.abs(size) < 0.000001) { - return kbn.toFixedScaled(size * 1.e9, decimals, scaledDecimals - decimals, -9, " ns"); - } - // Less than 1 ms, devide in µs - if (Math.abs(size) < 0.001) { - return kbn.toFixedScaled(size * 1.e6, decimals, scaledDecimals - decimals, -6, " µs"); - } - // Less than 1 second, devide in ms - if (Math.abs(size) < 1) { - return kbn.toFixedScaled(size * 1.e3, decimals, scaledDecimals - decimals, -3, " ms"); - } - - if (Math.abs(size) < 60) { - return kbn.toFixed(size, decimals) + " s"; - } - // Less than 1 hour, devide in minutes - else if (Math.abs(size) < 3600) { - return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 1, " min"); - } - // Less than one day, devide in hours - else if (Math.abs(size) < 86400) { - return kbn.toFixedScaled(size / 3600, decimals, scaledDecimals, 4, " hour"); - } - // Less than one week, devide in days - else if (Math.abs(size) < 604800) { - return kbn.toFixedScaled(size / 86400, decimals, scaledDecimals, 5, " day"); - } - // Less than one year, devide in week - else if (Math.abs(size) < 31536000) { - return kbn.toFixedScaled(size / 604800, decimals, scaledDecimals, 6, " week"); - } - - return kbn.toFixedScaled(size / 3.15569e7, decimals, scaledDecimals, 7, " year"); - }; - - kbn.valueFormats['µs'] = function(size, decimals, scaledDecimals) { - if (size === null) { return ""; } - - if (Math.abs(size) < 1000) { - return kbn.toFixed(size, decimals) + " µs"; - } - else if (Math.abs(size) < 1000000) { - return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, " ms"); - } - else { - return kbn.toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, " s"); - } - }; - - kbn.valueFormats.ns = function(size, decimals, scaledDecimals) { - if (size === null) { return ""; } - - if (Math.abs(size) < 1000) { - return kbn.toFixed(size, decimals) + " ns"; - } - else if (Math.abs(size) < 1000000) { - return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, " µs"); - } - else if (Math.abs(size) < 1000000000) { - return kbn.toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, " ms"); - } - else if (Math.abs(size) < 60000000000){ - return kbn.toFixedScaled(size / 1000000000, decimals, scaledDecimals, 9, " s"); - } - else { - return kbn.toFixedScaled(size / 60000000000, decimals, scaledDecimals, 12, " min"); - } - }; - - kbn.valueFormats.m = function(size, decimals, scaledDecimals) { - if (size === null) { return ""; } - - if (Math.abs(size) < 60) { - return kbn.toFixed(size, decimals) + " min"; - } - else if (Math.abs(size) < 1440) { - return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 2, " hour"); - } - else if (Math.abs(size) < 10080) { - return kbn.toFixedScaled(size / 1440, decimals, scaledDecimals, 3, " day"); - } - else if (Math.abs(size) < 604800) { - return kbn.toFixedScaled(size / 10080, decimals, scaledDecimals, 4, " week"); - } - else { - return kbn.toFixedScaled(size / 5.25948e5, decimals, scaledDecimals, 5, " year"); - } - }; - - kbn.valueFormats.h = function(size, decimals, scaledDecimals) { - if (size === null) { return ""; } - - if (Math.abs(size) < 24) { - return kbn.toFixed(size, decimals) + " hour"; - } - else if (Math.abs(size) < 168) { - return kbn.toFixedScaled(size / 24, decimals, scaledDecimals, 2, " day"); - } - else if (Math.abs(size) < 8760) { - return kbn.toFixedScaled(size / 168, decimals, scaledDecimals, 3, " week"); - } - else { - return kbn.toFixedScaled(size / 8760, decimals, scaledDecimals, 4, " year"); - } - }; - - kbn.valueFormats.d = function(size, decimals, scaledDecimals) { - if (size === null) { return ""; } - - if (Math.abs(size) < 7) { - return kbn.toFixed(size, decimals) + " day"; - } - else if (Math.abs(size) < 365) { - return kbn.toFixedScaled(size / 7, decimals, scaledDecimals, 2, " week"); - } - else { - return kbn.toFixedScaled(size / 365, decimals, scaledDecimals, 3, " year"); - } - }; - - kbn.toDuration = function(size, decimals, timeScale) { - if (size === null) { return ""; } - if (size === 0) { return "0 " + timeScale + "s"; } - if (size < 0) { return kbn.toDuration(-size, decimals, timeScale) + " ago"; } - - var units = [ - {short: "y", long: "year"}, - {short: "M", long: "month"}, - {short: "w", long: "week"}, - {short: "d", long: "day"}, - {short: "h", long: "hour"}, - {short: "m", long: "minute"}, - {short: "s", long: "second"}, - {short: "ms", long: "millisecond"} - ]; - // convert $size to milliseconds - // intervals_in_seconds uses seconds (duh), convert them to milliseconds here to minimize floating point errors - size *= kbn.intervals_in_seconds[units.find(function(e) { return e.long === timeScale; }).short] * 1000; - - var string = []; - // after first value >= 1 print only $decimals more - var decrementDecimals = false; - for (var i = 0; i < units.length && decimals >= 0; i++) { - var interval = kbn.intervals_in_seconds[units[i].short] * 1000; - var value = size / interval; - if (value >= 1 || decrementDecimals) { - decrementDecimals = true; - var floor = Math.floor(value); - var unit = units[i].long + (floor !== 1 ? "s" : ""); - string.push(floor + " " + unit); - size = size % interval; - decimals--; - } - } - - return string.join(", "); - }; - - kbn.valueFormats.dtdurationms = function(size, decimals) { - return kbn.toDuration(size, decimals, 'millisecond'); - }; - - kbn.valueFormats.dtdurations = function(size, decimals) { - return kbn.toDuration(size, decimals, 'second'); - }; - - kbn.valueFormats.dateTimeAsIso = function(epoch) { - var time = moment(epoch); - - if (moment().isSame(epoch, 'day')) { - return time.format('HH:mm:ss'); - } - return time.format('YYYY-MM-DD HH:mm:ss'); - }; - - kbn.valueFormats.dateTimeAsUS = function(epoch) { - var time = moment(epoch); - - if (moment().isSame(epoch, 'day')) { - return time.format('h:mm:ss a'); - } - return time.format('MM/DD/YYYY h:mm:ss a'); - }; - - kbn.valueFormats.dateTimeFromNow = function(epoch) { - return moment(epoch).fromNow(); - }; - - ///// FORMAT MENU ///// - - kbn.getUnitFormats = function() { - return [ - { - text: 'none', - submenu: [ - {text: 'none' , value: 'none' }, - {text: 'short', value: 'short' }, - {text: 'percent (0-100)', value: 'percent' }, - {text: 'percent (0.0-1.0)', value: 'percentunit'}, - {text: 'Humidity (%H)', value: 'humidity' }, - {text: 'ppm', value: 'ppm' }, - {text: 'decibel', value: 'dB' }, - {text: 'hexadecimal (0x)', value: 'hex0x' }, - {text: 'hexadecimal', value: 'hex' }, - {text: 'scientific notation', value: 'sci' }, - {text: 'locale format', value: 'locale' }, - ] - }, - { - text: 'currency', - submenu: [ - {text: 'Dollars ($)', value: 'currencyUSD'}, - {text: 'Pounds (£)', value: 'currencyGBP'}, - {text: 'Euro (€)', value: 'currencyEUR'}, - {text: 'Yen (¥)', value: 'currencyJPY'}, - {text: 'Rubles (₽)', value: 'currencyRUB'}, - {text: 'Hryvnias (₴)', value: 'currencyUAH'}, - {text: 'Real (R$)', value: 'currencyBRL'}, - {text: 'Danish Krone (kr)', value: 'currencyDKK'}, - {text: 'Icelandic Krone (kr)', value: 'currencyISK'}, - {text: 'Norwegian Krone (kr)', value: 'currencyNOK'}, - {text: 'Swedish Krone (kr)', value: 'currencySEK'}, - ] - }, - { - text: 'time', - submenu: [ - {text: 'Hertz (1/s)', value: 'hertz'}, - {text: 'nanoseconds (ns)' , value: 'ns' }, - {text: 'microseconds (µs)', value: 'µs' }, - {text: 'milliseconds (ms)', value: 'ms' }, - {text: 'seconds (s)', value: 's' }, - {text: 'minutes (m)', value: 'm' }, - {text: 'hours (h)', value: 'h' }, - {text: 'days (d)', value: 'd' }, - {text: 'duration (ms)', value: 'dtdurationms' }, - {text: 'duration (s)', value: 'dtdurations' }, - ] - }, - { - text: 'date & time', - submenu: [ - {text: 'YYYY-MM-DD HH:mm:ss', value: 'dateTimeAsIso' }, - {text: 'DD/MM/YYYY h:mm:ss a', value: 'dateTimeAsUS' }, - {text: 'From Now', value: 'dateTimeFromNow' }, - ] - }, - { - text: 'data (IEC)', - submenu: [ - {text: 'bits', value: 'bits' }, - {text: 'bytes', value: 'bytes' }, - {text: 'kibibytes', value: 'kbytes'}, - {text: 'mebibytes', value: 'mbytes'}, - {text: 'gibibytes', value: 'gbytes'}, - ] - }, - { - text: 'data (Metric)', - submenu: [ - {text: 'bits', value: 'decbits' }, - {text: 'bytes', value: 'decbytes' }, - {text: 'kilobytes', value: 'deckbytes'}, - {text: 'megabytes', value: 'decmbytes'}, - {text: 'gigabytes', value: 'decgbytes'}, - ] - }, - { - text: 'data rate', - submenu: [ - {text: 'packets/sec', value: 'pps'}, - {text: 'bits/sec', value: 'bps'}, - {text: 'bytes/sec', value: 'Bps'}, - {text: 'kilobits/sec', value: 'Kbits'}, - {text: 'kilobytes/sec', value: 'KBs'}, - {text: 'megabits/sec', value: 'Mbits'}, - {text: 'megabytes/sec', value: 'MBs'}, - {text: 'gigabytes/sec', value: 'GBs'}, - {text: 'gigabits/sec', value: 'Gbits'}, - ] - }, - { - text: 'throughput', - submenu: [ - {text: 'ops/sec (ops)', value: 'ops' }, - {text: 'reads/sec (rps)', value: 'rps' }, - {text: 'writes/sec (wps)', value: 'wps' }, - {text: 'I/O ops/sec (iops)', value: 'iops'}, - {text: 'ops/min (opm)', value: 'opm' }, - {text: 'reads/min (rpm)', value: 'rpm' }, - {text: 'writes/min (wpm)', value: 'wpm' }, - ] - }, - { - text: 'length', - submenu: [ - {text: 'millimetre (mm)', value: 'lengthmm'}, - {text: 'meter (m)', value: 'lengthm' }, - {text: 'kilometer (km)', value: 'lengthkm'}, - {text: 'mile (mi)', value: 'lengthmi'}, - ] - }, - { - text: 'mass', - submenu: [ - {text: 'milligram (mg)', value: 'massmg'}, - {text: 'gram (g)', value: 'massg' }, - {text: 'kilogram (kg)', value: 'masskg'}, - {text: 'metric ton (t)', value: 'masst' }, - ] - }, - { - text: 'velocity', - submenu: [ - {text: 'm/s', value: 'velocityms' }, - {text: 'km/h', value: 'velocitykmh' }, - {text: 'mph', value: 'velocitymph' }, - {text: 'knot (kn)', value: 'velocityknot'}, - ] - }, - { - text: 'volume', - submenu: [ - {text: 'millilitre', value: 'mlitre' }, - {text: 'litre', value: 'litre' }, - {text: 'cubic metre', value: 'm3' }, - {text: 'cubic decimetre', value: 'dm3' }, - {text: 'gallons', value: 'gallons'}, - ] - }, - { - text: 'energy', - submenu: [ - {text: 'watt (W)', value: 'watt' }, - {text: 'kilowatt (kW)', value: 'kwatt' }, - {text: 'volt-ampere (VA)', value: 'voltamp' }, - {text: 'kilovolt-ampere (kVA)', value: 'kvoltamp' }, - {text: 'volt-ampere reactive (var)', value: 'voltampreact'}, - {text: 'kilovolt-ampere reactive (kvar)', value: 'kvoltampreact'}, - {text: 'watt-hour (Wh)', value: 'watth' }, - {text: 'kilowatt-hour (kWh)', value: 'kwatth' }, - {text: 'joule (J)', value: 'joule' }, - {text: 'electron volt (eV)', value: 'ev' }, - {text: 'Ampere (A)', value: 'amp' }, - {text: 'Kiloampere (kA)', value: 'kamp' }, - {text: 'Volt (V)', value: 'volt' }, - {text: 'Kilovolt (kV)', value: 'kvolt' }, - {text: 'Decibel-milliwatt (dBm)', value: 'dBm' }, - ] - }, - { - text: 'temperature', - submenu: [ - {text: 'Celsius (°C)', value: 'celsius' }, - {text: 'Farenheit (°F)', value: 'farenheit' }, - {text: 'Kelvin (K)', value: 'kelvin' }, - ] - }, - { - text: 'pressure', - submenu: [ - {text: 'Millibars', value: 'pressurembar'}, - {text: 'Bars', value: 'pressurebar' }, - {text: 'Kilobars', value: 'pressurekbar'}, - {text: 'Hectopascals', value: 'pressurehpa' }, - {text: 'Inches of mercury', value: 'pressurehg' }, - {text: 'PSI', value: 'pressurepsi' }, - ] - }, - { - text: 'force', - submenu: [ - {text: 'Newton-meters (Nm)', value: 'forceNm' }, - {text: 'Kilonewton-meters (kNm)', value: 'forcekNm' }, - {text: 'Newtons (N)', value: 'forceN' }, - {text: 'Kilonewtons (kN)', value: 'forcekN' }, - ] - }, - { - text: 'flow', - submenu: [ - {text: 'Gallons/min (gpm)', value: 'flowgpm' }, - {text: 'Cubic meters/sec (cms)', value: 'flowcms' }, - {text: 'Cubic feet/sec (cfs)', value: 'flowcfs' }, - {text: 'Cubic feet/min (cfm)', value: 'flowcfm' }, - ] - } - ]; - }; - - return kbn; -}); diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts new file mode 100644 index 00000000000..f1c44cfae0e --- /dev/null +++ b/public/app/core/utils/kbn.ts @@ -0,0 +1,968 @@ +import _ from 'lodash'; +import moment from 'moment'; + +var kbn: any = {}; + +kbn.valueFormats = {}; + +kbn.regexEscape = function(value) { + return value.replace(/[\\^$*+?.()|[\]{}\/]/g, '\\$&'); +}; + +///// HELPER FUNCTIONS ///// + +kbn.round_interval = function(interval) { + switch (true) { + // 0.015s + case interval < 15: + return 10; // 0.01s + // 0.035s + case interval < 35: + return 20; // 0.02s + // 0.075s + case interval < 75: + return 50; // 0.05s + // 0.15s + case interval < 150: + return 100; // 0.1s + // 0.35s + case interval < 350: + return 200; // 0.2s + // 0.75s + case interval < 750: + return 500; // 0.5s + // 1.5s + case interval < 1500: + return 1000; // 1s + // 3.5s + case interval < 3500: + return 2000; // 2s + // 7.5s + case interval < 7500: + return 5000; // 5s + // 12.5s + case interval < 12500: + return 10000; // 10s + // 17.5s + case interval < 17500: + return 15000; // 15s + // 25s + case interval < 25000: + return 20000; // 20s + // 45s + case interval < 45000: + return 30000; // 30s + // 1.5m + case interval < 90000: + return 60000; // 1m + // 3.5m + case interval < 210000: + return 120000; // 2m + // 7.5m + case interval < 450000: + return 300000; // 5m + // 12.5m + case interval < 750000: + return 600000; // 10m + // 12.5m + case interval < 1050000: + return 900000; // 15m + // 25m + case interval < 1500000: + return 1200000; // 20m + // 45m + case interval < 2700000: + return 1800000; // 30m + // 1.5h + case interval < 5400000: + return 3600000; // 1h + // 2.5h + case interval < 9000000: + return 7200000; // 2h + // 4.5h + case interval < 16200000: + return 10800000; // 3h + // 9h + case interval < 32400000: + return 21600000; // 6h + // 1d + case interval < 86400000: + return 43200000; // 12h + // 1w + case interval < 604800000: + return 86400000; // 1d + // 3w + case interval < 1814400000: + return 604800000; // 1w + // 6w + case interval < 3628800000: + return 2592000000; // 30d + default: + return 31536000000; // 1y + } +}; + +kbn.secondsToHms = function(seconds) { + var numyears = Math.floor(seconds / 31536000); + if (numyears) { + return numyears + 'y'; + } + var numdays = Math.floor((seconds % 31536000) / 86400); + if (numdays) { + return numdays + 'd'; + } + var numhours = Math.floor(((seconds % 31536000) % 86400) / 3600); + if (numhours) { + return numhours + 'h'; + } + var numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60); + if (numminutes) { + return numminutes + 'm'; + } + var numseconds = Math.floor((((seconds % 31536000) % 86400) % 3600) % 60); + if (numseconds) { + return numseconds + 's'; + } + var nummilliseconds = Math.floor(seconds * 1000.0); + if (nummilliseconds) { + return nummilliseconds + 'ms'; + } + + return 'less than a millisecond'; //'just now' //or other string you like; +}; + +kbn.to_percent = function(nr, outof) { + return Math.floor(nr / outof * 10000) / 100 + '%'; +}; + +kbn.addslashes = function(str) { + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/\'/g, "\\'"); + str = str.replace(/\"/g, '\\"'); + str = str.replace(/\0/g, '\\0'); + return str; +}; + +kbn.interval_regex = /(\d+(?:\.\d+)?)(ms|[Mwdhmsy])/; + +// histogram & trends +kbn.intervals_in_seconds = { + y: 31536000, + M: 2592000, + w: 604800, + d: 86400, + h: 3600, + m: 60, + s: 1, + ms: 0.001, +}; + +kbn.calculateInterval = function(range, resolution, lowLimitInterval) { + var lowLimitMs = 1; // 1 millisecond default low limit + var intervalMs; + + if (lowLimitInterval) { + if (lowLimitInterval[0] === '>') { + lowLimitInterval = lowLimitInterval.slice(1); + } + lowLimitMs = kbn.interval_to_ms(lowLimitInterval); + } + + intervalMs = kbn.round_interval((range.to.valueOf() - range.from.valueOf()) / resolution); + if (lowLimitMs > intervalMs) { + intervalMs = lowLimitMs; + } + + return { + intervalMs: intervalMs, + interval: kbn.secondsToHms(intervalMs / 1000), + }; +}; + +kbn.describe_interval = function(str) { + var matches = str.match(kbn.interval_regex); + if (!matches || !_.has(kbn.intervals_in_seconds, matches[2])) { + throw new Error('Invalid interval string, expecting a number followed by one of "Mwdhmsy"'); + } else { + return { + sec: kbn.intervals_in_seconds[matches[2]], + type: matches[2], + count: parseInt(matches[1], 10), + }; + } +}; + +kbn.interval_to_ms = function(str) { + var info = kbn.describe_interval(str); + return info.sec * 1000 * info.count; +}; + +kbn.interval_to_seconds = function(str) { + var info = kbn.describe_interval(str); + return info.sec * info.count; +}; + +kbn.query_color_dot = function(color, diameter) { + return ( + '
' + ); +}; + +kbn.slugifyForUrl = function(str) { + return str + .toLowerCase() + .replace(/[^\w ]+/g, '') + .replace(/ +/g, '-'); +}; + +kbn.stringToJsRegex = function(str) { + if (str[0] !== '/') { + return new RegExp('^' + str + '$'); + } + + var match = str.match(new RegExp('^/(.*?)/(g?i?m?y?)$')); + return new RegExp(match[1], match[2]); +}; + +kbn.toFixed = function(value, decimals) { + if (value === null) { + return ''; + } + + var factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1; + var formatted = String(Math.round(value * factor) / factor); + + // if exponent return directly + if (formatted.indexOf('e') !== -1 || value === 0) { + return formatted; + } + + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. + if (decimals != null) { + var decimalPos = formatted.indexOf('.'); + var precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1; + if (precision < decimals) { + return (precision ? formatted : formatted + '.') + String(factor).substr(1, decimals - precision); + } + } + + return formatted; +}; + +kbn.toFixedScaled = function(value, decimals, scaledDecimals, additionalDecimals, ext) { + if (scaledDecimals === null) { + return kbn.toFixed(value, decimals) + ext; + } else { + return kbn.toFixed(value, scaledDecimals + additionalDecimals) + ext; + } +}; + +kbn.roundValue = function(num, decimals) { + if (num === null) { + return null; + } + var n = Math.pow(10, decimals); + var formatted = (n * num).toFixed(decimals); + return Math.round(parseFloat(formatted)) / n; +}; + +///// FORMAT FUNCTION CONSTRUCTORS ///// + +kbn.formatBuilders = {}; + +// Formatter which always appends a fixed unit string to the value. No +// scaling of the value is performed. +kbn.formatBuilders.fixedUnit = function(unit) { + return function(size, decimals) { + if (size === null) { + return ''; + } + return kbn.toFixed(size, decimals) + ' ' + unit; + }; +}; + +// Formatter which scales the unit string geometrically according to the given +// numeric factor. Repeatedly scales the value down by the factor until it is +// less than the factor in magnitude, or the end of the array is reached. +kbn.formatBuilders.scaledUnits = function(factor, extArray) { + return function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + + var steps = 0; + var limit = extArray.length; + + while (Math.abs(size) >= factor) { + steps++; + size /= factor; + + if (steps >= limit) { + return 'NA'; + } + } + + if (steps > 0 && scaledDecimals !== null) { + decimals = scaledDecimals + 3 * steps; + } + + return kbn.toFixed(size, decimals) + extArray[steps]; + }; +}; + +// Extension of the scaledUnits builder which uses SI decimal prefixes. If an +// offset is given, it adjusts the starting units at the given prefix; a value +// of 0 starts at no scale; -3 drops to nano, +2 starts at mega, etc. +kbn.formatBuilders.decimalSIPrefix = function(unit, offset) { + var prefixes = ['n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + prefixes = prefixes.slice(3 + (offset || 0)); + var units = prefixes.map(function(p) { + return ' ' + p + unit; + }); + return kbn.formatBuilders.scaledUnits(1000, units); +}; + +// Extension of the scaledUnits builder which uses SI binary prefixes. If +// offset is given, it starts the units at the given prefix; otherwise, the +// offset defaults to zero and the initial unit is not prefixed. +kbn.formatBuilders.binarySIPrefix = function(unit, offset) { + var prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'].slice(offset); + var units = prefixes.map(function(p) { + return ' ' + p + unit; + }); + return kbn.formatBuilders.scaledUnits(1024, units); +}; + +// Currency formatter for prefixing a symbol onto a number. Supports scaling +// up to the trillions. +kbn.formatBuilders.currency = function(symbol) { + var units = ['', 'K', 'M', 'B', 'T']; + var scaler = kbn.formatBuilders.scaledUnits(1000, units); + return function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + var scaled = scaler(size, decimals, scaledDecimals); + return symbol + scaled; + }; +}; + +kbn.formatBuilders.simpleCountUnit = function(symbol) { + var units = ['', 'K', 'M', 'B', 'T']; + var scaler = kbn.formatBuilders.scaledUnits(1000, units); + return function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + var scaled = scaler(size, decimals, scaledDecimals); + return scaled + ' ' + symbol; + }; +}; + +///// VALUE FORMATS ///// + +// Dimensionless Units +kbn.valueFormats.none = kbn.toFixed; +kbn.valueFormats.short = kbn.formatBuilders.scaledUnits(1000, [ + '', + ' K', + ' Mil', + ' Bil', + ' Tri', + ' Quadr', + ' Quint', + ' Sext', + ' Sept', +]); +kbn.valueFormats.dB = kbn.formatBuilders.fixedUnit('dB'); +kbn.valueFormats.ppm = kbn.formatBuilders.fixedUnit('ppm'); + +kbn.valueFormats.percent = function(size, decimals) { + if (size === null) { + return ''; + } + return kbn.toFixed(size, decimals) + '%'; +}; + +kbn.valueFormats.percentunit = function(size, decimals) { + if (size === null) { + return ''; + } + return kbn.toFixed(100 * size, decimals) + '%'; +}; + +/* Formats the value to hex. Uses float if specified decimals are not 0. + * There are two options, one with 0x, and one without */ + +kbn.valueFormats.hex = function(value, decimals) { + if (value == null) { + return ''; + } + return parseFloat(kbn.toFixed(value, decimals)) + .toString(16) + .toUpperCase(); +}; + +kbn.valueFormats.hex0x = function(value, decimals) { + if (value == null) { + return ''; + } + var hexString = kbn.valueFormats.hex(value, decimals); + if (hexString.substring(0, 1) === '-') { + return '-0x' + hexString.substring(1); + } + return '0x' + hexString; +}; + +kbn.valueFormats.sci = function(value, decimals) { + return value.toExponential(decimals); +}; + +kbn.valueFormats.locale = function(value, decimals) { + return value.toLocaleString(undefined, { maximumFractionDigits: decimals }); +}; + +// Currencies +kbn.valueFormats.currencyUSD = kbn.formatBuilders.currency('$'); +kbn.valueFormats.currencyGBP = kbn.formatBuilders.currency('£'); +kbn.valueFormats.currencyEUR = kbn.formatBuilders.currency('€'); +kbn.valueFormats.currencyJPY = kbn.formatBuilders.currency('¥'); +kbn.valueFormats.currencyRUB = kbn.formatBuilders.currency('₽'); +kbn.valueFormats.currencyUAH = kbn.formatBuilders.currency('₴'); +kbn.valueFormats.currencyBRL = kbn.formatBuilders.currency('R$'); +kbn.valueFormats.currencyDKK = kbn.formatBuilders.currency('kr'); +kbn.valueFormats.currencyISK = kbn.formatBuilders.currency('kr'); +kbn.valueFormats.currencyNOK = kbn.formatBuilders.currency('kr'); +kbn.valueFormats.currencySEK = kbn.formatBuilders.currency('kr'); + +// Data (Binary) +kbn.valueFormats.bits = kbn.formatBuilders.binarySIPrefix('b'); +kbn.valueFormats.bytes = kbn.formatBuilders.binarySIPrefix('B'); +kbn.valueFormats.kbytes = kbn.formatBuilders.binarySIPrefix('B', 1); +kbn.valueFormats.mbytes = kbn.formatBuilders.binarySIPrefix('B', 2); +kbn.valueFormats.gbytes = kbn.formatBuilders.binarySIPrefix('B', 3); + +// Data (Decimal) +kbn.valueFormats.decbits = kbn.formatBuilders.decimalSIPrefix('b'); +kbn.valueFormats.decbytes = kbn.formatBuilders.decimalSIPrefix('B'); +kbn.valueFormats.deckbytes = kbn.formatBuilders.decimalSIPrefix('B', 1); +kbn.valueFormats.decmbytes = kbn.formatBuilders.decimalSIPrefix('B', 2); +kbn.valueFormats.decgbytes = kbn.formatBuilders.decimalSIPrefix('B', 3); + +// Data Rate +kbn.valueFormats.pps = kbn.formatBuilders.decimalSIPrefix('pps'); +kbn.valueFormats.bps = kbn.formatBuilders.decimalSIPrefix('bps'); +kbn.valueFormats.Bps = kbn.formatBuilders.decimalSIPrefix('Bps'); +kbn.valueFormats.KBs = kbn.formatBuilders.decimalSIPrefix('Bs', 1); +kbn.valueFormats.Kbits = kbn.formatBuilders.decimalSIPrefix('bps', 1); +kbn.valueFormats.MBs = kbn.formatBuilders.decimalSIPrefix('Bs', 2); +kbn.valueFormats.Mbits = kbn.formatBuilders.decimalSIPrefix('bps', 2); +kbn.valueFormats.GBs = kbn.formatBuilders.decimalSIPrefix('Bs', 3); +kbn.valueFormats.Gbits = kbn.formatBuilders.decimalSIPrefix('bps', 3); + +// Throughput +kbn.valueFormats.ops = kbn.formatBuilders.simpleCountUnit('ops'); +kbn.valueFormats.rps = kbn.formatBuilders.simpleCountUnit('rps'); +kbn.valueFormats.wps = kbn.formatBuilders.simpleCountUnit('wps'); +kbn.valueFormats.iops = kbn.formatBuilders.simpleCountUnit('iops'); +kbn.valueFormats.opm = kbn.formatBuilders.simpleCountUnit('opm'); +kbn.valueFormats.rpm = kbn.formatBuilders.simpleCountUnit('rpm'); +kbn.valueFormats.wpm = kbn.formatBuilders.simpleCountUnit('wpm'); + +// Energy +kbn.valueFormats.watt = kbn.formatBuilders.decimalSIPrefix('W'); +kbn.valueFormats.kwatt = kbn.formatBuilders.decimalSIPrefix('W', 1); +kbn.valueFormats.voltamp = kbn.formatBuilders.decimalSIPrefix('VA'); +kbn.valueFormats.kvoltamp = kbn.formatBuilders.decimalSIPrefix('VA', 1); +kbn.valueFormats.voltampreact = kbn.formatBuilders.decimalSIPrefix('var'); +kbn.valueFormats.kvoltampreact = kbn.formatBuilders.decimalSIPrefix('var', 1); +kbn.valueFormats.watth = kbn.formatBuilders.decimalSIPrefix('Wh'); +kbn.valueFormats.kwatth = kbn.formatBuilders.decimalSIPrefix('Wh', 1); +kbn.valueFormats.joule = kbn.formatBuilders.decimalSIPrefix('J'); +kbn.valueFormats.ev = kbn.formatBuilders.decimalSIPrefix('eV'); +kbn.valueFormats.amp = kbn.formatBuilders.decimalSIPrefix('A'); +kbn.valueFormats.kamp = kbn.formatBuilders.decimalSIPrefix('A', 1); +kbn.valueFormats.volt = kbn.formatBuilders.decimalSIPrefix('V'); +kbn.valueFormats.kvolt = kbn.formatBuilders.decimalSIPrefix('V', 1); +kbn.valueFormats.dBm = kbn.formatBuilders.decimalSIPrefix('dBm'); + +// Temperature +kbn.valueFormats.celsius = kbn.formatBuilders.fixedUnit('°C'); +kbn.valueFormats.farenheit = kbn.formatBuilders.fixedUnit('°F'); +kbn.valueFormats.kelvin = kbn.formatBuilders.fixedUnit('K'); +kbn.valueFormats.humidity = kbn.formatBuilders.fixedUnit('%H'); + +// Pressure +kbn.valueFormats.pressurebar = kbn.formatBuilders.decimalSIPrefix('bar'); +kbn.valueFormats.pressurembar = kbn.formatBuilders.decimalSIPrefix('bar', -1); +kbn.valueFormats.pressurekbar = kbn.formatBuilders.decimalSIPrefix('bar', 1); +kbn.valueFormats.pressurehpa = kbn.formatBuilders.fixedUnit('hPa'); +kbn.valueFormats.pressurehg = kbn.formatBuilders.fixedUnit('"Hg'); +kbn.valueFormats.pressurepsi = kbn.formatBuilders.scaledUnits(1000, [' psi', ' ksi', ' Mpsi']); + +// Force +kbn.valueFormats.forceNm = kbn.formatBuilders.decimalSIPrefix('Nm'); +kbn.valueFormats.forcekNm = kbn.formatBuilders.decimalSIPrefix('Nm', 1); +kbn.valueFormats.forceN = kbn.formatBuilders.decimalSIPrefix('N'); +kbn.valueFormats.forcekN = kbn.formatBuilders.decimalSIPrefix('N', 1); + +// Length +kbn.valueFormats.lengthm = kbn.formatBuilders.decimalSIPrefix('m'); +kbn.valueFormats.lengthmm = kbn.formatBuilders.decimalSIPrefix('m', -1); +kbn.valueFormats.lengthkm = kbn.formatBuilders.decimalSIPrefix('m', 1); +kbn.valueFormats.lengthmi = kbn.formatBuilders.fixedUnit('mi'); + +// Mass +kbn.valueFormats.massmg = kbn.formatBuilders.decimalSIPrefix('g', -1); +kbn.valueFormats.massg = kbn.formatBuilders.decimalSIPrefix('g'); +kbn.valueFormats.masskg = kbn.formatBuilders.decimalSIPrefix('g', 1); +kbn.valueFormats.masst = kbn.formatBuilders.fixedUnit('t'); + +// Velocity +kbn.valueFormats.velocityms = kbn.formatBuilders.fixedUnit('m/s'); +kbn.valueFormats.velocitykmh = kbn.formatBuilders.fixedUnit('km/h'); +kbn.valueFormats.velocitymph = kbn.formatBuilders.fixedUnit('mph'); +kbn.valueFormats.velocityknot = kbn.formatBuilders.fixedUnit('kn'); + +// Volume +kbn.valueFormats.litre = kbn.formatBuilders.decimalSIPrefix('L'); +kbn.valueFormats.mlitre = kbn.formatBuilders.decimalSIPrefix('L', -1); +kbn.valueFormats.m3 = kbn.formatBuilders.decimalSIPrefix('m3'); +kbn.valueFormats.dm3 = kbn.formatBuilders.decimalSIPrefix('dm3'); +kbn.valueFormats.gallons = kbn.formatBuilders.fixedUnit('gal'); + +// Flow +kbn.valueFormats.flowgpm = kbn.formatBuilders.fixedUnit('gpm'); +kbn.valueFormats.flowcms = kbn.formatBuilders.fixedUnit('cms'); +kbn.valueFormats.flowcfs = kbn.formatBuilders.fixedUnit('cfs'); +kbn.valueFormats.flowcfm = kbn.formatBuilders.fixedUnit('cfm'); + +// Time +kbn.valueFormats.hertz = kbn.formatBuilders.decimalSIPrefix('Hz'); + +kbn.valueFormats.ms = function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 1000) { + return kbn.toFixed(size, decimals) + ' ms'; + } else if (Math.abs(size) < 60000) { + // Less than 1 min + return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' s'); + } else if (Math.abs(size) < 3600000) { + // Less than 1 hour, devide in minutes + return kbn.toFixedScaled(size / 60000, decimals, scaledDecimals, 5, ' min'); + } else if (Math.abs(size) < 86400000) { + // Less than one day, devide in hours + return kbn.toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, ' hour'); + } else if (Math.abs(size) < 31536000000) { + // Less than one year, devide in days + return kbn.toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, ' day'); + } + + return kbn.toFixedScaled(size / 31536000000, decimals, scaledDecimals, 10, ' year'); +}; + +kbn.valueFormats.s = function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + + // Less than 1 µs, devide in ns + if (Math.abs(size) < 0.000001) { + return kbn.toFixedScaled(size * 1e9, decimals, scaledDecimals - decimals, -9, ' ns'); + } + // Less than 1 ms, devide in µs + if (Math.abs(size) < 0.001) { + return kbn.toFixedScaled(size * 1e6, decimals, scaledDecimals - decimals, -6, ' µs'); + } + // Less than 1 second, devide in ms + if (Math.abs(size) < 1) { + return kbn.toFixedScaled(size * 1e3, decimals, scaledDecimals - decimals, -3, ' ms'); + } + + if (Math.abs(size) < 60) { + return kbn.toFixed(size, decimals) + ' s'; + } else if (Math.abs(size) < 3600) { + // Less than 1 hour, devide in minutes + return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 1, ' min'); + } else if (Math.abs(size) < 86400) { + // Less than one day, devide in hours + return kbn.toFixedScaled(size / 3600, decimals, scaledDecimals, 4, ' hour'); + } else if (Math.abs(size) < 604800) { + // Less than one week, devide in days + return kbn.toFixedScaled(size / 86400, decimals, scaledDecimals, 5, ' day'); + } else if (Math.abs(size) < 31536000) { + // Less than one year, devide in week + return kbn.toFixedScaled(size / 604800, decimals, scaledDecimals, 6, ' week'); + } + + return kbn.toFixedScaled(size / 3.15569e7, decimals, scaledDecimals, 7, ' year'); +}; + +kbn.valueFormats['µs'] = function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 1000) { + return kbn.toFixed(size, decimals) + ' µs'; + } else if (Math.abs(size) < 1000000) { + return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' ms'); + } else { + return kbn.toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' s'); + } +}; + +kbn.valueFormats.ns = function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 1000) { + return kbn.toFixed(size, decimals) + ' ns'; + } else if (Math.abs(size) < 1000000) { + return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' µs'); + } else if (Math.abs(size) < 1000000000) { + return kbn.toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' ms'); + } else if (Math.abs(size) < 60000000000) { + return kbn.toFixedScaled(size / 1000000000, decimals, scaledDecimals, 9, ' s'); + } else { + return kbn.toFixedScaled(size / 60000000000, decimals, scaledDecimals, 12, ' min'); + } +}; + +kbn.valueFormats.m = function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 60) { + return kbn.toFixed(size, decimals) + ' min'; + } else if (Math.abs(size) < 1440) { + return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 2, ' hour'); + } else if (Math.abs(size) < 10080) { + return kbn.toFixedScaled(size / 1440, decimals, scaledDecimals, 3, ' day'); + } else if (Math.abs(size) < 604800) { + return kbn.toFixedScaled(size / 10080, decimals, scaledDecimals, 4, ' week'); + } else { + return kbn.toFixedScaled(size / 5.25948e5, decimals, scaledDecimals, 5, ' year'); + } +}; + +kbn.valueFormats.h = function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 24) { + return kbn.toFixed(size, decimals) + ' hour'; + } else if (Math.abs(size) < 168) { + return kbn.toFixedScaled(size / 24, decimals, scaledDecimals, 2, ' day'); + } else if (Math.abs(size) < 8760) { + return kbn.toFixedScaled(size / 168, decimals, scaledDecimals, 3, ' week'); + } else { + return kbn.toFixedScaled(size / 8760, decimals, scaledDecimals, 4, ' year'); + } +}; + +kbn.valueFormats.d = function(size, decimals, scaledDecimals) { + if (size === null) { + return ''; + } + + if (Math.abs(size) < 7) { + return kbn.toFixed(size, decimals) + ' day'; + } else if (Math.abs(size) < 365) { + return kbn.toFixedScaled(size / 7, decimals, scaledDecimals, 2, ' week'); + } else { + return kbn.toFixedScaled(size / 365, decimals, scaledDecimals, 3, ' year'); + } +}; + +kbn.toDuration = function(size, decimals, timeScale) { + if (size === null) { + return ''; + } + if (size === 0) { + return '0 ' + timeScale + 's'; + } + if (size < 0) { + return kbn.toDuration(-size, decimals, timeScale) + ' ago'; + } + + var units = [ + { short: 'y', long: 'year' }, + { short: 'M', long: 'month' }, + { short: 'w', long: 'week' }, + { short: 'd', long: 'day' }, + { short: 'h', long: 'hour' }, + { short: 'm', long: 'minute' }, + { short: 's', long: 'second' }, + { short: 'ms', long: 'millisecond' }, + ]; + // convert $size to milliseconds + // intervals_in_seconds uses seconds (duh), convert them to milliseconds here to minimize floating point errors + size *= + kbn.intervals_in_seconds[ + units.find(function(e) { + return e.long === timeScale; + }).short + ] * 1000; + + var strings = []; + // after first value >= 1 print only $decimals more + var decrementDecimals = false; + for (var i = 0; i < units.length && decimals >= 0; i++) { + var interval = kbn.intervals_in_seconds[units[i].short] * 1000; + var value = size / interval; + if (value >= 1 || decrementDecimals) { + decrementDecimals = true; + var floor = Math.floor(value); + var unit = units[i].long + (floor !== 1 ? 's' : ''); + strings.push(floor + ' ' + unit); + size = size % interval; + decimals--; + } + } + + return strings.join(', '); +}; + +kbn.valueFormats.dtdurationms = function(size, decimals) { + return kbn.toDuration(size, decimals, 'millisecond'); +}; + +kbn.valueFormats.dtdurations = function(size, decimals) { + return kbn.toDuration(size, decimals, 'second'); +}; + +kbn.valueFormats.dateTimeAsIso = function(epoch) { + var time = moment(epoch); + + if (moment().isSame(epoch, 'day')) { + return time.format('HH:mm:ss'); + } + return time.format('YYYY-MM-DD HH:mm:ss'); +}; + +kbn.valueFormats.dateTimeAsUS = function(epoch) { + var time = moment(epoch); + + if (moment().isSame(epoch, 'day')) { + return time.format('h:mm:ss a'); + } + return time.format('MM/DD/YYYY h:mm:ss a'); +}; + +kbn.valueFormats.dateTimeFromNow = function(epoch) { + return moment(epoch).fromNow(); +}; + +///// FORMAT MENU ///// + +kbn.getUnitFormats = function() { + return [ + { + text: 'none', + submenu: [ + { text: 'none', value: 'none' }, + { text: 'short', value: 'short' }, + { text: 'percent (0-100)', value: 'percent' }, + { text: 'percent (0.0-1.0)', value: 'percentunit' }, + { text: 'Humidity (%H)', value: 'humidity' }, + { text: 'ppm', value: 'ppm' }, + { text: 'decibel', value: 'dB' }, + { text: 'hexadecimal (0x)', value: 'hex0x' }, + { text: 'hexadecimal', value: 'hex' }, + { text: 'scientific notation', value: 'sci' }, + { text: 'locale format', value: 'locale' }, + ], + }, + { + text: 'currency', + submenu: [ + { text: 'Dollars ($)', value: 'currencyUSD' }, + { text: 'Pounds (£)', value: 'currencyGBP' }, + { text: 'Euro (€)', value: 'currencyEUR' }, + { text: 'Yen (¥)', value: 'currencyJPY' }, + { text: 'Rubles (₽)', value: 'currencyRUB' }, + { text: 'Hryvnias (₴)', value: 'currencyUAH' }, + { text: 'Real (R$)', value: 'currencyBRL' }, + { text: 'Danish Krone (kr)', value: 'currencyDKK' }, + { text: 'Icelandic Krone (kr)', value: 'currencyISK' }, + { text: 'Norwegian Krone (kr)', value: 'currencyNOK' }, + { text: 'Swedish Krone (kr)', value: 'currencySEK' }, + ], + }, + { + text: 'time', + submenu: [ + { text: 'Hertz (1/s)', value: 'hertz' }, + { text: 'nanoseconds (ns)', value: 'ns' }, + { text: 'microseconds (µs)', value: 'µs' }, + { text: 'milliseconds (ms)', value: 'ms' }, + { text: 'seconds (s)', value: 's' }, + { text: 'minutes (m)', value: 'm' }, + { text: 'hours (h)', value: 'h' }, + { text: 'days (d)', value: 'd' }, + { text: 'duration (ms)', value: 'dtdurationms' }, + { text: 'duration (s)', value: 'dtdurations' }, + ], + }, + { + text: 'date & time', + submenu: [ + { text: 'YYYY-MM-DD HH:mm:ss', value: 'dateTimeAsIso' }, + { text: 'DD/MM/YYYY h:mm:ss a', value: 'dateTimeAsUS' }, + { text: 'From Now', value: 'dateTimeFromNow' }, + ], + }, + { + text: 'data (IEC)', + submenu: [ + { text: 'bits', value: 'bits' }, + { text: 'bytes', value: 'bytes' }, + { text: 'kibibytes', value: 'kbytes' }, + { text: 'mebibytes', value: 'mbytes' }, + { text: 'gibibytes', value: 'gbytes' }, + ], + }, + { + text: 'data (Metric)', + submenu: [ + { text: 'bits', value: 'decbits' }, + { text: 'bytes', value: 'decbytes' }, + { text: 'kilobytes', value: 'deckbytes' }, + { text: 'megabytes', value: 'decmbytes' }, + { text: 'gigabytes', value: 'decgbytes' }, + ], + }, + { + text: 'data rate', + submenu: [ + { text: 'packets/sec', value: 'pps' }, + { text: 'bits/sec', value: 'bps' }, + { text: 'bytes/sec', value: 'Bps' }, + { text: 'kilobits/sec', value: 'Kbits' }, + { text: 'kilobytes/sec', value: 'KBs' }, + { text: 'megabits/sec', value: 'Mbits' }, + { text: 'megabytes/sec', value: 'MBs' }, + { text: 'gigabytes/sec', value: 'GBs' }, + { text: 'gigabits/sec', value: 'Gbits' }, + ], + }, + { + text: 'throughput', + submenu: [ + { text: 'ops/sec (ops)', value: 'ops' }, + { text: 'reads/sec (rps)', value: 'rps' }, + { text: 'writes/sec (wps)', value: 'wps' }, + { text: 'I/O ops/sec (iops)', value: 'iops' }, + { text: 'ops/min (opm)', value: 'opm' }, + { text: 'reads/min (rpm)', value: 'rpm' }, + { text: 'writes/min (wpm)', value: 'wpm' }, + ], + }, + { + text: 'length', + submenu: [ + { text: 'millimetre (mm)', value: 'lengthmm' }, + { text: 'meter (m)', value: 'lengthm' }, + { text: 'kilometer (km)', value: 'lengthkm' }, + { text: 'mile (mi)', value: 'lengthmi' }, + ], + }, + { + text: 'mass', + submenu: [ + { text: 'milligram (mg)', value: 'massmg' }, + { text: 'gram (g)', value: 'massg' }, + { text: 'kilogram (kg)', value: 'masskg' }, + { text: 'metric ton (t)', value: 'masst' }, + ], + }, + { + text: 'velocity', + submenu: [ + { text: 'm/s', value: 'velocityms' }, + { text: 'km/h', value: 'velocitykmh' }, + { text: 'mph', value: 'velocitymph' }, + { text: 'knot (kn)', value: 'velocityknot' }, + ], + }, + { + text: 'volume', + submenu: [ + { text: 'millilitre', value: 'mlitre' }, + { text: 'litre', value: 'litre' }, + { text: 'cubic metre', value: 'm3' }, + { text: 'cubic decimetre', value: 'dm3' }, + { text: 'gallons', value: 'gallons' }, + ], + }, + { + text: 'energy', + submenu: [ + { text: 'watt (W)', value: 'watt' }, + { text: 'kilowatt (kW)', value: 'kwatt' }, + { text: 'volt-ampere (VA)', value: 'voltamp' }, + { text: 'kilovolt-ampere (kVA)', value: 'kvoltamp' }, + { text: 'volt-ampere reactive (var)', value: 'voltampreact' }, + { text: 'kilovolt-ampere reactive (kvar)', value: 'kvoltampreact' }, + { text: 'watt-hour (Wh)', value: 'watth' }, + { text: 'kilowatt-hour (kWh)', value: 'kwatth' }, + { text: 'joule (J)', value: 'joule' }, + { text: 'electron volt (eV)', value: 'ev' }, + { text: 'Ampere (A)', value: 'amp' }, + { text: 'Kiloampere (kA)', value: 'kamp' }, + { text: 'Volt (V)', value: 'volt' }, + { text: 'Kilovolt (kV)', value: 'kvolt' }, + { text: 'Decibel-milliwatt (dBm)', value: 'dBm' }, + ], + }, + { + text: 'temperature', + submenu: [ + { text: 'Celsius (°C)', value: 'celsius' }, + { text: 'Farenheit (°F)', value: 'farenheit' }, + { text: 'Kelvin (K)', value: 'kelvin' }, + ], + }, + { + text: 'pressure', + submenu: [ + { text: 'Millibars', value: 'pressurembar' }, + { text: 'Bars', value: 'pressurebar' }, + { text: 'Kilobars', value: 'pressurekbar' }, + { text: 'Hectopascals', value: 'pressurehpa' }, + { text: 'Inches of mercury', value: 'pressurehg' }, + { text: 'PSI', value: 'pressurepsi' }, + ], + }, + { + text: 'force', + submenu: [ + { text: 'Newton-meters (Nm)', value: 'forceNm' }, + { text: 'Kilonewton-meters (kNm)', value: 'forcekNm' }, + { text: 'Newtons (N)', value: 'forceN' }, + { text: 'Kilonewtons (kN)', value: 'forcekN' }, + ], + }, + { + text: 'flow', + submenu: [ + { text: 'Gallons/min (gpm)', value: 'flowgpm' }, + { text: 'Cubic meters/sec (cms)', value: 'flowcms' }, + { text: 'Cubic feet/sec (cfs)', value: 'flowcfs' }, + { text: 'Cubic feet/min (cfm)', value: 'flowcfm' }, + ], + }, + ]; +}; + +export default kbn; diff --git a/public/app/features/dashboard/dashboardLoaderSrv.js b/public/app/features/dashboard/dashboardLoaderSrv.js index c6df45f53b2..de0063dc56b 100644 --- a/public/app/features/dashboard/dashboardLoaderSrv.js +++ b/public/app/features/dashboard/dashboardLoaderSrv.js @@ -11,6 +11,8 @@ define([ function (angular, moment, _, $, kbn, dateMath, impressionStore) { 'use strict'; + kbn = kbn.default; + var module = angular.module('grafana.services'); module.service('dashboardLoaderSrv', function(backendSrv, diff --git a/public/app/features/dashboard/export/export_modal.ts b/public/app/features/dashboard/export/export_modal.ts index 6b877ffd239..826086ebdf0 100644 --- a/public/app/features/dashboard/export/export_modal.ts +++ b/public/app/features/dashboard/export/export_modal.ts @@ -1,8 +1,7 @@ -/// - import angular from 'angular'; -import coreModule from 'app/core/core_module'; +import {saveAs} from 'file-saver'; +import coreModule from 'app/core/core_module'; import {DashboardExporter} from './exporter'; export class DashExportCtrl { @@ -22,9 +21,8 @@ export class DashExportCtrl { } save() { - var blob = new Blob([angular.toJson(this.dash, true)], { type: "application/json;charset=utf-8" }); - var wnd: any = window; - wnd.saveAs(blob, this.dash.title + '-' + new Date().getTime() + '.json'); + var blob = new Blob([angular.toJson(this.dash, true)], {type: 'application/json;charset=utf-8'}); + saveAs(blob, this.dash.title + '-' + new Date().getTime() + '.json'); } saveJson() { @@ -44,7 +42,7 @@ export function dashExportDirective() { controller: DashExportCtrl, bindToController: true, controllerAs: 'ctrl', - scope: {dismiss: "&"} + scope: {dismiss: '&'}, }; } diff --git a/public/app/features/panellinks/linkSrv.js b/public/app/features/panellinks/linkSrv.js index c3a99bed4cb..89d89487c51 100644 --- a/public/app/features/panellinks/linkSrv.js +++ b/public/app/features/panellinks/linkSrv.js @@ -6,6 +6,8 @@ define([ function (angular, _, kbn) { 'use strict'; + kbn = kbn.default; + angular .module('grafana.services') .service('linkSrv', function(templateSrv, timeSrv) { diff --git a/public/app/features/plugins/buit_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts similarity index 100% rename from public/app/features/plugins/buit_in_plugins.ts rename to public/app/features/plugins/built_in_plugins.ts diff --git a/public/app/features/plugins/plugin_loader.ts b/public/app/features/plugins/plugin_loader.ts index 5c29ce36495..376f3586b2a 100644 --- a/public/app/features/plugins/plugin_loader.ts +++ b/public/app/features/plugins/plugin_loader.ts @@ -9,15 +9,24 @@ import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; import TableModel from 'app/core/table_model'; import {coreModule, appEvents, contextSrv} from 'app/core/core'; -import {Observable} from 'rxjs/Observable'; -import {Subject} from 'rxjs/Subject'; import * as datemath from 'app/core/utils/datemath'; import * as fileExport from 'app/core/utils/file_export'; import * as flatten from 'app/core/utils/flatten'; import * as ticks from 'app/core/utils/ticks'; -import builtInPlugins from './buit_in_plugins'; +import {impressions} from 'app/features/dashboard/impression_store'; +import builtInPlugins from './built_in_plugins'; import d3 from 'vendor/d3/d3'; +// rxjs +import {Observable} from 'rxjs/Observable'; +import {Subject} from 'rxjs/Subject'; + +// these imports add functions to Observable +import 'rxjs/add/observable/empty'; +import 'rxjs/add/observable/from'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/combineAll'; + System.config({ baseURL: 'public', defaultExtension: 'js', @@ -30,6 +39,9 @@ System.config({ text: 'vendor/plugin-text/text.js', css: 'vendor/plugin-css/css.js' }, + meta: { + '*': {esModule: true} + } }); // add cache busting @@ -56,8 +68,13 @@ exposeToPlugin('rxjs/Subject', Subject); exposeToPlugin('rxjs/Observable', Observable); exposeToPlugin('d3', d3); -exposeToPlugin('app/plugins/sdk', sdk); +exposeToPlugin('app/features/dashboard/impression_store', { + impressions: impressions, + __esModule: true +}); + +exposeToPlugin('app/plugins/sdk', sdk); exposeToPlugin('app/core/utils/datemath', datemath); exposeToPlugin('app/core/utils/file_export', fileExport); exposeToPlugin('app/core/utils/flatten', flatten); diff --git a/public/app/features/templating/adhoc_variable.ts b/public/app/features/templating/adhoc_variable.ts index 21c5bcf7e7e..b2e12e7521a 100644 --- a/public/app/features/templating/adhoc_variable.ts +++ b/public/app/features/templating/adhoc_variable.ts @@ -1,5 +1,3 @@ -/// - import _ from 'lodash'; import {Variable, assignModelProperties, variableTypes} from './variable'; diff --git a/public/app/features/templating/all.ts b/public/app/features/templating/all.ts index 72478d8ddd3..84ad8082c0b 100644 --- a/public/app/features/templating/all.ts +++ b/public/app/features/templating/all.ts @@ -1,15 +1,19 @@ -import './templateSrv'; import './editor_ctrl'; +import coreModule from 'app/core/core_module'; -import {VariableSrv} from './variable_srv'; -import {IntervalVariable} from './interval_variable'; -import {QueryVariable} from './query_variable'; -import {DatasourceVariable} from './datasource_variable'; -import {CustomVariable} from './custom_variable'; -import {ConstantVariable} from './constant_variable'; -import {AdhocVariable} from './adhoc_variable'; +import { TemplateSrv } from './template_srv'; +import { VariableSrv } from './variable_srv'; +import { IntervalVariable } from './interval_variable'; +import { QueryVariable } from './query_variable'; +import { DatasourceVariable } from './datasource_variable'; +import { CustomVariable } from './custom_variable'; +import { ConstantVariable } from './constant_variable'; +import { AdhocVariable } from './adhoc_variable'; + +coreModule.service('templateSrv', TemplateSrv); export { + TemplateSrv, VariableSrv, IntervalVariable, QueryVariable, diff --git a/public/app/features/templating/query_variable.ts b/public/app/features/templating/query_variable.ts index 4be19d23595..0c24ab23686 100644 --- a/public/app/features/templating/query_variable.ts +++ b/public/app/features/templating/query_variable.ts @@ -1,5 +1,3 @@ -/// - import _ from 'lodash'; import kbn from 'app/core/utils/kbn'; import {Variable, containsVariable, assignModelProperties, variableTypes} from './variable'; diff --git a/public/app/features/templating/specs/adhoc_variable_specs.ts b/public/app/features/templating/specs/adhoc_variable.jest.ts similarity index 55% rename from public/app/features/templating/specs/adhoc_variable_specs.ts rename to public/app/features/templating/specs/adhoc_variable.jest.ts index 65086ee7751..7b636506f95 100644 --- a/public/app/features/templating/specs/adhoc_variable_specs.ts +++ b/public/app/features/templating/specs/adhoc_variable.jest.ts @@ -1,5 +1,3 @@ -import {describe, it, expect} from 'test/lib/common'; - import {AdhocVariable} from '../adhoc_variable'; describe('AdhocVariable', function() { @@ -15,7 +13,7 @@ describe('AdhocVariable', function() { ] }); var urlValue = variable.getValueForUrl(); - expect(urlValue).to.eql(["key1|=|value1", "key2|!=|value2", "key3|=|value3a__gfp__value3b__gfp__value3c"]); + expect(urlValue).toMatchObject(["key1|=|value1", "key2|!=|value2", "key3|=|value3a__gfp__value3b__gfp__value3c"]); }); }); @@ -26,17 +24,17 @@ describe('AdhocVariable', function() { var variable = new AdhocVariable({}); variable.setValueFromUrl(["key1|=|value1", "key2|!=|value2", "key3|=|value3a__gfp__value3b__gfp__value3c"]); - expect(variable.filters[0].key).to.be('key1'); - expect(variable.filters[0].operator).to.be('='); - expect(variable.filters[0].value).to.be('value1'); + expect(variable.filters[0].key).toBe('key1'); + expect(variable.filters[0].operator).toBe('='); + expect(variable.filters[0].value).toBe('value1'); - expect(variable.filters[1].key).to.be('key2'); - expect(variable.filters[1].operator).to.be('!='); - expect(variable.filters[1].value).to.be('value2'); + expect(variable.filters[1].key).toBe('key2'); + expect(variable.filters[1].operator).toBe('!='); + expect(variable.filters[1].value).toBe('value2'); - expect(variable.filters[2].key).to.be('key3'); - expect(variable.filters[2].operator).to.be('='); - expect(variable.filters[2].value).to.be('value3a|value3b|value3c'); + expect(variable.filters[2].key).toBe('key3'); + expect(variable.filters[2].operator).toBe('='); + expect(variable.filters[2].value).toBe('value3a|value3b|value3c'); }); }); diff --git a/public/app/features/templating/specs/query_variable_specs.ts b/public/app/features/templating/specs/query_variable.jest.ts similarity index 62% rename from public/app/features/templating/specs/query_variable_specs.ts rename to public/app/features/templating/specs/query_variable.jest.ts index bf50b759a13..de0f56938f9 100644 --- a/public/app/features/templating/specs/query_variable_specs.ts +++ b/public/app/features/templating/specs/query_variable.jest.ts @@ -1,5 +1,3 @@ -import {describe, it, expect} from 'test/lib/common'; - import {QueryVariable} from '../query_variable'; describe('QueryVariable', () => { @@ -8,14 +6,14 @@ describe('QueryVariable', () => { it('should set defaults', () => { var variable = new QueryVariable({}, null, null, null, null); - expect(variable.datasource).to.be(null); - expect(variable.refresh).to.be(0); - expect(variable.sort).to.be(0); - expect(variable.name).to.be(''); - expect(variable.hide).to.be(0); - expect(variable.options.length).to.be(0); - expect(variable.multi).to.be(false); - expect(variable.includeAll).to.be(false); + expect(variable.datasource).toBe(null); + expect(variable.refresh).toBe(0); + expect(variable.sort).toBe(0); + expect(variable.name).toBe(''); + expect(variable.hide).toBe(0); + expect(variable.options.length).toBe(0); + expect(variable.multi).toBe(false); + expect(variable.includeAll).toBe(false); }); it('get model should copy changes back to model', () => { @@ -26,11 +24,11 @@ describe('QueryVariable', () => { variable.sort = 50; var model = variable.getSaveModel(); - expect(model.options.length).to.be(1); - expect(model.options[0].text).to.be('test'); - expect(model.datasource).to.be('google'); - expect(model.regex).to.be('asd'); - expect(model.sort).to.be(50); + expect(model.options.length).toBe(1); + expect(model.options[0].text).toBe('test'); + expect(model.datasource).toBe('google'); + expect(model.regex).toBe('asd'); + expect(model.sort).toBe(50); }); it('if refresh != 0 then remove options in presisted mode', () => { @@ -39,7 +37,7 @@ describe('QueryVariable', () => { variable.refresh = 1; var model = variable.getSaveModel(); - expect(model.options.length).to.be(0); + expect(model.options.length).toBe(0); }); }); @@ -67,14 +65,14 @@ describe('QueryVariable', () => { it('should return in same order', () => { var i = 0; - expect(result.length).to.be(11); - expect(result[i++].text).to.be(''); - expect(result[i++].text).to.be('0'); - expect(result[i++].text).to.be('1'); - expect(result[i++].text).to.be('3'); - expect(result[i++].text).to.be('4'); - expect(result[i++].text).to.be('5'); - expect(result[i++].text).to.be('6'); + expect(result.length).toBe(11); + expect(result[i++].text).toBe(''); + expect(result[i++].text).toBe('0'); + expect(result[i++].text).toBe('1'); + expect(result[i++].text).toBe('3'); + expect(result[i++].text).toBe('4'); + expect(result[i++].text).toBe('5'); + expect(result[i++].text).toBe('6'); }); }); }); diff --git a/public/app/features/templating/specs/template_srv_specs.ts b/public/app/features/templating/specs/template_srv.jest.ts similarity index 78% rename from public/app/features/templating/specs/template_srv_specs.ts rename to public/app/features/templating/specs/template_srv.jest.ts index 5a48fc8e173..178abc578b6 100644 --- a/public/app/features/templating/specs/template_srv_specs.ts +++ b/public/app/features/templating/specs/template_srv.jest.ts @@ -1,28 +1,11 @@ -import {describe, beforeEach, it, expect, angularMocks} from 'test/lib/common'; - -import '../all'; -import {Emitter} from 'app/core/core'; +import { TemplateSrv } from '../template_srv'; describe('templateSrv', function() { - var _templateSrv, _variableSrv; - - beforeEach(angularMocks.module('grafana.core')); - beforeEach(angularMocks.module('grafana.services')); - beforeEach(angularMocks.module($provide => { - $provide.value('timeSrv', {}); - $provide.value('datasourceSrv', {}); - })); - - beforeEach(angularMocks.inject(function(variableSrv, templateSrv) { - _templateSrv = templateSrv; - _variableSrv = variableSrv; - })); + var _templateSrv; function initTemplateSrv(variables) { - _variableSrv.init({ - templating: {list: variables}, - events: new Emitter(), - }); + _templateSrv = new TemplateSrv(); + _templateSrv.init(variables); } describe('init', function() { @@ -32,7 +15,7 @@ describe('templateSrv', function() { it('should initialize template data', function() { var target = _templateSrv.replace('this.[[test]].filters'); - expect(target).to.be('this.oogle.filters'); + expect(target).toBe('this.oogle.filters'); }); }); @@ -43,12 +26,12 @@ describe('templateSrv', function() { it('should replace $test with scoped value', function() { var target = _templateSrv.replace('this.$test.filters', {'test': {value: 'mupp', text: 'asd'}}); - expect(target).to.be('this.mupp.filters'); + expect(target).toBe('this.mupp.filters'); }); it('should replace $test with scoped text', function() { var target = _templateSrv.replaceWithText('this.$test.filters', {'test': {value: 'mupp', text: 'asd'}}); - expect(target).to.be('this.asd.filters'); + expect(target).toBe('this.asd.filters'); }); }); @@ -63,17 +46,17 @@ describe('templateSrv', function() { it('should return filters if datasourceName match', function() { var filters = _templateSrv.getAdhocFilters('oogle'); - expect(filters).to.eql([1]); + expect(filters).toMatchObject([1]); }); it('should return empty array if datasourceName does not match', function() { var filters = _templateSrv.getAdhocFilters('oogleasdasd'); - expect(filters).to.eql([]); + expect(filters).toMatchObject([]); }); it('should return filters when datasourceName match via data source variable', function() { var filters = _templateSrv.getAdhocFilters('logstash'); - expect(filters).to.eql([2]); + expect(filters).toMatchObject([2]); }); }); @@ -84,17 +67,17 @@ describe('templateSrv', function() { it('should replace $test with globbed value', function() { var target = _templateSrv.replace('this.$test.filters', {}, 'glob'); - expect(target).to.be('this.{value1,value2}.filters'); + expect(target).toBe('this.{value1,value2}.filters'); }); it('should replace $test with piped value', function() { var target = _templateSrv.replace('this=$test', {}, 'pipe'); - expect(target).to.be('this=value1|value2'); + expect(target).toBe('this=value1|value2'); }); it('should replace $test with piped value', function() { var target = _templateSrv.replace('this=$test', {}, 'pipe'); - expect(target).to.be('this=value1|value2'); + expect(target).toBe('this=value1|value2'); }); }); @@ -112,7 +95,7 @@ describe('templateSrv', function() { it('should replace $test with formatted all value', function() { var target = _templateSrv.replace('this.$test.filters', {}, 'glob'); - expect(target).to.be('this.{value1,value2}.filters'); + expect(target).toBe('this.{value1,value2}.filters'); }); }); @@ -131,12 +114,12 @@ describe('templateSrv', function() { it('should replace $test with formatted all value', function() { var target = _templateSrv.replace('this.$test.filters', {}, 'glob'); - expect(target).to.be('this.*.filters'); + expect(target).toBe('this.*.filters'); }); it('should not escape custom all value', function() { var target = _templateSrv.replace('this.$test', {}, 'regex'); - expect(target).to.be('this.*'); + expect(target).toBe('this.*'); }); }); @@ -144,49 +127,49 @@ describe('templateSrv', function() { it('should properly escape $test with lucene escape sequences', function() { initTemplateSrv([{type: 'query', name: 'test', current: {value: 'value/4' }}]); var target = _templateSrv.replace('this:$test', {}, 'lucene'); - expect(target).to.be("this:value\\\/4"); + expect(target).toBe("this:value\\\/4"); }); }); describe('format variable to string values', function() { it('single value should return value', function() { var result = _templateSrv.formatValue('test'); - expect(result).to.be('test'); + expect(result).toBe('test'); }); it('multi value and glob format should render glob string', function() { var result = _templateSrv.formatValue(['test','test2'], 'glob'); - expect(result).to.be('{test,test2}'); + expect(result).toBe('{test,test2}'); }); it('multi value and lucene should render as lucene expr', function() { var result = _templateSrv.formatValue(['test','test2'], 'lucene'); - expect(result).to.be('("test" OR "test2")'); + expect(result).toBe('("test" OR "test2")'); }); it('multi value and regex format should render regex string', function() { var result = _templateSrv.formatValue(['test.','test2'], 'regex'); - expect(result).to.be('(test\\.|test2)'); + expect(result).toBe('(test\\.|test2)'); }); it('multi value and pipe should render pipe string', function() { var result = _templateSrv.formatValue(['test','test2'], 'pipe'); - expect(result).to.be('test|test2'); + expect(result).toBe('test|test2'); }); it('multi value and distributed should render distributed string', function() { var result = _templateSrv.formatValue(['test','test2'], 'distributed', { name: 'build' }); - expect(result).to.be('test,build=test2'); + expect(result).toBe('test,build=test2'); }); it('multi value and distributed should render when not string', function() { var result = _templateSrv.formatValue(['test'], 'distributed', { name: 'build' }); - expect(result).to.be('test'); + expect(result).toBe('test'); }); it('slash should be properly escaped in regex format', function() { var result = _templateSrv.formatValue('Gi3/14', 'regex'); - expect(result).to.be('Gi3\\/14'); + expect(result).toBe('Gi3\\/14'); }); }); @@ -198,7 +181,7 @@ describe('templateSrv', function() { it('should return true if exists', function() { var result = _templateSrv.variableExists('$test'); - expect(result).to.be(true); + expect(result).toBe(true); }); }); @@ -209,17 +192,17 @@ describe('templateSrv', function() { it('should insert html', function() { var result = _templateSrv.highlightVariablesAsHtml('$test'); - expect(result).to.be('$test'); + expect(result).toBe('$test'); }); it('should insert html anywhere in string', function() { var result = _templateSrv.highlightVariablesAsHtml('this $test ok'); - expect(result).to.be('this $test ok'); + expect(result).toBe('this $test ok'); }); it('should ignore if variables does not exist', function() { var result = _templateSrv.highlightVariablesAsHtml('this $google ok'); - expect(result).to.be('this $google ok'); + expect(result).toBe('this $google ok'); }); }); @@ -230,19 +213,28 @@ describe('templateSrv', function() { it('should set current value and update template data', function() { var target = _templateSrv.replace('this.[[test]].filters'); - expect(target).to.be('this.muuuu.filters'); + expect(target).toBe('this.muuuu.filters'); }); }); describe('fillVariableValuesForUrl with multi value', function() { beforeEach(function() { - initTemplateSrv([{type: 'query', name: 'test', current: { value: ['val1', 'val2'] }}]); + initTemplateSrv([ + { + type: 'query', + name: 'test', + current: { value: ['val1', 'val2'] }, + getValueForUrl: function() { + return this.current.value; + } + } + ]); }); it('should set multiple url params', function() { var params = {}; _templateSrv.fillVariableValuesForUrl(params); - expect(params['var-test']).to.eql(['val1', 'val2']); + expect(params['var-test']).toMatchObject(['val1', 'val2']); }); }); @@ -254,7 +246,7 @@ describe('templateSrv', function() { it('should set scoped value as url params', function() { var params = {}; _templateSrv.fillVariableValuesForUrl(params, {'test': {value: 'val1'}}); - expect(params['var-test']).to.eql('val1'); + expect(params['var-test']).toBe('val1'); }); }); @@ -270,7 +262,7 @@ describe('templateSrv', function() { it('should replace with text except for grafanaVariables', function() { var target = _templateSrv.replaceWithText('Server: $server, period: $period'); - expect(target).to.be('Server: All, period: 13m'); + expect(target).toBe('Server: All, period: 13m'); }); }); @@ -281,7 +273,7 @@ describe('templateSrv', function() { it('should replace $__interval_ms with interval milliseconds', function() { var target = _templateSrv.replace('10 * $__interval_ms', {"__interval_ms": {text: "100", value: "100"}}); - expect(target).to.be('10 * 100'); + expect(target).toBe('10 * 100'); }); }); diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js deleted file mode 100644 index 31f8bdcbb13..00000000000 --- a/public/app/features/templating/templateSrv.js +++ /dev/null @@ -1,253 +0,0 @@ -define([ - 'angular', - 'lodash', - 'app/core/utils/kbn', -], -function (angular, _, kbn) { - 'use strict'; - - var module = angular.module('grafana.services'); - - module.service('templateSrv', function() { - var self = this; - - this._regex = /\$(\w+)|\[\[([\s\S]+?)\]\]/g; - this._index = {}; - this._texts = {}; - this._grafanaVariables = {}; - - // default built ins - this._builtIns = {}; - this._builtIns['__interval'] = {text: '1s', value: '1s'}; - this._builtIns['__interval_ms'] = {text: '100', value: '100'}; - - this.init = function(variables) { - this.variables = variables; - this.updateTemplateData(); - }; - - this.updateTemplateData = function() { - this._index = {}; - this._filters = {}; - - for (var i = 0; i < this.variables.length; i++) { - var variable = this.variables[i]; - - if (!variable.current || !variable.current.isNone && !variable.current.value) { - continue; - } - - this._index[variable.name] = variable; - } - }; - - this.variableInitialized = function(variable) { - this._index[variable.name] = variable; - }; - - this.getAdhocFilters = function(datasourceName) { - var filters = []; - - for (var i = 0; i < this.variables.length; i++) { - var variable = this.variables[i]; - if (variable.type !== 'adhoc') { - continue; - } - - if (variable.datasource === datasourceName) { - filters = filters.concat(variable.filters); - } - - if (variable.datasource.indexOf('$') === 0) { - if (this.replace(variable.datasource) === datasourceName) { - filters = filters.concat(variable.filters); - } - } - } - - return filters; - }; - - function luceneEscape(value) { - return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, "\\$1"); - } - - this.luceneFormat = function(value) { - if (typeof value === 'string') { - return luceneEscape(value); - } - var quotedValues = _.map(value, function(val) { - return '\"' + luceneEscape(val) + '\"'; - }); - return '(' + quotedValues.join(' OR ') + ')'; - }; - - this.formatValue = function(value, format, variable) { - // for some scopedVars there is no variable - variable = variable || {}; - - if (typeof format === 'function') { - return format(value, variable, this.formatValue); - } - - switch(format) { - case "regex": { - if (typeof value === 'string') { - return kbn.regexEscape(value); - } - - var escapedValues = _.map(value, kbn.regexEscape); - return '(' + escapedValues.join('|') + ')'; - } - case "lucene": { - return this.luceneFormat(value, format, variable); - } - case "pipe": { - if (typeof value === 'string') { - return value; - } - return value.join('|'); - } - case "distributed": { - if (typeof value === 'string') { - return value; - } - return this.distributeVariable(value, variable.name); - } - default: { - if (_.isArray(value)) { - return '{' + value.join(',') + '}'; - } - return value; - } - } - }; - - this.setGrafanaVariable = function (name, value) { - this._grafanaVariables[name] = value; - }; - - this.getVariableName = function(expression) { - this._regex.lastIndex = 0; - var match = this._regex.exec(expression); - if (!match) { - return null; - } - return match[1] || match[2]; - }; - - this.variableExists = function(expression) { - var name = this.getVariableName(expression); - return name && (self._index[name] !== void 0); - }; - - this.highlightVariablesAsHtml = function(str) { - if (!str || !_.isString(str)) { return str; } - - str = _.escape(str); - this._regex.lastIndex = 0; - return str.replace(this._regex, function(match, g1, g2) { - if (self._index[g1 || g2] || self._builtIns[g1 || g2]) { - return '' + match + ''; - } - return match; - }); - }; - - this.getAllValue = function(variable) { - if (variable.allValue) { - return variable.allValue; - } - var values = []; - for (var i = 1; i < variable.options.length; i++) { - values.push(variable.options[i].value); - } - return values; - }; - - this.replace = function(target, scopedVars, format) { - if (!target) { return target; } - - var variable, systemValue, value; - this._regex.lastIndex = 0; - - return target.replace(this._regex, function(match, g1, g2) { - variable = self._index[g1 || g2]; - - if (scopedVars) { - value = scopedVars[g1 || g2]; - if (value) { - return self.formatValue(value.value, format, variable); - } - } - - if (!variable) { - return match; - } - - systemValue = self._grafanaVariables[variable.current.value]; - if (systemValue) { - return self.formatValue(systemValue, format, variable); - } - - value = variable.current.value; - if (self.isAllValue(value)) { - value = self.getAllValue(variable); - // skip formating of custom all values - if (variable.allValue) { - return value; - } - } - - var res = self.formatValue(value, format, variable); - return res; - }); - }; - - this.isAllValue = function(value) { - return value === '$__all' || Array.isArray(value) && value[0] === '$__all'; - }; - - this.replaceWithText = function(target, scopedVars) { - if (!target) { return target; } - - var variable; - this._regex.lastIndex = 0; - - return target.replace(this._regex, function(match, g1, g2) { - if (scopedVars) { - var option = scopedVars[g1 || g2]; - if (option) { return option.text; } - } - - variable = self._index[g1 || g2]; - if (!variable) { return match; } - - return self._grafanaVariables[variable.current.value] || variable.current.text; - }); - }; - - this.fillVariableValuesForUrl = function(params, scopedVars) { - _.each(this.variables, function(variable) { - if (scopedVars && scopedVars[variable.name] !== void 0) { - params['var-' + variable.name] = scopedVars[variable.name].value; - } else { - params['var-' + variable.name] = variable.getValueForUrl(); - } - }); - }; - - this.distributeVariable = function(value, variable) { - value = _.map(value, function(val, index) { - if (index !== 0) { - return variable + "=" + val; - } else { - return val; - } - }); - return value.join(','); - }; - - }); - -}); diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts new file mode 100644 index 00000000000..8f832a2ed24 --- /dev/null +++ b/public/app/features/templating/template_srv.ts @@ -0,0 +1,244 @@ +import kbn from 'app/core/utils/kbn'; +import _ from 'lodash'; + +function luceneEscape(value) { + return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, "\\$1"); +} + +export class TemplateSrv { + variables: any[]; + + private regex = /\$(\w+)|\[\[([\s\S]+?)\]\]/g; + private index = {}; + private grafanaVariables = {}; + private builtIns = {}; + private filters = {}; + + constructor() { + this.builtIns['__interval'] = {text: '1s', value: '1s'}; + this.builtIns['__interval_ms'] = {text: '100', value: '100'}; + } + + init(variables) { + this.variables = variables; + this.updateTemplateData(); + } + + updateTemplateData() { + this.index = {}; + this.filters = {}; + + for (var i = 0; i < this.variables.length; i++) { + var variable = this.variables[i]; + + if (!variable.current || !variable.current.isNone && !variable.current.value) { + continue; + } + + this.index[variable.name] = variable; + } + } + + variableInitialized(variable) { + this.index[variable.name] = variable; + } + + getAdhocFilters(datasourceName) { + var filters = []; + + for (var i = 0; i < this.variables.length; i++) { + var variable = this.variables[i]; + if (variable.type !== 'adhoc') { + continue; + } + + if (variable.datasource === datasourceName) { + filters = filters.concat(variable.filters); + } + + if (variable.datasource.indexOf('$') === 0) { + if (this.replace(variable.datasource) === datasourceName) { + filters = filters.concat(variable.filters); + } + } + } + + return filters; + } + + luceneFormat(value) { + if (typeof value === 'string') { + return luceneEscape(value); + } + var quotedValues = _.map(value, function(val) { + return '\"' + luceneEscape(val) + '\"'; + }); + return '(' + quotedValues.join(' OR ') + ')'; + } + + formatValue(value, format, variable) { + // for some scopedVars there is no variable + variable = variable || {}; + + if (typeof format === 'function') { + return format(value, variable, this.formatValue); + } + + switch (format) { + case "regex": { + if (typeof value === 'string') { + return kbn.regexEscape(value); + } + + var escapedValues = _.map(value, kbn.regexEscape); + return '(' + escapedValues.join('|') + ')'; + } + case "lucene": { + return this.luceneFormat(value); + } + case "pipe": { + if (typeof value === 'string') { + return value; + } + return value.join('|'); + } + case "distributed": { + if (typeof value === 'string') { + return value; + } + return this.distributeVariable(value, variable.name); + } + default: { + if (_.isArray(value)) { + return '{' + value.join(',') + '}'; + } + return value; + } + } + } + + setGrafanaVariable(name, value) { + this.grafanaVariables[name] = value; + } + + getVariableName(expression) { + this.regex.lastIndex = 0; + var match = this.regex.exec(expression); + if (!match) { + return null; + } + return match[1] || match[2]; + } + + variableExists(expression) { + var name = this.getVariableName(expression); + return name && (this.index[name] !== void 0); + } + + highlightVariablesAsHtml(str) { + if (!str || !_.isString(str)) { return str; } + + str = _.escape(str); + this.regex.lastIndex = 0; + return str.replace(this.regex, (match, g1, g2) => { + if (this.index[g1 || g2] || this.builtIns[g1 || g2]) { + return '' + match + ''; + } + return match; + }); + } + + getAllValue(variable) { + if (variable.allValue) { + return variable.allValue; + } + var values = []; + for (var i = 1; i < variable.options.length; i++) { + values.push(variable.options[i].value); + } + return values; + } + + replace(target, scopedVars?, format?) { + if (!target) { return target; } + + var variable, systemValue, value; + this.regex.lastIndex = 0; + + return target.replace(this.regex, (match, g1, g2) => { + variable = this.index[g1 || g2]; + + if (scopedVars) { + value = scopedVars[g1 || g2]; + if (value) { + return this.formatValue(value.value, format, variable); + } + } + + if (!variable) { + return match; + } + + systemValue = this.grafanaVariables[variable.current.value]; + if (systemValue) { + return this.formatValue(systemValue, format, variable); + } + + value = variable.current.value; + if (this.isAllValue(value)) { + value = this.getAllValue(variable); + // skip formating of custom all values + if (variable.allValue) { + return value; + } + } + + var res = this.formatValue(value, format, variable); + return res; + }); + } + + isAllValue(value) { + return value === '$__all' || Array.isArray(value) && value[0] === '$__all'; + } + + replaceWithText(target, scopedVars) { + if (!target) { return target; } + + var variable; + this.regex.lastIndex = 0; + + return target.replace(this.regex, (match, g1, g2) => { + if (scopedVars) { + var option = scopedVars[g1 || g2]; + if (option) { return option.text; } + } + + variable = this.index[g1 || g2]; + if (!variable) { return match; } + + return this.grafanaVariables[variable.current.value] || variable.current.text; + }); + } + + fillVariableValuesForUrl(params, scopedVars) { + _.each(this.variables, function(variable) { + if (scopedVars && scopedVars[variable.name] !== void 0) { + params['var-' + variable.name] = scopedVars[variable.name].value; + } else { + params['var-' + variable.name] = variable.getValueForUrl(); + } + }); + } + + distributeVariable(value, variable) { + value = _.map(value, function(val, index) { + if (index !== 0) { + return variable + "=" + val; + } else { + return val; + } + }); + return value.join(','); + } +} diff --git a/public/app/features/templating/variable.ts b/public/app/features/templating/variable.ts index de7a444aece..4e8ef6fad8f 100644 --- a/public/app/features/templating/variable.ts +++ b/public/app/features/templating/variable.ts @@ -1,7 +1,5 @@ -/// - import kbn from 'app/core/utils/kbn'; -import {assignModelProperties} from 'app/core/core'; +import {assignModelProperties} from 'app/core/utils/model_utils'; export interface Variable { setValue(option); diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index 8037cafb3e6..d21bcf6413c 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -9,6 +9,8 @@ define([ function (angular, _, moment, dateMath, kbn, templatingVariable) { 'use strict'; + kbn = kbn.default; + /** @ngInject */ function CloudWatchDatasource(instanceSettings, $q, backendSrv, templateSrv, timeSrv) { this.type = 'cloudwatch'; @@ -354,6 +356,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) { var t = angular.copy(target); var scopedVar = {}; scopedVar[variable.name] = v; + t.refId = target.refId + '_' + v.value; t.dimensions[dimensionKey] = templateSrv.replace(t.dimensions[dimensionKey], scopedVar); return t; }).value(); diff --git a/public/app/plugins/datasource/elasticsearch/datasource.ts b/public/app/plugins/datasource/elasticsearch/datasource.ts index b165ad4c689..814b6c72da6 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.ts +++ b/public/app/plugins/datasource/elasticsearch/datasource.ts @@ -75,6 +75,12 @@ export class ElasticDatasource { return this.request('POST', url, data).then(function(results) { results.data.$$config = results.config; return results.data; + }).catch(err => { + if (err.data && err.data.error) { + throw {message: 'Elasticsearch error: ' + err.data.error.reason, error: err.data.error}; + } + + throw err; }); } diff --git a/public/app/plugins/datasource/graphite/add_graphite_func.js b/public/app/plugins/datasource/graphite/add_graphite_func.js index 52246fa1ff6..b4df928adca 100644 --- a/public/app/plugins/datasource/graphite/add_graphite_func.js +++ b/public/app/plugins/datasource/graphite/add_graphite_func.js @@ -7,6 +7,8 @@ define([ function (angular, _, $, gfunc) { 'use strict'; + gfunc = gfunc.default; + angular .module('grafana.directives') .directive('graphiteAddFunc', function($compile) { diff --git a/public/app/plugins/datasource/graphite/gfunc.d.ts b/public/app/plugins/datasource/graphite/gfunc.d.ts deleted file mode 100644 index c3318b8e133..00000000000 --- a/public/app/plugins/datasource/graphite/gfunc.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare var test: any; -export default test; diff --git a/public/app/plugins/datasource/graphite/gfunc.js b/public/app/plugins/datasource/graphite/gfunc.js deleted file mode 100644 index 3a6981e147e..00000000000 --- a/public/app/plugins/datasource/graphite/gfunc.js +++ /dev/null @@ -1,986 +0,0 @@ -define([ - 'lodash', - 'jquery' -], -function (_, $) { - 'use strict'; - - var index = []; - var categories = { - Combine: [], - Transform: [], - Calculate: [], - Filter: [], - Special: [] - }; - - function addFuncDef(funcDef) { - funcDef.params = funcDef.params || []; - funcDef.defaultParams = funcDef.defaultParams || []; - - if (funcDef.category) { - funcDef.category.push(funcDef); - } - index[funcDef.name] = funcDef; - index[funcDef.shortName || funcDef.name] = funcDef; - } - - var optionalSeriesRefArgs = [ - { name: 'other', type: 'value_or_series', optional: true }, - { name: 'other', type: 'value_or_series', optional: true }, - { name: 'other', type: 'value_or_series', optional: true }, - { name: 'other', type: 'value_or_series', optional: true }, - { name: 'other', type: 'value_or_series', optional: true } - ]; - - addFuncDef({ - name: 'scaleToSeconds', - category: categories.Transform, - params: [{ name: 'seconds', type: 'int' }], - defaultParams: [1], - }); - - addFuncDef({ - name: 'perSecond', - category: categories.Transform, - params: [{ name: "max value", type: "int", optional: true }], - defaultParams: [], - }); - - addFuncDef({ - name: "holtWintersForecast", - category: categories.Calculate, - }); - - addFuncDef({ - name: "holtWintersConfidenceBands", - category: categories.Calculate, - params: [{ name: "delta", type: 'int' }], - defaultParams: [3] - }); - - addFuncDef({ - name: "holtWintersAberration", - category: categories.Calculate, - params: [{ name: "delta", type: 'int' }], - defaultParams: [3] - }); - - addFuncDef({ - name: "nPercentile", - category: categories.Calculate, - params: [{ name: "Nth percentile", type: 'int' }], - defaultParams: [95] - }); - - addFuncDef({ - name: 'diffSeries', - params: optionalSeriesRefArgs, - defaultParams: ['#A'], - category: categories.Calculate, - }); - - addFuncDef({ - name: 'stddevSeries', - params: optionalSeriesRefArgs, - defaultParams: [''], - category: categories.Calculate, - }); - - addFuncDef({ - name: 'divideSeries', - params: optionalSeriesRefArgs, - defaultParams: ['#A'], - category: categories.Calculate, - }); - - addFuncDef({ - name: 'multiplySeries', - params: optionalSeriesRefArgs, - defaultParams: ['#A'], - category: categories.Calculate, - }); - - addFuncDef({ - name: 'asPercent', - params: optionalSeriesRefArgs, - defaultParams: ['#A'], - category: categories.Calculate, - }); - - addFuncDef({ - name: 'group', - params: optionalSeriesRefArgs, - defaultParams: ['#A', '#B'], - category: categories.Combine, - }); - - addFuncDef({ - name: 'sumSeries', - shortName: 'sum', - category: categories.Combine, - params: optionalSeriesRefArgs, - defaultParams: [''], - }); - - addFuncDef({ - name: 'averageSeries', - shortName: 'avg', - category: categories.Combine, - params: optionalSeriesRefArgs, - defaultParams: [''], - }); - - addFuncDef({ - name: 'rangeOfSeries', - category: categories.Combine - }); - - addFuncDef({ - name: 'percentileOfSeries', - category: categories.Combine, - params: [{ name: 'n', type: 'int' }, { name: 'interpolate', type: 'boolean', options: ['true', 'false'] }], - defaultParams: [95, 'false'] - }); - - addFuncDef({ - name: 'sumSeriesWithWildcards', - category: categories.Combine, - params: [ - { name: "node", type: "int" }, - { name: "node", type: "int", optional: true }, - { name: "node", type: "int", optional: true }, - { name: "node", type: "int", optional: true } - ], - defaultParams: [3] - }); - - addFuncDef({ - name: 'maxSeries', - shortName: 'max', - category: categories.Combine, - }); - - addFuncDef({ - name: 'minSeries', - shortName: 'min', - category: categories.Combine, - }); - - addFuncDef({ - name: 'averageSeriesWithWildcards', - category: categories.Combine, - params: [ - { name: "node", type: "int" }, - { name: "node", type: "int", optional: true }, - ], - defaultParams: [3] - }); - - addFuncDef({ - name: "alias", - category: categories.Special, - params: [{ name: "alias", type: 'string' }], - defaultParams: ['alias'] - }); - - addFuncDef({ - name: "aliasSub", - category: categories.Special, - params: [{ name: "search", type: 'string' }, { name: "replace", type: 'string' }], - defaultParams: ['', '\\1'] - }); - - addFuncDef({ - name: "stacked", - category: categories.Special, - params: [{ name: "stack", type: 'string' }], - defaultParams: ['stacked'] - }); - - addFuncDef({ - name: "consolidateBy", - category: categories.Special, - params: [ - { - name: 'function', - type: 'string', - options: ['sum', 'average', 'min', 'max'] - } - ], - defaultParams: ['max'] - }); - - addFuncDef({ - name: "cumulative", - category: categories.Special, - params: [], - defaultParams: [] - }); - - addFuncDef({ - name: "groupByNode", - category: categories.Special, - params: [ - { - name: "node", - type: "int", - options: [0,1,2,3,4,5,6,7,8,9,10,12] - }, - { - name: "function", - type: "string", - options: ['sum', 'avg', 'maxSeries'] - } - ], - defaultParams: [3, "sum"] - }); - - addFuncDef({ - name: 'aliasByNode', - category: categories.Special, - params: [ - { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] }, - { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true }, - { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true }, - { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true }, - ], - defaultParams: [3] - }); - - addFuncDef({ - name: 'substr', - category: categories.Special, - params: [ - { name: "start", type: "int", options: [-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,12] }, - { name: "stop", type: "int", options: [-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,12] }, - ], - defaultParams: [0, 0] - }); - - addFuncDef({ - name: 'sortByName', - category: categories.Special, - params: [{ name: 'natural', type: 'boolean', options: ['true', 'false'], optional: true }], - defaultParams: ['false'] - }); - - addFuncDef({ - name: 'sortByMaxima', - category: categories.Special - }); - - addFuncDef({ - name: 'sortByMinima', - category: categories.Special - }); - - addFuncDef({ - name: 'sortByTotal', - category: categories.Special - }); - - addFuncDef({ - name: 'aliasByMetric', - category: categories.Special, - }); - - addFuncDef({ - name: 'randomWalk', - fake: true, - category: categories.Special, - params: [{ name: "name", type: "string", }], - defaultParams: ['randomWalk'] - }); - - addFuncDef({ - name: 'countSeries', - category: categories.Special - }); - - addFuncDef({ - name: 'constantLine', - category: categories.Special, - params: [{ name: "value", type: "int", }], - defaultParams: [10] - }); - - addFuncDef({ - name: 'cactiStyle', - category: categories.Special, - }); - - addFuncDef({ - name: 'keepLastValue', - category: categories.Special, - params: [{ name: "n", type: "int", }], - defaultParams: [100] - }); - - addFuncDef({ - name: "changed", - category: categories.Special, - params: [], - defaultParams: [] - }); - - addFuncDef({ - name: 'scale', - category: categories.Transform, - params: [{ name: "factor", type: "int", }], - defaultParams: [1] - }); - - addFuncDef({ - name: 'offset', - category: categories.Transform, - params: [{ name: "amount", type: "int", }], - defaultParams: [10] - }); - - addFuncDef({ - name: 'transformNull', - category: categories.Transform, - params: [{ name: "amount", type: "int", }], - defaultParams: [0] - }); - - addFuncDef({ - name: 'integral', - category: categories.Transform, - }); - - addFuncDef({ - name: 'derivative', - category: categories.Transform, - }); - - addFuncDef({ - name: 'nonNegativeDerivative', - category: categories.Transform, - params: [{ name: "max value or 0", type: "int", optional: true }], - defaultParams: [''] - }); - - addFuncDef({ - name: 'timeShift', - category: categories.Transform, - params: [{ name: "amount", type: "select", options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] }], - defaultParams: ['1d'] - }); - - addFuncDef({ - name: 'timeStack', - category: categories.Transform, - params: [ - { name: "timeShiftUnit", type: "select", options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] }, - { name: "timeShiftStart", type: "int" }, - { name: "timeShiftEnd", type: "int" } - ], - defaultParams: ['1d', 0, 7] - }); - - addFuncDef({ - name: 'summarize', - category: categories.Transform, - params: [ - { name: "interval", type: "string" }, - { name: "func", type: "select", options: ['sum', 'avg', 'min', 'max', 'last'] }, - { name: "alignToFrom", type: "boolean", optional: true, options: ['false', 'true'] }, - ], - defaultParams: ['1h', 'sum', 'false'] - }); - - addFuncDef({ - name: 'smartSummarize', - category: categories.Transform, - params: [{ name: "interval", type: "string" }, { name: "func", type: "select", options: ['sum', 'avg', 'min', 'max', 'last'] }], - defaultParams: ['1h', 'sum'] - }); - - addFuncDef({ - name: 'absolute', - category: categories.Transform, - }); - - addFuncDef({ - name: 'hitcount', - category: categories.Transform, - params: [{ name: "interval", type: "string" }], - defaultParams: ['10s'] - }); - - addFuncDef({ - name: 'log', - category: categories.Transform, - params: [{ name: "base", type: "int" }], - defaultParams: ['10'] - }); - - addFuncDef({ - name: 'averageAbove', - category: categories.Filter, - params: [{ name: "n", type: "int", }], - defaultParams: [25] - }); - - addFuncDef({ - name: 'averageBelow', - category: categories.Filter, - params: [{ name: "n", type: "int", }], - defaultParams: [25] - }); - - addFuncDef({ - name: 'currentAbove', - category: categories.Filter, - params: [{ name: "n", type: "int", }], - defaultParams: [25] - }); - - addFuncDef({ - name: 'currentBelow', - category: categories.Filter, - params: [{ name: "n", type: "int", }], - defaultParams: [25] - }); - - addFuncDef({ - name: 'maximumAbove', - category: categories.Filter, - params: [{ name: "value", type: "int" }], - defaultParams: [0] - }); - - addFuncDef({ - name: 'maximumBelow', - category: categories.Filter, - params: [{ name: "value", type: "int" }], - defaultParams: [0] - }); - - addFuncDef({ - name: 'minimumAbove', - category: categories.Filter, - params: [{ name: "value", type: "int" }], - defaultParams: [0] - }); - - addFuncDef({ - name: 'minimumBelow', - category: categories.Filter, - params: [{ name: "value", type: "int" }], - defaultParams: [0] - }); - - addFuncDef({ - name: 'limit', - category: categories.Filter, - params: [{ name: "n", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'mostDeviant', - category: categories.Filter, - params: [{ name: "n", type: "int" }], - defaultParams: [10] - }); - - addFuncDef({ - name: "exclude", - category: categories.Filter, - params: [{ name: "exclude", type: 'string' }], - defaultParams: ['exclude'] - }); - - addFuncDef({ - name: 'highestCurrent', - category: categories.Filter, - params: [{ name: "count", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'highestMax', - category: categories.Filter, - params: [{ name: "count", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'lowestCurrent', - category: categories.Filter, - params: [{ name: "count", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'movingAverage', - category: categories.Filter, - params: [{ name: "windowSize", type: "int_or_interval", options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }], - defaultParams: [10] - }); - - addFuncDef({ - name: 'movingMedian', - category: categories.Filter, - params: [{ name: "windowSize", type: "int_or_interval", options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }], - defaultParams: ['5'] - }); - - addFuncDef({ - name: 'stdev', - category: categories.Filter, - params: [{ name: "n", type: "int" }, { name: "tolerance", type: "int" }], - defaultParams: [5,0.1] - }); - - addFuncDef({ - name: 'highestAverage', - category: categories.Filter, - params: [{ name: "count", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'lowestAverage', - category: categories.Filter, - params: [{ name: "count", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'removeAbovePercentile', - category: categories.Filter, - params: [{ name: "n", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'removeAboveValue', - category: categories.Filter, - params: [{ name: "n", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'removeBelowPercentile', - category: categories.Filter, - params: [{ name: "n", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'removeBelowValue', - category: categories.Filter, - params: [{ name: "n", type: "int" }], - defaultParams: [5] - }); - - addFuncDef({ - name: 'useSeriesAbove', - category: categories.Filter, - params: [ - { name: "value", type: "int" }, - { name: "search", type: "string" }, - { name: "replace", type: "string" } - ], - defaultParams: [0, 'search', 'replace'] - }); - - //////////////////// - // Graphite 1.0.x // - //////////////////// - - addFuncDef({ - name: 'aggregateLine', - category: categories.Combine, - params: [{ name: "func", type: "select", options: ['sum', 'avg', 'min', 'max', 'last']}], - defaultParams: ['avg'], - version: '1.0' - }); - - addFuncDef({ - name: 'averageOutsidePercentile', - category: categories.Filter, - params: [{ name: "n", type: "int", }], - defaultParams: [95], - version: '1.0' - }); - - addFuncDef({ - name: 'delay', - category: categories.Transform, - params: [{ name: 'steps', type: 'int', }], - defaultParams: [1], - version: '1.0' - }); - - addFuncDef({ - name: 'exponentialMovingAverage', - category: categories.Calculate, - params: [{ name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }], - defaultParams: [10], - version: '1.0' - }); - - addFuncDef({ - name: 'fallbackSeries', - category: categories.Special, - params: [{ name: 'fallback', type: 'string' }], - defaultParams: ['constantLine(0)'], - version: '1.0' - }); - - addFuncDef({ - name: "grep", - category: categories.Filter, - params: [{ name: "grep", type: 'string' }], - defaultParams: ['grep'], - version: '1.0' - }); - - addFuncDef({ - name: "groupByNodes", - category: categories.Special, - params: [ - { - name: "function", - type: "string", - options: ['sum', 'avg', 'maxSeries'] - }, - { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] }, - { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true }, - { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true }, - { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true }, - ], - defaultParams: ["sum", 3], - version: '1.0' - }); - - addFuncDef({ - name: 'integralByInterval', - category: categories.Transform, - params: [{ name: "intervalUnit", type: "select", options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] }], - defaultParams: ['1d'], - version: '1.0' - }); - - addFuncDef({ - name: 'interpolate', - category: categories.Transform, - params: [{ name: 'limit', type: 'int', optional: true}], - defaultParams: [], - version: '1.0' - }); - - addFuncDef({ - name: 'invert', - category: categories.Transform, - version: '1.0' - }); - - addFuncDef({ - name: 'isNonNull', - category: categories.Combine, - version: '1.0' - }); - - addFuncDef({ - name: 'linearRegression', - category: categories.Calculate, - params: [ - { name: "startSourceAt", type: "select", options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'], optional: true }, - { name: "endSourceAt", type: "select", options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'], optional: true } - ], - defaultParams: [], - version: '1.0' - }); - - addFuncDef({ - name: 'mapSeries', - shortName: 'map', - params: [{ name: "node", type: 'int' }], - defaultParams: [3], - category: categories.Combine, - version: '1.0' - }); - - addFuncDef({ - name: 'movingMin', - category: categories.Calculate, - params: [{ name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }], - defaultParams: [10], - version: '1.0' - }); - - addFuncDef({ - name: 'movingMax', - category: categories.Calculate, - params: [{ name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }], - defaultParams: [10], - version: '1.0' - }); - - addFuncDef({ - name: 'movingSum', - category: categories.Calculate, - params: [{ name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }], - defaultParams: [10], - version: '1.0' - }); - - addFuncDef({ - name: "multiplySeriesWithWildcards", - category: categories.Calculate, - params: [ - { name: "position", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] }, - { name: "position", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true }, - { name: "position", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true }, - { name: "position", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true }, - ], - defaultParams: [2], - version: '1.0' - }); - - addFuncDef({ - name: 'offsetToZero', - category: categories.Transform, - version: '1.0' - }); - - addFuncDef({ - name: 'pow', - category: categories.Transform, - params: [{ name: 'factor', type: 'int' }], - defaultParams: [10], - version: '1.0' - }); - - addFuncDef({ - name: 'powSeries', - category: categories.Transform, - params: optionalSeriesRefArgs, - defaultParams: [''], - version: '1.0' - }); - - addFuncDef({ - name: 'reduceSeries', - shortName: 'reduce', - params: [ - { name: "function", type: 'string', options: ['asPercent', 'diffSeries', 'divideSeries'] }, - { name: "reduceNode", type: 'int', options: [0,1,2,3,4,5,6,7,8,9,10,11,12,13] }, - { name: "reduceMatchers", type: 'string' }, - { name: "reduceMatchers", type: 'string' }, - ], - defaultParams: ['asPercent', 2, 'used_bytes', 'total_bytes'], - category: categories.Combine, - version: '1.0' - }); - - addFuncDef({ - name: 'removeBetweenPercentile', - category: categories.Filter, - params: [{ name: "n", type: "int", }], - defaultParams: [95], - version: '1.0' - }); - - addFuncDef({ - name: 'removeEmptySeries', - category: categories.Filter, - version: '1.0' - }); - - addFuncDef({ - name: 'squareRoot', - category: categories.Transform, - version: '1.0' - }); - - addFuncDef({ - name: 'timeSlice', - category: categories.Transform, - params: [ - { name: "startSliceAt", type: "select", options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d']}, - { name: "endSliceAt", type: "select", options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'], optional: true } - ], - defaultParams: ['-1h'], - version: '1.0' - }); - - addFuncDef({ - name: 'weightedAverage', - category: categories.Filter, - params: [ - { name: 'other', type: 'value_or_series', optional: true }, - { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] }, - ], - defaultParams: ['#A', 4], - version: '1.0' - }); - - addFuncDef({ - name: 'seriesByTag', - category: categories.Special, - params: [ - { name: "tagExpression", type: "string" }, - { name: "tagExpression", type: "string", optional: true }, - { name: "tagExpression", type: "string", optional: true }, - { name: "tagExpression", type: "string", optional: true }, - ], - version: '1.1' - }); - - addFuncDef({ - name: "groupByTags", - category: categories.Special, - params: [ - { - name: "function", - type: "string", - options: ['sum', 'avg', 'maxSeries'] - }, - { name: "tag", type: "string" }, - { name: "tag", type: "string", optional: true }, - { name: "tag", type: "string", optional: true }, - { name: "tag", type: "string", optional: true }, - ], - defaultParams: ["sum", "tag"], - version: '1.1' - }); - - addFuncDef({ - name: "aliasByTags", - category: categories.Special, - params: [ - { name: "tag", type: "string" }, - { name: "tag", type: "string", optional: true }, - { name: "tag", type: "string", optional: true }, - { name: "tag", type: "string", optional: true }, - ], - defaultParams: ["tag"], - version: '1.1' - }); - - _.each(categories, function(funcList, catName) { - categories[catName] = _.sortBy(funcList, 'name'); - }); - - function FuncInstance(funcDef, options) { - this.def = funcDef; - this.params = []; - - if (options && options.withDefaultParams) { - this.params = funcDef.defaultParams.slice(0); - } - - this.updateText(); - } - - FuncInstance.prototype.render = function(metricExp) { - var str = this.def.name + '('; - var parameters = _.map(this.params, function(value, index) { - - var paramType = this.def.params[index].type; - if (paramType === 'int' || paramType === 'value_or_series' || paramType === 'boolean') { - return value; - } - else if (paramType === 'int_or_interval' && $.isNumeric(value)) { - return value; - } - - return "'" + value + "'"; - - }.bind(this)); - - if (metricExp) { - parameters.unshift(metricExp); - } - - return str + parameters.join(', ') + ')'; - }; - - FuncInstance.prototype._hasMultipleParamsInString = function(strValue, index) { - if (strValue.indexOf(',') === -1) { - return false; - } - - return this.def.params[index + 1] && this.def.params[index + 1].optional; - }; - - FuncInstance.prototype.updateParam = function(strValue, index) { - // handle optional parameters - // if string contains ',' and next param is optional, split and update both - if (this._hasMultipleParamsInString(strValue, index)) { - _.each(strValue.split(','), function(partVal, idx) { - this.updateParam(partVal.trim(), index + idx); - }.bind(this)); - return; - } - - if (strValue === '' && this.def.params[index].optional) { - this.params.splice(index, 1); - } - else { - this.params[index] = strValue; - } - - this.updateText(); - }; - - FuncInstance.prototype.updateText = function () { - if (this.params.length === 0) { - this.text = this.def.name + '()'; - return; - } - - var text = this.def.name + '('; - text += this.params.join(', '); - text += ')'; - this.text = text; - }; - - function isVersionRelatedFunction(func, graphiteVersion) { - return isVersionGreaterOrEqual(graphiteVersion, func.version) || !func.version; - } - - function isVersionGreaterOrEqual(a, b) { - var a_num = Number(a); - var b_num = Number(b); - return a_num >= b_num; - } - - return { - createFuncInstance: function(funcDef, options) { - if (_.isString(funcDef)) { - if (!index[funcDef]) { - throw { message: 'Method not found ' + name }; - } - funcDef = index[funcDef]; - } - return new FuncInstance(funcDef, options); - }, - - getFuncDef: function(name) { - return index[name]; - }, - - getCategories: function(graphiteVersion) { - var filteredCategories = {}; - _.each(categories, function(functions, category) { - var filteredFuncs = _.filter(functions, function(func) { - return isVersionRelatedFunction(func, graphiteVersion); - }); - if (filteredFuncs.length) { - filteredCategories[category] = filteredFuncs; - } - }); - - return filteredCategories; - } - }; - -}); diff --git a/public/app/plugins/datasource/graphite/gfunc.ts b/public/app/plugins/datasource/graphite/gfunc.ts new file mode 100644 index 00000000000..a2510421ad4 --- /dev/null +++ b/public/app/plugins/datasource/graphite/gfunc.ts @@ -0,0 +1,1008 @@ +import _ from 'lodash'; + +var index = []; +var categories = { + Combine: [], + Transform: [], + Calculate: [], + Filter: [], + Special: [], +}; + +function addFuncDef(funcDef) { + funcDef.params = funcDef.params || []; + funcDef.defaultParams = funcDef.defaultParams || []; + + if (funcDef.category) { + funcDef.category.push(funcDef); + } + index[funcDef.name] = funcDef; + index[funcDef.shortName || funcDef.name] = funcDef; +} + +var optionalSeriesRefArgs = [ + { name: 'other', type: 'value_or_series', optional: true }, + { name: 'other', type: 'value_or_series', optional: true }, + { name: 'other', type: 'value_or_series', optional: true }, + { name: 'other', type: 'value_or_series', optional: true }, + { name: 'other', type: 'value_or_series', optional: true }, +]; + +addFuncDef({ + name: 'scaleToSeconds', + category: categories.Transform, + params: [{ name: 'seconds', type: 'int' }], + defaultParams: [1], +}); + +addFuncDef({ + name: 'perSecond', + category: categories.Transform, + params: [{ name: 'max value', type: 'int', optional: true }], + defaultParams: [], +}); + +addFuncDef({ + name: 'holtWintersForecast', + category: categories.Calculate, +}); + +addFuncDef({ + name: 'holtWintersConfidenceBands', + category: categories.Calculate, + params: [{ name: 'delta', type: 'int' }], + defaultParams: [3], +}); + +addFuncDef({ + name: 'holtWintersAberration', + category: categories.Calculate, + params: [{ name: 'delta', type: 'int' }], + defaultParams: [3], +}); + +addFuncDef({ + name: 'nPercentile', + category: categories.Calculate, + params: [{ name: 'Nth percentile', type: 'int' }], + defaultParams: [95], +}); + +addFuncDef({ + name: 'diffSeries', + params: optionalSeriesRefArgs, + defaultParams: ['#A'], + category: categories.Calculate, +}); + +addFuncDef({ + name: 'stddevSeries', + params: optionalSeriesRefArgs, + defaultParams: [''], + category: categories.Calculate, +}); + +addFuncDef({ + name: 'divideSeries', + params: optionalSeriesRefArgs, + defaultParams: ['#A'], + category: categories.Calculate, +}); + +addFuncDef({ + name: 'multiplySeries', + params: optionalSeriesRefArgs, + defaultParams: ['#A'], + category: categories.Calculate, +}); + +addFuncDef({ + name: 'asPercent', + params: optionalSeriesRefArgs, + defaultParams: ['#A'], + category: categories.Calculate, +}); + +addFuncDef({ + name: 'group', + params: optionalSeriesRefArgs, + defaultParams: ['#A', '#B'], + category: categories.Combine, +}); + +addFuncDef({ + name: 'sumSeries', + shortName: 'sum', + category: categories.Combine, + params: optionalSeriesRefArgs, + defaultParams: [''], +}); + +addFuncDef({ + name: 'averageSeries', + shortName: 'avg', + category: categories.Combine, + params: optionalSeriesRefArgs, + defaultParams: [''], +}); + +addFuncDef({ + name: 'rangeOfSeries', + category: categories.Combine, +}); + +addFuncDef({ + name: 'percentileOfSeries', + category: categories.Combine, + params: [{ name: 'n', type: 'int' }, { name: 'interpolate', type: 'boolean', options: ['true', 'false'] }], + defaultParams: [95, 'false'], +}); + +addFuncDef({ + name: 'sumSeriesWithWildcards', + category: categories.Combine, + params: [ + { name: 'node', type: 'int' }, + { name: 'node', type: 'int', optional: true }, + { name: 'node', type: 'int', optional: true }, + { name: 'node', type: 'int', optional: true }, + ], + defaultParams: [3], +}); + +addFuncDef({ + name: 'maxSeries', + shortName: 'max', + category: categories.Combine, +}); + +addFuncDef({ + name: 'minSeries', + shortName: 'min', + category: categories.Combine, +}); + +addFuncDef({ + name: 'averageSeriesWithWildcards', + category: categories.Combine, + params: [{ name: 'node', type: 'int' }, { name: 'node', type: 'int', optional: true }], + defaultParams: [3], +}); + +addFuncDef({ + name: 'alias', + category: categories.Special, + params: [{ name: 'alias', type: 'string' }], + defaultParams: ['alias'], +}); + +addFuncDef({ + name: 'aliasSub', + category: categories.Special, + params: [{ name: 'search', type: 'string' }, { name: 'replace', type: 'string' }], + defaultParams: ['', '\\1'], +}); + +addFuncDef({ + name: 'stacked', + category: categories.Special, + params: [{ name: 'stack', type: 'string' }], + defaultParams: ['stacked'], +}); + +addFuncDef({ + name: 'consolidateBy', + category: categories.Special, + params: [ + { + name: 'function', + type: 'string', + options: ['sum', 'average', 'min', 'max'], + }, + ], + defaultParams: ['max'], +}); + +addFuncDef({ + name: 'cumulative', + category: categories.Special, + params: [], + defaultParams: [], +}); + +addFuncDef({ + name: 'groupByNode', + category: categories.Special, + params: [ + { + name: 'node', + type: 'int', + options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12], + }, + { + name: 'function', + type: 'string', + options: ['sum', 'avg', 'maxSeries'], + }, + ], + defaultParams: [3, 'sum'], +}); + +addFuncDef({ + name: 'aliasByNode', + category: categories.Special, + params: [ + { name: 'node', type: 'int', options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12] }, + { name: 'node', type: 'int', options: [0, -1, -2, -3, -4, -5, -6, -7], optional: true }, + { name: 'node', type: 'int', options: [0, -1, -2, -3, -4, -5, -6, -7], optional: true }, + { name: 'node', type: 'int', options: [0, -1, -2, -3, -4, -5, -6, -7], optional: true }, + ], + defaultParams: [3], +}); + +addFuncDef({ + name: 'substr', + category: categories.Special, + params: [ + { name: 'start', type: 'int', options: [-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12] }, + { name: 'stop', type: 'int', options: [-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12] }, + ], + defaultParams: [0, 0], +}); + +addFuncDef({ + name: 'sortByName', + category: categories.Special, + params: [{ name: 'natural', type: 'boolean', options: ['true', 'false'], optional: true }], + defaultParams: ['false'], +}); + +addFuncDef({ + name: 'sortByMaxima', + category: categories.Special, +}); + +addFuncDef({ + name: 'sortByMinima', + category: categories.Special, +}); + +addFuncDef({ + name: 'sortByTotal', + category: categories.Special, +}); + +addFuncDef({ + name: 'aliasByMetric', + category: categories.Special, +}); + +addFuncDef({ + name: 'randomWalk', + fake: true, + category: categories.Special, + params: [{ name: 'name', type: 'string' }], + defaultParams: ['randomWalk'], +}); + +addFuncDef({ + name: 'countSeries', + category: categories.Special, +}); + +addFuncDef({ + name: 'constantLine', + category: categories.Special, + params: [{ name: 'value', type: 'int' }], + defaultParams: [10], +}); + +addFuncDef({ + name: 'cactiStyle', + category: categories.Special, +}); + +addFuncDef({ + name: 'keepLastValue', + category: categories.Special, + params: [{ name: 'n', type: 'int' }], + defaultParams: [100], +}); + +addFuncDef({ + name: 'changed', + category: categories.Special, + params: [], + defaultParams: [], +}); + +addFuncDef({ + name: 'scale', + category: categories.Transform, + params: [{ name: 'factor', type: 'int' }], + defaultParams: [1], +}); + +addFuncDef({ + name: 'offset', + category: categories.Transform, + params: [{ name: 'amount', type: 'int' }], + defaultParams: [10], +}); + +addFuncDef({ + name: 'transformNull', + category: categories.Transform, + params: [{ name: 'amount', type: 'int' }], + defaultParams: [0], +}); + +addFuncDef({ + name: 'integral', + category: categories.Transform, +}); + +addFuncDef({ + name: 'derivative', + category: categories.Transform, +}); + +addFuncDef({ + name: 'nonNegativeDerivative', + category: categories.Transform, + params: [{ name: 'max value or 0', type: 'int', optional: true }], + defaultParams: [''], +}); + +addFuncDef({ + name: 'timeShift', + category: categories.Transform, + params: [{ name: 'amount', type: 'select', options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] }], + defaultParams: ['1d'], +}); + +addFuncDef({ + name: 'timeStack', + category: categories.Transform, + params: [ + { name: 'timeShiftUnit', type: 'select', options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] }, + { name: 'timeShiftStart', type: 'int' }, + { name: 'timeShiftEnd', type: 'int' }, + ], + defaultParams: ['1d', 0, 7], +}); + +addFuncDef({ + name: 'summarize', + category: categories.Transform, + params: [ + { name: 'interval', type: 'string' }, + { name: 'func', type: 'select', options: ['sum', 'avg', 'min', 'max', 'last'] }, + { name: 'alignToFrom', type: 'boolean', optional: true, options: ['false', 'true'] }, + ], + defaultParams: ['1h', 'sum', 'false'], +}); + +addFuncDef({ + name: 'smartSummarize', + category: categories.Transform, + params: [ + { name: 'interval', type: 'string' }, + { name: 'func', type: 'select', options: ['sum', 'avg', 'min', 'max', 'last'] }, + ], + defaultParams: ['1h', 'sum'], +}); + +addFuncDef({ + name: 'absolute', + category: categories.Transform, +}); + +addFuncDef({ + name: 'hitcount', + category: categories.Transform, + params: [{ name: 'interval', type: 'string' }], + defaultParams: ['10s'], +}); + +addFuncDef({ + name: 'log', + category: categories.Transform, + params: [{ name: 'base', type: 'int' }], + defaultParams: ['10'], +}); + +addFuncDef({ + name: 'averageAbove', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [25], +}); + +addFuncDef({ + name: 'averageBelow', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [25], +}); + +addFuncDef({ + name: 'currentAbove', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [25], +}); + +addFuncDef({ + name: 'currentBelow', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [25], +}); + +addFuncDef({ + name: 'maximumAbove', + category: categories.Filter, + params: [{ name: 'value', type: 'int' }], + defaultParams: [0], +}); + +addFuncDef({ + name: 'maximumBelow', + category: categories.Filter, + params: [{ name: 'value', type: 'int' }], + defaultParams: [0], +}); + +addFuncDef({ + name: 'minimumAbove', + category: categories.Filter, + params: [{ name: 'value', type: 'int' }], + defaultParams: [0], +}); + +addFuncDef({ + name: 'minimumBelow', + category: categories.Filter, + params: [{ name: 'value', type: 'int' }], + defaultParams: [0], +}); + +addFuncDef({ + name: 'limit', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'mostDeviant', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [10], +}); + +addFuncDef({ + name: 'exclude', + category: categories.Filter, + params: [{ name: 'exclude', type: 'string' }], + defaultParams: ['exclude'], +}); + +addFuncDef({ + name: 'highestCurrent', + category: categories.Filter, + params: [{ name: 'count', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'highestMax', + category: categories.Filter, + params: [{ name: 'count', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'lowestCurrent', + category: categories.Filter, + params: [{ name: 'count', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'movingAverage', + category: categories.Filter, + params: [ + { name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }, + ], + defaultParams: [10], +}); + +addFuncDef({ + name: 'movingMedian', + category: categories.Filter, + params: [ + { name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }, + ], + defaultParams: ['5'], +}); + +addFuncDef({ + name: 'stdev', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }, { name: 'tolerance', type: 'int' }], + defaultParams: [5, 0.1], +}); + +addFuncDef({ + name: 'highestAverage', + category: categories.Filter, + params: [{ name: 'count', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'lowestAverage', + category: categories.Filter, + params: [{ name: 'count', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'removeAbovePercentile', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'removeAboveValue', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'removeBelowPercentile', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'removeBelowValue', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [5], +}); + +addFuncDef({ + name: 'useSeriesAbove', + category: categories.Filter, + params: [{ name: 'value', type: 'int' }, { name: 'search', type: 'string' }, { name: 'replace', type: 'string' }], + defaultParams: [0, 'search', 'replace'], +}); + +//////////////////// +// Graphite 1.0.x // +//////////////////// + +addFuncDef({ + name: 'aggregateLine', + category: categories.Combine, + params: [{ name: 'func', type: 'select', options: ['sum', 'avg', 'min', 'max', 'last'] }], + defaultParams: ['avg'], + version: '1.0', +}); + +addFuncDef({ + name: 'averageOutsidePercentile', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [95], + version: '1.0', +}); + +addFuncDef({ + name: 'delay', + category: categories.Transform, + params: [{ name: 'steps', type: 'int' }], + defaultParams: [1], + version: '1.0', +}); + +addFuncDef({ + name: 'exponentialMovingAverage', + category: categories.Calculate, + params: [ + { name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }, + ], + defaultParams: [10], + version: '1.0', +}); + +addFuncDef({ + name: 'fallbackSeries', + category: categories.Special, + params: [{ name: 'fallback', type: 'string' }], + defaultParams: ['constantLine(0)'], + version: '1.0', +}); + +addFuncDef({ + name: 'grep', + category: categories.Filter, + params: [{ name: 'grep', type: 'string' }], + defaultParams: ['grep'], + version: '1.0', +}); + +addFuncDef({ + name: 'groupByNodes', + category: categories.Special, + params: [ + { + name: 'function', + type: 'string', + options: ['sum', 'avg', 'maxSeries'], + }, + { name: 'node', type: 'int', options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12] }, + { name: 'node', type: 'int', options: [0, -1, -2, -3, -4, -5, -6, -7], optional: true }, + { name: 'node', type: 'int', options: [0, -1, -2, -3, -4, -5, -6, -7], optional: true }, + { name: 'node', type: 'int', options: [0, -1, -2, -3, -4, -5, -6, -7], optional: true }, + ], + defaultParams: ['sum', 3], + version: '1.0', +}); + +addFuncDef({ + name: 'integralByInterval', + category: categories.Transform, + params: [{ name: 'intervalUnit', type: 'select', options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] }], + defaultParams: ['1d'], + version: '1.0', +}); + +addFuncDef({ + name: 'interpolate', + category: categories.Transform, + params: [{ name: 'limit', type: 'int', optional: true }], + defaultParams: [], + version: '1.0', +}); + +addFuncDef({ + name: 'invert', + category: categories.Transform, + version: '1.0', +}); + +addFuncDef({ + name: 'isNonNull', + category: categories.Combine, + version: '1.0', +}); + +addFuncDef({ + name: 'linearRegression', + category: categories.Calculate, + params: [ + { + name: 'startSourceAt', + type: 'select', + options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'], + optional: true, + }, + { + name: 'endSourceAt', + type: 'select', + options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'], + optional: true, + }, + ], + defaultParams: [], + version: '1.0', +}); + +addFuncDef({ + name: 'mapSeries', + shortName: 'map', + params: [{ name: 'node', type: 'int' }], + defaultParams: [3], + category: categories.Combine, + version: '1.0', +}); + +addFuncDef({ + name: 'movingMin', + category: categories.Calculate, + params: [ + { name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }, + ], + defaultParams: [10], + version: '1.0', +}); + +addFuncDef({ + name: 'movingMax', + category: categories.Calculate, + params: [ + { name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }, + ], + defaultParams: [10], + version: '1.0', +}); + +addFuncDef({ + name: 'movingSum', + category: categories.Calculate, + params: [ + { name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }, + ], + defaultParams: [10], + version: '1.0', +}); + +addFuncDef({ + name: 'multiplySeriesWithWildcards', + category: categories.Calculate, + params: [ + { name: 'position', type: 'int', options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12] }, + { name: 'position', type: 'int', options: [0, -1, -2, -3, -4, -5, -6, -7], optional: true }, + { name: 'position', type: 'int', options: [0, -1, -2, -3, -4, -5, -6, -7], optional: true }, + { name: 'position', type: 'int', options: [0, -1, -2, -3, -4, -5, -6, -7], optional: true }, + ], + defaultParams: [2], + version: '1.0', +}); + +addFuncDef({ + name: 'offsetToZero', + category: categories.Transform, + version: '1.0', +}); + +addFuncDef({ + name: 'pow', + category: categories.Transform, + params: [{ name: 'factor', type: 'int' }], + defaultParams: [10], + version: '1.0', +}); + +addFuncDef({ + name: 'powSeries', + category: categories.Transform, + params: optionalSeriesRefArgs, + defaultParams: [''], + version: '1.0', +}); + +addFuncDef({ + name: 'reduceSeries', + shortName: 'reduce', + params: [ + { name: 'function', type: 'string', options: ['asPercent', 'diffSeries', 'divideSeries'] }, + { name: 'reduceNode', type: 'int', options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] }, + { name: 'reduceMatchers', type: 'string' }, + { name: 'reduceMatchers', type: 'string' }, + ], + defaultParams: ['asPercent', 2, 'used_bytes', 'total_bytes'], + category: categories.Combine, + version: '1.0', +}); + +addFuncDef({ + name: 'removeBetweenPercentile', + category: categories.Filter, + params: [{ name: 'n', type: 'int' }], + defaultParams: [95], + version: '1.0', +}); + +addFuncDef({ + name: 'removeEmptySeries', + category: categories.Filter, + version: '1.0', +}); + +addFuncDef({ + name: 'squareRoot', + category: categories.Transform, + version: '1.0', +}); + +addFuncDef({ + name: 'timeSlice', + category: categories.Transform, + params: [ + { name: 'startSliceAt', type: 'select', options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'] }, + { + name: 'endSliceAt', + type: 'select', + options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'], + optional: true, + }, + ], + defaultParams: ['-1h'], + version: '1.0', +}); + +addFuncDef({ + name: 'weightedAverage', + category: categories.Filter, + params: [ + { name: 'other', type: 'value_or_series', optional: true }, + { name: 'node', type: 'int', options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12] }, + ], + defaultParams: ['#A', 4], + version: '1.0', +}); + +addFuncDef({ + name: 'seriesByTag', + category: categories.Special, + params: [ + { name: 'tagExpression', type: 'string' }, + { name: 'tagExpression', type: 'string', optional: true }, + { name: 'tagExpression', type: 'string', optional: true }, + { name: 'tagExpression', type: 'string', optional: true }, + ], + version: '1.1', +}); + +addFuncDef({ + name: 'groupByTags', + category: categories.Special, + params: [ + { + name: 'function', + type: 'string', + options: ['sum', 'avg', 'maxSeries'], + }, + { name: 'tag', type: 'string' }, + { name: 'tag', type: 'string', optional: true }, + { name: 'tag', type: 'string', optional: true }, + { name: 'tag', type: 'string', optional: true }, + ], + defaultParams: ['sum', 'tag'], + version: '1.1', +}); + +addFuncDef({ + name: 'aliasByTags', + category: categories.Special, + params: [ + { name: 'tag', type: 'string' }, + { name: 'tag', type: 'string', optional: true }, + { name: 'tag', type: 'string', optional: true }, + { name: 'tag', type: 'string', optional: true }, + ], + defaultParams: ['tag'], + version: '1.1', +}); + +_.each(categories, function(funcList, catName) { + categories[catName] = _.sortBy(funcList, 'name'); +}); + +function FuncInstance(funcDef, options?) { + this.def = funcDef; + this.params = []; + + if (options && options.withDefaultParams) { + this.params = funcDef.defaultParams.slice(0); + } + + this.updateText(); +} + +function isNumeric(obj) { + return !isNaN(parseFloat(obj)) && isFinite(obj); +} + +FuncInstance.prototype.render = function(metricExp) { + var str = this.def.name + '('; + var parameters = _.map( + this.params, + function(value, index) { + var paramType = this.def.params[index].type; + if (paramType === 'int' || paramType === 'value_or_series' || paramType === 'boolean') { + return value; + } else if (paramType === 'int_or_interval' && isNumeric(value)) { + return value; + } + + return "'" + value + "'"; + }.bind(this), + ); + + if (metricExp) { + parameters.unshift(metricExp); + } + + return str + parameters.join(', ') + ')'; +}; + +FuncInstance.prototype._hasMultipleParamsInString = function(strValue, index) { + if (strValue.indexOf(',') === -1) { + return false; + } + + return this.def.params[index + 1] && this.def.params[index + 1].optional; +}; + +FuncInstance.prototype.updateParam = function(strValue, index) { + // handle optional parameters + // if string contains ',' and next param is optional, split and update both + if (this._hasMultipleParamsInString(strValue, index)) { + _.each( + strValue.split(','), + function(partVal, idx) { + this.updateParam(partVal.trim(), index + idx); + }.bind(this), + ); + return; + } + + if (strValue === '' && this.def.params[index].optional) { + this.params.splice(index, 1); + } else { + this.params[index] = strValue; + } + + this.updateText(); +}; + +FuncInstance.prototype.updateText = function() { + if (this.params.length === 0) { + this.text = this.def.name + '()'; + return; + } + + var text = this.def.name + '('; + text += this.params.join(', '); + text += ')'; + this.text = text; +}; + +function isVersionRelatedFunction(func, graphiteVersion) { + return isVersionGreaterOrEqual(graphiteVersion, func.version) || !func.version; +} + +function isVersionGreaterOrEqual(a, b) { + var a_num = Number(a); + var b_num = Number(b); + return a_num >= b_num; +} + +export default { + createFuncInstance: function(funcDef, options?) { + if (_.isString(funcDef)) { + if (!index[funcDef]) { + throw { message: 'Method not found ' + name }; + } + funcDef = index[funcDef]; + } + return new FuncInstance(funcDef, options); + }, + + getFuncDef: function(name) { + return index[name]; + }, + + getCategories: function(graphiteVersion) { + var filteredCategories: any = {}; + _.each(categories, function(functions, category) { + var filteredFuncs = _.filter(functions, function(func) { + return isVersionRelatedFunction(func, graphiteVersion); + }); + if (filteredFuncs.length) { + filteredCategories[category] = filteredFuncs; + } + }); + + return filteredCategories; + }, +}; diff --git a/public/app/plugins/datasource/graphite/specs/gfunc_specs.ts b/public/app/plugins/datasource/graphite/specs/gfunc.jest.ts similarity index 68% rename from public/app/plugins/datasource/graphite/specs/gfunc_specs.ts rename to public/app/plugins/datasource/graphite/specs/gfunc.jest.ts index 4f8f00d901b..49e3afacee2 100644 --- a/public/app/plugins/datasource/graphite/specs/gfunc_specs.ts +++ b/public/app/plugins/datasource/graphite/specs/gfunc.jest.ts @@ -1,25 +1,23 @@ - -import {describe, it, expect} from 'test/lib/common'; import gfunc from '../gfunc'; describe('when creating func instance from func names', function() { it('should return func instance', function() { var func = gfunc.createFuncInstance('sumSeries'); - expect(func).to.be.ok(); - expect(func.def.name).to.equal('sumSeries'); - expect(func.def.params.length).to.equal(5); - expect(func.def.defaultParams.length).to.equal(1); + expect(func).toBeTruthy(); + expect(func.def.name).toEqual('sumSeries'); + expect(func.def.params.length).toEqual(5); + expect(func.def.defaultParams.length).toEqual(1); }); it('should return func instance with shortName', function() { var func = gfunc.createFuncInstance('sum'); - expect(func).to.be.ok(); + expect(func).toBeTruthy(); }); it('should return func instance from funcDef', function() { var func = gfunc.createFuncInstance('sum'); var func2 = gfunc.createFuncInstance(func.def); - expect(func2).to.be.ok(); + expect(func2).toBeTruthy(); }); it('func instance should have text representation', function() { @@ -27,7 +25,7 @@ describe('when creating func instance from func names', function() { func.params[0] = 5; func.params[1] = 'avg'; func.updateText(); - expect(func.text).to.equal("groupByNode(5, avg)"); + expect(func.text).toEqual("groupByNode(5, avg)"); }); }); @@ -35,51 +33,51 @@ describe('when rendering func instance', function() { it('should handle single metric param', function() { var func = gfunc.createFuncInstance('sumSeries'); - expect(func.render('hello.metric')).to.equal("sumSeries(hello.metric)"); + expect(func.render('hello.metric')).toEqual("sumSeries(hello.metric)"); }); it('should include default params if options enable it', function() { var func = gfunc.createFuncInstance('scaleToSeconds', { withDefaultParams: true }); - expect(func.render('hello')).to.equal("scaleToSeconds(hello, 1)"); + expect(func.render('hello')).toEqual("scaleToSeconds(hello, 1)"); }); it('should handle int or interval params with number', function() { var func = gfunc.createFuncInstance('movingMedian'); func.params[0] = '5'; - expect(func.render('hello')).to.equal("movingMedian(hello, 5)"); + expect(func.render('hello')).toEqual("movingMedian(hello, 5)"); }); it('should handle int or interval params with interval string', function() { var func = gfunc.createFuncInstance('movingMedian'); func.params[0] = '5min'; - expect(func.render('hello')).to.equal("movingMedian(hello, '5min')"); + expect(func.render('hello')).toEqual("movingMedian(hello, '5min')"); }); it('should handle metric param and int param and string param', function() { var func = gfunc.createFuncInstance('groupByNode'); func.params[0] = 5; func.params[1] = 'avg'; - expect(func.render('hello.metric')).to.equal("groupByNode(hello.metric, 5, 'avg')"); + expect(func.render('hello.metric')).toEqual("groupByNode(hello.metric, 5, 'avg')"); }); it('should handle function with no metric param', function() { var func = gfunc.createFuncInstance('randomWalk'); func.params[0] = 'test'; - expect(func.render(undefined)).to.equal("randomWalk('test')"); + expect(func.render(undefined)).toEqual("randomWalk('test')"); }); it('should handle function multiple series params', function() { var func = gfunc.createFuncInstance('asPercent'); func.params[0] = '#B'; - expect(func.render('#A')).to.equal("asPercent(#A, #B)"); + expect(func.render('#A')).toEqual("asPercent(#A, #B)"); }); }); describe('when requesting function categories', function() { it('should return function categories', function() { - var catIndex = gfunc.getCategories(); - expect(catIndex.Special.length).to.be.greaterThan(8); + var catIndex = gfunc.getCategories('1.0'); + expect(catIndex.Special.length).toBeGreaterThan(8); }); }); @@ -87,14 +85,14 @@ describe('when updating func param', function() { it('should update param value and update text representation', function() { var func = gfunc.createFuncInstance('summarize', { withDefaultParams: true }); func.updateParam('1h', 0); - expect(func.params[0]).to.be('1h'); - expect(func.text).to.be('summarize(1h, sum, false)'); + expect(func.params[0]).toBe('1h'); + expect(func.text).toBe('summarize(1h, sum, false)'); }); it('should parse numbers as float', function() { var func = gfunc.createFuncInstance('scale'); func.updateParam('0.001', 0); - expect(func.params[0]).to.be('0.001'); + expect(func.params[0]).toBe('0.001'); }); }); @@ -102,24 +100,24 @@ describe('when updating func param with optional second parameter', function() { it('should update value and text', function() { var func = gfunc.createFuncInstance('aliasByNode'); func.updateParam('1', 0); - expect(func.params[0]).to.be('1'); + expect(func.params[0]).toBe('1'); }); it('should slit text and put value in second param', function() { var func = gfunc.createFuncInstance('aliasByNode'); func.updateParam('4,-5', 0); - expect(func.params[0]).to.be('4'); - expect(func.params[1]).to.be('-5'); - expect(func.text).to.be('aliasByNode(4, -5)'); + expect(func.params[0]).toBe('4'); + expect(func.params[1]).toBe('-5'); + expect(func.text).toBe('aliasByNode(4, -5)'); }); it('should remove second param when empty string is set', function() { var func = gfunc.createFuncInstance('aliasByNode'); func.updateParam('4,-5', 0); func.updateParam('', 1); - expect(func.params[0]).to.be('4'); - expect(func.params[1]).to.be(undefined); - expect(func.text).to.be('aliasByNode(4)'); + expect(func.params[0]).toBe('4'); + expect(func.params[1]).toBe(undefined); + expect(func.text).toBe('aliasByNode(4)'); }); }); diff --git a/public/app/plugins/datasource/graphite/specs/lexer_specs.ts b/public/app/plugins/datasource/graphite/specs/lexer.jest.ts similarity index 50% rename from public/app/plugins/datasource/graphite/specs/lexer_specs.ts rename to public/app/plugins/datasource/graphite/specs/lexer.jest.ts index e68c17099fe..78edc5f447d 100644 --- a/public/app/plugins/datasource/graphite/specs/lexer_specs.ts +++ b/public/app/plugins/datasource/graphite/specs/lexer.jest.ts @@ -1,5 +1,3 @@ - -import {describe, it, expect} from 'test/lib/common'; import {Lexer} from '../lexer'; describe('when lexing graphite expression', function() { @@ -7,120 +5,120 @@ describe('when lexing graphite expression', function() { it('should tokenize metric expression', function() { var lexer = new Lexer('metric.test.*.asd.count'); var tokens = lexer.tokenize(); - expect(tokens[0].value).to.be('metric'); - expect(tokens[1].value).to.be('.'); - expect(tokens[2].type).to.be('identifier'); - expect(tokens[4].type).to.be('identifier'); - expect(tokens[4].pos).to.be(13); + expect(tokens[0].value).toBe('metric'); + expect(tokens[1].value).toBe('.'); + expect(tokens[2].type).toBe('identifier'); + expect(tokens[4].type).toBe('identifier'); + expect(tokens[4].pos).toBe(13); }); it('should tokenize metric expression with dash', function() { var lexer = new Lexer('metric.test.se1-server-*.asd.count'); var tokens = lexer.tokenize(); - expect(tokens[4].type).to.be('identifier'); - expect(tokens[4].value).to.be('se1-server-*'); + expect(tokens[4].type).toBe('identifier'); + expect(tokens[4].value).toBe('se1-server-*'); }); it('should tokenize metric expression with dash2', function() { var lexer = new Lexer('net.192-168-1-1.192-168-1-9.ping_value.*'); var tokens = lexer.tokenize(); - expect(tokens[0].value).to.be('net'); - expect(tokens[2].value).to.be('192-168-1-1'); + expect(tokens[0].value).toBe('net'); + expect(tokens[2].value).toBe('192-168-1-1'); }); it('should tokenize metric expression with equal sign', function() { var lexer = new Lexer('apps=test'); var tokens = lexer.tokenize(); - expect(tokens[0].value).to.be('apps=test'); + expect(tokens[0].value).toBe('apps=test'); }); it('simple function2', function() { var lexer = new Lexer('offset(test.metric, -100)'); var tokens = lexer.tokenize(); - expect(tokens[2].type).to.be('identifier'); - expect(tokens[4].type).to.be('identifier'); - expect(tokens[6].type).to.be('number'); + expect(tokens[2].type).toBe('identifier'); + expect(tokens[4].type).toBe('identifier'); + expect(tokens[6].type).toBe('number'); }); it('should tokenize metric expression with curly braces', function() { var lexer = new Lexer('metric.se1-{first, second}.count'); var tokens = lexer.tokenize(); - expect(tokens.length).to.be(10); - expect(tokens[3].type).to.be('{'); - expect(tokens[4].value).to.be('first'); - expect(tokens[5].value).to.be(','); - expect(tokens[6].value).to.be('second'); + expect(tokens.length).toBe(10); + expect(tokens[3].type).toBe('{'); + expect(tokens[4].value).toBe('first'); + expect(tokens[5].value).toBe(','); + expect(tokens[6].value).toBe('second'); }); it('should tokenize metric expression with number segments', function() { var lexer = new Lexer("metric.10.12_10.test"); var tokens = lexer.tokenize(); - expect(tokens[0].type).to.be('identifier'); - expect(tokens[2].type).to.be('identifier'); - expect(tokens[2].value).to.be('10'); - expect(tokens[4].value).to.be('12_10'); - expect(tokens[4].type).to.be('identifier'); + expect(tokens[0].type).toBe('identifier'); + expect(tokens[2].type).toBe('identifier'); + expect(tokens[2].value).toBe('10'); + expect(tokens[4].value).toBe('12_10'); + expect(tokens[4].type).toBe('identifier'); }); it('should tokenize metric expression with segment that start with number', function() { var lexer = new Lexer("metric.001-server"); var tokens = lexer.tokenize(); - expect(tokens[0].type).to.be('identifier'); - expect(tokens[2].type).to.be('identifier'); - expect(tokens.length).to.be(3); + expect(tokens[0].type).toBe('identifier'); + expect(tokens[2].type).toBe('identifier'); + expect(tokens.length).toBe(3); }); it('should tokenize func call with numbered metric and number arg', function() { var lexer = new Lexer("scale(metric.10, 15)"); var tokens = lexer.tokenize(); - expect(tokens[0].type).to.be('identifier'); - expect(tokens[2].type).to.be('identifier'); - expect(tokens[2].value).to.be('metric'); - expect(tokens[4].value).to.be('10'); - expect(tokens[4].type).to.be('number'); - expect(tokens[6].type).to.be('number'); + expect(tokens[0].type).toBe('identifier'); + expect(tokens[2].type).toBe('identifier'); + expect(tokens[2].value).toBe('metric'); + expect(tokens[4].value).toBe('10'); + expect(tokens[4].type).toBe('number'); + expect(tokens[6].type).toBe('number'); }); it('should tokenize metric with template parameter', function() { var lexer = new Lexer("metric.[[server]].test"); var tokens = lexer.tokenize(); - expect(tokens[2].type).to.be('identifier'); - expect(tokens[2].value).to.be('[[server]]'); - expect(tokens[4].type).to.be('identifier'); + expect(tokens[2].type).toBe('identifier'); + expect(tokens[2].value).toBe('[[server]]'); + expect(tokens[4].type).toBe('identifier'); }); it('should tokenize metric with question mark', function() { var lexer = new Lexer("metric.server_??.test"); var tokens = lexer.tokenize(); - expect(tokens[2].type).to.be('identifier'); - expect(tokens[2].value).to.be('server_??'); - expect(tokens[4].type).to.be('identifier'); + expect(tokens[2].type).toBe('identifier'); + expect(tokens[2].value).toBe('server_??'); + expect(tokens[4].type).toBe('identifier'); }); it('should handle error with unterminated string', function() { var lexer = new Lexer("alias(metric, 'asd)"); var tokens = lexer.tokenize(); - expect(tokens[0].value).to.be('alias'); - expect(tokens[1].value).to.be('('); - expect(tokens[2].value).to.be('metric'); - expect(tokens[3].value).to.be(','); - expect(tokens[4].type).to.be('string'); - expect(tokens[4].isUnclosed).to.be(true); - expect(tokens[4].pos).to.be(20); + expect(tokens[0].value).toBe('alias'); + expect(tokens[1].value).toBe('('); + expect(tokens[2].value).toBe('metric'); + expect(tokens[3].value).toBe(','); + expect(tokens[4].type).toBe('string'); + expect(tokens[4].isUnclosed).toBe(true); + expect(tokens[4].pos).toBe(20); }); it('should handle float parameters', function() { var lexer = new Lexer("alias(metric, 0.002)"); var tokens = lexer.tokenize(); - expect(tokens[4].type).to.be('number'); - expect(tokens[4].value).to.be('0.002'); + expect(tokens[4].type).toBe('number'); + expect(tokens[4].value).toBe('0.002'); }); it('should handle bool parameters', function() { var lexer = new Lexer("alias(metric, true, false)"); var tokens = lexer.tokenize(); - expect(tokens[4].type).to.be('bool'); - expect(tokens[4].value).to.be('true'); - expect(tokens[6].type).to.be('bool'); + expect(tokens[4].type).toBe('bool'); + expect(tokens[4].value).toBe('true'); + expect(tokens[6].type).toBe('bool'); }); }); diff --git a/public/app/plugins/datasource/graphite/specs/parser.jest.ts b/public/app/plugins/datasource/graphite/specs/parser.jest.ts new file mode 100644 index 00000000000..76c6c24219f --- /dev/null +++ b/public/app/plugins/datasource/graphite/specs/parser.jest.ts @@ -0,0 +1,182 @@ +import {Parser} from '../parser'; + +describe('when parsing', function() { + + it('simple metric expression', function() { + var parser = new Parser('metric.test.*.asd.count'); + var rootNode = parser.getAst(); + + expect(rootNode.type).toBe('metric'); + expect(rootNode.segments.length).toBe(5); + expect(rootNode.segments[0].value).toBe('metric'); + }); + + it('simple metric expression with numbers in segments', function() { + var parser = new Parser('metric.10.15_20.5'); + var rootNode = parser.getAst(); + + expect(rootNode.type).toBe('metric'); + expect(rootNode.segments.length).toBe(4); + expect(rootNode.segments[1].value).toBe('10'); + expect(rootNode.segments[2].value).toBe('15_20'); + expect(rootNode.segments[3].value).toBe('5'); + }); + + it('simple metric expression with curly braces', function() { + var parser = new Parser('metric.se1-{count, max}'); + var rootNode = parser.getAst(); + + expect(rootNode.type).toBe('metric'); + expect(rootNode.segments.length).toBe(2); + expect(rootNode.segments[1].value).toBe('se1-{count,max}'); + }); + + it('simple metric expression with curly braces at start of segment and with post chars', function() { + var parser = new Parser('metric.{count, max}-something.count'); + var rootNode = parser.getAst(); + + expect(rootNode.type).toBe('metric'); + expect(rootNode.segments.length).toBe(3); + expect(rootNode.segments[1].value).toBe('{count,max}-something'); + }); + + it('simple function', function() { + var parser = new Parser('sum(test)'); + var rootNode = parser.getAst(); + expect(rootNode.type).toBe('function'); + expect(rootNode.params.length).toBe(1); + }); + + it('simple function2', function() { + var parser = new Parser('offset(test.metric, -100)'); + var rootNode = parser.getAst(); + expect(rootNode.type).toBe('function'); + expect(rootNode.params[0].type).toBe('metric'); + expect(rootNode.params[1].type).toBe('number'); + }); + + it('simple function with string arg', function() { + var parser = new Parser("randomWalk('test')"); + var rootNode = parser.getAst(); + expect(rootNode.type).toBe('function'); + expect(rootNode.params.length).toBe(1); + expect(rootNode.params[0].type).toBe('string'); + }); + + it('function with multiple args', function() { + var parser = new Parser("sum(test, 1, 'test')"); + var rootNode = parser.getAst(); + + expect(rootNode.type).toBe('function'); + expect(rootNode.params.length).toBe(3); + expect(rootNode.params[0].type).toBe('metric'); + expect(rootNode.params[1].type).toBe('number'); + expect(rootNode.params[2].type).toBe('string'); + }); + + it('function with nested function', function() { + var parser = new Parser("sum(scaleToSeconds(test, 1))"); + var rootNode = parser.getAst(); + + expect(rootNode.type).toBe('function'); + expect(rootNode.params.length).toBe(1); + expect(rootNode.params[0].type).toBe('function'); + expect(rootNode.params[0].name).toBe('scaleToSeconds'); + expect(rootNode.params[0].params.length).toBe(2); + expect(rootNode.params[0].params[0].type).toBe('metric'); + expect(rootNode.params[0].params[1].type).toBe('number'); + }); + + it('function with multiple series', function() { + var parser = new Parser("sum(test.test.*.count, test.timers.*.count)"); + var rootNode = parser.getAst(); + + expect(rootNode.type).toBe('function'); + expect(rootNode.params.length).toBe(2); + expect(rootNode.params[0].type).toBe('metric'); + expect(rootNode.params[1].type).toBe('metric'); + }); + + it('function with templated series', function() { + var parser = new Parser("sum(test.[[server]].count)"); + var rootNode = parser.getAst(); + + expect(rootNode.message).toBe(undefined); + expect(rootNode.params[0].type).toBe('metric'); + expect(rootNode.params[0].segments[1].type).toBe('segment'); + expect(rootNode.params[0].segments[1].value).toBe('[[server]]'); + }); + + it('invalid metric expression', function() { + var parser = new Parser('metric.test.*.asd.'); + var rootNode = parser.getAst(); + + expect(rootNode.message).toBe('Expected metric identifier instead found end of string'); + expect(rootNode.pos).toBe(19); + }); + + it('invalid function expression missing closing parenthesis', function() { + var parser = new Parser('sum(test'); + var rootNode = parser.getAst(); + + expect(rootNode.message).toBe('Expected closing parenthesis instead found end of string'); + expect(rootNode.pos).toBe(9); + }); + + it('unclosed string in function', function() { + var parser = new Parser("sum('test)"); + var rootNode = parser.getAst(); + + expect(rootNode.message).toBe('Unclosed string parameter'); + expect(rootNode.pos).toBe(11); + }); + + it('handle issue #69', function() { + var parser = new Parser('cactiStyle(offset(scale(net.192-168-1-1.192-168-1-9.ping_value.*,0.001),-100))'); + var rootNode = parser.getAst(); + expect(rootNode.type).toBe('function'); + }); + + it('handle float function arguments', function() { + var parser = new Parser('scale(test, 0.002)'); + var rootNode = parser.getAst(); + expect(rootNode.type).toBe('function'); + expect(rootNode.params[1].type).toBe('number'); + expect(rootNode.params[1].value).toBe(0.002); + }); + + it('handle curly brace pattern at start', function() { + var parser = new Parser('{apps}.test'); + var rootNode = parser.getAst(); + expect(rootNode.type).toBe('metric'); + expect(rootNode.segments[0].value).toBe('{apps}'); + expect(rootNode.segments[1].value).toBe('test'); + }); + + it('series parameters', function() { + var parser = new Parser('asPercent(#A, #B)'); + var rootNode = parser.getAst(); + expect(rootNode.type).toBe('function'); + expect(rootNode.params[0].type).toBe('series-ref'); + expect(rootNode.params[0].value).toBe('#A'); + expect(rootNode.params[1].value).toBe('#B'); + }); + + it('series parameters, issue 2788', function() { + var parser = new Parser("summarize(diffSeries(#A, #B), '10m', 'sum', false)"); + var rootNode = parser.getAst(); + expect(rootNode.type).toBe('function'); + expect(rootNode.params[0].type).toBe('function'); + expect(rootNode.params[1].value).toBe('10m'); + expect(rootNode.params[3].type).toBe('bool'); + }); + + it('should parse metric expression with ip number segments', function() { + var parser = new Parser('5.10.123.5'); + var rootNode = parser.getAst(); + expect(rootNode.segments[0].value).toBe('5'); + expect(rootNode.segments[1].value).toBe('10'); + expect(rootNode.segments[2].value).toBe('123'); + expect(rootNode.segments[3].value).toBe('5'); + }); +}); diff --git a/public/app/plugins/datasource/graphite/specs/parser_specs.ts b/public/app/plugins/datasource/graphite/specs/parser_specs.ts deleted file mode 100644 index a69dcb56dfb..00000000000 --- a/public/app/plugins/datasource/graphite/specs/parser_specs.ts +++ /dev/null @@ -1,183 +0,0 @@ -import {describe, it, expect} from 'test/lib/common'; -import {Parser} from '../parser'; - -describe('when parsing', function() { - - it('simple metric expression', function() { - var parser = new Parser('metric.test.*.asd.count'); - var rootNode = parser.getAst(); - - expect(rootNode.type).to.be('metric'); - expect(rootNode.segments.length).to.be(5); - expect(rootNode.segments[0].value).to.be('metric'); - }); - - it('simple metric expression with numbers in segments', function() { - var parser = new Parser('metric.10.15_20.5'); - var rootNode = parser.getAst(); - - expect(rootNode.type).to.be('metric'); - expect(rootNode.segments.length).to.be(4); - expect(rootNode.segments[1].value).to.be('10'); - expect(rootNode.segments[2].value).to.be('15_20'); - expect(rootNode.segments[3].value).to.be('5'); - }); - - it('simple metric expression with curly braces', function() { - var parser = new Parser('metric.se1-{count, max}'); - var rootNode = parser.getAst(); - - expect(rootNode.type).to.be('metric'); - expect(rootNode.segments.length).to.be(2); - expect(rootNode.segments[1].value).to.be('se1-{count,max}'); - }); - - it('simple metric expression with curly braces at start of segment and with post chars', function() { - var parser = new Parser('metric.{count, max}-something.count'); - var rootNode = parser.getAst(); - - expect(rootNode.type).to.be('metric'); - expect(rootNode.segments.length).to.be(3); - expect(rootNode.segments[1].value).to.be('{count,max}-something'); - }); - - it('simple function', function() { - var parser = new Parser('sum(test)'); - var rootNode = parser.getAst(); - expect(rootNode.type).to.be('function'); - expect(rootNode.params.length).to.be(1); - }); - - it('simple function2', function() { - var parser = new Parser('offset(test.metric, -100)'); - var rootNode = parser.getAst(); - expect(rootNode.type).to.be('function'); - expect(rootNode.params[0].type).to.be('metric'); - expect(rootNode.params[1].type).to.be('number'); - }); - - it('simple function with string arg', function() { - var parser = new Parser("randomWalk('test')"); - var rootNode = parser.getAst(); - expect(rootNode.type).to.be('function'); - expect(rootNode.params.length).to.be(1); - expect(rootNode.params[0].type).to.be('string'); - }); - - it('function with multiple args', function() { - var parser = new Parser("sum(test, 1, 'test')"); - var rootNode = parser.getAst(); - - expect(rootNode.type).to.be('function'); - expect(rootNode.params.length).to.be(3); - expect(rootNode.params[0].type).to.be('metric'); - expect(rootNode.params[1].type).to.be('number'); - expect(rootNode.params[2].type).to.be('string'); - }); - - it('function with nested function', function() { - var parser = new Parser("sum(scaleToSeconds(test, 1))"); - var rootNode = parser.getAst(); - - expect(rootNode.type).to.be('function'); - expect(rootNode.params.length).to.be(1); - expect(rootNode.params[0].type).to.be('function'); - expect(rootNode.params[0].name).to.be('scaleToSeconds'); - expect(rootNode.params[0].params.length).to.be(2); - expect(rootNode.params[0].params[0].type).to.be('metric'); - expect(rootNode.params[0].params[1].type).to.be('number'); - }); - - it('function with multiple series', function() { - var parser = new Parser("sum(test.test.*.count, test.timers.*.count)"); - var rootNode = parser.getAst(); - - expect(rootNode.type).to.be('function'); - expect(rootNode.params.length).to.be(2); - expect(rootNode.params[0].type).to.be('metric'); - expect(rootNode.params[1].type).to.be('metric'); - }); - - it('function with templated series', function() { - var parser = new Parser("sum(test.[[server]].count)"); - var rootNode = parser.getAst(); - - expect(rootNode.message).to.be(undefined); - expect(rootNode.params[0].type).to.be('metric'); - expect(rootNode.params[0].segments[1].type).to.be('segment'); - expect(rootNode.params[0].segments[1].value).to.be('[[server]]'); - }); - - it('invalid metric expression', function() { - var parser = new Parser('metric.test.*.asd.'); - var rootNode = parser.getAst(); - - expect(rootNode.message).to.be('Expected metric identifier instead found end of string'); - expect(rootNode.pos).to.be(19); - }); - - it('invalid function expression missing closing parenthesis', function() { - var parser = new Parser('sum(test'); - var rootNode = parser.getAst(); - - expect(rootNode.message).to.be('Expected closing parenthesis instead found end of string'); - expect(rootNode.pos).to.be(9); - }); - - it('unclosed string in function', function() { - var parser = new Parser("sum('test)"); - var rootNode = parser.getAst(); - - expect(rootNode.message).to.be('Unclosed string parameter'); - expect(rootNode.pos).to.be(11); - }); - - it('handle issue #69', function() { - var parser = new Parser('cactiStyle(offset(scale(net.192-168-1-1.192-168-1-9.ping_value.*,0.001),-100))'); - var rootNode = parser.getAst(); - expect(rootNode.type).to.be('function'); - }); - - it('handle float function arguments', function() { - var parser = new Parser('scale(test, 0.002)'); - var rootNode = parser.getAst(); - expect(rootNode.type).to.be('function'); - expect(rootNode.params[1].type).to.be('number'); - expect(rootNode.params[1].value).to.be(0.002); - }); - - it('handle curly brace pattern at start', function() { - var parser = new Parser('{apps}.test'); - var rootNode = parser.getAst(); - expect(rootNode.type).to.be('metric'); - expect(rootNode.segments[0].value).to.be('{apps}'); - expect(rootNode.segments[1].value).to.be('test'); - }); - - it('series parameters', function() { - var parser = new Parser('asPercent(#A, #B)'); - var rootNode = parser.getAst(); - expect(rootNode.type).to.be('function'); - expect(rootNode.params[0].type).to.be('series-ref'); - expect(rootNode.params[0].value).to.be('#A'); - expect(rootNode.params[1].value).to.be('#B'); - }); - - it('series parameters, issue 2788', function() { - var parser = new Parser("summarize(diffSeries(#A, #B), '10m', 'sum', false)"); - var rootNode = parser.getAst(); - expect(rootNode.type).to.be('function'); - expect(rootNode.params[0].type).to.be('function'); - expect(rootNode.params[1].value).to.be('10m'); - expect(rootNode.params[3].type).to.be('bool'); - }); - - it('should parse metric expression with ip number segments', function() { - var parser = new Parser('5.10.123.5'); - var rootNode = parser.getAst(); - expect(rootNode.segments[0].value).to.be('5'); - expect(rootNode.segments[1].value).to.be('10'); - expect(rootNode.segments[2].value).to.be('123'); - expect(rootNode.segments[3].value).to.be('5'); - }); -}); diff --git a/public/app/plugins/datasource/postgres/datasource.ts b/public/app/plugins/datasource/postgres/datasource.ts index 54e3bdc41cd..d75aecbc281 100644 --- a/public/app/plugins/datasource/postgres/datasource.ts +++ b/public/app/plugins/datasource/postgres/datasource.ts @@ -20,6 +20,10 @@ export class PostgresDatasource { return '\'' + value + '\''; } + if (typeof value === 'number') { + return value.toString(); + } + var quotedValues = _.map(value, function(val) { return '\'' + val + '\''; }); diff --git a/public/app/plugins/datasource/postgres/module.ts b/public/app/plugins/datasource/postgres/module.ts index 09a3f43c9ea..ccb03f069ab 100644 --- a/public/app/plugins/datasource/postgres/module.ts +++ b/public/app/plugins/datasource/postgres/module.ts @@ -16,8 +16,8 @@ class PostgresConfigCtrl { const defaultQuery = `SELECT extract(epoch from time_column) AS time, - title_column as title, - description_column as text + text_column as text, + tags_column as tags FROM metric_table WHERE diff --git a/public/app/plugins/datasource/prometheus/completer.ts b/public/app/plugins/datasource/prometheus/completer.ts index 449a5f34b49..dc2def62a91 100644 --- a/public/app/plugins/datasource/prometheus/completer.ts +++ b/public/app/plugins/datasource/prometheus/completer.ts @@ -8,7 +8,7 @@ export class PromCompleter { labelNameCache: any; labelValueCache: any; - identifierRegexps = [/[\[\]a-zA-Z_0-9=]/]; + identifierRegexps = [/\[/, /[a-zA-Z0-9_:]/]; constructor(private datasource: PrometheusDatasource) { this.labelQueryCache = {}; @@ -73,13 +73,15 @@ export class PromCompleter { }); } - if (prefix === '[') { + if (token.type === 'paren.lparen' && token.value === '[') { var vectors = []; for (let unit of ['s', 'm', 'h']) { for (let value of [1,5,10,30]) { vectors.push({caption: value+unit, value: '['+value+unit, meta: 'range vector'}); } } + vectors.push({caption: '$__interval', value: '[$__interval', meta: 'range vector'}); + vectors.push({caption: '$__interval_ms', value: '[$__interval_ms', meta: 'range vector'}); callback(null, vectors); return; } diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index e7b7cf36aa0..98776f37962 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -168,7 +168,7 @@ export class PrometheusDatasource { if (interval !== 0 && range / intervalFactor / interval > 11000) { interval = Math.ceil(range / intervalFactor / 11000); } - return Math.max(interval * intervalFactor, minInterval); + return Math.max(interval * intervalFactor, minInterval, 1); } performTimeSeriesQuery(query, start, end) { diff --git a/public/app/plugins/datasource/prometheus/mode-prometheus.js b/public/app/plugins/datasource/prometheus/mode-prometheus.js index 165c1a364c4..60f9a0501ac 100644 --- a/public/app/plugins/datasource/prometheus/mode-prometheus.js +++ b/public/app/plugins/datasource/prometheus/mode-prometheus.js @@ -43,10 +43,10 @@ var PrometheusHighlightRules = function() { regex : "\\d+[smhdwy]" }, { token : keywordMapper, - regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b" + regex : "[a-zA-Z_:][a-zA-Z0-9_:]*" }, { token : "keyword.operator", - regex : "\\+|\\-|\\*|\\/|%|\\^|=|==|!=|<=|>=|<|>|=\\~|!\\~" + regex : "\\+|\\-|\\*|\\/|%|\\^|==|!=|<=|>=|<|>" }, { token : "paren.lparen", regex : "[[(]" @@ -75,8 +75,7 @@ var PrometheusHighlightRules = function() { regex : '"[^"]*"|\'[^\']*\'' }, { token : "punctuation.operator", - regex : ",", - push : 'start-label-matcher' + regex : "," }, { token : "paren.rparen", regex : "}", @@ -112,11 +111,6 @@ var keyWordsCompletions = prometheusKeyWords.map(function(word) { }); var prometheusFunctions = [ - { - name: 'abs()', value: 'abs', - def: 'abs(v instant-vector)', - docText: 'Returns the input vector with all sample values converted to their absolute value.' - }, { name: 'abs()', value: 'abs', def: 'abs(v instant-vector)', diff --git a/public/app/plugins/datasource/prometheus/specs/completer_specs.ts b/public/app/plugins/datasource/prometheus/specs/completer_specs.ts index b8b5ef023f0..30dbae291f6 100644 --- a/public/app/plugins/datasource/prometheus/specs/completer_specs.ts +++ b/public/app/plugins/datasource/prometheus/specs/completer_specs.ts @@ -44,12 +44,18 @@ describe('Prometheus editor completer', function() { describe('When inside brackets', () => { it('Should return range vectors', () => { const session = getSessionStub({ - currentToken: {}, - tokens: [], - line: '', + currentToken: {type: 'paren.lparen', value: '[', index: 2, start: 9}, + tokens: [ + {type: 'identifier', value: 'node_cpu'}, + {type: 'paren.lparen', value: '['} + ], + line: 'node_cpu[', }); - completer.getCompletions(editor, session, {row: 0, column: 10}, '[', (s, res) => { - expect(res[0]).to.eql({caption: '1s', value: '[1s', meta: 'range vector'}); + + return completer.getCompletions(editor, session, {row: 0, column: 10}, '[', (s, res) => { + expect(res[0].caption).to.eql('1s'); + expect(res[0].value).to.eql('[1s'); + expect(res[0].meta).to.eql('range vector'); }); }); }); diff --git a/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts index 702ad8b5990..9d528c735de 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts @@ -294,6 +294,20 @@ describe('PrometheusDatasource', function() { ctx.ds.query(query); ctx.$httpBackend.verifyNoOutstandingExpectation(); }); + + it('step should never go below 1', function() { + var query = { + // 6 hour range + range: { from: moment(1508318768202), to: moment(1508318770118) }, + targets: [{expr: 'test'}], + interval: '100ms' + }; + var urlExpected = 'proxied/api/v1/query_range?query=test&start=1508318769&end=1508318771&step=1'; + ctx.$httpBackend.expect('GET', urlExpected).respond(response); + ctx.ds.query(query); + ctx.$httpBackend.verifyNoOutstandingExpectation(); + }); + it('should be auto interval when greater than min interval', function() { var query = { // 6 hour range diff --git a/public/app/plugins/panel/gettingstarted/module.ts b/public/app/plugins/panel/gettingstarted/module.ts index 1d8f1fbda92..e32aed00c0f 100644 --- a/public/app/plugins/panel/gettingstarted/module.ts +++ b/public/app/plugins/panel/gettingstarted/module.ts @@ -34,7 +34,7 @@ class GettingStartedPanelCtrl extends PanelCtrl { check: () => { return $q.when( datasourceSrv.getMetricSources().filter(item => { - return item.meta.builtIn === false; + return item.meta.builtIn !== true; }).length > 0 ); } diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index f378e52a111..8ad6e999833 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -343,7 +343,7 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) { eventManager.addFlotEvents(annotations, options); configureAxisOptions(data, options); - sortedSeries = _.sortBy(data, function(series) { return series.zindex; }); + sortedSeries = sortSeries(data, ctrl.panel); function callPlot(incrementRenderCounter) { try { @@ -374,6 +374,41 @@ function graphDirective($rootScope, timeSrv, popoverSrv, contextSrv) { } } + function sortSeries(series, panel) { + var sortBy = panel.legend.sort; + var sortOrder = panel.legend.sortDesc; + var haveSortBy = sortBy !== null || sortBy !== undefined; + var haveSortOrder = sortOrder !== null || sortOrder !== undefined; + + if (panel.stack && haveSortBy && haveSortOrder) { + var desc = desc = panel.legend.sortDesc === true ? 1 : -1; + series.sort((x, y) => { + if (x.stats[sortBy] > y.stats[sortBy]) { + return 1 * desc; + } + if (x.stats[sortBy] < y.stats[sortBy]) { + return -1 * desc; + } + + return 0; + }); + } + + series.sort((x, y) => { + if (x.zindex > y.zindex) { + return 1; + } + + if (x.zindex < y.zindex) { + return -1; + } + + return 0; + }); + + return series; + } + function translateFillOption(fill) { if (panel.percentage && panel.stack) { return fill === 0 ? 0.001 : fill/10; diff --git a/public/app/plugins/panel/graph/legend.js b/public/app/plugins/panel/graph/legend.js index b8c2970b8cd..1a29743b09d 100644 --- a/public/app/plugins/panel/graph/legend.js +++ b/public/app/plugins/panel/graph/legend.js @@ -80,13 +80,13 @@ function (angular, _, $) { if (panel.legend.sortDesc === false) { panel.legend.sort = null; panel.legend.sortDesc = null; - render(); + ctrl.render(); return; } panel.legend.sortDesc = !panel.legend.sortDesc; panel.legend.sort = stat; - render(); + ctrl.render(); } function getTableHeaderHtml(statName) { diff --git a/public/app/plugins/panel/graph/specs/graph_specs.ts b/public/app/plugins/panel/graph/specs/graph_specs.ts index 11560e71c47..184b6715428 100644 --- a/public/app/plugins/panel/graph/specs/graph_specs.ts +++ b/public/app/plugins/panel/graph/specs/graph_specs.ts @@ -15,16 +15,16 @@ describe('grafanaGraph', function() { beforeEach(angularMocks.module('grafana.core')); function graphScenario(desc, func, elementWidth = 500) { - describe(desc, function() { + describe(desc, () => { var ctx: any = {}; - ctx.setup = function(setupFunc) { + ctx.setup = (setupFunc) => { - beforeEach(angularMocks.module(function($provide) { + beforeEach(angularMocks.module(($provide) => { $provide.value("timeSrv", new helpers.TimeSrvStub()); })); - beforeEach(angularMocks.inject(function($rootScope, $compile) { + beforeEach(angularMocks.inject(($rootScope, $compile) => { var ctrl: any = { height: 200, panel: { @@ -75,7 +75,7 @@ describe('grafanaGraph', function() { alias: 'series1' })); ctx.data.push(new TimeSeries({ - datapoints: [[1,1],[2,2]], + datapoints: [[1,10],[2,20]], alias: 'series2' })); @@ -96,15 +96,15 @@ describe('grafanaGraph', function() { }); } - graphScenario('simple lines options', function(ctx) { - ctx.setup(function(ctrl) { + graphScenario('simple lines options', (ctx) => { + ctx.setup((ctrl) => { ctrl.panel.lines = true; ctrl.panel.fill = 5; ctrl.panel.linewidth = 3; ctrl.panel.steppedLine = true; }); - it('should configure plot with correct options', function() { + it('should configure plot with correct options', () => { expect(ctx.plotOptions.series.lines.show).to.be(true); expect(ctx.plotOptions.series.lines.fill).to.be(0.5); expect(ctx.plotOptions.series.lines.lineWidth).to.be(3); @@ -112,6 +112,55 @@ describe('grafanaGraph', function() { }); }); + graphScenario('sort series as legend', (ctx) => { + describe("with sort as legend undefined", () => { + ctx.setup((ctrl) => { + ctrl.panel.legend.sort = undefined; + }); + + it("should not modify order of time series", () => { + expect(ctx.plotData[0].alias).to.be('series1'); + expect(ctx.plotData[1].alias).to.be('series2'); + }); + }); + + describe("with sort as legend set to min. descending order", () => { + ctx.setup((ctrl) => { + ctrl.panel.legend.sort = 'min'; + ctrl.panel.legend.sortDesc = true; + }); + + it("highest value should be first", () => { + expect(ctx.plotData[1].alias).to.be('series2'); + expect(ctx.plotData[0].alias).to.be('series1'); + }); + }); + + describe("with sort as legend set to min. ascending order", () => { + ctx.setup((ctrl) => { + ctrl.panel.legend.sort = 'min'; + ctrl.panel.legend.sortDesc = true; + }); + + it("lowest value should be first", () => { + expect(ctx.plotData[0].alias).to.be('series1'); + expect(ctx.plotData[1].alias).to.be('series2'); + }); + }); + + describe("with sort as legend set to current. ascending order", () => { + ctx.setup((ctrl) => { + ctrl.panel.legend.sort = 'current'; + ctrl.panel.legend.sortDesc = false; + }); + + it("highest last value should be first", () => { + expect(ctx.plotData[1].alias).to.be('series2'); + expect(ctx.plotData[0].alias).to.be('series1'); + }); + }); + }); + graphScenario('when logBase is log 10', function(ctx) { ctx.setup(function(ctrl, data) { ctrl.panel.yaxes[0].logBase = 10; @@ -251,7 +300,7 @@ describe('grafanaGraph', function() { }); it('should set barWidth', function() { - expect(ctx.plotOptions.series.bars.barWidth).to.be(1/1.5); + expect(ctx.plotOptions.series.bars.barWidth).to.be(10/1.5); }); }); diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index edf012a0209..d432aa97576 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -152,7 +152,7 @@ function drawLegendValues(elem, colorScale, rangeFrom, rangeTo, maxValue, minVal .tickSize(2); let colorRect = legendElem.find(":first-child"); - let posY = colorRect.height() + 2; + let posY = getSvgElemHeight(legendElem) + 2; let posX = getSvgElemX(colorRect); d3.select(legendElem.get(0)).append("g") @@ -256,7 +256,16 @@ function getOpacityScale(options, maxValue, minValue = 0) { function getSvgElemX(elem) { let svgElem = elem.get(0); if (svgElem && svgElem.x && svgElem.x.baseVal) { - return elem.get(0).x.baseVal.value; + return svgElem.x.baseVal.value; + } else { + return 0; + } +} + +function getSvgElemHeight(elem) { + let svgElem = elem.get(0); + if (svgElem && svgElem.height && svgElem.height.baseVal) { + return svgElem.height.baseVal.value; } else { return 0; } diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 10a5e1b3d4e..19c93e0fb8a 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -1,13 +1,11 @@ -/// - import {MetricsPanelCtrl} from 'app/plugins/sdk'; import _ from 'lodash'; import kbn from 'app/core/utils/kbn'; -import TimeSeries from 'app/core/time_series'; +import TimeSeries from 'app/core/time_series2'; import {axesEditor} from './axes_editor'; import {heatmapDisplayEditor} from './display_editor'; import rendering from './rendering'; -import {convertToHeatMap, convertToCards, elasticHistogramToHeatmap, calculateBucketSize, getMinLog} from './heatmap_data_converter'; +import {convertToHeatMap, convertToCards, elasticHistogramToHeatmap, calculateBucketSize } from './heatmap_data_converter'; let X_BUCKET_NUMBER_DEFAULT = 30; let Y_BUCKET_NUMBER_DEFAULT = 10; @@ -250,7 +248,6 @@ export class HeatmapCtrl extends MetricsPanelCtrl { }); series.flotpairs = series.getFlotPairs(this.panel.nullPointMode); - series.minLog = getMinLog(series); let datapoints = seriesData.datapoints || []; if (datapoints && datapoints.length > 0) { @@ -266,7 +263,7 @@ export class HeatmapCtrl extends MetricsPanelCtrl { parseSeries(series) { let min = _.min(_.map(series, s => s.stats.min)); - let minLog = _.min(_.map(series, s => s.minLog)); + let minLog = _.min(_.map(series, s => s.stats.logmin)); let max = _.max(_.map(series, s => s.stats.max)); return { diff --git a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts index 7115dc0e218..31360aa552e 100644 --- a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts +++ b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts @@ -1,5 +1,3 @@ -/// - import _ from 'lodash'; let VALUE_INDEX = 0; @@ -320,12 +318,6 @@ function convertToLogScaleValueBuckets(xBucket, yBucketSplitFactor, logBase) { return buckets; } -// Get minimum non zero value. -function getMinLog(series) { - let values = _.compact(_.map(series.datapoints, p => p[0])); - return _.min(values); -} - /** * Logarithm for custom base * @param value @@ -432,7 +424,6 @@ export { elasticHistogramToHeatmap, convertToCards, mergeZeroBuckets, - getMinLog, getValueBucketBound, isHeatmapDataEqual, calculateBucketSize diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index 96b93be2741..50e852d5653 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -71,9 +71,8 @@ export default function link(scope, elem, attrs, ctrl) { function getYAxisWidth(elem) { let axis_text = elem.selectAll(".axis-y text").nodes(); let max_text_width = _.max(_.map(axis_text, text => { - let el = $(text); - // Use JQuery outerWidth() to compute full element width - return el.outerWidth(); + // Use SVG getBBox method + return text.getBBox().width; })); return max_text_width; diff --git a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter.jest.ts similarity index 85% rename from public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts rename to public/app/plugins/panel/heatmap/specs/heatmap_data_converter.jest.ts index e9be85b2784..fcebbd1c256 100644 --- a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts +++ b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter.jest.ts @@ -1,5 +1,3 @@ -/// - import _ from 'lodash'; import { describe, beforeEach, it, expect } from '../../../../../test/lib/common'; import TimeSeries from 'app/core/time_series2'; @@ -45,23 +43,23 @@ describe('isHeatmapDataEqual', () => { let emptyValues = _.cloneDeep(ctx.heatmapA); emptyValues['1422774000000'].buckets['1'].values = []; - expect(isHeatmapDataEqual(ctx.heatmapA, ctx.heatmapB)).to.be(true); - expect(isHeatmapDataEqual(ctx.heatmapB, ctx.heatmapA)).to.be(true); + expect(isHeatmapDataEqual(ctx.heatmapA, ctx.heatmapB)).toBe(true); + expect(isHeatmapDataEqual(ctx.heatmapB, ctx.heatmapA)).toBe(true); - expect(isHeatmapDataEqual(ctx.heatmapA, heatmapC)).to.be(true); - expect(isHeatmapDataEqual(heatmapC, ctx.heatmapA)).to.be(true); + expect(isHeatmapDataEqual(ctx.heatmapA, heatmapC)).toBe(true); + expect(isHeatmapDataEqual(heatmapC, ctx.heatmapA)).toBe(true); - expect(isHeatmapDataEqual(ctx.heatmapA, heatmapD)).to.be(false); - expect(isHeatmapDataEqual(heatmapD, ctx.heatmapA)).to.be(false); + expect(isHeatmapDataEqual(ctx.heatmapA, heatmapD)).toBe(false); + expect(isHeatmapDataEqual(heatmapD, ctx.heatmapA)).toBe(false); - expect(isHeatmapDataEqual(ctx.heatmapA, heatmapE)).to.be(false); - expect(isHeatmapDataEqual(heatmapE, ctx.heatmapA)).to.be(false); + expect(isHeatmapDataEqual(ctx.heatmapA, heatmapE)).toBe(false); + expect(isHeatmapDataEqual(heatmapE, ctx.heatmapA)).toBe(false); - expect(isHeatmapDataEqual(empty, ctx.heatmapA)).to.be(false); - expect(isHeatmapDataEqual(ctx.heatmapA, empty)).to.be(false); + expect(isHeatmapDataEqual(empty, ctx.heatmapA)).toBe(false); + expect(isHeatmapDataEqual(ctx.heatmapA, empty)).toBe(false); - expect(isHeatmapDataEqual(emptyValues, ctx.heatmapA)).to.be(false); - expect(isHeatmapDataEqual(ctx.heatmapA, emptyValues)).to.be(false); + expect(isHeatmapDataEqual(emptyValues, ctx.heatmapA)).toBe(false); + expect(isHeatmapDataEqual(ctx.heatmapA, emptyValues)).toBe(false); }); }); @@ -87,7 +85,7 @@ describe('calculateBucketSize', () => { it('should properly calculate bucket size', () => { _.each(ctx.bounds_set, (b) => { let bucketSize = calculateBucketSize(b.bounds, ctx.logBase); - expect(bucketSize).to.be(b.size); + expect(bucketSize).toBe(b.size); }); }); }); @@ -108,7 +106,7 @@ describe('calculateBucketSize', () => { it('should properly calculate bucket size', () => { _.each(ctx.bounds_set, (b) => { let bucketSize = calculateBucketSize(b.bounds, ctx.logBase); - expect(isEqual(bucketSize, b.size)).to.be(true); + expect(isEqual(bucketSize, b.size)).toBe(true); }); }); }); @@ -162,7 +160,7 @@ describe('HeatmapDataConverter', () => { }; let heatmap = convertToHeatMap(ctx.series, ctx.yBucketSize, ctx.xBucketSize, ctx.logBase); - expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).to.be(true); + expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).toBe(true); }); }); @@ -190,7 +188,7 @@ describe('HeatmapDataConverter', () => { }; let heatmap = convertToHeatMap(ctx.series, ctx.yBucketSize, ctx.xBucketSize, ctx.logBase); - expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).to.be(true); + expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).toBe(true); }); }); }); @@ -240,7 +238,7 @@ describe('ES Histogram converter', () => { }; let heatmap = elasticHistogramToHeatmap(ctx.series); - expect(heatmap).to.eql(expectedHeatmap); + expect(heatmap).toMatchObject(expectedHeatmap); }); }); }); @@ -273,13 +271,13 @@ describe('convertToCards', () => { {x: 1422774060000, y: 2, count: 2, values: [2, 3], yBounds: {}} ]; let res = convertToCards(buckets); - expect(res.cards).to.eql(expectedCards); + expect(res.cards).toMatchObject(expectedCards); }); it('should build proper cards stats', () => { let expectedStats = {min: 1, max: 2}; let res = convertToCards(buckets); - expect(res.cardStats).to.eql(expectedStats); + expect(res.cardStats).toMatchObject(expectedStats); }); }); diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts index cab3f698ae6..b2c1907058e 100644 --- a/public/app/plugins/panel/table/renderer.ts +++ b/public/app/plugins/panel/table/renderer.ts @@ -1,5 +1,3 @@ -/// - import _ from 'lodash'; import moment from 'moment'; import kbn from 'app/core/utils/kbn'; diff --git a/public/app/plugins/panel/table/specs/renderer_specs.ts b/public/app/plugins/panel/table/specs/renderer.jest.ts similarity index 81% rename from public/app/plugins/panel/table/specs/renderer_specs.ts rename to public/app/plugins/panel/table/specs/renderer.jest.ts index 0726755dfe0..61ecbeca30f 100644 --- a/public/app/plugins/panel/table/specs/renderer_specs.ts +++ b/public/app/plugins/panel/table/specs/renderer.jest.ts @@ -1,5 +1,3 @@ -import {describe, it, expect} from 'test/lib/common'; - import _ from 'lodash'; import TableModel from 'app/core/table_model'; import {TableRenderer} from '../renderer'; @@ -92,84 +90,84 @@ describe('when rendering table', () => { it('time column should be formated', () => { var html = renderer.renderCell(0, 0, 1388556366666); - expect(html).to.be('2014-01-01T06:06:06Z'); + expect(html).toBe('2014-01-01T06:06:06Z'); }); it('undefined time column should be rendered as -', () => { var html = renderer.renderCell(0, 0, undefined); - expect(html).to.be('-'); + expect(html).toBe('-'); }); it('null time column should be rendered as -', () => { var html = renderer.renderCell(0, 0, null); - expect(html).to.be('-'); + expect(html).toBe('-'); }); it('number column with unit specified should ignore style unit', () => { var html = renderer.renderCell(5, 0, 1230); - expect(html).to.be('1.23 kbps'); + expect(html).toBe('1.23 kbps'); }); it('number column should be formated', () => { var html = renderer.renderCell(1, 0, 1230); - expect(html).to.be('1.230 s'); + expect(html).toBe('1.230 s'); }); it('number style should ignore string values', () => { var html = renderer.renderCell(1, 0, 'asd'); - expect(html).to.be('asd'); + expect(html).toBe('asd'); }); it('colored cell should have style', () => { var html = renderer.renderCell(2, 0, 40); - expect(html).to.be('40.0'); + expect(html).toBe('40.0'); }); it('colored cell should have style', () => { var html = renderer.renderCell(2, 0, 55); - expect(html).to.be('55.0'); + expect(html).toBe('55.0'); }); it('colored cell should have style', () => { var html = renderer.renderCell(2, 0, 85); - expect(html).to.be('85.0'); + expect(html).toBe('85.0'); }); it('unformated undefined should be rendered as string', () => { var html = renderer.renderCell(3, 0, 'value'); - expect(html).to.be('value'); + expect(html).toBe('value'); }); it('string style with escape html should return escaped html', () => { var html = renderer.renderCell(4, 0, "&breaking
the
row"); - expect(html).to.be('&breaking <br /> the <br /> row'); + expect(html).toBe('&breaking <br /> the <br /> row'); }); it('undefined formater should return escaped html', () => { var html = renderer.renderCell(3, 0, "&breaking
the
row"); - expect(html).to.be('&breaking <br /> the <br /> row'); + expect(html).toBe('&breaking <br /> the <br /> row'); }); it('undefined value should render as -', () => { var html = renderer.renderCell(3, 0, undefined); - expect(html).to.be(''); + expect(html).toBe(''); }); it('sanitized value should render as', () => { var html = renderer.renderCell(6, 0, 'text link'); - expect(html).to.be('sanitized'); + expect(html).toBe('sanitized'); }); it('Time column title should be Timestamp', () => { - expect(table.columns[0].title).to.be('Timestamp'); + expect(table.columns[0].title).toBe('Timestamp'); }); it('Value column title should be Val', () => { - expect(table.columns[1].title).to.be('Val'); + expect(table.columns[1].title).toBe('Val'); }); it('Colored column title should be Colored', () => { - expect(table.columns[2].title).to.be('Colored'); + expect(table.columns[2].title).toBe('Colored'); }); it('link should render as', () => { @@ -182,7 +180,7 @@ describe('when rendering table', () => { `; - expect(normalize(html)).to.be(normalize(expectedHtml)); + expect(normalize(html)).toBe(normalize(expectedHtml)); }); }); }); diff --git a/public/app/plugins/panel/table/specs/transformers_specs.ts b/public/app/plugins/panel/table/specs/transformers.jest.ts similarity index 58% rename from public/app/plugins/panel/table/specs/transformers_specs.ts rename to public/app/plugins/panel/table/specs/transformers.jest.ts index 38b8355fa9e..68af0ca7319 100644 --- a/public/app/plugins/panel/table/specs/transformers_specs.ts +++ b/public/app/plugins/panel/table/specs/transformers.jest.ts @@ -1,5 +1,3 @@ -import {describe, beforeEach, it, expect} from 'test/lib/common'; - import {transformers, transformDataToTable} from '../transformers'; describe('when transforming time series table', () => { @@ -29,18 +27,18 @@ describe('when transforming time series table', () => { }); it('should return 3 rows', () => { - expect(table.rows.length).to.be(3); - expect(table.rows[0][1]).to.be('series1'); - expect(table.rows[1][1]).to.be('series1'); - expect(table.rows[2][1]).to.be('series2'); - expect(table.rows[0][2]).to.be(12.12); + expect(table.rows.length).toBe(3); + expect(table.rows[0][1]).toBe('series1'); + expect(table.rows[1][1]).toBe('series1'); + expect(table.rows[2][1]).toBe('series2'); + expect(table.rows[0][2]).toBe(12.12); }); it('should return 3 rows', () => { - expect(table.columns.length).to.be(3); - expect(table.columns[0].text).to.be('Time'); - expect(table.columns[1].text).to.be('Metric'); - expect(table.columns[2].text).to.be('Value'); + expect(table.columns.length).toBe(3); + expect(table.columns[0].text).toBe('Time'); + expect(table.columns[1].text).toBe('Metric'); + expect(table.columns[2].text).toBe('Value'); }); }); @@ -54,20 +52,20 @@ describe('when transforming time series table', () => { }); it ('should return 3 columns', () => { - expect(table.columns.length).to.be(3); - expect(table.columns[0].text).to.be('Time'); - expect(table.columns[1].text).to.be('series1'); - expect(table.columns[2].text).to.be('series2'); + expect(table.columns.length).toBe(3); + expect(table.columns[0].text).toBe('Time'); + expect(table.columns[1].text).toBe('series1'); + expect(table.columns[2].text).toBe('series2'); }); it ('should return 2 rows', () => { - expect(table.rows.length).to.be(2); - expect(table.rows[0][1]).to.be(12.12); - expect(table.rows[0][2]).to.be(16.12); + expect(table.rows.length).toBe(2); + expect(table.rows[0][1]).toBe(12.12); + expect(table.rows[0][2]).toBe(16.12); }); it ('should be undefined when no value for timestamp', () => { - expect(table.rows[1][2]).to.be(undefined); + expect(table.rows[1][2]).toBe(undefined); }); }); @@ -83,17 +81,17 @@ describe('when transforming time series table', () => { }); it('should return 2 rows', () => { - expect(table.rows.length).to.be(2); - expect(table.rows[0][0]).to.be('series1'); - expect(table.rows[0][1]).to.be(14.44); - expect(table.rows[0][2]).to.be(12.12); + expect(table.rows.length).toBe(2); + expect(table.rows[0][0]).toBe('series1'); + expect(table.rows[0][1]).toBe(14.44); + expect(table.rows[0][2]).toBe(12.12); }); it('should return 2 columns', () => { - expect(table.columns.length).to.be(3); - expect(table.columns[0].text).to.be('Metric'); - expect(table.columns[1].text).to.be('Max'); - expect(table.columns[2].text).to.be('Min'); + expect(table.columns.length).toBe(3); + expect(table.columns[0].text).toBe('Metric'); + expect(table.columns[1].text).toBe('Max'); + expect(table.columns[2].text).toBe('Min'); }); }); @@ -124,9 +122,9 @@ describe('when transforming time series table', () => { describe('getColumns', function() { it('should return nested properties', function() { var columns = transformers['json'].getColumns(rawData); - expect(columns[0].text).to.be('timestamp'); - expect(columns[1].text).to.be('message'); - expect(columns[2].text).to.be('nested.level2'); + expect(columns[0].text).toBe('timestamp'); + expect(columns[1].text).toBe('message'); + expect(columns[2].text).toBe('nested.level2'); }); }); @@ -136,17 +134,17 @@ describe('when transforming time series table', () => { }); it ('should return 2 columns', () => { - expect(table.columns.length).to.be(3); - expect(table.columns[0].text).to.be('Timestamp'); - expect(table.columns[1].text).to.be('Message'); - expect(table.columns[2].text).to.be('nested.level2'); + expect(table.columns.length).toBe(3); + expect(table.columns[0].text).toBe('Timestamp'); + expect(table.columns[1].text).toBe('Message'); + expect(table.columns[2].text).toBe('nested.level2'); }); it ('should return 2 rows', () => { - expect(table.rows.length).to.be(1); - expect(table.rows[0][0]).to.be('time'); - expect(table.rows[0][1]).to.be('message'); - expect(table.rows[0][2]).to.be('level2-value'); + expect(table.rows.length).toBe(1); + expect(table.rows[0][0]).toBe('time'); + expect(table.rows[0][1]).toBe('message'); + expect(table.rows[0][2]).toBe('level2-value'); }); }); }); @@ -169,16 +167,16 @@ describe('when transforming time series table', () => { }); it ('should return 4 columns', () => { - expect(table.columns.length).to.be(4); - expect(table.columns[0].text).to.be('Time'); - expect(table.columns[1].text).to.be('Title'); - expect(table.columns[2].text).to.be('Text'); - expect(table.columns[3].text).to.be('Tags'); + expect(table.columns.length).toBe(4); + expect(table.columns[0].text).toBe('Time'); + expect(table.columns[1].text).toBe('Title'); + expect(table.columns[2].text).toBe('Text'); + expect(table.columns[3].text).toBe('Tags'); }); it ('should return 1 rows', () => { - expect(table.rows.length).to.be(1); - expect(table.rows[0][0]).to.be(1000); + expect(table.rows.length).toBe(1); + expect(table.rows[0][0]).toBe(1000); }); }); diff --git a/public/app/plugins/panel/table/transformers.ts b/public/app/plugins/panel/table/transformers.ts index f77da17e227..e23adb43f19 100644 --- a/public/app/plugins/panel/table/transformers.ts +++ b/public/app/plugins/panel/table/transformers.ts @@ -1,5 +1,3 @@ -/// - import _ from 'lodash'; import flatten from '../../../core/utils/flatten'; import TimeSeries from '../../../core/time_series2'; diff --git a/public/test/core/utils/kbn_specs.js b/public/test/core/utils/kbn_specs.js deleted file mode 100644 index 12e9862f88a..00000000000 --- a/public/test/core/utils/kbn_specs.js +++ /dev/null @@ -1,339 +0,0 @@ -define([ - 'app/core/utils/kbn', - 'app/core/utils/datemath', - 'moment' -], function(kbn, dateMath, moment) { - 'use strict'; - - describe('unit format menu', function() { - var menu = kbn.getUnitFormats(); - menu.map(function(submenu) { - describe('submenu ' + submenu.text, function() { - it('should have a title', function() { expect(submenu.text).to.be.a('string'); }); - it('should have a submenu', function() { expect(submenu.submenu).to.be.an('array'); }); - submenu.submenu.map(function(entry) { - describe('entry ' + entry.text, function() { - it('should have a title', function() { expect(entry.text).to.be.a('string'); }); - it('should have a format', function() { expect(entry.value).to.be.a('string'); }); - it('should have a valid format', function() { - expect(kbn.valueFormats[entry.value]).to.be.a('function'); - }); - }); - }); - }); - }); - }); - - function describeValueFormat(desc, value, tickSize, tickDecimals, result) { - - describe('value format: ' + desc, function() { - it('should translate ' + value + ' as ' + result, function() { - var scaledDecimals = tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10); - var str = kbn.valueFormats[desc](value, tickDecimals, scaledDecimals); - expect(str).to.be(result); - }); - }); - - } - - describeValueFormat('ms', 0.0024, 0.0005, 4, '0.0024 ms'); - describeValueFormat('ms', 100, 1, 0, '100 ms'); - describeValueFormat('ms', 1250, 10, 0, '1.25 s'); - describeValueFormat('ms', 1250, 300, 0, '1.3 s'); - describeValueFormat('ms', 65150, 10000, 0, '1.1 min'); - describeValueFormat('ms', 6515000, 1500000, 0, '1.8 hour'); - describeValueFormat('ms', 651500000, 150000000, 0, '8 day'); - - describeValueFormat('none', 2.75e-10, 0, 10, '3e-10'); - describeValueFormat('none', 0, 0, 2, '0'); - describeValueFormat('dB', 10, 1000, 2, '10.00 dB'); - - describeValueFormat('percent', 0, 0, 0, '0%'); - describeValueFormat('percent', 53, 0, 1, '53.0%'); - describeValueFormat('percentunit', 0.0, 0, 0, '0%'); - describeValueFormat('percentunit', 0.278, 0, 1, '27.8%'); - describeValueFormat('percentunit', 1.0, 0, 0, '100%'); - - describeValueFormat('currencyUSD', 7.42, 10000, 2, '$7.42'); - describeValueFormat('currencyUSD', 1532.82, 1000, 1, '$1.53K'); - describeValueFormat('currencyUSD', 18520408.7, 10000000, 0, '$19M'); - - describeValueFormat('bytes', -1.57e+308, -1.57e+308, 2, 'NA'); - - describeValueFormat('ns', 25, 1, 0, '25 ns'); - describeValueFormat('ns', 2558, 50, 0, '2.56 µs'); - - describeValueFormat('ops', 123, 1, 0, '123 ops'); - describeValueFormat('rps', 456000, 1000, -1, '456K rps'); - describeValueFormat('rps', 123456789, 1000000, 2, '123.457M rps'); - describeValueFormat('wps', 789000000, 1000000, -1, '789M wps'); - describeValueFormat('iops', 11000000000, 1000000000, -1, '11B iops'); - - describeValueFormat('s', 1.23456789e-7, 1e-10, 8, '123.5 ns'); - describeValueFormat('s', 1.23456789e-4, 1e-7, 5, '123.5 µs'); - describeValueFormat('s', 1.23456789e-3, 1e-6, 4, '1.235 ms'); - describeValueFormat('s', 1.23456789e-2, 1e-5, 3, '12.35 ms'); - describeValueFormat('s', 1.23456789e-1, 1e-4, 2, '123.5 ms'); - describeValueFormat('s', 24, 1, 0, '24 s'); - describeValueFormat('s', 246, 1, 0, '4.1 min'); - describeValueFormat('s', 24567, 100, 0, '6.82 hour'); - describeValueFormat('s', 24567890, 10000, 0, '40.62 week'); - describeValueFormat('s', 24567890000, 1000000, 0, '778.53 year'); - - describeValueFormat('m', 24, 1, 0, '24 min'); - describeValueFormat('m', 246, 10, 0, '4.1 hour'); - describeValueFormat('m', 6545, 10, 0, '4.55 day'); - describeValueFormat('m', 24567, 100, 0, '2.44 week'); - describeValueFormat('m', 24567892, 10000, 0, '46.7 year'); - - describeValueFormat('h', 21, 1, 0, '21 hour'); - describeValueFormat('h', 145, 1, 0, '6.04 day'); - describeValueFormat('h', 1234, 100, 0, '7.3 week'); - describeValueFormat('h', 9458, 1000, 0, '1.08 year'); - - describeValueFormat('d', 3, 1, 0, '3 day'); - describeValueFormat('d', 245, 100, 0, '35 week'); - describeValueFormat('d', 2456, 10, 0, '6.73 year'); - - describe('date time formats', function() { - it('should format as iso date', function() { - var str = kbn.valueFormats.dateTimeAsIso(1505634997920, 1); - expect(str).to.be(moment(1505634997920).format('YYYY-MM-DD HH:mm:ss')); - }); - - it('should format as iso date and skip date when today', function() { - var now = moment(); - var str = kbn.valueFormats.dateTimeAsIso(now.valueOf(), 1); - expect(str).to.be(now.format("HH:mm:ss")); - }); - - it('should format as US date', function() { - var str = kbn.valueFormats.dateTimeAsUS(1505634997920, 1); - expect(str).to.be(moment(1505634997920).format('MM/DD/YYYY H:mm:ss a')); - }); - - it('should format as US date and skip date when today', function() { - var now = moment(); - var str = kbn.valueFormats.dateTimeAsUS(now.valueOf(), 1); - expect(str).to.be(now.format("h:mm:ss a")); - }); - - it('should format as from now with days', function() { - var daysAgo = moment().add(-7, 'd'); - var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1); - expect(str).to.be('7 days ago'); - }); - - it('should format as from now with minutes', function() { - var daysAgo = moment().add(-2, 'm'); - var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1); - expect(str).to.be('2 minutes ago'); - }); - }); - - describe('kbn.toFixed and negative decimals', function() { - it('should treat as zero decimals', function() { - var str = kbn.toFixed(186.123, -2); - expect(str).to.be('186'); - }); - }); - - describe('kbn ms format when scaled decimals is null do not use it', function() { - it('should use specified decimals', function() { - var str = kbn.valueFormats['ms'](10000086.123, 1, null); - expect(str).to.be('2.8 hour'); - }); - }); - - describe('kbn kbytes format when scaled decimals is null do not use it', function() { - it('should use specified decimals', function() { - var str = kbn.valueFormats['kbytes'](10000000, 3, null); - expect(str).to.be('9.537 GiB'); - }); - }); - - describe('kbn deckbytes format when scaled decimals is null do not use it', function() { - it('should use specified decimals', function() { - var str = kbn.valueFormats['deckbytes'](10000000, 3, null); - expect(str).to.be('10.000 GB'); - }); - }); - - describe('kbn roundValue', function() { - it('should should handle null value', function() { - var str = kbn.roundValue(null, 2); - expect(str).to.be(null); - }); - }); - - describe('calculateInterval', function() { - it('1h 100 resultion', function() { - var range = { from: dateMath.parse('now-1h'), to: dateMath.parse('now') }; - var res = kbn.calculateInterval(range, 100, null); - expect(res.interval).to.be('30s'); - }); - - it('10m 1600 resolution', function() { - var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') }; - var res = kbn.calculateInterval(range, 1600, null); - expect(res.interval).to.be('500ms'); - expect(res.intervalMs).to.be(500); - }); - - it('fixed user min interval', function() { - var range = {from: dateMath.parse('now-10m'), to: dateMath.parse('now')}; - var res = kbn.calculateInterval(range, 1600, '10s'); - expect(res.interval).to.be('10s'); - expect(res.intervalMs).to.be(10000); - }); - - it('short time range and user low limit', function() { - var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') }; - var res = kbn.calculateInterval(range, 1600, '>10s'); - expect(res.interval).to.be('10s'); - }); - - it('large time range and user low limit', function() { - var range = {from: dateMath.parse('now-14d'), to: dateMath.parse('now')}; - var res = kbn.calculateInterval(range, 1000, '>10s'); - expect(res.interval).to.be('20m'); - }); - - it('10s 900 resolution and user low limit in ms', function() { - var range = { from: dateMath.parse('now-10s'), to: dateMath.parse('now') }; - var res = kbn.calculateInterval(range, 900, '>15ms'); - expect(res.interval).to.be('15ms'); - }); - - it('1d 1 resolution', function() { - var range = { from: dateMath.parse('now-1d'), to: dateMath.parse('now') }; - var res = kbn.calculateInterval(range, 1, null); - expect(res.interval).to.be('1d'); - expect(res.intervalMs).to.be(86400000); - }); - - it('86399s 1 resolution', function() { - var range = { from: dateMath.parse('now-86390s'), to: dateMath.parse('now') }; - var res = kbn.calculateInterval(range, 1, null); - expect(res.interval).to.be('12h'); - expect(res.intervalMs).to.be(43200000); - }); - }); - - describe('hex', function() { - it('positive integer', function() { - var str = kbn.valueFormats.hex(100, 0); - expect(str).to.be('64'); - }); - it('negative integer', function() { - var str = kbn.valueFormats.hex(-100, 0); - expect(str).to.be('-64'); - }); - it('null', function() { - var str = kbn.valueFormats.hex(null, 0); - expect(str).to.be(''); - }); - it('positive float', function() { - var str = kbn.valueFormats.hex(50.52, 1); - expect(str).to.be('32.8'); - }); - it('negative float', function() { - var str = kbn.valueFormats.hex(-50.333, 2); - expect(str).to.be('-32.547AE147AE14'); - }); - }); - - describe('hex 0x', function() { - it('positive integeter', function() { - var str = kbn.valueFormats.hex0x(7999,0); - expect(str).to.be('0x1F3F'); - }); - it('negative integer', function() { - var str = kbn.valueFormats.hex0x(-584,0); - expect(str).to.be('-0x248'); - }); - it('null', function() { - var str = kbn.valueFormats.hex0x(null, 0); - expect(str).to.be(''); - }); - it('positive float', function() { - var str = kbn.valueFormats.hex0x(74.443, 3); - expect(str).to.be('0x4A.716872B020C4'); - }); - it('negative float', function() { - var str = kbn.valueFormats.hex0x(-65.458, 1); - expect(str).to.be('-0x41.8'); - }); - }); - - describe('duration', function() { - it('null', function() { - var str = kbn.toDuration(null, 0, "millisecond"); - expect(str).to.be(''); - }); - it('0 milliseconds', function() { - var str = kbn.toDuration(0, 0, "millisecond"); - expect(str).to.be('0 milliseconds'); - }); - it('1 millisecond', function() { - var str = kbn.toDuration(1, 0, "millisecond"); - expect(str).to.be('1 millisecond'); - }); - it('-1 millisecond', function() { - var str = kbn.toDuration(-1, 0, "millisecond"); - expect(str).to.be('1 millisecond ago'); - }); - it('seconds', function() { - var str = kbn.toDuration(1, 0, "second"); - expect(str).to.be('1 second'); - }); - it('minutes', function() { - var str = kbn.toDuration(1, 0, "minute"); - expect(str).to.be('1 minute'); - }); - it('hours', function() { - var str = kbn.toDuration(1, 0, "hour"); - expect(str).to.be('1 hour'); - }); - it('days', function() { - var str = kbn.toDuration(1, 0, "day"); - expect(str).to.be('1 day'); - }); - it('weeks', function() { - var str = kbn.toDuration(1, 0, "week"); - expect(str).to.be('1 week'); - }); - it('months', function() { - var str = kbn.toDuration(1, 0, "month"); - expect(str).to.be('1 month'); - }); - it('years', function() { - var str = kbn.toDuration(1, 0, "year"); - expect(str).to.be('1 year'); - }); - it('decimal days', function() { - var str = kbn.toDuration(1.5, 2, "day"); - expect(str).to.be('1 day, 12 hours, 0 minutes'); - }); - it('decimal months', function() { - var str = kbn.toDuration(1.5, 3, "month"); - expect(str).to.be('1 month, 2 weeks, 1 day, 0 hours'); - }); - it('no decimals', function() { - var str = kbn.toDuration(38898367008, 0, "millisecond"); - expect(str).to.be('1 year'); - }); - it('1 decimal', function() { - var str = kbn.toDuration(38898367008, 1, "millisecond"); - expect(str).to.be('1 year, 2 months'); - }); - it('too many decimals', function() { - var str = kbn.toDuration(38898367008, 20, "millisecond"); - expect(str).to.be('1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds, 8 milliseconds'); - }); - it('floating point error', function() { - var str = kbn.toDuration(36993906007, 8, "millisecond"); - expect(str).to.be('1 year, 2 months, 0 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds'); - }); - }); -}); diff --git a/public/test/jest-setup.ts b/public/test/jest-setup.ts new file mode 100644 index 00000000000..82edfc9e5ad --- /dev/null +++ b/public/test/jest-setup.ts @@ -0,0 +1,4 @@ +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; + +configure({ adapter: new Adapter() }); diff --git a/public/test/jest-shim.ts b/public/test/jest-shim.ts new file mode 100644 index 00000000000..80c4bb3d21b --- /dev/null +++ b/public/test/jest-shim.ts @@ -0,0 +1,6 @@ +declare var global: NodeJS.Global; + +(global).requestAnimationFrame = (callback) => { + setTimeout(callback, 0); +}; + diff --git a/scripts/circle-test.sh b/scripts/circle-test.sh index 7dd5defac97..ca0ec6124d0 100755 --- a/scripts/circle-test.sh +++ b/scripts/circle-test.sh @@ -16,8 +16,16 @@ rm -rf node_modules npm install -g yarn --quiet yarn install --pure-lockfile --no-progress -exit_if_fail npm test -exit_if_fail npm build +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" exit_if_fail test -z "$(gofmt -s -l ./pkg | tee /dev/stderr)" @@ -29,4 +37,17 @@ echo "building binaries" exit_if_fail go run build.go build echo "running go test" -exit_if_fail go test -v ./pkg/... + +set -e +echo "" > coverage.txt + +for d in $(go list ./pkg/...); do + exit_if_fail go test -race -coverprofile=profile.out -covermode=atomic $d + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done + +echo "Publishing go code coverage" +bash <(curl -s https://codecov.io/bash) -cF go diff --git a/scripts/grunt/default_task.js b/scripts/grunt/default_task.js index bd141287edc..d4c1f75e95e 100644 --- a/scripts/grunt/default_task.js +++ b/scripts/grunt/default_task.js @@ -14,6 +14,7 @@ module.exports = function(grunt) { 'jshint', 'sasslint', 'exec:tslint', + "exec:jest", 'karma:test', 'no-only-tests' ]); diff --git a/scripts/grunt/options/exec.js b/scripts/grunt/options/exec.js index 7d37293d2df..2b631d897fa 100644 --- a/scripts/grunt/options/exec.js +++ b/scripts/grunt/options/exec.js @@ -1,6 +1,13 @@ module.exports = function(config, grunt) { 'use strict' + + var coverage = ''; + if (config.coverage) { + coverage = '--coverage'; + } + return { - tslint : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json --type-check", + 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, }; }; diff --git a/scripts/webpack/webpack.prod.js b/scripts/webpack/webpack.prod.js index 6d254c7ab0b..31fed77d749 100644 --- a/scripts/webpack/webpack.prod.js +++ b/scripts/webpack/webpack.prod.js @@ -26,6 +26,10 @@ module.exports = merge(common, { ] }, + devServer: { + stats: 'errors-only', + }, + plugins: [ new ExtractTextPlugin({ filename: 'grafana.[name].css', diff --git a/tslint.json b/tslint.json index 9e3a4f29747..f1b414c916e 100644 --- a/tslint.json +++ b/tslint.json @@ -2,7 +2,8 @@ "rules": { "no-string-throw": true, "no-unused-expression": true, - "no-unused-variable": true, + "no-unused-variable": false, + "no-use-before-declare": false, "no-duplicate-variable": true, "curly": true, "class-name": true, @@ -33,7 +34,6 @@ "no-string-literal": false, "no-switch-case-fall-through": false, "no-trailing-whitespace": true, - "no-use-before-declare": true, "no-var-keyword": false, "object-literal-sort-keys": false, "one-line": [true, diff --git a/yarn.lock b/yarn.lock index 326d2176eb5..b195e8f34b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -193,6 +193,10 @@ version "1.0.4" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-1.0.4.tgz#f6e011bf3f7eea616cce79b6f1a0722010822f3a" +"@types/jest@^21.1.4": + version "21.1.4" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-21.1.4.tgz#83c5c474dd6dee5bef9d014ff36787edfd4ab5a7" + "@types/node@^6.0.46": version "6.0.88" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.88.tgz#f618f11a944f6a18d92b5c472028728a3e3d4b66" @@ -222,14 +226,14 @@ JSONStream@~1.3.1: version "4.0.2" resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57" +abab@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" + abbrev@1, abbrev@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" -abbrev@1.0.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" - accepts@1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" @@ -250,6 +254,12 @@ acorn-dynamic-import@^2.0.0: dependencies: acorn "^4.0.3" +acorn-globals@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" + dependencies: + acorn "^4.0.4" + acorn-jsx@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" @@ -260,7 +270,7 @@ acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" -acorn@^4.0.3: +acorn@^4.0.3, acorn@^4.0.4: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" @@ -368,6 +378,10 @@ ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" +ansi-escapes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -405,6 +419,12 @@ app-root-path@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.0.1.tgz#cd62dcf8e4fd5a417efc664d2e5b10653c651b46" +append-transform@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" + dependencies: + default-require-extensions "^1.0.0" + aproba@^1.0.3, aproba@^1.1.1, aproba@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1" @@ -480,6 +500,10 @@ array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -514,7 +538,7 @@ arraybuffer.slice@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" -arrify@^1.0.0: +arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -552,6 +576,10 @@ ast-types@0.9.6: version "0.9.6" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -564,11 +592,11 @@ async@0.2.x, async@~0.2.6, async@~0.2.9: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" -async@1.x, async@^1.4.0, async@^1.5.2, async@~1.5.2: +async@^1.4.0, async@^1.5.2, async@~1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.0.0, async@^2.1.2, async@^2.1.5, async@^2.4.1: +async@^2.0.0, async@^2.1.2, async@^2.1.4, async@^2.1.5, async@^2.4.1: version "2.5.0" resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d" dependencies: @@ -630,7 +658,7 @@ babel-code-frame@^6.11.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: esutils "^2.0.2" js-tokens "^3.0.2" -babel-core@^6.26.0: +babel-core@^6.0.0, babel-core@^6.24.1, babel-core@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8" dependencies: @@ -654,7 +682,7 @@ babel-core@^6.26.0: slash "^1.0.0" source-map "^0.5.6" -babel-generator@^6.26.0: +babel-generator@^6.18.0, babel-generator@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" dependencies: @@ -742,6 +770,13 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" +babel-jest@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-21.2.0.tgz#2ce059519a9374a2c46f2455b6fbef5ad75d863e" + dependencies: + babel-plugin-istanbul "^4.0.0" + babel-preset-jest "^21.2.0" + babel-loader@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.2.tgz#f6cbe122710f1aa2af4d881c6d5b54358ca24126" @@ -762,6 +797,22 @@ babel-plugin-check-es2015-constants@^6.22.0: dependencies: babel-runtime "^6.22.0" +babel-plugin-istanbul@^4.0.0, babel-plugin-istanbul@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz#6760cdd977f411d3e175bb064f2bc327d99b2b6e" + dependencies: + find-up "^2.1.0" + istanbul-lib-instrument "^1.7.5" + test-exclude "^4.1.1" + +babel-plugin-jest-hoist@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-21.2.0.tgz#2cef637259bd4b628a6cace039de5fcd14dbb006" + +babel-plugin-syntax-object-rest-spread@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + babel-plugin-transform-es2015-arrow-functions@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" @@ -980,6 +1031,13 @@ babel-preset-es2015@^6.24.1: babel-plugin-transform-es2015-unicode-regex "^6.24.1" babel-plugin-transform-regenerator "^6.24.1" +babel-preset-jest@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-21.2.0.tgz#ff9d2bce08abd98e8a36d9a8a5189b9173b85638" + dependencies: + babel-plugin-jest-hoist "^21.2.0" + babel-plugin-syntax-object-rest-spread "^6.13.0" + babel-register@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" @@ -999,7 +1057,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runti core-js "^2.4.0" regenerator-runtime "^0.11.0" -babel-template@^6.24.1, babel-template@^6.26.0: +babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" dependencies: @@ -1009,7 +1067,7 @@ babel-template@^6.24.1, babel-template@^6.26.0: babylon "^6.18.0" lodash "^4.17.4" -babel-traverse@^6.24.1, babel-traverse@^6.26.0: +babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" dependencies: @@ -1023,7 +1081,7 @@ babel-traverse@^6.24.1, babel-traverse@^6.26.0: invariant "^2.2.2" lodash "^4.17.4" -babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: +babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" dependencies: @@ -1208,6 +1266,12 @@ brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" +browser-resolve@^1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" + dependencies: + resolve "1.1.7" + browser-stdout@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" @@ -1271,6 +1335,12 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" +bser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" + dependencies: + node-int64 "^0.4.0" + buffer-crc32@^0.2.1: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -1353,6 +1423,10 @@ callsites@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + camel-case@3.0.x, camel-case@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" @@ -1708,7 +1782,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.11.0, commander@2.11.x, commander@~2.11.0: +commander@2.11.0, commander@2.11.x, commander@^2.8.1, commander@^2.9.0, commander@~2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" @@ -1718,7 +1792,7 @@ commander@2.8.x: dependencies: graceful-readlink ">= 1.0.0" -commander@2.9.x, commander@^2.8.1, commander@^2.9.0, commander@~2.9.0: +commander@2.9.x, commander@~2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: @@ -1823,11 +1897,15 @@ content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" +content-type-parser@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94" + content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" -convert-source-map@^1.5.0: +convert-source-map@^1.4.0, convert-source-map@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" @@ -2071,6 +2149,16 @@ csso@~2.3.1: clap "^1.0.9" source-map "^0.5.3" +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" + +"cssstyle@>= 0.2.37 < 0.3.0": + version "0.2.37" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" + dependencies: + cssom "0.3.x" + cst@^0.4.3: version "0.4.10" resolved "https://registry.yarnpkg.com/cst/-/cst-0.4.10.tgz#9c05c825290a762f0a85c0aabb8c0fe035ae8516" @@ -2117,14 +2205,14 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -dateformat@^1.0.6, dateformat@~1.0.12: +dateformat@~1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" dependencies: get-stdin "^4.0.1" meow "^3.3.0" -debug@2, debug@2.6.9, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.4.1, debug@^2.6.8: +debug@2, debug@2.6.9, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.4.1, debug@^2.6.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -2174,6 +2262,12 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +default-require-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" + dependencies: + strip-bom "^2.0.0" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -2263,7 +2357,7 @@ di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" -diff@3.3.1: +diff@3.3.1, diff@^3.2.0: version "3.3.1" resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" @@ -2271,10 +2365,6 @@ diff@^2.0.2: version "2.2.3" resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99" -diff@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" - diffie-hellman@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" @@ -2526,15 +2616,16 @@ entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" -enzyme-adapter-react-16@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.0.tgz#e7edd5536743818dcbef336d40d7da59b3a7db8e" +enzyme-adapter-react-16@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.2.tgz#8c6f431f17c69e1e9eeb25ca4bd92f31971eb2dd" dependencies: enzyme-adapter-utils "^1.0.0" lodash "^4.17.4" object.assign "^4.0.4" object.values "^1.0.4" prop-types "^15.5.10" + react-test-renderer "^16.0.0-0" enzyme-adapter-utils@^1.0.0: version "1.0.0" @@ -2544,9 +2635,9 @@ enzyme-adapter-utils@^1.0.0: object.assign "^4.0.4" prop-types "^15.5.10" -enzyme@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.0.0.tgz#94ce364254dc654c4e619b25eecc644bf6481de7" +enzyme@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.1.0.tgz#d8ca84085790fbcec6ed40badd14478faee4c25a" dependencies: cheerio "^1.0.0-rc.2" function.prototype.name "^1.0.3" @@ -2557,7 +2648,7 @@ enzyme@^3.0.0: object.entries "^1.0.4" object.values "^1.0.4" raf "^3.3.2" - rst-selector-parser "^2.2.1" + rst-selector-parser "^2.2.2" err-code@^1.0.0: version "1.1.2" @@ -2678,7 +2769,7 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -escodegen@1.8.x: +escodegen@^1.6.1: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" dependencies: @@ -2743,10 +2834,14 @@ espree@^3.1.6: acorn "^5.1.1" acorn-jsx "^3.0.0" -esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: +esprima@^2.6.0, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" +esprima@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + esprima@~3.1.0: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -2804,6 +2899,12 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +exec-sh@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.1.tgz#163b98a6e89e6b65b47c2a28d215bc1f63989c38" + dependencies: + merge "^1.1.3" + execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -2883,6 +2984,17 @@ expect.js@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/expect.js/-/expect.js-0.2.0.tgz#1028533d2c1c363f74a6796ff57ec0520ded2be1" +expect@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-21.2.1.tgz#003ac2ac7005c3c29e73b38a272d4afadd6d1d7b" + dependencies: + ansi-styles "^3.2.0" + jest-diff "^21.2.1" + jest-get-type "^21.2.0" + jest-matcher-utils "^21.2.1" + jest-message-util "^21.2.1" + jest-regex-util "^21.2.0" + expose-loader@^0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.3.tgz#35fbd3659789e4faa81f59de8b7e9fc39e466d51" @@ -2989,6 +3101,12 @@ fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" +fb-watchman@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" + dependencies: + bser "^2.0.0" + fbjs@^0.8.16: version "0.8.16" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" @@ -3039,6 +3157,13 @@ filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" +fileset@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" + dependencies: + glob "^7.0.3" + minimatch "^3.0.3" + filesize@^3.5.9: version "3.5.10" resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.10.tgz#fc8fa23ddb4ef9e5e0ab6e1e64f679a24a56761f" @@ -3223,6 +3348,14 @@ fs-extra@^3.0.1: jsonfile "^3.0.0" universalify "^0.1.0" +fs-extra@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" @@ -3252,7 +3385,7 @@ fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" -fsevents@^1.0.0: +fsevents@^1.0.0, fsevents@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" dependencies: @@ -3379,7 +3512,7 @@ glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glo once "^1.3.0" path-is-absolute "^1.0.0" -glob@^5.0.1, glob@^5.0.15, glob@~5.0.0: +glob@^5.0.1, glob@~5.0.0: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" dependencies: @@ -3463,6 +3596,10 @@ growl@1.10.3: version "1.10.3" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + grunt-angular-templates@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/grunt-angular-templates/-/grunt-angular-templates-1.1.0.tgz#109603a2b95ff01019b718c70341268f09d8be19" @@ -3659,7 +3796,7 @@ gzip-size@^3.0.0: dependencies: duplexer "^0.1.1" -handlebars@^4.0.1: +handlebars@^4.0.3: version "4.0.10" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f" dependencies: @@ -3819,6 +3956,12 @@ html-comment-regex@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" +html-encoding-sniffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz#79bf7a785ea495fe66165e734153f363ff5437da" + dependencies: + whatwg-encoding "^1.0.1" + html-loader@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-0.5.1.tgz#4f1e8396a1ea6ab42bedc987dfac058070861ebe" @@ -3959,6 +4102,10 @@ i@0.3.x: version "0.3.5" resolved "https://registry.yarnpkg.com/i/-/i-0.3.5.tgz#1d2b854158ec8169113c6cb7f6b6801e99e211d5" +iconv-lite@0.4.13: + version "0.4.13" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" + iconv-lite@0.4.19, iconv-lite@~0.4.13: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -4408,30 +4555,280 @@ isstream@0.1.x, isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -istanbul@^0.4.0: - version "0.4.5" - resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" +istanbul-api@^1.1.1: + version "1.1.14" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.14.tgz#25bc5701f7c680c0ffff913de46e3619a3a6e680" dependencies: - abbrev "1.0.x" - async "1.x" - escodegen "1.8.x" - esprima "2.7.x" - glob "^5.0.15" - handlebars "^4.0.1" - js-yaml "3.x" - mkdirp "0.5.x" - nopt "3.x" - once "1.x" - resolve "1.1.x" - supports-color "^3.1.0" - which "^1.1.1" - wordwrap "^1.0.0" + async "^2.1.4" + fileset "^2.0.2" + istanbul-lib-coverage "^1.1.1" + istanbul-lib-hook "^1.0.7" + istanbul-lib-instrument "^1.8.0" + istanbul-lib-report "^1.1.1" + istanbul-lib-source-maps "^1.2.1" + istanbul-reports "^1.1.2" + js-yaml "^3.7.0" + mkdirp "^0.5.1" + once "^1.4.0" + +istanbul-lib-coverage@^1.0.1, istanbul-lib-coverage@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da" + +istanbul-lib-hook@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz#dd6607f03076578fe7d6f2a630cf143b49bacddc" + dependencies: + append-transform "^0.4.0" + +istanbul-lib-instrument@^1.4.2, istanbul-lib-instrument@^1.7.5, istanbul-lib-instrument@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz#66f6c9421cc9ec4704f76f2db084ba9078a2b532" + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.18.0" + istanbul-lib-coverage "^1.1.1" + semver "^5.3.0" + +istanbul-lib-report@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#f0e55f56655ffa34222080b7a0cd4760e1405fc9" + dependencies: + istanbul-lib-coverage "^1.1.1" + mkdirp "^0.5.1" + path-parse "^1.0.5" + supports-color "^3.1.2" + +istanbul-lib-source-maps@^1.1.0, istanbul-lib-source-maps@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz#a6fe1acba8ce08eebc638e572e294d267008aa0c" + dependencies: + debug "^2.6.3" + istanbul-lib-coverage "^1.1.1" + mkdirp "^0.5.1" + rimraf "^2.6.1" + source-map "^0.5.3" + +istanbul-reports@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.2.tgz#0fb2e3f6aa9922bd3ce45d05d8ab4d5e8e07bd4f" + dependencies: + handlebars "^4.0.3" + +jest-changed-files@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-21.2.0.tgz#5dbeecad42f5d88b482334902ce1cba6d9798d29" + dependencies: + throat "^4.0.0" + +jest-cli@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-21.2.1.tgz#9c528b6629d651911138d228bdb033c157ec8c00" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + is-ci "^1.0.10" + istanbul-api "^1.1.1" + istanbul-lib-coverage "^1.0.1" + istanbul-lib-instrument "^1.4.2" + istanbul-lib-source-maps "^1.1.0" + jest-changed-files "^21.2.0" + jest-config "^21.2.1" + jest-environment-jsdom "^21.2.1" + jest-haste-map "^21.2.0" + jest-message-util "^21.2.1" + jest-regex-util "^21.2.0" + jest-resolve-dependencies "^21.2.0" + jest-runner "^21.2.1" + jest-runtime "^21.2.1" + jest-snapshot "^21.2.1" + jest-util "^21.2.1" + micromatch "^2.3.11" + node-notifier "^5.0.2" + pify "^3.0.0" + slash "^1.0.0" + string-length "^2.0.0" + strip-ansi "^4.0.0" + which "^1.2.12" + worker-farm "^1.3.1" + yargs "^9.0.0" + +jest-config@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-21.2.1.tgz#c7586c79ead0bcc1f38c401e55f964f13bf2a480" + dependencies: + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^21.2.1" + jest-environment-node "^21.2.1" + jest-get-type "^21.2.0" + jest-jasmine2 "^21.2.1" + jest-regex-util "^21.2.0" + jest-resolve "^21.2.0" + jest-util "^21.2.1" + jest-validate "^21.2.1" + pretty-format "^21.2.1" + +jest-diff@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-21.2.1.tgz#46cccb6cab2d02ce98bc314011764bb95b065b4f" + dependencies: + chalk "^2.0.1" + diff "^3.2.0" + jest-get-type "^21.2.0" + pretty-format "^21.2.1" + +jest-docblock@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" + +jest-environment-jsdom@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-21.2.1.tgz#38d9980c8259b2a608ec232deee6289a60d9d5b4" + dependencies: + jest-mock "^21.2.0" + jest-util "^21.2.1" + jsdom "^9.12.0" + +jest-environment-node@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-21.2.1.tgz#98c67df5663c7fbe20f6e792ac2272c740d3b8c8" + dependencies: + jest-mock "^21.2.0" + jest-util "^21.2.1" jest-get-type@^21.2.0: version "21.2.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23" -jest-validate@^21.1.0: +jest-haste-map@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-21.2.0.tgz#1363f0a8bb4338f24f001806571eff7a4b2ff3d8" + dependencies: + fb-watchman "^2.0.0" + graceful-fs "^4.1.11" + jest-docblock "^21.2.0" + micromatch "^2.3.11" + sane "^2.0.0" + worker-farm "^1.3.1" + +jest-jasmine2@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-21.2.1.tgz#9cc6fc108accfa97efebce10c4308548a4ea7592" + dependencies: + chalk "^2.0.1" + expect "^21.2.1" + graceful-fs "^4.1.11" + jest-diff "^21.2.1" + jest-matcher-utils "^21.2.1" + jest-message-util "^21.2.1" + jest-snapshot "^21.2.1" + p-cancelable "^0.3.0" + +jest-matcher-utils@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-21.2.1.tgz#72c826eaba41a093ac2b4565f865eb8475de0f64" + dependencies: + chalk "^2.0.1" + jest-get-type "^21.2.0" + pretty-format "^21.2.1" + +jest-message-util@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-21.2.1.tgz#bfe5d4692c84c827d1dcf41823795558f0a1acbe" + dependencies: + chalk "^2.0.1" + micromatch "^2.3.11" + slash "^1.0.0" + +jest-mock@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-21.2.0.tgz#7eb0770e7317968165f61ea2a7281131534b3c0f" + +jest-regex-util@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-21.2.0.tgz#1b1e33e63143babc3e0f2e6c9b5ba1eb34b2d530" + +jest-resolve-dependencies@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-21.2.0.tgz#9e231e371e1a736a1ad4e4b9a843bc72bfe03d09" + dependencies: + jest-regex-util "^21.2.0" + +jest-resolve@^21.2.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-21.2.0.tgz#068913ad2ba6a20218e5fd32471f3874005de3a6" + dependencies: + browser-resolve "^1.11.2" + chalk "^2.0.1" + is-builtin-module "^1.0.0" + +jest-runner@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-21.2.1.tgz#194732e3e518bfb3d7cbfc0fd5871246c7e1a467" + dependencies: + jest-config "^21.2.1" + jest-docblock "^21.2.0" + jest-haste-map "^21.2.0" + jest-jasmine2 "^21.2.1" + jest-message-util "^21.2.1" + jest-runtime "^21.2.1" + jest-util "^21.2.1" + pify "^3.0.0" + throat "^4.0.0" + worker-farm "^1.3.1" + +jest-runtime@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-21.2.1.tgz#99dce15309c670442eee2ebe1ff53a3cbdbbb73e" + dependencies: + babel-core "^6.0.0" + babel-jest "^21.2.0" + babel-plugin-istanbul "^4.0.0" + chalk "^2.0.1" + convert-source-map "^1.4.0" + graceful-fs "^4.1.11" + jest-config "^21.2.1" + jest-haste-map "^21.2.0" + jest-regex-util "^21.2.0" + jest-resolve "^21.2.0" + jest-util "^21.2.1" + json-stable-stringify "^1.0.1" + micromatch "^2.3.11" + slash "^1.0.0" + strip-bom "3.0.0" + write-file-atomic "^2.1.0" + yargs "^9.0.0" + +jest-snapshot@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-21.2.1.tgz#29e49f16202416e47343e757e5eff948c07fd7b0" + dependencies: + chalk "^2.0.1" + jest-diff "^21.2.1" + jest-matcher-utils "^21.2.1" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^21.2.1" + +jest-util@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-21.2.1.tgz#a274b2f726b0897494d694a6c3d6a61ab819bb78" + dependencies: + callsites "^2.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.11" + jest-message-util "^21.2.1" + jest-mock "^21.2.0" + jest-validate "^21.2.1" + mkdirp "^0.5.1" + +jest-validate@^21.1.0, jest-validate@^21.2.1: version "21.2.1" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7" dependencies: @@ -4440,6 +4837,12 @@ jest-validate@^21.1.0: leven "^2.1.0" pretty-format "^21.2.1" +jest@^21.2.1: + version "21.2.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-21.2.1.tgz#c964e0b47383768a1438e3ccf3c3d470327604e1" + dependencies: + jest-cli "^21.2.1" + jquery@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787" @@ -4452,13 +4855,20 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-yaml@3.x, js-yaml@^3.4.3, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4, js-yaml@~3.5.2: +js-yaml@^3.4.3, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4, js-yaml@~3.5.2: version "3.5.5" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.5.5.tgz#0377c38017cabc7322b0d1fbcd25a491641f2fbe" dependencies: argparse "^1.0.2" esprima "^2.6.0" +js-yaml@^3.7.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@~3.4.0: version "3.4.6" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.4.6.tgz#6be1b23f6249f53d293370fd4d1aaa63ce1b4eb0" @@ -4526,6 +4936,30 @@ jsdoctypeparser@~1.2.0: dependencies: lodash "^3.7.0" +jsdom@^9.12.0: + version "9.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4" + dependencies: + abab "^1.0.3" + acorn "^4.0.4" + acorn-globals "^3.1.0" + array-equal "^1.0.0" + content-type-parser "^1.0.1" + cssom ">= 0.3.2 < 0.4.0" + cssstyle ">= 0.2.37 < 0.3.0" + escodegen "^1.6.1" + html-encoding-sniffer "^1.0.1" + nwmatcher ">= 1.3.9 < 2.0.0" + parse5 "^1.5.1" + request "^2.79.0" + sax "^1.2.1" + symbol-tree "^3.2.1" + tough-cookie "^2.3.2" + webidl-conversions "^4.0.0" + whatwg-encoding "^1.0.1" + whatwg-url "^4.3.0" + xml-name-validator "^2.0.1" + jsesc@^0.5.0, jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" @@ -4604,6 +5038,12 @@ jsonfile@^3.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -4639,16 +5079,6 @@ karma-chrome-launcher@~2.2.0: fs-access "^1.0.0" which "^1.2.1" -karma-coverage@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-1.1.1.tgz#5aff8b39cf6994dc22de4c84362c76001b637cf6" - dependencies: - dateformat "^1.0.6" - istanbul "^0.4.0" - lodash "^3.8.0" - minimatch "^3.0.0" - source-map "^0.5.1" - karma-expect@~1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/karma-expect/-/karma-expect-1.1.3.tgz#c6b0a56ff18903db11af4f098cc6e7cf198ce275" @@ -5127,6 +5557,12 @@ make-fetch-happen@^2.4.13: socks-proxy-agent "^3.0.0" ssri "^4.1.6" +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + dependencies: + tmpl "1.0.x" + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -5201,7 +5637,7 @@ merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" -merge@^1.2.0: +merge@^1.1.3, merge@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" @@ -5209,7 +5645,7 @@ methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micromatch@^2.1.5: +micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -5278,7 +5714,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.0, minimatch@~3.0.2: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.0, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -5298,7 +5734,7 @@ minimist@1.1.x: version "1.1.3" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8" -minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0: +minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -5349,7 +5785,7 @@ mkdirp@0.5.0: dependencies: minimist "0.0.8" -mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -5438,6 +5874,10 @@ nanomatch@^1.2.1: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + natural-compare@~1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.2.2.tgz#1f96d60e3141cac1b6d05653ce0daeac763af6aa" @@ -5548,6 +5988,10 @@ node-gyp@^3.3.1, node-gyp@~3.6.2: tar "^2.0.0" which "1" +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + node-libs-browser@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646" @@ -5576,6 +6020,15 @@ node-libs-browser@^2.0.0: util "^0.10.3" vm-browserify "0.0.4" +node-notifier@^5.0.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.1.2.tgz#2fa9e12605fa10009d44549d6fcd8a63dde0e4ff" + dependencies: + growly "^1.3.0" + semver "^5.3.0" + shellwords "^0.1.0" + which "^1.2.12" + node-pre-gyp@0.6.35: version "0.6.35" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.35.tgz#1c161fc9fbf1f3ffecd751959f0fdbd12a56c4ab" @@ -5635,7 +6088,7 @@ node-sass@^4.0.0: colors "0.5.x" underscore "~1.4.4" -"nopt@2 || 3", nopt@3.x, nopt@~3.0.6: +"nopt@2 || 3", nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" dependencies: @@ -5886,6 +6339,10 @@ number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" +"nwmatcher@>= 1.3.9 < 2.0.0": + version "1.4.3" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c" + oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -5969,7 +6426,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0, once@~1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0, once@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -6057,6 +6514,10 @@ osenv@0, osenv@^0.1.4, osenv@~0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -6153,6 +6614,10 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse5@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" + parse5@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.2.tgz#05eff57f0ef4577fb144a79f8b9a967a6cc44510" @@ -6915,7 +7380,7 @@ react-sizeme@^2.3.6: invariant "^2.2.2" lodash "^4.17.4" -react-test-renderer@^16.0.0: +react-test-renderer@^16.0.0, react-test-renderer@^16.0.0-0: version "16.0.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.0.0.tgz#9fe7b8308f2f71f29fc356d4102086f131c9cb15" dependencies: @@ -7285,7 +7750,7 @@ resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" -resolve@1.1.x, resolve@~1.1.0: +resolve@1.1.7, resolve@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -7337,7 +7802,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" -rst-selector-parser@^2.2.1: +rst-selector-parser@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.2.tgz#9927b619bd5af8dc23a76c64caef04edf90d2c65" dependencies: @@ -7378,6 +7843,20 @@ samsam@1.1.2, samsam@~1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" +sane@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-2.2.0.tgz#d6d2e2fcab00e3d283c93b912b7c3a20846f1d56" + dependencies: + anymatch "^1.3.0" + exec-sh "^0.2.0" + fb-watchman "^2.0.0" + minimatch "^3.0.2" + minimist "^1.1.1" + walker "~1.0.5" + watch "~0.18.0" + optionalDependencies: + fsevents "^1.1.1" + sass-graph@^2.1.1: version "2.2.4" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" @@ -7416,7 +7895,7 @@ sass-loader@^6.0.6: lodash.tail "^4.1.1" pify "^3.0.0" -sax@~1.2.1: +sax@^1.2.1, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -7574,6 +8053,10 @@ shelljs@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" +shellwords@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -7748,6 +8231,12 @@ source-map-support@^0.4.0, source-map-support@^0.4.15: dependencies: source-map "^0.5.6" +source-map-support@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.0.tgz#2018a7ad2bdf8faf2691e5fddab26bed5a2bacab" + dependencies: + source-map "^0.6.0" + source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -7762,7 +8251,7 @@ source-map@0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" -source-map@0.5.x, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: +source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -7772,6 +8261,10 @@ source-map@^0.1.41: dependencies: amdefine ">=0.0.4" +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + source-map@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" @@ -7914,6 +8407,13 @@ string-length@^1.0.0: dependencies: strip-ansi "^3.0.0" +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -7971,16 +8471,16 @@ strip-ansi@^4.0.0, strip-ansi@~4.0.0: dependencies: ansi-regex "^3.0.0" +strip-bom@3.0.0, strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" dependencies: is-utf8 "^0.2.0" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -8013,7 +8513,7 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.2.3: +supports-color@^3.1.2, supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: @@ -8042,6 +8542,10 @@ symbol-observable@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" +symbol-tree@^3.2.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" + systemjs-plugin-css@^0.1.36: version "0.1.36" resolved "https://registry.yarnpkg.com/systemjs-plugin-css/-/systemjs-plugin-css-0.1.36.tgz#1ab38811ae6dd71190cefe33f1fe41976a09387d" @@ -8111,6 +8615,16 @@ term-size@^1.2.0: dependencies: execa "^0.7.0" +test-exclude@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26" + dependencies: + arrify "^1.0.1" + micromatch "^2.3.11" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + "tether-drop@https://github.com/torkelo/drop": version "1.5.0" resolved "https://github.com/torkelo/drop#fc83ca88db0076fbf6359cbe1743a9ef0f1ee6e1" @@ -8125,6 +8639,10 @@ text-table@^0.2.0, text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + throttleit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" @@ -8175,6 +8693,10 @@ tmp@0.0.31, tmp@0.0.x: dependencies: os-tmpdir "~1.0.1" +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + to-array@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" @@ -8220,12 +8742,16 @@ toposort@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.4.tgz#a86107690cbee8cae43b349d2f60162500924dfc" -tough-cookie@~2.3.0: +tough-cookie@^2.3.2, tough-cookie@~2.3.0: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" dependencies: punycode "^1.4.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -8242,6 +8768,21 @@ tryor@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b" +ts-jest@^21.1.3: + version "21.1.3" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-21.1.3.tgz#cc3c552e7e8a67db9ededc28c00ae98223614ddc" + dependencies: + babel-core "^6.24.1" + babel-plugin-istanbul "^4.1.4" + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-preset-jest "^21.2.0" + fs-extra "^4.0.0" + jest-config "^21.2.1" + jest-util "^21.2.1" + pkg-dir "^2.0.0" + source-map-support "^0.5.0" + yargs "^9.0.1" + ts-loader@^2.3.7: version "2.3.7" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-2.3.7.tgz#a9028ced473bee12f28a75f9c5b139979d33f2fc" @@ -8626,6 +9167,19 @@ walkdir@^0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.0.11.tgz#a16d025eb931bd03b52f308caed0f40fcebe9532" +walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + dependencies: + makeerror "1.0.x" + +watch@~0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986" + dependencies: + exec-sh "^0.2.0" + minimist "^1.2.0" + watchpack@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac" @@ -8640,6 +9194,14 @@ wcwidth@^1.0.0: dependencies: defaults "^1.0.3" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + +webidl-conversions@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + webpack-bundle-analyzer@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.9.0.tgz#b58bc34cc30b27ffdbaf3d00bf27aba6fa29c6e3" @@ -8721,10 +9283,23 @@ webpack@^3.6.0: webpack-sources "^1.0.1" yargs "^8.0.2" +whatwg-encoding@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz#3c6c451a198ee7aec55b1ec61d0920c67801a5f4" + dependencies: + iconv-lite "0.4.13" + whatwg-fetch@>=0.10.0: version "2.0.3" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" +whatwg-url@^4.3.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0" + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + whet.extend@~0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" @@ -8737,7 +9312,7 @@ which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@1, which@^1.1.1, which@^1.2.1, which@^1.2.10, which@^1.2.14, which@^1.2.4, which@^1.2.9, which@^1.3.0, which@~1.3.0: +which@1, which@^1.2.1, which@^1.2.10, which@^1.2.12, which@^1.2.14, which@^1.2.4, which@^1.2.9, which@^1.3.0, which@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: @@ -8781,15 +9356,15 @@ wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" -wordwrap@^1.0.0, wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" -worker-farm@~1.5.0: +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +worker-farm@^1.3.1, worker-farm@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.5.0.tgz#adfdf0cd40581465ed0a1f648f9735722afd5c8d" dependencies: @@ -8807,7 +9382,7 @@ wrappy@1, wrappy@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -write-file-atomic@^2.0.0, write-file-atomic@~2.1.0: +write-file-atomic@^2.0.0, write-file-atomic@^2.1.0, write-file-atomic@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.1.0.tgz#1769f4b551eedce419f0505deae2e26763542d37" dependencies: @@ -8847,6 +9422,10 @@ xml-char-classes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d" +xml-name-validator@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" + xmlbuilder@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-3.1.0.tgz#2c86888f2d4eade850fa38ca7f7223f7209516e1" @@ -8921,6 +9500,24 @@ yargs@^8.0.2: y18n "^3.2.1" yargs-parser "^7.0.0" +yargs@^9.0.0, yargs@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c" + dependencies: + camelcase "^4.1.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + read-pkg-up "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^7.0.0" + yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"