From c346aca26d9183fe3245db5be1c81fcf2c340f8e Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 11 Apr 2017 16:30:20 -0700 Subject: [PATCH 001/145] allow setting the database --- .../plugins/datasource/influxdb/datasource.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 98c7ba87bd2..ddce4d8e8f9 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -157,7 +157,7 @@ export default class InfluxDatasource { return false; }; - metricFindQuery(query) { + metricFindQuery(query: string, db?: string) { var interpolated = this.templateSrv.replace(query, null, 'regex'); return this._seriesQuery(interpolated) @@ -176,10 +176,10 @@ export default class InfluxDatasource { return this.metricFindQuery(query); } - _seriesQuery(query) { + _seriesQuery(query: string, db?: string) { if (!query) { return this.$q.when({results: []}); } - return this._influxRequest('GET', '/query', {q: query, epoch: 'ms'}); + return this._influxRequest('GET', '/query', {q: query, epoch: 'ms'}, db); } serializeParams(params) { @@ -207,19 +207,19 @@ export default class InfluxDatasource { }); } - _influxRequest(method, url, data) { - var self = this; - - var currentUrl = self.urls.shift(); - self.urls.push(currentUrl); + _influxRequest(method: string, url: string, data: any, db?: string) { + var currentUrl = this.urls.shift(); + this.urls.push(currentUrl); var params: any = { - u: self.username, - p: self.password, + u: this.username, + p: this.password, }; - if (self.database) { - params.db = self.database; + if (db) { + params.db = db; + } else if (this.database) { + params.db = this.database; } if (method === 'GET') { @@ -241,8 +241,8 @@ export default class InfluxDatasource { if (this.basicAuth || this.withCredentials) { options.withCredentials = true; } - if (self.basicAuth) { - options.headers.Authorization = self.basicAuth; + if (this.basicAuth) { + options.headers.Authorization = this.basicAuth; } return this.backendSrv.datasourceRequest(options).then(result => { From 7009184d6eec4f1612ca036ad32f68b33cdafa3d Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 12 Apr 2017 09:02:06 -0700 Subject: [PATCH 002/145] pass database parameter in the options --- .../plugins/datasource/influxdb/datasource.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index ddce4d8e8f9..37a28d45142 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -157,10 +157,10 @@ export default class InfluxDatasource { return false; }; - metricFindQuery(query: string, db?: string) { + metricFindQuery(query: string, options?: any) { var interpolated = this.templateSrv.replace(query, null, 'regex'); - return this._seriesQuery(interpolated) + return this._seriesQuery(interpolated, options) .then(_.curry(this.responseParser.parse)(query)); } @@ -176,10 +176,10 @@ export default class InfluxDatasource { return this.metricFindQuery(query); } - _seriesQuery(query: string, db?: string) { + _seriesQuery(query: string, options?: any) { if (!query) { return this.$q.when({results: []}); } - return this._influxRequest('GET', '/query', {q: query, epoch: 'ms'}, db); + return this._influxRequest('GET', '/query', {q: query, epoch: 'ms'}, options); } serializeParams(params) { @@ -207,7 +207,7 @@ export default class InfluxDatasource { }); } - _influxRequest(method: string, url: string, data: any, db?: string) { + _influxRequest(method: string, url: string, data: any, options?: any) { var currentUrl = this.urls.shift(); this.urls.push(currentUrl); @@ -216,8 +216,8 @@ export default class InfluxDatasource { p: this.password, }; - if (db) { - params.db = db; + if (options && options.database) { + params.db = options.database; } else if (this.database) { params.db = this.database; } @@ -227,7 +227,7 @@ export default class InfluxDatasource { data = null; } - var options: any = { + var req: any = { method: method, url: currentUrl + url, params: params, @@ -237,15 +237,15 @@ export default class InfluxDatasource { paramSerializer: this.serializeParams, }; - options.headers = options.headers || {}; + req.headers = req.headers || {}; if (this.basicAuth || this.withCredentials) { - options.withCredentials = true; + req.withCredentials = true; } if (this.basicAuth) { - options.headers.Authorization = this.basicAuth; + req.headers.Authorization = this.basicAuth; } - return this.backendSrv.datasourceRequest(options).then(result => { + return this.backendSrv.datasourceRequest(req).then(result => { return result.data; }, function(err) { if (err.status !== 0 || err.status >= 300) { From 16819da08d9a353fc6599d72836d36019d8ca5ef Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 18 Apr 2017 09:27:25 -0700 Subject: [PATCH 003/145] pass the options along with a _seriesQuery --- public/app/plugins/datasource/influxdb/datasource.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 37a28d45142..929c673433b 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -85,7 +85,7 @@ export default class InfluxDatasource { // replace templated variables allQueries = this.templateSrv.replace(allQueries, scopedVars); - return this._seriesQuery(allQueries).then((data): any => { + return this._seriesQuery(allQueries, options).then((data): any => { if (!data || !data.results) { return []; } @@ -131,7 +131,7 @@ export default class InfluxDatasource { var query = options.annotation.query.replace('$timeFilter', timeFilter); query = this.templateSrv.replace(query, null, 'regex'); - return this._seriesQuery(query).then(data => { + return this._seriesQuery(query, options).then(data => { if (!data || !data.results || !data.results[0]) { throw { message: 'No results in response from InfluxDB' }; } @@ -167,13 +167,13 @@ export default class InfluxDatasource { getTagKeys(options) { var queryBuilder = new InfluxQueryBuilder({measurement: '', tags: []}, this.database); var query = queryBuilder.buildExploreQuery('TAG_KEYS'); - return this.metricFindQuery(query); + return this.metricFindQuery(query, options); } getTagValues(options) { var queryBuilder = new InfluxQueryBuilder({measurement: '', tags: []}, this.database); var query = queryBuilder.buildExploreQuery('TAG_VALUES', options.key); - return this.metricFindQuery(query); + return this.metricFindQuery(query, options); } _seriesQuery(query: string, options?: any) { From 15e84a1c692f5daf4984da15aa2dad696b257fad Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 26 Apr 2017 15:41:10 -0700 Subject: [PATCH 004/145] use targets[0] as the options --- public/app/plugins/datasource/influxdb/datasource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index ec472898583..ac18992b16d 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -85,7 +85,7 @@ export default class InfluxDatasource { // replace templated variables allQueries = this.templateSrv.replace(allQueries, scopedVars); - return this._seriesQuery(allQueries, options).then((data): any => { + return this._seriesQuery(allQueries, targets[0]).then((data): any => { if (!data || !data.results) { return []; } From 26e62b4d0690b19962e23a56840ad1cad6d226e3 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 1 May 2017 20:28:56 -0700 Subject: [PATCH 005/145] use the original options parameter --- public/app/plugins/datasource/influxdb/datasource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index ac18992b16d..ec472898583 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -85,7 +85,7 @@ export default class InfluxDatasource { // replace templated variables allQueries = this.templateSrv.replace(allQueries, scopedVars); - return this._seriesQuery(allQueries, targets[0]).then((data): any => { + return this._seriesQuery(allQueries, options).then((data): any => { if (!data || !data.results) { return []; } From 967efea520d61bc6a9cf2d0cfa29c90ce018a658 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 22 Sep 2017 13:28:53 +0200 Subject: [PATCH 006/145] fix merge issue --- public/app/plugins/datasource/influxdb/datasource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 9e568866579..bab2d597a67 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -211,7 +211,7 @@ export default class InfluxDatasource { if (this.username) { params.u = this.username; - params.u = this.password; + params.p = this.password; } if (options && options.database) { From 4440133f4dd9b4ca19ea540815683ff0428a7944 Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 18 Oct 2017 23:33:10 +0200 Subject: [PATCH 007/145] Add a setting to allow DB queries --- pkg/api/pluginproxy/ds_proxy.go | 4 +++- public/app/plugins/datasource/influxdb/datasource.ts | 5 +++++ .../app/plugins/datasource/influxdb/partials/config.html | 8 +++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pkg/api/pluginproxy/ds_proxy.go b/pkg/api/pluginproxy/ds_proxy.go index faac8c03c62..72309c5c855 100644 --- a/pkg/api/pluginproxy/ds_proxy.go +++ b/pkg/api/pluginproxy/ds_proxy.go @@ -166,7 +166,9 @@ func (proxy *DataSourceProxy) getDirector() func(req *http.Request) { func (proxy *DataSourceProxy) validateRequest() error { if proxy.ds.Type == m.DS_INFLUXDB { if proxy.ctx.Query("db") != proxy.ds.Database { - return errors.New("Datasource is not configured to allow this database") + if(!proxy.ds.JsonData.Get("allowDatabaseQuery").MustBool(false)) { + return errors.New("Datasource is not configured to allow this database"); + } } } diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 7b53d0e1d97..80294396395 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -17,6 +17,7 @@ export default class InfluxDatasource { basicAuth: any; withCredentials: any; interval: any; + allowDatabaseQuery: boolean; supportAnnotations: boolean; supportMetrics: boolean; responseParser: any; @@ -35,6 +36,7 @@ export default class InfluxDatasource { this.basicAuth = instanceSettings.basicAuth; this.withCredentials = instanceSettings.withCredentials; this.interval = (instanceSettings.jsonData || {}).timeInterval; + this.allowDatabaseQuery = (instanceSettings.jsonData || {}).allowDatabaseQuery === true; this.supportAnnotations = true; this.supportMetrics = true; this.responseParser = new ResponseParser(); @@ -214,6 +216,9 @@ export default class InfluxDatasource { if (options && options.database) { params.db = options.database; + if (params.db !== this.database && !this.allowDatabaseQuery) { + return this.$q.reject( { message: 'This datasource does not allow changing database' } ); + } } else if (this.database) { params.db = this.database; } diff --git a/public/app/plugins/datasource/influxdb/partials/config.html b/public/app/plugins/datasource/influxdb/partials/config.html index 9cb6f5ba749..4b861083928 100644 --- a/public/app/plugins/datasource/influxdb/partials/config.html +++ b/public/app/plugins/datasource/influxdb/partials/config.html @@ -23,10 +23,16 @@ + +
- Min time interval + Min time interval A lower limit for the auto group by time interval. Recommended to be set to write frequency, From fb9c714a9a0e68b8572b94852d880afd7b7919a2 Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 18 Oct 2017 23:49:33 +0200 Subject: [PATCH 008/145] run go fmt --- pkg/api/pluginproxy/ds_proxy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/api/pluginproxy/ds_proxy.go b/pkg/api/pluginproxy/ds_proxy.go index 72309c5c855..200a1a02bcc 100644 --- a/pkg/api/pluginproxy/ds_proxy.go +++ b/pkg/api/pluginproxy/ds_proxy.go @@ -166,8 +166,8 @@ func (proxy *DataSourceProxy) getDirector() func(req *http.Request) { func (proxy *DataSourceProxy) validateRequest() error { if proxy.ds.Type == m.DS_INFLUXDB { if proxy.ctx.Query("db") != proxy.ds.Database { - if(!proxy.ds.JsonData.Get("allowDatabaseQuery").MustBool(false)) { - return errors.New("Datasource is not configured to allow this database"); + if !proxy.ds.JsonData.Get("allowDatabaseQuery").MustBool(false) { + return errors.New("Datasource is not configured to allow this database") } } } From 7a7e40739349edb8a65719cd2fa2f1bba4592587 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 15 Mar 2018 00:02:38 +0100 Subject: [PATCH 009/145] fix lint problems From cae9c28f7031ff686a78b5c0a910c9532d375b8b Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 15 Mar 2018 00:03:47 +0100 Subject: [PATCH 010/145] fix lint problems --- public/sass/_variables.scss | 29 +- public/sass/base/_forms.scss | 44 +- public/sass/base/_grafana_icons.scss | 123 +- public/sass/base/_normalize.scss | 20 +- public/sass/base/_reboot.scss | 8 +- public/sass/base/font-awesome/_core.scss | 3 +- public/sass/base/font-awesome/_mixins.scss | 7 +- public/sass/base/font-awesome/_path.scss | 19 +- public/sass/base/font-awesome/_variables.scss | 1576 ++++++++--------- public/sass/components/_drop.scss | 12 +- public/sass/components/_filter-list.scss | 6 +- public/sass/components/_footer.scss | 4 +- public/sass/components/_json_explorer.scss | 6 +- public/sass/components/_jsontree.scss | 4 +- .../components/_panel_gettingstarted.scss | 4 +- public/sass/components/_row.scss | 2 +- public/sass/components/_shortcuts.scss | 2 +- public/sass/components/_switch.scss | 8 +- public/sass/components/_tabs.scss | 9 +- public/sass/components/_timepicker.scss | 4 +- public/sass/grafana.dark.scss | 6 +- public/sass/mixins/_drop_element.scss | 52 +- public/sass/mixins/_forms.scss | 3 +- public/sass/mixins/_mixins.scss | 63 +- public/sass/pages/_login.scss | 2 +- public/sass/pages/_playlist.scss | 4 +- public/sass/utils/_validation.scss | 2 +- public/test/core/utils/version_specs.ts | 36 +- public/test/jest-shim.ts | 3 +- public/test/mocks/backend_srv.ts | 5 +- public/test/specs/helpers.ts | 22 +- .../bradfitz/gomemcache/memcache/memcache.go | 2 +- .../github.com/hashicorp/go-plugin/client.go | 2 +- .../hashicorp/go-plugin/rpc_client.go | 2 +- .../hashicorp/go-plugin/rpc_server.go | 2 +- .../sergi/go-diff/diffmatchpatch/diff.go | 26 +- .../sergi/go-diff/diffmatchpatch/patch.go | 4 +- vendor/golang.org/x/net/http2/transport.go | 4 +- vendor/golang.org/x/text/language/gen.go | 2 +- vendor/golang.org/x/text/language/lookup.go | 56 +- vendor/golang.org/x/text/language/tables.go | 6 +- vendor/golang.org/x/text/unicode/cldr/cldr.go | 2 +- .../golang.org/x/text/unicode/cldr/resolve.go | 4 +- .../golang.org/x/text/unicode/cldr/slice.go | 2 +- .../x/text/unicode/norm/maketables.go | 2 +- vendor/gopkg.in/macaron.v1/context.go | 2 +- 46 files changed, 1144 insertions(+), 1062 deletions(-) diff --git a/public/sass/_variables.scss b/public/sass/_variables.scss index 5a5f495470d..f46cacb0dd1 100644 --- a/public/sass/_variables.scss +++ b/public/sass/_variables.scss @@ -53,9 +53,9 @@ $enable-flex: true; // Typography // ------------------------- -$font-family-sans-serif: 'Roboto', Helvetica, Arial, sans-serif; -$font-family-serif: Georgia, 'Times New Roman', Times, serif; -$font-family-monospace: Menlo, Monaco, Consolas, 'Courier New', monospace; +$font-family-sans-serif: "Roboto", Helvetica, Arial, sans-serif; +$font-family-serif: Georgia, "Times New Roman", Times, serif; +$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; $font-family-base: $font-family-sans-serif !default; $font-size-root: 14px !default; @@ -90,7 +90,7 @@ $lead-font-size: 1.25rem !default; $lead-font-weight: 300 !default; $headings-margin-bottom: ($spacer / 2) !default; -$headings-font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; +$headings-font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; $headings-font-weight: 400 !default; $headings-line-height: 1.1 !default; @@ -152,9 +152,16 @@ $input-padding-y-sm: 4px !default; $input-padding-x-lg: 20px !default; $input-padding-y-lg: 10px !default; -$input-height: (($font-size-base * $line-height-base) + ($input-padding-y * 2)) !default; -$input-height-lg: ( ($font-size-lg * $line-height-lg) + ($input-padding-y-lg * 2)) !default; -$input-height-sm: ( ($font-size-sm * $line-height-sm) + ($input-padding-y-sm * 2)) !default; +$input-height: (($font-size-base * $line-height-base) + ($input-padding-y * 2)) + !default; +$input-height-lg: ( + ($font-size-lg * $line-height-lg) + ($input-padding-y-lg * 2) + ) + !default; +$input-height-sm: ( + ($font-size-sm * $line-height-sm) + ($input-padding-y-sm * 2) + ) + !default; $form-group-margin-bottom: $spacer-y !default; $gf-form-margin: 0.2rem; @@ -214,9 +221,9 @@ $panel-padding: 0px 10px 5px 10px; $tabs-padding: 10px 15px 9px; $external-services: ( - github: (bgColor: #464646, borderColor: #393939, icon: ''), - google: (bgColor: #e84d3c, borderColor: #b83e31, icon: ''), - grafanacom: (bgColor: inherit, borderColor: #393939, icon: ''), - oauth: (bgColor: inherit, borderColor: #393939, icon: '') + github: (bgColor: #464646, borderColor: #393939, icon: ""), + google: (bgColor: #e84d3c, borderColor: #b83e31, icon: ""), + grafanacom: (bgColor: inherit, borderColor: #393939, icon: ""), + oauth: (bgColor: inherit, borderColor: #393939, icon: "") ) !default; diff --git a/public/sass/base/_forms.scss b/public/sass/base/_forms.scss index 239bb8d4e1e..3197eb57991 100644 --- a/public/sass/base/_forms.scss +++ b/public/sass/base/_forms.scss @@ -58,19 +58,19 @@ textarea { } // Reset width of input images, buttons, radios, checkboxes -input[type='file'], -input[type='image'], -input[type='submit'], -input[type='reset'], -input[type='button'], -input[type='radio'], -input[type='checkbox'] { +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { width: auto; // Override of generic input selector } // Set the height of select and file controls to match text inputs select, -input[type='file'] { +input[type="file"] { height: $input-height; /* In IE7, the height of the select element cannot be changed by height, only font-size */ line-height: $input-height; } @@ -90,19 +90,19 @@ select[size] { // Focus for select, file, radio, and checkbox select:focus, -input[type='file']:focus, -input[type='radio']:focus, -input[type='checkbox']:focus { +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { @include tab-focus(); } // not a big fan of number fields -input[type='number']::-webkit-outer-spin-button, -input[type='number']::-webkit-inner-spin-button { +input[type="number"]::-webkit-outer-spin-button, +input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } -input[type='number'] { +input[type="number"] { -moz-appearance: textfield; } // Placeholder @@ -155,15 +155,15 @@ textarea[readonly] { } // Explicitly reset the colors here -input[type='radio'][disabled], -input[type='checkbox'][disabled], -input[type='radio'][readonly], -input[type='checkbox'][readonly] { +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { cursor: $cursor-disabled; background-color: transparent; } -input[type='text'].input-fluid { +input[type="text"].input-fluid { width: 100%; box-sizing: border-box; padding: 10px; @@ -172,7 +172,7 @@ input[type='text'].input-fluid { height: 100%; } -input[type='checkbox'].cr1 { +input[type="checkbox"].cr1 { display: none; } @@ -194,7 +194,7 @@ label.cr1 { cursor: pointer; } -input[type='checkbox'].cr1:checked + label { +input[type="checkbox"].cr1:checked + label { background: url($checkboxImageUrl) 0px -18px no-repeat; } @@ -203,7 +203,7 @@ input[type='checkbox'].cr1:checked + label { display: block; overflow: hidden; padding-right: 10px; - input[type='text'] { + input[type="text"] { width: 100%; padding: 5px 6px; height: 100%; diff --git a/public/sass/base/_grafana_icons.scss b/public/sass/base/_grafana_icons.scss index 73574ff9efc..55e57f6d087 100644 --- a/public/sass/base/_grafana_icons.scss +++ b/public/sass/base/_grafana_icons.scss @@ -1,17 +1,18 @@ @font-face { - font-family: 'grafana-icons'; - src: url('../fonts/grafana-icons.eot?okx5td'); - src: url('../fonts/grafana-icons.eot?okx5td#iefix') format('embedded-opentype'), - url('../fonts/grafana-icons.ttf?okx5td') format('truetype'), - url('../fonts/grafana-icons.woff?okx5td') format('woff'), - url('../fonts/grafana-icons.svg?okx5td#grafana-icons') format('svg'); + font-family: "grafana-icons"; + src: url("../fonts/grafana-icons.eot?okx5td"); + src: url("../fonts/grafana-icons.eot?okx5td#iefix") + format("embedded-opentype"), + url("../fonts/grafana-icons.ttf?okx5td") format("truetype"), + url("../fonts/grafana-icons.woff?okx5td") format("woff"), + url("../fonts/grafana-icons.svg?okx5td#grafana-icons") format("svg"); font-weight: normal; font-style: normal; } .icon-gf { /* use !important to prevent issues with browser extensions that change fonts */ - font-family: 'grafana-icons' !important; + font-family: "grafana-icons" !important; speak: none; font-style: normal; font-weight: normal; @@ -36,165 +37,165 @@ } .icon-gf-raintank_wordmark:before { - content: '\e600'; + content: "\e600"; } .micon-gf-raintank_icn:before { - content: '\e601'; + content: "\e601"; } .icon-gf-raintank_r-icn:before { - content: '\e905'; + content: "\e905"; } .icon-gf-check-alt:before { - content: '\e603'; + content: "\e603"; } .icon-gf-check:before { - content: '\e604'; + content: "\e604"; } .icon-gf-collector:before { - content: '\e605'; + content: "\e605"; } .icon-gf-dashboard:before { - content: '\e606'; + content: "\e606"; } .icon-gf-panel:before { - content: '\e904'; + content: "\e904"; } .icon-gf-datasources:before { - content: '\e607'; + content: "\e607"; } .icon-gf-endpoint-tiny:before { - content: '\e608'; + content: "\e608"; } .icon-gf-endpoint:before { - content: '\e609'; + content: "\e609"; } .icon-gf-page:before { - content: '\e908'; + content: "\e908"; } .icon-gf-filter:before { - content: '\e60a'; + content: "\e60a"; } .icon-gf-status:before { - content: '\e60b'; + content: "\e60b"; } .icon-gf-monitoring:before { - content: '\e60c'; + content: "\e60c"; } .icon-gf-monitoring-tiny:before { - content: '\e620'; + content: "\e620"; } .icon-gf-jump-to-dashboard:before { - content: '\e60d'; + content: "\e60d"; } .icon-gf-warn, .icon-gf-warning:before { - content: '\e60e'; + content: "\e60e"; } .icon-gf-nodata:before { - content: '\e60f'; + content: "\e60f"; } .icon-gf-critical:before { - content: '\e610'; + content: "\e610"; } .icon-gf-crit:before { - content: '\e610'; + content: "\e610"; } .icon-gf-online:before { - content: '\e611'; + content: "\e611"; } .icon-gf-event-error:before { - content: '\e623'; + content: "\e623"; } .icon-gf-event:before { - content: '\e624'; + content: "\e624"; } .icon-gf-sadface:before { - content: '\e907'; + content: "\e907"; } .icon-gf-private-collector:before { - content: '\e612'; + content: "\e612"; } .icon-gf-alert:before { - content: '\e61f'; + content: "\e61f"; } .icon-gf-alert-disabled:before { - content: '\e621'; + content: "\e621"; } .icon-gf-refresh:before { - content: '\e613'; + content: "\e613"; } .icon-gf-save:before { - content: '\e614'; + content: "\e614"; } .icon-gf-share:before { - content: '\e616'; + content: "\e616"; } .icon-gf-star:before { - content: '\e617'; + content: "\e617"; } .icon-gf-search:before { - content: '\e618'; + content: "\e618"; } .icon-gf-settings:before { - content: '\e615'; + content: "\e615"; } .icon-gf-add:before { - content: '\e619'; + content: "\e619"; } .icon-gf-remove:before { - content: '\e61a'; + content: "\e61a"; } .icon-gf-video:before { - content: '\e61b'; + content: "\e61b"; } .icon-gf-bulk_action:before { - content: '\e61c'; + content: "\e61c"; } .icon-gf-grabber:before { - content: '\e90b'; + content: "\e90b"; } .icon-gf-users:before { - content: '\e622'; + content: "\e622"; } .icon-gf-globe:before { - content: '\e61d'; + content: "\e61d"; } .icon-gf-snapshot:before { - content: '\e61e'; + content: "\e61e"; } .icon-gf-play-grafana-icon:before { - content: '\e629'; + content: "\e629"; } .icon-gf-grafana-icon:before { - content: '\e625'; + content: "\e625"; } .icon-gf-email:before { - content: '\e628'; + content: "\e628"; } .icon-gf-stopwatch:before { - content: '\e626'; + content: "\e626"; } .icon-gf-skull:before { - content: '\e900'; + content: "\e900"; } .icon-gf-probe:before { - content: '\e901'; + content: "\e901"; } .icon-gf-apps:before { - content: '\e902'; + content: "\e902"; } .icon-gf-scale:before { - content: '\e906'; + content: "\e906"; } .icon-gf-pending:before { - content: '\e909'; + content: "\e909"; } .icon-gf-verified:before { - content: '\e90a'; + content: "\e90a"; } .icon-gf-worldping:before { - content: '\e627'; + content: "\e627"; } .icon-gf-grafana_wordmark:before { - content: '\e903'; + content: "\e903"; } diff --git a/public/sass/base/_normalize.scss b/public/sass/base/_normalize.scss index fff6ad95d7c..057f5c63602 100644 --- a/public/sass/base/_normalize.scss +++ b/public/sass/base/_normalize.scss @@ -291,9 +291,9 @@ select { // button, -html input[type='button'], -// 1 input[type='reset'], -input[type='submit'] { +html input[type="button"], +// 1 input[type="reset"], +input[type="submit"] { -webkit-appearance: button; // 2 cursor: pointer; // 3 } @@ -334,8 +334,8 @@ input { // 2. Remove excess padding in IE 8/9/10. // -input[type='checkbox'], -input[type='radio'] { +input[type="checkbox"], +input[type="radio"] { box-sizing: border-box; // 1 padding: 0; // 2 } @@ -346,8 +346,8 @@ input[type='radio'] { // decrement button to change from `default` to `text`. // -input[type='number']::-webkit-inner-spin-button, -input[type='number']::-webkit-outer-spin-button { +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { height: auto; } @@ -355,7 +355,7 @@ input[type='number']::-webkit-outer-spin-button { // Address `appearance` set to `searchfield` in Safari and Chrome. // -input[type='search'] { +input[type="search"] { -webkit-appearance: textfield; } @@ -365,8 +365,8 @@ input[type='search'] { // padding (and `textfield` appearance). // -input[type='search']::-webkit-search-cancel-button, -input[type='search']::-webkit-search-decoration { +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } diff --git a/public/sass/base/_reboot.scss b/public/sass/base/_reboot.scss index ccbb17a07b6..e34bdfdb9ed 100644 --- a/public/sass/base/_reboot.scss +++ b/public/sass/base/_reboot.scss @@ -87,7 +87,7 @@ body { // might still respond to pointer events. // // Credit: https://github.com/suitcss/base -[tabindex='-1']:focus { +[tabindex="-1"]:focus { outline: none !important; } @@ -214,7 +214,7 @@ img { // for traditionally non-focusable elements with role="button" // see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile -[role='button'] { +[role="button"] { cursor: pointer; } @@ -231,7 +231,7 @@ img { a, area, button, -[role='button'], +[role="button"], input, label, select, @@ -320,7 +320,7 @@ legend { // border: 0; } -input[type='search'] { +input[type="search"] { // This overrides the extra rounded corners on search inputs in iOS so that our // `.form-control` class can properly style them. Note that this cannot simply // be added to `.form-control` as it's not specific enough. For details, see diff --git a/public/sass/base/font-awesome/_core.scss b/public/sass/base/font-awesome/_core.scss index a5411f108af..9940b63463d 100644 --- a/public/sass/base/font-awesome/_core.scss +++ b/public/sass/base/font-awesome/_core.scss @@ -3,7 +3,8 @@ .#{$fa-css-prefix} { display: inline-block; - font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration + font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} + FontAwesome; // shortening font declaration font-size: inherit; // can't have font-size inherit on line above, so need to override text-rendering: auto; // optimizelegibility throws things off #1094 -webkit-font-smoothing: antialiased; diff --git a/public/sass/base/font-awesome/_mixins.scss b/public/sass/base/font-awesome/_mixins.scss index f2a92c1e922..19771009107 100644 --- a/public/sass/base/font-awesome/_mixins.scss +++ b/public/sass/base/font-awesome/_mixins.scss @@ -3,7 +3,8 @@ @mixin fa-icon() { display: inline-block; - font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration + font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} + FontAwesome; // shortening font declaration font-size: inherit; // can't have font-size inherit on line above, so need to override text-rendering: auto; // optimizelegibility throws things off #1094 -webkit-font-smoothing: antialiased; @@ -11,14 +12,14 @@ } @mixin fa-icon-rotate($degrees, $rotation) { - -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})'; + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; -webkit-transform: rotate($degrees); -ms-transform: rotate($degrees); transform: rotate($degrees); } @mixin fa-icon-flip($horiz, $vert, $rotation) { - -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)'; + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; -webkit-transform: scale($horiz, $vert); -ms-transform: scale($horiz, $vert); transform: scale($horiz, $vert); diff --git a/public/sass/base/font-awesome/_path.scss b/public/sass/base/font-awesome/_path.scss index d452de2170a..0316afa161d 100644 --- a/public/sass/base/font-awesome/_path.scss +++ b/public/sass/base/font-awesome/_path.scss @@ -2,13 +2,18 @@ * -------------------------- */ @font-face { - font-family: 'FontAwesome'; - src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); - src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), - url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), - url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), - url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), - url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); + font-family: "FontAwesome"; + src: url("#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}"); + src: url("#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}") + format("embedded-opentype"), + url("#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}") + format("woff2"), + url("#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}") + format("woff"), + url("#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}") + format("truetype"), + url("#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular") + format("svg"); // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts font-weight: normal; font-style: normal; diff --git a/public/sass/base/font-awesome/_variables.scss b/public/sass/base/font-awesome/_variables.scss index 1e91e831e66..a4bd3cd87b9 100644 --- a/public/sass/base/font-awesome/_variables.scss +++ b/public/sass/base/font-awesome/_variables.scss @@ -1,799 +1,799 @@ // Variables // -------------------------- -$fa-font-path: '../fonts' !default; +$fa-font-path: "../fonts" !default; $fa-font-size-base: 14px !default; $fa-line-height-base: 1 !default; //$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts" !default; // for referencing Bootstrap CDN font files directly $fa-css-prefix: fa !default; -$fa-version: '4.7.0' !default; +$fa-version: "4.7.0" !default; $fa-border-color: #eee !default; $fa-inverse: #fff !default; $fa-li-width: (30em / 14) !default; -$fa-var-500px: '\f26e'; -$fa-var-address-book: '\f2b9'; -$fa-var-address-book-o: '\f2ba'; -$fa-var-address-card: '\f2bb'; -$fa-var-address-card-o: '\f2bc'; -$fa-var-adjust: '\f042'; -$fa-var-adn: '\f170'; -$fa-var-align-center: '\f037'; -$fa-var-align-justify: '\f039'; -$fa-var-align-left: '\f036'; -$fa-var-align-right: '\f038'; -$fa-var-amazon: '\f270'; -$fa-var-ambulance: '\f0f9'; -$fa-var-american-sign-language-interpreting: '\f2a3'; -$fa-var-anchor: '\f13d'; -$fa-var-android: '\f17b'; -$fa-var-angellist: '\f209'; -$fa-var-angle-double-down: '\f103'; -$fa-var-angle-double-left: '\f100'; -$fa-var-angle-double-right: '\f101'; -$fa-var-angle-double-up: '\f102'; -$fa-var-angle-down: '\f107'; -$fa-var-angle-left: '\f104'; -$fa-var-angle-right: '\f105'; -$fa-var-angle-up: '\f106'; -$fa-var-apple: '\f179'; -$fa-var-archive: '\f187'; -$fa-var-area-chart: '\f1fe'; -$fa-var-arrow-circle-down: '\f0ab'; -$fa-var-arrow-circle-left: '\f0a8'; -$fa-var-arrow-circle-o-down: '\f01a'; -$fa-var-arrow-circle-o-left: '\f190'; -$fa-var-arrow-circle-o-right: '\f18e'; -$fa-var-arrow-circle-o-up: '\f01b'; -$fa-var-arrow-circle-right: '\f0a9'; -$fa-var-arrow-circle-up: '\f0aa'; -$fa-var-arrow-down: '\f063'; -$fa-var-arrow-left: '\f060'; -$fa-var-arrow-right: '\f061'; -$fa-var-arrow-up: '\f062'; -$fa-var-arrows: '\f047'; -$fa-var-arrows-alt: '\f0b2'; -$fa-var-arrows-h: '\f07e'; -$fa-var-arrows-v: '\f07d'; -$fa-var-asl-interpreting: '\f2a3'; -$fa-var-assistive-listening-systems: '\f2a2'; -$fa-var-asterisk: '\f069'; -$fa-var-at: '\f1fa'; -$fa-var-audio-description: '\f29e'; -$fa-var-automobile: '\f1b9'; -$fa-var-backward: '\f04a'; -$fa-var-balance-scale: '\f24e'; -$fa-var-ban: '\f05e'; -$fa-var-bandcamp: '\f2d5'; -$fa-var-bank: '\f19c'; -$fa-var-bar-chart: '\f080'; -$fa-var-bar-chart-o: '\f080'; -$fa-var-barcode: '\f02a'; -$fa-var-bars: '\f0c9'; -$fa-var-bath: '\f2cd'; -$fa-var-bathtub: '\f2cd'; -$fa-var-battery: '\f240'; -$fa-var-battery-0: '\f244'; -$fa-var-battery-1: '\f243'; -$fa-var-battery-2: '\f242'; -$fa-var-battery-3: '\f241'; -$fa-var-battery-4: '\f240'; -$fa-var-battery-empty: '\f244'; -$fa-var-battery-full: '\f240'; -$fa-var-battery-half: '\f242'; -$fa-var-battery-quarter: '\f243'; -$fa-var-battery-three-quarters: '\f241'; -$fa-var-bed: '\f236'; -$fa-var-beer: '\f0fc'; -$fa-var-behance: '\f1b4'; -$fa-var-behance-square: '\f1b5'; -$fa-var-bell: '\f0f3'; -$fa-var-bell-o: '\f0a2'; -$fa-var-bell-slash: '\f1f6'; -$fa-var-bell-slash-o: '\f1f7'; -$fa-var-bicycle: '\f206'; -$fa-var-binoculars: '\f1e5'; -$fa-var-birthday-cake: '\f1fd'; -$fa-var-bitbucket: '\f171'; -$fa-var-bitbucket-square: '\f172'; -$fa-var-bitcoin: '\f15a'; -$fa-var-black-tie: '\f27e'; -$fa-var-blind: '\f29d'; -$fa-var-bluetooth: '\f293'; -$fa-var-bluetooth-b: '\f294'; -$fa-var-bold: '\f032'; -$fa-var-bolt: '\f0e7'; -$fa-var-bomb: '\f1e2'; -$fa-var-book: '\f02d'; -$fa-var-bookmark: '\f02e'; -$fa-var-bookmark-o: '\f097'; -$fa-var-braille: '\f2a1'; -$fa-var-briefcase: '\f0b1'; -$fa-var-btc: '\f15a'; -$fa-var-bug: '\f188'; -$fa-var-building: '\f1ad'; -$fa-var-building-o: '\f0f7'; -$fa-var-bullhorn: '\f0a1'; -$fa-var-bullseye: '\f140'; -$fa-var-bus: '\f207'; -$fa-var-buysellads: '\f20d'; -$fa-var-cab: '\f1ba'; -$fa-var-calculator: '\f1ec'; -$fa-var-calendar: '\f073'; -$fa-var-calendar-check-o: '\f274'; -$fa-var-calendar-minus-o: '\f272'; -$fa-var-calendar-o: '\f133'; -$fa-var-calendar-plus-o: '\f271'; -$fa-var-calendar-times-o: '\f273'; -$fa-var-camera: '\f030'; -$fa-var-camera-retro: '\f083'; -$fa-var-car: '\f1b9'; -$fa-var-caret-down: '\f0d7'; -$fa-var-caret-left: '\f0d9'; -$fa-var-caret-right: '\f0da'; -$fa-var-caret-square-o-down: '\f150'; -$fa-var-caret-square-o-left: '\f191'; -$fa-var-caret-square-o-right: '\f152'; -$fa-var-caret-square-o-up: '\f151'; -$fa-var-caret-up: '\f0d8'; -$fa-var-cart-arrow-down: '\f218'; -$fa-var-cart-plus: '\f217'; -$fa-var-cc: '\f20a'; -$fa-var-cc-amex: '\f1f3'; -$fa-var-cc-diners-club: '\f24c'; -$fa-var-cc-discover: '\f1f2'; -$fa-var-cc-jcb: '\f24b'; -$fa-var-cc-mastercard: '\f1f1'; -$fa-var-cc-paypal: '\f1f4'; -$fa-var-cc-stripe: '\f1f5'; -$fa-var-cc-visa: '\f1f0'; -$fa-var-certificate: '\f0a3'; -$fa-var-chain: '\f0c1'; -$fa-var-chain-broken: '\f127'; -$fa-var-check: '\f00c'; -$fa-var-check-circle: '\f058'; -$fa-var-check-circle-o: '\f05d'; -$fa-var-check-square: '\f14a'; -$fa-var-check-square-o: '\f046'; -$fa-var-chevron-circle-down: '\f13a'; -$fa-var-chevron-circle-left: '\f137'; -$fa-var-chevron-circle-right: '\f138'; -$fa-var-chevron-circle-up: '\f139'; -$fa-var-chevron-down: '\f078'; -$fa-var-chevron-left: '\f053'; -$fa-var-chevron-right: '\f054'; -$fa-var-chevron-up: '\f077'; -$fa-var-child: '\f1ae'; -$fa-var-chrome: '\f268'; -$fa-var-circle: '\f111'; -$fa-var-circle-o: '\f10c'; -$fa-var-circle-o-notch: '\f1ce'; -$fa-var-circle-thin: '\f1db'; -$fa-var-clipboard: '\f0ea'; -$fa-var-clock-o: '\f017'; -$fa-var-clone: '\f24d'; -$fa-var-close: '\f00d'; -$fa-var-cloud: '\f0c2'; -$fa-var-cloud-download: '\f0ed'; -$fa-var-cloud-upload: '\f0ee'; -$fa-var-cny: '\f157'; -$fa-var-code: '\f121'; -$fa-var-code-fork: '\f126'; -$fa-var-codepen: '\f1cb'; -$fa-var-codiepie: '\f284'; -$fa-var-coffee: '\f0f4'; -$fa-var-cog: '\f013'; -$fa-var-cogs: '\f085'; -$fa-var-columns: '\f0db'; -$fa-var-comment: '\f075'; -$fa-var-comment-o: '\f0e5'; -$fa-var-commenting: '\f27a'; -$fa-var-commenting-o: '\f27b'; -$fa-var-comments: '\f086'; -$fa-var-comments-o: '\f0e6'; -$fa-var-compass: '\f14e'; -$fa-var-compress: '\f066'; -$fa-var-connectdevelop: '\f20e'; -$fa-var-contao: '\f26d'; -$fa-var-copy: '\f0c5'; -$fa-var-copyright: '\f1f9'; -$fa-var-creative-commons: '\f25e'; -$fa-var-credit-card: '\f09d'; -$fa-var-credit-card-alt: '\f283'; -$fa-var-crop: '\f125'; -$fa-var-crosshairs: '\f05b'; -$fa-var-css3: '\f13c'; -$fa-var-cube: '\f1b2'; -$fa-var-cubes: '\f1b3'; -$fa-var-cut: '\f0c4'; -$fa-var-cutlery: '\f0f5'; -$fa-var-dashboard: '\f0e4'; -$fa-var-dashcube: '\f210'; -$fa-var-database: '\f1c0'; -$fa-var-deaf: '\f2a4'; -$fa-var-deafness: '\f2a4'; -$fa-var-dedent: '\f03b'; -$fa-var-delicious: '\f1a5'; -$fa-var-desktop: '\f108'; -$fa-var-deviantart: '\f1bd'; -$fa-var-diamond: '\f219'; -$fa-var-digg: '\f1a6'; -$fa-var-dollar: '\f155'; -$fa-var-dot-circle-o: '\f192'; -$fa-var-download: '\f019'; -$fa-var-dribbble: '\f17d'; -$fa-var-drivers-license: '\f2c2'; -$fa-var-drivers-license-o: '\f2c3'; -$fa-var-dropbox: '\f16b'; -$fa-var-drupal: '\f1a9'; -$fa-var-edge: '\f282'; -$fa-var-edit: '\f044'; -$fa-var-eercast: '\f2da'; -$fa-var-eject: '\f052'; -$fa-var-ellipsis-h: '\f141'; -$fa-var-ellipsis-v: '\f142'; -$fa-var-empire: '\f1d1'; -$fa-var-envelope: '\f0e0'; -$fa-var-envelope-o: '\f003'; -$fa-var-envelope-open: '\f2b6'; -$fa-var-envelope-open-o: '\f2b7'; -$fa-var-envelope-square: '\f199'; -$fa-var-envira: '\f299'; -$fa-var-eraser: '\f12d'; -$fa-var-etsy: '\f2d7'; -$fa-var-eur: '\f153'; -$fa-var-euro: '\f153'; -$fa-var-exchange: '\f0ec'; -$fa-var-exclamation: '\f12a'; -$fa-var-exclamation-circle: '\f06a'; -$fa-var-exclamation-triangle: '\f071'; -$fa-var-expand: '\f065'; -$fa-var-expeditedssl: '\f23e'; -$fa-var-external-link: '\f08e'; -$fa-var-external-link-square: '\f14c'; -$fa-var-eye: '\f06e'; -$fa-var-eye-slash: '\f070'; -$fa-var-eyedropper: '\f1fb'; -$fa-var-fa: '\f2b4'; -$fa-var-facebook: '\f09a'; -$fa-var-facebook-f: '\f09a'; -$fa-var-facebook-official: '\f230'; -$fa-var-facebook-square: '\f082'; -$fa-var-fast-backward: '\f049'; -$fa-var-fast-forward: '\f050'; -$fa-var-fax: '\f1ac'; -$fa-var-feed: '\f09e'; -$fa-var-female: '\f182'; -$fa-var-fighter-jet: '\f0fb'; -$fa-var-file: '\f15b'; -$fa-var-file-archive-o: '\f1c6'; -$fa-var-file-audio-o: '\f1c7'; -$fa-var-file-code-o: '\f1c9'; -$fa-var-file-excel-o: '\f1c3'; -$fa-var-file-image-o: '\f1c5'; -$fa-var-file-movie-o: '\f1c8'; -$fa-var-file-o: '\f016'; -$fa-var-file-pdf-o: '\f1c1'; -$fa-var-file-photo-o: '\f1c5'; -$fa-var-file-picture-o: '\f1c5'; -$fa-var-file-powerpoint-o: '\f1c4'; -$fa-var-file-sound-o: '\f1c7'; -$fa-var-file-text: '\f15c'; -$fa-var-file-text-o: '\f0f6'; -$fa-var-file-video-o: '\f1c8'; -$fa-var-file-word-o: '\f1c2'; -$fa-var-file-zip-o: '\f1c6'; -$fa-var-files-o: '\f0c5'; -$fa-var-film: '\f008'; -$fa-var-filter: '\f0b0'; -$fa-var-fire: '\f06d'; -$fa-var-fire-extinguisher: '\f134'; -$fa-var-firefox: '\f269'; -$fa-var-first-order: '\f2b0'; -$fa-var-flag: '\f024'; -$fa-var-flag-checkered: '\f11e'; -$fa-var-flag-o: '\f11d'; -$fa-var-flash: '\f0e7'; -$fa-var-flask: '\f0c3'; -$fa-var-flickr: '\f16e'; -$fa-var-floppy-o: '\f0c7'; -$fa-var-folder: '\f07b'; -$fa-var-folder-o: '\f114'; -$fa-var-folder-open: '\f07c'; -$fa-var-folder-open-o: '\f115'; -$fa-var-font: '\f031'; -$fa-var-font-awesome: '\f2b4'; -$fa-var-fonticons: '\f280'; -$fa-var-fort-awesome: '\f286'; -$fa-var-forumbee: '\f211'; -$fa-var-forward: '\f04e'; -$fa-var-foursquare: '\f180'; -$fa-var-free-code-camp: '\f2c5'; -$fa-var-frown-o: '\f119'; -$fa-var-futbol-o: '\f1e3'; -$fa-var-gamepad: '\f11b'; -$fa-var-gavel: '\f0e3'; -$fa-var-gbp: '\f154'; -$fa-var-ge: '\f1d1'; -$fa-var-gear: '\f013'; -$fa-var-gears: '\f085'; -$fa-var-genderless: '\f22d'; -$fa-var-get-pocket: '\f265'; -$fa-var-gg: '\f260'; -$fa-var-gg-circle: '\f261'; -$fa-var-gift: '\f06b'; -$fa-var-git: '\f1d3'; -$fa-var-git-square: '\f1d2'; -$fa-var-github: '\f09b'; -$fa-var-github-alt: '\f113'; -$fa-var-github-square: '\f092'; -$fa-var-gitlab: '\f296'; -$fa-var-gittip: '\f184'; -$fa-var-glass: '\f000'; -$fa-var-glide: '\f2a5'; -$fa-var-glide-g: '\f2a6'; -$fa-var-globe: '\f0ac'; -$fa-var-google: '\f1a0'; -$fa-var-google-plus: '\f0d5'; -$fa-var-google-plus-circle: '\f2b3'; -$fa-var-google-plus-official: '\f2b3'; -$fa-var-google-plus-square: '\f0d4'; -$fa-var-google-wallet: '\f1ee'; -$fa-var-graduation-cap: '\f19d'; -$fa-var-gratipay: '\f184'; -$fa-var-grav: '\f2d6'; -$fa-var-group: '\f0c0'; -$fa-var-h-square: '\f0fd'; -$fa-var-hacker-news: '\f1d4'; -$fa-var-hand-grab-o: '\f255'; -$fa-var-hand-lizard-o: '\f258'; -$fa-var-hand-o-down: '\f0a7'; -$fa-var-hand-o-left: '\f0a5'; -$fa-var-hand-o-right: '\f0a4'; -$fa-var-hand-o-up: '\f0a6'; -$fa-var-hand-paper-o: '\f256'; -$fa-var-hand-peace-o: '\f25b'; -$fa-var-hand-pointer-o: '\f25a'; -$fa-var-hand-rock-o: '\f255'; -$fa-var-hand-scissors-o: '\f257'; -$fa-var-hand-spock-o: '\f259'; -$fa-var-hand-stop-o: '\f256'; -$fa-var-handshake-o: '\f2b5'; -$fa-var-hard-of-hearing: '\f2a4'; -$fa-var-hashtag: '\f292'; -$fa-var-hdd-o: '\f0a0'; -$fa-var-header: '\f1dc'; -$fa-var-headphones: '\f025'; -$fa-var-heart: '\f004'; -$fa-var-heart-o: '\f08a'; -$fa-var-heartbeat: '\f21e'; -$fa-var-history: '\f1da'; -$fa-var-home: '\f015'; -$fa-var-hospital-o: '\f0f8'; -$fa-var-hotel: '\f236'; -$fa-var-hourglass: '\f254'; -$fa-var-hourglass-1: '\f251'; -$fa-var-hourglass-2: '\f252'; -$fa-var-hourglass-3: '\f253'; -$fa-var-hourglass-end: '\f253'; -$fa-var-hourglass-half: '\f252'; -$fa-var-hourglass-o: '\f250'; -$fa-var-hourglass-start: '\f251'; -$fa-var-houzz: '\f27c'; -$fa-var-html5: '\f13b'; -$fa-var-i-cursor: '\f246'; -$fa-var-id-badge: '\f2c1'; -$fa-var-id-card: '\f2c2'; -$fa-var-id-card-o: '\f2c3'; -$fa-var-ils: '\f20b'; -$fa-var-image: '\f03e'; -$fa-var-imdb: '\f2d8'; -$fa-var-inbox: '\f01c'; -$fa-var-indent: '\f03c'; -$fa-var-industry: '\f275'; -$fa-var-info: '\f129'; -$fa-var-info-circle: '\f05a'; -$fa-var-inr: '\f156'; -$fa-var-instagram: '\f16d'; -$fa-var-institution: '\f19c'; -$fa-var-internet-explorer: '\f26b'; -$fa-var-intersex: '\f224'; -$fa-var-ioxhost: '\f208'; -$fa-var-italic: '\f033'; -$fa-var-joomla: '\f1aa'; -$fa-var-jpy: '\f157'; -$fa-var-jsfiddle: '\f1cc'; -$fa-var-key: '\f084'; -$fa-var-keyboard-o: '\f11c'; -$fa-var-krw: '\f159'; -$fa-var-language: '\f1ab'; -$fa-var-laptop: '\f109'; -$fa-var-lastfm: '\f202'; -$fa-var-lastfm-square: '\f203'; -$fa-var-leaf: '\f06c'; -$fa-var-leanpub: '\f212'; -$fa-var-legal: '\f0e3'; -$fa-var-lemon-o: '\f094'; -$fa-var-level-down: '\f149'; -$fa-var-level-up: '\f148'; -$fa-var-life-bouy: '\f1cd'; -$fa-var-life-buoy: '\f1cd'; -$fa-var-life-ring: '\f1cd'; -$fa-var-life-saver: '\f1cd'; -$fa-var-lightbulb-o: '\f0eb'; -$fa-var-line-chart: '\f201'; -$fa-var-link: '\f0c1'; -$fa-var-linkedin: '\f0e1'; -$fa-var-linkedin-square: '\f08c'; -$fa-var-linode: '\f2b8'; -$fa-var-linux: '\f17c'; -$fa-var-list: '\f03a'; -$fa-var-list-alt: '\f022'; -$fa-var-list-ol: '\f0cb'; -$fa-var-list-ul: '\f0ca'; -$fa-var-location-arrow: '\f124'; -$fa-var-lock: '\f023'; -$fa-var-long-arrow-down: '\f175'; -$fa-var-long-arrow-left: '\f177'; -$fa-var-long-arrow-right: '\f178'; -$fa-var-long-arrow-up: '\f176'; -$fa-var-low-vision: '\f2a8'; -$fa-var-magic: '\f0d0'; -$fa-var-magnet: '\f076'; -$fa-var-mail-forward: '\f064'; -$fa-var-mail-reply: '\f112'; -$fa-var-mail-reply-all: '\f122'; -$fa-var-male: '\f183'; -$fa-var-map: '\f279'; -$fa-var-map-marker: '\f041'; -$fa-var-map-o: '\f278'; -$fa-var-map-pin: '\f276'; -$fa-var-map-signs: '\f277'; -$fa-var-mars: '\f222'; -$fa-var-mars-double: '\f227'; -$fa-var-mars-stroke: '\f229'; -$fa-var-mars-stroke-h: '\f22b'; -$fa-var-mars-stroke-v: '\f22a'; -$fa-var-maxcdn: '\f136'; -$fa-var-meanpath: '\f20c'; -$fa-var-medium: '\f23a'; -$fa-var-medkit: '\f0fa'; -$fa-var-meetup: '\f2e0'; -$fa-var-meh-o: '\f11a'; -$fa-var-mercury: '\f223'; -$fa-var-microchip: '\f2db'; -$fa-var-microphone: '\f130'; -$fa-var-microphone-slash: '\f131'; -$fa-var-minus: '\f068'; -$fa-var-minus-circle: '\f056'; -$fa-var-minus-square: '\f146'; -$fa-var-minus-square-o: '\f147'; -$fa-var-mixcloud: '\f289'; -$fa-var-mobile: '\f10b'; -$fa-var-mobile-phone: '\f10b'; -$fa-var-modx: '\f285'; -$fa-var-money: '\f0d6'; -$fa-var-moon-o: '\f186'; -$fa-var-mortar-board: '\f19d'; -$fa-var-motorcycle: '\f21c'; -$fa-var-mouse-pointer: '\f245'; -$fa-var-music: '\f001'; -$fa-var-navicon: '\f0c9'; -$fa-var-neuter: '\f22c'; -$fa-var-newspaper-o: '\f1ea'; -$fa-var-object-group: '\f247'; -$fa-var-object-ungroup: '\f248'; -$fa-var-odnoklassniki: '\f263'; -$fa-var-odnoklassniki-square: '\f264'; -$fa-var-opencart: '\f23d'; -$fa-var-openid: '\f19b'; -$fa-var-opera: '\f26a'; -$fa-var-optin-monster: '\f23c'; -$fa-var-outdent: '\f03b'; -$fa-var-pagelines: '\f18c'; -$fa-var-paint-brush: '\f1fc'; -$fa-var-paper-plane: '\f1d8'; -$fa-var-paper-plane-o: '\f1d9'; -$fa-var-paperclip: '\f0c6'; -$fa-var-paragraph: '\f1dd'; -$fa-var-paste: '\f0ea'; -$fa-var-pause: '\f04c'; -$fa-var-pause-circle: '\f28b'; -$fa-var-pause-circle-o: '\f28c'; -$fa-var-paw: '\f1b0'; -$fa-var-paypal: '\f1ed'; -$fa-var-pencil: '\f040'; -$fa-var-pencil-square: '\f14b'; -$fa-var-pencil-square-o: '\f044'; -$fa-var-percent: '\f295'; -$fa-var-phone: '\f095'; -$fa-var-phone-square: '\f098'; -$fa-var-photo: '\f03e'; -$fa-var-picture-o: '\f03e'; -$fa-var-pie-chart: '\f200'; -$fa-var-pied-piper: '\f2ae'; -$fa-var-pied-piper-alt: '\f1a8'; -$fa-var-pied-piper-pp: '\f1a7'; -$fa-var-pinterest: '\f0d2'; -$fa-var-pinterest-p: '\f231'; -$fa-var-pinterest-square: '\f0d3'; -$fa-var-plane: '\f072'; -$fa-var-play: '\f04b'; -$fa-var-play-circle: '\f144'; -$fa-var-play-circle-o: '\f01d'; -$fa-var-plug: '\f1e6'; -$fa-var-plus: '\f067'; -$fa-var-plus-circle: '\f055'; -$fa-var-plus-square: '\f0fe'; -$fa-var-plus-square-o: '\f196'; -$fa-var-podcast: '\f2ce'; -$fa-var-power-off: '\f011'; -$fa-var-print: '\f02f'; -$fa-var-product-hunt: '\f288'; -$fa-var-puzzle-piece: '\f12e'; -$fa-var-qq: '\f1d6'; -$fa-var-qrcode: '\f029'; -$fa-var-question: '\f128'; -$fa-var-question-circle: '\f059'; -$fa-var-question-circle-o: '\f29c'; -$fa-var-quora: '\f2c4'; -$fa-var-quote-left: '\f10d'; -$fa-var-quote-right: '\f10e'; -$fa-var-ra: '\f1d0'; -$fa-var-random: '\f074'; -$fa-var-ravelry: '\f2d9'; -$fa-var-rebel: '\f1d0'; -$fa-var-recycle: '\f1b8'; -$fa-var-reddit: '\f1a1'; -$fa-var-reddit-alien: '\f281'; -$fa-var-reddit-square: '\f1a2'; -$fa-var-refresh: '\f021'; -$fa-var-registered: '\f25d'; -$fa-var-remove: '\f00d'; -$fa-var-renren: '\f18b'; -$fa-var-reorder: '\f0c9'; -$fa-var-repeat: '\f01e'; -$fa-var-reply: '\f112'; -$fa-var-reply-all: '\f122'; -$fa-var-resistance: '\f1d0'; -$fa-var-retweet: '\f079'; -$fa-var-rmb: '\f157'; -$fa-var-road: '\f018'; -$fa-var-rocket: '\f135'; -$fa-var-rotate-left: '\f0e2'; -$fa-var-rotate-right: '\f01e'; -$fa-var-rouble: '\f158'; -$fa-var-rss: '\f09e'; -$fa-var-rss-square: '\f143'; -$fa-var-rub: '\f158'; -$fa-var-ruble: '\f158'; -$fa-var-rupee: '\f156'; -$fa-var-s15: '\f2cd'; -$fa-var-safari: '\f267'; -$fa-var-save: '\f0c7'; -$fa-var-scissors: '\f0c4'; -$fa-var-scribd: '\f28a'; -$fa-var-search: '\f002'; -$fa-var-search-minus: '\f010'; -$fa-var-search-plus: '\f00e'; -$fa-var-sellsy: '\f213'; -$fa-var-send: '\f1d8'; -$fa-var-send-o: '\f1d9'; -$fa-var-server: '\f233'; -$fa-var-share: '\f064'; -$fa-var-share-alt: '\f1e0'; -$fa-var-share-alt-square: '\f1e1'; -$fa-var-share-square: '\f14d'; -$fa-var-share-square-o: '\f045'; -$fa-var-shekel: '\f20b'; -$fa-var-sheqel: '\f20b'; -$fa-var-shield: '\f132'; -$fa-var-ship: '\f21a'; -$fa-var-shirtsinbulk: '\f214'; -$fa-var-shopping-bag: '\f290'; -$fa-var-shopping-basket: '\f291'; -$fa-var-shopping-cart: '\f07a'; -$fa-var-shower: '\f2cc'; -$fa-var-sign-in: '\f090'; -$fa-var-sign-language: '\f2a7'; -$fa-var-sign-out: '\f08b'; -$fa-var-signal: '\f012'; -$fa-var-signing: '\f2a7'; -$fa-var-simplybuilt: '\f215'; -$fa-var-sitemap: '\f0e8'; -$fa-var-skyatlas: '\f216'; -$fa-var-skype: '\f17e'; -$fa-var-slack: '\f198'; -$fa-var-sliders: '\f1de'; -$fa-var-slideshare: '\f1e7'; -$fa-var-smile-o: '\f118'; -$fa-var-snapchat: '\f2ab'; -$fa-var-snapchat-ghost: '\f2ac'; -$fa-var-snapchat-square: '\f2ad'; -$fa-var-snowflake-o: '\f2dc'; -$fa-var-soccer-ball-o: '\f1e3'; -$fa-var-sort: '\f0dc'; -$fa-var-sort-alpha-asc: '\f15d'; -$fa-var-sort-alpha-desc: '\f15e'; -$fa-var-sort-amount-asc: '\f160'; -$fa-var-sort-amount-desc: '\f161'; -$fa-var-sort-asc: '\f0de'; -$fa-var-sort-desc: '\f0dd'; -$fa-var-sort-down: '\f0dd'; -$fa-var-sort-numeric-asc: '\f162'; -$fa-var-sort-numeric-desc: '\f163'; -$fa-var-sort-up: '\f0de'; -$fa-var-soundcloud: '\f1be'; -$fa-var-space-shuttle: '\f197'; -$fa-var-spinner: '\f110'; -$fa-var-spoon: '\f1b1'; -$fa-var-spotify: '\f1bc'; -$fa-var-square: '\f0c8'; -$fa-var-square-o: '\f096'; -$fa-var-stack-exchange: '\f18d'; -$fa-var-stack-overflow: '\f16c'; -$fa-var-star: '\f005'; -$fa-var-star-half: '\f089'; -$fa-var-star-half-empty: '\f123'; -$fa-var-star-half-full: '\f123'; -$fa-var-star-half-o: '\f123'; -$fa-var-star-o: '\f006'; -$fa-var-steam: '\f1b6'; -$fa-var-steam-square: '\f1b7'; -$fa-var-step-backward: '\f048'; -$fa-var-step-forward: '\f051'; -$fa-var-stethoscope: '\f0f1'; -$fa-var-sticky-note: '\f249'; -$fa-var-sticky-note-o: '\f24a'; -$fa-var-stop: '\f04d'; -$fa-var-stop-circle: '\f28d'; -$fa-var-stop-circle-o: '\f28e'; -$fa-var-street-view: '\f21d'; -$fa-var-strikethrough: '\f0cc'; -$fa-var-stumbleupon: '\f1a4'; -$fa-var-stumbleupon-circle: '\f1a3'; -$fa-var-subscript: '\f12c'; -$fa-var-subway: '\f239'; -$fa-var-suitcase: '\f0f2'; -$fa-var-sun-o: '\f185'; -$fa-var-superpowers: '\f2dd'; -$fa-var-superscript: '\f12b'; -$fa-var-support: '\f1cd'; -$fa-var-table: '\f0ce'; -$fa-var-tablet: '\f10a'; -$fa-var-tachometer: '\f0e4'; -$fa-var-tag: '\f02b'; -$fa-var-tags: '\f02c'; -$fa-var-tasks: '\f0ae'; -$fa-var-taxi: '\f1ba'; -$fa-var-telegram: '\f2c6'; -$fa-var-television: '\f26c'; -$fa-var-tencent-weibo: '\f1d5'; -$fa-var-terminal: '\f120'; -$fa-var-text-height: '\f034'; -$fa-var-text-width: '\f035'; -$fa-var-th: '\f00a'; -$fa-var-th-large: '\f009'; -$fa-var-th-list: '\f00b'; -$fa-var-themeisle: '\f2b2'; -$fa-var-thermometer: '\f2c7'; -$fa-var-thermometer-0: '\f2cb'; -$fa-var-thermometer-1: '\f2ca'; -$fa-var-thermometer-2: '\f2c9'; -$fa-var-thermometer-3: '\f2c8'; -$fa-var-thermometer-4: '\f2c7'; -$fa-var-thermometer-empty: '\f2cb'; -$fa-var-thermometer-full: '\f2c7'; -$fa-var-thermometer-half: '\f2c9'; -$fa-var-thermometer-quarter: '\f2ca'; -$fa-var-thermometer-three-quarters: '\f2c8'; -$fa-var-thumb-tack: '\f08d'; -$fa-var-thumbs-down: '\f165'; -$fa-var-thumbs-o-down: '\f088'; -$fa-var-thumbs-o-up: '\f087'; -$fa-var-thumbs-up: '\f164'; -$fa-var-ticket: '\f145'; -$fa-var-times: '\f00d'; -$fa-var-times-circle: '\f057'; -$fa-var-times-circle-o: '\f05c'; -$fa-var-times-rectangle: '\f2d3'; -$fa-var-times-rectangle-o: '\f2d4'; -$fa-var-tint: '\f043'; -$fa-var-toggle-down: '\f150'; -$fa-var-toggle-left: '\f191'; -$fa-var-toggle-off: '\f204'; -$fa-var-toggle-on: '\f205'; -$fa-var-toggle-right: '\f152'; -$fa-var-toggle-up: '\f151'; -$fa-var-trademark: '\f25c'; -$fa-var-train: '\f238'; -$fa-var-transgender: '\f224'; -$fa-var-transgender-alt: '\f225'; -$fa-var-trash: '\f1f8'; -$fa-var-trash-o: '\f014'; -$fa-var-tree: '\f1bb'; -$fa-var-trello: '\f181'; -$fa-var-tripadvisor: '\f262'; -$fa-var-trophy: '\f091'; -$fa-var-truck: '\f0d1'; -$fa-var-try: '\f195'; -$fa-var-tty: '\f1e4'; -$fa-var-tumblr: '\f173'; -$fa-var-tumblr-square: '\f174'; -$fa-var-turkish-lira: '\f195'; -$fa-var-tv: '\f26c'; -$fa-var-twitch: '\f1e8'; -$fa-var-twitter: '\f099'; -$fa-var-twitter-square: '\f081'; -$fa-var-umbrella: '\f0e9'; -$fa-var-underline: '\f0cd'; -$fa-var-undo: '\f0e2'; -$fa-var-universal-access: '\f29a'; -$fa-var-university: '\f19c'; -$fa-var-unlink: '\f127'; -$fa-var-unlock: '\f09c'; -$fa-var-unlock-alt: '\f13e'; -$fa-var-unsorted: '\f0dc'; -$fa-var-upload: '\f093'; -$fa-var-usb: '\f287'; -$fa-var-usd: '\f155'; -$fa-var-user: '\f007'; -$fa-var-user-circle: '\f2bd'; -$fa-var-user-circle-o: '\f2be'; -$fa-var-user-md: '\f0f0'; -$fa-var-user-o: '\f2c0'; -$fa-var-user-plus: '\f234'; -$fa-var-user-secret: '\f21b'; -$fa-var-user-times: '\f235'; -$fa-var-users: '\f0c0'; -$fa-var-vcard: '\f2bb'; -$fa-var-vcard-o: '\f2bc'; -$fa-var-venus: '\f221'; -$fa-var-venus-double: '\f226'; -$fa-var-venus-mars: '\f228'; -$fa-var-viacoin: '\f237'; -$fa-var-viadeo: '\f2a9'; -$fa-var-viadeo-square: '\f2aa'; -$fa-var-video-camera: '\f03d'; -$fa-var-vimeo: '\f27d'; -$fa-var-vimeo-square: '\f194'; -$fa-var-vine: '\f1ca'; -$fa-var-vk: '\f189'; -$fa-var-volume-control-phone: '\f2a0'; -$fa-var-volume-down: '\f027'; -$fa-var-volume-off: '\f026'; -$fa-var-volume-up: '\f028'; -$fa-var-warning: '\f071'; -$fa-var-wechat: '\f1d7'; -$fa-var-weibo: '\f18a'; -$fa-var-weixin: '\f1d7'; -$fa-var-whatsapp: '\f232'; -$fa-var-wheelchair: '\f193'; -$fa-var-wheelchair-alt: '\f29b'; -$fa-var-wifi: '\f1eb'; -$fa-var-wikipedia-w: '\f266'; -$fa-var-window-close: '\f2d3'; -$fa-var-window-close-o: '\f2d4'; -$fa-var-window-maximize: '\f2d0'; -$fa-var-window-minimize: '\f2d1'; -$fa-var-window-restore: '\f2d2'; -$fa-var-windows: '\f17a'; -$fa-var-won: '\f159'; -$fa-var-wordpress: '\f19a'; -$fa-var-wpbeginner: '\f297'; -$fa-var-wpexplorer: '\f2de'; -$fa-var-wpforms: '\f298'; -$fa-var-wrench: '\f0ad'; -$fa-var-xing: '\f168'; -$fa-var-xing-square: '\f169'; -$fa-var-y-combinator: '\f23b'; -$fa-var-y-combinator-square: '\f1d4'; -$fa-var-yahoo: '\f19e'; -$fa-var-yc: '\f23b'; -$fa-var-yc-square: '\f1d4'; -$fa-var-yelp: '\f1e9'; -$fa-var-yen: '\f157'; -$fa-var-yoast: '\f2b1'; -$fa-var-youtube: '\f167'; -$fa-var-youtube-play: '\f16a'; -$fa-var-youtube-square: '\f166'; +$fa-var-500px: "\f26e"; +$fa-var-address-book: "\f2b9"; +$fa-var-address-book-o: "\f2ba"; +$fa-var-address-card: "\f2bb"; +$fa-var-address-card-o: "\f2bc"; +$fa-var-adjust: "\f042"; +$fa-var-adn: "\f170"; +$fa-var-align-center: "\f037"; +$fa-var-align-justify: "\f039"; +$fa-var-align-left: "\f036"; +$fa-var-align-right: "\f038"; +$fa-var-amazon: "\f270"; +$fa-var-ambulance: "\f0f9"; +$fa-var-american-sign-language-interpreting: "\f2a3"; +$fa-var-anchor: "\f13d"; +$fa-var-android: "\f17b"; +$fa-var-angellist: "\f209"; +$fa-var-angle-double-down: "\f103"; +$fa-var-angle-double-left: "\f100"; +$fa-var-angle-double-right: "\f101"; +$fa-var-angle-double-up: "\f102"; +$fa-var-angle-down: "\f107"; +$fa-var-angle-left: "\f104"; +$fa-var-angle-right: "\f105"; +$fa-var-angle-up: "\f106"; +$fa-var-apple: "\f179"; +$fa-var-archive: "\f187"; +$fa-var-area-chart: "\f1fe"; +$fa-var-arrow-circle-down: "\f0ab"; +$fa-var-arrow-circle-left: "\f0a8"; +$fa-var-arrow-circle-o-down: "\f01a"; +$fa-var-arrow-circle-o-left: "\f190"; +$fa-var-arrow-circle-o-right: "\f18e"; +$fa-var-arrow-circle-o-up: "\f01b"; +$fa-var-arrow-circle-right: "\f0a9"; +$fa-var-arrow-circle-up: "\f0aa"; +$fa-var-arrow-down: "\f063"; +$fa-var-arrow-left: "\f060"; +$fa-var-arrow-right: "\f061"; +$fa-var-arrow-up: "\f062"; +$fa-var-arrows: "\f047"; +$fa-var-arrows-alt: "\f0b2"; +$fa-var-arrows-h: "\f07e"; +$fa-var-arrows-v: "\f07d"; +$fa-var-asl-interpreting: "\f2a3"; +$fa-var-assistive-listening-systems: "\f2a2"; +$fa-var-asterisk: "\f069"; +$fa-var-at: "\f1fa"; +$fa-var-audio-description: "\f29e"; +$fa-var-automobile: "\f1b9"; +$fa-var-backward: "\f04a"; +$fa-var-balance-scale: "\f24e"; +$fa-var-ban: "\f05e"; +$fa-var-bandcamp: "\f2d5"; +$fa-var-bank: "\f19c"; +$fa-var-bar-chart: "\f080"; +$fa-var-bar-chart-o: "\f080"; +$fa-var-barcode: "\f02a"; +$fa-var-bars: "\f0c9"; +$fa-var-bath: "\f2cd"; +$fa-var-bathtub: "\f2cd"; +$fa-var-battery: "\f240"; +$fa-var-battery-0: "\f244"; +$fa-var-battery-1: "\f243"; +$fa-var-battery-2: "\f242"; +$fa-var-battery-3: "\f241"; +$fa-var-battery-4: "\f240"; +$fa-var-battery-empty: "\f244"; +$fa-var-battery-full: "\f240"; +$fa-var-battery-half: "\f242"; +$fa-var-battery-quarter: "\f243"; +$fa-var-battery-three-quarters: "\f241"; +$fa-var-bed: "\f236"; +$fa-var-beer: "\f0fc"; +$fa-var-behance: "\f1b4"; +$fa-var-behance-square: "\f1b5"; +$fa-var-bell: "\f0f3"; +$fa-var-bell-o: "\f0a2"; +$fa-var-bell-slash: "\f1f6"; +$fa-var-bell-slash-o: "\f1f7"; +$fa-var-bicycle: "\f206"; +$fa-var-binoculars: "\f1e5"; +$fa-var-birthday-cake: "\f1fd"; +$fa-var-bitbucket: "\f171"; +$fa-var-bitbucket-square: "\f172"; +$fa-var-bitcoin: "\f15a"; +$fa-var-black-tie: "\f27e"; +$fa-var-blind: "\f29d"; +$fa-var-bluetooth: "\f293"; +$fa-var-bluetooth-b: "\f294"; +$fa-var-bold: "\f032"; +$fa-var-bolt: "\f0e7"; +$fa-var-bomb: "\f1e2"; +$fa-var-book: "\f02d"; +$fa-var-bookmark: "\f02e"; +$fa-var-bookmark-o: "\f097"; +$fa-var-braille: "\f2a1"; +$fa-var-briefcase: "\f0b1"; +$fa-var-btc: "\f15a"; +$fa-var-bug: "\f188"; +$fa-var-building: "\f1ad"; +$fa-var-building-o: "\f0f7"; +$fa-var-bullhorn: "\f0a1"; +$fa-var-bullseye: "\f140"; +$fa-var-bus: "\f207"; +$fa-var-buysellads: "\f20d"; +$fa-var-cab: "\f1ba"; +$fa-var-calculator: "\f1ec"; +$fa-var-calendar: "\f073"; +$fa-var-calendar-check-o: "\f274"; +$fa-var-calendar-minus-o: "\f272"; +$fa-var-calendar-o: "\f133"; +$fa-var-calendar-plus-o: "\f271"; +$fa-var-calendar-times-o: "\f273"; +$fa-var-camera: "\f030"; +$fa-var-camera-retro: "\f083"; +$fa-var-car: "\f1b9"; +$fa-var-caret-down: "\f0d7"; +$fa-var-caret-left: "\f0d9"; +$fa-var-caret-right: "\f0da"; +$fa-var-caret-square-o-down: "\f150"; +$fa-var-caret-square-o-left: "\f191"; +$fa-var-caret-square-o-right: "\f152"; +$fa-var-caret-square-o-up: "\f151"; +$fa-var-caret-up: "\f0d8"; +$fa-var-cart-arrow-down: "\f218"; +$fa-var-cart-plus: "\f217"; +$fa-var-cc: "\f20a"; +$fa-var-cc-amex: "\f1f3"; +$fa-var-cc-diners-club: "\f24c"; +$fa-var-cc-discover: "\f1f2"; +$fa-var-cc-jcb: "\f24b"; +$fa-var-cc-mastercard: "\f1f1"; +$fa-var-cc-paypal: "\f1f4"; +$fa-var-cc-stripe: "\f1f5"; +$fa-var-cc-visa: "\f1f0"; +$fa-var-certificate: "\f0a3"; +$fa-var-chain: "\f0c1"; +$fa-var-chain-broken: "\f127"; +$fa-var-check: "\f00c"; +$fa-var-check-circle: "\f058"; +$fa-var-check-circle-o: "\f05d"; +$fa-var-check-square: "\f14a"; +$fa-var-check-square-o: "\f046"; +$fa-var-chevron-circle-down: "\f13a"; +$fa-var-chevron-circle-left: "\f137"; +$fa-var-chevron-circle-right: "\f138"; +$fa-var-chevron-circle-up: "\f139"; +$fa-var-chevron-down: "\f078"; +$fa-var-chevron-left: "\f053"; +$fa-var-chevron-right: "\f054"; +$fa-var-chevron-up: "\f077"; +$fa-var-child: "\f1ae"; +$fa-var-chrome: "\f268"; +$fa-var-circle: "\f111"; +$fa-var-circle-o: "\f10c"; +$fa-var-circle-o-notch: "\f1ce"; +$fa-var-circle-thin: "\f1db"; +$fa-var-clipboard: "\f0ea"; +$fa-var-clock-o: "\f017"; +$fa-var-clone: "\f24d"; +$fa-var-close: "\f00d"; +$fa-var-cloud: "\f0c2"; +$fa-var-cloud-download: "\f0ed"; +$fa-var-cloud-upload: "\f0ee"; +$fa-var-cny: "\f157"; +$fa-var-code: "\f121"; +$fa-var-code-fork: "\f126"; +$fa-var-codepen: "\f1cb"; +$fa-var-codiepie: "\f284"; +$fa-var-coffee: "\f0f4"; +$fa-var-cog: "\f013"; +$fa-var-cogs: "\f085"; +$fa-var-columns: "\f0db"; +$fa-var-comment: "\f075"; +$fa-var-comment-o: "\f0e5"; +$fa-var-commenting: "\f27a"; +$fa-var-commenting-o: "\f27b"; +$fa-var-comments: "\f086"; +$fa-var-comments-o: "\f0e6"; +$fa-var-compass: "\f14e"; +$fa-var-compress: "\f066"; +$fa-var-connectdevelop: "\f20e"; +$fa-var-contao: "\f26d"; +$fa-var-copy: "\f0c5"; +$fa-var-copyright: "\f1f9"; +$fa-var-creative-commons: "\f25e"; +$fa-var-credit-card: "\f09d"; +$fa-var-credit-card-alt: "\f283"; +$fa-var-crop: "\f125"; +$fa-var-crosshairs: "\f05b"; +$fa-var-css3: "\f13c"; +$fa-var-cube: "\f1b2"; +$fa-var-cubes: "\f1b3"; +$fa-var-cut: "\f0c4"; +$fa-var-cutlery: "\f0f5"; +$fa-var-dashboard: "\f0e4"; +$fa-var-dashcube: "\f210"; +$fa-var-database: "\f1c0"; +$fa-var-deaf: "\f2a4"; +$fa-var-deafness: "\f2a4"; +$fa-var-dedent: "\f03b"; +$fa-var-delicious: "\f1a5"; +$fa-var-desktop: "\f108"; +$fa-var-deviantart: "\f1bd"; +$fa-var-diamond: "\f219"; +$fa-var-digg: "\f1a6"; +$fa-var-dollar: "\f155"; +$fa-var-dot-circle-o: "\f192"; +$fa-var-download: "\f019"; +$fa-var-dribbble: "\f17d"; +$fa-var-drivers-license: "\f2c2"; +$fa-var-drivers-license-o: "\f2c3"; +$fa-var-dropbox: "\f16b"; +$fa-var-drupal: "\f1a9"; +$fa-var-edge: "\f282"; +$fa-var-edit: "\f044"; +$fa-var-eercast: "\f2da"; +$fa-var-eject: "\f052"; +$fa-var-ellipsis-h: "\f141"; +$fa-var-ellipsis-v: "\f142"; +$fa-var-empire: "\f1d1"; +$fa-var-envelope: "\f0e0"; +$fa-var-envelope-o: "\f003"; +$fa-var-envelope-open: "\f2b6"; +$fa-var-envelope-open-o: "\f2b7"; +$fa-var-envelope-square: "\f199"; +$fa-var-envira: "\f299"; +$fa-var-eraser: "\f12d"; +$fa-var-etsy: "\f2d7"; +$fa-var-eur: "\f153"; +$fa-var-euro: "\f153"; +$fa-var-exchange: "\f0ec"; +$fa-var-exclamation: "\f12a"; +$fa-var-exclamation-circle: "\f06a"; +$fa-var-exclamation-triangle: "\f071"; +$fa-var-expand: "\f065"; +$fa-var-expeditedssl: "\f23e"; +$fa-var-external-link: "\f08e"; +$fa-var-external-link-square: "\f14c"; +$fa-var-eye: "\f06e"; +$fa-var-eye-slash: "\f070"; +$fa-var-eyedropper: "\f1fb"; +$fa-var-fa: "\f2b4"; +$fa-var-facebook: "\f09a"; +$fa-var-facebook-f: "\f09a"; +$fa-var-facebook-official: "\f230"; +$fa-var-facebook-square: "\f082"; +$fa-var-fast-backward: "\f049"; +$fa-var-fast-forward: "\f050"; +$fa-var-fax: "\f1ac"; +$fa-var-feed: "\f09e"; +$fa-var-female: "\f182"; +$fa-var-fighter-jet: "\f0fb"; +$fa-var-file: "\f15b"; +$fa-var-file-archive-o: "\f1c6"; +$fa-var-file-audio-o: "\f1c7"; +$fa-var-file-code-o: "\f1c9"; +$fa-var-file-excel-o: "\f1c3"; +$fa-var-file-image-o: "\f1c5"; +$fa-var-file-movie-o: "\f1c8"; +$fa-var-file-o: "\f016"; +$fa-var-file-pdf-o: "\f1c1"; +$fa-var-file-photo-o: "\f1c5"; +$fa-var-file-picture-o: "\f1c5"; +$fa-var-file-powerpoint-o: "\f1c4"; +$fa-var-file-sound-o: "\f1c7"; +$fa-var-file-text: "\f15c"; +$fa-var-file-text-o: "\f0f6"; +$fa-var-file-video-o: "\f1c8"; +$fa-var-file-word-o: "\f1c2"; +$fa-var-file-zip-o: "\f1c6"; +$fa-var-files-o: "\f0c5"; +$fa-var-film: "\f008"; +$fa-var-filter: "\f0b0"; +$fa-var-fire: "\f06d"; +$fa-var-fire-extinguisher: "\f134"; +$fa-var-firefox: "\f269"; +$fa-var-first-order: "\f2b0"; +$fa-var-flag: "\f024"; +$fa-var-flag-checkered: "\f11e"; +$fa-var-flag-o: "\f11d"; +$fa-var-flash: "\f0e7"; +$fa-var-flask: "\f0c3"; +$fa-var-flickr: "\f16e"; +$fa-var-floppy-o: "\f0c7"; +$fa-var-folder: "\f07b"; +$fa-var-folder-o: "\f114"; +$fa-var-folder-open: "\f07c"; +$fa-var-folder-open-o: "\f115"; +$fa-var-font: "\f031"; +$fa-var-font-awesome: "\f2b4"; +$fa-var-fonticons: "\f280"; +$fa-var-fort-awesome: "\f286"; +$fa-var-forumbee: "\f211"; +$fa-var-forward: "\f04e"; +$fa-var-foursquare: "\f180"; +$fa-var-free-code-camp: "\f2c5"; +$fa-var-frown-o: "\f119"; +$fa-var-futbol-o: "\f1e3"; +$fa-var-gamepad: "\f11b"; +$fa-var-gavel: "\f0e3"; +$fa-var-gbp: "\f154"; +$fa-var-ge: "\f1d1"; +$fa-var-gear: "\f013"; +$fa-var-gears: "\f085"; +$fa-var-genderless: "\f22d"; +$fa-var-get-pocket: "\f265"; +$fa-var-gg: "\f260"; +$fa-var-gg-circle: "\f261"; +$fa-var-gift: "\f06b"; +$fa-var-git: "\f1d3"; +$fa-var-git-square: "\f1d2"; +$fa-var-github: "\f09b"; +$fa-var-github-alt: "\f113"; +$fa-var-github-square: "\f092"; +$fa-var-gitlab: "\f296"; +$fa-var-gittip: "\f184"; +$fa-var-glass: "\f000"; +$fa-var-glide: "\f2a5"; +$fa-var-glide-g: "\f2a6"; +$fa-var-globe: "\f0ac"; +$fa-var-google: "\f1a0"; +$fa-var-google-plus: "\f0d5"; +$fa-var-google-plus-circle: "\f2b3"; +$fa-var-google-plus-official: "\f2b3"; +$fa-var-google-plus-square: "\f0d4"; +$fa-var-google-wallet: "\f1ee"; +$fa-var-graduation-cap: "\f19d"; +$fa-var-gratipay: "\f184"; +$fa-var-grav: "\f2d6"; +$fa-var-group: "\f0c0"; +$fa-var-h-square: "\f0fd"; +$fa-var-hacker-news: "\f1d4"; +$fa-var-hand-grab-o: "\f255"; +$fa-var-hand-lizard-o: "\f258"; +$fa-var-hand-o-down: "\f0a7"; +$fa-var-hand-o-left: "\f0a5"; +$fa-var-hand-o-right: "\f0a4"; +$fa-var-hand-o-up: "\f0a6"; +$fa-var-hand-paper-o: "\f256"; +$fa-var-hand-peace-o: "\f25b"; +$fa-var-hand-pointer-o: "\f25a"; +$fa-var-hand-rock-o: "\f255"; +$fa-var-hand-scissors-o: "\f257"; +$fa-var-hand-spock-o: "\f259"; +$fa-var-hand-stop-o: "\f256"; +$fa-var-handshake-o: "\f2b5"; +$fa-var-hard-of-hearing: "\f2a4"; +$fa-var-hashtag: "\f292"; +$fa-var-hdd-o: "\f0a0"; +$fa-var-header: "\f1dc"; +$fa-var-headphones: "\f025"; +$fa-var-heart: "\f004"; +$fa-var-heart-o: "\f08a"; +$fa-var-heartbeat: "\f21e"; +$fa-var-history: "\f1da"; +$fa-var-home: "\f015"; +$fa-var-hospital-o: "\f0f8"; +$fa-var-hotel: "\f236"; +$fa-var-hourglass: "\f254"; +$fa-var-hourglass-1: "\f251"; +$fa-var-hourglass-2: "\f252"; +$fa-var-hourglass-3: "\f253"; +$fa-var-hourglass-end: "\f253"; +$fa-var-hourglass-half: "\f252"; +$fa-var-hourglass-o: "\f250"; +$fa-var-hourglass-start: "\f251"; +$fa-var-houzz: "\f27c"; +$fa-var-html5: "\f13b"; +$fa-var-i-cursor: "\f246"; +$fa-var-id-badge: "\f2c1"; +$fa-var-id-card: "\f2c2"; +$fa-var-id-card-o: "\f2c3"; +$fa-var-ils: "\f20b"; +$fa-var-image: "\f03e"; +$fa-var-imdb: "\f2d8"; +$fa-var-inbox: "\f01c"; +$fa-var-indent: "\f03c"; +$fa-var-industry: "\f275"; +$fa-var-info: "\f129"; +$fa-var-info-circle: "\f05a"; +$fa-var-inr: "\f156"; +$fa-var-instagram: "\f16d"; +$fa-var-institution: "\f19c"; +$fa-var-internet-explorer: "\f26b"; +$fa-var-intersex: "\f224"; +$fa-var-ioxhost: "\f208"; +$fa-var-italic: "\f033"; +$fa-var-joomla: "\f1aa"; +$fa-var-jpy: "\f157"; +$fa-var-jsfiddle: "\f1cc"; +$fa-var-key: "\f084"; +$fa-var-keyboard-o: "\f11c"; +$fa-var-krw: "\f159"; +$fa-var-language: "\f1ab"; +$fa-var-laptop: "\f109"; +$fa-var-lastfm: "\f202"; +$fa-var-lastfm-square: "\f203"; +$fa-var-leaf: "\f06c"; +$fa-var-leanpub: "\f212"; +$fa-var-legal: "\f0e3"; +$fa-var-lemon-o: "\f094"; +$fa-var-level-down: "\f149"; +$fa-var-level-up: "\f148"; +$fa-var-life-bouy: "\f1cd"; +$fa-var-life-buoy: "\f1cd"; +$fa-var-life-ring: "\f1cd"; +$fa-var-life-saver: "\f1cd"; +$fa-var-lightbulb-o: "\f0eb"; +$fa-var-line-chart: "\f201"; +$fa-var-link: "\f0c1"; +$fa-var-linkedin: "\f0e1"; +$fa-var-linkedin-square: "\f08c"; +$fa-var-linode: "\f2b8"; +$fa-var-linux: "\f17c"; +$fa-var-list: "\f03a"; +$fa-var-list-alt: "\f022"; +$fa-var-list-ol: "\f0cb"; +$fa-var-list-ul: "\f0ca"; +$fa-var-location-arrow: "\f124"; +$fa-var-lock: "\f023"; +$fa-var-long-arrow-down: "\f175"; +$fa-var-long-arrow-left: "\f177"; +$fa-var-long-arrow-right: "\f178"; +$fa-var-long-arrow-up: "\f176"; +$fa-var-low-vision: "\f2a8"; +$fa-var-magic: "\f0d0"; +$fa-var-magnet: "\f076"; +$fa-var-mail-forward: "\f064"; +$fa-var-mail-reply: "\f112"; +$fa-var-mail-reply-all: "\f122"; +$fa-var-male: "\f183"; +$fa-var-map: "\f279"; +$fa-var-map-marker: "\f041"; +$fa-var-map-o: "\f278"; +$fa-var-map-pin: "\f276"; +$fa-var-map-signs: "\f277"; +$fa-var-mars: "\f222"; +$fa-var-mars-double: "\f227"; +$fa-var-mars-stroke: "\f229"; +$fa-var-mars-stroke-h: "\f22b"; +$fa-var-mars-stroke-v: "\f22a"; +$fa-var-maxcdn: "\f136"; +$fa-var-meanpath: "\f20c"; +$fa-var-medium: "\f23a"; +$fa-var-medkit: "\f0fa"; +$fa-var-meetup: "\f2e0"; +$fa-var-meh-o: "\f11a"; +$fa-var-mercury: "\f223"; +$fa-var-microchip: "\f2db"; +$fa-var-microphone: "\f130"; +$fa-var-microphone-slash: "\f131"; +$fa-var-minus: "\f068"; +$fa-var-minus-circle: "\f056"; +$fa-var-minus-square: "\f146"; +$fa-var-minus-square-o: "\f147"; +$fa-var-mixcloud: "\f289"; +$fa-var-mobile: "\f10b"; +$fa-var-mobile-phone: "\f10b"; +$fa-var-modx: "\f285"; +$fa-var-money: "\f0d6"; +$fa-var-moon-o: "\f186"; +$fa-var-mortar-board: "\f19d"; +$fa-var-motorcycle: "\f21c"; +$fa-var-mouse-pointer: "\f245"; +$fa-var-music: "\f001"; +$fa-var-navicon: "\f0c9"; +$fa-var-neuter: "\f22c"; +$fa-var-newspaper-o: "\f1ea"; +$fa-var-object-group: "\f247"; +$fa-var-object-ungroup: "\f248"; +$fa-var-odnoklassniki: "\f263"; +$fa-var-odnoklassniki-square: "\f264"; +$fa-var-opencart: "\f23d"; +$fa-var-openid: "\f19b"; +$fa-var-opera: "\f26a"; +$fa-var-optin-monster: "\f23c"; +$fa-var-outdent: "\f03b"; +$fa-var-pagelines: "\f18c"; +$fa-var-paint-brush: "\f1fc"; +$fa-var-paper-plane: "\f1d8"; +$fa-var-paper-plane-o: "\f1d9"; +$fa-var-paperclip: "\f0c6"; +$fa-var-paragraph: "\f1dd"; +$fa-var-paste: "\f0ea"; +$fa-var-pause: "\f04c"; +$fa-var-pause-circle: "\f28b"; +$fa-var-pause-circle-o: "\f28c"; +$fa-var-paw: "\f1b0"; +$fa-var-paypal: "\f1ed"; +$fa-var-pencil: "\f040"; +$fa-var-pencil-square: "\f14b"; +$fa-var-pencil-square-o: "\f044"; +$fa-var-percent: "\f295"; +$fa-var-phone: "\f095"; +$fa-var-phone-square: "\f098"; +$fa-var-photo: "\f03e"; +$fa-var-picture-o: "\f03e"; +$fa-var-pie-chart: "\f200"; +$fa-var-pied-piper: "\f2ae"; +$fa-var-pied-piper-alt: "\f1a8"; +$fa-var-pied-piper-pp: "\f1a7"; +$fa-var-pinterest: "\f0d2"; +$fa-var-pinterest-p: "\f231"; +$fa-var-pinterest-square: "\f0d3"; +$fa-var-plane: "\f072"; +$fa-var-play: "\f04b"; +$fa-var-play-circle: "\f144"; +$fa-var-play-circle-o: "\f01d"; +$fa-var-plug: "\f1e6"; +$fa-var-plus: "\f067"; +$fa-var-plus-circle: "\f055"; +$fa-var-plus-square: "\f0fe"; +$fa-var-plus-square-o: "\f196"; +$fa-var-podcast: "\f2ce"; +$fa-var-power-off: "\f011"; +$fa-var-print: "\f02f"; +$fa-var-product-hunt: "\f288"; +$fa-var-puzzle-piece: "\f12e"; +$fa-var-qq: "\f1d6"; +$fa-var-qrcode: "\f029"; +$fa-var-question: "\f128"; +$fa-var-question-circle: "\f059"; +$fa-var-question-circle-o: "\f29c"; +$fa-var-quora: "\f2c4"; +$fa-var-quote-left: "\f10d"; +$fa-var-quote-right: "\f10e"; +$fa-var-ra: "\f1d0"; +$fa-var-random: "\f074"; +$fa-var-ravelry: "\f2d9"; +$fa-var-rebel: "\f1d0"; +$fa-var-recycle: "\f1b8"; +$fa-var-reddit: "\f1a1"; +$fa-var-reddit-alien: "\f281"; +$fa-var-reddit-square: "\f1a2"; +$fa-var-refresh: "\f021"; +$fa-var-registered: "\f25d"; +$fa-var-remove: "\f00d"; +$fa-var-renren: "\f18b"; +$fa-var-reorder: "\f0c9"; +$fa-var-repeat: "\f01e"; +$fa-var-reply: "\f112"; +$fa-var-reply-all: "\f122"; +$fa-var-resistance: "\f1d0"; +$fa-var-retweet: "\f079"; +$fa-var-rmb: "\f157"; +$fa-var-road: "\f018"; +$fa-var-rocket: "\f135"; +$fa-var-rotate-left: "\f0e2"; +$fa-var-rotate-right: "\f01e"; +$fa-var-rouble: "\f158"; +$fa-var-rss: "\f09e"; +$fa-var-rss-square: "\f143"; +$fa-var-rub: "\f158"; +$fa-var-ruble: "\f158"; +$fa-var-rupee: "\f156"; +$fa-var-s15: "\f2cd"; +$fa-var-safari: "\f267"; +$fa-var-save: "\f0c7"; +$fa-var-scissors: "\f0c4"; +$fa-var-scribd: "\f28a"; +$fa-var-search: "\f002"; +$fa-var-search-minus: "\f010"; +$fa-var-search-plus: "\f00e"; +$fa-var-sellsy: "\f213"; +$fa-var-send: "\f1d8"; +$fa-var-send-o: "\f1d9"; +$fa-var-server: "\f233"; +$fa-var-share: "\f064"; +$fa-var-share-alt: "\f1e0"; +$fa-var-share-alt-square: "\f1e1"; +$fa-var-share-square: "\f14d"; +$fa-var-share-square-o: "\f045"; +$fa-var-shekel: "\f20b"; +$fa-var-sheqel: "\f20b"; +$fa-var-shield: "\f132"; +$fa-var-ship: "\f21a"; +$fa-var-shirtsinbulk: "\f214"; +$fa-var-shopping-bag: "\f290"; +$fa-var-shopping-basket: "\f291"; +$fa-var-shopping-cart: "\f07a"; +$fa-var-shower: "\f2cc"; +$fa-var-sign-in: "\f090"; +$fa-var-sign-language: "\f2a7"; +$fa-var-sign-out: "\f08b"; +$fa-var-signal: "\f012"; +$fa-var-signing: "\f2a7"; +$fa-var-simplybuilt: "\f215"; +$fa-var-sitemap: "\f0e8"; +$fa-var-skyatlas: "\f216"; +$fa-var-skype: "\f17e"; +$fa-var-slack: "\f198"; +$fa-var-sliders: "\f1de"; +$fa-var-slideshare: "\f1e7"; +$fa-var-smile-o: "\f118"; +$fa-var-snapchat: "\f2ab"; +$fa-var-snapchat-ghost: "\f2ac"; +$fa-var-snapchat-square: "\f2ad"; +$fa-var-snowflake-o: "\f2dc"; +$fa-var-soccer-ball-o: "\f1e3"; +$fa-var-sort: "\f0dc"; +$fa-var-sort-alpha-asc: "\f15d"; +$fa-var-sort-alpha-desc: "\f15e"; +$fa-var-sort-amount-asc: "\f160"; +$fa-var-sort-amount-desc: "\f161"; +$fa-var-sort-asc: "\f0de"; +$fa-var-sort-desc: "\f0dd"; +$fa-var-sort-down: "\f0dd"; +$fa-var-sort-numeric-asc: "\f162"; +$fa-var-sort-numeric-desc: "\f163"; +$fa-var-sort-up: "\f0de"; +$fa-var-soundcloud: "\f1be"; +$fa-var-space-shuttle: "\f197"; +$fa-var-spinner: "\f110"; +$fa-var-spoon: "\f1b1"; +$fa-var-spotify: "\f1bc"; +$fa-var-square: "\f0c8"; +$fa-var-square-o: "\f096"; +$fa-var-stack-exchange: "\f18d"; +$fa-var-stack-overflow: "\f16c"; +$fa-var-star: "\f005"; +$fa-var-star-half: "\f089"; +$fa-var-star-half-empty: "\f123"; +$fa-var-star-half-full: "\f123"; +$fa-var-star-half-o: "\f123"; +$fa-var-star-o: "\f006"; +$fa-var-steam: "\f1b6"; +$fa-var-steam-square: "\f1b7"; +$fa-var-step-backward: "\f048"; +$fa-var-step-forward: "\f051"; +$fa-var-stethoscope: "\f0f1"; +$fa-var-sticky-note: "\f249"; +$fa-var-sticky-note-o: "\f24a"; +$fa-var-stop: "\f04d"; +$fa-var-stop-circle: "\f28d"; +$fa-var-stop-circle-o: "\f28e"; +$fa-var-street-view: "\f21d"; +$fa-var-strikethrough: "\f0cc"; +$fa-var-stumbleupon: "\f1a4"; +$fa-var-stumbleupon-circle: "\f1a3"; +$fa-var-subscript: "\f12c"; +$fa-var-subway: "\f239"; +$fa-var-suitcase: "\f0f2"; +$fa-var-sun-o: "\f185"; +$fa-var-superpowers: "\f2dd"; +$fa-var-superscript: "\f12b"; +$fa-var-support: "\f1cd"; +$fa-var-table: "\f0ce"; +$fa-var-tablet: "\f10a"; +$fa-var-tachometer: "\f0e4"; +$fa-var-tag: "\f02b"; +$fa-var-tags: "\f02c"; +$fa-var-tasks: "\f0ae"; +$fa-var-taxi: "\f1ba"; +$fa-var-telegram: "\f2c6"; +$fa-var-television: "\f26c"; +$fa-var-tencent-weibo: "\f1d5"; +$fa-var-terminal: "\f120"; +$fa-var-text-height: "\f034"; +$fa-var-text-width: "\f035"; +$fa-var-th: "\f00a"; +$fa-var-th-large: "\f009"; +$fa-var-th-list: "\f00b"; +$fa-var-themeisle: "\f2b2"; +$fa-var-thermometer: "\f2c7"; +$fa-var-thermometer-0: "\f2cb"; +$fa-var-thermometer-1: "\f2ca"; +$fa-var-thermometer-2: "\f2c9"; +$fa-var-thermometer-3: "\f2c8"; +$fa-var-thermometer-4: "\f2c7"; +$fa-var-thermometer-empty: "\f2cb"; +$fa-var-thermometer-full: "\f2c7"; +$fa-var-thermometer-half: "\f2c9"; +$fa-var-thermometer-quarter: "\f2ca"; +$fa-var-thermometer-three-quarters: "\f2c8"; +$fa-var-thumb-tack: "\f08d"; +$fa-var-thumbs-down: "\f165"; +$fa-var-thumbs-o-down: "\f088"; +$fa-var-thumbs-o-up: "\f087"; +$fa-var-thumbs-up: "\f164"; +$fa-var-ticket: "\f145"; +$fa-var-times: "\f00d"; +$fa-var-times-circle: "\f057"; +$fa-var-times-circle-o: "\f05c"; +$fa-var-times-rectangle: "\f2d3"; +$fa-var-times-rectangle-o: "\f2d4"; +$fa-var-tint: "\f043"; +$fa-var-toggle-down: "\f150"; +$fa-var-toggle-left: "\f191"; +$fa-var-toggle-off: "\f204"; +$fa-var-toggle-on: "\f205"; +$fa-var-toggle-right: "\f152"; +$fa-var-toggle-up: "\f151"; +$fa-var-trademark: "\f25c"; +$fa-var-train: "\f238"; +$fa-var-transgender: "\f224"; +$fa-var-transgender-alt: "\f225"; +$fa-var-trash: "\f1f8"; +$fa-var-trash-o: "\f014"; +$fa-var-tree: "\f1bb"; +$fa-var-trello: "\f181"; +$fa-var-tripadvisor: "\f262"; +$fa-var-trophy: "\f091"; +$fa-var-truck: "\f0d1"; +$fa-var-try: "\f195"; +$fa-var-tty: "\f1e4"; +$fa-var-tumblr: "\f173"; +$fa-var-tumblr-square: "\f174"; +$fa-var-turkish-lira: "\f195"; +$fa-var-tv: "\f26c"; +$fa-var-twitch: "\f1e8"; +$fa-var-twitter: "\f099"; +$fa-var-twitter-square: "\f081"; +$fa-var-umbrella: "\f0e9"; +$fa-var-underline: "\f0cd"; +$fa-var-undo: "\f0e2"; +$fa-var-universal-access: "\f29a"; +$fa-var-university: "\f19c"; +$fa-var-unlink: "\f127"; +$fa-var-unlock: "\f09c"; +$fa-var-unlock-alt: "\f13e"; +$fa-var-unsorted: "\f0dc"; +$fa-var-upload: "\f093"; +$fa-var-usb: "\f287"; +$fa-var-usd: "\f155"; +$fa-var-user: "\f007"; +$fa-var-user-circle: "\f2bd"; +$fa-var-user-circle-o: "\f2be"; +$fa-var-user-md: "\f0f0"; +$fa-var-user-o: "\f2c0"; +$fa-var-user-plus: "\f234"; +$fa-var-user-secret: "\f21b"; +$fa-var-user-times: "\f235"; +$fa-var-users: "\f0c0"; +$fa-var-vcard: "\f2bb"; +$fa-var-vcard-o: "\f2bc"; +$fa-var-venus: "\f221"; +$fa-var-venus-double: "\f226"; +$fa-var-venus-mars: "\f228"; +$fa-var-viacoin: "\f237"; +$fa-var-viadeo: "\f2a9"; +$fa-var-viadeo-square: "\f2aa"; +$fa-var-video-camera: "\f03d"; +$fa-var-vimeo: "\f27d"; +$fa-var-vimeo-square: "\f194"; +$fa-var-vine: "\f1ca"; +$fa-var-vk: "\f189"; +$fa-var-volume-control-phone: "\f2a0"; +$fa-var-volume-down: "\f027"; +$fa-var-volume-off: "\f026"; +$fa-var-volume-up: "\f028"; +$fa-var-warning: "\f071"; +$fa-var-wechat: "\f1d7"; +$fa-var-weibo: "\f18a"; +$fa-var-weixin: "\f1d7"; +$fa-var-whatsapp: "\f232"; +$fa-var-wheelchair: "\f193"; +$fa-var-wheelchair-alt: "\f29b"; +$fa-var-wifi: "\f1eb"; +$fa-var-wikipedia-w: "\f266"; +$fa-var-window-close: "\f2d3"; +$fa-var-window-close-o: "\f2d4"; +$fa-var-window-maximize: "\f2d0"; +$fa-var-window-minimize: "\f2d1"; +$fa-var-window-restore: "\f2d2"; +$fa-var-windows: "\f17a"; +$fa-var-won: "\f159"; +$fa-var-wordpress: "\f19a"; +$fa-var-wpbeginner: "\f297"; +$fa-var-wpexplorer: "\f2de"; +$fa-var-wpforms: "\f298"; +$fa-var-wrench: "\f0ad"; +$fa-var-xing: "\f168"; +$fa-var-xing-square: "\f169"; +$fa-var-y-combinator: "\f23b"; +$fa-var-y-combinator-square: "\f1d4"; +$fa-var-yahoo: "\f19e"; +$fa-var-yc: "\f23b"; +$fa-var-yc-square: "\f1d4"; +$fa-var-yelp: "\f1e9"; +$fa-var-yen: "\f157"; +$fa-var-yoast: "\f2b1"; +$fa-var-youtube: "\f167"; +$fa-var-youtube-play: "\f16a"; +$fa-var-youtube-square: "\f166"; diff --git a/public/sass/components/_drop.scss b/public/sass/components/_drop.scss index 8d9d4fc6b7d..6568414ed88 100644 --- a/public/sass/components/_drop.scss +++ b/public/sass/components/_drop.scss @@ -5,13 +5,13 @@ $useDropShadow: false; $attachmentOffset: 0%; $easing: cubic-bezier(0, 0, 0.265, 1); -@include drop-theme('error', $popover-error-bg, $popover-color); -@include drop-theme('popover', $popover-bg, $popover-color, $popover-border-color); -@include drop-theme('help', $popover-help-bg, $popover-help-color); +@include drop-theme("error", $popover-error-bg, $popover-color); +@include drop-theme("popover", $popover-bg, $popover-color, $popover-border-color); +@include drop-theme("help", $popover-help-bg, $popover-help-color); -@include drop-animation-scale('drop', 'help', $attachmentOffset: $attachmentOffset, $easing: $easing); -@include drop-animation-scale('drop', 'error', $attachmentOffset: $attachmentOffset, $easing: $easing); -@include drop-animation-scale('drop', 'popover', $attachmentOffset: $attachmentOffset, $easing: $easing); +@include drop-animation-scale("drop", "help", $attachmentOffset: $attachmentOffset, $easing: $easing); +@include drop-animation-scale("drop", "error", $attachmentOffset: $attachmentOffset, $easing: $easing); +@include drop-animation-scale("drop", "popover", $attachmentOffset: $attachmentOffset, $easing: $easing); .drop-element { z-index: 10000; diff --git a/public/sass/components/_filter-list.scss b/public/sass/components/_filter-list.scss index 90d0a21c539..7713aa05ac2 100644 --- a/public/sass/components/_filter-list.scss +++ b/public/sass/components/_filter-list.scss @@ -67,17 +67,17 @@ text-transform: uppercase; &.online { - background-image: url('/img/online.svg'); + background-image: url("/img/online.svg"); color: $online; } &.warn { - background-image: url('/img/warn-tiny.svg'); + background-image: url("/img/warn-tiny.svg"); color: $warn; } &.critical { - background-image: url('/img/critical.svg'); + background-image: url("/img/critical.svg"); color: $critical; } } diff --git a/public/sass/components/_footer.scss b/public/sass/components/_footer.scss index cec6f820118..8b7d64e47fe 100644 --- a/public/sass/components/_footer.scss +++ b/public/sass/components/_footer.scss @@ -25,7 +25,7 @@ display: inline-block; padding-right: 2px; &::after { - content: ' | '; + content: " | "; padding-left: 2px; } } @@ -33,7 +33,7 @@ li:last-child { &::after { padding-left: 0; - content: ''; + content: ""; } } } diff --git a/public/sass/components/_json_explorer.scss b/public/sass/components/_json_explorer.scss index aa212cd2dab..2b1be8bd4f5 100644 --- a/public/sass/components/_json_explorer.scss +++ b/public/sass/components/_json_explorer.scss @@ -21,10 +21,10 @@ display: none; } &.json-formatter-object::after { - content: 'No properties'; + content: "No properties"; } &.json-formatter-array::after { - content: '[]'; + content: "[]"; } } } @@ -87,7 +87,7 @@ &::after { display: inline-block; transition: transform $json-explorer-rotate-time ease-in; - content: '►'; + content: "►"; } } diff --git a/public/sass/components/_jsontree.scss b/public/sass/components/_jsontree.scss index 0a0497a0627..665deda0f12 100644 --- a/public/sass/components/_jsontree.scss +++ b/public/sass/components/_jsontree.scss @@ -35,12 +35,12 @@ json-tree { color: $variable; padding: 5px 10px 5px 15px; &::after { - content: ':'; + content: ":"; } } json-node.expandable { &::before { - content: '\25b6'; + content: "\25b6"; position: absolute; left: 0px; font-size: 8px; diff --git a/public/sass/components/_panel_gettingstarted.scss b/public/sass/components/_panel_gettingstarted.scss index 7b935e3707d..1fb3eda1834 100644 --- a/public/sass/components/_panel_gettingstarted.scss +++ b/public/sass/components/_panel_gettingstarted.scss @@ -52,7 +52,7 @@ $path-position: $marker-size-half - ($path-height / 2); &::after { right: -50%; - content: ''; + content: ""; display: block; position: absolute; z-index: 1; @@ -105,7 +105,7 @@ $path-position: $marker-size-half - ($path-height / 2); // change icon to check .icon-gf::before { - content: '\e604'; + content: "\e604"; } } .progress-text { diff --git a/public/sass/components/_row.scss b/public/sass/components/_row.scss index 9cd564a4edf..3c1465a30bc 100644 --- a/public/sass/components/_row.scss +++ b/public/sass/components/_row.scss @@ -69,7 +69,7 @@ cursor: move; width: 1rem; height: 100%; - background: url('../img/grab_dark.svg') no-repeat 50% 50%; + background: url("../img/grab_dark.svg") no-repeat 50% 50%; background-size: 8px; visibility: hidden; position: absolute; diff --git a/public/sass/components/_shortcuts.scss b/public/sass/components/_shortcuts.scss index 83e112648cf..b5f61872585 100644 --- a/public/sass/components/_shortcuts.scss +++ b/public/sass/components/_shortcuts.scss @@ -33,7 +33,7 @@ text-align: center; margin-right: 0.3rem; padding: 3px 5px; - font: 11px Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: 10px; color: #555; vertical-align: middle; diff --git a/public/sass/components/_switch.scss b/public/sass/components/_switch.scss index 11ab2e3554a..c7eb1914103 100644 --- a/public/sass/components/_switch.scss +++ b/public/sass/components/_switch.scss @@ -64,8 +64,8 @@ } input + label::before { - font-family: 'FontAwesome'; - content: '\f096'; // square-o + font-family: "FontAwesome"; + content: "\f096"; // square-o color: $text-color-weak; transition: transform 0.4s; backface-visibility: hidden; @@ -73,11 +73,11 @@ } input + label::after { - content: '\f046'; // check-square-o + content: "\f046"; // check-square-o color: $orange; text-shadow: $text-shadow-strong; - font-family: 'FontAwesome'; + font-family: "FontAwesome"; transition: transform 0.4s; transform: rotateY(180deg); backface-visibility: hidden; diff --git a/public/sass/components/_tabs.scss b/public/sass/components/_tabs.scss index 44a116cd0a4..197d5892652 100644 --- a/public/sass/components/_tabs.scss +++ b/public/sass/components/_tabs.scss @@ -44,13 +44,18 @@ &::before { display: block; - content: ' '; + content: " "; position: absolute; left: 0; right: 0; height: 2px; top: 0; - background-image: linear-gradient(to right, #ffd500 0%, #ff4400 99%, #ff4400 100%); + background-image: linear-gradient( + to right, + #ffd500 0%, + #ff4400 99%, + #ff4400 100% + ); } } } diff --git a/public/sass/components/_timepicker.scss b/public/sass/components/_timepicker.scss index b9e39ae9e04..2d7a12c3d01 100644 --- a/public/sass/components/_timepicker.scss +++ b/public/sass/components/_timepicker.scss @@ -103,10 +103,10 @@ } .fa-chevron-left::before { - content: '\f053'; + content: "\f053"; } .fa-chevron-right::before { - content: '\f054'; + content: "\f054"; } .glyphicon-chevron-right { diff --git a/public/sass/grafana.dark.scss b/public/sass/grafana.dark.scss index f7f5163f36f..53193d213e6 100644 --- a/public/sass/grafana.dark.scss +++ b/public/sass/grafana.dark.scss @@ -1,3 +1,3 @@ -@import 'variables'; -@import 'variables.dark'; -@import 'grafana'; +@import "variables"; +@import "variables.dark"; +@import "grafana"; diff --git a/public/sass/mixins/_drop_element.scss b/public/sass/mixins/_drop_element.scss index eb354b219fc..e7e53382e0e 100644 --- a/public/sass/mixins/_drop_element.scss +++ b/public/sass/mixins/_drop_element.scss @@ -15,7 +15,7 @@ border: 1px solid $border-color; &:before { - content: ''; + content: ""; display: block; position: absolute; width: 0; @@ -88,7 +88,8 @@ left: $popover-arrow-size * 2; } - &.drop-element-attached-top.drop-element-attached-left.drop-target-attached-middle .drop-content { + &.drop-element-attached-top.drop-element-attached-left.drop-target-attached-middle + .drop-content { margin-top: $popover-arrow-size; &:before { @@ -98,7 +99,8 @@ } } - &.drop-element-attached-top.drop-element-attached-right.drop-target-attached-middle .drop-content { + &.drop-element-attached-top.drop-element-attached-right.drop-target-attached-middle + .drop-content { margin-top: $popover-arrow-size; &:before { @@ -108,7 +110,8 @@ } } - &.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-middle .drop-content { + &.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-middle + .drop-content { margin-bottom: $popover-arrow-size; &:before { @@ -118,7 +121,8 @@ } } - &.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-middle .drop-content { + &.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-middle + .drop-content { margin-bottom: $popover-arrow-size; &:before { @@ -129,7 +133,8 @@ } // Top and bottom corners - &.drop-element-attached-top.drop-element-attached-left.drop-target-attached-bottom .drop-content { + &.drop-element-attached-top.drop-element-attached-left.drop-target-attached-bottom + .drop-content { margin-top: $popover-arrow-size; &:before { @@ -139,7 +144,8 @@ } } - &.drop-element-attached-top.drop-element-attached-right.drop-target-attached-bottom .drop-content { + &.drop-element-attached-top.drop-element-attached-right.drop-target-attached-bottom + .drop-content { margin-top: $popover-arrow-size; &:before { @@ -149,7 +155,8 @@ } } - &.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-top .drop-content { + &.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-top + .drop-content { margin-bottom: $popover-arrow-size; &:before { @@ -159,7 +166,8 @@ } } - &.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-top .drop-content { + &.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-top + .drop-content { margin-bottom: $popover-arrow-size; &:before { @@ -170,7 +178,8 @@ } // Side corners - &.drop-element-attached-top.drop-element-attached-right.drop-target-attached-left .drop-content { + &.drop-element-attached-top.drop-element-attached-right.drop-target-attached-left + .drop-content { margin-right: $popover-arrow-size; &:before { @@ -180,7 +189,8 @@ } } - &.drop-element-attached-top.drop-element-attached-left.drop-target-attached-right .drop-content { + &.drop-element-attached-top.drop-element-attached-left.drop-target-attached-right + .drop-content { margin-left: $popover-arrow-size; &:before { @@ -190,7 +200,8 @@ } } - &.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-left .drop-content { + &.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-left + .drop-content { margin-right: $popover-arrow-size; &:before { @@ -200,7 +211,8 @@ } } - &.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-right .drop-content { + &.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-right + .drop-content { margin-left: $popover-arrow-size; &:before { @@ -212,7 +224,7 @@ } } -@mixin drop-animation-scale($themePrefix: 'drop', $themeName: 'default', $attachmentOffset: 0, $easing: 'linear') { +@mixin drop-animation-scale($themePrefix: "drop", $themeName: "default", $attachmentOffset: 0, $easing: "linear") { .#{$themePrefix}-element.#{$themePrefix}-#{$themeName} { transform: translateZ(0); transition: opacity 100ms; @@ -235,16 +247,20 @@ } } // Centers and middles - &.#{$themePrefix}-element-attached-bottom.#{$themePrefix}-element-attached-center .#{$themePrefix}-content { + &.#{$themePrefix}-element-attached-bottom.#{$themePrefix}-element-attached-center + .#{$themePrefix}-content { transform-origin: 50% calc(100% + #{$attachmentOffset}); } - &.#{$themePrefix}-element-attached-top.#{$themePrefix}-element-attached-center .#{$themePrefix}-content { + &.#{$themePrefix}-element-attached-top.#{$themePrefix}-element-attached-center + .#{$themePrefix}-content { transform-origin: 50% (-$attachmentOffset); } - &.#{$themePrefix}-element-attached-right.#{$themePrefix}-element-attached-middle .#{$themePrefix}-content { + &.#{$themePrefix}-element-attached-right.#{$themePrefix}-element-attached-middle + .#{$themePrefix}-content { transform-origin: calc(100% + #{$attachmentOffset}) 50%; } - &.#{$themePrefix}-element-attached-left.#{$themePrefix}-element-attached-middle .#{$themePrefix}-content { + &.#{$themePrefix}-element-attached-left.#{$themePrefix}-element-attached-middle + .#{$themePrefix}-content { transform-origin: -($attachmentOffset 50%); } // Top and bottom corners diff --git a/public/sass/mixins/_forms.scss b/public/sass/mixins/_forms.scss index 2f163e0f46b..ce488f0f636 100644 --- a/public/sass/mixins/_forms.scss +++ b/public/sass/mixins/_forms.scss @@ -41,7 +41,8 @@ &:focus { border-color: $input-border-focus; outline: none; - $shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px $input-box-shadow-focus; + $shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), + 0 0 5px $input-box-shadow-focus; @include box-shadow($shadow); } } diff --git a/public/sass/mixins/_mixins.scss b/public/sass/mixins/_mixins.scss index 2e3c2fdcdd8..f3be6af56ba 100644 --- a/public/sass/mixins/_mixins.scss +++ b/public/sass/mixins/_mixins.scss @@ -1,6 +1,6 @@ @mixin clearfix() { &::after { - content: ''; + content: ""; display: table; clear: both; } @@ -265,10 +265,20 @@ // Add an alphatransparency value to any background or border color (via Elyse Holladay) #translucent { @mixin background($color: $white, $alpha: 1) { - background-color: hsla(hue($color), saturation($color), lightness($color), $alpha); + background-color: hsla( + hue($color), + saturation($color), + lightness($color), + $alpha + ); } @mixin border($color: $white, $alpha: 1) { - border-color: hsla(hue($color), saturation($color), lightness($color), $alpha); + border-color: hsla( + hue($color), + saturation($color), + lightness($color), + $alpha + ); @include background-clip(padding-box); } } @@ -284,37 +294,66 @@ // Gradients @mixin gradient-horizontal($startColor: #555, $endColor: #333) { background-color: $endColor; - background-image: linear-gradient(to right, $startColor, $endColor); // Standard, IE10 + background-image: linear-gradient( + to right, + $startColor, + $endColor + ); // Standard, IE10 background-repeat: repeat-x; } @mixin gradient-vertical($startColor: #555, $endColor: #333) { background-color: mix($startColor, $endColor, 60%); - background-image: linear-gradient(to bottom, $startColor, $endColor); // Standard, IE10 + background-image: linear-gradient( + to bottom, + $startColor, + $endColor + ); // Standard, IE10 background-repeat: repeat-x; } @mixin gradient-directional($startColor: #555, $endColor: #333, $deg: 45deg) { background-color: $endColor; background-repeat: repeat-x; - background-image: linear-gradient($deg, $startColor, $endColor); // Standard, IE10 + background-image: linear-gradient( + $deg, + $startColor, + $endColor + ); // Standard, IE10 } @mixin gradient-horizontal-three-colors($startColor: #00b3ee, $midColor: #7a43b6, $colorStop: 50%, $endColor: #c3325f) { background-color: mix($midColor, $endColor, 80%); - background-image: linear-gradient(to right, $startColor, $midColor $colorStop, $endColor); + background-image: linear-gradient( + to right, + $startColor, + $midColor $colorStop, + $endColor + ); background-repeat: no-repeat; } @mixin gradient-vertical-three-colors($startColor: #00b3ee, $midColor: #7a43b6, $colorStop: 50%, $endColor: #c3325f) { background-color: mix($midColor, $endColor, 80%); - background-image: linear-gradient($startColor, $midColor $colorStop, $endColor); + background-image: linear-gradient( + $startColor, + $midColor $colorStop, + $endColor + ); background-repeat: no-repeat; } @mixin gradient-radial($innerColor: #555, $outerColor: #333) { background-color: $outerColor; - background-image: -webkit-gradient(radial, center center, 0, center center, 460, from($innerColor), to($outerColor)); + background-image: -webkit-gradient( + radial, + center center, + 0, + center center, + 460, + from($innerColor), + to($outerColor) + ); background-image: -webkit-radial-gradient(circle, $innerColor, $outerColor); background-image: -moz-radial-gradient(circle, $innerColor, $outerColor); background-image: -o-radial-gradient(circle, $innerColor, $outerColor); @@ -341,7 +380,11 @@ @mixin left-brand-border-gradient() { border: none; - border-image: linear-gradient(rgba(255, 213, 0, 1) 0%, rgba(255, 68, 0, 1) 99%, rgba(255, 68, 0, 1) 100%); + border-image: linear-gradient( + rgba(255, 213, 0, 1) 0%, + rgba(255, 68, 0, 1) 99%, + rgba(255, 68, 0, 1) 100% + ); border-image-slice: 1; border-style: solid; border-top: 0; diff --git a/public/sass/pages/_login.scss b/public/sass/pages/_login.scss index ce1e9eb4ea1..8622eec4e99 100644 --- a/public/sass/pages/_login.scss +++ b/public/sass/pages/_login.scss @@ -371,7 +371,7 @@ select:-webkit-autofill:focus { left: 0; right: 0; height: 100%; - content: ''; + content: ""; display: block; } diff --git a/public/sass/pages/_playlist.scss b/public/sass/pages/_playlist.scss index 37fa7f17e20..5dd1c92cbd2 100644 --- a/public/sass/pages/_playlist.scss +++ b/public/sass/pages/_playlist.scss @@ -84,11 +84,11 @@ background-color: $list-item-bg; margin-bottom: 4px; .search-result-icon:before { - content: '\f009'; + content: "\f009"; } &.search-item-dash-home .search-result-icon:before { - content: '\f015'; + content: "\f015"; } } diff --git a/public/sass/utils/_validation.scss b/public/sass/utils/_validation.scss index c074d847490..86b7c008bfd 100644 --- a/public/sass/utils/_validation.scss +++ b/public/sass/utils/_validation.scss @@ -1,4 +1,4 @@ -input[type='text'].ng-dirty.ng-invalid { +input[type="text"].ng-dirty.ng-invalid { } input.validation-error, diff --git a/public/test/core/utils/version_specs.ts b/public/test/core/utils/version_specs.ts index 9abc4f326fc..a057c8e16bd 100644 --- a/public/test/core/utils/version_specs.ts +++ b/public/test/core/utils/version_specs.ts @@ -1,8 +1,8 @@ -import { describe, beforeEach, it, expect } from 'test/lib/common'; +import {describe, beforeEach, it, expect} from 'test/lib/common'; -import { SemVersion, isVersionGtOrEq } from 'app/core/utils/version'; +import {SemVersion, isVersionGtOrEq} from 'app/core/utils/version'; -describe('SemVersion', () => { +describe("SemVersion", () => { let version = '1.0.0-alpha.1'; describe('parsing', () => { @@ -23,13 +23,13 @@ describe('SemVersion', () => { it('should detect greater version properly', () => { let semver = new SemVersion(version); let cases = [ - { value: '3.4.5', expected: true }, - { value: '3.4.4', expected: true }, - { value: '3.4.6', expected: false }, - { value: '4', expected: false }, - { value: '3.5', expected: false }, + {value: '3.4.5', expected: true}, + {value: '3.4.4', expected: true}, + {value: '3.4.6', expected: false}, + {value: '4', expected: false}, + {value: '3.5', expected: false}, ]; - cases.forEach(testCase => { + cases.forEach((testCase) => { expect(semver.isGtOrEq(testCase.value)).to.be(testCase.expected); }); }); @@ -38,16 +38,16 @@ describe('SemVersion', () => { describe('isVersionGtOrEq', () => { it('should compare versions properly (a >= b)', () => { let cases = [ - { values: ['3.4.5', '3.4.5'], expected: true }, - { values: ['3.4.5', '3.4.4'], expected: true }, - { values: ['3.4.5', '3.4.6'], expected: false }, - { values: ['3.4', '3.4.0'], expected: true }, - { values: ['3', '3.0.0'], expected: true }, - { values: ['3.1.1-beta1', '3.1'], expected: true }, - { values: ['3.4.5', '4'], expected: false }, - { values: ['3.4.5', '3.5'], expected: false }, + {values: ['3.4.5', '3.4.5'], expected: true}, + {values: ['3.4.5', '3.4.4'] , expected: true}, + {values: ['3.4.5', '3.4.6'], expected: false}, + {values: ['3.4', '3.4.0'], expected: true}, + {values: ['3', '3.0.0'], expected: true}, + {values: ['3.1.1-beta1', '3.1'], expected: true}, + {values: ['3.4.5', '4'], expected: false}, + {values: ['3.4.5', '3.5'], expected: false}, ]; - cases.forEach(testCase => { + cases.forEach((testCase) => { expect(isVersionGtOrEq(testCase.values[0], testCase.values[1])).to.be(testCase.expected); }); }); diff --git a/public/test/jest-shim.ts b/public/test/jest-shim.ts index f9af0c4a3f3..80c4bb3d21b 100644 --- a/public/test/jest-shim.ts +++ b/public/test/jest-shim.ts @@ -1,5 +1,6 @@ declare var global: NodeJS.Global; -(global).requestAnimationFrame = callback => { +(global).requestAnimationFrame = (callback) => { setTimeout(callback, 0); }; + diff --git a/public/test/mocks/backend_srv.ts b/public/test/mocks/backend_srv.ts index 32c04866395..666de593722 100644 --- a/public/test/mocks/backend_srv.ts +++ b/public/test/mocks/backend_srv.ts @@ -1,5 +1,8 @@ export class BackendSrvMock { search: any; - constructor() {} + constructor() { + } + } + diff --git a/public/test/specs/helpers.ts b/public/test/specs/helpers.ts index b92e853d8f8..8e83915362f 100644 --- a/public/test/specs/helpers.ts +++ b/public/test/specs/helpers.ts @@ -1,8 +1,8 @@ import _ from 'lodash'; import config from 'app/core/config'; import * as dateMath from 'app/core/utils/datemath'; -import { angularMocks, sinon } from '../lib/common'; -import { PanelModel } from 'app/features/dashboard/panel_model'; +import {angularMocks, sinon} from '../lib/common'; +import {PanelModel} from 'app/features/dashboard/panel_model'; export function ControllerTestContext() { var self = this; @@ -42,8 +42,8 @@ export function ControllerTestContext() { self.$location = $location; self.$browser = $browser; self.$q = $q; - self.panel = new PanelModel({ type: 'test' }); - self.dashboard = { meta: {} }; + self.panel = new PanelModel({type: 'test'}); + self.dashboard = {meta: {}}; $rootScope.appEvent = sinon.spy(); $rootScope.onAppEvent = sinon.spy(); @@ -53,14 +53,14 @@ export function ControllerTestContext() { $rootScope.colors.push('#' + i); } - config.panels['test'] = { info: {} }; + config.panels['test'] = {info: {}}; self.ctrl = $controller( Ctrl, - { $scope: self.scope }, + {$scope: self.scope}, { panel: self.panel, dashboard: self.dashboard, - } + }, ); }); }; @@ -72,7 +72,7 @@ export function ControllerTestContext() { self.$browser = $browser; self.scope.contextSrv = {}; self.scope.panel = {}; - self.scope.dashboard = { meta: {} }; + self.scope.dashboard = {meta: {}}; self.scope.dashboardMeta = {}; self.scope.dashboardViewState = new DashboardViewStateStub(); self.scope.appEvent = sinon.spy(); @@ -131,7 +131,7 @@ export function DashboardViewStateStub() { export function TimeSrvStub() { this.init = sinon.spy(); - this.time = { from: 'now-1h', to: 'now' }; + this.time = {from: 'now-1h', to: 'now'}; this.timeRange = function(parse) { if (parse === false) { return this.time; @@ -159,7 +159,7 @@ export function ContextSrvStub() { export function TemplateSrvStub() { this.variables = []; - this.templateSettings = { interpolate: /\[\[([\s\S]+?)\]\]/g }; + this.templateSettings = {interpolate: /\[\[([\s\S]+?)\]\]/g}; this.data = {}; this.replace = function(text) { return _.template(text, this.templateSettings)(this.data); @@ -188,7 +188,7 @@ var allDeps = { TimeSrvStub: TimeSrvStub, ControllerTestContext: ControllerTestContext, ServiceTestContext: ServiceTestContext, - DashboardViewStateStub: DashboardViewStateStub, + DashboardViewStateStub: DashboardViewStateStub }; // for legacy diff --git a/vendor/github.com/bradfitz/gomemcache/memcache/memcache.go b/vendor/github.com/bradfitz/gomemcache/memcache/memcache.go index 8508063bc35..b98a7653467 100644 --- a/vendor/github.com/bradfitz/gomemcache/memcache/memcache.go +++ b/vendor/github.com/bradfitz/gomemcache/memcache/memcache.go @@ -457,7 +457,7 @@ func (c *Client) GetMulti(keys []string) (map[string]*Item, error) { } var err error - for range keyMap { + for _ = range keyMap { if ge := <-ch; ge != nil { err = ge } diff --git a/vendor/github.com/hashicorp/go-plugin/client.go b/vendor/github.com/hashicorp/go-plugin/client.go index de03690703f..b912826b200 100644 --- a/vendor/github.com/hashicorp/go-plugin/client.go +++ b/vendor/github.com/hashicorp/go-plugin/client.go @@ -567,7 +567,7 @@ func (c *Client) Start() (addr net.Addr, err error) { // so they don't block since it is an io.Pipe defer func() { go func() { - for range linesCh { + for _ = range linesCh { } }() }() diff --git a/vendor/github.com/hashicorp/go-plugin/rpc_client.go b/vendor/github.com/hashicorp/go-plugin/rpc_client.go index 4d99d42c7e1..f30a4b1d387 100644 --- a/vendor/github.com/hashicorp/go-plugin/rpc_client.go +++ b/vendor/github.com/hashicorp/go-plugin/rpc_client.go @@ -75,7 +75,7 @@ func NewRPCClient(conn io.ReadWriteCloser, plugins map[string]Plugin) (*RPCClien // Connect stdout, stderr streams stdstream := make([]net.Conn, 2) - for i := range stdstream { + for i, _ := range stdstream { stdstream[i], err = mux.Open() if err != nil { mux.Close() diff --git a/vendor/github.com/hashicorp/go-plugin/rpc_server.go b/vendor/github.com/hashicorp/go-plugin/rpc_server.go index 168ef7dd944..5bb18dd5db1 100644 --- a/vendor/github.com/hashicorp/go-plugin/rpc_server.go +++ b/vendor/github.com/hashicorp/go-plugin/rpc_server.go @@ -78,7 +78,7 @@ func (s *RPCServer) ServeConn(conn io.ReadWriteCloser) { // Connect the stdstreams (in, out, err) stdstream := make([]net.Conn, 2) - for i := range stdstream { + for i, _ := range stdstream { stdstream[i], err = mux.Accept() if err != nil { mux.Close() diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go index 1857f93f226..82ad7bc8f1c 100644 --- a/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go +++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go @@ -85,7 +85,7 @@ func (dmp *DiffMatchPatch) diffMainRunes(text1, text2 []rune, checklines bool, d // Restore the prefix and suffix. if len(commonprefix) != 0 { - diffs = append([]Diff{{DiffEqual, string(commonprefix)}}, diffs...) + diffs = append([]Diff{Diff{DiffEqual, string(commonprefix)}}, diffs...) } if len(commonsuffix) != 0 { diffs = append(diffs, Diff{DiffEqual, string(commonsuffix)}) @@ -122,16 +122,16 @@ func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, dea } // Shorter text is inside the longer text (speedup). return []Diff{ - {op, string(longtext[:i])}, - {DiffEqual, string(shorttext)}, - {op, string(longtext[i+len(shorttext):])}, + Diff{op, string(longtext[:i])}, + Diff{DiffEqual, string(shorttext)}, + Diff{op, string(longtext[i+len(shorttext):])}, } } else if len(shorttext) == 1 { // Single character string. // After the previous speedup, the character can't be an equality. return []Diff{ - {DiffDelete, string(text1)}, - {DiffInsert, string(text2)}, + Diff{DiffDelete, string(text1)}, + Diff{DiffInsert, string(text2)}, } // Check to see if the problem can be split in two. } else if hm := dmp.diffHalfMatch(text1, text2); hm != nil { @@ -145,7 +145,7 @@ func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, dea diffsA := dmp.diffMainRunes(text1A, text2A, checklines, deadline) diffsB := dmp.diffMainRunes(text1B, text2B, checklines, deadline) // Merge the results. - return append(diffsA, append([]Diff{{DiffEqual, string(midCommon)}}, diffsB...)...) + return append(diffsA, append([]Diff{Diff{DiffEqual, string(midCommon)}}, diffsB...)...) } else if checklines && len(text1) > 100 && len(text2) > 100 { return dmp.diffLineMode(text1, text2, deadline) } @@ -330,8 +330,8 @@ func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time) } // Diff took too long and hit the deadline or number of diffs equals number of characters, no commonality at all. return []Diff{ - {DiffDelete, string(runes1)}, - {DiffInsert, string(runes2)}, + Diff{DiffDelete, string(runes1)}, + Diff{DiffInsert, string(runes2)}, } } @@ -673,7 +673,7 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff { insPoint := equalities.data diffs = append( diffs[:insPoint], - append([]Diff{{DiffDelete, lastequality}}, diffs[insPoint:]...)...) + append([]Diff{Diff{DiffDelete, lastequality}}, diffs[insPoint:]...)...) // Change second copy to insert. diffs[insPoint+1].Type = DiffInsert @@ -726,7 +726,7 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff { // Overlap found. Insert an equality and trim the surrounding edits. diffs = append( diffs[:pointer], - append([]Diff{{DiffEqual, insertion[:overlapLength1]}}, diffs[pointer:]...)...) + append([]Diff{Diff{DiffEqual, insertion[:overlapLength1]}}, diffs[pointer:]...)...) diffs[pointer-1].Text = deletion[0 : len(deletion)-overlapLength1] @@ -955,7 +955,7 @@ func (dmp *DiffMatchPatch) DiffCleanupEfficiency(diffs []Diff) []Diff { // Duplicate record. diffs = append(diffs[:insPoint], - append([]Diff{{DiffDelete, lastequality}}, diffs[insPoint:]...)...) + append([]Diff{Diff{DiffDelete, lastequality}}, diffs[insPoint:]...)...) // Change second copy to insert. diffs[insPoint+1].Type = DiffInsert @@ -1028,7 +1028,7 @@ func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff { if x > 0 && diffs[x-1].Type == DiffEqual { diffs[x-1].Text += string(textInsert[:commonlength]) } else { - diffs = append([]Diff{{DiffEqual, string(textInsert[:commonlength])}}, diffs...) + diffs = append([]Diff{Diff{DiffEqual, string(textInsert[:commonlength])}}, diffs...) pointer++ } textInsert = textInsert[commonlength:] diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go index 1708a96fbed..223c43c4268 100644 --- a/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go +++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go @@ -93,7 +93,7 @@ func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch { // Add the prefix. prefix := text[max(0, patch.Start2-padding):patch.Start2] if len(prefix) != 0 { - patch.diffs = append([]Diff{{DiffEqual, prefix}}, patch.diffs...) + patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...) } // Add the suffix. suffix := text[patch.Start2+patch.Length1 : min(len(text), patch.Start2+patch.Length1+padding)] @@ -336,7 +336,7 @@ func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string { // Add some padding on start of first diff. if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual { // Add nullPadding equality. - patches[0].diffs = append([]Diff{{DiffEqual, nullPadding}}, patches[0].diffs...) + patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...) patches[0].Start1 -= paddingLength // Should be 0. patches[0].Start2 -= paddingLength // Should be 0. patches[0].Length1 += paddingLength diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index a3fe975f049..e6b321f4bb6 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -321,9 +321,7 @@ func (noCachedConnError) Error() string { return "http2: no cached c // or its equivalent renamed type in net/http2's h2_bundle.go. Both types // may coexist in the same running program. func isNoCachedConnError(err error) bool { - _, ok := err.(interface { - IsHTTP2NoCachedConnError() - }) + _, ok := err.(interface{ IsHTTP2NoCachedConnError() }) return ok } diff --git a/vendor/golang.org/x/text/language/gen.go b/vendor/golang.org/x/text/language/gen.go index fea288d4621..302f1940aaf 100644 --- a/vendor/golang.org/x/text/language/gen.go +++ b/vendor/golang.org/x/text/language/gen.go @@ -1050,7 +1050,7 @@ func (b *builder) writeRegion() { m49Index := [9]int16{} fromM49 := []uint16{} m49 := []int{} - for k := range fromM49map { + for k, _ := range fromM49map { m49 = append(m49, int(k)) } sort.Ints(m49) diff --git a/vendor/golang.org/x/text/language/lookup.go b/vendor/golang.org/x/text/language/lookup.go index 96d16dac9d3..1d80ac37082 100644 --- a/vendor/golang.org/x/text/language/lookup.go +++ b/vendor/golang.org/x/text/language/lookup.go @@ -344,39 +344,39 @@ var ( // grandfatheredMap holds a mapping from legacy and grandfathered tags to // their base language or index to more elaborate tag. grandfatheredMap = map[[maxLen]byte]int16{ - {'a', 'r', 't', '-', 'l', 'o', 'j', 'b', 'a', 'n'}: _jbo, // art-lojban - {'i', '-', 'a', 'm', 'i'}: _ami, // i-ami - {'i', '-', 'b', 'n', 'n'}: _bnn, // i-bnn - {'i', '-', 'h', 'a', 'k'}: _hak, // i-hak - {'i', '-', 'k', 'l', 'i', 'n', 'g', 'o', 'n'}: _tlh, // i-klingon - {'i', '-', 'l', 'u', 'x'}: _lb, // i-lux - {'i', '-', 'n', 'a', 'v', 'a', 'j', 'o'}: _nv, // i-navajo - {'i', '-', 'p', 'w', 'n'}: _pwn, // i-pwn - {'i', '-', 't', 'a', 'o'}: _tao, // i-tao - {'i', '-', 't', 'a', 'y'}: _tay, // i-tay - {'i', '-', 't', 's', 'u'}: _tsu, // i-tsu - {'n', 'o', '-', 'b', 'o', 'k'}: _nb, // no-bok - {'n', 'o', '-', 'n', 'y', 'n'}: _nn, // no-nyn - {'s', 'g', 'n', '-', 'b', 'e', '-', 'f', 'r'}: _sfb, // sgn-BE-FR - {'s', 'g', 'n', '-', 'b', 'e', '-', 'n', 'l'}: _vgt, // sgn-BE-NL - {'s', 'g', 'n', '-', 'c', 'h', '-', 'd', 'e'}: _sgg, // sgn-CH-DE - {'z', 'h', '-', 'g', 'u', 'o', 'y', 'u'}: _cmn, // zh-guoyu - {'z', 'h', '-', 'h', 'a', 'k', 'k', 'a'}: _hak, // zh-hakka - {'z', 'h', '-', 'm', 'i', 'n', '-', 'n', 'a', 'n'}: _nan, // zh-min-nan - {'z', 'h', '-', 'x', 'i', 'a', 'n', 'g'}: _hsn, // zh-xiang + [maxLen]byte{'a', 'r', 't', '-', 'l', 'o', 'j', 'b', 'a', 'n'}: _jbo, // art-lojban + [maxLen]byte{'i', '-', 'a', 'm', 'i'}: _ami, // i-ami + [maxLen]byte{'i', '-', 'b', 'n', 'n'}: _bnn, // i-bnn + [maxLen]byte{'i', '-', 'h', 'a', 'k'}: _hak, // i-hak + [maxLen]byte{'i', '-', 'k', 'l', 'i', 'n', 'g', 'o', 'n'}: _tlh, // i-klingon + [maxLen]byte{'i', '-', 'l', 'u', 'x'}: _lb, // i-lux + [maxLen]byte{'i', '-', 'n', 'a', 'v', 'a', 'j', 'o'}: _nv, // i-navajo + [maxLen]byte{'i', '-', 'p', 'w', 'n'}: _pwn, // i-pwn + [maxLen]byte{'i', '-', 't', 'a', 'o'}: _tao, // i-tao + [maxLen]byte{'i', '-', 't', 'a', 'y'}: _tay, // i-tay + [maxLen]byte{'i', '-', 't', 's', 'u'}: _tsu, // i-tsu + [maxLen]byte{'n', 'o', '-', 'b', 'o', 'k'}: _nb, // no-bok + [maxLen]byte{'n', 'o', '-', 'n', 'y', 'n'}: _nn, // no-nyn + [maxLen]byte{'s', 'g', 'n', '-', 'b', 'e', '-', 'f', 'r'}: _sfb, // sgn-BE-FR + [maxLen]byte{'s', 'g', 'n', '-', 'b', 'e', '-', 'n', 'l'}: _vgt, // sgn-BE-NL + [maxLen]byte{'s', 'g', 'n', '-', 'c', 'h', '-', 'd', 'e'}: _sgg, // sgn-CH-DE + [maxLen]byte{'z', 'h', '-', 'g', 'u', 'o', 'y', 'u'}: _cmn, // zh-guoyu + [maxLen]byte{'z', 'h', '-', 'h', 'a', 'k', 'k', 'a'}: _hak, // zh-hakka + [maxLen]byte{'z', 'h', '-', 'm', 'i', 'n', '-', 'n', 'a', 'n'}: _nan, // zh-min-nan + [maxLen]byte{'z', 'h', '-', 'x', 'i', 'a', 'n', 'g'}: _hsn, // zh-xiang // Grandfathered tags with no modern replacement will be converted as // follows: - {'c', 'e', 'l', '-', 'g', 'a', 'u', 'l', 'i', 's', 'h'}: -1, // cel-gaulish - {'e', 'n', '-', 'g', 'b', '-', 'o', 'e', 'd'}: -2, // en-GB-oed - {'i', '-', 'd', 'e', 'f', 'a', 'u', 'l', 't'}: -3, // i-default - {'i', '-', 'e', 'n', 'o', 'c', 'h', 'i', 'a', 'n'}: -4, // i-enochian - {'i', '-', 'm', 'i', 'n', 'g', 'o'}: -5, // i-mingo - {'z', 'h', '-', 'm', 'i', 'n'}: -6, // zh-min + [maxLen]byte{'c', 'e', 'l', '-', 'g', 'a', 'u', 'l', 'i', 's', 'h'}: -1, // cel-gaulish + [maxLen]byte{'e', 'n', '-', 'g', 'b', '-', 'o', 'e', 'd'}: -2, // en-GB-oed + [maxLen]byte{'i', '-', 'd', 'e', 'f', 'a', 'u', 'l', 't'}: -3, // i-default + [maxLen]byte{'i', '-', 'e', 'n', 'o', 'c', 'h', 'i', 'a', 'n'}: -4, // i-enochian + [maxLen]byte{'i', '-', 'm', 'i', 'n', 'g', 'o'}: -5, // i-mingo + [maxLen]byte{'z', 'h', '-', 'm', 'i', 'n'}: -6, // zh-min // CLDR-specific tag. - {'r', 'o', 'o', 't'}: 0, // root - {'e', 'n', '-', 'u', 's', '-', 'p', 'o', 's', 'i', 'x'}: -7, // en_US_POSIX" + [maxLen]byte{'r', 'o', 'o', 't'}: 0, // root + [maxLen]byte{'e', 'n', '-', 'u', 's', '-', 'p', 'o', 's', 'i', 'x'}: -7, // en_US_POSIX" } altTagIndex = [...]uint8{0, 17, 31, 45, 61, 74, 86, 102} diff --git a/vendor/golang.org/x/text/language/tables.go b/vendor/golang.org/x/text/language/tables.go index a28524e1d72..b738d457b5d 100644 --- a/vendor/golang.org/x/text/language/tables.go +++ b/vendor/golang.org/x/text/language/tables.go @@ -3348,9 +3348,9 @@ var regionToGroups = [358]uint8{ // Size: 18 bytes, 3 elements var paradigmLocales = [3][3]uint16{ - 0: {0x139, 0x0, 0x7b}, - 1: {0x13e, 0x0, 0x1f}, - 2: {0x3c0, 0x41, 0xee}, + 0: [3]uint16{0x139, 0x0, 0x7b}, + 1: [3]uint16{0x13e, 0x0, 0x1f}, + 2: [3]uint16{0x3c0, 0x41, 0xee}, } type mutualIntelligibility struct { diff --git a/vendor/golang.org/x/text/unicode/cldr/cldr.go b/vendor/golang.org/x/text/unicode/cldr/cldr.go index 19b8cefd706..2197f8ac268 100644 --- a/vendor/golang.org/x/text/unicode/cldr/cldr.go +++ b/vendor/golang.org/x/text/unicode/cldr/cldr.go @@ -110,7 +110,7 @@ func (cldr *CLDR) Supplemental() *SupplementalData { func (cldr *CLDR) Locales() []string { loc := []string{"root"} hasRoot := false - for l := range cldr.locale { + for l, _ := range cldr.locale { if l == "root" { hasRoot = true continue diff --git a/vendor/golang.org/x/text/unicode/cldr/resolve.go b/vendor/golang.org/x/text/unicode/cldr/resolve.go index c6919216b80..691b5903fe4 100644 --- a/vendor/golang.org/x/text/unicode/cldr/resolve.go +++ b/vendor/golang.org/x/text/unicode/cldr/resolve.go @@ -289,7 +289,7 @@ var distinguishing = map[string][]string{ "mzone": nil, "from": nil, "to": nil, - "type": { + "type": []string{ "abbreviationFallback", "default", "mapping", @@ -527,7 +527,7 @@ func (cldr *CLDR) inheritSlice(enc, v, parent reflect.Value) (res reflect.Value, } } keys := make([]string, 0, len(index)) - for k := range index { + for k, _ := range index { keys = append(keys, k) } sort.Strings(keys) diff --git a/vendor/golang.org/x/text/unicode/cldr/slice.go b/vendor/golang.org/x/text/unicode/cldr/slice.go index ea5f31a3903..388c983ff13 100644 --- a/vendor/golang.org/x/text/unicode/cldr/slice.go +++ b/vendor/golang.org/x/text/unicode/cldr/slice.go @@ -83,7 +83,7 @@ func (s Slice) Group(fn func(e Elem) string) []Slice { m[key] = append(m[key], vi) } keys := []string{} - for k := range m { + for k, _ := range m { keys = append(keys, k) } sort.Strings(keys) diff --git a/vendor/golang.org/x/text/unicode/norm/maketables.go b/vendor/golang.org/x/text/unicode/norm/maketables.go index f66778d450a..338c395ee6f 100644 --- a/vendor/golang.org/x/text/unicode/norm/maketables.go +++ b/vendor/golang.org/x/text/unicode/norm/maketables.go @@ -241,7 +241,7 @@ func compactCCC() { m[c.ccc] = 0 } cccs := []int{} - for v := range m { + for v, _ := range m { cccs = append(cccs, int(v)) } sort.Ints(cccs) diff --git a/vendor/gopkg.in/macaron.v1/context.go b/vendor/gopkg.in/macaron.v1/context.go index 0f86e0d41ec..94a8c45d7da 100644 --- a/vendor/gopkg.in/macaron.v1/context.go +++ b/vendor/gopkg.in/macaron.v1/context.go @@ -270,7 +270,7 @@ func (ctx *Context) SetParams(name, val string) { // ReplaceAllParams replace all current params with given params func (ctx *Context) ReplaceAllParams(params Params) { - ctx.params = params + ctx.params = params; } // ParamsEscape returns escapred params result. From 1ce3e49e72d92e44fc6e4fe8ab4446a93d6ae262 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 15 Mar 2018 00:05:15 +0100 Subject: [PATCH 011/145] fix lint problems --- .../core/components/Login/LoginBackground.tsx | 16 +++++----- .../app/core/components/PasswordStrength.tsx | 15 +++++---- .../components/colorpicker/ColorPalette.tsx | 4 +-- .../colorpicker/ColorPickerPopover.tsx | 31 ++++++++----------- .../components/colorpicker/SpectrumPicker.tsx | 24 +++++++------- .../core/components/search/SearchResult.tsx | 31 ++++++++++++------- .../app/core/specs/PasswordStrength.jest.tsx | 13 +++++--- public/app/core/utils/kbn.ts | 12 +++---- .../dashboard/dashgrid/DashboardPanel.tsx | 15 +++++---- 9 files changed, 86 insertions(+), 75 deletions(-) diff --git a/public/app/core/components/Login/LoginBackground.tsx b/public/app/core/components/Login/LoginBackground.tsx index fb554845240..83e228ab6e0 100644 --- a/public/app/core/components/Login/LoginBackground.tsx +++ b/public/app/core/components/Login/LoginBackground.tsx @@ -4,14 +4,10 @@ const xCount = 50; const yCount = 50; function Cell({ x, y, flipIndex }) { - const index = y * xCount + x; + const index = (y * xCount) + x; const bgColor1 = getColor(x, y); return ( -
+
); } @@ -35,7 +31,7 @@ export default class LoginBackground extends Component { } flipElements() { - const elementIndexToFlip = getRandomInt(0, xCount * yCount - 1); + const elementIndexToFlip = getRandomInt(0, (xCount * yCount) - 1); this.setState(prevState => { return { ...prevState, @@ -61,7 +57,9 @@ export default class LoginBackground extends Component { return (
{Array.from(Array(xCount)).map((el2, x) => { - return ; + return ( + + ); })}
); @@ -1238,5 +1236,5 @@ function getColor(x, y) { // let randY = getRandomInt(0, y); // let randIndex = randY * xCount + randX; - return colors[(y * xCount + x) % colors.length]; + return colors[(y*xCount + x) % colors.length]; } diff --git a/public/app/core/components/PasswordStrength.tsx b/public/app/core/components/PasswordStrength.tsx index 4830ebdb61b..8f92b18445c 100644 --- a/public/app/core/components/PasswordStrength.tsx +++ b/public/app/core/components/PasswordStrength.tsx @@ -5,27 +5,28 @@ export interface IProps { } export class PasswordStrength extends React.Component { + constructor(props) { super(props); } render() { const { password } = this.props; - let strengthText = 'strength: strong like a bull.'; - let strengthClass = 'password-strength-good'; + let strengthText = "strength: strong like a bull."; + let strengthClass = "password-strength-good"; if (!password) { return null; } if (password.length <= 8) { - strengthText = 'strength: you can do better.'; - strengthClass = 'password-strength-ok'; + strengthText = "strength: you can do better."; + strengthClass = "password-strength-ok"; } if (password.length < 4) { - strengthText = 'strength: weak sauce.'; - strengthClass = 'password-strength-bad'; + strengthText = "strength: weak sauce."; + strengthClass = "password-strength-bad"; } return ( @@ -35,3 +36,5 @@ export class PasswordStrength extends React.Component { ); } } + + diff --git a/public/app/core/components/colorpicker/ColorPalette.tsx b/public/app/core/components/colorpicker/ColorPalette.tsx index 812827996c7..07b25a32046 100644 --- a/public/app/core/components/colorpicker/ColorPalette.tsx +++ b/public/app/core/components/colorpicker/ColorPalette.tsx @@ -29,8 +29,7 @@ export class ColorPalette extends React.Component { key={paletteColor} className={'pointer fa ' + cssClass} style={{ color: paletteColor }} - onClick={this.onColorSelect(paletteColor)} - > + onClick={this.onColorSelect(paletteColor)}>   ); @@ -42,3 +41,4 @@ export class ColorPalette extends React.Component { ); } } + diff --git a/public/app/core/components/colorpicker/ColorPickerPopover.tsx b/public/app/core/components/colorpicker/ColorPickerPopover.tsx index 4ac4161a160..360c3fdd5c4 100644 --- a/public/app/core/components/colorpicker/ColorPickerPopover.tsx +++ b/public/app/core/components/colorpicker/ColorPickerPopover.tsx @@ -19,7 +19,7 @@ export class ColorPickerPopover extends React.Component { this.state = { tab: 'palette', color: this.props.color || DEFAULT_COLOR, - colorString: this.props.color || DEFAULT_COLOR, + colorString: this.props.color || DEFAULT_COLOR }; } @@ -32,7 +32,7 @@ export class ColorPickerPopover extends React.Component { if (newColor.isValid()) { this.setState({ color: newColor.toString(), - colorString: newColor.toString(), + colorString: newColor.toString() }); this.props.onColorSelect(color); } @@ -50,7 +50,7 @@ export class ColorPickerPopover extends React.Component { onColorStringChange(e) { let colorString = e.target.value; this.setState({ - colorString: colorString, + colorString: colorString }); let newColor = tinycolor(colorString); @@ -71,11 +71,11 @@ export class ColorPickerPopover extends React.Component { componentDidMount() { this.pickerNavElem.find('li:first').addClass('active'); - this.pickerNavElem.on('show', e => { + this.pickerNavElem.on('show', (e) => { // use href attr (#name => name) let tab = e.target.hash.slice(1); this.setState({ - tab: tab, + tab: tab }); }); } @@ -97,24 +97,19 @@ export class ColorPickerPopover extends React.Component {
-
{currentTab}
+
+ {currentTab} +
- + +
); diff --git a/public/app/core/components/colorpicker/SpectrumPicker.tsx b/public/app/core/components/colorpicker/SpectrumPicker.tsx index 7242c094cde..eef04545308 100644 --- a/public/app/core/components/colorpicker/SpectrumPicker.tsx +++ b/public/app/core/components/colorpicker/SpectrumPicker.tsx @@ -29,17 +29,14 @@ export class SpectrumPicker extends React.Component { } componentDidMount() { - let spectrumOptions = _.assignIn( - { - flat: true, - showAlpha: true, - showButtons: false, - color: this.props.color, - appendTo: this.elem, - move: this.onSpectrumMove, - }, - this.props.options - ); + let spectrumOptions = _.assignIn({ + flat: true, + showAlpha: true, + showButtons: false, + color: this.props.color, + appendTo: this.elem, + move: this.onSpectrumMove, + }, this.props.options); this.elem.spectrum(spectrumOptions); this.elem.spectrum('show'); @@ -67,6 +64,9 @@ export class SpectrumPicker extends React.Component { } render() { - return
; + return ( +
+ ); } } + diff --git a/public/app/core/components/search/SearchResult.tsx b/public/app/core/components/search/SearchResult.tsx index 5ab4bba8edb..6d6b001cc1d 100644 --- a/public/app/core/components/search/SearchResult.tsx +++ b/public/app/core/components/search/SearchResult.tsx @@ -1,7 +1,7 @@ -import React from 'react'; -import classNames from 'classnames'; -import { observer } from 'mobx-react'; -import { store } from 'app/stores/store'; +import React from "react"; +import classNames from "classnames"; +import { observer } from "mobx-react"; +import { store } from "app/stores/store"; export interface SearchResultProps { search: any; @@ -13,7 +13,7 @@ export class SearchResult extends React.Component { super(props); this.state = { - search: store.search, + search: store.search }; store.search.query(); @@ -56,20 +56,29 @@ export class SearchResultSection extends React.Component { render() { let collapseClassNames = classNames({ fa: true, - 'fa-plus': !this.props.section.expanded, - 'fa-minus': this.props.section.expanded, - 'search-section__header__toggle': true, + "fa-plus": !this.props.section.expanded, + "fa-minus": this.props.section.expanded, + "search-section__header__toggle": true }); return (
- - {this.props.section.title} + + + {this.props.section.title} +
{this.props.section.expanded && ( -
{this.props.section.items.map(this.renderItem)}
+
+ {this.props.section.items.map(this.renderItem)} +
)}
); diff --git a/public/app/core/specs/PasswordStrength.jest.tsx b/public/app/core/specs/PasswordStrength.jest.tsx index 1bd52ee6d50..a0a2df69029 100644 --- a/public/app/core/specs/PasswordStrength.jest.tsx +++ b/public/app/core/specs/PasswordStrength.jest.tsx @@ -1,21 +1,24 @@ import React from 'react'; -import { shallow } from 'enzyme'; +import {shallow} from 'enzyme'; -import { PasswordStrength } from '../components/PasswordStrength'; +import {PasswordStrength} from '../components/PasswordStrength'; describe('PasswordStrength', () => { + it('should have class bad if length below 4', () => { const wrapper = shallow(); - expect(wrapper.find('.password-strength-bad')).toHaveLength(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')).toHaveLength(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')).toHaveLength(1); + expect(wrapper.find(".password-strength-good")).toHaveLength(1); }); + }); + diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts index c2bcf1ba70e..3b78ccfc001 100644 --- a/public/app/core/utils/kbn.ts +++ b/public/app/core/utils/kbn.ts @@ -133,12 +133,12 @@ kbn.secondsToHms = function(seconds) { kbn.secondsToHhmmss = function(seconds) { var strings = []; - var numhours = Math.floor(seconds / 3600); - var numminutes = Math.floor((seconds % 3600) / 60); - var numseconds = Math.floor((seconds % 3600) % 60); - numhours > 9 ? strings.push('' + numhours) : strings.push('0' + numhours); - numminutes > 9 ? strings.push('' + numminutes) : strings.push('0' + numminutes); - numseconds > 9 ? strings.push('' + numseconds) : strings.push('0' + numseconds); + var numhours = Math.floor(seconds/3600); + var numminutes = Math.floor((seconds%3600)/60); + var numseconds = Math.floor((seconds%3600)%60); + numhours > 9 ? strings.push(''+numhours) : strings.push('0'+numhours); + numminutes > 9 ? strings.push(''+numminutes) : strings.push('0'+numminutes); + numseconds > 9 ? strings.push(''+numseconds) : strings.push('0'+numseconds); return strings.join(':'); }; diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index f135d94431a..27fe64d4660 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import { PanelModel } from '../panel_model'; -import { PanelContainer } from './PanelContainer'; -import { AttachedPanel } from './PanelLoader'; -import { DashboardRow } from './DashboardRow'; -import { AddPanelPanel } from './AddPanelPanel'; +import {PanelModel} from '../panel_model'; +import {PanelContainer} from './PanelContainer'; +import {AttachedPanel} from './PanelLoader'; +import {DashboardRow} from './DashboardRow'; +import {AddPanelPanel} from './AddPanelPanel'; export interface DashboardPanelProps { panel: PanelModel; @@ -46,6 +46,9 @@ export class DashboardPanel extends React.Component { return ; } - return
(this.element = element)} className="panel-height-helper" />; + return ( +
this.element = element} className="panel-height-helper" /> + ); } } + From 1080c113f6ea576cee65cf7bd66dd2c3f3e66287 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 15 Mar 2018 00:07:20 +0100 Subject: [PATCH 012/145] fix lint problems --- .../components/EmptyListCTA/EmptyListCTA.tsx | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/public/app/core/components/EmptyListCTA/EmptyListCTA.tsx b/public/app/core/components/EmptyListCTA/EmptyListCTA.tsx index c32edee156d..1583303dfa1 100644 --- a/public/app/core/components/EmptyListCTA/EmptyListCTA.tsx +++ b/public/app/core/components/EmptyListCTA/EmptyListCTA.tsx @@ -1,37 +1,34 @@ import React, { Component } from 'react'; export interface IProps { - model: any; + model: any; } class EmptyListCTA extends Component { - render() { - const { - title, - buttonIcon, - buttonLink, - buttonTitle, - proTip, - proTipLink, - proTipLinkTitle, - proTipTarget, - } = this.props.model; - return ( -
-
{title}
- - - {buttonTitle} - -
- ProTip: {proTip} - - {proTipLinkTitle} - -
-
- ); - } + render() { + const { + title, + buttonIcon, + buttonLink, + buttonTitle, + proTip, + proTipLink, + proTipLinkTitle, + proTipTarget + } = this.props.model; + return ( +
+
{title}
+ {buttonTitle} +
+ ProTip: {proTip} + {proTipLinkTitle} +
+
+ ); + } } export default EmptyListCTA; From 3aed867b4ba094d7315db3333096b16b85fb9b51 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 15 Mar 2018 10:35:50 +0100 Subject: [PATCH 013/145] fix merge error --- public/app/plugins/datasource/influxdb/datasource.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 19df26b9e63..90337fdc3f2 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -228,10 +228,10 @@ export default class InfluxDatasource { } _influxRequest(method: string, url: string, data: any, options?: any) { - var currentUrl = this.urls.shift(); + const currentUrl = this.urls.shift(); this.urls.push(currentUrl); - var params: any = {}; + let params: any = {}; if (this.username) { params.u = this.username; @@ -252,7 +252,7 @@ export default class InfluxDatasource { data = null; } - var req: any = { + let req: any = { method: method, url: currentUrl + url, params: params, @@ -270,7 +270,7 @@ export default class InfluxDatasource { req.headers.Authorization = this.basicAuth; } - return this.backendSrv.datasourceRequest(options).then( + return this.backendSrv.datasourceRequest(req).then( result => { return result.data; }, From ad88e5398c663feb853e7fb1bc8bb2dacd0b9380 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 15 Mar 2018 12:57:09 +0100 Subject: [PATCH 014/145] remove --- pkg/api/pluginproxy/ds_proxy.go | 4 +--- .../plugins/datasource/influxdb/datasource.ts | 5 ----- .../datasource/influxdb/partials/config.html | 20 +++++++++++++------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/pkg/api/pluginproxy/ds_proxy.go b/pkg/api/pluginproxy/ds_proxy.go index 0b0cd4ee79f..b861a344c75 100644 --- a/pkg/api/pluginproxy/ds_proxy.go +++ b/pkg/api/pluginproxy/ds_proxy.go @@ -180,9 +180,7 @@ func (proxy *DataSourceProxy) getDirector() func(req *http.Request) { func (proxy *DataSourceProxy) validateRequest() error { if proxy.ds.Type == m.DS_INFLUXDB { if proxy.ctx.Query("db") != proxy.ds.Database { - if !proxy.ds.JsonData.Get("allowDatabaseQuery").MustBool(false) { - return errors.New("Datasource is not configured to allow this database") - } + return errors.New("Datasource is not configured to allow this database") } } diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 90337fdc3f2..8b213ca0f36 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -16,7 +16,6 @@ export default class InfluxDatasource { basicAuth: any; withCredentials: any; interval: any; - allowDatabaseQuery: boolean; supportAnnotations: boolean; supportMetrics: boolean; responseParser: any; @@ -35,7 +34,6 @@ export default class InfluxDatasource { this.basicAuth = instanceSettings.basicAuth; this.withCredentials = instanceSettings.withCredentials; this.interval = (instanceSettings.jsonData || {}).timeInterval; - this.allowDatabaseQuery = (instanceSettings.jsonData || {}).allowDatabaseQuery === true; this.supportAnnotations = true; this.supportMetrics = true; this.responseParser = new ResponseParser(); @@ -240,9 +238,6 @@ export default class InfluxDatasource { if (options && options.database) { params.db = options.database; - if (params.db !== this.database && !this.allowDatabaseQuery) { - return this.$q.reject({ message: 'This datasource does not allow changing database' }); - } } else if (this.database) { params.db = this.database; } diff --git a/public/app/plugins/datasource/influxdb/partials/config.html b/public/app/plugins/datasource/influxdb/partials/config.html index 4b861083928..a70a1de98a4 100644 --- a/public/app/plugins/datasource/influxdb/partials/config.html +++ b/public/app/plugins/datasource/influxdb/partials/config.html @@ -23,16 +23,24 @@
- + +
+
+
Database Access
+

+ Setting the database for this datasource does not deny access to other databases. The InfluxDB query syntax allows + switching the database in the query. For example: + SHOW MEASUREMENTS ON _internal or SELECT * FROM "_internal".."database" LIMIT 10 +

+ To support data isolation and security, make sure appropriate permissions are configured in InfluxDB. +

+
+
- Min time interval + Min time interval A lower limit for the auto group by time interval. Recommended to be set to write frequency, From a04c4ba4541e8641b2ef07f37d280f20e7aa4472 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 15 Mar 2018 13:01:17 +0100 Subject: [PATCH 015/145] allow any database for influx proxy --- pkg/api/pluginproxy/ds_proxy.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/api/pluginproxy/ds_proxy.go b/pkg/api/pluginproxy/ds_proxy.go index b861a344c75..4b84c40643c 100644 --- a/pkg/api/pluginproxy/ds_proxy.go +++ b/pkg/api/pluginproxy/ds_proxy.go @@ -178,12 +178,6 @@ func (proxy *DataSourceProxy) getDirector() func(req *http.Request) { } func (proxy *DataSourceProxy) validateRequest() error { - if proxy.ds.Type == m.DS_INFLUXDB { - if proxy.ctx.Query("db") != proxy.ds.Database { - return errors.New("Datasource is not configured to allow this database") - } - } - if !checkWhiteList(proxy.ctx, proxy.targetUrl.Host) { return errors.New("Target url is not a valid target") } From fc9014f9204df635577c4bc0cb57a0bfdb21e25b Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Fri, 16 Mar 2018 12:36:36 +0100 Subject: [PATCH 016/145] added indent to dashboards inside folder in search dropdown, and added indent to dashboard icon in search item --- public/app/core/components/search/search_results.html | 2 +- public/sass/components/_search.scss | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/public/app/core/components/search/search_results.html b/public/app/core/components/search/search_results.html index 4e5bc88e0a9..7435f8d0b7e 100644 --- a/public/app/core/components/search/search_results.html +++ b/public/app/core/components/search/search_results.html @@ -20,7 +20,7 @@
- +
Date: Mon, 26 Mar 2018 14:14:57 +0200 Subject: [PATCH 017/145] styled login page for ie11 --- public/sass/pages/_login.scss | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/sass/pages/_login.scss b/public/sass/pages/_login.scss index 8622eec4e99..de10808f122 100644 --- a/public/sass/pages/_login.scss +++ b/public/sass/pages/_login.scss @@ -3,6 +3,7 @@ $login-border: #8daac5; .login { background-position: center; min-height: 85vh; + height: 80vh; background-repeat: no-repeat; min-width: 100%; margin-left: 0; @@ -290,9 +291,14 @@ select:-webkit-autofill:focus { } @include media-breakpoint-up(md) { + .login-content { + flex: 1 0 100%; + } + .login-branding { width: 45%; padding: 2rem 4rem; + flex-grow: 1; .logo-icon { width: 130px; @@ -371,7 +377,7 @@ select:-webkit-autofill:focus { left: 0; right: 0; height: 100%; - content: ""; + content: ''; display: block; } From dbcba4a0094f9c360216182d0d326a0bca127d09 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 27 Mar 2018 10:41:47 +0200 Subject: [PATCH 018/145] sidemenu fix for internet explorer 11, changed icon width/height to pixels and added height to logo --- public/sass/base/_icons.scss | 6 ++++-- public/sass/components/_sidemenu.scss | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/public/sass/base/_icons.scss b/public/sass/base/_icons.scss index c701cc1249e..31e5ee62d6f 100644 --- a/public/sass/base/_icons.scss +++ b/public/sass/base/_icons.scss @@ -1,8 +1,10 @@ .gicon { line-height: 1; display: inline-block; - width: 1.1057142857em; - height: 1.1057142857em; + //width: 1.1057142857em; + //height: 1.1057142857em; + height: 22px; + width: 22px; text-align: center; background-repeat: no-repeat; background-position: center; diff --git a/public/sass/components/_sidemenu.scss b/public/sass/components/_sidemenu.scss index 8a5c3779714..e48ab0597a2 100644 --- a/public/sass/components/_sidemenu.scss +++ b/public/sass/components/_sidemenu.scss @@ -178,6 +178,7 @@ li.sidemenu-org-switcher { padding: 0.4rem 1rem 0.4rem 0.65rem; min-height: $navbarHeight; position: relative; + height: $navbarHeight - 1px; &:hover { background: $navbarButtonBackgroundHighlight; From d4be953d23a4dd15eb2c26a003c11cd0f40dc898 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 27 Mar 2018 12:36:13 +0200 Subject: [PATCH 019/145] fixed alignment in search + fixed issue ie popup --- public/app/features/dashboard/unsaved_changes_srv.ts | 4 ++-- public/sass/components/_search.scss | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/public/app/features/dashboard/unsaved_changes_srv.ts b/public/app/features/dashboard/unsaved_changes_srv.ts index ebf0101cee0..d4c12b8bcd6 100644 --- a/public/app/features/dashboard/unsaved_changes_srv.ts +++ b/public/app/features/dashboard/unsaved_changes_srv.ts @@ -35,12 +35,12 @@ export class Tracker { $window.onbeforeunload = () => { if (this.ignoreChanges()) { - return null; + return undefined; } if (this.hasChanges()) { return 'There are unsaved changes to this dashboard'; } - return null; + return undefined; }; scope.$on('$locationChangeStart', (event, next) => { diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index 47d4a926968..e69068b730a 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -31,7 +31,6 @@ //padding: 0.5rem 1.5rem 0.5rem 0; padding: 1rem 1rem 0.75rem 1rem; height: 51px; - line-height: 51px; box-sizing: border-box; outline: none; background: $side-menu-bg; From a3f15ced6805d3d949d02c5a8a1d5267a6dac3a7 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 28 Mar 2018 14:19:17 +0200 Subject: [PATCH 020/145] fixed graphpanel editmode and custom width for right side legend for IE11 --- public/app/plugins/panel/graph/legend.ts | 5 ++++- public/sass/pages/_dashboard.scss | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index d1186ae0b1e..4dfeb75ff55 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -131,8 +131,11 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { elem.empty(); // Set min-width if side style and there is a value, otherwise remove the CSS propery - var width = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + 'px' : ''; + // Set width so it works with IE11 + var width: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + 'px' : ''; + var ieWidth: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth - 1 + 'px' : ''; elem.css('min-width', width); + elem.css('width', ieWidth); elem.toggleClass('graph-legend-table', panel.legend.alignAsTable === true); diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index 871db4dfc2d..c957b6af790 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -33,7 +33,7 @@ div.flot-text { border: $panel-border; position: relative; border-radius: 3px; - height: 100%; + //height: 100%; &.panel-transparent { background-color: transparent; From d40b7433ead654eea5e942eb095b94ac1ea73c76 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 28 Mar 2018 15:06:20 +0200 Subject: [PATCH 021/145] removed padding for icons and added margin --- public/sass/pages/_alerting.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/pages/_alerting.scss b/public/sass/pages/_alerting.scss index f44e26d5c20..5a481b55a8a 100644 --- a/public/sass/pages/_alerting.scss +++ b/public/sass/pages/_alerting.scss @@ -108,7 +108,7 @@ justify-content: center; align-items: center; width: 40px; - padding: 0 28px 0 16px; + margin-right: 8px; .icon-gf, .fa { font-size: 200%; From d95eabfeec00a2386a64db9461e119ff0c8ac4f6 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 28 Mar 2018 17:39:00 +0200 Subject: [PATCH 022/145] docs: adds provisioning examples for all datasources closes #11363 --- .../features/datasources/cloudwatch.md | 33 +++++++++++++++++++ .../features/datasources/elasticsearch.md | 19 +++++++++++ docs/sources/features/datasources/graphite.md | 17 ++++++++++ docs/sources/features/datasources/influxdb.md | 18 ++++++++++ docs/sources/features/datasources/mysql.md | 17 ++++++++++ docs/sources/features/datasources/opentsdb.md | 18 ++++++++++ docs/sources/features/datasources/postgres.md | 20 +++++++++++ .../features/datasources/prometheus.md | 15 +++++++++ 8 files changed, 157 insertions(+) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index f7f8138b5e9..3013f6b1fc9 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -173,3 +173,36 @@ Amazon provides 1 million CloudWatch API requests each month at no additional ch it costs $0.01 per 1,000 GetMetricStatistics or ListMetrics requests. For each query Grafana will issue a GetMetricStatistics request and every time you pick a dimension in the query editor Grafana will issue a ListMetrics request. + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here is an example of how you can configure the Cloudwatch datasource using configuration. + +Using a credentials file +```yaml +apiVersion: 1 + +datasources: + - name: Cloudwatch + type: cloudwatch + jsonData: + authType: credentials + defaultRegion: eu-west-2 +``` + +Using `accessKey` and `secretKey` + +```yaml +apiVersion: 1 + +datasources: + - name: Cloudwatch + type: cloudwatch + jsonData: + authType: keys + defaultRegion: eu-west-2 + secureJsonData: + accessKey: "" + secretKey: "" +``` diff --git a/docs/sources/features/datasources/elasticsearch.md b/docs/sources/features/datasources/elasticsearch.md index 6ce17113a9b..2cfe74588d1 100644 --- a/docs/sources/features/datasources/elasticsearch.md +++ b/docs/sources/features/datasources/elasticsearch.md @@ -137,3 +137,22 @@ Query | You can leave the search query blank or specify a lucene query Time | The name of the time field, needs to be date field. Text | Event description field. Tags | Optional field name to use for event tags (can be an array or a CSV string). + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: Elastic + type: elasticsearch + access: proxy + database: "[metrics-]YYYY.MM.DD" + url: http://localhost:9200 + jsonData: + interval: Daily + timeField: "@timestamp" +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/graphite.md b/docs/sources/features/datasources/graphite.md index 7c4187da9ae..040a21e5d0c 100644 --- a/docs/sources/features/datasources/graphite.md +++ b/docs/sources/features/datasources/graphite.md @@ -120,3 +120,20 @@ queries via the Dashboard menu / Annotations view. Graphite supports two ways to query annotations. A regular metric query, for this you use the `Graphite query` textbox. A Graphite events query, use the `Graphite event tags` textbox, specify a tag or wildcard (leave empty should also work) + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: Graphite + type: graphite + access: proxy + url: http://localhost:8080 + jsonData: + graphiteVersion: "1.1" +``` diff --git a/docs/sources/features/datasources/influxdb.md b/docs/sources/features/datasources/influxdb.md index 6d0918a0d01..e36adb3784e 100644 --- a/docs/sources/features/datasources/influxdb.md +++ b/docs/sources/features/datasources/influxdb.md @@ -174,3 +174,21 @@ SELECT title, description from events WHERE $timeFilter order asc For InfluxDB you need to enter a query like in the above example. You need to have the ```where $timeFilter``` part. If you only select one column you will not need to enter anything in the column mapping fields. The Tags field can be a comma separated string. + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: InfluxDB + type: influxdb + access: proxy + database: site + user: grafana + password: grafana + url: http://localhost:8086 +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index 7fae7441b6d..dc05b0b31de 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -225,3 +225,20 @@ tags | Optional field name to use for event tags as a comma separated string. ## Alerting Time series queries should work in alerting conditions. Table formatted queries is not yet supported in alert rule conditions. + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: MySQL + type: mysql + url: localhost:3306 + database: grafana + user: grafana + password: password +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index 03795473ff7..f67861e2a51 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -88,3 +88,21 @@ Query | Description *tag_values(cpu, hostanme, env=$env, region=$region)* | Return tag values for cpu metric, selected env tag value, selected region tag value and tag key hostname For details on OpenTSDB metric queries checkout the official [OpenTSDB documentation](http://opentsdb.net/docs/build/html/index.html) + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: OpenTsdb + type: opentsdb + access: proxy + url: http://localhost:4242 + jsonData: + tsdbResolution: 1 + tsdbVersion: 1 +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 7d52df2fd3e..b70ab1cde04 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -217,3 +217,23 @@ tags | Optional field name to use for event tags as a comma separated string. Time series queries should work in alerting conditions. Table formatted queries is not yet supported in alert rule conditions. + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: Postgres + type: postgres + url: localhost:5432 + database: grafana + user: grafana + password: password + jsonData: + sslmode: "disable" # disable/require/verify-ca/verify-full + +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index c9bb16441ca..ebd9ce32a47 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -100,3 +100,18 @@ The step option is useful to limit the number of events returned from your query ## Getting Grafana metrics into Prometheus Since 4.6.0 Grafana exposes metrics for Prometheus on the `/metrics` endpoint. We also bundle a dashboard within Grafana so you can get started viewing your metrics faster. You can import the bundled dashboard by going to the data source edit page and click the dashboard tab. There you can find a dashboard for Grafana and one for Prometheus. Import and start viewing all the metrics! + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://localhost:9090 +``` \ No newline at end of file From 2ccbf12d1c57cdbbf6c5ad59208faf6604e19075 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 28 Mar 2018 18:03:33 +0200 Subject: [PATCH 023/145] settings: return error instead of ignoring it closes #11281 --- pkg/setting/setting.go | 25 +++++++++++++++++++------ pkg/setting/setting_test.go | 7 +++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 5b79e866964..30a40602b1c 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -223,7 +223,7 @@ func shouldRedactURLKey(s string) bool { return strings.Contains(uppercased, "DATABASE_URL") } -func applyEnvVariableOverrides() { +func applyEnvVariableOverrides() error { appliedEnvOverrides = make([]string, 0) for _, section := range Cfg.Sections() { for _, key := range section.Keys() { @@ -238,7 +238,10 @@ func applyEnvVariableOverrides() { envValue = "*********" } if shouldRedactURLKey(envKey) { - u, _ := url.Parse(envValue) + u, err := url.Parse(envValue) + if err != nil { + return fmt.Errorf("could not parse environment variable. key: %s, value: %s. error: %v", envKey, envValue, err) + } ui := u.User if ui != nil { _, exists := ui.Password() @@ -252,6 +255,8 @@ func applyEnvVariableOverrides() { } } } + + return nil } func applyCommandLineDefaultProperties(props map[string]string) { @@ -377,7 +382,7 @@ func loadSpecifedConfigFile(configFile string) error { return nil } -func loadConfiguration(args *CommandLineArgs) { +func loadConfiguration(args *CommandLineArgs) error { var err error // load config defaults @@ -395,7 +400,7 @@ func loadConfiguration(args *CommandLineArgs) { if err != nil { fmt.Println(fmt.Sprintf("Failed to parse defaults.ini, %v", err)) os.Exit(1) - return + return err } Cfg.BlockMode = false @@ -413,7 +418,10 @@ func loadConfiguration(args *CommandLineArgs) { } // apply environment overrides - applyEnvVariableOverrides() + err = applyEnvVariableOverrides() + if err != nil { + return err + } // apply command line overrides applyCommandLineProperties(commandLineProps) @@ -424,6 +432,8 @@ func loadConfiguration(args *CommandLineArgs) { // update data path and logging config DataPath = makeAbsolute(Cfg.Section("paths").Key("data").String(), HomePath) initLogging() + + return err } func pathExists(path string) bool { @@ -471,7 +481,10 @@ func validateStaticRootPath() error { func NewConfigContext(args *CommandLineArgs) error { setHomePath(args) - loadConfiguration(args) + err := loadConfiguration(args) + if err != nil { + return err + } Env = Cfg.Section("").Key("app_mode").MustString("development") InstanceName = Cfg.Section("").Key("instance_name").MustString("unknown_instance_name") diff --git a/pkg/setting/setting_test.go b/pkg/setting/setting_test.go index 640a1648340..87f9916075e 100644 --- a/pkg/setting/setting_test.go +++ b/pkg/setting/setting_test.go @@ -37,6 +37,13 @@ func TestLoadingSettings(t *testing.T) { So(appliedEnvOverrides, ShouldContain, "GF_SECURITY_ADMIN_PASSWORD=*********") }) + Convey("Should replace password when defined in environment2", func() { + os.Setenv("GF_DATABASE_URL", "postgres://grafana:sec{ret@postgres:5432/grafana") + err := NewConfigContext(&CommandLineArgs{HomePath: "../../"}) + + So(err, ShouldNotBeNil) + }) + Convey("Should replace password in URL when url environment is defined", func() { os.Setenv("GF_DATABASE_URL", "mysql://user:secret@localhost:3306/database") NewConfigContext(&CommandLineArgs{HomePath: "../../"}) From 45d9bfca97a3a3ab98889ad41d516667d575c735 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 28 Mar 2018 22:51:21 +0200 Subject: [PATCH 024/145] print to stderr since logger might not exist --- pkg/cmd/grafana-server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/grafana-server/server.go b/pkg/cmd/grafana-server/server.go index 5bbf43087ec..b8387403161 100644 --- a/pkg/cmd/grafana-server/server.go +++ b/pkg/cmd/grafana-server/server.go @@ -111,7 +111,7 @@ func (g *GrafanaServerImpl) initLogging() { }) if err != nil { - g.log.Error(err.Error()) + fmt.Fprintf(os.Stderr, "Failed to start grafana. error: %s\n", err.Error()) os.Exit(1) } From 8195c085fa2a095fe753f725663ff7217cc14365 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 29 Mar 2018 09:15:15 +0200 Subject: [PATCH 025/145] bounnd the esc key to exit timepicker --- public/app/core/services/keybindingSrv.ts | 7 +++++++ public/app/features/dashboard/timepicker/timepicker.ts | 3 +++ 2 files changed, 10 insertions(+) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 0d468b6980f..829a3415cc1 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -10,6 +10,7 @@ import 'mousetrap-global-bind'; export class KeybindingSrv { helpModal: boolean; modalOpen = false; + timepickerOpen = false; /** @ngInject */ constructor(private $rootScope, private $location) { @@ -22,6 +23,7 @@ export class KeybindingSrv { this.setupGlobal(); appEvents.on('show-modal', () => (this.modalOpen = true)); + $rootScope.onAppEvent('openTimepicker', () => (this.timepickerOpen = true)); } setupGlobal() { @@ -72,6 +74,11 @@ export class KeybindingSrv { appEvents.emit('hide-modal'); + if (this.timepickerOpen === true) { + this.$rootScope.appEvent('closeTimepicker'); + this.timepickerOpen = false; + } + if (!this.modalOpen) { this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false }); } else { diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 2434e691515..19c3db7f6d3 100644 --- a/public/app/features/dashboard/timepicker/timepicker.ts +++ b/public/app/features/dashboard/timepicker/timepicker.ts @@ -32,6 +32,7 @@ export class TimePickerCtrl { $rootScope.onAppEvent('shift-time-forward', () => this.move(1), $scope); $rootScope.onAppEvent('shift-time-backward', () => this.move(-1), $scope); $rootScope.onAppEvent('refresh', this.onRefresh.bind(this), $scope); + $rootScope.onAppEvent('closeTimepicker', this.openDropdown.bind(this), $scope); // init options this.panel = this.dashboard.timepicker; @@ -100,6 +101,8 @@ export class TimePickerCtrl { return; } + this.$rootScope.appEvent('openTimepicker'); + this.onRefresh(); this.editTimeRaw = this.timeRaw; this.timeOptions = rangeUtil.getRelativeTimesList(this.panel, this.rangeString); From 1f1719c49822d2a43f225143bb6d389408ad7b43 Mon Sep 17 00:00:00 2001 From: Julien Pivotto Date: Tue, 27 Mar 2018 16:16:00 +0200 Subject: [PATCH 026/145] Fix #10555 #6888 Better escape for Prometheus variables Prior this commit, C:\bar was escaped C:\\\bar. Should be C:\\\\bar. Signed-off-by: Julien Pivotto --- .../datasource/prometheus/datasource.ts | 10 +++-- .../prometheus/specs/datasource.jest.ts | 39 ++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 4c736f2c664..6cf6c713a90 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -6,8 +6,12 @@ import * as dateMath from 'app/core/utils/datemath'; import PrometheusMetricFindQuery from './metric_find_query'; import { ResultTransformer } from './result_transformer'; -function prometheusSpecialRegexEscape(value) { - return value.replace(/[\\^$*+?.()|[\]{}]/g, '\\\\$&'); +export function prometheusRegularEscape(value) { + return value.replace(/'/g, "\\\\'"); +} + +export function prometheusSpecialRegexEscape(value) { + return prometheusRegularEscape(value.replace(/\\/g, '\\\\\\\\').replace(/[$^*{}\[\]+?.()]/g, '\\\\$&')); } export class PrometheusDatasource { @@ -80,7 +84,7 @@ export class PrometheusDatasource { interpolateQueryExpr(value, variable, defaultFormatFn) { // if no multi or include all do not regexEscape if (!variable.multi && !variable.includeAll) { - return value; + return prometheusRegularEscape(value); } if (typeof value === 'string') { diff --git a/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts b/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts index cca74e023e7..d2620b93bbc 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import moment from 'moment'; import q from 'q'; -import { PrometheusDatasource } from '../datasource'; +import { PrometheusDatasource, prometheusSpecialRegexEscape, prometheusRegularEscape } from '../datasource'; describe('PrometheusDatasource', () => { let ctx: any = {}; @@ -101,4 +101,41 @@ describe('PrometheusDatasource', () => { }); }); }); + + describe('Prometheus regular escaping', function() { + it('should not escape simple string', function() { + expect(prometheusRegularEscape('cryptodepression')).toEqual('cryptodepression'); + }); + it("should escape '", function() { + expect(prometheusRegularEscape("looking'glass")).toEqual("looking\\\\'glass"); + }); + it('should escape multiple characters', function() { + expect(prometheusRegularEscape("'looking'glass'")).toEqual("\\\\'looking\\\\'glass\\\\'"); + }); + }); + + describe('Prometheus regexes escaping', function() { + it('should not escape simple string', function() { + expect(prometheusSpecialRegexEscape('cryptodepression')).toEqual('cryptodepression'); + }); + it('should escape $^*+?.()\\', function() { + expect(prometheusSpecialRegexEscape("looking'glass")).toEqual("looking\\\\'glass"); + expect(prometheusSpecialRegexEscape('looking{glass')).toEqual('looking\\\\{glass'); + expect(prometheusSpecialRegexEscape('looking}glass')).toEqual('looking\\\\}glass'); + expect(prometheusSpecialRegexEscape('looking[glass')).toEqual('looking\\\\[glass'); + expect(prometheusSpecialRegexEscape('looking]glass')).toEqual('looking\\\\]glass'); + expect(prometheusSpecialRegexEscape('looking$glass')).toEqual('looking\\\\$glass'); + expect(prometheusSpecialRegexEscape('looking^glass')).toEqual('looking\\\\^glass'); + expect(prometheusSpecialRegexEscape('looking*glass')).toEqual('looking\\\\*glass'); + expect(prometheusSpecialRegexEscape('looking+glass')).toEqual('looking\\\\+glass'); + expect(prometheusSpecialRegexEscape('looking?glass')).toEqual('looking\\\\?glass'); + expect(prometheusSpecialRegexEscape('looking.glass')).toEqual('looking\\\\.glass'); + expect(prometheusSpecialRegexEscape('looking(glass')).toEqual('looking\\\\(glass'); + expect(prometheusSpecialRegexEscape('looking)glass')).toEqual('looking\\\\)glass'); + expect(prometheusSpecialRegexEscape('looking\\glass')).toEqual('looking\\\\\\\\glass'); + }); + it('should escape multiple special characters', function() { + expect(prometheusSpecialRegexEscape('+looking$glass?')).toEqual('\\\\+looking\\\\$glass\\\\?'); + }); + }); }); From 65f7c5f08f891bab0cbb87e6cf18fbd8aa23d93b Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 29 Mar 2018 10:40:45 +0200 Subject: [PATCH 027/145] started migration to ts --- .../app/plugins/panel/graph/graph_tooltip.js | 292 ------------------ .../app/plugins/panel/graph/graph_tooltip.ts | 290 +++++++++++++++++ 2 files changed, 290 insertions(+), 292 deletions(-) delete mode 100644 public/app/plugins/panel/graph/graph_tooltip.js create mode 100644 public/app/plugins/panel/graph/graph_tooltip.ts diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js deleted file mode 100644 index 89197717e42..00000000000 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ /dev/null @@ -1,292 +0,0 @@ -define([ - 'jquery', - 'app/core/core', -], -function ($, core) { - 'use strict'; - - var appEvents = core.appEvents; - - function GraphTooltip(elem, dashboard, scope, getSeriesFn) { - var self = this; - var ctrl = scope.ctrl; - var panel = ctrl.panel; - - var $tooltip = $('
'); - - this.destroy = function() { - $tooltip.remove(); - }; - - this.findHoverIndexFromDataPoints = function(posX, series, last) { - var ps = series.datapoints.pointsize; - var initial = last*ps; - var len = series.datapoints.points.length; - for (var j = initial; j < len; j += ps) { - // Special case of a non stepped line, highlight the very last point just before a null point - if ((!series.lines.steps && series.datapoints.points[initial] != null && series.datapoints.points[j] == null) - //normal case - || series.datapoints.points[j] > posX) { - return Math.max(j - ps, 0)/ps; - } - } - return j/ps - 1; - }; - - this.findHoverIndexFromData = function(posX, series) { - var lower = 0; - var upper = series.data.length - 1; - var middle; - while (true) { - if (lower > upper) { - return Math.max(upper, 0); - } - middle = Math.floor((lower + upper) / 2); - if (series.data[middle][0] === posX) { - return middle; - } else if (series.data[middle][0] < posX) { - lower = middle + 1; - } else { - upper = middle - 1; - } - } - }; - - this.renderAndShow = function(absoluteTime, innerHtml, pos, xMode) { - if (xMode === 'time') { - innerHtml = '
'+ absoluteTime + '
' + innerHtml; - } - $tooltip.html(innerHtml).place_tt(pos.pageX + 20, pos.pageY); - }; - - this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) { - var value, i, series, hoverIndex, hoverDistance, pointTime, yaxis; - // 3 sub-arrays, 1st for hidden series, 2nd for left yaxis, 3rd for right yaxis. - var results = [[],[],[]]; - - //now we know the current X (j) position for X and Y values - var last_value = 0; //needed for stacked values - - var minDistance, minTime; - - for (i = 0; i < seriesList.length; i++) { - series = seriesList[i]; - - if (!series.data.length || (panel.legend.hideEmpty && series.allIsNull)) { - // Init value so that it does not brake series sorting - results[0].push({ hidden: true, value: 0 }); - continue; - } - - if (!series.data.length || (panel.legend.hideZero && series.allIsZero)) { - // Init value so that it does not brake series sorting - results[0].push({ hidden: true, value: 0 }); - continue; - } - - hoverIndex = this.findHoverIndexFromData(pos.x, series); - hoverDistance = pos.x - series.data[hoverIndex][0]; - pointTime = series.data[hoverIndex][0]; - - // Take the closest point before the cursor, or if it does not exist, the closest after - if (! minDistance - || (hoverDistance >=0 && (hoverDistance < minDistance || minDistance < 0)) - || (hoverDistance < 0 && hoverDistance > minDistance)) { - minDistance = hoverDistance; - minTime = pointTime; - } - - if (series.stack) { - if (panel.tooltip.value_type === 'individual') { - value = series.data[hoverIndex][1]; - } else if (!series.stack) { - value = series.data[hoverIndex][1]; - } else { - last_value += series.data[hoverIndex][1]; - value = last_value; - } - } else { - value = series.data[hoverIndex][1]; - } - - // Highlighting multiple Points depending on the plot type - if (series.lines.steps || series.stack) { - // stacked and steppedLine plots can have series with different length. - // Stacked series can increase its length on each new stacked serie if null points found, - // to speed the index search we begin always on the last found hoverIndex. - hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); - } - - // Be sure we have a yaxis so that it does not brake series sorting - yaxis = 0; - if (series.yaxis) { - yaxis = series.yaxis.n; - } - - results[yaxis].push({ - value: value, - hoverIndex: hoverIndex, - color: series.color, - label: series.aliasEscaped, - time: pointTime, - distance: hoverDistance, - index: i - }); - } - - // Contat the 3 sub-arrays - results = results[0].concat(results[1],results[2]); - - // Time of the point closer to pointer - results.time = minTime; - - return results; - }; - - elem.mouseleave(function () { - if (panel.tooltip.shared) { - var plot = elem.data().plot; - if (plot) { - $tooltip.detach(); - plot.unhighlight(); - } - } - appEvents.emit('graph-hover-clear'); - }); - - elem.bind("plothover", function (event, pos, item) { - self.show(pos, item); - - // broadcast to other graph panels that we are hovering! - pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height(); - appEvents.emit('graph-hover', {pos: pos, panel: panel}); - }); - - elem.bind("plotclick", function (event, pos, item) { - appEvents.emit('graph-click', {pos: pos, panel: panel, item: item}); - }); - - this.clear = function(plot) { - $tooltip.detach(); - plot.clearCrosshair(); - plot.unhighlight(); - }; - - this.show = function(pos, item) { - var plot = elem.data().plot; - var plotData = plot.getData(); - var xAxes = plot.getXAxes(); - var xMode = xAxes[0].options.mode; - var seriesList = getSeriesFn(); - var allSeriesMode = panel.tooltip.shared; - var group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat; - - // if panelRelY is defined another panel wants us to show a tooltip - // get pageX from position on x axis and pageY from relative position in original panel - if (pos.panelRelY) { - var pointOffset = plot.pointOffset({x: pos.x}); - if (Number.isNaN(pointOffset.left) || pointOffset.left < 0 || pointOffset.left > elem.width()) { - self.clear(plot); - return; - } - pos.pageX = elem.offset().left + pointOffset.left; - pos.pageY = elem.offset().top + elem.height() * pos.panelRelY; - var isVisible = pos.pageY >= $(window).scrollTop() && pos.pageY <= $(window).innerHeight() + $(window).scrollTop(); - if (!isVisible) { - self.clear(plot); - return; - } - plot.setCrosshair(pos); - allSeriesMode = true; - - if (dashboard.sharedCrosshairModeOnly()) { - // if only crosshair mode we are done - return; - } - } - - if (seriesList.length === 0) { - return; - } - - if (seriesList[0].hasMsResolution) { - tooltipFormat = 'YYYY-MM-DD HH:mm:ss.SSS'; - } else { - tooltipFormat = 'YYYY-MM-DD HH:mm:ss'; - } - - if (allSeriesMode) { - plot.unhighlight(); - - var seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos); - - seriesHtml = ''; - - absoluteTime = dashboard.formatDate(seriesHoverInfo.time, tooltipFormat); - - // Dynamically reorder the hovercard for the current time point if the - // option is enabled. - if (panel.tooltip.sort === 2) { - seriesHoverInfo.sort(function(a, b) { - return b.value - a.value; - }); - } else if (panel.tooltip.sort === 1) { - seriesHoverInfo.sort(function(a, b) { - return a.value - b.value; - }); - } - - for (i = 0; i < seriesHoverInfo.length; i++) { - hoverInfo = seriesHoverInfo[i]; - - if (hoverInfo.hidden) { - continue; - } - - var highlightClass = ''; - if (item && hoverInfo.index === item.seriesIndex) { - highlightClass = 'graph-tooltip-list-item--highlight'; - } - - series = seriesList[hoverInfo.index]; - - value = series.formatValue(hoverInfo.value); - - seriesHtml += '
'; - seriesHtml += ' ' + hoverInfo.label + ':
'; - seriesHtml += '
' + value + '
'; - plot.highlight(hoverInfo.index, hoverInfo.hoverIndex); - } - - self.renderAndShow(absoluteTime, seriesHtml, pos, xMode); - } - // single series tooltip - else if (item) { - series = seriesList[item.seriesIndex]; - group = '
'; - group += ' ' + series.aliasEscaped + ':
'; - - if (panel.stack && panel.tooltip.value_type === 'individual') { - value = item.datapoint[1] - item.datapoint[2]; - } - else { - value = item.datapoint[1]; - } - - value = series.formatValue(value); - - absoluteTime = dashboard.formatDate(item.datapoint[0], tooltipFormat); - - group += '
' + value + '
'; - - self.renderAndShow(absoluteTime, group, pos, xMode); - } - // no hit - else { - $tooltip.detach(); - } - }; - } - - return GraphTooltip; -}); diff --git a/public/app/plugins/panel/graph/graph_tooltip.ts b/public/app/plugins/panel/graph/graph_tooltip.ts new file mode 100644 index 00000000000..846a165bbaf --- /dev/null +++ b/public/app/plugins/panel/graph/graph_tooltip.ts @@ -0,0 +1,290 @@ +import $ from 'jquery'; +import { appEvents } from 'app/core/core'; + +//var appEvents = appEvents; + +export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { + let self = this; + let ctrl = scope.ctrl; + let panel = ctrl.panel; + + let $tooltip = $('
'); + + this.destroy = function() { + $tooltip.remove(); + }; + + this.findHoverIndexFromDataPoints = function(posX, series, last) { + var ps = series.datapoints.pointsize; + var initial = last * ps; + var len = series.datapoints.points.length; + for (var j = initial; j < len; j += ps) { + // Special case of a non stepped line, highlight the very last point just before a null point + if ( + (!series.lines.steps && series.datapoints.points[initial] != null && series.datapoints.points[j] == null) || + //normal case + series.datapoints.points[j] > posX + ) { + return Math.max(j - ps, 0) / ps; + } + } + return j / ps - 1; + }; + + this.findHoverIndexFromData = function(posX, series) { + var lower = 0; + var upper = series.data.length - 1; + var middle; + while (true) { + if (lower > upper) { + return Math.max(upper, 0); + } + middle = Math.floor((lower + upper) / 2); + if (series.data[middle][0] === posX) { + return middle; + } else if (series.data[middle][0] < posX) { + lower = middle + 1; + } else { + upper = middle - 1; + } + } + }; + + this.renderAndShow = function(absoluteTime, innerHtml, pos, xMode) { + if (xMode === 'time') { + innerHtml = '
' + absoluteTime + '
' + innerHtml; + } + $tooltip.html(innerHtml).place_tt(pos.pageX + 20, pos.pageY); + }; + + this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) { + var value, i, series, hoverIndex, hoverDistance, pointTime, yaxis; + // 3 sub-arrays, 1st for hidden series, 2nd for left yaxis, 3rd for right yaxis. + var results: any = [[], [], []]; + + //now we know the current X (j) position for X and Y values + var last_value = 0; //needed for stacked values + + var minDistance, minTime; + + for (i = 0; i < seriesList.length; i++) { + series = seriesList[i]; + + if (!series.data.length || (panel.legend.hideEmpty && series.allIsNull)) { + // Init value so that it does not brake series sorting + results[0].push({ hidden: true, value: 0 }); + continue; + } + + if (!series.data.length || (panel.legend.hideZero && series.allIsZero)) { + // Init value so that it does not brake series sorting + results[0].push({ hidden: true, value: 0 }); + continue; + } + + hoverIndex = this.findHoverIndexFromData(pos.x, series); + hoverDistance = pos.x - series.data[hoverIndex][0]; + pointTime = series.data[hoverIndex][0]; + + // Take the closest point before the cursor, or if it does not exist, the closest after + if ( + !minDistance || + (hoverDistance >= 0 && (hoverDistance < minDistance || minDistance < 0)) || + (hoverDistance < 0 && hoverDistance > minDistance) + ) { + minDistance = hoverDistance; + minTime = pointTime; + } + + if (series.stack) { + if (panel.tooltip.value_type === 'individual') { + value = series.data[hoverIndex][1]; + } else if (!series.stack) { + value = series.data[hoverIndex][1]; + } else { + last_value += series.data[hoverIndex][1]; + value = last_value; + } + } else { + value = series.data[hoverIndex][1]; + } + + // Highlighting multiple Points depending on the plot type + if (series.lines.steps || series.stack) { + // stacked and steppedLine plots can have series with different length. + // Stacked series can increase its length on each new stacked serie if null points found, + // to speed the index search we begin always on the last found hoverIndex. + hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); + } + + // Be sure we have a yaxis so that it does not brake series sorting + yaxis = 0; + if (series.yaxis) { + yaxis = series.yaxis.n; + } + + results[yaxis].push({ + value: value, + hoverIndex: hoverIndex, + color: series.color, + label: series.aliasEscaped, + time: pointTime, + distance: hoverDistance, + index: i, + }); + } + + // Contat the 3 sub-arrays + results = results[0].concat(results[1], results[2]); + + // Time of the point closer to pointer + results.time = minTime; + + return results; + }; + + elem.mouseleave(function() { + if (panel.tooltip.shared) { + var plot = elem.data().plot; + if (plot) { + $tooltip.detach(); + plot.unhighlight(); + } + } + appEvents.emit('graph-hover-clear'); + }); + + elem.bind('plothover', function(event, pos, item) { + self.show(pos, item); + + // broadcast to other graph panels that we are hovering! + pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height(); + appEvents.emit('graph-hover', { pos: pos, panel: panel }); + }); + + elem.bind('plotclick', function(event, pos, item) { + appEvents.emit('graph-click', { pos: pos, panel: panel, item: item }); + }); + + this.clear = function(plot) { + $tooltip.detach(); + plot.clearCrosshair(); + plot.unhighlight(); + }; + + this.show = function(pos, item) { + var plot = elem.data().plot; + var plotData = plot.getData(); + var xAxes = plot.getXAxes(); + var xMode = xAxes[0].options.mode; + var seriesList = getSeriesFn(); + var allSeriesMode = panel.tooltip.shared; + var group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat; + + // if panelRelY is defined another panel wants us to show a tooltip + // get pageX from position on x axis and pageY from relative position in original panel + if (pos.panelRelY) { + var pointOffset = plot.pointOffset({ x: pos.x }); + if (Number.isNaN(pointOffset.left) || pointOffset.left < 0 || pointOffset.left > elem.width()) { + self.clear(plot); + return; + } + pos.pageX = elem.offset().left + pointOffset.left; + pos.pageY = elem.offset().top + elem.height() * pos.panelRelY; + var isVisible = + pos.pageY >= $(window).scrollTop() && pos.pageY <= $(window).innerHeight() + $(window).scrollTop(); + if (!isVisible) { + self.clear(plot); + return; + } + plot.setCrosshair(pos); + allSeriesMode = true; + + if (dashboard.sharedCrosshairModeOnly()) { + // if only crosshair mode we are done + return; + } + } + + if (seriesList.length === 0) { + return; + } + + if (seriesList[0].hasMsResolution) { + tooltipFormat = 'YYYY-MM-DD HH:mm:ss.SSS'; + } else { + tooltipFormat = 'YYYY-MM-DD HH:mm:ss'; + } + + if (allSeriesMode) { + plot.unhighlight(); + + var seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos); + + seriesHtml = ''; + + absoluteTime = dashboard.formatDate(seriesHoverInfo.time, tooltipFormat); + + // Dynamically reorder the hovercard for the current time point if the + // option is enabled. + if (panel.tooltip.sort === 2) { + seriesHoverInfo.sort(function(a, b) { + return b.value - a.value; + }); + } else if (panel.tooltip.sort === 1) { + seriesHoverInfo.sort(function(a, b) { + return a.value - b.value; + }); + } + + for (i = 0; i < seriesHoverInfo.length; i++) { + hoverInfo = seriesHoverInfo[i]; + + if (hoverInfo.hidden) { + continue; + } + + var highlightClass = ''; + if (item && hoverInfo.index === item.seriesIndex) { + highlightClass = 'graph-tooltip-list-item--highlight'; + } + + series = seriesList[hoverInfo.index]; + + value = series.formatValue(hoverInfo.value); + + seriesHtml += + '
'; + seriesHtml += + ' ' + hoverInfo.label + ':
'; + seriesHtml += '
' + value + '
'; + plot.highlight(hoverInfo.index, hoverInfo.hoverIndex); + } + + self.renderAndShow(absoluteTime, seriesHtml, pos, xMode); + } else if (item) { + // single series tooltip + series = seriesList[item.seriesIndex]; + group = '
'; + group += + ' ' + series.aliasEscaped + ':
'; + + if (panel.stack && panel.tooltip.value_type === 'individual') { + value = item.datapoint[1] - item.datapoint[2]; + } else { + value = item.datapoint[1]; + } + + value = series.formatValue(value); + + absoluteTime = dashboard.formatDate(item.datapoint[0], tooltipFormat); + + group += '
' + value + '
'; + + self.renderAndShow(absoluteTime, group, pos, xMode); + } else { + // no hit + $tooltip.detach(); + } + }; +} From 7b9b34c6e188344665948cca705c0e4d24389608 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 29 Mar 2018 11:51:34 +0200 Subject: [PATCH 028/145] migrated graph_tooltip to ts --- .../app/plugins/panel/graph/graph_tooltip.ts | 49 +++++++++---------- .../panel/graph/specs/tooltip_specs.ts | 5 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/public/app/plugins/panel/graph/graph_tooltip.ts b/public/app/plugins/panel/graph/graph_tooltip.ts index 846a165bbaf..509d15b8a25 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.ts +++ b/public/app/plugins/panel/graph/graph_tooltip.ts @@ -1,8 +1,6 @@ import $ from 'jquery'; import { appEvents } from 'app/core/core'; -//var appEvents = appEvents; - export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { let self = this; let ctrl = scope.ctrl; @@ -15,10 +13,11 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { }; this.findHoverIndexFromDataPoints = function(posX, series, last) { - var ps = series.datapoints.pointsize; - var initial = last * ps; - var len = series.datapoints.points.length; - for (var j = initial; j < len; j += ps) { + let ps = series.datapoints.pointsize; + let initial = last * ps; + let len = series.datapoints.points.length; + let j; + for (j = initial; j < len; j += ps) { // Special case of a non stepped line, highlight the very last point just before a null point if ( (!series.lines.steps && series.datapoints.points[initial] != null && series.datapoints.points[j] == null) || @@ -32,9 +31,9 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { }; this.findHoverIndexFromData = function(posX, series) { - var lower = 0; - var upper = series.data.length - 1; - var middle; + let lower = 0; + let upper = series.data.length - 1; + let middle; while (true) { if (lower > upper) { return Math.max(upper, 0); @@ -58,14 +57,14 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { }; this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) { - var value, i, series, hoverIndex, hoverDistance, pointTime, yaxis; + let value, i, series, hoverIndex, hoverDistance, pointTime, yaxis; // 3 sub-arrays, 1st for hidden series, 2nd for left yaxis, 3rd for right yaxis. - var results: any = [[], [], []]; + let results: any = [[], [], []]; //now we know the current X (j) position for X and Y values - var last_value = 0; //needed for stacked values + let last_value = 0; //needed for stacked values - var minDistance, minTime; + let minDistance, minTime; for (i = 0; i < seriesList.length; i++) { series = seriesList[i]; @@ -145,7 +144,7 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { elem.mouseleave(function() { if (panel.tooltip.shared) { - var plot = elem.data().plot; + let plot = elem.data().plot; if (plot) { $tooltip.detach(); plot.unhighlight(); @@ -173,25 +172,25 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { }; this.show = function(pos, item) { - var plot = elem.data().plot; - var plotData = plot.getData(); - var xAxes = plot.getXAxes(); - var xMode = xAxes[0].options.mode; - var seriesList = getSeriesFn(); - var allSeriesMode = panel.tooltip.shared; - var group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat; + let plot = elem.data().plot; + let plotData = plot.getData(); + let xAxes = plot.getXAxes(); + let xMode = xAxes[0].options.mode; + let seriesList = getSeriesFn(); + let allSeriesMode = panel.tooltip.shared; + let group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat; // if panelRelY is defined another panel wants us to show a tooltip // get pageX from position on x axis and pageY from relative position in original panel if (pos.panelRelY) { - var pointOffset = plot.pointOffset({ x: pos.x }); + let pointOffset = plot.pointOffset({ x: pos.x }); if (Number.isNaN(pointOffset.left) || pointOffset.left < 0 || pointOffset.left > elem.width()) { self.clear(plot); return; } pos.pageX = elem.offset().left + pointOffset.left; pos.pageY = elem.offset().top + elem.height() * pos.panelRelY; - var isVisible = + let isVisible = pos.pageY >= $(window).scrollTop() && pos.pageY <= $(window).innerHeight() + $(window).scrollTop(); if (!isVisible) { self.clear(plot); @@ -219,7 +218,7 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { if (allSeriesMode) { plot.unhighlight(); - var seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos); + let seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos); seriesHtml = ''; @@ -244,7 +243,7 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { continue; } - var highlightClass = ''; + let highlightClass = ''; if (item && hoverInfo.index === item.seriesIndex) { highlightClass = 'graph-tooltip-list-item--highlight'; } diff --git a/public/app/plugins/panel/graph/specs/tooltip_specs.ts b/public/app/plugins/panel/graph/specs/tooltip_specs.ts index c12697eadac..7dd5ed9b8a9 100644 --- a/public/app/plugins/panel/graph/specs/tooltip_specs.ts +++ b/public/app/plugins/panel/graph/specs/tooltip_specs.ts @@ -11,6 +11,7 @@ var scope = { var elem = $('
'); var dashboard = {}; +var getSeriesFn; function describeSharedTooltip(desc, fn) { var ctx: any = {}; @@ -30,7 +31,7 @@ function describeSharedTooltip(desc, fn) { describe(desc, function() { beforeEach(function() { ctx.setupFn(); - var tooltip = new GraphTooltip(elem, dashboard, scope); + var tooltip = new GraphTooltip(elem, dashboard, scope, getSeriesFn); ctx.results = tooltip.getMultiSeriesPlotHoverInfo(ctx.data, ctx.pos); }); @@ -39,7 +40,7 @@ function describeSharedTooltip(desc, fn) { } describe('findHoverIndexFromData', function() { - var tooltip = new GraphTooltip(elem, dashboard, scope); + var tooltip = new GraphTooltip(elem, dashboard, scope, getSeriesFn); var series = { data: [[100, 0], [101, 0], [102, 0], [103, 0], [104, 0], [105, 0], [106, 0], [107, 0]], }; From 9ef5f2700dd12fc7bfa1c6ac1c4f7cedcf2a52f3 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 29 Mar 2018 15:02:00 +0200 Subject: [PATCH 029/145] timepicker now closes without exiting edit/view mode, close order: modal, timepicker, view --- public/app/core/services/keybindingSrv.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 829a3415cc1..e51c0477ffa 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -74,13 +74,13 @@ export class KeybindingSrv { appEvents.emit('hide-modal'); - if (this.timepickerOpen === true) { - this.$rootScope.appEvent('closeTimepicker'); - this.timepickerOpen = false; - } - if (!this.modalOpen) { - this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false }); + if (this.timepickerOpen) { + this.$rootScope.appEvent('closeTimepicker'); + this.timepickerOpen = false; + } else { + this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false }); + } } else { this.modalOpen = false; } From de6cd7ed0bf754e8af1e1a7ca54ff1040b760ac2 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 29 Mar 2018 22:21:41 +0200 Subject: [PATCH 030/145] docker: add users and groups to ldap block --- docker/blocks/openldap/Dockerfile | 1 + docker/blocks/openldap/entrypoint.sh | 10 +++++++--- docker/blocks/openldap/notes.md | 13 +++++++++++++ docker/blocks/openldap/prepopulate/admin.ldif | 10 ++++++++++ docker/blocks/openldap/prepopulate/adminsgroup.ldif | 5 +++++ docker/blocks/openldap/prepopulate/editor.ldif | 10 ++++++++++ docker/blocks/openldap/prepopulate/usersgroup.ldif | 5 +++++ docker/blocks/openldap/prepopulate/viewer.ldif | 9 +++++++++ 8 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 docker/blocks/openldap/notes.md create mode 100644 docker/blocks/openldap/prepopulate/admin.ldif create mode 100644 docker/blocks/openldap/prepopulate/adminsgroup.ldif create mode 100644 docker/blocks/openldap/prepopulate/editor.ldif create mode 100644 docker/blocks/openldap/prepopulate/usersgroup.ldif create mode 100644 docker/blocks/openldap/prepopulate/viewer.ldif diff --git a/docker/blocks/openldap/Dockerfile b/docker/blocks/openldap/Dockerfile index d073e274356..54e383a6a97 100644 --- a/docker/blocks/openldap/Dockerfile +++ b/docker/blocks/openldap/Dockerfile @@ -17,6 +17,7 @@ EXPOSE 389 VOLUME ["/etc/ldap", "/var/lib/ldap"] COPY modules/ /etc/ldap.dist/modules +COPY prepopulate/ /etc/ldap.dist/prepopulate COPY entrypoint.sh /entrypoint.sh diff --git a/docker/blocks/openldap/entrypoint.sh b/docker/blocks/openldap/entrypoint.sh index 39a8b892de8..d560b78d388 100755 --- a/docker/blocks/openldap/entrypoint.sh +++ b/docker/blocks/openldap/entrypoint.sh @@ -65,7 +65,7 @@ EOF fi if [[ -n "$SLAPD_ADDITIONAL_SCHEMAS" ]]; then - IFS=","; declare -a schemas=($SLAPD_ADDITIONAL_SCHEMAS) + IFS=","; declare -a schemas=($SLAPD_ADDITIONAL_SCHEMAS); unset IFS for schema in "${schemas[@]}"; do slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/schema/${schema}.ldif" >/dev/null 2>&1 @@ -73,14 +73,18 @@ EOF fi if [[ -n "$SLAPD_ADDITIONAL_MODULES" ]]; then - IFS=","; declare -a modules=($SLAPD_ADDITIONAL_MODULES) + IFS=","; declare -a modules=($SLAPD_ADDITIONAL_MODULES); unset IFS for module in "${modules[@]}"; do slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/modules/${module}.ldif" >/dev/null 2>&1 done fi - chown -R openldap:openldap /etc/ldap/slapd.d/ + for file in `ls /etc/ldap/prepopulate/*.ldif`; do + slapadd -F /etc/ldap/slapd.d -l "$file" + done + + chown -R openldap:openldap /etc/ldap/slapd.d/ /var/lib/ldap/ /var/run/slapd/ else slapd_configs_in_env=`env | grep 'SLAPD_'` diff --git a/docker/blocks/openldap/notes.md b/docker/blocks/openldap/notes.md new file mode 100644 index 00000000000..71813c2899a --- /dev/null +++ b/docker/blocks/openldap/notes.md @@ -0,0 +1,13 @@ +# Notes on OpenLdap Docker Block + +Any ldif files added to the prepopulate subdirectory will be automatically imported into the OpenLdap database. + +The ldif files add three users, `ldapviewer`, `ldapeditor` and `ldapadmin`. Two groups, `admins` and `users`, are added that correspond with the group mappings in the default conf/ldap.toml. `ldapadmin` is a member of `admins` and `ldapeditor` is a member of `users`. + +Note that users that are added here need to specify a `memberOf` attribute manually as well as the `member` attribute for the group. The `memberOf` module usually does this automatically (if you add a group in Apache Directory Studio for example) but this does not work in the entrypoint script as it uses the `slapadd` command to add entries before the server has started and before the `memberOf` module is loaded. + +After adding ldif files to `prepopulate`: + +1. Remove your current docker image: `docker rm docker_openldap_1` +2. Build: `docker-compose build` +3. `docker-compose up` diff --git a/docker/blocks/openldap/prepopulate/admin.ldif b/docker/blocks/openldap/prepopulate/admin.ldif new file mode 100644 index 00000000000..3f4406d5810 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/admin.ldif @@ -0,0 +1,10 @@ +dn: cn=ldapadmin,dc=grafana,dc=org +mail: ldapadmin@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldapadmin +cn: ldapadmin +memberOf: cn=admins,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/adminsgroup.ldif b/docker/blocks/openldap/prepopulate/adminsgroup.ldif new file mode 100644 index 00000000000..d8dece4e458 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/adminsgroup.ldif @@ -0,0 +1,5 @@ +dn: cn=admins,dc=grafana,dc=org +cn: admins +member: cn=ldapadmin,dc=grafana,dc=org +objectClass: groupOfNames +objectClass: top diff --git a/docker/blocks/openldap/prepopulate/editor.ldif b/docker/blocks/openldap/prepopulate/editor.ldif new file mode 100644 index 00000000000..eba3adc4352 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/editor.ldif @@ -0,0 +1,10 @@ +dn: cn=ldapeditor,dc=grafana,dc=org +mail: ldapeditor@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldapeditor +cn: ldapeditor +memberOf: cn=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/usersgroup.ldif b/docker/blocks/openldap/prepopulate/usersgroup.ldif new file mode 100644 index 00000000000..a1de3a50d38 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/usersgroup.ldif @@ -0,0 +1,5 @@ +dn: cn=users,dc=grafana,dc=org +cn: users +member: cn=ldapeditor,dc=grafana,dc=org +objectClass: groupOfNames +objectClass: top diff --git a/docker/blocks/openldap/prepopulate/viewer.ldif b/docker/blocks/openldap/prepopulate/viewer.ldif new file mode 100644 index 00000000000..f699a7df57b --- /dev/null +++ b/docker/blocks/openldap/prepopulate/viewer.ldif @@ -0,0 +1,9 @@ +dn: cn=ldapviewer,dc=grafana,dc=org +mail: ldapviewer@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldapviewer +cn: ldapviewer From cb156ee30b415a3b8fea5ad9bfc8a81f5b85cedf Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Fri, 30 Mar 2018 16:42:48 +0200 Subject: [PATCH 031/145] fix some typos --- docs/sources/reference/dashboard.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sources/reference/dashboard.md b/docs/sources/reference/dashboard.md index dbc3ed8635c..59a5e4155ba 100644 --- a/docs/sources/reference/dashboard.md +++ b/docs/sources/reference/dashboard.md @@ -71,13 +71,13 @@ Each field in the dashboard JSON is explained below with its usage: | **timepicker** | timepicker metadata, see [timepicker section](#timepicker) for details | | **templating** | templating metadata, see [templating section](#templating) for details | | **annotations** | annotations metadata, see [annotations section](#annotations) for details | -| **schemaVersion** | version of the JSON schema (integer), incremented each time a Grafana update brings changes to the said schema | +| **schemaVersion** | version of the JSON schema (integer), incremented each time a Grafana update brings changes to said schema | | **version** | version of the dashboard (integer), incremented each time the dashboard is updated | | **panels** | panels array, see below for detail. | ## Panels -Panels are the building blocks a dashboard. It consists of datasource queries, type of graphs, aliases, etc. Panel JSON consists of an array of JSON objects, each representing a different panel. Most of the fields are common for all panels but some fields depends on the panel type. Following is an example of panel JSON of a text panel. +Panels are the building blocks of a dashboard. It consists of datasource queries, type of graphs, aliases, etc. Panel JSON consists of an array of JSON objects, each representing a different panel. Most of the fields are common for all panels but some fields depend on the panel type. Following is an example of panel JSON of a text panel. ```json "panels": [ @@ -105,7 +105,7 @@ The gridPos property describes the panel size and position in grid coordinates. - `x` The x position, in same unit as `w`. - `y` The y position, in same unit as `h`. -The grid has a negative gravity that moves panels up if there i empty space above a panel. +The grid has a negative gravity that moves panels up if there is empty space above a panel. ### timepicker @@ -161,7 +161,7 @@ Usage of the fields is explained below: ### templating -`templating` fields contains array of template variables with their saved values along with some other metadata, for example: +`templating` field contains an array of template variables with their saved values along with some other metadata, for example: ```json "templating": { @@ -236,7 +236,7 @@ Usage of the above mentioned fields in the templating section is explained below | Name | Usage | | ---- | ----- | | **enable** | whether templating is enabled or not | -| **list** | an array of objects representing, each representing one template variable | +| **list** | an array of objects each representing one template variable | | **allFormat** | format to use while fetching all values from datasource, eg: `wildcard`, `glob`, `regex`, `pipe`, etc. | | **current** | shows current selected variable text/value on the dashboard | | **datasource** | shows datasource for the variables | From 2bdcebb5b1ce52f090dfd39e3367d05596c65521 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Fri, 30 Mar 2018 16:47:51 +0200 Subject: [PATCH 032/145] add article --- docs/sources/reference/dashboard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/reference/dashboard.md b/docs/sources/reference/dashboard.md index 59a5e4155ba..30581968743 100644 --- a/docs/sources/reference/dashboard.md +++ b/docs/sources/reference/dashboard.md @@ -161,7 +161,7 @@ Usage of the fields is explained below: ### templating -`templating` field contains an array of template variables with their saved values along with some other metadata, for example: +The `templating` field contains an array of template variables with their saved values along with some other metadata, for example: ```json "templating": { From 4538ffc0e827777f7731a4519ca102d17d4857e3 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 30 Mar 2018 20:30:57 +0200 Subject: [PATCH 033/145] changelog: adds note about closing #11555 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03ad228f3c3..48b5ab29aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * **Shortcuts**: Add shortcut for duplicate panel [#11102](https://github.com/grafana/grafana/issues/11102) * **AuthProxy**: Support IPv6 in Auth proxy white list [#11330](https://github.com/grafana/grafana/pull/11330), thx [@corny](https://github.com/corny) * **SMTP**: Don't connect to STMP server using TLS unless configured. [#7189](https://github.com/grafana/grafana/issues/7189) +* **Prometheus**: Escape backslash in labels correctly. [#10555](https://github.com/grafana/grafana/issues/10555), thx [@roidelapluie](https://github.com/roidelapluie) # 5.0.4 (2018-03-28) From 13deb891f35c963a64d2dfe6db1282d6dbde67c2 Mon Sep 17 00:00:00 2001 From: Alexey Velikiy Date: Mon, 2 Apr 2018 09:09:10 +0300 Subject: [PATCH 034/145] No need for node_modules/bin in npm run-script (#11449) how run-script works: https://docs.npmjs.com/cli/run-script --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6dcfc16b82b..030219fe587 100644 --- a/package.json +++ b/package.json @@ -104,10 +104,10 @@ "test": "grunt test", "test:coverage": "grunt test --coverage=true", "lint": "tslint -c tslint.json --project tsconfig.json --type-check", - "karma": "node ./node_modules/grunt-cli/bin/grunt karma:dev", - "jest": "node ./node_modules/jest-cli/bin/jest.js --notify --watch", - "api-tests": "node ./node_modules/jest-cli/bin/jest.js --notify --watch --config=tests/api/jest.js", - "precommit": "lint-staged && node ./node_modules/grunt-cli/bin/grunt precommit" + "karma": "grunt karma:dev", + "jest": "jest --notify --watch", + "api-tests": "jest --notify --watch --config=tests/api/jest.js", + "precommit": "lint-staged && grunt precommit" }, "lint-staged": { "*.{ts,tsx}": [ From 00f67ea7c7511ff5cb18fd05cfaba9c6f299e98f Mon Sep 17 00:00:00 2001 From: Alexey Velikiy Date: Mon, 2 Apr 2018 09:13:22 +0300 Subject: [PATCH 035/145] rm panel.type constrain from threshold_mapper.ts (#11448) --- public/app/features/alerting/threshold_mapper.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/app/features/alerting/threshold_mapper.ts b/public/app/features/alerting/threshold_mapper.ts index 3025e13aacd..9142c74b6e3 100644 --- a/public/app/features/alerting/threshold_mapper.ts +++ b/public/app/features/alerting/threshold_mapper.ts @@ -1,9 +1,5 @@ export class ThresholdMapper { static alertToGraphThresholds(panel) { - if (panel.type !== 'graph') { - return false; - } - for (var i = 0; i < panel.alert.conditions.length; i++) { let condition = panel.alert.conditions[i]; if (condition.type !== 'query') { From 77d2ee9add2c45c0bd6ffbffff9ea2648c39e82e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 27 Mar 2018 23:27:23 +0300 Subject: [PATCH 036/145] Initially move to baron scrollbar --- package.json | 1 + .../core/components/ScrollBar/ScrollBar.tsx | 31 +++-- public/app/core/components/scroll/scroll.ts | 37 ++++- public/app/core/components/search/search.html | 2 + public/app/features/panel/panel_directive.ts | 31 ++++- public/app/partials/dashboard.html | 22 +-- public/app/plugins/panel/graph/legend.ts | 48 +++++-- public/app/plugins/panel/graph/template.ts | 4 +- public/sass/components/_panel_add_panel.scss | 5 +- public/sass/components/_panel_graph.scss | 7 +- public/sass/components/_scrollbar.scss | 126 +++++++++++++++++- public/sass/components/_search.scss | 7 + public/views/index.template.html | 3 +- 13 files changed, 276 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 6dcfc16b82b..724bbffb6fa 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,7 @@ "angular-route": "^1.6.6", "angular-sanitize": "^1.6.6", "babel-polyfill": "^6.26.0", + "baron": "^3.0.3", "brace": "^0.10.0", "classnames": "^2.2.5", "clipboard": "^1.7.1", diff --git a/public/app/core/components/ScrollBar/ScrollBar.tsx b/public/app/core/components/ScrollBar/ScrollBar.tsx index 7d9e015df94..a358dc1926a 100644 --- a/public/app/core/components/ScrollBar/ScrollBar.tsx +++ b/public/app/core/components/ScrollBar/ScrollBar.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import PerfectScrollbar from 'perfect-scrollbar'; +import baron from 'baron'; export interface Props { children: any; @@ -8,31 +8,36 @@ export interface Props { export default class ScrollBar extends React.Component { private container: any; - private ps: PerfectScrollbar; + private scrollbar: baron; constructor(props) { super(props); } componentDidMount() { - this.ps = new PerfectScrollbar(this.container, { - wheelPropagation: true, + this.scrollbar = baron({ + root: this.container.parentElement, + scroller: this.container, + bar: '.baron__bar', + barOnCls: '_scrollbar', + scrollingCls: '_scrolling', + track: '.baron__track', }); } componentDidUpdate() { - this.ps.update(); + this.scrollbar.update(); } componentWillUnmount() { - this.ps.destroy(); + this.scrollbar.dispose(); } // methods can be invoked by outside setScrollTop(top) { if (this.container) { this.container.scrollTop = top; - this.ps.update(); + this.scrollbar.update(); return true; } @@ -42,7 +47,7 @@ export default class ScrollBar extends React.Component { setScrollLeft(left) { if (this.container) { this.container.scrollLeft = left; - this.ps.update(); + this.scrollbar.update(); return true; } @@ -55,8 +60,14 @@ export default class ScrollBar extends React.Component { render() { return ( -
- {this.props.children} +
+
+ {this.props.children} +
+ +
+
+
); } diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index fbf5fd6cd37..ad8b4ed3d8e 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -1,15 +1,41 @@ -import PerfectScrollbar from 'perfect-scrollbar'; +import $ from 'jquery'; +import baron from 'baron'; import coreModule from 'app/core/core_module'; import appEvents from 'app/core/app_events'; +const scrollBarHTML = ` +
+
+
+`; + +const scrollRootClass = 'baron baron__root'; +const scrollerClass = 'baron__scroller'; + export function geminiScrollbar() { return { restrict: 'A', link: function(scope, elem, attrs) { - let scrollbar = new PerfectScrollbar(elem[0], { - wheelPropagation: true, - wheelSpeed: 3, + let scrollRoot = elem.parent(); + let scroller = elem; + + if (attrs.grafanaScrollbar && attrs.grafanaScrollbar === 'scrollonroot') { + scrollRoot = scroller; + } + + scrollRoot.addClass(scrollRootClass); + $(scrollBarHTML).appendTo(scrollRoot); + elem.addClass(scrollerClass); + + let scrollbar = baron({ + root: scrollRoot[0], + scroller: scroller[0], + bar: '.baron__bar', + barOnCls: '_scrollbar', + scrollingCls: '_scrolling', + track: '.baron__track', }); + let lastPos = 0; appEvents.on( @@ -37,7 +63,8 @@ export function geminiScrollbar() { }); scope.$on('$destroy', () => { - scrollbar.destroy(); + // scrollbar.destroy(); + scrollbar.dispose(); }); }, }; diff --git a/public/app/core/components/search/search.html b/public/app/core/components/search/search.html index acaf0730a6b..afb9e723cad 100644 --- a/public/app/core/components/search/search.html +++ b/public/app/core/components/search/search.html @@ -19,6 +19,7 @@
+
No dashboards matching your query were found.
+
diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index dec7868a553..4edf293c801 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -1,6 +1,7 @@ import angular from 'angular'; +import $ from 'jquery'; import Drop from 'tether-drop'; -import PerfectScrollbar from 'perfect-scrollbar'; +import baron from 'baron'; var module = angular.module('grafana.directives'); @@ -86,6 +87,9 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { function panelHeightUpdated() { panelContent.css({ height: ctrl.height + 'px' }); + } + + function resizeScrollableContent() { if (panelScrollbar) { panelScrollbar.update(); } @@ -100,8 +104,26 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { // update scrollbar after mounting ctrl.events.on('component-did-mount', () => { if (ctrl.__proto__.constructor.scrollable) { - panelScrollbar = new PerfectScrollbar(panelContent[0], { - wheelPropagation: true, + const scrollRootClass = 'baron baron__root baron__clipper panel-content--scrollable'; + const scrollerClass = 'baron__scroller'; + const scrollBarHTML = ` +
+
+
+ `; + + let scrollRoot = panelContent; + let scroller = panelContent.find(':first-child').find(':first-child'); + scrollRoot.addClass(scrollRootClass); + $(scrollBarHTML).appendTo(scrollRoot); + scroller.addClass(scrollerClass); + + panelScrollbar = baron({ + root: scrollRoot[0], + scroller: scroller[0], + bar: '.baron__bar', + barOnCls: '_scrollbar', + scrollingCls: '_scrolling', }); } }); @@ -110,6 +132,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { ctrl.calculatePanelHeight(); panelHeightUpdated(); $timeout(() => { + resizeScrollableContent(); ctrl.render(); }); }); @@ -199,7 +222,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { } if (panelScrollbar) { - panelScrollbar.update(); + panelScrollbar.dispose(); } }); }, diff --git a/public/app/partials/dashboard.html b/public/app/partials/dashboard.html index 210275d2200..a3d5a4439ee 100644 --- a/public/app/partials/dashboard.html +++ b/public/app/partials/dashboard.html @@ -1,18 +1,20 @@
-
- - +
+
+ + -
- - +
+ + - - + + +
diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index d1186ae0b1e..761671c356d 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -1,7 +1,7 @@ import angular from 'angular'; import _ from 'lodash'; import $ from 'jquery'; -import PerfectScrollbar from 'perfect-scrollbar'; +import baron from 'baron'; var module = angular.module('grafana.directives'); @@ -250,23 +250,53 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { } function addScrollbar() { - const scrollbarOptions = { - // Number of pixels the content height can surpass the container height without enabling the scroll bar. - scrollYMarginOffset: 2, - suppressScrollX: true, - wheelPropagation: true, + const scrollRootClass = 'baron baron__root'; + const scrollerClass = 'baron__scroller'; + const scrollBarHTML = ` +
+
+
+ `; + + let scrollRoot = elem.parent(); + // let scroller = elem.find(':first-child').first(); + let scroller = elem; + + // clear existing scroll bar track to prevent duplication + elem + .parent() + .find('.baron__track') + .remove(); + + scrollRoot.addClass(scrollRootClass); + $(scrollBarHTML).appendTo(scrollRoot); + scroller.addClass(scrollerClass); + + // Fix .graph-legend-content max-height + // Couldn't find how to do it via CSS + const legendHeight = scrollRoot.height(); + elem.css('max-height', legendHeight); + + let scrollbarParams = { + root: scrollRoot[0], + scroller: scroller[0], + bar: '.baron__bar', + track: '.baron__track', + barOnCls: '_scrollbar', + scrollingCls: '_scrolling', }; if (!legendScrollbar) { - legendScrollbar = new PerfectScrollbar(elem[0], scrollbarOptions); + legendScrollbar = baron(scrollbarParams); } else { - legendScrollbar.update(); + destroyScrollbar(); + legendScrollbar = baron(scrollbarParams); } } function destroyScrollbar() { if (legendScrollbar) { - legendScrollbar.destroy(); + legendScrollbar.dispose(); legendScrollbar = undefined; } } diff --git a/public/app/plugins/panel/graph/template.ts b/public/app/plugins/panel/graph/template.ts index 0b9eb8227df..c897327fe1a 100644 --- a/public/app/plugins/panel/graph/template.ts +++ b/public/app/plugins/panel/graph/template.ts @@ -3,7 +3,9 @@ var template = `
-
+
+
+
`; diff --git a/public/sass/components/_panel_add_panel.scss b/public/sass/components/_panel_add_panel.scss index 51754a54d92..ee5d757d841 100644 --- a/public/sass/components/_panel_add_panel.scss +++ b/public/sass/components/_panel_add_panel.scss @@ -1,5 +1,9 @@ .add-panel { height: 100%; + + .baron__root { + height: calc(100% - 43px); + } } .add-panel__header { @@ -39,7 +43,6 @@ flex-direction: row; flex-wrap: wrap; overflow: auto; - height: calc(100% - 43px); align-content: flex-start; justify-content: space-around; position: relative; diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index e15cd576367..5885b769564 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -53,7 +53,7 @@ max-height: 30%; margin: 0; text-align: center; - padding-top: 6px; + // padding-top: 6px; position: relative; .popover-content { @@ -61,6 +61,11 @@ } } +.graph-legend-content { + position: relative; + padding-top: 6px; +} + .graph-legend-icon { position: relative; padding-right: 4px; diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 42818e786f6..216ff1fa1c4 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -9,6 +9,11 @@ -ms-touch-action: auto; } +// ._scrollbar { +// overflow-x: hidden !important; +// overflow-y: auto; +// } + /* * Scrollbar rail styles */ @@ -104,13 +109,19 @@ // Srollbars // -::-webkit-scrollbar { - width: 8px; - height: 8px; -} +// ::-webkit-scrollbar { +// width: 8px; +// height: 8px; +// } -::-webkit-scrollbar:hover { - height: 8px; +// ::-webkit-scrollbar:hover { +// height: 8px; +// } + +::-webkit-scrollbar { + // Hide system scrollbar (Mac OS X) + width: 0; + height: 0; } ::-webkit-scrollbar-button:start:decrement, @@ -172,3 +183,106 @@ border-top: 1px solid $scrollbarBorder; border-left: 1px solid $scrollbarBorder; } + +// Baron styles + +.baron { + display: inline-block; + overflow: hidden; +} + +.baron__clipper { + position: relative; + overflow: hidden; + height: 100%; +} + +.baron__scroller { + overflow-y: scroll; + -ms-overflow-style: none; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin: 0; + border: 0; + padding: 0; + width: 100%; + height: 100%; + -webkit-overflow-scrolling: touch; + /* remove line to customize scrollbar in iOs */ +} + +.baron__scroller::-webkit-scrollbar { + width: 0; + height: 0; +} + +.baron__track { + display: none; + position: absolute; + top: 0; + right: 0; + bottom: 0; +} + +.baron._scrollbar .baron__track { + display: block; +} + +.baron__free { + position: absolute; + top: 0; + bottom: 0; + right: 0; +} + +.baron__bar { + display: none; + position: absolute; + right: 0; + z-index: 1; + // width: 10px; + background: #999; + + // height: 15px; + width: 15px; + transition: background-color 0.2s linear, opacity 0.2s linear; + opacity: 0; +} + +.baron._scrollbar .baron__bar { + display: block; + + @include gradient-vertical($scrollbarBackground, $scrollbarBackground2); + border-radius: 6px; + width: 6px; + /* there must be 'right' for ps__thumb-y */ + right: 0px; + /* please don't change 'position' */ + position: absolute; + + // background-color: transparent; + // opacity: 0.6; + + &:hover, + &:focus { + // background-color: transparent; + opacity: 0.9; + } +} + +.baron._scrolling > .baron__track .baron__bar { + opacity: 0.6; +} + +.baron__control { + display: none; +} + +.baron.panel-content--scrollable { + // Width needs to be set to prevent content width issues + width: 100%; + + .baron__scroller { + padding-top: 1px; + } +} diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index 47d4a926968..ba37d7ce98f 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -61,6 +61,8 @@ display: flex; flex-direction: column; flex-grow: 1; + + // overflow-y: scroll; } .search-dropdown__col_2 { @@ -99,6 +101,11 @@ } } +.search-results-scroller { + position: relative; + height: 100%; +} + .search-results-container { height: 100%; display: block; diff --git a/public/views/index.template.html b/public/views/index.template.html index 2d408f70f8c..4cb4a0d57ce 100644 --- a/public/views/index.template.html +++ b/public/views/index.template.html @@ -16,7 +16,7 @@ - + @@ -40,6 +40,7 @@
+
From efaf267deba548189e89e04a7557d48ebb46ca04 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 2 Apr 2018 20:47:09 +0300 Subject: [PATCH 037/145] scrollbar: fix legend rendering issues --- public/app/plugins/panel/graph/legend.ts | 19 ++++++------------- public/app/plugins/panel/graph/template.ts | 4 +--- public/sass/components/_panel_graph.scss | 7 ++++--- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index 761671c356d..1952f7241e7 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -238,8 +238,10 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { tbodyElem.append(tableHeaderElem); tbodyElem.append(seriesElements); elem.append(tbodyElem); + tbodyElem.wrap('
'); } else { - elem.append(seriesElements); + elem.append('
'); + elem.find('.graph-legend-content').append(seriesElements); } if (!panel.legend.rightSide || (panel.legend.rightSide && legendWidth !== legendRightDefaultWidth)) { @@ -258,25 +260,16 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
`; - let scrollRoot = elem.parent(); - // let scroller = elem.find(':first-child').first(); - let scroller = elem; + let scrollRoot = elem; + let scroller = elem.find('.graph-legend-content'); // clear existing scroll bar track to prevent duplication - elem - .parent() - .find('.baron__track') - .remove(); + scrollRoot.find('.baron__track').remove(); scrollRoot.addClass(scrollRootClass); $(scrollBarHTML).appendTo(scrollRoot); scroller.addClass(scrollerClass); - // Fix .graph-legend-content max-height - // Couldn't find how to do it via CSS - const legendHeight = scrollRoot.height(); - elem.css('max-height', legendHeight); - let scrollbarParams = { root: scrollRoot[0], scroller: scroller[0], diff --git a/public/app/plugins/panel/graph/template.ts b/public/app/plugins/panel/graph/template.ts index c897327fe1a..0b9eb8227df 100644 --- a/public/app/plugins/panel/graph/template.ts +++ b/public/app/plugins/panel/graph/template.ts @@ -3,9 +3,7 @@ var template = `
-
-
-
+
`; diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index 5885b769564..83747d6caaf 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -53,9 +53,11 @@ max-height: 30%; margin: 0; text-align: center; - // padding-top: 6px; + padding-top: 6px; position: relative; + height: 100%; + .popover-content { padding: 0; } @@ -63,7 +65,6 @@ .graph-legend-content { position: relative; - padding-top: 6px; } .graph-legend-icon { @@ -129,9 +130,9 @@ .graph-legend-table { tbody { display: block; + position: relative; overflow-y: auto; overflow-x: hidden; - height: 100%; padding-bottom: 1px; padding-right: 5px; padding-left: 5px; From 7b75b251b12752969bf926334f63e441745e52bd Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 2 Apr 2018 21:15:10 +0300 Subject: [PATCH 038/145] scrollbar: fix Firefox issue (white stripe on the right of scrollbar) --- public/sass/components/_scrollbar.scss | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 216ff1fa1c4..ec6f64a13e4 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -189,6 +189,10 @@ .baron { display: inline-block; overflow: hidden; + + // Width needs to be set to prevent content width issues + // Set to 99% instead of 100% for fixing Firefox issue (white stripe on the right of scrollbar) + width: 99%; } .baron__clipper { @@ -280,7 +284,8 @@ .baron.panel-content--scrollable { // Width needs to be set to prevent content width issues - width: 100%; + // Set to 99% instead of 100% for fixing Firefox issue (white stripe on the right of scrollbar) + width: 99%; .baron__scroller { padding-top: 1px; From 6320bdf39399b0f73b65025f3c55f4d264fc1dc0 Mon Sep 17 00:00:00 2001 From: Alexey Velikiy Date: Tue, 3 Apr 2018 07:21:36 +0300 Subject: [PATCH 039/145] Webpack Grafana plugin template project to links (#11457) --- PLUGIN_DEV.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PLUGIN_DEV.md b/PLUGIN_DEV.md index 9d831a95697..4e2e080ebe6 100644 --- a/PLUGIN_DEV.md +++ b/PLUGIN_DEV.md @@ -9,6 +9,7 @@ upgrading Grafana please check here before creating an issue. - [Datasource plugin written in typescript](https://github.com/grafana/typescript-template-datasource) - [Simple json dataource plugin](https://github.com/grafana/simple-json-datasource) - [Plugin development guide](http://docs.grafana.org/plugins/developing/development/) +- [Webpack Grafana plugin template project](https://github.com/CorpGlory/grafana-plugin-template-webpack) ## Changes in v4.6 From 98e1404fed0a1cab9cdc6f7404d805459f374f48 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 3 Apr 2018 09:39:46 +0200 Subject: [PATCH 040/145] added if to onAppevent, renamed appevent, add appevent to applyCustom and setRelativeFilter --- public/app/core/services/keybindingSrv.ts | 8 +++++++- public/app/features/dashboard/timepicker/timepicker.ts | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index e51c0477ffa..35cd7808d12 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -23,7 +23,13 @@ export class KeybindingSrv { this.setupGlobal(); appEvents.on('show-modal', () => (this.modalOpen = true)); - $rootScope.onAppEvent('openTimepicker', () => (this.timepickerOpen = true)); + $rootScope.onAppEvent('escTimepicker', () => { + if (!this.timepickerOpen) { + this.timepickerOpen = true; + } else { + this.timepickerOpen = false; + } + }); } setupGlobal() { diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 19c3db7f6d3..32ce07e4468 100644 --- a/public/app/features/dashboard/timepicker/timepicker.ts +++ b/public/app/features/dashboard/timepicker/timepicker.ts @@ -96,13 +96,12 @@ export class TimePickerCtrl { } openDropdown() { + this.$rootScope.appEvent('escTimepicker'); if (this.isOpen) { this.isOpen = false; return; } - this.$rootScope.appEvent('openTimepicker'); - this.onRefresh(); this.editTimeRaw = this.timeRaw; this.timeOptions = rangeUtil.getRelativeTimesList(this.panel, this.rangeString); @@ -118,6 +117,7 @@ export class TimePickerCtrl { } applyCustom() { + this.$rootScope.appEvent('escTimepicker'); if (this.refresh.value !== this.dashboard.refresh) { this.timeSrv.setAutoRefresh(this.refresh.value); } @@ -139,6 +139,7 @@ export class TimePickerCtrl { } setRelativeFilter(timespan) { + this.$rootScope.appEvent('escTimepicker'); var range = { from: timespan.from, to: timespan.to }; if (this.panel.nowDelay && range.to === 'now') { From 8b2441e0985f68b11e16f153c9a485cc2c873656 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 09:51:29 +0200 Subject: [PATCH 041/145] mssql: typos in help sections Fixes #11455 --- .../plugins/datasource/mssql/partials/annotations.editor.html | 2 +- public/app/plugins/datasource/mssql/partials/query.editor.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/mssql/partials/annotations.editor.html b/public/app/plugins/datasource/mssql/partials/annotations.editor.html index 75eaa3ed1d9..8a94c470379 100644 --- a/public/app/plugins/datasource/mssql/partials/annotations.editor.html +++ b/public/app/plugins/datasource/mssql/partials/annotations.editor.html @@ -28,7 +28,7 @@ An annotation is an event that is overlayed on top of graphs. The query can have Macros: - $__time(column) -> column AS time - $__timeEpoch(column) -> DATEDIFF(second, '1970-01-01', column) AS time -- $__timeFilter(column) -> column >= DATEADD(s, 18446744066914186738, '1970-01-01') AND column &t;= DATEADD(s, 18446744066914187038, '1970-01-01') +- $__timeFilter(column) -> column >= DATEADD(s, 18446744066914186738, '1970-01-01') AND column <= DATEADD(s, 18446744066914187038, '1970-01-01') - $__unixEpochFilter(column) -> column >= 1492750877 AND column <= 1492750877 Or build your own conditionals using these macros which just return the values: diff --git a/public/app/plugins/datasource/mssql/partials/query.editor.html b/public/app/plugins/datasource/mssql/partials/query.editor.html index c7dc030be6e..f29dfa18db2 100644 --- a/public/app/plugins/datasource/mssql/partials/query.editor.html +++ b/public/app/plugins/datasource/mssql/partials/query.editor.html @@ -49,7 +49,7 @@ Table: Macros: - $__time(column) -> column AS time - $__timeEpoch(column) -> DATEDIFF(second, '1970-01-01', column) AS time -- $__timeFilter(column) -> column >= DATEADD(s, 18446744066914186738, '1970-01-01') AND column &t;= DATEADD(s, 18446744066914187038, '1970-01-01') +- $__timeFilter(column) -> column >= DATEADD(s, 18446744066914186738, '1970-01-01') AND column <= DATEADD(s, 18446744066914187038, '1970-01-01') - $__unixEpochFilter(column) -> column >= 1492750877 AND column <= 1492750877 - $__timeGroup(column, '5m'[, fillvalue]) -> CAST(ROUND(DATEDIFF(second, '1970-01-01', column)/300.0, 0) as bigint)*300. Providing a fillValue of NULL or floating value will automatically fill empty series in timerange with that value. From feb222f63327c8dbac43a2d44d4207f80bcead2c Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 3 Apr 2018 09:53:14 +0200 Subject: [PATCH 042/145] changed variable for tabbed close btn hover, and changed text-strong variable for lighttheme, removed commented out variable --- public/sass/_variables.light.scss | 3 +-- public/sass/components/_tabbed_view.scss | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index a59350d2195..bb8f93dbe69 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -59,9 +59,8 @@ $critical: #ec2128; $body-bg: $gray-7; $page-bg: $gray-7; $body-color: $gray-1; -//$text-color: $dark-4; $text-color: $gray-1; -$text-color-strong: $white; +$text-color-strong: $dark-2; $text-color-weak: $gray-2; $text-color-faint: $gray-4; $text-color-emphasis: $dark-5; diff --git a/public/sass/components/_tabbed_view.scss b/public/sass/components/_tabbed_view.scss index dfd760753fe..bf95d453504 100644 --- a/public/sass/components/_tabbed_view.scss +++ b/public/sass/components/_tabbed_view.scss @@ -43,7 +43,7 @@ font-size: 120%; } &:hover { - color: $white; + color: $text-color-strong; } } From 3fcd2627110e180d3538eda4cc01d82f1c2cef84 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 3 Apr 2018 14:48:48 +0300 Subject: [PATCH 043/145] scrollbar: fix side menu on mobile devices --- public/sass/components/_scrollbar.scss | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index ec6f64a13e4..776c85d96b5 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -192,7 +192,17 @@ // Width needs to be set to prevent content width issues // Set to 99% instead of 100% for fixing Firefox issue (white stripe on the right of scrollbar) - width: 99%; + min-width: 99%; +} + +// Fix for side menu on mobile devices +.sidemenu-open.sidemenu-open--xs { + .main-view.baron { + min-width: 0%; + } +} +.main-view.baron { + min-width: 99%; } .baron__clipper { From 658c6a8ff4b959b6cd394550ea7d9ecc7d2e301c Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 3 Apr 2018 15:43:25 +0200 Subject: [PATCH 044/145] changed from margin to padding --- public/sass/pages/_alerting.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/sass/pages/_alerting.scss b/public/sass/pages/_alerting.scss index 5a481b55a8a..fb6b6e78d1b 100644 --- a/public/sass/pages/_alerting.scss +++ b/public/sass/pages/_alerting.scss @@ -108,7 +108,8 @@ justify-content: center; align-items: center; width: 40px; - margin-right: 8px; + //margin-right: 8px; + padding: 0 4px 0 2px; .icon-gf, .fa { font-size: 200%; From 533f3a9e8c7df8dfd0f769c707c49aff55241bf8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 19:09:49 +0200 Subject: [PATCH 045/145] settings: fixes test For some reason, the url parse does not fail anymore for curly braces. Add a colon in the first segment to make sure the url parse fails. --- pkg/setting/setting_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/setting/setting_test.go b/pkg/setting/setting_test.go index 87f9916075e..2da728b7298 100644 --- a/pkg/setting/setting_test.go +++ b/pkg/setting/setting_test.go @@ -37,8 +37,8 @@ func TestLoadingSettings(t *testing.T) { So(appliedEnvOverrides, ShouldContain, "GF_SECURITY_ADMIN_PASSWORD=*********") }) - Convey("Should replace password when defined in environment2", func() { - os.Setenv("GF_DATABASE_URL", "postgres://grafana:sec{ret@postgres:5432/grafana") + Convey("Should return an error when url is invalid", func() { + os.Setenv("GF_DATABASE_URL", "postgres.%31://grafana:secret@postgres:5432/grafana") err := NewConfigContext(&CommandLineArgs{HomePath: "../../"}) So(err, ShouldNotBeNil) From a6c76355285aef2f477e288a9104293870ed8ac6 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 19:39:50 +0200 Subject: [PATCH 046/145] graphite: fixes #11434 --- public/app/plugins/datasource/graphite/add_graphite_func.ts | 1 + public/app/plugins/datasource/graphite/func_editor.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/public/app/plugins/datasource/graphite/add_graphite_func.ts b/public/app/plugins/datasource/graphite/add_graphite_func.ts index f2a596c7071..6e64b5d12d0 100644 --- a/public/app/plugins/datasource/graphite/add_graphite_func.ts +++ b/public/app/plugins/datasource/graphite/add_graphite_func.ts @@ -4,6 +4,7 @@ import $ from 'jquery'; import rst2html from 'rst2html'; import Drop from 'tether-drop'; +/** @ngInject */ export function graphiteAddFunc($compile) { const inputTemplate = ''; diff --git a/public/app/plugins/datasource/graphite/func_editor.ts b/public/app/plugins/datasource/graphite/func_editor.ts index 86135aef343..82a838e7660 100644 --- a/public/app/plugins/datasource/graphite/func_editor.ts +++ b/public/app/plugins/datasource/graphite/func_editor.ts @@ -3,6 +3,7 @@ import _ from 'lodash'; import $ from 'jquery'; import rst2html from 'rst2html'; +/** @ngInject */ export function graphiteFuncEditor($compile, templateSrv, popoverSrv) { const funcSpanTemplate = '{{func.def.name}}('; const paramTemplate = From 4a93766143a73fe8af896c25cf102e1ca93a90f7 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Fri, 16 Mar 2018 12:56:39 -0400 Subject: [PATCH 047/145] Add case-insensitive sort for variables. --- public/app/features/templating/editor_ctrl.ts | 2 ++ public/app/features/templating/query_variable.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/public/app/features/templating/editor_ctrl.ts b/public/app/features/templating/editor_ctrl.ts index 428770a21e5..f20e93be42c 100644 --- a/public/app/features/templating/editor_ctrl.ts +++ b/public/app/features/templating/editor_ctrl.ts @@ -23,6 +23,8 @@ export class VariableEditorCtrl { { value: 2, text: 'Alphabetical (desc)' }, { value: 3, text: 'Numerical (asc)' }, { value: 4, text: 'Numerical (desc)' }, + { value: 5, text: 'Alphabetical (case-insensitive, asc)' }, + { value: 6, text: 'Alphabetical (case-insensitive, desc)' }, ]; $scope.hideOptions = [{ value: 0, text: '' }, { value: 1, text: 'Label' }, { value: 2, text: 'Variable' }]; diff --git a/public/app/features/templating/query_variable.ts b/public/app/features/templating/query_variable.ts index 58c7b692581..b87167ad646 100644 --- a/public/app/features/templating/query_variable.ts +++ b/public/app/features/templating/query_variable.ts @@ -197,6 +197,8 @@ export class QueryVariable implements Variable { return parseInt(matches[1], 10); } }); + } else if (sortType === 3) { + options = _.sortBy(options, opt => { return _.toLower(opt.text); }); } if (reverseSort) { From 70eb281840f9d7b786eb315356ed4f195b489506 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 22:34:16 +0200 Subject: [PATCH 048/145] variables: adds test for variable sorting ref #11280 --- .../templating/specs/query_variable.jest.ts | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/public/app/features/templating/specs/query_variable.jest.ts b/public/app/features/templating/specs/query_variable.jest.ts index 7840d9e4242..ce753a4b205 100644 --- a/public/app/features/templating/specs/query_variable.jest.ts +++ b/public/app/features/templating/specs/query_variable.jest.ts @@ -40,11 +40,11 @@ describe('QueryVariable', () => { }); describe('can convert and sort metric names', () => { - var variable = new QueryVariable({}, null, null, null, null); - variable.sort = 3; // Numerical (asc) + const variable = new QueryVariable({}, null, null, null, null); + let input; - describe('can sort a mixed array of metric variables', () => { - var input = [ + beforeEach(() => { + input = [ { text: '0', value: '0' }, { text: '1', value: '1' }, { text: null, value: 3 }, @@ -58,11 +58,18 @@ describe('QueryVariable', () => { { text: '', value: undefined }, { text: undefined, value: '' }, ]; + }); + + describe('can sort a mixed array of metric variables in numeric order', () => { + let result; + + beforeEach(() => { + variable.sort = 3; // Numerical (asc) + result = variable.metricNamesToVariableValues(input); + }); - var result = variable.metricNamesToVariableValues(input); it('should return in same order', () => { var i = 0; - expect(result.length).toBe(11); expect(result[i++].text).toBe(''); expect(result[i++].text).toBe('0'); @@ -73,5 +80,27 @@ describe('QueryVariable', () => { expect(result[i++].text).toBe('6'); }); }); + + describe('can sort a mixed array of metric variables in alphabetical order', () => { + let result; + + beforeEach(() => { + variable.sort = 5; // Alphabetical CI (asc) + result = variable.metricNamesToVariableValues(input); + }); + + it('should return in same order', () => { + var i = 0; + console.log(result); + 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('10'); + expect(result[i++].text).toBe('3'); + expect(result[i++].text).toBe('4'); + expect(result[i++].text).toBe('5'); + }); + }); }); }); From 4b1c1acab42f06e317ddbf2e91ff59336f2a9306 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 22:40:48 +0200 Subject: [PATCH 049/145] changelog: adds note for #11128 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48b5ab29aa6..3f5b76486c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * **AuthProxy**: Support IPv6 in Auth proxy white list [#11330](https://github.com/grafana/grafana/pull/11330), thx [@corny](https://github.com/corny) * **SMTP**: Don't connect to STMP server using TLS unless configured. [#7189](https://github.com/grafana/grafana/issues/7189) * **Prometheus**: Escape backslash in labels correctly. [#10555](https://github.com/grafana/grafana/issues/10555), thx [@roidelapluie](https://github.com/roidelapluie) +* **Variables** Case-insensitive sorting for template values [#11128](https://github.com/grafana/grafana/issues/11128) thx [@cross](https://github.com/cross) # 5.0.4 (2018-03-28) From 9841c81952de6eb13e433ffb6d820dc43ea55b96 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 4 Apr 2018 13:40:08 +0200 Subject: [PATCH 050/145] Notes for closing #7119 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f5b76486c4..4d221480de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * **Prometheus**: Show template variable candidate in query editor [#9210](https://github.com/grafana/grafana/issues/9210), thx [@mtanda](https://github.com/mtanda) * **Prometheus**: Support POST for query and query_range [#9859](https://github.com/grafana/grafana/pull/9859), thx [@mtanda](https://github.com/mtanda) * **Alerting**: Add support for retries on alert queries [#5855](https://github.com/grafana/grafana/issues/5855), thx [@Thib17](https://github.com/Thib17) +* **Table**: Table plugin value mappings [#7119](https://github.com/grafana/grafana/issues/7119), thx [infernix](https://github.com/infernix) ### Minor * **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes) From a265c77cf907d3522515d9818d6e77d127e70715 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 14:51:46 +0300 Subject: [PATCH 051/145] scrollbar: fix Firefox scroll position restore --- public/app/features/dashboard/view_state_srv.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/view_state_srv.ts b/public/app/features/dashboard/view_state_srv.ts index 576b8b6fce8..fa471b89989 100644 --- a/public/app/features/dashboard/view_state_srv.ts +++ b/public/app/features/dashboard/view_state_srv.ts @@ -196,9 +196,10 @@ export class DashboardViewState { this.oldTimeRange = ctrl.range; this.fullscreenPanel = panelScope; + // Firefox doesn't return scrollTop postion properly if 'dash-scroll' is emitted after setViewMode() + this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 }); this.dashboard.setViewMode(ctrl.panel, true, ctrl.editMode); this.$scope.appEvent('panel-fullscreen-enter', { panelId: ctrl.panel.id }); - this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 }); } registerPanel(panelScope) { From 13f6d3be87a6c49a6b1f691324e5aca23aa94158 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 4 Apr 2018 14:16:39 +0200 Subject: [PATCH 052/145] migrated last all.js to ts --- public/app/core/services/all.js | 13 ------------- public/app/core/services/all.ts | 10 ++++++++++ public/app/features/all.js | 15 --------------- public/app/features/all.ts | 13 +++++++++++++ public/app/features/panel/all.js | 9 --------- public/app/features/panel/all.ts | 7 +++++++ public/app/features/playlist/all.js | 7 ------- public/app/features/playlist/all.ts | 5 +++++ 8 files changed, 35 insertions(+), 44 deletions(-) delete mode 100644 public/app/core/services/all.js create mode 100644 public/app/core/services/all.ts delete mode 100644 public/app/features/all.js create mode 100644 public/app/features/all.ts delete mode 100644 public/app/features/panel/all.js create mode 100644 public/app/features/panel/all.ts delete mode 100644 public/app/features/playlist/all.js create mode 100644 public/app/features/playlist/all.ts diff --git a/public/app/core/services/all.js b/public/app/core/services/all.js deleted file mode 100644 index 0a973440c5e..00000000000 --- a/public/app/core/services/all.js +++ /dev/null @@ -1,13 +0,0 @@ -define([ - './alert_srv', - './util_srv', - './context_srv', - './timer', - './analytics', - './popover_srv', - './segment_srv', - './backend_srv', - './dynamic_directive_srv', - './bridge_srv' -], -function () {}); diff --git a/public/app/core/services/all.ts b/public/app/core/services/all.ts new file mode 100644 index 00000000000..989015d2872 --- /dev/null +++ b/public/app/core/services/all.ts @@ -0,0 +1,10 @@ +import './alert_srv'; +import './util_srv'; +import './context_srv'; +import './timer'; +import './analytics'; +import './popover_srv'; +import './segment_srv'; +import './backend_srv'; +import './dynamic_directive_srv'; +import './bridge_srv'; diff --git a/public/app/features/all.js b/public/app/features/all.js deleted file mode 100644 index 759be6c11d2..00000000000 --- a/public/app/features/all.js +++ /dev/null @@ -1,15 +0,0 @@ -define([ - './panellinks/module', - './dashlinks/module', - './annotations/all', - './templating/all', - './plugins/all', - './dashboard/all', - './playlist/all', - './snapshot/all', - './panel/all', - './org/all', - './admin/admin', - './alerting/all', - './styleguide/styleguide', -], function () {}); diff --git a/public/app/features/all.ts b/public/app/features/all.ts new file mode 100644 index 00000000000..df987a8b59b --- /dev/null +++ b/public/app/features/all.ts @@ -0,0 +1,13 @@ +import './panellinks/module'; +import './dashlinks/module'; +import './annotations/all'; +import './templating/all'; +import './plugins/all'; +import './dashboard/all'; +import './playlist/all'; +import './snapshot/all'; +import './panel/all'; +import './org/all'; +import './admin/admin'; +import './alerting/all'; +import './styleguide/styleguide'; diff --git a/public/app/features/panel/all.js b/public/app/features/panel/all.js deleted file mode 100644 index aaa6d0d4ed0..00000000000 --- a/public/app/features/panel/all.js +++ /dev/null @@ -1,9 +0,0 @@ -define([ - './panel_header', - './panel_directive', - './solo_panel_ctrl', - './query_ctrl', - './panel_editor_tab', - './query_editor_row', - './query_troubleshooter', -], function () {}); diff --git a/public/app/features/panel/all.ts b/public/app/features/panel/all.ts new file mode 100644 index 00000000000..bdf1a097352 --- /dev/null +++ b/public/app/features/panel/all.ts @@ -0,0 +1,7 @@ +import './panel_header'; +import './panel_directive'; +import './solo_panel_ctrl'; +import './query_ctrl'; +import './panel_editor_tab'; +import './query_editor_row'; +import './query_troubleshooter'; diff --git a/public/app/features/playlist/all.js b/public/app/features/playlist/all.js deleted file mode 100644 index 3b07b0d74c5..00000000000 --- a/public/app/features/playlist/all.js +++ /dev/null @@ -1,7 +0,0 @@ -define([ - './playlists_ctrl', - './playlist_search', - './playlist_srv', - './playlist_edit_ctrl', - './playlist_routes' -], function () {}); diff --git a/public/app/features/playlist/all.ts b/public/app/features/playlist/all.ts new file mode 100644 index 00000000000..eb427b883ca --- /dev/null +++ b/public/app/features/playlist/all.ts @@ -0,0 +1,5 @@ +import './playlists_ctrl'; +import './playlist_search'; +import './playlist_srv'; +import './playlist_edit_ctrl'; +import './playlist_routes'; From 84dce3282aaac2a5d2b83de9af027619a880936c Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 4 Apr 2018 15:26:23 +0200 Subject: [PATCH 053/145] migrated playlist-routes to ts --- .../app/features/playlist/playlist_routes.js | 39 ------------------- .../app/features/playlist/playlist_routes.ts | 33 ++++++++++++++++ 2 files changed, 33 insertions(+), 39 deletions(-) delete mode 100644 public/app/features/playlist/playlist_routes.js create mode 100644 public/app/features/playlist/playlist_routes.ts diff --git a/public/app/features/playlist/playlist_routes.js b/public/app/features/playlist/playlist_routes.js deleted file mode 100644 index 193b0b52b20..00000000000 --- a/public/app/features/playlist/playlist_routes.js +++ /dev/null @@ -1,39 +0,0 @@ -define([ - 'angular', - 'lodash' -], -function (angular) { - 'use strict'; - - var module = angular.module('grafana.routes'); - - module.config(function($routeProvider) { - $routeProvider - .when('/playlists', { - templateUrl: 'public/app/features/playlist/partials/playlists.html', - controllerAs: 'ctrl', - controller : 'PlaylistsCtrl' - }) - .when('/playlists/create', { - templateUrl: 'public/app/features/playlist/partials/playlist.html', - controllerAs: 'ctrl', - controller : 'PlaylistEditCtrl' - }) - .when('/playlists/edit/:id', { - templateUrl: 'public/app/features/playlist/partials/playlist.html', - controllerAs: 'ctrl', - controller : 'PlaylistEditCtrl' - }) - .when('/playlists/play/:id', { - templateUrl: 'public/app/features/playlist/partials/playlists.html', - controllerAs: 'ctrl', - controller : 'PlaylistsCtrl', - resolve: { - init: function(playlistSrv, $route) { - var playlistId = $route.current.params.id; - playlistSrv.start(playlistId); - } - } - }); - }); -}); diff --git a/public/app/features/playlist/playlist_routes.ts b/public/app/features/playlist/playlist_routes.ts new file mode 100644 index 00000000000..c94446c2c1b --- /dev/null +++ b/public/app/features/playlist/playlist_routes.ts @@ -0,0 +1,33 @@ +import angular from 'angular'; + +function grafanaRoutes($routeProvider) { + $routeProvider + .when('/playlists', { + templateUrl: 'public/app/features/playlist/partials/playlists.html', + controllerAs: 'ctrl', + controller: 'PlaylistsCtrl', + }) + .when('/playlists/create', { + templateUrl: 'public/app/features/playlist/partials/playlist.html', + controllerAs: 'ctrl', + controller: 'PlaylistEditCtrl', + }) + .when('/playlists/edit/:id', { + templateUrl: 'public/app/features/playlist/partials/playlist.html', + controllerAs: 'ctrl', + controller: 'PlaylistEditCtrl', + }) + .when('/playlists/play/:id', { + templateUrl: 'public/app/features/playlist/partials/playlists.html', + controllerAs: 'ctrl', + controller: 'PlaylistsCtrl', + resolve: { + init: function(playlistSrv, $route) { + let playlistId = $route.current.params.id; + playlistSrv.start(playlistId); + }, + }, + }); +} + +angular.module('grafana.routes').config(grafanaRoutes); From 0273365df3dd25656330cd638b56617cd221d060 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 4 Apr 2018 16:20:01 +0200 Subject: [PATCH 054/145] created closeDropdown function, renamed appevent, added second appevent for open timepicker --- public/app/core/services/keybindingSrv.ts | 9 ++------- .../features/dashboard/timepicker/timepicker.ts | 16 +++++++++------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 35cd7808d12..55d968fd981 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -23,13 +23,8 @@ export class KeybindingSrv { this.setupGlobal(); appEvents.on('show-modal', () => (this.modalOpen = true)); - $rootScope.onAppEvent('escTimepicker', () => { - if (!this.timepickerOpen) { - this.timepickerOpen = true; - } else { - this.timepickerOpen = false; - } - }); + $rootScope.onAppEvent('timepickerOpen', () => (this.timepickerOpen = true)); + $rootScope.onAppEvent('timepickerClosed', () => (this.timepickerOpen = false)); } setupGlobal() { diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 32ce07e4468..33cfff92e7f 100644 --- a/public/app/features/dashboard/timepicker/timepicker.ts +++ b/public/app/features/dashboard/timepicker/timepicker.ts @@ -22,7 +22,6 @@ export class TimePickerCtrl { refresh: any; isUtc: boolean; firstDayOfWeek: number; - closeDropdown: any; isOpen: boolean; /** @ngInject */ @@ -96,9 +95,8 @@ export class TimePickerCtrl { } openDropdown() { - this.$rootScope.appEvent('escTimepicker'); if (this.isOpen) { - this.isOpen = false; + this.closeDropdown(); return; } @@ -114,16 +112,21 @@ export class TimePickerCtrl { this.refresh.options.unshift({ text: 'off' }); this.isOpen = true; + this.$rootScope.appEvent('timepickerOpen'); + } + + closeDropdown() { + this.isOpen = false; + this.$rootScope.appEvent('timepickerClosed'); } applyCustom() { - this.$rootScope.appEvent('escTimepicker'); if (this.refresh.value !== this.dashboard.refresh) { this.timeSrv.setAutoRefresh(this.refresh.value); } this.timeSrv.setTime(this.editTimeRaw); - this.isOpen = false; + this.closeDropdown(); } absoluteFromChanged() { @@ -139,7 +142,6 @@ export class TimePickerCtrl { } setRelativeFilter(timespan) { - this.$rootScope.appEvent('escTimepicker'); var range = { from: timespan.from, to: timespan.to }; if (this.panel.nowDelay && range.to === 'now') { @@ -147,7 +149,7 @@ export class TimePickerCtrl { } this.timeSrv.setTime(range); - this.isOpen = false; + this.closeDropdown(); } } From 6b598f34cdab793228c1ad0d94ecc3a9f85718b1 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 19:00:28 +0300 Subject: [PATCH 055/145] scrollbar: fix 'legendScrollbar.destroy is not a function' error --- public/app/plugins/panel/graph/legend.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index 1952f7241e7..c4ff5b31355 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -19,7 +19,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { scope.$on('$destroy', function() { if (legendScrollbar) { - legendScrollbar.destroy(); + legendScrollbar.dispose(); } }); From 4df4249aeadf16247350685eb92ec64f875f56b6 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 19:21:17 +0300 Subject: [PATCH 056/145] scrollbar: fix dashboard width bug --- public/app/core/components/scroll/scroll.ts | 12 ++++++++++-- public/sass/components/_scrollbar.scss | 7 +------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index ad8b4ed3d8e..0eb1c68ab71 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -27,14 +27,17 @@ export function geminiScrollbar() { $(scrollBarHTML).appendTo(scrollRoot); elem.addClass(scrollerClass); - let scrollbar = baron({ + let scrollParams = { root: scrollRoot[0], scroller: scroller[0], bar: '.baron__bar', barOnCls: '_scrollbar', scrollingCls: '_scrolling', track: '.baron__track', - }); + direction: 'v', + }; + + let scrollbar = baron(scrollParams); let lastPos = 0; @@ -57,6 +60,11 @@ export function geminiScrollbar() { scope ); + appEvents.on('toggle-sidemenu', evt => { + // force updating dashboard width + scrollbar.scroll(); + }); + scope.$on('$routeChangeSuccess', () => { lastPos = 0; elem[0].scrollTop = 0; diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 776c85d96b5..7fb8ac6608c 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -196,13 +196,8 @@ } // Fix for side menu on mobile devices -.sidemenu-open.sidemenu-open--xs { - .main-view.baron { - min-width: 0%; - } -} .main-view.baron { - min-width: 99%; + min-width: unset; } .baron__clipper { From 175937a679393080d0f4276aa8a0090a44ce14c6 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 19:26:29 +0300 Subject: [PATCH 057/145] scrollbar: remove perfect-scrollbar and add baron to package list --- package.json | 1 - yarn.lock | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 724bbffb6fa..e3c0db170b6 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,6 @@ "moment": "^2.18.1", "mousetrap": "^1.6.0", "mousetrap-global-bind": "^1.1.0", - "perfect-scrollbar": "^1.2.0", "prop-types": "^15.6.0", "react": "^16.2.0", "react-dom": "^16.2.0", diff --git a/yarn.lock b/yarn.lock index 2de60f03c27..1cc9ccc869c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1162,6 +1162,10 @@ balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" +baron@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/baron/-/baron-3.0.3.tgz#0f0a08a567062882e130a0ecfd41a46d52103f4a" + base64-arraybuffer@0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" @@ -7503,10 +7507,6 @@ pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" -perfect-scrollbar@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.2.0.tgz#ad23a2529c17f4535f21d1486f8bc3046e31a9d2" - performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" From b69316752a4c27b1a9011d0a0e81b36b6cc76d7e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 19:51:12 +0300 Subject: [PATCH 058/145] scrollbar: fix dashboard width updating for different modes --- public/app/core/components/grafana_app.ts | 1 + public/app/core/components/scroll/scroll.ts | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index 798a40cb1bf..1e3f0cb9119 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -167,6 +167,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop if (sidemenuHidden) { sidemenuHidden = false; body.addClass('sidemenu-open'); + appEvents.emit('toggle-inactive-mode'); $timeout(function() { $rootScope.$broadcast('render'); }, 100); diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index 0eb1c68ab71..ebe105a4fee 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -60,10 +60,16 @@ export function geminiScrollbar() { scope ); - appEvents.on('toggle-sidemenu', evt => { - // force updating dashboard width + // force updating dashboard width + appEvents.on('toggle-sidemenu', forceUpdate); + appEvents.on('toggle-sidemenu-hidden', forceUpdate); + appEvents.on('toggle-view-mode', forceUpdate); + appEvents.on('toggle-kiosk-mode', forceUpdate); + appEvents.on('toggle-inactive-mode', forceUpdate); + + function forceUpdate() { scrollbar.scroll(); - }); + } scope.$on('$routeChangeSuccess', () => { lastPos = 0; From 90aab445586b4a5ca00fba4d2f6de72d223d1235 Mon Sep 17 00:00:00 2001 From: Jarno Tuovinen Date: Wed, 4 Apr 2018 21:07:22 +0300 Subject: [PATCH 059/145] Use curly brackets around hyperlink help text #11478 (#11479) --- public/app/plugins/panel/table/column_options.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/panel/table/column_options.html b/public/app/plugins/panel/table/column_options.html index 9e8e4b404ae..4a4a6d0db9c 100644 --- a/public/app/plugins/panel/table/column_options.html +++ b/public/app/plugins/panel/table/column_options.html @@ -163,10 +163,10 @@ Use special variables to specify cell values:
- $__cell refers to current cell value + ${__cell} refers to current cell value
- $__cell_n refers to Nth column value in current row. Column indexes are started from 0. For instance, - $__cell_1 refers to second column's value. + ${__cell_n} refers to Nth column value in current row. Column indexes are started from 0. For instance, + ${__cell_1} refers to second column's value.
From b4ef55f5d027014a43404658581409b4c8d82f63 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 5 Apr 2018 11:51:32 +0300 Subject: [PATCH 060/145] scrollbar: fix potential memory leaks in event handlers --- public/app/core/components/scroll/scroll.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index ebe105a4fee..12321bc4f5c 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -61,11 +61,11 @@ export function geminiScrollbar() { ); // force updating dashboard width - appEvents.on('toggle-sidemenu', forceUpdate); - appEvents.on('toggle-sidemenu-hidden', forceUpdate); - appEvents.on('toggle-view-mode', forceUpdate); - appEvents.on('toggle-kiosk-mode', forceUpdate); - appEvents.on('toggle-inactive-mode', forceUpdate); + appEvents.on('toggle-sidemenu', forceUpdate, scope); + appEvents.on('toggle-sidemenu-hidden', forceUpdate, scope); + appEvents.on('toggle-view-mode', forceUpdate, scope); + appEvents.on('toggle-kiosk-mode', forceUpdate, scope); + appEvents.on('toggle-inactive-mode', forceUpdate, scope); function forceUpdate() { scrollbar.scroll(); From b321a21cb569d1ac3c0dc88c53393aea2e48d2d5 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 5 Apr 2018 11:00:15 +0200 Subject: [PATCH 061/145] removed indent for manage dashboards --- public/sass/components/_search.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index e6b3795d752..ab63f96be50 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -206,7 +206,7 @@ padding: 5px; flex: 0 0 auto; font-size: 19px; - padding: 5px 2px 5px 16px; + padding: 5px 2px 5px 10px; } .search-item__tags { From 7083e8a0a921cb8ab2013f733351cbc796010b71 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 5 Apr 2018 14:09:32 +0200 Subject: [PATCH 062/145] migrated segment_srv to ts --- public/app/core/services/segment_srv.js | 111 ------------------------ public/app/core/services/segment_srv.ts | 111 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 111 deletions(-) delete mode 100644 public/app/core/services/segment_srv.js create mode 100644 public/app/core/services/segment_srv.ts diff --git a/public/app/core/services/segment_srv.js b/public/app/core/services/segment_srv.js deleted file mode 100644 index 71d0cbfe7a9..00000000000 --- a/public/app/core/services/segment_srv.js +++ /dev/null @@ -1,111 +0,0 @@ -define([ - 'angular', - 'lodash', - '../core_module', -], -function (angular, _, coreModule) { - 'use strict'; - - coreModule.default.service('uiSegmentSrv', function($sce, templateSrv) { - var self = this; - - function MetricSegment(options) { - if (options === '*' || options.value === '*') { - this.value = '*'; - this.html = $sce.trustAsHtml(''); - this.type = options.type; - this.expandable = true; - return; - } - - if (_.isString(options)) { - this.value = options; - this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); - return; - } - - // temp hack to work around legacy inconsistency in segment model - this.text = options.value; - - this.cssClass = options.cssClass; - this.custom = options.custom; - this.type = options.type; - this.fake = options.fake; - this.value = options.value; - this.selectMode = options.selectMode; - this.type = options.type; - this.expandable = options.expandable; - this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); - } - - this.getSegmentForValue = function(value, fallbackText) { - if (value) { - return this.newSegment(value); - } else { - return this.newSegment({value: fallbackText, fake: true}); - } - }; - - this.newSelectMeasurement = function() { - return new MetricSegment({value: 'select measurement', fake: true}); - }; - - this.newFake = function(text, type, cssClass) { - return new MetricSegment({value: text, fake: true, type: type, cssClass: cssClass}); - }; - - this.newSegment = function(options) { - return new MetricSegment(options); - }; - - this.newKey = function(key) { - return new MetricSegment({value: key, type: 'key', cssClass: 'query-segment-key' }); - }; - - this.newKeyValue = function(value) { - return new MetricSegment({value: value, type: 'value', cssClass: 'query-segment-value' }); - }; - - this.newCondition = function(condition) { - return new MetricSegment({value: condition, type: 'condition', cssClass: 'query-keyword' }); - }; - - this.newOperator = function(op) { - return new MetricSegment({value: op, type: 'operator', cssClass: 'query-segment-operator' }); - }; - - this.newOperators = function(ops) { - return _.map(ops, function(op) { - return new MetricSegment({value: op, type: 'operator', cssClass: 'query-segment-operator' }); - }); - }; - - this.transformToSegments = function(addTemplateVars, variableTypeFilter) { - return function(results) { - var segments = _.map(results, function(segment) { - return self.newSegment({value: segment.text, expandable: segment.expandable}); - }); - - if (addTemplateVars) { - _.each(templateSrv.variables, function(variable) { - if (variableTypeFilter === void 0 || variableTypeFilter === variable.type) { - segments.unshift(self.newSegment({ type: 'value', value: '$' + variable.name, expandable: true })); - } - }); - } - - return segments; - }; - }; - - this.newSelectMetric = function() { - return new MetricSegment({value: 'select metric', fake: true}); - }; - - this.newPlusButton = function() { - return new MetricSegment({fake: true, html: '', type: 'plus-button', cssClass: 'query-part' }); - }; - - }); - -}); diff --git a/public/app/core/services/segment_srv.ts b/public/app/core/services/segment_srv.ts new file mode 100644 index 00000000000..042340e6102 --- /dev/null +++ b/public/app/core/services/segment_srv.ts @@ -0,0 +1,111 @@ +import _ from 'lodash'; +import coreModule from '../core_module'; + +/** @ngInject */ +export function uiSegmentSrv($sce, templateSrv) { + let self = this; + + function MetricSegment(options) { + if (options === '*' || options.value === '*') { + this.value = '*'; + this.html = $sce.trustAsHtml(''); + this.type = options.type; + this.expandable = true; + return; + } + + if (_.isString(options)) { + this.value = options; + this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); + return; + } + + // temp hack to work around legacy inconsistency in segment model + this.text = options.value; + + this.cssClass = options.cssClass; + this.custom = options.custom; + this.type = options.type; + this.fake = options.fake; + this.value = options.value; + this.selectMode = options.selectMode; + this.type = options.type; + this.expandable = options.expandable; + this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); + } + + this.getSegmentForValue = function(value, fallbackText) { + if (value) { + return this.newSegment(value); + } else { + return this.newSegment({ value: fallbackText, fake: true }); + } + }; + + this.newSelectMeasurement = function() { + return new MetricSegment({ value: 'select measurement', fake: true }); + }; + + this.newFake = function(text, type, cssClass) { + return new MetricSegment({ value: text, fake: true, type: type, cssClass: cssClass }); + }; + + this.newSegment = function(options) { + return new MetricSegment(options); + }; + + this.newKey = function(key) { + return new MetricSegment({ value: key, type: 'key', cssClass: 'query-segment-key' }); + }; + + this.newKeyValue = function(value) { + return new MetricSegment({ value: value, type: 'value', cssClass: 'query-segment-value' }); + }; + + this.newCondition = function(condition) { + return new MetricSegment({ value: condition, type: 'condition', cssClass: 'query-keyword' }); + }; + + this.newOperator = function(op) { + return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' }); + }; + + this.newOperators = function(ops) { + return _.map(ops, function(op) { + return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' }); + }); + }; + + this.transformToSegments = function(addTemplateVars, variableTypeFilter) { + return function(results) { + let segments = _.map(results, function(segment) { + return self.newSegment({ value: segment.text, expandable: segment.expandable }); + }); + + if (addTemplateVars) { + _.each(templateSrv.variables, function(variable) { + if (variableTypeFilter === void 0 || variableTypeFilter === variable.type) { + segments.unshift(self.newSegment({ type: 'value', value: '$' + variable.name, expandable: true })); + } + }); + } + + return segments; + }; + }; + + this.newSelectMetric = function() { + return new MetricSegment({ value: 'select metric', fake: true }); + }; + + this.newPlusButton = function() { + return new MetricSegment({ + fake: true, + html: '', + type: 'plus-button', + cssClass: 'query-part', + }); + }; +} + +coreModule.service('uiSegmentSrv', uiSegmentSrv); From 714d555e366f0dd752a8b1630b99be7a4187ef5b Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 5 Apr 2018 14:33:27 +0200 Subject: [PATCH 063/145] migrated dash_class to ts --- public/app/core/directives/dash_class.js | 36 ------------------------ public/app/core/directives/dash_class.ts | 31 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 36 deletions(-) delete mode 100644 public/app/core/directives/dash_class.js create mode 100644 public/app/core/directives/dash_class.ts diff --git a/public/app/core/directives/dash_class.js b/public/app/core/directives/dash_class.js deleted file mode 100644 index 4a139272632..00000000000 --- a/public/app/core/directives/dash_class.js +++ /dev/null @@ -1,36 +0,0 @@ -define([ - 'lodash', - 'jquery', - '../core_module', -], -function (_, $, coreModule) { - 'use strict'; - - coreModule.default.directive('dashClass', function() { - return { - link: function($scope, elem) { - - $scope.onAppEvent('panel-fullscreen-enter', function() { - elem.toggleClass('panel-in-fullscreen', true); - }); - - $scope.onAppEvent('panel-fullscreen-exit', function() { - elem.toggleClass('panel-in-fullscreen', false); - }); - - $scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) { - if (newValue) { - elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue)); - setTimeout(function() { - elem.toggleClass('dashboard-page--settings-open', _.isString(newValue)); - }, 10); - } else { - elem.removeClass('dashboard-page--settings-opening'); - elem.removeClass('dashboard-page--settings-open'); - } - }); - } - }; - }); - -}); diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts new file mode 100644 index 00000000000..031338d3c5b --- /dev/null +++ b/public/app/core/directives/dash_class.ts @@ -0,0 +1,31 @@ +import _ from 'lodash'; +import coreModule from '../core_module'; + +/** @ngInject */ +export function dashClass() { + return { + link: function($scope, elem) { + $scope.onAppEvent('panel-fullscreen-enter', function() { + elem.toggleClass('panel-in-fullscreen', true); + }); + + $scope.onAppEvent('panel-fullscreen-exit', function() { + elem.toggleClass('panel-in-fullscreen', false); + }); + + $scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) { + if (newValue) { + elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue)); + setTimeout(function() { + elem.toggleClass('dashboard-page--settings-open', _.isString(newValue)); + }, 10); + } else { + elem.removeClass('dashboard-page--settings-opening'); + elem.removeClass('dashboard-page--settings-open'); + } + }); + }, + }; +} + +coreModule.directive('dashClass', dashClass); From fcfc33ee5795c5cd745248450212086394c7999e Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 5 Apr 2018 15:02:54 +0200 Subject: [PATCH 064/145] changelog: adds note for #11165 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d221480de2..2f763a15c82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * **Prometheus**: Support POST for query and query_range [#9859](https://github.com/grafana/grafana/pull/9859), thx [@mtanda](https://github.com/mtanda) * **Alerting**: Add support for retries on alert queries [#5855](https://github.com/grafana/grafana/issues/5855), thx [@Thib17](https://github.com/Thib17) * **Table**: Table plugin value mappings [#7119](https://github.com/grafana/grafana/issues/7119), thx [infernix](https://github.com/infernix) +* **IE11**: IE 11 compatibility [#11165](https://github.com/grafana/grafana/issues/11165) ### Minor * **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes) From fa43782299a6fde669d9ecd186eb1aa094db0067 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 5 Apr 2018 15:56:07 +0300 Subject: [PATCH 065/145] scrollbar: fix graph legend height --- public/app/plugins/panel/graph/legend.ts | 8 ++++---- public/app/plugins/panel/graph/template.ts | 4 +++- public/sass/components/_panel_graph.scss | 8 +++++++- public/sass/components/_scrollbar.scss | 4 ++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index c4ff5b31355..d3b548859de 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -238,10 +238,10 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { tbodyElem.append(tableHeaderElem); tbodyElem.append(seriesElements); elem.append(tbodyElem); - tbodyElem.wrap('
'); + tbodyElem.wrap('
'); } else { - elem.append('
'); - elem.find('.graph-legend-content').append(seriesElements); + elem.append('
'); + elem.find('.graph-legend-scroll').append(seriesElements); } if (!panel.legend.rightSide || (panel.legend.rightSide && legendWidth !== legendRightDefaultWidth)) { @@ -261,7 +261,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { `; let scrollRoot = elem; - let scroller = elem.find('.graph-legend-content'); + let scroller = elem.find('.graph-legend-scroll'); // clear existing scroll bar track to prevent duplication scrollRoot.find('.baron__track').remove(); diff --git a/public/app/plugins/panel/graph/template.ts b/public/app/plugins/panel/graph/template.ts index 0b9eb8227df..c897327fe1a 100644 --- a/public/app/plugins/panel/graph/template.ts +++ b/public/app/plugins/panel/graph/template.ts @@ -3,7 +3,9 @@ var template = `
-
+
+
+
`; diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index 83747d6caaf..cc5c601e0ea 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -49,6 +49,7 @@ } .graph-legend { + display: flex; flex: 0 1 auto; max-height: 30%; margin: 0; @@ -56,7 +57,8 @@ padding-top: 6px; position: relative; - height: 100%; + // fix for Firefox (white stripe on the right of scrollbar) + width: 99%; .popover-content { padding: 0; @@ -67,6 +69,10 @@ position: relative; } +.graph-legend-scroll { + position: relative; +} + .graph-legend-icon { position: relative; padding-right: 4px; diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 7fb8ac6608c..93b81fc1b4b 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -192,12 +192,12 @@ // Width needs to be set to prevent content width issues // Set to 99% instead of 100% for fixing Firefox issue (white stripe on the right of scrollbar) - min-width: 99%; + width: 99%; } // Fix for side menu on mobile devices .main-view.baron { - min-width: unset; + width: unset; } .baron__clipper { From 9f07ae72ee4bebc41ec06bc85793529bf045aa66 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 5 Apr 2018 16:47:20 +0300 Subject: [PATCH 066/145] scrollbar: fix search scroller in mobile view --- public/sass/components/_search.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index f54ac4b3d5c..8338a5d72ae 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -103,18 +103,20 @@ } .search-results-scroller { + display: flex; position: relative; - height: 100%; } .search-results-container { - height: 100%; display: block; padding: $spacer; position: relative; flex-grow: 10; margin-bottom: 1rem; + // Fix for search scroller in mobile view + height: unset; + .label-tag { margin-left: 6px; font-size: 11px; From 35bc4e4632a1195b59e8b52cf0e969a08f24e470 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 5 Apr 2018 17:10:32 +0200 Subject: [PATCH 067/145] migrated metric_segment to ts --- public/app/core/directives/metric_segment.js | 246 ----------------- public/app/core/directives/metric_segment.ts | 261 +++++++++++++++++++ 2 files changed, 261 insertions(+), 246 deletions(-) delete mode 100644 public/app/core/directives/metric_segment.js create mode 100644 public/app/core/directives/metric_segment.ts diff --git a/public/app/core/directives/metric_segment.js b/public/app/core/directives/metric_segment.js deleted file mode 100644 index 7ba4a5a5259..00000000000 --- a/public/app/core/directives/metric_segment.js +++ /dev/null @@ -1,246 +0,0 @@ -define([ - 'lodash', - 'jquery', - '../core_module', -], -function (_, $, coreModule) { - 'use strict'; - - coreModule.default.directive('metricSegment', function($compile, $sce) { - var inputTemplate = ''; - - var linkTemplate = ''; - - var selectTemplate = ''; - - return { - scope: { - segment: "=", - getOptions: "&", - onChange: "&", - debounce: "@", - }, - link: function($scope, elem) { - var $input = $(inputTemplate); - var segment = $scope.segment; - var $button = $(segment.selectMode ? selectTemplate : linkTemplate); - var options = null; - var cancelBlur = null; - var linkMode = true; - var debounceLookup = $scope.debounce; - - $input.appendTo(elem); - $button.appendTo(elem); - - $scope.updateVariableValue = function(value) { - if (value === '' || segment.value === value) { - return; - } - - value = _.unescape(value); - - $scope.$apply(function() { - var selected = _.find($scope.altSegments, {value: value}); - if (selected) { - segment.value = selected.value; - segment.html = selected.html || selected.value; - segment.fake = false; - segment.expandable = selected.expandable; - - if (selected.type) { - segment.type = selected.type; - } - } - else if (segment.custom !== 'false') { - segment.value = value; - segment.html = $sce.trustAsHtml(value); - segment.expandable = true; - segment.fake = false; - } - - $scope.onChange(); - }); - }; - - $scope.switchToLink = function(fromClick) { - if (linkMode && !fromClick) { return; } - - clearTimeout(cancelBlur); - cancelBlur = null; - linkMode = true; - $input.hide(); - $button.show(); - $scope.updateVariableValue($input.val()); - }; - - $scope.inputBlur = function() { - // happens long before the click event on the typeahead options - // need to have long delay because the blur - cancelBlur = setTimeout($scope.switchToLink, 200); - }; - - $scope.source = function(query, callback) { - $scope.$apply(function() { - $scope.getOptions({ $query: query }).then(function(altSegments) { - $scope.altSegments = altSegments; - options = _.map($scope.altSegments, function(alt) { - return _.escape(alt.value); - }); - - // add custom values - if (segment.custom !== 'false') { - if (!segment.fake && _.indexOf(options, segment.value) === -1) { - options.unshift(segment.value); - } - } - - callback(options); - }); - }); - }; - - $scope.updater = function(value) { - if (value === segment.value) { - clearTimeout(cancelBlur); - $input.focus(); - return value; - } - - $input.val(value); - $scope.switchToLink(true); - - return value; - }; - - $scope.matcher = function(item) { - var str = this.query; - if (str[0] === '/') { str = str.substring(1); } - if (str[str.length - 1] === '/') { str = str.substring(0, str.length-1); } - try { - return item.toLowerCase().match(str.toLowerCase()); - } catch(e) { - return false; - } - }; - - $input.attr('data-provide', 'typeahead'); - $input.typeahead({ source: $scope.source, minLength: 0, items: 10000, updater: $scope.updater, matcher: $scope.matcher }); - - var typeahead = $input.data('typeahead'); - typeahead.lookup = function () { - this.query = this.$element.val() || ''; - var items = this.source(this.query, $.proxy(this.process, this)); - return items ? this.process(items) : items; - }; - - if (debounceLookup) { - typeahead.lookup = _.debounce(typeahead.lookup, 500, {leading: true}); - } - - $button.keydown(function(evt) { - // trigger typeahead on down arrow or enter key - if (evt.keyCode === 40 || evt.keyCode === 13) { - $button.click(); - } - }); - - $button.click(function() { - options = null; - $input.css('width', (Math.max($button.width(), 80) + 16) + 'px'); - - $button.hide(); - $input.show(); - $input.focus(); - - linkMode = false; - - var typeahead = $input.data('typeahead'); - if (typeahead) { - $input.val(''); - typeahead.lookup(); - } - }); - - $input.blur($scope.inputBlur); - - $compile(elem.contents())($scope); - } - }; - }); - - coreModule.default.directive('metricSegmentModel', function(uiSegmentSrv, $q) { - return { - template: '', - restrict: 'E', - scope: { - property: "=", - options: "=", - getOptions: "&", - onChange: "&", - }, - link: { - pre: function postLink($scope, elem, attrs) { - var cachedOptions; - - $scope.valueToSegment = function(value) { - var option = _.find($scope.options, {value: value}); - var segment = { - cssClass: attrs.cssClass, - custom: attrs.custom, - value: option ? option.text : value, - selectMode: attrs.selectMode, - }; - - return uiSegmentSrv.newSegment(segment); - }; - - $scope.getOptionsInternal = function() { - if ($scope.options) { - cachedOptions = $scope.options; - return $q.when(_.map($scope.options, function(option) { - return {value: option.text}; - })); - } else { - return $scope.getOptions().then(function(options) { - cachedOptions = options; - return _.map(options, function(option) { - if (option.html) { - return option; - } - return {value: option.text}; - }); - }); - } - }; - - $scope.onSegmentChange = function() { - if (cachedOptions) { - var option = _.find(cachedOptions, {text: $scope.segment.value}); - if (option && option.value !== $scope.property) { - $scope.property = option.value; - } else if (attrs.custom !== 'false') { - $scope.property = $scope.segment.value; - } - } else { - $scope.property = $scope.segment.value; - } - - // needs to call this after digest so - // property is synced with outerscope - $scope.$$postDigest(function() { - $scope.$apply(function() { - $scope.onChange(); - }); - }); - }; - - $scope.segment = $scope.valueToSegment($scope.property); - } - } - }; - }); -}); diff --git a/public/app/core/directives/metric_segment.ts b/public/app/core/directives/metric_segment.ts new file mode 100644 index 00000000000..d1054b23793 --- /dev/null +++ b/public/app/core/directives/metric_segment.ts @@ -0,0 +1,261 @@ +import _ from 'lodash'; +import $ from 'jquery'; +import coreModule from '../core_module'; + +export function metricSegment($compile, $sce) { + let inputTemplate = + ''; + + let linkTemplate = + ''; + + let selectTemplate = + ''; + + return { + scope: { + segment: '=', + getOptions: '&', + onChange: '&', + debounce: '@', + }, + link: function($scope, elem) { + let $input = $(inputTemplate); + let segment = $scope.segment; + let $button = $(segment.selectMode ? selectTemplate : linkTemplate); + let options = null; + let cancelBlur = null; + let linkMode = true; + let debounceLookup = $scope.debounce; + + $input.appendTo(elem); + $button.appendTo(elem); + + $scope.updateVariableValue = function(value) { + if (value === '' || segment.value === value) { + return; + } + + value = _.unescape(value); + + $scope.$apply(function() { + let selected = _.find($scope.altSegments, { value: value }); + if (selected) { + segment.value = selected.value; + segment.html = selected.html || selected.value; + segment.fake = false; + segment.expandable = selected.expandable; + + if (selected.type) { + segment.type = selected.type; + } + } else if (segment.custom !== 'false') { + segment.value = value; + segment.html = $sce.trustAsHtml(value); + segment.expandable = true; + segment.fake = false; + } + + $scope.onChange(); + }); + }; + + $scope.switchToLink = function(fromClick) { + if (linkMode && !fromClick) { + return; + } + + clearTimeout(cancelBlur); + cancelBlur = null; + linkMode = true; + $input.hide(); + $button.show(); + $scope.updateVariableValue($input.val()); + }; + + $scope.inputBlur = function() { + // happens long before the click event on the typeahead options + // need to have long delay because the blur + cancelBlur = setTimeout($scope.switchToLink, 200); + }; + + $scope.source = function(query, callback) { + $scope.$apply(function() { + $scope.getOptions({ $query: query }).then(function(altSegments) { + $scope.altSegments = altSegments; + options = _.map($scope.altSegments, function(alt) { + return _.escape(alt.value); + }); + + // add custom values + if (segment.custom !== 'false') { + if (!segment.fake && _.indexOf(options, segment.value) === -1) { + options.unshift(segment.value); + } + } + + callback(options); + }); + }); + }; + + $scope.updater = function(value) { + if (value === segment.value) { + clearTimeout(cancelBlur); + $input.focus(); + return value; + } + + $input.val(value); + $scope.switchToLink(true); + + return value; + }; + + $scope.matcher = function(item) { + let str = this.query; + if (str[0] === '/') { + str = str.substring(1); + } + if (str[str.length - 1] === '/') { + str = str.substring(0, str.length - 1); + } + try { + return item.toLowerCase().match(str.toLowerCase()); + } catch (e) { + return false; + } + }; + + $input.attr('data-provide', 'typeahead'); + $input.typeahead({ + source: $scope.source, + minLength: 0, + items: 10000, + updater: $scope.updater, + matcher: $scope.matcher, + }); + + let typeahead = $input.data('typeahead'); + typeahead.lookup = function() { + this.query = this.$element.val() || ''; + let items = this.source(this.query, $.proxy(this.process, this)); + return items ? this.process(items) : items; + }; + + if (debounceLookup) { + typeahead.lookup = _.debounce(typeahead.lookup, 500, { leading: true }); + } + + $button.keydown(function(evt) { + // trigger typeahead on down arrow or enter key + if (evt.keyCode === 40 || evt.keyCode === 13) { + $button.click(); + } + }); + + $button.click(function() { + options = null; + $input.css('width', Math.max($button.width(), 80) + 16 + 'px'); + + $button.hide(); + $input.show(); + $input.focus(); + + linkMode = false; + + let typeahead = $input.data('typeahead'); + if (typeahead) { + $input.val(''); + typeahead.lookup(); + } + }); + + $input.blur($scope.inputBlur); + + $compile(elem.contents())($scope); + }, + }; +} + +export function metricSegmentModel(uiSegmentSrv, $q) { + return { + template: + '', + restrict: 'E', + scope: { + property: '=', + options: '=', + getOptions: '&', + onChange: '&', + }, + link: { + pre: function postLink($scope, elem, attrs) { + let cachedOptions; + + $scope.valueToSegment = function(value) { + let option = _.find($scope.options, { value: value }); + let segment = { + cssClass: attrs.cssClass, + custom: attrs.custom, + value: option ? option.text : value, + selectMode: attrs.selectMode, + }; + + return uiSegmentSrv.newSegment(segment); + }; + + $scope.getOptionsInternal = function() { + if ($scope.options) { + cachedOptions = $scope.options; + return $q.when( + _.map($scope.options, function(option) { + return { value: option.text }; + }) + ); + } else { + return $scope.getOptions().then(function(options) { + cachedOptions = options; + return _.map(options, function(option) { + if (option.html) { + return option; + } + return { value: option.text }; + }); + }); + } + }; + + $scope.onSegmentChange = function() { + if (cachedOptions) { + let option = _.find(cachedOptions, { text: $scope.segment.value }); + if (option && option.value !== $scope.property) { + $scope.property = option.value; + } else if (attrs.custom !== 'false') { + $scope.property = $scope.segment.value; + } + } else { + $scope.property = $scope.segment.value; + } + + // needs to call this after digest so + // property is synced with outerscope + $scope.$$postDigest(function() { + $scope.$apply(function() { + $scope.onChange(); + }); + }); + }; + + $scope.segment = $scope.valueToSegment($scope.property); + }, + }, + }; +} + +coreModule.directive('metricSegment', metricSegment); +coreModule.directive('metricSegmentModel', metricSegmentModel); From 6d6ecbd458a841d226bf308990b47affcca474cd Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Fri, 6 Apr 2018 10:54:06 +0200 Subject: [PATCH 068/145] fixed sidemenu icon issue created by earlier pr --- public/sass/base/_icons.scss | 6 ++---- public/sass/components/_sidemenu.scss | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/sass/base/_icons.scss b/public/sass/base/_icons.scss index 31e5ee62d6f..c701cc1249e 100644 --- a/public/sass/base/_icons.scss +++ b/public/sass/base/_icons.scss @@ -1,10 +1,8 @@ .gicon { line-height: 1; display: inline-block; - //width: 1.1057142857em; - //height: 1.1057142857em; - height: 22px; - width: 22px; + width: 1.1057142857em; + height: 1.1057142857em; text-align: center; background-repeat: no-repeat; background-position: center; diff --git a/public/sass/components/_sidemenu.scss b/public/sass/components/_sidemenu.scss index e48ab0597a2..d1372484074 100644 --- a/public/sass/components/_sidemenu.scss +++ b/public/sass/components/_sidemenu.scss @@ -123,6 +123,8 @@ position: relative; opacity: 0.7; font-size: 130%; + height: 22px; + width: 22px; } .fa { From c7cd754a94781076d2b481bd825d8aae83826d64 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Fri, 6 Apr 2018 15:02:32 +0200 Subject: [PATCH 069/145] migrating to ts --- .../core/directives/value_select_dropdown.js | 283 ---------------- .../core/directives/value_select_dropdown.ts | 303 ++++++++++++++++++ 2 files changed, 303 insertions(+), 283 deletions(-) delete mode 100644 public/app/core/directives/value_select_dropdown.js create mode 100644 public/app/core/directives/value_select_dropdown.ts diff --git a/public/app/core/directives/value_select_dropdown.js b/public/app/core/directives/value_select_dropdown.js deleted file mode 100644 index a2bae1c34d3..00000000000 --- a/public/app/core/directives/value_select_dropdown.js +++ /dev/null @@ -1,283 +0,0 @@ -define([ - 'angular', - 'lodash', - '../core_module', -], -function (angular, _, coreModule) { - 'use strict'; - - coreModule.default.controller('ValueSelectDropdownCtrl', function($q) { - var vm = this; - - vm.show = function() { - vm.oldVariableText = vm.variable.current.text; - vm.highlightIndex = -1; - - vm.options = vm.variable.options; - vm.selectedValues = _.filter(vm.options, {selected: true}); - - vm.tags = _.map(vm.variable.tags, function(value) { - var tag = { text: value, selected: false }; - _.each(vm.variable.current.tags, function(tagObj) { - if (tagObj.text === value) { - tag = tagObj; - } - }); - return tag; - }); - - vm.search = { - query: '', - options: vm.options.slice(0, Math.min(vm.options.length, 1000)) - }; - - vm.dropdownVisible = true; - }; - - vm.updateLinkText = function() { - var current = vm.variable.current; - - if (current.tags && current.tags.length) { - // filer out values that are in selected tags - var selectedAndNotInTag = _.filter(vm.variable.options, function(option) { - if (!option.selected) { return false; } - for (var i = 0; i < current.tags.length; i++) { - var tag = current.tags[i]; - if (_.indexOf(tag.values, option.value) !== -1) { - return false; - } - } - return true; - }); - - // convert values to text - var currentTexts = _.map(selectedAndNotInTag, 'text'); - - // join texts - vm.linkText = currentTexts.join(' + '); - if (vm.linkText.length > 0) { - vm.linkText += ' + '; - } - } else { - vm.linkText = vm.variable.current.text; - } - }; - - vm.clearSelections = function() { - _.each(vm.options, function(option) { - option.selected = false; - }); - - vm.selectionsChanged(false); - }; - - vm.selectTag = function(tag) { - tag.selected = !tag.selected; - var tagValuesPromise; - if (!tag.values) { - tagValuesPromise = vm.variable.getValuesForTag(tag.text); - } else { - tagValuesPromise = $q.when(tag.values); - } - - tagValuesPromise.then(function(values) { - tag.values = values; - tag.valuesText = values.join(' + '); - _.each(vm.options, function(option) { - if (_.indexOf(tag.values, option.value) !== -1) { - option.selected = tag.selected; - } - }); - - vm.selectionsChanged(false); - }); - }; - - vm.keyDown = function (evt) { - if (evt.keyCode === 27) { - vm.hide(); - } - if (evt.keyCode === 40) { - vm.moveHighlight(1); - } - if (evt.keyCode === 38) { - vm.moveHighlight(-1); - } - if (evt.keyCode === 13) { - if (vm.search.options.length === 0) { - vm.commitChanges(); - } else { - vm.selectValue(vm.search.options[vm.highlightIndex], {}, true, false); - } - } - if (evt.keyCode === 32) { - vm.selectValue(vm.search.options[vm.highlightIndex], {}, false, false); - } - }; - - vm.moveHighlight = function(direction) { - vm.highlightIndex = (vm.highlightIndex + direction) % vm.search.options.length; - }; - - vm.selectValue = function(option, event, commitChange, excludeOthers) { - if (!option) { return; } - - option.selected = vm.variable.multi ? !option.selected: true; - - commitChange = commitChange || false; - excludeOthers = excludeOthers || false; - - var setAllExceptCurrentTo = function(newValue) { - _.each(vm.options, function(other) { - if (option !== other) { other.selected = newValue; } - }); - }; - - // commit action (enter key), should not deselect it - if (commitChange) { - option.selected = true; - } - - if (option.text === 'All' || excludeOthers) { - setAllExceptCurrentTo(false); - commitChange = true; - } - else if (!vm.variable.multi) { - setAllExceptCurrentTo(false); - commitChange = true; - } else if (event.ctrlKey || event.metaKey || event.shiftKey) { - commitChange = true; - setAllExceptCurrentTo(false); - } - - vm.selectionsChanged(commitChange); - }; - - vm.selectionsChanged = function(commitChange) { - vm.selectedValues = _.filter(vm.options, {selected: true}); - - if (vm.selectedValues.length > 1) { - if (vm.selectedValues[0].text === 'All') { - vm.selectedValues[0].selected = false; - vm.selectedValues = vm.selectedValues.slice(1, vm.selectedValues.length); - } - } - - // validate selected tags - _.each(vm.tags, function(tag) { - if (tag.selected) { - _.each(tag.values, function(value) { - if (!_.find(vm.selectedValues, {value: value})) { - tag.selected = false; - } - }); - } - }); - - vm.selectedTags = _.filter(vm.tags, {selected: true}); - vm.variable.current.value = _.map(vm.selectedValues, 'value'); - vm.variable.current.text = _.map(vm.selectedValues, 'text').join(' + '); - vm.variable.current.tags = vm.selectedTags; - - if (!vm.variable.multi) { - vm.variable.current.value = vm.selectedValues[0].value; - } - - if (commitChange) { - vm.commitChanges(); - } - }; - - vm.commitChanges = function() { - // if we have a search query and no options use that - if (vm.search.options.length === 0 && vm.search.query.length > 0) { - vm.variable.current = {text: vm.search.query, value: vm.search.query}; - } - else if (vm.selectedValues.length === 0) { - // make sure one option is selected - vm.options[0].selected = true; - vm.selectionsChanged(false); - } - - vm.dropdownVisible = false; - vm.updateLinkText(); - - if (vm.variable.current.text !== vm.oldVariableText) { - vm.onUpdated(); - } - }; - - vm.queryChanged = function() { - vm.highlightIndex = -1; - vm.search.options = _.filter(vm.options, function(option) { - return option.text.toLowerCase().indexOf(vm.search.query.toLowerCase()) !== -1; - }); - - vm.search.options = vm.search.options.slice(0, Math.min(vm.search.options.length, 1000)); - }; - - vm.init = function() { - vm.selectedTags = vm.variable.current.tags || []; - vm.updateLinkText(); - }; - - }); - - coreModule.default.directive('valueSelectDropdown', function($compile, $window, $timeout, $rootScope) { - return { - scope: { variable: "=", onUpdated: "&"}, - templateUrl: 'public/app/partials/valueSelectDropdown.html', - controller: 'ValueSelectDropdownCtrl', - controllerAs: 'vm', - bindToController: true, - link: function(scope, elem) { - var bodyEl = angular.element($window.document.body); - var linkEl = elem.find('.variable-value-link'); - var inputEl = elem.find('input'); - - function openDropdown() { - inputEl.css('width', Math.max(linkEl.width(), 80) + 'px'); - - inputEl.show(); - linkEl.hide(); - - inputEl.focus(); - $timeout(function() { bodyEl.on('click', bodyOnClick); }, 0, false); - } - - function switchToLink() { - inputEl.hide(); - linkEl.show(); - bodyEl.off('click', bodyOnClick); - } - - function bodyOnClick (e) { - if (elem.has(e.target).length === 0) { - scope.$apply(function() { - scope.vm.commitChanges(); - }); - } - } - - scope.$watch('vm.dropdownVisible', function(newValue) { - if (newValue) { - openDropdown(); - } else { - switchToLink(); - } - }); - - var cleanUp = $rootScope.$on('template-variable-value-updated', function() { - scope.vm.updateLinkText(); - }); - - scope.$on("$destroy", function() { - cleanUp(); - }); - - scope.vm.init(); - }, - }; - }); - -}); diff --git a/public/app/core/directives/value_select_dropdown.ts b/public/app/core/directives/value_select_dropdown.ts new file mode 100644 index 00000000000..628b1480a4f --- /dev/null +++ b/public/app/core/directives/value_select_dropdown.ts @@ -0,0 +1,303 @@ +import angular from 'angular'; +import _ from 'lodash'; +import coreModule from '../core_module'; + +export class ValueSelectDropdownCtrl { + dropdownVisible: any; + highlightIndex: any; + linkText: any; + oldVariableText: any; + options: any; + search: any; + selectedTags: any; + selectedValues: any; + tags: any; + variable: any; + + hide: any; + onUpdated: any; + + constructor(private $q) {} + + show() { + this.oldVariableText = this.variable.current.text; + this.highlightIndex = -1; + + this.options = this.variable.options; + this.selectedValues = _.filter(this.options, { selected: true }); + + this.tags = _.map(this.variable.tags, function(value) { + let tag = { text: value, selected: false }; + _.each(this.variable.current.tags, function(tagObj) { + if (tagObj.text === value) { + tag = tagObj; + } + }); + return tag; + }); + + this.search = { + query: '', + options: this.options.slice(0, Math.min(this.options.length, 1000)), + }; + + this.dropdownVisible = true; + } + + updateLinkText() { + let current = this.variable.current; + + if (current.tags && current.tags.length) { + // filer out values that are in selected tags + let selectedAndNotInTag = _.filter(this.variable.options, function(option) { + if (!option.selected) { + return false; + } + for (let i = 0; i < current.tags.length; i++) { + let tag = current.tags[i]; + if (_.indexOf(tag.values, option.value) !== -1) { + return false; + } + } + return true; + }); + + // convert values to text + let currentTexts = _.map(selectedAndNotInTag, 'text'); + + // join texts + this.linkText = currentTexts.join(' + '); + if (this.linkText.length > 0) { + this.linkText += ' + '; + } + } else { + this.linkText = this.variable.current.text; + } + } + + clearSelections() { + _.each(this.options, function(option) { + option.selected = false; + }); + + this.selectionsChanged(false); + } + + selectTag(tag) { + tag.selected = !tag.selected; + let tagValuesPromise; + if (!tag.values) { + tagValuesPromise = this.variable.getValuesForTag(tag.text); + } else { + tagValuesPromise = this.$q.when(tag.values); + } + + tagValuesPromise.then(function(values) { + tag.values = values; + tag.valuesText = values.join(' + '); + _.each(this.options, function(option) { + if (_.indexOf(tag.values, option.value) !== -1) { + option.selected = tag.selected; + } + }); + + this.selectionsChanged(false); + }); + } + + keyDown(evt) { + if (evt.keyCode === 27) { + this.hide(); + } + if (evt.keyCode === 40) { + this.moveHighlight(1); + } + if (evt.keyCode === 38) { + this.moveHighlight(-1); + } + if (evt.keyCode === 13) { + if (this.search.options.length === 0) { + this.commitChanges(); + } else { + this.selectValue(this.search.options[this.highlightIndex], {}, true, false); + } + } + if (evt.keyCode === 32) { + this.selectValue(this.search.options[this.highlightIndex], {}, false, false); + } + } + + moveHighlight(direction) { + this.highlightIndex = (this.highlightIndex + direction) % this.search.options.length; + } + + selectValue(option, event, commitChange, excludeOthers) { + if (!option) { + return; + } + + option.selected = this.variable.multi ? !option.selected : true; + + commitChange = commitChange || false; + excludeOthers = excludeOthers || false; + + let setAllExceptCurrentTo = function(newValue) { + _.each(this.options, function(other) { + if (option !== other) { + other.selected = newValue; + } + }); + }; + + // commit action (enter key), should not deselect it + if (commitChange) { + option.selected = true; + } + + if (option.text === 'All' || excludeOthers) { + setAllExceptCurrentTo(false); + commitChange = true; + } else if (!this.variable.multi) { + setAllExceptCurrentTo(false); + commitChange = true; + } else if (event.ctrlKey || event.metaKey || event.shiftKey) { + commitChange = true; + setAllExceptCurrentTo(false); + } + + this.selectionsChanged(commitChange); + } + + selectionsChanged(commitChange) { + this.selectedValues = _.filter(this.options, { selected: true }); + + if (this.selectedValues.length > 1) { + if (this.selectedValues[0].text === 'All') { + this.selectedValues[0].selected = false; + this.selectedValues = this.selectedValues.slice(1, this.selectedValues.length); + } + } + + // validate selected tags + _.each(this.tags, function(tag) { + if (tag.selected) { + _.each(tag.values, function(value) { + if (!_.find(this.selectedValues, { value: value })) { + tag.selected = false; + } + }); + } + }); + + this.selectedTags = _.filter(this.tags, { selected: true }); + this.variable.current.value = _.map(this.selectedValues, 'value'); + this.variable.current.text = _.map(this.selectedValues, 'text').join(' + '); + this.variable.current.tags = this.selectedTags; + + if (!this.variable.multi) { + this.variable.current.value = this.selectedValues[0].value; + } + + if (commitChange) { + this.commitChanges(); + } + } + + commitChanges() { + // if we have a search query and no options use that + if (this.search.options.length === 0 && this.search.query.length > 0) { + this.variable.current = { text: this.search.query, value: this.search.query }; + } else if (this.selectedValues.length === 0) { + // make sure one option is selected + this.options[0].selected = true; + this.selectionsChanged(false); + } + + this.dropdownVisible = false; + this.updateLinkText(); + + if (this.variable.current.text !== this.oldVariableText) { + this.onUpdated(); + } + } + + queryChanged() { + this.highlightIndex = -1; + this.search.options = _.filter(this.options, function(option) { + return option.text.toLowerCase().indexOf(this.search.query.toLowerCase()) !== -1; + }); + + this.search.options = this.search.options.slice(0, Math.min(this.search.options.length, 1000)); + } + + init() { + this.selectedTags = this.variable.current.tags || []; + this.updateLinkText(); + } +} + +export function valueSelectDropdown($compile, $window, $timeout, $rootScope) { + return { + scope: { variable: '=', onUpdated: '&' }, + templateUrl: 'public/app/partials/valueSelectDropdown.html', + controller: 'ValueSelectDropdownCtrl', + controllerAs: 'vm', + bindToController: true, + link: function(scope, elem) { + let bodyEl = angular.element($window.document.body); + let linkEl = elem.find('.variable-value-link'); + let inputEl = elem.find('input'); + + function openDropdown() { + inputEl.css('width', Math.max(linkEl.width(), 80) + 'px'); + + inputEl.show(); + linkEl.hide(); + + inputEl.focus(); + $timeout( + function() { + bodyEl.on('click', bodyOnClick); + }, + 0, + false + ); + } + + function switchToLink() { + inputEl.hide(); + linkEl.show(); + bodyEl.off('click', bodyOnClick); + } + + function bodyOnClick(e) { + if (elem.has(e.target).length === 0) { + scope.$apply(function() { + scope.vm.commitChanges(); + }); + } + } + + scope.$watch('vm.dropdownVisible', function(newValue) { + if (newValue) { + openDropdown(); + } else { + switchToLink(); + } + }); + + let cleanUp = $rootScope.$on('template-variable-value-updated', function() { + scope.vm.updateLinkText(); + }); + + scope.$on('$destroy', function() { + cleanUp(); + }); + + scope.vm.init(); + }, + }; +} + +coreModule.controller('ValueSelectDropdownCtrl', ValueSelectDropdownCtrl); +coreModule.directive('valueSelectDropdown', valueSelectDropdown); From d3d64337b48969d7f7367f8a5122ac3410833961 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 6 Apr 2018 20:00:03 +0300 Subject: [PATCH 070/145] scrollbar: styles cleanup --- public/app/core/components/scroll/scroll.ts | 1 - public/app/features/panel/panel_directive.ts | 2 ++ public/app/plugins/panel/graph/legend.ts | 5 ++--- public/sass/components/_panel_graph.scss | 5 ++++- public/sass/components/_scrollbar.scss | 9 ++------- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index 12321bc4f5c..3f9865e6dce 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -77,7 +77,6 @@ export function geminiScrollbar() { }); scope.$on('$destroy', () => { - // scrollbar.destroy(); scrollbar.dispose(); }); }, diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 4edf293c801..90ff42f4ac6 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -125,6 +125,8 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { barOnCls: '_scrollbar', scrollingCls: '_scrolling', }); + + panelScrollbar.scroll(); } }); diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index f61df4d2683..752dc591147 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -18,9 +18,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { const legendRightDefaultWidth = 10; scope.$on('$destroy', function() { - if (legendScrollbar) { - legendScrollbar.dispose(); - } + destroyScrollbar(); }); ctrl.events.on('render-legend', () => { @@ -288,6 +286,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { destroyScrollbar(); legendScrollbar = baron(scrollbarParams); } + legendScrollbar.scroll(); } function destroyScrollbar() { diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index cc5c601e0ea..031192dd984 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -58,7 +58,7 @@ position: relative; // fix for Firefox (white stripe on the right of scrollbar) - width: 99%; + width: calc(100% - 1px); .popover-content { padding: 0; @@ -67,6 +67,9 @@ .graph-legend-content { position: relative; + + // fix for Firefox (white stripe on the right of scrollbar) + width: calc(100% - 1px); } .graph-legend-scroll { diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 93b81fc1b4b..51e061e54b9 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -189,10 +189,6 @@ .baron { display: inline-block; overflow: hidden; - - // Width needs to be set to prevent content width issues - // Set to 99% instead of 100% for fixing Firefox issue (white stripe on the right of scrollbar) - width: 99%; } // Fix for side menu on mobile devices @@ -203,7 +199,6 @@ .baron__clipper { position: relative; overflow: hidden; - height: 100%; } .baron__scroller { @@ -289,8 +284,8 @@ .baron.panel-content--scrollable { // Width needs to be set to prevent content width issues - // Set to 99% instead of 100% for fixing Firefox issue (white stripe on the right of scrollbar) - width: 99%; + // Set to less than 100% for fixing Firefox issue (white stripe on the right of scrollbar) + width: calc(100% - 2px); .baron__scroller { padding-top: 1px; From 41e5d66e399ea096bea99b5922722eb3c08b92b6 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 6 Apr 2018 20:31:44 +0300 Subject: [PATCH 071/145] scrollbar: fix add panel height bug --- public/app/features/dashboard/dashgrid/AddPanelPanel.tsx | 2 +- public/sass/components/_panel_add_panel.scss | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx b/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx index aeb840c317a..98d1657f4bd 100644 --- a/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx +++ b/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx @@ -103,7 +103,7 @@ export class AddPanelPanel extends React.Component +
diff --git a/public/sass/components/_panel_add_panel.scss b/public/sass/components/_panel_add_panel.scss index ee5d757d841..4a17438ffeb 100644 --- a/public/sass/components/_panel_add_panel.scss +++ b/public/sass/components/_panel_add_panel.scss @@ -1,3 +1,7 @@ +.add-panel-container { + height: 100%; +} + .add-panel { height: 100%; From ce787b88bf1525c555137f85d405cb0b481485b8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 6 Apr 2018 21:29:42 +0200 Subject: [PATCH 072/145] css: quick fix after IE11 changes Temporary fix so that the add panel panel works properly. This will break a3f15ced6805d3d949d02c5a8a1d5267a6dac3a7. --- public/sass/pages/_dashboard.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index c957b6af790..871db4dfc2d 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -33,7 +33,7 @@ div.flot-text { border: $panel-border; position: relative; border-radius: 3px; - //height: 100%; + height: 100%; &.panel-transparent { background-color: transparent; From d60c7b201316268725124fe113bb4ea27ab50e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 9 Apr 2018 08:18:58 +0200 Subject: [PATCH 073/145] docs: updated debian distro in install docs to stretch, closes #11527 --- docs/sources/installation/debian.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 27c0e41ac15..8b51a75a826 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -34,7 +34,7 @@ sudo dpkg -i grafana_5.0.4_amd64.deb Add the following line to your `/etc/apt/sources.list` file. ```bash -deb https://packagecloud.io/grafana/stable/debian/ jessie main +deb https://packagecloud.io/grafana/stable/debian/ stretch main ``` Use the above line even if you are on Ubuntu or another Debian version. @@ -42,7 +42,7 @@ There is also a testing repository if you want beta or release candidates. ```bash -deb https://packagecloud.io/grafana/testing/debian/ jessie main +deb https://packagecloud.io/grafana/testing/debian/ stretch main ``` Then add the [Package Cloud](https://packagecloud.io/grafana) key. This From 25be937bfa2f8ede81323079a33a8011289d5522 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 9 Apr 2018 09:55:10 +0200 Subject: [PATCH 074/145] docs: fixes typo --- docs/sources/features/datasources/cloudwatch.md | 2 +- docs/sources/features/datasources/elasticsearch.md | 2 +- docs/sources/features/datasources/graphite.md | 2 +- docs/sources/features/datasources/influxdb.md | 2 +- docs/sources/features/datasources/mysql.md | 2 +- docs/sources/features/datasources/opentsdb.md | 2 +- docs/sources/features/datasources/postgres.md | 2 +- docs/sources/features/datasources/prometheus.md | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index 3013f6b1fc9..bd668bfe93c 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -176,7 +176,7 @@ Grafana will issue a ListMetrics request. ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here is an example of how you can configure the Cloudwatch datasource using configuration. Using a credentials file diff --git a/docs/sources/features/datasources/elasticsearch.md b/docs/sources/features/datasources/elasticsearch.md index 2cfe74588d1..8acc40f87c2 100644 --- a/docs/sources/features/datasources/elasticsearch.md +++ b/docs/sources/features/datasources/elasticsearch.md @@ -140,7 +140,7 @@ Tags | Optional field name to use for event tags (can be an array or a CSV strin ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/graphite.md b/docs/sources/features/datasources/graphite.md index 040a21e5d0c..fb2c6f0bd52 100644 --- a/docs/sources/features/datasources/graphite.md +++ b/docs/sources/features/datasources/graphite.md @@ -123,7 +123,7 @@ specify a tag or wildcard (leave empty should also work) ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/influxdb.md b/docs/sources/features/datasources/influxdb.md index e36adb3784e..6b0d92c8e6f 100644 --- a/docs/sources/features/datasources/influxdb.md +++ b/docs/sources/features/datasources/influxdb.md @@ -177,7 +177,7 @@ Tags field can be a comma separated string. ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index dc05b0b31de..3b6daed622f 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -228,7 +228,7 @@ Time series queries should work in alerting conditions. Table formatted queries ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index f67861e2a51..a233d4fefe8 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -91,7 +91,7 @@ For details on OpenTSDB metric queries checkout the official [OpenTSDB documenta ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index b70ab1cde04..6384d101dff 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -220,7 +220,7 @@ conditions. ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index ebd9ce32a47..ba0ef626745 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -103,7 +103,7 @@ Since 4.6.0 Grafana exposes metrics for Prometheus on the `/metrics` endpoint. W ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml From 95132165f85c4309c6932aacbe49b58ba5ed13e3 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 9 Apr 2018 12:47:19 +0200 Subject: [PATCH 075/145] data source: rename direct/proxy access mode in data source settings Changes access drop down options names: - proxy -> Server (Default) - direct -> Browser Replace access drop down info icon/tooltip with expandable/collapsable help section. --- .../plugins/partials/ds_http_settings.html | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/public/app/features/plugins/partials/ds_http_settings.html b/public/app/features/plugins/partials/ds_http_settings.html index 03df677ba13..b9f5683129c 100644 --- a/public/app/features/plugins/partials/ds_http_settings.html +++ b/public/app/features/plugins/partials/ds_http_settings.html @@ -1,5 +1,3 @@ - -

HTTP

@@ -13,12 +11,12 @@

Specify a complete HTTP URL (for example http://your_server:8080)

- Your access method is Direct, this means the URL + Your access method is Browser, this means the URL needs to be accessible from the browser. - Your access method is currently Proxy, this means the URL - needs to be accessible from the grafana backend. + Your access method is Server, this means the URL + needs to be accessible from the grafana backend/server.
@@ -27,14 +25,38 @@
Access -
- - - Direct = URL is used directly from browser
- Proxy = Grafana backend will proxy the request -
+
+
+
+ +
+
+ +
+
+

+ Access mode controls how requests to the data source will be handled. + Server should be the preferred way if nothing else stated. +

+
Server access mode (Default):
+

+ All requests will be made from the browser to Grafana backend/server which in turn will forward the requests to the data source + and by that circumvent possible Cross-Origin Resource Sharing (CORS) requirements. + The URL needs to be accessible from the grafana backend/server if you select this access mode. +

+
Browser access mode:
+

+ All requests will be made from the browser directly to the data source and may be subject to + Cross-Origin Resource Sharing (CORS) requirements. The URL needs to be accessible from the browser if you select this + access mode. +

+
@@ -135,4 +157,3 @@
- From 00524e68273d83566f9312add6d653f4a870b012 Mon Sep 17 00:00:00 2001 From: Sven Klemm <31455525+svenklemm@users.noreply.github.com> Date: Mon, 9 Apr 2018 12:51:17 +0200 Subject: [PATCH 076/145] prevent angular from evaluating {{hostname}} in tooltip (#11514) --- .../plugins/datasource/prometheus/partials/query.editor.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/prometheus/partials/query.editor.html b/public/app/plugins/datasource/prometheus/partials/query.editor.html index 8d6e89c3406..68791d96c19 100644 --- a/public/app/plugins/datasource/prometheus/partials/query.editor.html +++ b/public/app/plugins/datasource/prometheus/partials/query.editor.html @@ -14,7 +14,7 @@ data-min-length=0 data-items=1000 ng-model-onblur ng-change="ctrl.refreshMetricData()"> - Controls the name of the time series, using name or pattern. For example {{hostname}} will be replaced with label value for + Controls the name of the time series, using name or pattern. For example {{hostname}} will be replaced with label value for the label hostname.
From d9ba16f550afca4112fcab6a0641f164608e91d5 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 9 Apr 2018 14:21:20 +0300 Subject: [PATCH 077/145] scrollbar: fix phantomjs rendering error --- public/sass/components/_scrollbar.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 51e061e54b9..4bd0807bb0b 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -187,7 +187,7 @@ // Baron styles .baron { - display: inline-block; + // display: inline-block; // this brakes phantomjs rendering (width becomes 0) overflow: hidden; } From 113bfb3d3eafeaad1453d371e3cab889b9d13f70 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 9 Apr 2018 13:28:32 +0200 Subject: [PATCH 078/145] don't convert to uint64 --- pkg/tsdb/postgres/macros.go | 12 ++++++------ pkg/tsdb/postgres/macros_test.go | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pkg/tsdb/postgres/macros.go b/pkg/tsdb/postgres/macros.go index 23daeebec5a..f96b3896041 100644 --- a/pkg/tsdb/postgres/macros.go +++ b/pkg/tsdb/postgres/macros.go @@ -83,11 +83,11 @@ 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("extract(epoch from %s) BETWEEN %d AND %d", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("extract(epoch from %s) BETWEEN %d AND %d", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__timeFrom": - return fmt.Sprintf("to_timestamp(%d)", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("to_timestamp(%d)", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil case "__timeTo": - return fmt.Sprintf("to_timestamp(%d)", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("to_timestamp(%d)", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval and optional fill value", name) @@ -114,11 +114,11 @@ 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 >= %d AND %s <= %d", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil case "__unixEpochTo": - return fmt.Sprintf("%d", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/postgres/macros_test.go b/pkg/tsdb/postgres/macros_test.go index b18acced963..1485f8a261f 100644 --- a/pkg/tsdb/postgres/macros_test.go +++ b/pkg/tsdb/postgres/macros_test.go @@ -85,5 +85,12 @@ func TestMacroEngine(t *testing.T) { So(sql, ShouldEqual, "select 18446744066914187038") }) + timeRange := &tsdb.TimeRange{From: "-315622800000", To: "315529200000"} // 1960-1980 + Convey("interpolate __timeFilter function before epoch", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "WHERE extract(epoch from time_column) BETWEEN -315622800 AND 315529200") + }) }) } From 7d6c8aa61299d48f2308b56d057c45a3feb35f38 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 9 Apr 2018 13:34:35 +0200 Subject: [PATCH 079/145] add mssql and mysql --- pkg/tsdb/mssql/macros.go | 12 ++++++------ pkg/tsdb/mysql/macros.go | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/tsdb/mssql/macros.go b/pkg/tsdb/mssql/macros.go index 9d41cd03255..6fd1550b048 100644 --- a/pkg/tsdb/mssql/macros.go +++ b/pkg/tsdb/mssql/macros.go @@ -82,11 +82,11 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= DATEADD(s, %d, '1970-01-01') AND %s <= DATEADD(s, %d, '1970-01-01')", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= DATEADD(s, %d, '1970-01-01') AND %s <= DATEADD(s, %d, '1970-01-01')", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__timeFrom": - return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil case "__timeTo": - return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) @@ -113,11 +113,11 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil case "__unixEpochTo": - return fmt.Sprintf("%d", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/mysql/macros.go b/pkg/tsdb/mysql/macros.go index a292f209429..92f6968f12a 100644 --- a/pkg/tsdb/mysql/macros.go +++ b/pkg/tsdb/mysql/macros.go @@ -77,11 +77,11 @@ func (m *MySqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= FROM_UNIXTIME(%d) AND %s <= FROM_UNIXTIME(%d)", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= FROM_UNIXTIME(%d) AND %s <= FROM_UNIXTIME(%d)", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__timeFrom": - return fmt.Sprintf("FROM_UNIXTIME(%d)", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("FROM_UNIXTIME(%d)", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil case "__timeTo": - return fmt.Sprintf("FROM_UNIXTIME(%d)", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("FROM_UNIXTIME(%d)", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) @@ -108,11 +108,11 @@ func (m *MySqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil case "__unixEpochTo": - return fmt.Sprintf("%d", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil default: return "", fmt.Errorf("Unknown macro %v", name) } From 920a0c4fec5149fc1bc3b42646a909bc036178dd Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 9 Apr 2018 13:49:13 +0200 Subject: [PATCH 080/145] skip mssql fix --- pkg/tsdb/mssql/macros.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/tsdb/mssql/macros.go b/pkg/tsdb/mssql/macros.go index 6fd1550b048..9d41cd03255 100644 --- a/pkg/tsdb/mssql/macros.go +++ b/pkg/tsdb/mssql/macros.go @@ -82,11 +82,11 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= DATEADD(s, %d, '1970-01-01') AND %s <= DATEADD(s, %d, '1970-01-01')", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= DATEADD(s, %d, '1970-01-01') AND %s <= DATEADD(s, %d, '1970-01-01')", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__timeFrom": - return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil case "__timeTo": - return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) @@ -113,11 +113,11 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil case "__unixEpochTo": - return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil default: return "", fmt.Errorf("Unknown macro %v", name) } From 1c9ebd5bd8c46cb5b32e7cbe0f94d4f5bded1447 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 9 Apr 2018 14:01:09 +0200 Subject: [PATCH 081/145] fix test --- pkg/tsdb/postgres/macros_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/tsdb/postgres/macros_test.go b/pkg/tsdb/postgres/macros_test.go index 1485f8a261f..838a73ce240 100644 --- a/pkg/tsdb/postgres/macros_test.go +++ b/pkg/tsdb/postgres/macros_test.go @@ -85,7 +85,7 @@ func TestMacroEngine(t *testing.T) { So(sql, ShouldEqual, "select 18446744066914187038") }) - timeRange := &tsdb.TimeRange{From: "-315622800000", To: "315529200000"} // 1960-1980 + timeRange = &tsdb.TimeRange{From: "-315622800000", To: "315529200000"} // 1960-1980 Convey("interpolate __timeFilter function before epoch", func() { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) From 2889a405a774fed0cebc168bb2894f0c747c2727 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 9 Apr 2018 14:56:42 +0200 Subject: [PATCH 082/145] docs: improves provisoning example text --- docs/sources/features/datasources/cloudwatch.md | 5 +++-- docs/sources/features/datasources/elasticsearch.md | 3 ++- docs/sources/features/datasources/graphite.md | 3 ++- docs/sources/features/datasources/influxdb.md | 3 ++- docs/sources/features/datasources/mysql.md | 3 ++- docs/sources/features/datasources/opentsdb.md | 3 ++- docs/sources/features/datasources/postgres.md | 3 ++- docs/sources/features/datasources/prometheus.md | 3 ++- 8 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index bd668bfe93c..3aa310eaade 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -176,8 +176,9 @@ Grafana will issue a ListMetrics request. ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). -Here is an example of how you can configure the Cloudwatch datasource using configuration. +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + +Here are some examples of how you can configure the Cloudwatch datasource using configuration. Using a credentials file ```yaml diff --git a/docs/sources/features/datasources/elasticsearch.md b/docs/sources/features/datasources/elasticsearch.md index 8acc40f87c2..c0b5f4b9283 100644 --- a/docs/sources/features/datasources/elasticsearch.md +++ b/docs/sources/features/datasources/elasticsearch.md @@ -140,7 +140,8 @@ Tags | Optional field name to use for event tags (can be an array or a CSV strin ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/graphite.md b/docs/sources/features/datasources/graphite.md index fb2c6f0bd52..895664b09ad 100644 --- a/docs/sources/features/datasources/graphite.md +++ b/docs/sources/features/datasources/graphite.md @@ -123,7 +123,8 @@ specify a tag or wildcard (leave empty should also work) ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/influxdb.md b/docs/sources/features/datasources/influxdb.md index 6b0d92c8e6f..4c5109ef0bc 100644 --- a/docs/sources/features/datasources/influxdb.md +++ b/docs/sources/features/datasources/influxdb.md @@ -177,7 +177,8 @@ Tags field can be a comma separated string. ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index 3b6daed622f..51483bfa36a 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -228,7 +228,8 @@ Time series queries should work in alerting conditions. Table formatted queries ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index a233d4fefe8..d29753b0071 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -91,7 +91,8 @@ For details on OpenTSDB metric queries checkout the official [OpenTSDB documenta ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 6384d101dff..104aedc5d5d 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -220,7 +220,8 @@ conditions. ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index ba0ef626745..84cb8c2e333 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -103,7 +103,8 @@ Since 4.6.0 Grafana exposes metrics for Prometheus on the `/metrics` endpoint. W ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml From 5f47523d7682c8297718a5cb18f2bd7a0b71387a Mon Sep 17 00:00:00 2001 From: Caleb Tote Date: Mon, 9 Apr 2018 15:09:37 -0400 Subject: [PATCH 083/145] Update annotations.md to contain correct annotations api path --- docs/sources/http_api/annotations.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/sources/http_api/annotations.md b/docs/sources/http_api/annotations.md index 19c2a5c386c..038ff085674 100644 --- a/docs/sources/http_api/annotations.md +++ b/docs/sources/http_api/annotations.md @@ -180,14 +180,14 @@ Content-Type: application/json ## Delete Annotation By Id -`DELETE /api/annotation/:id` +`DELETE /api/annotations/:id` Deletes the annotation that matches the specified id. **Example Request**: ```http -DELETE /api/annotation/1 HTTP/1.1 +DELETE /api/annotations/1 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk @@ -204,14 +204,14 @@ Content-Type: application/json ## Delete Annotation By RegionId -`DELETE /api/annotation/region/:id` +`DELETE /api/annotations/region/:id` Deletes the annotation that matches the specified region id. A region is an annotation that covers a timerange and has a start and end time. In the Grafana database, this is a stored as two annotations connected by a region id. **Example Request**: ```http -DELETE /api/annotation/region/1 HTTP/1.1 +DELETE /api/annotations/region/1 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk From c860f2105240282470258fb8bc401389ab4ac497 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 9 Apr 2018 21:33:18 +0200 Subject: [PATCH 084/145] playlist: add missing nginject attribute --- public/app/features/playlist/playlist_routes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/features/playlist/playlist_routes.ts b/public/app/features/playlist/playlist_routes.ts index c94446c2c1b..b898820e371 100644 --- a/public/app/features/playlist/playlist_routes.ts +++ b/public/app/features/playlist/playlist_routes.ts @@ -1,5 +1,6 @@ import angular from 'angular'; +/** @ngInject */ function grafanaRoutes($routeProvider) { $routeProvider .when('/playlists', { From 6719bdf9bde8057716a606c5b68adfa0df4d11fc Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 10 Apr 2018 09:24:54 +0200 Subject: [PATCH 085/145] added @ngInject --- public/app/core/directives/metric_segment.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/app/core/directives/metric_segment.ts b/public/app/core/directives/metric_segment.ts index d1054b23793..3718d7fbd4a 100644 --- a/public/app/core/directives/metric_segment.ts +++ b/public/app/core/directives/metric_segment.ts @@ -2,6 +2,7 @@ import _ from 'lodash'; import $ from 'jquery'; import coreModule from '../core_module'; +/** @ngInject */ export function metricSegment($compile, $sce) { let inputTemplate = ' Date: Tue, 10 Apr 2018 10:32:30 +0200 Subject: [PATCH 086/145] tsdb: add support for more data types when converting sql time column to epoch (ms) --- pkg/tsdb/sql_engine.go | 30 +++++- pkg/tsdb/sql_engine_test.go | 182 +++++++++++++++++++++++++++++++----- pkg/tsdb/time_range.go | 5 + 3 files changed, 192 insertions(+), 25 deletions(-) diff --git a/pkg/tsdb/sql_engine.go b/pkg/tsdb/sql_engine.go index 16370a4ea7f..0f35cadf4d6 100644 --- a/pkg/tsdb/sql_engine.go +++ b/pkg/tsdb/sql_engine.go @@ -135,16 +135,16 @@ func (e *DefaultSqlEngine) Query( return result, nil } -// ConvertTimeColumnToEpochMs converts column named time to unix timestamp in milliseconds +// ConvertSqlTimeColumnToEpochMs converts column named time to unix timestamp in milliseconds // to make native datetime types and epoch dates work in annotation and table queries. func ConvertSqlTimeColumnToEpochMs(values RowValues, timeIndex int) { if timeIndex >= 0 { switch value := values[timeIndex].(type) { case time.Time: - values[timeIndex] = EpochPrecisionToMs(float64(value.Unix())) + values[timeIndex] = EpochPrecisionToMs(float64(value.UnixNano())) case *time.Time: if value != nil { - values[timeIndex] = EpochPrecisionToMs(float64((*value).Unix())) + values[timeIndex] = EpochPrecisionToMs(float64((*value).UnixNano())) } case int64: values[timeIndex] = int64(EpochPrecisionToMs(float64(value))) @@ -152,12 +152,36 @@ func ConvertSqlTimeColumnToEpochMs(values RowValues, timeIndex int) { if value != nil { values[timeIndex] = int64(EpochPrecisionToMs(float64(*value))) } + case uint64: + values[timeIndex] = int64(EpochPrecisionToMs(float64(value))) + case *uint64: + if value != nil { + values[timeIndex] = int64(EpochPrecisionToMs(float64(*value))) + } + case int32: + values[timeIndex] = int64(EpochPrecisionToMs(float64(value))) + case *int32: + if value != nil { + values[timeIndex] = int64(EpochPrecisionToMs(float64(*value))) + } + case uint32: + values[timeIndex] = int64(EpochPrecisionToMs(float64(value))) + case *uint32: + if value != nil { + values[timeIndex] = int64(EpochPrecisionToMs(float64(*value))) + } case float64: values[timeIndex] = EpochPrecisionToMs(value) case *float64: if value != nil { values[timeIndex] = EpochPrecisionToMs(*value) } + case float32: + values[timeIndex] = EpochPrecisionToMs(float64(value)) + case *float32: + if value != nil { + values[timeIndex] = EpochPrecisionToMs(float64(*value)) + } } } } diff --git a/pkg/tsdb/sql_engine_test.go b/pkg/tsdb/sql_engine_test.go index 48aac2c4d45..f8856489230 100644 --- a/pkg/tsdb/sql_engine_test.go +++ b/pkg/tsdb/sql_engine_test.go @@ -9,37 +9,175 @@ import ( func TestSqlEngine(t *testing.T) { Convey("SqlEngine", t, func() { - Convey("Given row values with time columns when converting them", func() { - dt := time.Date(2018, 3, 14, 21, 20, 6, 527e6, time.UTC) - fixtures := make([]interface{}, 8) - fixtures[0] = dt - fixtures[1] = dt.Unix() * 1000 - fixtures[2] = dt.Unix() - fixtures[3] = float64(dt.Unix() * 1000) - fixtures[4] = float64(dt.Unix()) + dt := time.Date(2018, 3, 14, 21, 20, 6, 527e6, time.UTC) - var nilDt *time.Time - var nilInt64 *int64 - var nilFloat64 *float64 - fixtures[5] = nilDt - fixtures[6] = nilInt64 - fixtures[7] = nilFloat64 + Convey("Given row values with time.Time as time columns", func() { + var nilPointer *time.Time + + fixtures := make([]interface{}, 3) + fixtures[0] = dt + fixtures[1] = &dt + fixtures[2] = nilPointer for i := range fixtures { ConvertSqlTimeColumnToEpochMs(fixtures, i) } - Convey("Should convert sql time columns to epoch time in ms ", func() { - expected := float64(dt.Unix() * 1000) + Convey("When converting them should return epoch time with millisecond precision ", func() { + expected := float64(dt.UnixNano() / 1e6) So(fixtures[0].(float64), ShouldEqual, expected) - So(fixtures[1].(int64), ShouldEqual, expected) - So(fixtures[2].(int64), ShouldEqual, expected) - So(fixtures[3].(float64), ShouldEqual, expected) - So(fixtures[4].(float64), ShouldEqual, expected) + So(fixtures[1].(float64), ShouldEqual, expected) + So(fixtures[2], ShouldBeNil) + }) + }) - So(fixtures[5], ShouldBeNil) + Convey("Given row values with int64 as time columns", func() { + tSeconds := dt.Unix() + tMilliseconds := dt.UnixNano() / 1e6 + tNanoSeconds := dt.UnixNano() + var nilPointer *int64 + + fixtures := make([]interface{}, 7) + fixtures[0] = tSeconds + fixtures[1] = &tSeconds + fixtures[2] = tMilliseconds + fixtures[3] = &tMilliseconds + fixtures[4] = tNanoSeconds + fixtures[5] = &tNanoSeconds + fixtures[6] = nilPointer + + for i := range fixtures { + ConvertSqlTimeColumnToEpochMs(fixtures, i) + } + + Convey("When converting them should return epoch time with millisecond precision ", func() { + So(fixtures[0].(int64), ShouldEqual, tSeconds*1e3) + So(fixtures[1].(int64), ShouldEqual, tSeconds*1e3) + So(fixtures[2].(int64), ShouldEqual, tMilliseconds) + So(fixtures[3].(int64), ShouldEqual, tMilliseconds) + So(fixtures[4].(int64), ShouldEqual, tMilliseconds) + So(fixtures[5].(int64), ShouldEqual, tMilliseconds) So(fixtures[6], ShouldBeNil) - So(fixtures[7], ShouldBeNil) + }) + }) + + Convey("Given row values with uin64 as time columns", func() { + tSeconds := uint64(dt.Unix()) + tMilliseconds := uint64(dt.UnixNano() / 1e6) + tNanoSeconds := uint64(dt.UnixNano()) + var nilPointer *uint64 + + fixtures := make([]interface{}, 7) + fixtures[0] = tSeconds + fixtures[1] = &tSeconds + fixtures[2] = tMilliseconds + fixtures[3] = &tMilliseconds + fixtures[4] = tNanoSeconds + fixtures[5] = &tNanoSeconds + fixtures[6] = nilPointer + + for i := range fixtures { + ConvertSqlTimeColumnToEpochMs(fixtures, i) + } + + Convey("When converting them should return epoch time with millisecond precision ", func() { + So(fixtures[0].(int64), ShouldEqual, tSeconds*1e3) + So(fixtures[1].(int64), ShouldEqual, tSeconds*1e3) + So(fixtures[2].(int64), ShouldEqual, tMilliseconds) + So(fixtures[3].(int64), ShouldEqual, tMilliseconds) + So(fixtures[4].(int64), ShouldEqual, tMilliseconds) + So(fixtures[5].(int64), ShouldEqual, tMilliseconds) + So(fixtures[6], ShouldBeNil) + }) + }) + + Convey("Given row values with int32 as time columns", func() { + tSeconds := int32(dt.Unix()) + var nilInt *int32 + + fixtures := make([]interface{}, 3) + fixtures[0] = tSeconds + fixtures[1] = &tSeconds + fixtures[2] = nilInt + + for i := range fixtures { + ConvertSqlTimeColumnToEpochMs(fixtures, i) + } + + Convey("When converting them should return epoch time with millisecond precision ", func() { + So(fixtures[0].(int64), ShouldEqual, dt.Unix()*1e3) + So(fixtures[1].(int64), ShouldEqual, dt.Unix()*1e3) + So(fixtures[2], ShouldBeNil) + }) + }) + + Convey("Given row values with uint32 as time columns", func() { + tSeconds := uint32(dt.Unix()) + var nilInt *uint32 + + fixtures := make([]interface{}, 3) + fixtures[0] = tSeconds + fixtures[1] = &tSeconds + fixtures[2] = nilInt + + for i := range fixtures { + ConvertSqlTimeColumnToEpochMs(fixtures, i) + } + + Convey("When converting them should return epoch time with millisecond precision ", func() { + So(fixtures[0].(int64), ShouldEqual, dt.Unix()*1e3) + So(fixtures[1].(int64), ShouldEqual, dt.Unix()*1e3) + So(fixtures[2], ShouldBeNil) + }) + }) + + Convey("Given row values with float64 as time columns", func() { + tSeconds := float64(dt.Unix()) + tMilliseconds := float64(dt.UnixNano() / 1e6) + tNanoSeconds := float64(dt.UnixNano()) + var nilPointer *float64 + + fixtures := make([]interface{}, 7) + fixtures[0] = tSeconds + fixtures[1] = &tSeconds + fixtures[2] = tMilliseconds + fixtures[3] = &tMilliseconds + fixtures[4] = tNanoSeconds + fixtures[5] = &tNanoSeconds + fixtures[6] = nilPointer + + for i := range fixtures { + ConvertSqlTimeColumnToEpochMs(fixtures, i) + } + + Convey("When converting them should return epoch time with millisecond precision ", func() { + So(fixtures[0].(float64), ShouldEqual, tSeconds*1e3) + So(fixtures[1].(float64), ShouldEqual, tSeconds*1e3) + So(fixtures[2].(float64), ShouldEqual, tMilliseconds) + So(fixtures[3].(float64), ShouldEqual, tMilliseconds) + So(fixtures[4].(float64), ShouldEqual, tMilliseconds) + So(fixtures[5].(float64), ShouldEqual, tMilliseconds) + So(fixtures[6], ShouldBeNil) + }) + }) + + Convey("Given row values with float32 as time columns", func() { + tSeconds := float32(dt.Unix()) + var nilInt *float32 + + fixtures := make([]interface{}, 3) + fixtures[0] = tSeconds + fixtures[1] = &tSeconds + fixtures[2] = nilInt + + for i := range fixtures { + ConvertSqlTimeColumnToEpochMs(fixtures, i) + } + + Convey("When converting them should return epoch time with millisecond precision ", func() { + So(fixtures[0].(float64), ShouldEqual, float32(dt.Unix()*1e3)) + So(fixtures[1].(float64), ShouldEqual, float32(dt.Unix()*1e3)) + So(fixtures[2], ShouldBeNil) }) }) }) diff --git a/pkg/tsdb/time_range.go b/pkg/tsdb/time_range.go index fd0cb3f8e82..47076eb3c6b 100644 --- a/pkg/tsdb/time_range.go +++ b/pkg/tsdb/time_range.go @@ -96,5 +96,10 @@ func EpochPrecisionToMs(value float64) float64 { return float64(value * 1e3) } + s := strconv.FormatFloat(value, 'f', -1, 64) + if len(s) == 19 { + return float64(value / 1e6) + } + return float64(value) } From 5c120c2c11e9a85dff1f0e9953014a07734a6cb1 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 10 Apr 2018 10:58:45 +0200 Subject: [PATCH 087/145] mysql: mysql tests should use a db server with UTC To get rid of issues involving date/time when testing. Also, makes it possible to run mysql integration tests for both grafana config db and tsdb at the same time using GRAFANA_TEST_DB=mysql go test ./pkg/... --- docker/blocks/mysql_tests/Dockerfile | 3 + docker/blocks/mysql_tests/dashboard.json | 158 +++++++++++++----- docker/blocks/mysql_tests/docker-compose.yaml | 6 +- docker/blocks/mysql_tests/setup.sql | 2 + pkg/services/sqlstore/sqlstore.go | 11 +- pkg/tsdb/mysql/mysql_test.go | 38 +++-- 6 files changed, 156 insertions(+), 62 deletions(-) create mode 100644 docker/blocks/mysql_tests/Dockerfile create mode 100644 docker/blocks/mysql_tests/setup.sql diff --git a/docker/blocks/mysql_tests/Dockerfile b/docker/blocks/mysql_tests/Dockerfile new file mode 100644 index 00000000000..fa91fa3c023 --- /dev/null +++ b/docker/blocks/mysql_tests/Dockerfile @@ -0,0 +1,3 @@ +FROM mysql:latest +ADD setup.sql /docker-entrypoint-initdb.d +CMD ["mysqld"] \ No newline at end of file diff --git a/docker/blocks/mysql_tests/dashboard.json b/docker/blocks/mysql_tests/dashboard.json index 3ab08a7da35..53f313315bd 100644 --- a/docker/blocks/mysql_tests/dashboard.json +++ b/docker/blocks/mysql_tests/dashboard.json @@ -7,14 +7,6 @@ "type": "datasource", "pluginId": "mysql", "pluginName": "MySQL" - }, - { - "name": "DS_MSSQL_TEST", - "label": "MSSQL Test", - "description": "", - "type": "datasource", - "pluginId": "mssql", - "pluginName": "Microsoft SQL Server" } ], "__requires": [ @@ -30,12 +22,6 @@ "name": "Graph", "version": "5.0.0" }, - { - "type": "datasource", - "id": "mssql", - "name": "Microsoft SQL Server", - "version": "1.0.0" - }, { "type": "datasource", "id": "mysql", @@ -114,7 +100,7 @@ "gnetId": null, "graphTooltip": 0, "id": null, - "iteration": 1521715720483, + "iteration": 1523320712115, "links": [], "panels": [ { @@ -349,7 +335,7 @@ { "alias": "Time", "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "time_sec", + "pattern": "time", "type": "date" }, { @@ -457,7 +443,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -536,7 +526,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -615,7 +609,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -694,7 +692,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -773,7 +775,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -852,7 +858,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -941,7 +951,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1034,7 +1048,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1123,7 +1141,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1204,7 +1226,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1293,7 +1319,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1374,7 +1404,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1463,7 +1497,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1544,7 +1582,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1634,14 +1676,18 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, "bars": true, "dashLength": 10, "dashes": false, - "datasource": "${DS_MSSQL_TEST}", + "datasource": "${DS_MYSQL_TEST}", "fill": 1, "gridPos": { "h": 8, @@ -1717,7 +1763,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1807,7 +1857,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1890,7 +1944,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1980,7 +2038,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2063,7 +2125,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2153,7 +2219,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2236,7 +2306,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } } ], "refresh": false, @@ -2315,8 +2389,8 @@ ] }, "time": { - "from": "2018-03-15T11:30:00.000Z", - "to": "2018-03-15T12:55:01.000Z" + "from": "2018-03-15T12:30:00.000Z", + "to": "2018-03-15T13:55:01.000Z" }, "timepicker": { "refresh_intervals": [ @@ -2346,5 +2420,5 @@ "timezone": "", "title": "MySQL Data Source Test", "uid": "Hmf8FDkmz", - "version": 9 + "version": 12 } \ No newline at end of file diff --git a/docker/blocks/mysql_tests/docker-compose.yaml b/docker/blocks/mysql_tests/docker-compose.yaml index 3c59b66b5ac..035a6167017 100644 --- a/docker/blocks/mysql_tests/docker-compose.yaml +++ b/docker/blocks/mysql_tests/docker-compose.yaml @@ -1,5 +1,6 @@ mysqltests: - image: mysql:latest + build: + context: blocks/mysql_tests environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: grafana_tests @@ -7,7 +8,4 @@ MYSQL_PASSWORD: password ports: - "3306:3306" - volumes: - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro tmpfs: /var/lib/mysql:rw diff --git a/docker/blocks/mysql_tests/setup.sql b/docker/blocks/mysql_tests/setup.sql new file mode 100644 index 00000000000..be917a1c542 --- /dev/null +++ b/docker/blocks/mysql_tests/setup.sql @@ -0,0 +1,2 @@ +CREATE DATABASE grafana_ds_tests; +GRANT ALL PRIVILEGES ON grafana_ds_tests.* TO 'grafana'; diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 6aace350193..21069eab01a 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -258,7 +258,7 @@ func InitTestDB(t *testing.T) *xorm.Engine { // x.ShowSQL() if err != nil { - t.Fatalf("Failed to init in memory sqllite3 db %v", err) + t.Fatalf("Failed to init test database: %v", err) } sqlutil.CleanDB(x) @@ -269,3 +269,12 @@ func InitTestDB(t *testing.T) *xorm.Engine { return x } + +func IsTestDbMySql() bool { + if db, present := os.LookupEnv("GRAFANA_TEST_DB"); present { + return db == dbMySql + } + + return false +} + diff --git a/pkg/tsdb/mysql/mysql_test.go b/pkg/tsdb/mysql/mysql_test.go index 750704c9965..b8d7fc8d42b 100644 --- a/pkg/tsdb/mysql/mysql_test.go +++ b/pkg/tsdb/mysql/mysql_test.go @@ -3,25 +3,35 @@ package mysql import ( "fmt" "math/rand" + "strings" "testing" "time" "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore/sqlutil" "github.com/grafana/grafana/pkg/tsdb" . "github.com/smartystreets/goconvey/convey" ) -// To run this test, remove the Skip from SkipConvey -// and set up a MySQL db named grafana_tests and a user/password grafana/password +// To run this test, set runMySqlTests=true +// and set up a MySQL db named grafana_ds_tests and a user/password grafana/password // Use the docker/blocks/mysql_tests/docker-compose.yaml to spin up a // preconfigured MySQL server suitable for running these tests. // Thers's also a dashboard.json in same directory that you can import to Grafana // once you've created a datasource for the test server/database. func TestMySQL(t *testing.T) { - SkipConvey("MySQL", t, func() { + // change to true to run the MySQL tests + runMySqlTests := false + // runMySqlTests := true + + if !(sqlstore.IsTestDbMySql() || runMySqlTests) { + t.Skip() + } + + Convey("MySQL", t, func() { x := InitMySQLTestDB(t) endpoint := &MysqlQueryEndpoint{ @@ -35,7 +45,7 @@ func TestMySQL(t *testing.T) { sess := x.NewSession() defer sess.Close() - fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.Local) + fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC) Convey("Given a table with different native data types", func() { if exists, err := sess.IsTableExist("mysql_types"); err != nil || exists { @@ -121,9 +131,8 @@ func TestMySQL(t *testing.T) { So(column[7].(float64), ShouldEqual, 1.11) So(column[8].(float64), ShouldEqual, 2.22) So(*column[9].(*float32), ShouldEqual, 3.33) - _, offset := time.Now().Zone() - So(column[10].(time.Time), ShouldHappenWithin, time.Duration(10*time.Second), time.Now().Add(time.Duration(offset)*time.Second)) - So(column[11].(time.Time), ShouldHappenWithin, time.Duration(10*time.Second), time.Now().Add(time.Duration(offset)*time.Second)) + So(column[10].(time.Time), ShouldHappenWithin, time.Duration(10*time.Second), time.Now()) + So(column[11].(time.Time), ShouldHappenWithin, time.Duration(10*time.Second), time.Now()) So(column[12].(string), ShouldEqual, "11:11:11") So(column[13].(int64), ShouldEqual, 2018) So(*column[14].(*[]byte), ShouldHaveSameTypeAs, []byte{1}) @@ -137,8 +146,7 @@ func TestMySQL(t *testing.T) { So(column[22].(string), ShouldEqual, "longblob") So(column[23].(string), ShouldEqual, "val2") So(column[24].(string), ShouldEqual, "a,b") - So(column[25].(time.Time).Format("2006-01-02T00:00:00Z"), ShouldEqual, time.Now().Format("2006-01-02T00:00:00Z")) - So(column[26].(float64), ShouldEqual, float64(1514764861000)) + So(column[25].(time.Time).Format("2006-01-02T00:00:00Z"), ShouldEqual, time.Now().UTC().Format("2006-01-02T00:00:00Z")) So(column[27], ShouldEqual, nil) So(column[28], ShouldEqual, nil) So(column[29], ShouldEqual, "") @@ -647,16 +655,16 @@ func TestMySQL(t *testing.T) { } func InitMySQLTestDB(t *testing.T) *xorm.Engine { - x, err := xorm.NewEngine(sqlutil.TestDB_Mysql.DriverName, sqlutil.TestDB_Mysql.ConnStr+"&parseTime=true") - x.DatabaseTZ = time.Local - x.TZLocation = time.Local - - // x.ShowSQL() - + x, err := xorm.NewEngine(sqlutil.TestDB_Mysql.DriverName, strings.Replace(sqlutil.TestDB_Mysql.ConnStr, "/grafana_tests", "/grafana_ds_tests", 1)) if err != nil { t.Fatalf("Failed to init mysql db %v", err) } + x.DatabaseTZ = time.UTC + x.TZLocation = time.UTC + + // x.ShowSQL() + return x } From af626466240fac57130bbb608d9db78b8816e4ed Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 10 Apr 2018 11:01:43 +0200 Subject: [PATCH 088/145] mysql: fix precision for time columns in time series query mode --- pkg/tsdb/mysql/mysql.go | 14 +- pkg/tsdb/mysql/mysql_test.go | 295 ++++++++++++++++++++++++++++++++--- 2 files changed, 279 insertions(+), 30 deletions(-) diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go index 483974c55a4..83027d4b210 100644 --- a/pkg/tsdb/mysql/mysql.go +++ b/pkg/tsdb/mysql/mysql.go @@ -8,7 +8,6 @@ import ( "math" "reflect" "strconv" - "time" "github.com/go-sql-driver/mysql" "github.com/go-xorm/core" @@ -239,15 +238,18 @@ func (e MysqlQueryEndpoint) transformToTimeSeries(query *tsdb.Query, rows *core. return err } + // converts column named time to unix timestamp in milliseconds to make + // native mysql datetime types and epoch dates work in + // annotation and table queries. + tsdb.ConvertSqlTimeColumnToEpochMs(values, timeIndex) + switch columnValue := values[timeIndex].(type) { case int64: - timestamp = float64(columnValue * 1000) + timestamp = float64(columnValue) case float64: - timestamp = columnValue * 1000 - case time.Time: - timestamp = float64(columnValue.UnixNano() / 1e6) + timestamp = columnValue default: - return fmt.Errorf("Invalid type for column time, must be of type timestamp or unix timestamp, got: %T %v", columnValue, columnValue) + return fmt.Errorf("Invalid type for column time/time_sec, must be of type timestamp or unix timestamp, got: %T %v", columnValue, columnValue) } if metricIndex >= 0 { diff --git a/pkg/tsdb/mysql/mysql_test.go b/pkg/tsdb/mysql/mysql_test.go index b8d7fc8d42b..530c8fb6c8a 100644 --- a/pkg/tsdb/mysql/mysql_test.go +++ b/pkg/tsdb/mysql/mysql_test.go @@ -147,6 +147,7 @@ func TestMySQL(t *testing.T) { So(column[23].(string), ShouldEqual, "val2") So(column[24].(string), ShouldEqual, "a,b") So(column[25].(time.Time).Format("2006-01-02T00:00:00Z"), ShouldEqual, time.Now().UTC().Format("2006-01-02T00:00:00Z")) + So(column[26].(float64), ShouldEqual, float64(1.514764861123456*1e12)) So(column[27], ShouldEqual, nil) So(column[28], ShouldEqual, nil) So(column[29], ShouldEqual, "") @@ -185,10 +186,8 @@ func TestMySQL(t *testing.T) { }) } - for _, s := range series { - _, err = sess.Insert(s) - So(err, ShouldBeNil) - } + _, err = sess.InsertMulti(series) + So(err, ShouldBeNil) Convey("When doing a metric query using timeGroup", func() { query := &tsdb.TsdbQuery{ @@ -309,10 +308,19 @@ func TestMySQL(t *testing.T) { Convey("Given a table with metrics having multiple values and measurements", func() { type metric_values struct { - Time time.Time - Measurement string - ValueOne int64 `xorm:"integer 'valueOne'"` - ValueTwo int64 `xorm:"integer 'valueTwo'"` + Time time.Time `xorm:"datetime 'time' not null"` + TimeNullable *time.Time `xorm:"datetime 'timeNullable' null"` + TimeInt64 int64 `xorm:"bigint(20) 'timeInt64' not null"` + TimeInt64Nullable *int64 `xorm:"bigint(20) 'timeInt64Nullable' null"` + TimeFloat64 float64 `xorm:"double 'timeFloat64' not null"` + TimeFloat64Nullable *float64 `xorm:"double 'timeFloat64Nullable' null"` + TimeInt32 int32 `xorm:"int(11) 'timeInt32' not null"` + TimeInt32Nullable *int32 `xorm:"int(11) 'timeInt32Nullable' null"` + TimeFloat32 float32 `xorm:"double 'timeFloat32' not null"` + TimeFloat32Nullable *float32 `xorm:"double 'timeFloat32Nullable' null"` + Measurement string + ValueOne int64 `xorm:"integer 'valueOne'"` + ValueTwo int64 `xorm:"integer 'valueTwo'"` } if exist, err := sess.IsTableExist(metric_values{}); err != nil || exist { @@ -327,26 +335,265 @@ func TestMySQL(t *testing.T) { return rand.Int63n(max-min) + min } + var tInitial time.Time + series := []*metric_values{} - for _, t := range genTimeRangeByInterval(fromStart.Add(-30*time.Minute), 90*time.Minute, 5*time.Minute) { - series = append(series, &metric_values{ - Time: t, - Measurement: "Metric A", - ValueOne: rnd(0, 100), - ValueTwo: rnd(0, 100), - }) - series = append(series, &metric_values{ - Time: t, - Measurement: "Metric B", - ValueOne: rnd(0, 100), - ValueTwo: rnd(0, 100), - }) + for i, t := range genTimeRangeByInterval(fromStart.Add(-30*time.Minute), 90*time.Minute, 5*time.Minute) { + if i == 0 { + tInitial = t + } + tSeconds := t.Unix() + tSecondsInt32 := int32(tSeconds) + tSecondsFloat32 := float32(tSeconds) + tMilliseconds := tSeconds * 1e3 + tMillisecondsFloat := float64(tMilliseconds) + t2 := t + first := metric_values{ + Time: t, + TimeNullable: &t2, + TimeInt64: tMilliseconds, + TimeInt64Nullable: &(tMilliseconds), + TimeFloat64: tMillisecondsFloat, + TimeFloat64Nullable: &tMillisecondsFloat, + TimeInt32: tSecondsInt32, + TimeInt32Nullable: &tSecondsInt32, + TimeFloat32: tSecondsFloat32, + TimeFloat32Nullable: &tSecondsFloat32, + Measurement: "Metric A", + ValueOne: rnd(0, 100), + ValueTwo: rnd(0, 100), + } + second := first + second.Measurement = "Metric B" + second.ValueOne = rnd(0, 100) + second.ValueTwo = rnd(0, 100) + + series = append(series, &first) + series = append(series, &second) } - for _, s := range series { - _, err := sess.Insert(s) + _, err = sess.InsertMulti(series) + So(err, ShouldBeNil) + + Convey("When doing a metric query using time as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) So(err, ShouldBeNil) - } + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using time (nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT timeNullable as time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int64) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT timeInt64 as time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int64 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT timeInt64Nullable as time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (float64) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT timeFloat64 as time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (float64 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT timeFloat64Nullable as time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int32) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT timeInt32 as time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int32 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT timeInt32Nullable as time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (float32) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT timeFloat32 as time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + }) + + Convey("When doing a metric query using epoch (float32 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT timeFloat32Nullable as time, valueOne FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + }) Convey("When doing a metric query grouping by time and select metric column should return correct series", func() { query := &tsdb.TsdbQuery{ From 1783c534fd45cc8a679b262b0c7245404ae4c880 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 10 Apr 2018 11:03:58 +0200 Subject: [PATCH 089/145] postgres: fix precision for time columns in time series query mode --- pkg/tsdb/postgres/postgres.go | 12 +- pkg/tsdb/postgres/postgres_test.go | 244 ++++++++++++++++++++++++++--- 2 files changed, 229 insertions(+), 27 deletions(-) diff --git a/pkg/tsdb/postgres/postgres.go b/pkg/tsdb/postgres/postgres.go index 5f6b56ebcf1..e17cc783f38 100644 --- a/pkg/tsdb/postgres/postgres.go +++ b/pkg/tsdb/postgres/postgres.go @@ -7,7 +7,6 @@ import ( "math" "net/url" "strconv" - "time" "github.com/go-xorm/core" "github.com/grafana/grafana/pkg/components/null" @@ -219,13 +218,16 @@ func (e PostgresQueryEndpoint) transformToTimeSeries(query *tsdb.Query, rows *co return err } + // converts column named time to unix timestamp in milliseconds to make + // native mysql datetime types and epoch dates work in + // annotation and table queries. + tsdb.ConvertSqlTimeColumnToEpochMs(values, timeIndex) + switch columnValue := values[timeIndex].(type) { case int64: - timestamp = float64(columnValue * 1000) + timestamp = float64(columnValue) case float64: - timestamp = columnValue * 1000 - case time.Time: - timestamp = float64(columnValue.UnixNano() / 1e6) + timestamp = columnValue default: return fmt.Errorf("Invalid type for column time, must be of type timestamp or unix timestamp, got: %T %v", columnValue, columnValue) } diff --git a/pkg/tsdb/postgres/postgres_test.go b/pkg/tsdb/postgres/postgres_test.go index 3f2203ac7a4..43e8419a329 100644 --- a/pkg/tsdb/postgres/postgres_test.go +++ b/pkg/tsdb/postgres/postgres_test.go @@ -156,8 +156,7 @@ func TestPostgres(t *testing.T) { }) } - for _, s := range series { - _, err = sess.Insert(s) + _, err = sess.InsertMulti(series) So(err, ShouldBeNil) } @@ -280,10 +279,18 @@ func TestPostgres(t *testing.T) { Convey("Given a table with metrics having multiple values and measurements", func() { type metric_values struct { - Time time.Time - Measurement string - ValueOne int64 `xorm:"integer 'valueOne'"` - ValueTwo int64 `xorm:"integer 'valueTwo'"` + Time time.Time + TimeInt64 int64 `xorm:"bigint 'timeInt64' not null"` + TimeInt64Nullable *int64 `xorm:"bigint 'timeInt64Nullable' null"` + TimeFloat64 float64 `xorm:"double 'timeFloat64' not null"` + TimeFloat64Nullable *float64 `xorm:"double 'timeFloat64Nullable' null"` + TimeInt32 int32 `xorm:"int(11) 'timeInt32' not null"` + TimeInt32Nullable *int32 `xorm:"int(11) 'timeInt32Nullable' null"` + TimeFloat32 float32 `xorm:"double 'timeFloat32' not null"` + TimeFloat32Nullable *float32 `xorm:"double 'timeFloat32Nullable' null"` + Measurement string + ValueOne int64 `xorm:"integer 'valueOne'"` + ValueTwo int64 `xorm:"integer 'valueTwo'"` } if exist, err := sess.IsTableExist(metric_values{}); err != nil || exist { @@ -298,27 +305,220 @@ func TestPostgres(t *testing.T) { return rand.Int63n(max-min) + min } + var tInitial time.Time + series := []*metric_values{} - for _, t := range genTimeRangeByInterval(fromStart.Add(-30*time.Minute), 90*time.Minute, 5*time.Minute) { - series = append(series, &metric_values{ - Time: t, - Measurement: "Metric A", - ValueOne: rnd(0, 100), - ValueTwo: rnd(0, 100), - }) - series = append(series, &metric_values{ - Time: t, - Measurement: "Metric B", - ValueOne: rnd(0, 100), - ValueTwo: rnd(0, 100), - }) + for i, t := range genTimeRangeByInterval(fromStart.Add(-30*time.Minute), 90*time.Minute, 5*time.Minute) { + if i == 0 { + tInitial = t + } + tSeconds := t.Unix() + tSecondsInt32 := int32(tSeconds) + tSecondsFloat32 := float32(tSeconds) + tMilliseconds := tSeconds * 1e3 + tMillisecondsFloat := float64(tMilliseconds) + first := metric_values{ + Time: t, + TimeInt64: tMilliseconds, + TimeInt64Nullable: &(tMilliseconds), + TimeFloat64: tMillisecondsFloat, + TimeFloat64Nullable: &tMillisecondsFloat, + TimeInt32: tSecondsInt32, + TimeInt32Nullable: &tSecondsInt32, + TimeFloat32: tSecondsFloat32, + TimeFloat32Nullable: &tSecondsFloat32, + Measurement: "Metric A", + ValueOne: rnd(0, 100), + ValueTwo: rnd(0, 100), + } + second := first + second.Measurement = "Metric B" + second.ValueOne = rnd(0, 100) + second.ValueTwo = rnd(0, 100) + + series = append(series, &first) + series = append(series, &second) } - for _, s := range series { - _, err := sess.Insert(s) + _, err = sess.InsertMulti(series) + So(err, ShouldBeNil) + + Convey("When doing a metric query using epoch (int64) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT "timeInt64" as time, "valueOne" FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int64 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT "timeInt64Nullable" as time, "valueOne" FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (float64) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT "timeFloat64" as time, "valueOne" FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (float64 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT "timeFloat64Nullable" as time, "valueOne" FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int32) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT "timeInt32" as time, "valueOne" FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int32 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT "timeInt32Nullable" as time, "valueOne" FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (float32) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT "timeFloat32" as time, "valueOne" FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, } + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + }) + + Convey("When doing a metric query using epoch (float32 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT "timeFloat32Nullable" as time, "valueOne" FROM metric_values ORDER BY time LIMIT 1`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + }) + Convey("When doing a metric query grouping by time and select metric column should return correct series", func() { query := &tsdb.TsdbQuery{ Queries: []*tsdb.Query{ @@ -473,7 +673,7 @@ func TestPostgres(t *testing.T) { columns := queryResult.Tables[0].Rows[0] //Should be in milliseconds - So(columns[0].(float64), ShouldEqual, float64(dt.Unix()*1000)) + So(columns[0].(float64), ShouldEqual, float64(dt.UnixNano()/1e6)) }) Convey("When doing an annotation query with a time column in epoch second format should return ms", func() { From 0317ecbf0d8b595ec2ec08f05df4d2d2128b406f Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 10 Apr 2018 11:08:30 +0200 Subject: [PATCH 090/145] postgres: support running multiple postgres integration tests Makes it possible to run mysql integration tests for both grafana config db and tsdb at the same time using GRAFANA_TEST_DB=postgres go test ./pkg/... --- docker/blocks/postgres_tests/Dockerfile | 3 + docker/blocks/postgres_tests/dashboard.json | 134 +++++++++++++++--- .../blocks/postgres_tests/docker-compose.yaml | 3 +- docker/blocks/postgres_tests/setup.sql | 3 + pkg/services/sqlstore/sqlstore.go | 7 + pkg/tsdb/postgres/postgres_test.go | 37 +++-- 6 files changed, 149 insertions(+), 38 deletions(-) create mode 100644 docker/blocks/postgres_tests/Dockerfile create mode 100644 docker/blocks/postgres_tests/setup.sql diff --git a/docker/blocks/postgres_tests/Dockerfile b/docker/blocks/postgres_tests/Dockerfile new file mode 100644 index 00000000000..afe4d199651 --- /dev/null +++ b/docker/blocks/postgres_tests/Dockerfile @@ -0,0 +1,3 @@ +FROM postgres:latest +ADD setup.sql /docker-entrypoint-initdb.d +CMD ["postgres"] \ No newline at end of file diff --git a/docker/blocks/postgres_tests/dashboard.json b/docker/blocks/postgres_tests/dashboard.json index eea95863716..9efbe90bdfe 100644 --- a/docker/blocks/postgres_tests/dashboard.json +++ b/docker/blocks/postgres_tests/dashboard.json @@ -100,7 +100,7 @@ "gnetId": null, "graphTooltip": 0, "id": null, - "iteration": 1521725946837, + "iteration": 1523320929325, "links": [], "panels": [ { @@ -443,7 +443,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -522,7 +526,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -601,7 +609,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -680,7 +692,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -759,7 +775,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -838,7 +858,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -927,7 +951,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1008,7 +1036,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1097,7 +1129,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1178,7 +1214,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1267,7 +1307,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1348,7 +1392,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1437,7 +1485,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1518,7 +1570,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1608,7 +1664,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1691,7 +1751,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1781,7 +1845,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1864,7 +1932,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1954,7 +2026,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2037,7 +2113,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2127,7 +2207,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2210,7 +2294,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } } ], "refresh": false, diff --git a/docker/blocks/postgres_tests/docker-compose.yaml b/docker/blocks/postgres_tests/docker-compose.yaml index 44b66e8e558..f5ce0a5a3d3 100644 --- a/docker/blocks/postgres_tests/docker-compose.yaml +++ b/docker/blocks/postgres_tests/docker-compose.yaml @@ -1,5 +1,6 @@ postgrestest: - image: postgres:latest + build: + context: blocks/postgres_tests environment: POSTGRES_USER: grafanatest POSTGRES_PASSWORD: grafanatest diff --git a/docker/blocks/postgres_tests/setup.sql b/docker/blocks/postgres_tests/setup.sql new file mode 100644 index 00000000000..b182b7c292d --- /dev/null +++ b/docker/blocks/postgres_tests/setup.sql @@ -0,0 +1,3 @@ +CREATE DATABASE grafanadstest; +REVOKE CONNECT ON DATABASE grafanadstest FROM PUBLIC; +GRANT CONNECT ON DATABASE grafanadstest TO grafanatest; \ No newline at end of file diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 21069eab01a..782318fa188 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -278,3 +278,10 @@ func IsTestDbMySql() bool { return false } +func IsTestDbPostgres() bool { + if db, present := os.LookupEnv("GRAFANA_TEST_DB"); present { + return db == dbPostgres + } + + return false +} diff --git a/pkg/tsdb/postgres/postgres_test.go b/pkg/tsdb/postgres/postgres_test.go index 43e8419a329..d35ba2b3209 100644 --- a/pkg/tsdb/postgres/postgres_test.go +++ b/pkg/tsdb/postgres/postgres_test.go @@ -3,26 +3,36 @@ package postgres import ( "fmt" "math/rand" + "strings" "testing" "time" "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore/sqlutil" "github.com/grafana/grafana/pkg/tsdb" _ "github.com/lib/pq" . "github.com/smartystreets/goconvey/convey" ) -// To run this test, remove the Skip from SkipConvey -// and set up a PostgreSQL db named grafanatest and a user/password grafanatest/grafanatest! +// To run this test, set runMySqlTests=true +// and set up a PostgreSQL db named grafanadstest and a user/password grafanatest/grafanatest! // Use the docker/blocks/postgres_tests/docker-compose.yaml to spin up a // preconfigured Postgres server suitable for running these tests. // Thers's also a dashboard.json in same directory that you can import to Grafana // once you've created a datasource for the test server/database. func TestPostgres(t *testing.T) { - SkipConvey("PostgreSQL", t, func() { + // change to true to run the MySQL tests + runPostgresTests := false + // runPostgresTests := true + + if !(sqlstore.IsTestDbPostgres() || runPostgresTests) { + t.Skip() + } + + Convey("PostgreSQL", t, func() { x := InitPostgresTestDB(t) endpoint := &PostgresQueryEndpoint{ @@ -157,8 +167,7 @@ func TestPostgres(t *testing.T) { } _, err = sess.InsertMulti(series) - So(err, ShouldBeNil) - } + So(err, ShouldBeNil) Convey("When doing a metric query using timeGroup", func() { query := &tsdb.TsdbQuery{ @@ -451,7 +460,7 @@ func TestPostgres(t *testing.T) { So(len(queryResult.Series), ShouldEqual, 1) So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) - }) + }) Convey("When doing a metric query using epoch (int32 nullable) as time column should return metric with time in milliseconds", func() { query := &tsdb.TsdbQuery{ @@ -473,7 +482,7 @@ func TestPostgres(t *testing.T) { So(len(queryResult.Series), ShouldEqual, 1) So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) - }) + }) Convey("When doing a metric query using epoch (float32) as time column should return metric with time in milliseconds", func() { query := &tsdb.TsdbQuery{ @@ -486,7 +495,7 @@ func TestPostgres(t *testing.T) { RefId: "A", }, }, - } + } resp, err := endpoint.Query(nil, nil, query) So(err, ShouldBeNil) @@ -508,7 +517,7 @@ func TestPostgres(t *testing.T) { RefId: "A", }, }, - } + } resp, err := endpoint.Query(nil, nil, query) So(err, ShouldBeNil) @@ -826,16 +835,16 @@ func TestPostgres(t *testing.T) { } func InitPostgresTestDB(t *testing.T) *xorm.Engine { - x, err := xorm.NewEngine(sqlutil.TestDB_Postgres.DriverName, sqlutil.TestDB_Postgres.ConnStr) + x, err := xorm.NewEngine(sqlutil.TestDB_Postgres.DriverName, strings.Replace(sqlutil.TestDB_Postgres.ConnStr, "dbname=grafanatest", "dbname=grafanadstest", 1)) + if err != nil { + t.Fatalf("Failed to init postgres db %v", err) + } + x.DatabaseTZ = time.UTC x.TZLocation = time.UTC // x.ShowSQL() - if err != nil { - t.Fatalf("Failed to init postgres db %v", err) - } - return x } From 9d84e6f31f8c828564cf04874b8ce2d88294588d Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 10 Apr 2018 11:10:56 +0200 Subject: [PATCH 091/145] mssql: fix precision for time columns in time series query mode --- docker/blocks/mssql_tests/dashboard.json | 148 ++++++++++--- pkg/tsdb/mssql/mssql.go | 15 +- pkg/tsdb/mssql/mssql_test.go | 258 ++++++++++++++++++++--- 3 files changed, 358 insertions(+), 63 deletions(-) diff --git a/docker/blocks/mssql_tests/dashboard.json b/docker/blocks/mssql_tests/dashboard.json index 20e3907b48b..80994254093 100644 --- a/docker/blocks/mssql_tests/dashboard.json +++ b/docker/blocks/mssql_tests/dashboard.json @@ -100,7 +100,7 @@ "gnetId": null, "graphTooltip": 0, "id": null, - "iteration": 1521715844826, + "iteration": 1523320861623, "links": [], "panels": [ { @@ -443,7 +443,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -522,7 +526,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -601,7 +609,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -680,7 +692,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -759,7 +775,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -838,7 +858,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -927,7 +951,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1026,7 +1054,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1115,7 +1147,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1196,7 +1232,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1285,7 +1325,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1366,7 +1410,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1455,7 +1503,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1536,7 +1588,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1619,7 +1675,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1702,7 +1762,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1792,7 +1856,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1875,7 +1943,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1965,7 +2037,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2048,7 +2124,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2138,7 +2218,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2221,7 +2305,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2311,7 +2399,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -2394,7 +2486,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } } ], "refresh": false, @@ -2504,5 +2600,5 @@ "timezone": "", "title": "Microsoft SQL Server Data Source Test", "uid": "GlAqcPgmz", - "version": 57 + "version": 58 } \ No newline at end of file diff --git a/pkg/tsdb/mssql/mssql.go b/pkg/tsdb/mssql/mssql.go index 2638fd8bb40..3a440859f9f 100644 --- a/pkg/tsdb/mssql/mssql.go +++ b/pkg/tsdb/mssql/mssql.go @@ -8,8 +8,6 @@ import ( "strconv" "strings" - "time" - "math" _ "github.com/denisenkom/go-mssqldb" @@ -231,15 +229,18 @@ func (e MssqlQueryEndpoint) transformToTimeSeries(query *tsdb.Query, rows *core. return err } + // converts column named time to unix timestamp in milliseconds to make + // native mysql datetime types and epoch dates work in + // annotation and table queries. + tsdb.ConvertSqlTimeColumnToEpochMs(values, timeIndex) + switch columnValue := values[timeIndex].(type) { case int64: - timestamp = float64(columnValue * 1000) + timestamp = float64(columnValue) case float64: - timestamp = columnValue * 1000 - case time.Time: - timestamp = (float64(columnValue.Unix()) * 1000) + float64(columnValue.Nanosecond()/1e6) // in case someone is trying to map times beyond 2262 :D + timestamp = columnValue default: - return fmt.Errorf("Invalid type for column time, must be of type timestamp or unix timestamp") + return fmt.Errorf("Invalid type for column time, must be of type timestamp or unix timestamp, got: %T %v", columnValue, columnValue) } if metricIndex >= 0 { diff --git a/pkg/tsdb/mssql/mssql_test.go b/pkg/tsdb/mssql/mssql_test.go index 4bd1e3a8ad7..dc527d09bd9 100644 --- a/pkg/tsdb/mssql/mssql_test.go +++ b/pkg/tsdb/mssql/mssql_test.go @@ -188,10 +188,8 @@ func TestMSSQL(t *testing.T) { }) } - for _, s := range series { - _, err = sess.Insert(s) - So(err, ShouldBeNil) - } + _, err = sess.InsertMulti(series) + So(err, ShouldBeNil) Convey("When doing a metric query using timeGroup", func() { query := &tsdb.TsdbQuery{ @@ -312,10 +310,18 @@ func TestMSSQL(t *testing.T) { Convey("Given a table with metrics having multiple values and measurements", func() { type metric_values struct { - Time time.Time - Measurement string - ValueOne int64 `xorm:"integer 'valueOne'"` - ValueTwo int64 `xorm:"integer 'valueTwo'"` + Time time.Time + TimeInt64 int64 `xorm:"bigint 'timeInt64' not null"` + TimeInt64Nullable *int64 `xorm:"bigint 'timeInt64Nullable' null"` + TimeFloat64 float64 `xorm:"float 'timeFloat64' not null"` + TimeFloat64Nullable *float64 `xorm:"float 'timeFloat64Nullable' null"` + TimeInt32 int32 `xorm:"int(11) 'timeInt32' not null"` + TimeInt32Nullable *int32 `xorm:"int(11) 'timeInt32Nullable' null"` + TimeFloat32 float32 `xorm:"float(11) 'timeFloat32' not null"` + TimeFloat32Nullable *float32 `xorm:"float(11) 'timeFloat32Nullable' null"` + Measurement string + ValueOne int64 `xorm:"integer 'valueOne'"` + ValueTwo int64 `xorm:"integer 'valueTwo'"` } if exist, err := sess.IsTableExist(metric_values{}); err != nil || exist { @@ -330,26 +336,219 @@ func TestMSSQL(t *testing.T) { return rand.Int63n(max-min) + min } + var tInitial time.Time + series := []*metric_values{} - for _, t := range genTimeRangeByInterval(fromStart.Add(-30*time.Minute), 90*time.Minute, 5*time.Minute) { - series = append(series, &metric_values{ - Time: t, - Measurement: "Metric A", - ValueOne: rnd(0, 100), - ValueTwo: rnd(0, 100), - }) - series = append(series, &metric_values{ - Time: t, - Measurement: "Metric B", - ValueOne: rnd(0, 100), - ValueTwo: rnd(0, 100), - }) + for i, t := range genTimeRangeByInterval(fromStart.Add(-30*time.Minute), 90*time.Minute, 5*time.Minute) { + if i == 0 { + tInitial = t + } + tSeconds := t.Unix() + tSecondsInt32 := int32(tSeconds) + tSecondsFloat32 := float32(tSeconds) + tMilliseconds := tSeconds * 1e3 + tMillisecondsFloat := float64(tMilliseconds) + first := metric_values{ + Time: t, + TimeInt64: tMilliseconds, + TimeInt64Nullable: &(tMilliseconds), + TimeFloat64: tMillisecondsFloat, + TimeFloat64Nullable: &tMillisecondsFloat, + TimeInt32: tSecondsInt32, + TimeInt32Nullable: &tSecondsInt32, + TimeFloat32: tSecondsFloat32, + TimeFloat32Nullable: &tSecondsFloat32, + Measurement: "Metric A", + ValueOne: rnd(0, 100), + ValueTwo: rnd(0, 100), + } + second := first + second.Measurement = "Metric B" + second.ValueOne = rnd(0, 100) + second.ValueTwo = rnd(0, 100) + + series = append(series, &first) + series = append(series, &second) } - for _, s := range series { - _, err = sess.Insert(s) + _, err = sess.InsertMulti(series) + So(err, ShouldBeNil) + + Convey("When doing a metric query using epoch (int64) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT TOP 1 timeInt64 as time, valueOne FROM metric_values ORDER BY time`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) So(err, ShouldBeNil) - } + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int64 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT TOP 1 timeInt64Nullable as time, valueOne FROM metric_values ORDER BY time`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (float64) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT TOP 1 timeFloat64 as time, valueOne FROM metric_values ORDER BY time`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (float64 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT TOP 1 timeFloat64Nullable as time, valueOne FROM metric_values ORDER BY time`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int32) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT TOP 1 timeInt32 as time, valueOne FROM metric_values ORDER BY time`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (int32 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT TOP 1 timeInt32Nullable as time, valueOne FROM metric_values ORDER BY time`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(tInitial.UnixNano()/1e6)) + }) + + Convey("When doing a metric query using epoch (float32) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT TOP 1 timeFloat32 as time, valueOne FROM metric_values ORDER BY time`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + }) + + Convey("When doing a metric query using epoch (float32 nullable) as time column should return metric with time in milliseconds", func() { + query := &tsdb.TsdbQuery{ + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "rawSql": `SELECT TOP 1 timeFloat32Nullable as time, valueOne FROM metric_values ORDER BY time`, + "format": "time_series", + }), + RefId: "A", + }, + }, + } + + resp, err := endpoint.Query(nil, nil, query) + So(err, ShouldBeNil) + queryResult := resp.Results["A"] + So(queryResult.Error, ShouldBeNil) + + So(len(queryResult.Series), ShouldEqual, 1) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + }) Convey("When doing a metric query grouping by time and select metric column should return correct series", func() { query := &tsdb.TsdbQuery{ @@ -476,7 +675,6 @@ func TestMSSQL(t *testing.T) { resp, err := endpoint.Query(nil, nil, query) queryResult := resp.Results["A"] So(err, ShouldBeNil) - fmt.Println("query", "sql", queryResult.Meta) So(queryResult.Error, ShouldBeNil) So(len(queryResult.Series), ShouldEqual, 4) @@ -696,7 +894,7 @@ func TestMSSQL(t *testing.T) { columns := queryResult.Tables[0].Rows[0] //Should be in milliseconds - So(columns[0].(float64), ShouldEqual, float64(dt.Unix()*1000)) + So(columns[0].(float64), ShouldEqual, float64(dt.UnixNano()/1e6)) }) Convey("When doing an annotation query with a time column in epoch second format should return ms", func() { @@ -850,15 +1048,15 @@ func TestMSSQL(t *testing.T) { func InitMSSQLTestDB(t *testing.T) *xorm.Engine { x, err := xorm.NewEngine(sqlutil.TestDB_Mssql.DriverName, strings.Replace(sqlutil.TestDB_Mssql.ConnStr, "localhost", serverIP, 1)) + if err != nil { + t.Fatalf("Failed to init mssql db %v", err) + } + x.DatabaseTZ = time.UTC x.TZLocation = time.UTC // x.ShowSQL() - if err != nil { - t.Fatalf("Failed to init mssql db %v", err) - } - return x } From 5f67d4268d68c4a3469bff97f5535480ba898405 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 10 Apr 2018 15:48:51 +0200 Subject: [PATCH 092/145] added @ngInject --- public/app/core/directives/value_select_dropdown.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/app/core/directives/value_select_dropdown.ts b/public/app/core/directives/value_select_dropdown.ts index 628b1480a4f..78f46b5aa1b 100644 --- a/public/app/core/directives/value_select_dropdown.ts +++ b/public/app/core/directives/value_select_dropdown.ts @@ -17,6 +17,7 @@ export class ValueSelectDropdownCtrl { hide: any; onUpdated: any; + /** @ngInject */ constructor(private $q) {} show() { @@ -236,6 +237,7 @@ export class ValueSelectDropdownCtrl { } } +/** @ngInject */ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) { return { scope: { variable: '=', onUpdated: '&' }, From 97f67ddcb87581f579359ea62f8f510fce9c917a Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 10 Apr 2018 16:36:00 +0200 Subject: [PATCH 093/145] tsdb: improved floating point support when converting sql time column to epoch (ms) --- pkg/tsdb/sql_engine_test.go | 15 +++++++++------ pkg/tsdb/time_range.go | 12 ++++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/pkg/tsdb/sql_engine_test.go b/pkg/tsdb/sql_engine_test.go index f8856489230..4c6951a0196 100644 --- a/pkg/tsdb/sql_engine_test.go +++ b/pkg/tsdb/sql_engine_test.go @@ -1,6 +1,7 @@ package tsdb import ( + "fmt" "testing" "time" @@ -9,7 +10,7 @@ import ( func TestSqlEngine(t *testing.T) { Convey("SqlEngine", t, func() { - dt := time.Date(2018, 3, 14, 21, 20, 6, 527e6, time.UTC) + dt := time.Date(2018, 3, 14, 21, 20, 6, int(527345*time.Microsecond), time.UTC) Convey("Given row values with time.Time as time columns", func() { var nilPointer *time.Time @@ -24,7 +25,7 @@ func TestSqlEngine(t *testing.T) { } Convey("When converting them should return epoch time with millisecond precision ", func() { - expected := float64(dt.UnixNano() / 1e6) + expected := float64(dt.UnixNano()) / float64(time.Millisecond) So(fixtures[0].(float64), ShouldEqual, expected) So(fixtures[1].(float64), ShouldEqual, expected) So(fixtures[2], ShouldBeNil) @@ -132,8 +133,8 @@ func TestSqlEngine(t *testing.T) { }) Convey("Given row values with float64 as time columns", func() { - tSeconds := float64(dt.Unix()) - tMilliseconds := float64(dt.UnixNano() / 1e6) + tSeconds := float64(dt.UnixNano()) / float64(time.Second) + tMilliseconds := float64(dt.UnixNano()) / float64(time.Millisecond) tNanoSeconds := float64(dt.UnixNano()) var nilPointer *float64 @@ -151,10 +152,12 @@ func TestSqlEngine(t *testing.T) { } Convey("When converting them should return epoch time with millisecond precision ", func() { - So(fixtures[0].(float64), ShouldEqual, tSeconds*1e3) - So(fixtures[1].(float64), ShouldEqual, tSeconds*1e3) + So(fixtures[0].(float64), ShouldEqual, tMilliseconds) + So(fixtures[1].(float64), ShouldEqual, tMilliseconds) So(fixtures[2].(float64), ShouldEqual, tMilliseconds) So(fixtures[3].(float64), ShouldEqual, tMilliseconds) + fmt.Println(fixtures[4].(float64)) + fmt.Println(tMilliseconds) So(fixtures[4].(float64), ShouldEqual, tMilliseconds) So(fixtures[5].(float64), ShouldEqual, tMilliseconds) So(fixtures[6], ShouldBeNil) diff --git a/pkg/tsdb/time_range.go b/pkg/tsdb/time_range.go index 47076eb3c6b..ed5b77d61de 100644 --- a/pkg/tsdb/time_range.go +++ b/pkg/tsdb/time_range.go @@ -92,14 +92,14 @@ func (tr *TimeRange) ParseTo() (time.Time, error) { // EpochPrecisionToMs converts epoch precision to millisecond, if needed. // Only seconds to milliseconds supported right now func EpochPrecisionToMs(value float64) float64 { - if int64(value)/1e10 == 0 { - return float64(value * 1e3) + s := strconv.FormatFloat(value, 'e', -1, 64) + if strings.HasSuffix(s, "e+09") { + return value * float64(1e3) } - s := strconv.FormatFloat(value, 'f', -1, 64) - if len(s) == 19 { - return float64(value / 1e6) + if strings.HasSuffix(s, "e+18") { + return value / float64(time.Millisecond) } - return float64(value) + return value } From 6cb891dca84c444362fb97b861894339f299ef20 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 10 Apr 2018 16:37:39 +0200 Subject: [PATCH 094/145] mysql: use a datetime column with microsecond precision in test --- pkg/tsdb/mysql/mysql_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/tsdb/mysql/mysql_test.go b/pkg/tsdb/mysql/mysql_test.go index 530c8fb6c8a..827ebfa9555 100644 --- a/pkg/tsdb/mysql/mysql_test.go +++ b/pkg/tsdb/mysql/mysql_test.go @@ -309,7 +309,7 @@ func TestMySQL(t *testing.T) { Convey("Given a table with metrics having multiple values and measurements", func() { type metric_values struct { Time time.Time `xorm:"datetime 'time' not null"` - TimeNullable *time.Time `xorm:"datetime 'timeNullable' null"` + TimeNullable *time.Time `xorm:"datetime(6) 'timeNullable' null"` TimeInt64 int64 `xorm:"bigint(20) 'timeInt64' not null"` TimeInt64Nullable *int64 `xorm:"bigint(20) 'timeInt64Nullable' null"` TimeFloat64 float64 `xorm:"double 'timeFloat64' not null"` From be4b715aad729c93683223f52351a18811c34110 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 10 Apr 2018 16:59:48 +0200 Subject: [PATCH 095/145] docker: change mysql container so that it uses utc --- docker/blocks/mysql/dashboard.json | 39 ++++++++++++++++--------- docker/blocks/mysql/docker-compose.yaml | 3 -- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/docker/blocks/mysql/dashboard.json b/docker/blocks/mysql/dashboard.json index e2b791f82e6..dba7847cc72 100644 --- a/docker/blocks/mysql/dashboard.json +++ b/docker/blocks/mysql/dashboard.json @@ -2,7 +2,7 @@ "__inputs": [ { "name": "DS_MYSQL", - "label": "Mysql", + "label": "MySQL", "description": "", "type": "datasource", "pluginId": "mysql", @@ -20,19 +20,19 @@ "type": "panel", "id": "graph", "name": "Graph", - "version": "" + "version": "5.0.0" }, { "type": "datasource", "id": "mysql", "name": "MySQL", - "version": "1.0.0" + "version": "5.0.0" }, { "type": "panel", "id": "table", "name": "Table", - "version": "" + "version": "5.0.0" } ], "annotations": { @@ -53,7 +53,7 @@ "gnetId": null, "graphTooltip": 0, "id": null, - "iteration": 1518602729468, + "iteration": 1523372133566, "links": [], "panels": [ { @@ -118,7 +118,7 @@ ], "thresholds": [], "timeFrom": null, - "timeShift": "1h", + "timeShift": null, "title": "Average logins / $summarize", "tooltip": { "shared": true, @@ -150,7 +150,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -204,7 +208,7 @@ ], "thresholds": [], "timeFrom": null, - "timeShift": "1h", + "timeShift": null, "title": "Average payments started/ended / $summarize", "tooltip": { "shared": true, @@ -236,7 +240,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -284,7 +292,7 @@ ], "thresholds": [], "timeFrom": null, - "timeShift": "1h", + "timeShift": null, "title": "Max CPU / $summarize", "tooltip": { "shared": true, @@ -316,7 +324,11 @@ "min": null, "show": true } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "columns": [], @@ -369,7 +381,7 @@ "target": "" } ], - "timeShift": "1h", + "timeShift": null, "title": "Values", "transform": "table", "type": "table" @@ -428,7 +440,6 @@ "auto_count": 5, "auto_min": "10s", "current": { - "selected": true, "text": "1m", "value": "1m" }, @@ -545,5 +556,5 @@ "timezone": "", "title": "Grafana Fake Data Gen - MySQL", "uid": "DGsCac3kz", - "version": 6 + "version": 8 } \ No newline at end of file diff --git a/docker/blocks/mysql/docker-compose.yaml b/docker/blocks/mysql/docker-compose.yaml index f7881e66539..53ff9da62a7 100644 --- a/docker/blocks/mysql/docker-compose.yaml +++ b/docker/blocks/mysql/docker-compose.yaml @@ -7,9 +7,6 @@ MYSQL_PASSWORD: password ports: - "3306:3306" - volumes: - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --innodb_monitor_enable=all] fake-mysql-data: From 93e1546f733d0113672fc163f327872ea1bac126 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 10 Apr 2018 22:22:12 +0200 Subject: [PATCH 096/145] alerting: handle invalid json format closes #11003 --- pkg/services/alerting/notifiers/dingding.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index e32b9d34f91..14eacef5831 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -72,7 +72,10 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { this.log.Error("Failed to create Json data", "error", err, "dingding", this.Name) } - body, _ := bodyJSON.MarshalJSON() + body, err := bodyJSON.MarshalJSON() + if err != nil { + return err + } cmd := &m.SendWebhookSync{ Url: this.Url, From 82aa6cf46b64b404683a332b536dad64702fbe07 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 11 Apr 2018 10:26:05 +0200 Subject: [PATCH 097/145] converted functions to arrow functions --- .../core/directives/value_select_dropdown.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/public/app/core/directives/value_select_dropdown.ts b/public/app/core/directives/value_select_dropdown.ts index 78f46b5aa1b..2b8d5de5ad8 100644 --- a/public/app/core/directives/value_select_dropdown.ts +++ b/public/app/core/directives/value_select_dropdown.ts @@ -27,9 +27,9 @@ export class ValueSelectDropdownCtrl { this.options = this.variable.options; this.selectedValues = _.filter(this.options, { selected: true }); - this.tags = _.map(this.variable.tags, function(value) { + this.tags = _.map(this.variable.tags, value => { let tag = { text: value, selected: false }; - _.each(this.variable.current.tags, function(tagObj) { + _.each(this.variable.current.tags, tagObj => { if (tagObj.text === value) { tag = tagObj; } @@ -50,7 +50,7 @@ export class ValueSelectDropdownCtrl { if (current.tags && current.tags.length) { // filer out values that are in selected tags - let selectedAndNotInTag = _.filter(this.variable.options, function(option) { + let selectedAndNotInTag = _.filter(this.variable.options, option => { if (!option.selected) { return false; } @@ -77,7 +77,7 @@ export class ValueSelectDropdownCtrl { } clearSelections() { - _.each(this.options, function(option) { + _.each(this.options, option => { option.selected = false; }); @@ -93,10 +93,10 @@ export class ValueSelectDropdownCtrl { tagValuesPromise = this.$q.when(tag.values); } - tagValuesPromise.then(function(values) { + tagValuesPromise.then(values => { tag.values = values; tag.valuesText = values.join(' + '); - _.each(this.options, function(option) { + _.each(this.options, option => { if (_.indexOf(tag.values, option.value) !== -1) { option.selected = tag.selected; } @@ -143,7 +143,7 @@ export class ValueSelectDropdownCtrl { excludeOthers = excludeOthers || false; let setAllExceptCurrentTo = function(newValue) { - _.each(this.options, function(other) { + _.each(this.options, other => { if (option !== other) { other.selected = newValue; } @@ -180,9 +180,9 @@ export class ValueSelectDropdownCtrl { } // validate selected tags - _.each(this.tags, function(tag) { + _.each(this.tags, tag => { if (tag.selected) { - _.each(tag.values, function(value) { + _.each(tag.values, value => { if (!_.find(this.selectedValues, { value: value })) { tag.selected = false; } @@ -224,7 +224,7 @@ export class ValueSelectDropdownCtrl { queryChanged() { this.highlightIndex = -1; - this.search.options = _.filter(this.options, function(option) { + this.search.options = _.filter(this.options, option => { return option.text.toLowerCase().indexOf(this.search.query.toLowerCase()) !== -1; }); @@ -280,7 +280,7 @@ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) { } } - scope.$watch('vm.dropdownVisible', function(newValue) { + scope.$watch('vm.dropdownVisible', newValue => { if (newValue) { openDropdown(); } else { @@ -288,11 +288,11 @@ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) { } }); - let cleanUp = $rootScope.$on('template-variable-value-updated', function() { + let cleanUp = $rootScope.$on('template-variable-value-updated', () => { scope.vm.updateLinkText(); }); - scope.$on('$destroy', function() { + scope.$on('$destroy', () => { cleanUp(); }); From 25ec7b5b027fbd99a7dd5c4f446d9a4f5e2c44ec Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 11 Apr 2018 14:30:39 +0200 Subject: [PATCH 098/145] scrollbar: use native scroll for page --- .../app/core/components/scroll/page_scroll.ts | 37 +++++++++++++++++++ public/app/core/core.ts | 2 + public/app/partials/dashboard.html | 4 +- public/sass/components/_panel_graph.scss | 1 + public/sass/components/_scrollbar.scss | 20 ++++------ public/views/index.template.html | 3 +- 6 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 public/app/core/components/scroll/page_scroll.ts diff --git a/public/app/core/components/scroll/page_scroll.ts b/public/app/core/components/scroll/page_scroll.ts new file mode 100644 index 00000000000..4782ad6d060 --- /dev/null +++ b/public/app/core/components/scroll/page_scroll.ts @@ -0,0 +1,37 @@ +import coreModule from 'app/core/core_module'; +import appEvents from 'app/core/app_events'; + +export function pageScrollbar() { + return { + restrict: 'A', + link: function(scope, elem, attrs) { + let lastPos = 0; + + appEvents.on( + 'dash-scroll', + evt => { + if (evt.restore) { + elem[0].scrollTop = lastPos; + return; + } + + lastPos = elem[0].scrollTop; + + if (evt.animate) { + elem.animate({ scrollTop: evt.pos }, 500); + } else { + elem[0].scrollTop = evt.pos; + } + }, + scope + ); + + scope.$on('$routeChangeSuccess', () => { + lastPos = 0; + elem[0].scrollTop = 0; + }); + }, + }; +} + +coreModule.directive('pageScrollbar', pageScrollbar); diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 353d8762a9a..fb7021fe883 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -47,6 +47,7 @@ import { NavModelSrv, NavModel } from './nav_model_srv'; import { userPicker } from './components/user_picker'; import { teamPicker } from './components/team_picker'; import { geminiScrollbar } from './components/scroll/scroll'; +import { pageScrollbar } from './components/scroll/page_scroll'; import { gfPageDirective } from './components/gf_page'; import { orgSwitcher } from './components/org_switcher'; import { profiler } from './profiler'; @@ -85,6 +86,7 @@ export { userPicker, teamPicker, geminiScrollbar, + pageScrollbar, gfPageDirective, orgSwitcher, manageDashboardsDirective, diff --git a/public/app/partials/dashboard.html b/public/app/partials/dashboard.html index a3d5a4439ee..d07127b0115 100644 --- a/public/app/partials/dashboard.html +++ b/public/app/partials/dashboard.html @@ -1,8 +1,8 @@
-
-
+
+
diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index 031192dd984..a19e0072518 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -74,6 +74,7 @@ .graph-legend-scroll { position: relative; + overflow: auto !important; } .graph-legend-icon { diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 4bd0807bb0b..d8d698e73a6 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -106,22 +106,16 @@ opacity: 0.9; } -// Srollbars +// Scrollbars // -// ::-webkit-scrollbar { -// width: 8px; -// height: 8px; -// } - -// ::-webkit-scrollbar:hover { -// height: 8px; -// } - ::-webkit-scrollbar { - // Hide system scrollbar (Mac OS X) - width: 0; - height: 0; + width: 8px; + height: 8px; +} + +::-webkit-scrollbar:hover { + height: 8px; } ::-webkit-scrollbar-button:start:decrement, diff --git a/public/views/index.template.html b/public/views/index.template.html index 4cb4a0d57ce..9f151527b88 100644 --- a/public/views/index.template.html +++ b/public/views/index.template.html @@ -40,8 +40,7 @@
- -
+
From 041067f5f0bf3296718d86c1971a5d92ed189abd Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 11 Apr 2018 14:31:23 +0200 Subject: [PATCH 099/145] Windows build updated to go1.10. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 5d67edca9d9..2b0bddde162 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ clone_folder: c:\gopath\src\github.com\grafana\grafana environment: nodejs_version: "6" GOPATH: c:\gopath - GOVERSION: 1.9.2 + GOVERSION: 1.10 install: - rmdir c:\go /s /q From 8c04eb272d26c948e4fcbebd821a88095b6a2790 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 11 Apr 2018 17:38:53 +0200 Subject: [PATCH 100/145] panel: add baron scroller to correct element This resolves issue with alert list panel getting scrollbars attached to incorrect element. Now the panel content are rendered correctly and all content are displayed as expected. --- public/app/features/panel/panel_directive.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 90ff42f4ac6..e549ca262d3 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -113,7 +113,8 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { `; let scrollRoot = panelContent; - let scroller = panelContent.find(':first-child').find(':first-child'); + let scroller = panelContent.find(':first').find(':first'); + scrollRoot.addClass(scrollRootClass); $(scrollBarHTML).appendTo(scrollRoot); scroller.addClass(scrollerClass); From 2956011b60ee2d9e50a474b46b1bb72038d9e714 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 11 Apr 2018 17:44:51 +0200 Subject: [PATCH 101/145] fix so that dash list panel are rendered correctly This resolves issue with dash list panel getting scrollbars attached to incorrect elements. Now the panel content are rendered correctly and all content are displayed as expected. --- public/app/plugins/panel/dashlist/module.html | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/public/app/plugins/panel/dashlist/module.html b/public/app/plugins/panel/dashlist/module.html index 8fa3e7ef71f..fdba0c79f35 100644 --- a/public/app/plugins/panel/dashlist/module.html +++ b/public/app/plugins/panel/dashlist/module.html @@ -1,17 +1,19 @@ -
-
-
- {{group.header}} -
-
- - - {{dash.title}} - - - - - +
+
+
+
+ {{group.header}} +
+
From 9549aadd2676d88ef6ec25a2f8e1d24b4d8caf46 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 11 Apr 2018 17:49:40 +0200 Subject: [PATCH 102/145] fix so that page scrollbars can be scrolled by keyboard on page load The page scrollbars are custom, not rendered on the body element and with css property overflow set for scroll to be enabled. For being able to scroll the page using the keyboard when a page loads, some custom code was needed. This fix should both work when doing a full reload of a url and when navigating to other pages/dashboards. For those pages having an input field that are focused on load, scrolling by keyboard (arrow up/down) will obviously not work. --- public/app/core/components/scroll/page_scroll.ts | 4 ++++ public/views/index.template.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/app/core/components/scroll/page_scroll.ts b/public/app/core/components/scroll/page_scroll.ts index 4782ad6d060..e6db344a4d6 100644 --- a/public/app/core/components/scroll/page_scroll.ts +++ b/public/app/core/components/scroll/page_scroll.ts @@ -29,7 +29,11 @@ export function pageScrollbar() { scope.$on('$routeChangeSuccess', () => { lastPos = 0; elem[0].scrollTop = 0; + elem[0].focus(); }); + + elem[0].tabIndex = -1; + elem[0].focus(); }, }; } diff --git a/public/views/index.template.html b/public/views/index.template.html index 9f151527b88..79da1d7179c 100644 --- a/public/views/index.template.html +++ b/public/views/index.template.html @@ -40,7 +40,7 @@
-
+
From a1b53674a9167cba1c2f32c84a04e15172c74339 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 11 Apr 2018 18:12:48 +0200 Subject: [PATCH 103/145] dashboard: show baron scrollbar in dashboard panel when mouse is over This should make the scrolling user experience much better since it will highlight and show that content actually can be scrolled when hovering over a panel. --- public/sass/components/_scrollbar.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index d8d698e73a6..6bfaa246428 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -268,10 +268,14 @@ } } -.baron._scrolling > .baron__track .baron__bar { +.panel-hover-highlight .baron__track .baron__bar { opacity: 0.6; } +.baron._scrolling > .baron__track .baron__bar { + opacity: 0.9; +} + .baron__control { display: none; } From db83255460a82d679e77dc7ad3f1cadcac193aae Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 11 Apr 2018 19:19:33 +0200 Subject: [PATCH 104/145] scrollbar: remove unused div Fixes the height of the settings menu --- public/app/partials/dashboard.html | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/public/app/partials/dashboard.html b/public/app/partials/dashboard.html index d07127b0115..9506587c515 100644 --- a/public/app/partials/dashboard.html +++ b/public/app/partials/dashboard.html @@ -2,19 +2,17 @@
-
- - + + -
- - +
+ + - - -
-
-
+ + +
+
From cdcaaa5d03aace13fa382f5f47ae73c283e027a3 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 11 Apr 2018 20:12:32 +0200 Subject: [PATCH 105/145] minor scrollbar fixes Hide page scrollbar when rendering using phantomjs. Hide baron scrollbar when rendering using phantomjs. --- public/sass/components/_scrollbar.scss | 5 +++++ public/sass/layout/_page.scss | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 6bfaa246428..78173b73f47 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -276,6 +276,11 @@ opacity: 0.9; } +// fix for phantomjs +.body--phantomjs .baron__track .baron__bar { + opacity: 0 !important; +} + .baron__control { display: none; } diff --git a/public/sass/layout/_page.scss b/public/sass/layout/_page.scss index 03941a47408..d7399865e53 100644 --- a/public/sass/layout/_page.scss +++ b/public/sass/layout/_page.scss @@ -34,6 +34,13 @@ } } +// fix for phantomjs +.body--phantomjs { + .scroll-canvas { + overflow: hidden; + } +} + .page-body { padding-top: $spacer*2; min-height: 500px; From ee7943b9b29eee05a52519bafa1091c62cb44ffc Mon Sep 17 00:00:00 2001 From: Tim O'Guin Date: Wed, 11 Apr 2018 21:29:11 -0500 Subject: [PATCH 106/145] Add minimal IAM policy example for CloudWatch data source --- .../features/datasources/cloudwatch.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index f7f8138b5e9..a8ee775d5cb 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -43,6 +43,40 @@ server is running on AWS you can use IAM Roles and authentication will be handle Checkout AWS docs on [IAM Roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html) +## IAM Policies + +Grafana needs permissions granted via IAM to be able to read from CloudWatch +and EC2. Attach these permissions to IAM roles to utilized Grafana's build-in +role support. + +Here is a minimal policy example: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowReadingMetricsFromCloudWatch", + "Effect": "Allow", + "Action": [ + "cloudwatch:ListMetrics", + "cloudwatch:GetMetricStatistics" + ], + "Resource": "*" + }, + { + "Sid": "AllowReadingTagsFromEC2", + "Effect": "Allow", + "Action": [ + "ec2:DescribeTags", + "ec2:DescribeInstances" + ], + "Resource": "*" + } + ] +} +``` + ### AWS credentials file Create a file at `~/.aws/credentials`. That is the `HOME` path for user running grafana-server. From 1f0dfbbf86f0d1733c57d1d5639cbbfd898bd489 Mon Sep 17 00:00:00 2001 From: Tim O'Guin Date: Wed, 11 Apr 2018 21:36:07 -0500 Subject: [PATCH 107/145] Improve wording --- docs/sources/features/datasources/cloudwatch.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index a8ee775d5cb..cb4a408336e 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -45,9 +45,9 @@ Checkout AWS docs on [IAM Roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGu ## IAM Policies -Grafana needs permissions granted via IAM to be able to read from CloudWatch -and EC2. Attach these permissions to IAM roles to utilized Grafana's build-in -role support. +Grafana needs permissions granted via IAM to be able to read CloudWatch metrics +and EC2 tags/instances. You can attach these permissions to IAM roles and +utilize Grafana's built-in support for assuming roles. Here is a minimal policy example: From 76c87d1a7466393037b2ddee4bc1ad1b2b0680e5 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 12 Apr 2018 10:52:21 +0200 Subject: [PATCH 108/145] scrollbar: fixes continuation scrolling for iOS The -webkit-overflow-scrolling is an iOS only property: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling that turns on momementum/continuation scrolling for iOS devices. This means that when swiping, the scroll continues for a half second rather than instantly stopping the scroll when the user lifts their finger from the screen. --- public/sass/layout/_page.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/public/sass/layout/_page.scss b/public/sass/layout/_page.scss index d7399865e53..c80d461541e 100644 --- a/public/sass/layout/_page.scss +++ b/public/sass/layout/_page.scss @@ -28,6 +28,7 @@ width: 100%; overflow: auto; height: 100%; + -webkit-overflow-scrolling: touch; &--dashboard { height: calc(100% - 56px); From b220162b91e761ecb759ccda420db785b57a7c6c Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 12 Apr 2018 10:44:04 +0200 Subject: [PATCH 109/145] build: remove code cov --- scripts/circle-test-backend.sh | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/scripts/circle-test-backend.sh b/scripts/circle-test-backend.sh index a63d6354fa6..71fc598b609 100755 --- a/scripts/circle-test-backend.sh +++ b/scripts/circle-test-backend.sh @@ -20,17 +20,4 @@ echo "building backend with install to cache pkgs" exit_if_fail time go install ./pkg/cmd/grafana-server echo "running go test" - -set -e -echo "" > coverage.txt - -time for d in $(go list ./pkg/...); do - exit_if_fail go test -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 +go test ./pkg/... From d4398479f52df8f21dcc16b673d6899d2bda48b6 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 12 Apr 2018 11:26:27 +0200 Subject: [PATCH 110/145] scrollbar: fix so no overflow for legend under graph Adds 1px of padding to the graph legend scroll div so that a non-table legend does not get an unnecessary scroll bar. --- public/sass/components/_panel_graph.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index a19e0072518..8a818d78c05 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -75,6 +75,7 @@ .graph-legend-scroll { position: relative; overflow: auto !important; + padding: 1px; } .graph-legend-icon { From 74c10515ade9e41558295f3fe2c635dfdab26cb3 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 12 Apr 2018 14:22:19 +0300 Subject: [PATCH 111/145] fix right side legend rendering in phantomjs --- public/app/plugins/panel/graph/legend.ts | 7 ++++--- public/sass/components/_panel_graph.scss | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index 752dc591147..b668555b6a6 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -16,6 +16,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { var i; var legendScrollbar; const legendRightDefaultWidth = 10; + let legendElem = elem.parent(); scope.$on('$destroy', function() { destroyScrollbar(); @@ -110,7 +111,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { } function render() { - let legendWidth = elem.width(); + let legendWidth = legendElem.width(); if (!ctrl.panel.legend.show) { elem.empty(); firstRender = true; @@ -132,8 +133,8 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { // Set width so it works with IE11 var width: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + 'px' : ''; var ieWidth: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth - 1 + 'px' : ''; - elem.css('min-width', width); - elem.css('width', ieWidth); + legendElem.css('min-width', width); + legendElem.css('width', ieWidth); elem.toggleClass('graph-legend-table', panel.legend.alignAsTable === true); diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index a19e0072518..d952b9aa703 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -7,6 +7,10 @@ @include media-breakpoint-up(sm) { flex-direction: row; + .graph-panel__chart { + flex: 2 1 10px; + } + .graph-legend { flex: 0 1 10px; max-height: 100%; @@ -131,8 +135,20 @@ // fix for phantomjs .body--phantomjs { .graph-panel--legend-right { + .graph-legend { + display: inline-block; + } + + .graph-panel__chart { + display: flex; + } + .graph-legend-table { display: table; + + .graph-legend-scroll { + display: table; + } } } } From f8543fced95ca7ccbf280700966c1e053ff28567 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 12 Apr 2018 14:29:35 +0300 Subject: [PATCH 112/145] remove mistakenly added styles --- public/sass/components/_panel_graph.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index 2dbae0f1d08..72f3ca3dbbe 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -7,10 +7,6 @@ @include media-breakpoint-up(sm) { flex-direction: row; - .graph-panel__chart { - flex: 2 1 10px; - } - .graph-legend { flex: 0 1 10px; max-height: 100%; From 74f570fdf459518d535611b569ce35eebb74774e Mon Sep 17 00:00:00 2001 From: flopp999 <21694965+flopp999@users.noreply.github.com> Date: Thu, 12 Apr 2018 17:38:10 +0200 Subject: [PATCH 113/145] change annotation limit from 10 to 100 --- pkg/services/sqlstore/annotation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/sqlstore/annotation.go b/pkg/services/sqlstore/annotation.go index 76f1819a18c..18794281e33 100644 --- a/pkg/services/sqlstore/annotation.go +++ b/pkg/services/sqlstore/annotation.go @@ -202,7 +202,7 @@ func (r *SqlAnnotationRepo) Find(query *annotations.ItemQuery) ([]*annotations.I } if query.Limit == 0 { - query.Limit = 10 + query.Limit = 100 } sql.WriteString(fmt.Sprintf(" ORDER BY epoch DESC LIMIT %v", query.Limit)) From f5586b1270ebd92e5fc2ebb8a22f1c1bbc186f43 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 12 Apr 2018 18:53:12 +0200 Subject: [PATCH 114/145] tsdb: sql data sources should handle time ranges before epoch start correctly --- pkg/tsdb/mssql/macros.go | 12 +- pkg/tsdb/mssql/macros_test.go | 271 +++++++++++++++++++++---------- pkg/tsdb/mysql/macros.go | 12 +- pkg/tsdb/mysql/macros_test.go | 233 ++++++++++++++++++-------- pkg/tsdb/postgres/macros.go | 12 +- pkg/tsdb/postgres/macros_test.go | 218 ++++++++++++++++++------- pkg/tsdb/time_range.go | 16 ++ 7 files changed, 546 insertions(+), 228 deletions(-) diff --git a/pkg/tsdb/mssql/macros.go b/pkg/tsdb/mssql/macros.go index 9d41cd03255..f53e06131be 100644 --- a/pkg/tsdb/mssql/macros.go +++ b/pkg/tsdb/mssql/macros.go @@ -82,11 +82,11 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= DATEADD(s, %d, '1970-01-01') AND %s <= DATEADD(s, %d, '1970-01-01')", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= DATEADD(s, %d, '1970-01-01') AND %s <= DATEADD(s, %d, '1970-01-01')", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil case "__timeFrom": - return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil case "__timeTo": - return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", int64(m.TimeRange.GetToAsSecondsEpoch())), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) @@ -113,11 +113,11 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", uint64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil case "__unixEpochTo": - return fmt.Sprintf("%d", uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsSecondsEpoch())), nil default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/mssql/macros_test.go b/pkg/tsdb/mssql/macros_test.go index 12a9b0d82be..ae0d4f67d2b 100644 --- a/pkg/tsdb/mssql/macros_test.go +++ b/pkg/tsdb/mssql/macros_test.go @@ -1,6 +1,8 @@ package mssql import ( + "fmt" + "strconv" "testing" "time" @@ -13,112 +15,213 @@ import ( func TestMacroEngine(t *testing.T) { Convey("MacroEngine", t, func() { engine := &MsSqlMacroEngine{} - timeRange := &tsdb.TimeRange{From: "5m", To: "now"} query := &tsdb.Query{ Model: simplejson.New(), } - Convey("interpolate __time function", func() { - sql, err := engine.Interpolate(query, nil, "select $__time(time_column)") - So(err, ShouldBeNil) + Convey("Given a time range between 2018-04-12 00:00 and 2018-04-12 00:05", func() { + from := time.Date(2018, 4, 12, 18, 0, 0, 0, time.UTC) + to := from.Add(5 * time.Minute) + timeRange := tsdb.NewFakeTimeRange("5m", "now", to) - So(sql, ShouldEqual, "select time_column AS time") + Convey("interpolate __time function", func() { + sql, err := engine.Interpolate(query, nil, "select $__time(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "select time_column AS time") + }) + + Convey("interpolate __timeEpoch function", func() { + sql, err := engine.Interpolate(query, nil, "select $__timeEpoch(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "select DATEDIFF(second, '1970-01-01', time_column) AS time") + }) + + Convey("interpolate __timeEpoch function wrapped in aggregation", func() { + sql, err := engine.Interpolate(query, nil, "select min($__timeEpoch(time_column))") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "select min(DATEDIFF(second, '1970-01-01', time_column) AS time)") + }) + + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= DATEADD(s, %d, '1970-01-01') AND time_column <= DATEADD(s, %d, '1970-01-01')", from.Unix(), to.Unix())) + }) + + Convey("interpolate __timeGroup function", func() { + sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m')") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "GROUP BY CAST(ROUND(DATEDIFF(second, '1970-01-01', time_column)/300.0, 0) as bigint)*300") + }) + + Convey("interpolate __timeGroup function with spaces around arguments", func() { + sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column , '5m')") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "GROUP BY CAST(ROUND(DATEDIFF(second, '1970-01-01', time_column)/300.0, 0) as bigint)*300") + }) + + Convey("interpolate __timeGroup function with fill (value = NULL)", func() { + _, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m', NULL)") + + fill := query.Model.Get("fill").MustBool() + fillNull := query.Model.Get("fillNull").MustBool() + fillInterval := query.Model.Get("fillInterval").MustInt() + + So(err, ShouldBeNil) + So(fill, ShouldBeTrue) + So(fillNull, ShouldBeTrue) + So(fillInterval, ShouldEqual, 5*time.Minute.Seconds()) + }) + + Convey("interpolate __timeGroup function with fill (value = float)", func() { + _, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m', 1.5)") + + fill := query.Model.Get("fill").MustBool() + fillValue := query.Model.Get("fillValue").MustFloat64() + fillInterval := query.Model.Get("fillInterval").MustInt() + + So(err, ShouldBeNil) + So(fill, ShouldBeTrue) + So(fillValue, ShouldEqual, 1.5) + So(fillInterval, ShouldEqual, 5*time.Minute.Seconds()) + }) + + Convey("interpolate __timeFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", from.Unix())) + }) + + Convey("interpolate __timeTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", to.Unix())) + }) + + Convey("interpolate __unixEpochFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select time_column >= %d AND time_column <= %d", from.Unix(), to.Unix())) + }) + + Convey("interpolate __unixEpochFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", from.Unix())) + }) + + Convey("interpolate __unixEpochTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) + }) }) - Convey("interpolate __timeEpoch function", func() { - sql, err := engine.Interpolate(query, nil, "select $__timeEpoch(time_column)") - So(err, ShouldBeNil) + Convey("Given a time range between 1960-02-01 07:00 and 1965-02-03 08:00", func() { + from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC) + to := time.Date(1965, 2, 3, 8, 0, 0, 0, time.UTC) + timeRange := tsdb.NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10)) - So(sql, ShouldEqual, "select DATEDIFF(second, '1970-01-01', time_column) AS time") + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= DATEADD(s, %d, '1970-01-01') AND time_column <= DATEADD(s, %d, '1970-01-01')", from.Unix(), to.Unix())) + }) + + Convey("interpolate __timeFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", from.Unix())) + }) + + Convey("interpolate __timeTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", to.Unix())) + }) + + Convey("interpolate __unixEpochFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select time_column >= %d AND time_column <= %d", from.Unix(), to.Unix())) + }) + + Convey("interpolate __unixEpochFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", from.Unix())) + }) + + Convey("interpolate __unixEpochTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) + }) }) - Convey("interpolate __timeEpoch function wrapped in aggregation", func() { - sql, err := engine.Interpolate(query, nil, "select min($__timeEpoch(time_column))") - So(err, ShouldBeNil) + Convey("Given a time range between 1960-02-01 07:00 and 1980-02-03 08:00", func() { + from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC) + to := time.Date(1980, 2, 3, 8, 0, 0, 0, time.UTC) + timeRange := tsdb.NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10)) - So(sql, ShouldEqual, "select min(DATEDIFF(second, '1970-01-01', time_column) AS time)") - }) + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) - Convey("interpolate __timeFilter function", func() { - sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") - So(err, ShouldBeNil) + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= DATEADD(s, %d, '1970-01-01') AND time_column <= DATEADD(s, %d, '1970-01-01')", from.Unix(), to.Unix())) + }) - So(sql, ShouldEqual, "WHERE time_column >= DATEADD(s, 18446744066914186738, '1970-01-01') AND time_column <= DATEADD(s, 18446744066914187038, '1970-01-01')") - }) + Convey("interpolate __timeFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") + So(err, ShouldBeNil) - Convey("interpolate __timeGroup function", func() { - sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m')") - So(err, ShouldBeNil) + So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", from.Unix())) + }) - So(sql, ShouldEqual, "GROUP BY CAST(ROUND(DATEDIFF(second, '1970-01-01', time_column)/300.0, 0) as bigint)*300") - }) + Convey("interpolate __timeTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") + So(err, ShouldBeNil) - Convey("interpolate __timeGroup function with spaces around arguments", func() { - sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column , '5m')") - So(err, ShouldBeNil) + So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", to.Unix())) + }) - So(sql, ShouldEqual, "GROUP BY CAST(ROUND(DATEDIFF(second, '1970-01-01', time_column)/300.0, 0) as bigint)*300") - }) + Convey("interpolate __unixEpochFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time_column)") + So(err, ShouldBeNil) - Convey("interpolate __timeGroup function with fill (value = NULL)", func() { - _, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m', NULL)") + So(sql, ShouldEqual, fmt.Sprintf("select time_column >= %d AND time_column <= %d", from.Unix(), to.Unix())) + }) - fill := query.Model.Get("fill").MustBool() - fillNull := query.Model.Get("fillNull").MustBool() - fillInterval := query.Model.Get("fillInterval").MustInt() + Convey("interpolate __unixEpochFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") + So(err, ShouldBeNil) - So(err, ShouldBeNil) - So(fill, ShouldBeTrue) - So(fillNull, ShouldBeTrue) - So(fillInterval, ShouldEqual, 5*time.Minute.Seconds()) - }) + So(sql, ShouldEqual, fmt.Sprintf("select %d", from.Unix())) + }) - Convey("interpolate __timeGroup function with fill (value = float)", func() { - _, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m', 1.5)") + Convey("interpolate __unixEpochTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") + So(err, ShouldBeNil) - fill := query.Model.Get("fill").MustBool() - fillValue := query.Model.Get("fillValue").MustFloat64() - fillInterval := query.Model.Get("fillInterval").MustInt() - - So(err, ShouldBeNil) - So(fill, ShouldBeTrue) - So(fillValue, ShouldEqual, 1.5) - So(fillInterval, ShouldEqual, 5*time.Minute.Seconds()) - }) - - Convey("interpolate __timeFrom function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select DATEADD(second, 18446744066914186738, '1970-01-01')") - }) - - Convey("interpolate __timeTo function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select DATEADD(second, 18446744066914187038, '1970-01-01')") - }) - - Convey("interpolate __unixEpochFilter function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time_column)") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select time_column >= 18446744066914186738 AND time_column <= 18446744066914187038") - }) - - Convey("interpolate __unixEpochFrom function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select 18446744066914186738") - }) - - Convey("interpolate __unixEpochTo function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select 18446744066914187038") + So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) + }) }) }) } diff --git a/pkg/tsdb/mysql/macros.go b/pkg/tsdb/mysql/macros.go index 92f6968f12a..d4a08bd6241 100644 --- a/pkg/tsdb/mysql/macros.go +++ b/pkg/tsdb/mysql/macros.go @@ -77,11 +77,11 @@ func (m *MySqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= FROM_UNIXTIME(%d) AND %s <= FROM_UNIXTIME(%d)", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= FROM_UNIXTIME(%d) AND %s <= FROM_UNIXTIME(%d)", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil case "__timeFrom": - return fmt.Sprintf("FROM_UNIXTIME(%d)", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("FROM_UNIXTIME(%d)", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil case "__timeTo": - return fmt.Sprintf("FROM_UNIXTIME(%d)", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("FROM_UNIXTIME(%d)", int64(m.TimeRange.GetToAsSecondsEpoch())), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) @@ -108,11 +108,11 @@ func (m *MySqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil case "__unixEpochTo": - return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsSecondsEpoch())), nil default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/mysql/macros_test.go b/pkg/tsdb/mysql/macros_test.go index a89ba16ab78..66ec143eac8 100644 --- a/pkg/tsdb/mysql/macros_test.go +++ b/pkg/tsdb/mysql/macros_test.go @@ -1,7 +1,10 @@ package mysql import ( + "fmt" + "strconv" "testing" + "time" "github.com/grafana/grafana/pkg/tsdb" . "github.com/smartystreets/goconvey/convey" @@ -11,79 +14,179 @@ func TestMacroEngine(t *testing.T) { Convey("MacroEngine", t, func() { engine := &MySqlMacroEngine{} query := &tsdb.Query{} - timeRange := &tsdb.TimeRange{From: "5m", To: "now"} - Convey("interpolate __time function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__time(time_column)") - So(err, ShouldBeNil) + Convey("Given a time range between 2018-04-12 00:00 and 2018-04-12 00:05", func() { + from := time.Date(2018, 4, 12, 18, 0, 0, 0, time.UTC) + to := from.Add(5 * time.Minute) + timeRange := tsdb.NewFakeTimeRange("5m", "now", to) - So(sql, ShouldEqual, "select UNIX_TIMESTAMP(time_column) as time_sec") + Convey("interpolate __time function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__time(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "select UNIX_TIMESTAMP(time_column) as time_sec") + }) + + Convey("interpolate __time function wrapped in aggregation", func() { + sql, err := engine.Interpolate(query, timeRange, "select min($__time(time_column))") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "select min(UNIX_TIMESTAMP(time_column) as time_sec)") + }) + + Convey("interpolate __timeGroup function", func() { + + sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m')") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "GROUP BY cast(cast(UNIX_TIMESTAMP(time_column)/(300) as signed)*300 as signed)") + }) + + Convey("interpolate __timeGroup function with spaces around arguments", func() { + + sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column , '5m')") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "GROUP BY cast(cast(UNIX_TIMESTAMP(time_column)/(300) as signed)*300 as signed)") + }) + + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= FROM_UNIXTIME(%d) AND time_column <= FROM_UNIXTIME(%d)", from.Unix(), to.Unix())) + }) + + Convey("interpolate __timeFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", from.Unix())) + }) + + Convey("interpolate __timeTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", to.Unix())) + }) + + Convey("interpolate __unixEpochFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select time >= %d AND time <= %d", from.Unix(), to.Unix())) + }) + + Convey("interpolate __unixEpochFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", from.Unix())) + }) + + Convey("interpolate __unixEpochTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) + }) }) - Convey("interpolate __time function wrapped in aggregation", func() { - sql, err := engine.Interpolate(query, timeRange, "select min($__time(time_column))") - So(err, ShouldBeNil) + Convey("Given a time range between 1960-02-01 07:00 and 1965-02-03 08:00", func() { + from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC) + to := time.Date(1965, 2, 3, 8, 0, 0, 0, time.UTC) + timeRange := tsdb.NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10)) - So(sql, ShouldEqual, "select min(UNIX_TIMESTAMP(time_column) as time_sec)") + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= FROM_UNIXTIME(%d) AND time_column <= FROM_UNIXTIME(%d)", from.Unix(), to.Unix())) + }) + + Convey("interpolate __timeFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", from.Unix())) + }) + + Convey("interpolate __timeTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", to.Unix())) + }) + + Convey("interpolate __unixEpochFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select time >= %d AND time <= %d", from.Unix(), to.Unix())) + }) + + Convey("interpolate __unixEpochFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", from.Unix())) + }) + + Convey("interpolate __unixEpochTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) + }) }) - Convey("interpolate __timeFilter function", func() { - sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") - So(err, ShouldBeNil) + Convey("Given a time range between 1960-02-01 07:00 and 1980-02-03 08:00", func() { + from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC) + to := time.Date(1980, 2, 3, 8, 0, 0, 0, time.UTC) + timeRange := tsdb.NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10)) - So(sql, ShouldEqual, "WHERE time_column >= FROM_UNIXTIME(18446744066914186738) AND time_column <= FROM_UNIXTIME(18446744066914187038)") + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= FROM_UNIXTIME(%d) AND time_column <= FROM_UNIXTIME(%d)", from.Unix(), to.Unix())) + }) + + Convey("interpolate __timeFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", from.Unix())) + }) + + Convey("interpolate __timeTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", to.Unix())) + }) + + Convey("interpolate __unixEpochFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select time >= %d AND time <= %d", from.Unix(), to.Unix())) + }) + + Convey("interpolate __unixEpochFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", from.Unix())) + }) + + Convey("interpolate __unixEpochTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) + }) }) - - Convey("interpolate __timeFrom function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select FROM_UNIXTIME(18446744066914186738)") - }) - - Convey("interpolate __timeGroup function", func() { - - sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m')") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "GROUP BY cast(cast(UNIX_TIMESTAMP(time_column)/(300) as signed)*300 as signed)") - }) - - Convey("interpolate __timeGroup function with spaces around arguments", func() { - - sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column , '5m')") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "GROUP BY cast(cast(UNIX_TIMESTAMP(time_column)/(300) as signed)*300 as signed)") - }) - - Convey("interpolate __timeTo function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select FROM_UNIXTIME(18446744066914187038)") - }) - - Convey("interpolate __unixEpochFilter function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(18446744066914186738)") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select 18446744066914186738 >= 18446744066914186738 AND 18446744066914186738 <= 18446744066914187038") - }) - - Convey("interpolate __unixEpochFrom function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select 18446744066914186738") - }) - - Convey("interpolate __unixEpochTo function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select 18446744066914187038") - }) - }) } diff --git a/pkg/tsdb/postgres/macros.go b/pkg/tsdb/postgres/macros.go index f96b3896041..b28b7bd36da 100644 --- a/pkg/tsdb/postgres/macros.go +++ b/pkg/tsdb/postgres/macros.go @@ -83,11 +83,11 @@ 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("extract(epoch from %s) BETWEEN %d AND %d", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("extract(epoch from %s) BETWEEN %d AND %d", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), int64(m.TimeRange.GetToAsSecondsEpoch())), nil case "__timeFrom": - return fmt.Sprintf("to_timestamp(%d)", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("to_timestamp(%d)", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil case "__timeTo": - return fmt.Sprintf("to_timestamp(%d)", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("to_timestamp(%d)", int64(m.TimeRange.GetToAsSecondsEpoch())), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval and optional fill value", name) @@ -114,11 +114,11 @@ 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 >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil case "__unixEpochTo": - return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsMsEpoch()/1000)), nil + return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsSecondsEpoch())), nil default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/postgres/macros_test.go b/pkg/tsdb/postgres/macros_test.go index 838a73ce240..f441690a429 100644 --- a/pkg/tsdb/postgres/macros_test.go +++ b/pkg/tsdb/postgres/macros_test.go @@ -1,7 +1,10 @@ package postgres import ( + "fmt" + "strconv" "testing" + "time" "github.com/grafana/grafana/pkg/tsdb" . "github.com/smartystreets/goconvey/convey" @@ -11,86 +14,179 @@ func TestMacroEngine(t *testing.T) { Convey("MacroEngine", t, func() { engine := &PostgresMacroEngine{} query := &tsdb.Query{} - timeRange := &tsdb.TimeRange{From: "5m", To: "now"} - Convey("interpolate __time function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__time(time_column)") - So(err, ShouldBeNil) + Convey("Given a time range between 2018-04-12 00:00 and 2018-04-12 00:05", func() { + from := time.Date(2018, 4, 12, 18, 0, 0, 0, time.UTC) + to := from.Add(5 * time.Minute) + timeRange := tsdb.NewFakeTimeRange("5m", "now", to) - So(sql, ShouldEqual, "select time_column AS \"time\"") + Convey("interpolate __time function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__time(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "select time_column AS \"time\"") + }) + + Convey("interpolate __time function wrapped in aggregation", func() { + sql, err := engine.Interpolate(query, timeRange, "select min($__time(time_column))") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "select min(time_column AS \"time\")") + }) + + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("WHERE extract(epoch from time_column) BETWEEN %d AND %d", from.Unix(), to.Unix())) + }) + + Convey("interpolate __timeFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select to_timestamp(%d)", from.Unix())) + }) + + Convey("interpolate __timeGroup function", func() { + + sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m')") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "GROUP BY (extract(epoch from time_column)/300)::bigint*300 AS time") + }) + + Convey("interpolate __timeGroup function with spaces between args", func() { + + sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column , '5m')") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "GROUP BY (extract(epoch from time_column)/300)::bigint*300 AS time") + }) + + Convey("interpolate __timeTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select to_timestamp(%d)", to.Unix())) + }) + + Convey("interpolate __unixEpochFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select time >= %d AND time <= %d", from.Unix(), to.Unix())) + }) + + Convey("interpolate __unixEpochFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", from.Unix())) + }) + + Convey("interpolate __unixEpochTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) + }) }) - Convey("interpolate __time function wrapped in aggregation", func() { - sql, err := engine.Interpolate(query, timeRange, "select min($__time(time_column))") - So(err, ShouldBeNil) + Convey("Given a time range between 1960-02-01 07:00 and 1965-02-03 08:00", func() { + from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC) + to := time.Date(1965, 2, 3, 8, 0, 0, 0, time.UTC) + timeRange := tsdb.NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10)) - So(sql, ShouldEqual, "select min(time_column AS \"time\")") + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("WHERE extract(epoch from time_column) BETWEEN %d AND %d", from.Unix(), to.Unix())) + }) + + Convey("interpolate __timeFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select to_timestamp(%d)", from.Unix())) + }) + + Convey("interpolate __timeTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select to_timestamp(%d)", to.Unix())) + }) + + Convey("interpolate __unixEpochFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select time >= %d AND time <= %d", from.Unix(), to.Unix())) + }) + + Convey("interpolate __unixEpochFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", from.Unix())) + }) + + Convey("interpolate __unixEpochTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) + }) }) - Convey("interpolate __timeFilter function", func() { - sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") - So(err, ShouldBeNil) + Convey("Given a time range between 1960-02-01 07:00 and 1980-02-03 08:00", func() { + from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC) + to := time.Date(1980, 2, 3, 8, 0, 0, 0, time.UTC) + timeRange := tsdb.NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10)) - So(sql, ShouldEqual, "WHERE extract(epoch from time_column) BETWEEN 18446744066914186738 AND 18446744066914187038") - }) + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) - Convey("interpolate __timeFrom function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") - So(err, ShouldBeNil) + So(sql, ShouldEqual, fmt.Sprintf("WHERE extract(epoch from time_column) BETWEEN %d AND %d", from.Unix(), to.Unix())) + }) - So(sql, ShouldEqual, "select to_timestamp(18446744066914186738)") - }) + Convey("interpolate __timeFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") + So(err, ShouldBeNil) - Convey("interpolate __timeGroup function", func() { + So(sql, ShouldEqual, fmt.Sprintf("select to_timestamp(%d)", from.Unix())) + }) - sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m')") - So(err, ShouldBeNil) + Convey("interpolate __timeTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") + So(err, ShouldBeNil) - So(sql, ShouldEqual, "GROUP BY (extract(epoch from time_column)/300)::bigint*300 AS time") - }) + So(sql, ShouldEqual, fmt.Sprintf("select to_timestamp(%d)", to.Unix())) + }) - Convey("interpolate __timeGroup function with spaces between args", func() { + Convey("interpolate __unixEpochFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time)") + So(err, ShouldBeNil) - sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column , '5m')") - So(err, ShouldBeNil) + So(sql, ShouldEqual, fmt.Sprintf("select time >= %d AND time <= %d", from.Unix(), to.Unix())) + }) - So(sql, ShouldEqual, "GROUP BY (extract(epoch from time_column)/300)::bigint*300 AS time") - }) + Convey("interpolate __unixEpochFrom function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") + So(err, ShouldBeNil) - Convey("interpolate __timeTo function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") - So(err, ShouldBeNil) + So(sql, ShouldEqual, fmt.Sprintf("select %d", from.Unix())) + }) - So(sql, ShouldEqual, "select to_timestamp(18446744066914187038)") - }) + Convey("interpolate __unixEpochTo function", func() { + sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") + So(err, ShouldBeNil) - Convey("interpolate __unixEpochFilter function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(18446744066914186738)") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select 18446744066914186738 >= 18446744066914186738 AND 18446744066914186738 <= 18446744066914187038") - }) - - Convey("interpolate __unixEpochFrom function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFrom()") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select 18446744066914186738") - }) - - Convey("interpolate __unixEpochTo function", func() { - sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochTo()") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "select 18446744066914187038") - }) - - timeRange = &tsdb.TimeRange{From: "-315622800000", To: "315529200000"} // 1960-1980 - Convey("interpolate __timeFilter function before epoch", func() { - sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") - So(err, ShouldBeNil) - - So(sql, ShouldEqual, "WHERE extract(epoch from time_column) BETWEEN -315622800 AND 315529200") + So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) + }) }) }) } diff --git a/pkg/tsdb/time_range.go b/pkg/tsdb/time_range.go index fd0cb3f8e82..5ebad362bf7 100644 --- a/pkg/tsdb/time_range.go +++ b/pkg/tsdb/time_range.go @@ -15,6 +15,14 @@ func NewTimeRange(from, to string) *TimeRange { } } +func NewFakeTimeRange(from, to string, now time.Time) *TimeRange { + return &TimeRange{ + From: from, + To: to, + now: now, + } +} + type TimeRange struct { From string To string @@ -25,10 +33,18 @@ func (tr *TimeRange) GetFromAsMsEpoch() int64 { return tr.MustGetFrom().UnixNano() / int64(time.Millisecond) } +func (tr *TimeRange) GetFromAsSecondsEpoch() int64 { + return tr.GetFromAsMsEpoch() / 1000 +} + func (tr *TimeRange) GetToAsMsEpoch() int64 { return tr.MustGetTo().UnixNano() / int64(time.Millisecond) } +func (tr *TimeRange) GetToAsSecondsEpoch() int64 { + return tr.GetToAsMsEpoch() / 1000 +} + func (tr *TimeRange) MustGetFrom() time.Time { if res, err := tr.ParseFrom(); err != nil { return time.Unix(0, 0) From a86ee304ff9d7f30f044eebdf673c13fcbc0a7b9 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 12 Apr 2018 19:08:35 +0200 Subject: [PATCH 115/145] tsdb: remove unnecessary type casts in sql data sources macro engines --- pkg/tsdb/mssql/macros.go | 12 ++++++------ pkg/tsdb/mysql/macros.go | 12 ++++++------ pkg/tsdb/postgres/macros.go | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pkg/tsdb/mssql/macros.go b/pkg/tsdb/mssql/macros.go index f53e06131be..bb9489cd654 100644 --- a/pkg/tsdb/mssql/macros.go +++ b/pkg/tsdb/mssql/macros.go @@ -82,11 +82,11 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= DATEADD(s, %d, '1970-01-01') AND %s <= DATEADD(s, %d, '1970-01-01')", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("%s >= DATEADD(s, %d, '1970-01-01') AND %s <= DATEADD(s, %d, '1970-01-01')", args[0], m.TimeRange.GetFromAsSecondsEpoch(), args[0], m.TimeRange.GetToAsSecondsEpoch()), nil case "__timeFrom": - return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil + return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", m.TimeRange.GetFromAsSecondsEpoch()), nil case "__timeTo": - return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", m.TimeRange.GetToAsSecondsEpoch()), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) @@ -113,11 +113,11 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], m.TimeRange.GetFromAsSecondsEpoch(), args[0], m.TimeRange.GetToAsSecondsEpoch()), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil + return fmt.Sprintf("%d", m.TimeRange.GetFromAsSecondsEpoch()), nil case "__unixEpochTo": - return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("%d", m.TimeRange.GetToAsSecondsEpoch()), nil default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/mysql/macros.go b/pkg/tsdb/mysql/macros.go index d4a08bd6241..fadcbe4edbc 100644 --- a/pkg/tsdb/mysql/macros.go +++ b/pkg/tsdb/mysql/macros.go @@ -77,11 +77,11 @@ func (m *MySqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= FROM_UNIXTIME(%d) AND %s <= FROM_UNIXTIME(%d)", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("%s >= FROM_UNIXTIME(%d) AND %s <= FROM_UNIXTIME(%d)", args[0], m.TimeRange.GetFromAsSecondsEpoch(), args[0], m.TimeRange.GetToAsSecondsEpoch()), nil case "__timeFrom": - return fmt.Sprintf("FROM_UNIXTIME(%d)", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil + return fmt.Sprintf("FROM_UNIXTIME(%d)", m.TimeRange.GetFromAsSecondsEpoch()), nil case "__timeTo": - return fmt.Sprintf("FROM_UNIXTIME(%d)", int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("FROM_UNIXTIME(%d)", m.TimeRange.GetToAsSecondsEpoch()), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) @@ -108,11 +108,11 @@ func (m *MySqlMacroEngine) evaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], m.TimeRange.GetFromAsSecondsEpoch(), args[0], m.TimeRange.GetToAsSecondsEpoch()), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil + return fmt.Sprintf("%d", m.TimeRange.GetFromAsSecondsEpoch()), nil case "__unixEpochTo": - return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("%d", m.TimeRange.GetToAsSecondsEpoch()), nil default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/postgres/macros.go b/pkg/tsdb/postgres/macros.go index b28b7bd36da..bd0ac0cc620 100644 --- a/pkg/tsdb/postgres/macros.go +++ b/pkg/tsdb/postgres/macros.go @@ -83,11 +83,11 @@ 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("extract(epoch from %s) BETWEEN %d AND %d", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("extract(epoch from %s) BETWEEN %d AND %d", args[0], m.TimeRange.GetFromAsSecondsEpoch(), m.TimeRange.GetToAsSecondsEpoch()), nil case "__timeFrom": - return fmt.Sprintf("to_timestamp(%d)", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil + return fmt.Sprintf("to_timestamp(%d)", m.TimeRange.GetFromAsSecondsEpoch()), nil case "__timeTo": - return fmt.Sprintf("to_timestamp(%d)", int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("to_timestamp(%d)", m.TimeRange.GetToAsSecondsEpoch()), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval and optional fill value", name) @@ -114,11 +114,11 @@ 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 >= %d AND %s <= %d", args[0], int64(m.TimeRange.GetFromAsSecondsEpoch()), args[0], int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], m.TimeRange.GetFromAsSecondsEpoch(), args[0], m.TimeRange.GetToAsSecondsEpoch()), nil case "__unixEpochFrom": - return fmt.Sprintf("%d", int64(m.TimeRange.GetFromAsSecondsEpoch())), nil + return fmt.Sprintf("%d", m.TimeRange.GetFromAsSecondsEpoch()), nil case "__unixEpochTo": - return fmt.Sprintf("%d", int64(m.TimeRange.GetToAsSecondsEpoch())), nil + return fmt.Sprintf("%d", m.TimeRange.GetToAsSecondsEpoch()), nil default: return "", fmt.Errorf("Unknown macro %v", name) } From a43e7c7b3fe323ea6cb2407a44d865bdcd9541af Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Thu, 12 Apr 2018 21:14:58 +0200 Subject: [PATCH 116/145] =?UTF-8?q?Add=20another=20URL=20param=20=C2=ABina?= =?UTF-8?q?ctive=C2=BB=20which=20works=20like=20=C2=ABkiosk=C2=BB=20but=20?= =?UTF-8?q?with=20title?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #11228 --- public/app/core/components/grafana_app.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index 798a40cb1bf..01218c40529 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -117,6 +117,14 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop appEvents.emit('toggle-kiosk-mode'); } + // check for 'inactive' url param for clean looks like kiosk, but with title + if (data.params.inactive) { + body.addClass('user-activity-low'); + + // for some reason, with this class it looks cleanest + body.addClass('sidemenu-open'); + } + // close all drops for (let drop of Drop.drops) { drop.destroy(); From f143cb655a5e8c691083b1ab66fd763264d547cb Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Thu, 12 Apr 2018 21:38:28 +0200 Subject: [PATCH 117/145] Mention the ?inactive parameter in the docs --- docs/sources/reference/playlist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/reference/playlist.md b/docs/sources/reference/playlist.md index 5a6bf921334..182e69eebd0 100644 --- a/docs/sources/reference/playlist.md +++ b/docs/sources/reference/playlist.md @@ -49,7 +49,7 @@ Click the back button to rewind to the previous Dashboard in the Playlist. In TV mode the top navbar, row & panel controls will all fade to transparent. This happens automatically after one minute of user inactivity but can also be toggled manually -with the `d v` sequence shortcut. Any mouse movement or keyboard action will +with the `d v` sequence shortcut, or by appending the parameter `?inactive` to the dashboard URL. Any mouse movement or keyboard action will restore navbar & controls. Another feature is the kiosk mode - in kiosk mode the navbar is completely hidden/removed from view. This can be enabled with the `d k` From 55d262655a025c243c4e24d192b3009e3fea26fb Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 13 Apr 2018 11:06:57 +0200 Subject: [PATCH 118/145] changelog: adds note for #11569 [skip ci] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f763a15c82..170d366cb24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,8 @@ * **AuthProxy**: Support IPv6 in Auth proxy white list [#11330](https://github.com/grafana/grafana/pull/11330), thx [@corny](https://github.com/corny) * **SMTP**: Don't connect to STMP server using TLS unless configured. [#7189](https://github.com/grafana/grafana/issues/7189) * **Prometheus**: Escape backslash in labels correctly. [#10555](https://github.com/grafana/grafana/issues/10555), thx [@roidelapluie](https://github.com/roidelapluie) -* **Variables** Case-insensitive sorting for template values [#11128](https://github.com/grafana/grafana/issues/11128) thx [@cross](https://github.com/cross) +* **Variables**: Case-insensitive sorting for template values [#11128](https://github.com/grafana/grafana/issues/11128) thx [@cross](https://github.com/cross) +* **Annotations (native)**: Change default limit from 10 to 100 when querying api [#11569](https://github.com/grafana/grafana/issues/11569), thx [@flopp999](https://github.com/flopp999) # 5.0.4 (2018-03-28) From 9ad8a77a21badc331c5467221eb63da91978c9a2 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Fri, 13 Apr 2018 14:53:36 +0200 Subject: [PATCH 119/145] ordered user orgs alphabeticaly fixes #11556 --- pkg/services/sqlstore/user.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index f42ff5fb2ed..db7e851435c 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -333,6 +333,7 @@ func GetUserOrgList(query *m.GetUserOrgListQuery) error { sess.Join("INNER", "org", "org_user.org_id=org.id") sess.Where("org_user.user_id=?", query.UserId) sess.Cols("org.name", "org_user.role", "org_user.org_id") + sess.OrderBy("org.name") err := sess.Find(&query.Result) return err } From 14bf06726382baf8689c66b07e4ee4123833d2be Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 13 Apr 2018 15:25:13 +0200 Subject: [PATCH 120/145] docs: improves provisoning example for each datasource --- docs/sources/administration/provisioning.md | 1 + docs/sources/features/datasources/cloudwatch.md | 2 +- docs/sources/features/datasources/elasticsearch.md | 2 +- docs/sources/features/datasources/graphite.md | 2 +- docs/sources/features/datasources/influxdb.md | 2 +- docs/sources/features/datasources/mysql.md | 2 +- docs/sources/features/datasources/opentsdb.md | 2 +- docs/sources/features/datasources/postgres.md | 2 +- docs/sources/features/datasources/prometheus.md | 4 ++-- 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/sources/administration/provisioning.md b/docs/sources/administration/provisioning.md index b43ea68bcd8..7936a1708eb 100644 --- a/docs/sources/administration/provisioning.md +++ b/docs/sources/administration/provisioning.md @@ -138,6 +138,7 @@ datasources: ``` #### Custom Settings per Datasource +Please refer to each datasource documentation for specific provisioning examples. | Datasource | Misc | | ---- | ---- | diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index 3aa310eaade..31c51ab96c8 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -178,7 +178,7 @@ Grafana will issue a ListMetrics request. It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) -Here are some examples of how you can configure the Cloudwatch datasource using configuration. +Here are some provisioning examples for this datasource. Using a credentials file ```yaml diff --git a/docs/sources/features/datasources/elasticsearch.md b/docs/sources/features/datasources/elasticsearch.md index c0b5f4b9283..db17aafd271 100644 --- a/docs/sources/features/datasources/elasticsearch.md +++ b/docs/sources/features/datasources/elasticsearch.md @@ -142,7 +142,7 @@ Tags | Optional field name to use for event tags (can be an array or a CSV strin It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) -Here are some examples of how you can configure the Cloudwatch datasource using configuration. +Here are some provisioning examples for this datasource. ```yaml apiVersion: 1 diff --git a/docs/sources/features/datasources/graphite.md b/docs/sources/features/datasources/graphite.md index 895664b09ad..97233572878 100644 --- a/docs/sources/features/datasources/graphite.md +++ b/docs/sources/features/datasources/graphite.md @@ -125,7 +125,7 @@ specify a tag or wildcard (leave empty should also work) It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) -Here are some examples of how you can configure the Cloudwatch datasource using configuration. +Here are some provisioning examples for this datasource. ```yaml apiVersion: 1 diff --git a/docs/sources/features/datasources/influxdb.md b/docs/sources/features/datasources/influxdb.md index 4c5109ef0bc..b49e0f9dfc6 100644 --- a/docs/sources/features/datasources/influxdb.md +++ b/docs/sources/features/datasources/influxdb.md @@ -179,7 +179,7 @@ Tags field can be a comma separated string. It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) -Here are some examples of how you can configure the Cloudwatch datasource using configuration. +Here are some provisioning examples for this datasource. ```yaml apiVersion: 1 diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index 51483bfa36a..8eaab86a906 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -230,7 +230,7 @@ Time series queries should work in alerting conditions. Table formatted queries It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) -Here are some examples of how you can configure the Cloudwatch datasource using configuration. +Here are some provisioning examples for this datasource. ```yaml apiVersion: 1 diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index d29753b0071..6333861dca7 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -93,7 +93,7 @@ For details on OpenTSDB metric queries checkout the official [OpenTSDB documenta It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) -Here are some examples of how you can configure the Cloudwatch datasource using configuration. +Here are some provisioning examples for this datasource. ```yaml apiVersion: 1 diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 104aedc5d5d..00ad2db4c16 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -222,7 +222,7 @@ conditions. It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) -Here are some examples of how you can configure the Cloudwatch datasource using configuration. +Here are some provisioning examples for this datasource. ```yaml apiVersion: 1 diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index 84cb8c2e333..17a24ed0684 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -34,7 +34,7 @@ Name | Description *Basic Auth* | Enable basic authentication to the Prometheus data source. *User* | Name of your Prometheus user *Password* | Database user's password -*Scrape interval* | This will be used as a lower limit for the Prometheus step query parameter. Default value is 15s. +*Scrape interval* | This will be used as a lower limit for the Prometheus step query parameter. Default value is 15s. ## Query editor @@ -105,7 +105,7 @@ Since 4.6.0 Grafana exposes metrics for Prometheus on the `/metrics` endpoint. W It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) -Here are some examples of how you can configure the Cloudwatch datasource using configuration. +Here are some provisioning examples for this datasource. ```yaml apiVersion: 1 From 511b34eb5c28c86c59f56f06e77d38345ca54621 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 13 Apr 2018 16:37:49 +0200 Subject: [PATCH 121/145] docs: improves provisoning example for postgres [skip ci] --- docs/sources/features/datasources/postgres.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 00ad2db4c16..405e8a7fdec 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -233,7 +233,8 @@ datasources: url: localhost:5432 database: grafana user: grafana - password: password + secureJsonData: + password: "Password!" jsonData: sslmode: "disable" # disable/require/verify-ca/verify-full From 91fb2e07ce9b7bf9ee433fd08e3d53506e20a53d Mon Sep 17 00:00:00 2001 From: Mario Trangoni Date: Fri, 13 Apr 2018 18:40:14 +0200 Subject: [PATCH 122/145] pkg: fix codespell issues --- pkg/api/metrics.go | 2 +- pkg/api/static/static.go | 2 +- pkg/components/dashdiffs/formatter_json.go | 2 +- pkg/components/dynmap/dynmap_test.go | 4 ++-- pkg/middleware/recovery.go | 2 +- pkg/models/dashboards.go | 2 +- pkg/models/folders.go | 2 +- pkg/plugins/plugins_test.go | 2 +- pkg/plugins/queries.go | 2 +- pkg/services/alerting/engine_test.go | 8 ++++---- pkg/services/alerting/notifiers/hipchat.go | 2 +- pkg/services/alerting/notifiers/slack.go | 2 +- pkg/services/alerting/notifiers/teams.go | 4 ++-- pkg/services/alerting/notifiers/telegram_test.go | 2 +- pkg/services/alerting/result_handler.go | 2 +- pkg/services/alerting/scheduler.go | 6 +++--- pkg/services/guardian/guardian.go | 2 +- pkg/services/provisioning/dashboards/config_reader.go | 2 +- pkg/services/provisioning/datasources/config_reader.go | 2 +- pkg/services/sqlstore/alert_notification_test.go | 2 +- pkg/tsdb/opentsdb/opentsdb_test.go | 2 +- pkg/tsdb/postgres/macros.go | 2 +- 22 files changed, 29 insertions(+), 29 deletions(-) diff --git a/pkg/api/metrics.go b/pkg/api/metrics.go index 5c06d652b70..c1b8ffe595e 100644 --- a/pkg/api/metrics.go +++ b/pkg/api/metrics.go @@ -75,7 +75,7 @@ func GetTestDataScenarios(c *m.ReqContext) Response { return JSON(200, &result) } -// Genereates a index out of range error +// Generates a index out of range error func GenerateError(c *m.ReqContext) Response { var array []string return JSON(200, array[20]) diff --git a/pkg/api/static/static.go b/pkg/api/static/static.go index 7a61c85b4f3..2a35dd11fa6 100644 --- a/pkg/api/static/static.go +++ b/pkg/api/static/static.go @@ -48,7 +48,7 @@ type StaticOptions struct { // Expires defines which user-defined function to use for producing a HTTP Expires Header // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching AddHeaders func(ctx *macaron.Context) - // FileSystem is the interface for supporting any implmentation of file system. + // FileSystem is the interface for supporting any implementation of file system. FileSystem http.FileSystem } diff --git a/pkg/components/dashdiffs/formatter_json.go b/pkg/components/dashdiffs/formatter_json.go index 3a9ddcc4ee3..488a345d492 100644 --- a/pkg/components/dashdiffs/formatter_json.go +++ b/pkg/components/dashdiffs/formatter_json.go @@ -22,7 +22,7 @@ const ( ) var ( - // changeTypeToSymbol is used for populating the terminating characer in + // changeTypeToSymbol is used for populating the terminating character in // the diff changeTypeToSymbol = map[ChangeType]string{ ChangeNil: "", diff --git a/pkg/components/dynmap/dynmap_test.go b/pkg/components/dynmap/dynmap_test.go index cc002ea06e0..1dacee163f1 100644 --- a/pkg/components/dynmap/dynmap_test.go +++ b/pkg/components/dynmap/dynmap_test.go @@ -76,10 +76,10 @@ func TestFirst(t *testing.T) { assert.True(s == "fallback", "must get string return fallback") s, err = j.GetString("name") - assert.True(s == "anton" && err == nil, "name shoud match") + assert.True(s == "anton" && err == nil, "name should match") s, err = j.GetString("address", "street") - assert.True(s == "Street 42" && err == nil, "street shoud match") + assert.True(s == "Street 42" && err == nil, "street should match") //log.Println("s: ", s.String()) _, err = j.GetNumber("age") diff --git a/pkg/middleware/recovery.go b/pkg/middleware/recovery.go index ec289387aa4..456bc91354e 100644 --- a/pkg/middleware/recovery.go +++ b/pkg/middleware/recovery.go @@ -35,7 +35,7 @@ var ( slash = []byte("/") ) -// stack returns a nicely formated stack frame, skipping skip frames +// stack returns a nicely formatted stack frame, skipping skip frames func stack(skip int) []byte { buf := new(bytes.Buffer) // the returned data // As we loop, we open files and read them. These variables record the currently diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 8cd2b01811c..6393595abb3 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -157,7 +157,7 @@ func NewDashboardFromJson(data *simplejson.Json) *Dashboard { return dash } -// GetDashboardModel turns the command into the savable model +// GetDashboardModel turns the command into the saveable model func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard { dash := NewDashboardFromJson(cmd.Dashboard) userId := cmd.UserId diff --git a/pkg/models/folders.go b/pkg/models/folders.go index c61620a11fc..0c876edcfd7 100644 --- a/pkg/models/folders.go +++ b/pkg/models/folders.go @@ -32,7 +32,7 @@ type Folder struct { HasAcl bool } -// GetDashboardModel turns the command into the savable model +// GetDashboardModel turns the command into the saveable model func (cmd *CreateFolderCommand) GetDashboardModel(orgId int64, userId int64) *Dashboard { dashFolder := NewDashboardFolder(strings.TrimSpace(cmd.Title)) dashFolder.OrgId = orgId diff --git a/pkg/plugins/plugins_test.go b/pkg/plugins/plugins_test.go index 4d3ccb4502b..00329b4a8a1 100644 --- a/pkg/plugins/plugins_test.go +++ b/pkg/plugins/plugins_test.go @@ -12,7 +12,7 @@ import ( func TestPluginScans(t *testing.T) { - Convey("When scaning for plugins", t, func() { + Convey("When scanning for plugins", t, func() { setting.StaticRootPath, _ = filepath.Abs("../../public/") setting.Cfg = ini.Empty() err := initPlugins(context.Background()) diff --git a/pkg/plugins/queries.go b/pkg/plugins/queries.go index 5ae1825a88f..5bd412d2cc9 100644 --- a/pkg/plugins/queries.go +++ b/pkg/plugins/queries.go @@ -37,7 +37,7 @@ func GetPluginSettings(orgId int64) (map[string]*m.PluginSettingInfoDTO, error) // if it's included in app check app settings if pluginDef.IncludedInAppId != "" { - // app componets are by default disabled + // app components are by default disabled opt.Enabled = false if appSettings, ok := pluginMap[pluginDef.IncludedInAppId]; ok { diff --git a/pkg/services/alerting/engine_test.go b/pkg/services/alerting/engine_test.go index 64f954c6dd5..63108bbb9aa 100644 --- a/pkg/services/alerting/engine_test.go +++ b/pkg/services/alerting/engine_test.go @@ -10,7 +10,7 @@ import ( ) type FakeEvalHandler struct { - SuccessCallID int // 0 means never sucess + SuccessCallID int // 0 means never success CallNb int } @@ -87,7 +87,7 @@ func TestEngineProcessJob(t *testing.T) { Convey("Should trigger as many retries as needed", func() { - Convey("never sucess -> max retries number", func() { + Convey("never success -> max retries number", func() { expectedAttempts := alertMaxAttempts evalHandler := NewFakeEvalHandler(0) engine.evalHandler = evalHandler @@ -96,7 +96,7 @@ func TestEngineProcessJob(t *testing.T) { So(evalHandler.CallNb, ShouldEqual, expectedAttempts) }) - Convey("always sucess -> never retry", func() { + Convey("always success -> never retry", func() { expectedAttempts := 1 evalHandler := NewFakeEvalHandler(1) engine.evalHandler = evalHandler @@ -105,7 +105,7 @@ func TestEngineProcessJob(t *testing.T) { So(evalHandler.CallNb, ShouldEqual, expectedAttempts) }) - Convey("some errors before sucess -> some retries", func() { + Convey("some errors before success -> some retries", func() { expectedAttempts := int(math.Ceil(float64(alertMaxAttempts) / 2)) evalHandler := NewFakeEvalHandler(expectedAttempts) engine.evalHandler = evalHandler diff --git a/pkg/services/alerting/notifiers/hipchat.go b/pkg/services/alerting/notifiers/hipchat.go index f1f63d42a04..58e1b7bd71e 100644 --- a/pkg/services/alerting/notifiers/hipchat.go +++ b/pkg/services/alerting/notifiers/hipchat.go @@ -111,7 +111,7 @@ func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error { } message := "" - if evalContext.Rule.State != models.AlertStateOK { //dont add message when going back to alert state ok. + if evalContext.Rule.State != models.AlertStateOK { //don't add message when going back to alert state ok. message += " " + evalContext.Rule.Message } diff --git a/pkg/services/alerting/notifiers/slack.go b/pkg/services/alerting/notifiers/slack.go index e051a71740a..a8139b62726 100644 --- a/pkg/services/alerting/notifiers/slack.go +++ b/pkg/services/alerting/notifiers/slack.go @@ -129,7 +129,7 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error { } message := this.Mention - if evalContext.Rule.State != m.AlertStateOK { //dont add message when going back to alert state ok. + if evalContext.Rule.State != m.AlertStateOK { //don't add message when going back to alert state ok. message += " " + evalContext.Rule.Message } image_url := "" diff --git a/pkg/services/alerting/notifiers/teams.go b/pkg/services/alerting/notifiers/teams.go index 9a9e93dbc47..7f62340d0e1 100644 --- a/pkg/services/alerting/notifiers/teams.go +++ b/pkg/services/alerting/notifiers/teams.go @@ -13,7 +13,7 @@ func init() { alerting.RegisterNotifier(&alerting.NotifierPlugin{ Type: "teams", Name: "Microsoft Teams", - Description: "Sends notifications using Incomming Webhook connector to Microsoft Teams", + Description: "Sends notifications using Incoming Webhook connector to Microsoft Teams", Factory: NewTeamsNotifier, OptionsTemplate: `

Teams settings

@@ -76,7 +76,7 @@ func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error { } message := this.Mention - if evalContext.Rule.State != m.AlertStateOK { //dont add message when going back to alert state ok. + if evalContext.Rule.State != m.AlertStateOK { //don't add message when going back to alert state ok. message += " " + evalContext.Rule.Message } else { message += " " // summary must not be empty diff --git a/pkg/services/alerting/notifiers/telegram_test.go b/pkg/services/alerting/notifiers/telegram_test.go index 05be787dced..98c8d884ad0 100644 --- a/pkg/services/alerting/notifiers/telegram_test.go +++ b/pkg/services/alerting/notifiers/telegram_test.go @@ -100,7 +100,7 @@ func TestTelegramNotifier(t *testing.T) { So(caption, ShouldContainSubstring, "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I promise ") }) - Convey("Metrics should be skipped if they dont fit", func() { + Convey("Metrics should be skipped if they don't fit", func() { evalContext := alerting.NewEvalContext(nil, &alerting.Rule{ Name: "This is an alarm", Message: "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I ", diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index 8f9deb758a6..5d95e090c9e 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -56,7 +56,7 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { if err := bus.Dispatch(cmd); err != nil { if err == m.ErrCannotChangeStateOnPausedAlert { - handler.log.Error("Cannot change state on alert thats pause", "error", err) + handler.log.Error("Cannot change state on alert that's paused", "error", err) return err } diff --git a/pkg/services/alerting/scheduler.go b/pkg/services/alerting/scheduler.go index 151f802ec15..b0a3f8303c4 100644 --- a/pkg/services/alerting/scheduler.go +++ b/pkg/services/alerting/scheduler.go @@ -58,7 +58,7 @@ func (s *SchedulerImpl) Tick(tickTime time.Time, execQueue chan *Job) { if job.OffsetWait && now%job.Offset == 0 { job.OffsetWait = false - s.enque(job, execQueue) + s.enqueue(job, execQueue) continue } @@ -66,13 +66,13 @@ func (s *SchedulerImpl) Tick(tickTime time.Time, execQueue chan *Job) { if job.Offset > 0 { job.OffsetWait = true } else { - s.enque(job, execQueue) + s.enqueue(job, execQueue) } } } } -func (s *SchedulerImpl) enque(job *Job, execQueue chan *Job) { +func (s *SchedulerImpl) enqueue(job *Job, execQueue chan *Job) { s.log.Debug("Scheduler: Putting job on to exec queue", "name", job.Rule.Name, "id", job.Rule.Id) execQueue <- job } diff --git a/pkg/services/guardian/guardian.go b/pkg/services/guardian/guardian.go index 811b38cac86..6e13817b902 100644 --- a/pkg/services/guardian/guardian.go +++ b/pkg/services/guardian/guardian.go @@ -113,7 +113,7 @@ func (g *dashboardGuardianImpl) checkAcl(permission m.PermissionType, acl []*m.D return false, err } - // evalute team rules + // evaluate team rules for _, p := range acl { for _, ug := range teams { if ug.Id == p.TeamId && p.Permission >= permission { diff --git a/pkg/services/provisioning/dashboards/config_reader.go b/pkg/services/provisioning/dashboards/config_reader.go index 9030ba609b9..8ac79df0fac 100644 --- a/pkg/services/provisioning/dashboards/config_reader.go +++ b/pkg/services/provisioning/dashboards/config_reader.go @@ -58,7 +58,7 @@ func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) { files, err := ioutil.ReadDir(cr.path) if err != nil { - cr.log.Error("cant read dashboard provisioning files from directory", "path", cr.path) + cr.log.Error("can't read dashboard provisioning files from directory", "path", cr.path) return dashboards, nil } diff --git a/pkg/services/provisioning/datasources/config_reader.go b/pkg/services/provisioning/datasources/config_reader.go index 58ed5472a6b..4b8931f0ed3 100644 --- a/pkg/services/provisioning/datasources/config_reader.go +++ b/pkg/services/provisioning/datasources/config_reader.go @@ -19,7 +19,7 @@ func (cr *configReader) readConfig(path string) ([]*DatasourcesAsConfig, error) files, err := ioutil.ReadDir(path) if err != nil { - cr.log.Error("cant read datasource provisioning files from directory", "path", path) + cr.log.Error("can't read datasource provisioning files from directory", "path", path) return datasources, nil } diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index d37062fb58f..761114978a8 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -21,7 +21,7 @@ func TestAlertNotificationSQLAccess(t *testing.T) { } err := GetAlertNotifications(cmd) - fmt.Printf("errror %v", err) + fmt.Printf("error %v", err) So(err, ShouldBeNil) So(cmd.Result, ShouldBeNil) }) diff --git a/pkg/tsdb/opentsdb/opentsdb_test.go b/pkg/tsdb/opentsdb/opentsdb_test.go index 094deb9e8ec..fe03599f54d 100644 --- a/pkg/tsdb/opentsdb/opentsdb_test.go +++ b/pkg/tsdb/opentsdb/opentsdb_test.go @@ -35,7 +35,7 @@ func TestOpenTsdbExecutor(t *testing.T) { }) - Convey("Build metric with downsampling diabled", func() { + Convey("Build metric with downsampling disabled", func() { query := &tsdb.Query{ Model: simplejson.New(), diff --git a/pkg/tsdb/postgres/macros.go b/pkg/tsdb/postgres/macros.go index 23daeebec5a..dee25592623 100644 --- a/pkg/tsdb/postgres/macros.go +++ b/pkg/tsdb/postgres/macros.go @@ -79,7 +79,7 @@ func (m *PostgresMacroEngine) evaluateMacro(name string, args []string) (string, } return fmt.Sprintf("extract(epoch from %s) as \"time\"", args[0]), nil case "__timeFilter": - // dont use to_timestamp in this macro for redshift compatibility #9566 + // don't use to_timestamp in this macro for redshift compatibility #9566 if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } From 3424fa94c29a1a59ba59bb100e363aa53321bd59 Mon Sep 17 00:00:00 2001 From: Mario Trangoni Date: Fri, 13 Apr 2018 18:44:49 +0200 Subject: [PATCH 123/145] scripts: fix codespell issues --- scripts/build/build_container.sh | 2 +- scripts/build/publish.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/build_container.sh b/scripts/build/build_container.sh index f130e2f94db..16406993e75 100755 --- a/scripts/build/build_container.sh +++ b/scripts/build/build_container.sh @@ -12,6 +12,6 @@ if [[ -e ~/docker/centos.tar ]]; then else docker build --rm=false --tag "grafana/buildcontainer" ./scripts/build/ - # save docker container so we dont have to recreate it next run + # save docker container so we don't have to recreate it next run docker save grafana/buildcontainer > ~/docker/centos.tar; fi diff --git a/scripts/build/publish.go b/scripts/build/publish.go index 7e88c06f67f..500cb0d48ab 100644 --- a/scripts/build/publish.go +++ b/scripts/build/publish.go @@ -125,7 +125,7 @@ func postRequest(url string, obj interface{}, desc string) { } else { log.Printf("Action: %s \t Failed - Status: %v", desc, res.Status) log.Printf("Resp: %s", body) - log.Fatalf("Quiting") + log.Fatalf("Quitting") } } } From 3fb204cc0deb56fad483f5367287591d5896b1a7 Mon Sep 17 00:00:00 2001 From: Mario Trangoni Date: Fri, 13 Apr 2018 19:02:28 +0200 Subject: [PATCH 124/145] CHANGELOG.md: fix codespell issues --- CHANGELOG.md | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 170d366cb24..dd83280584c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,7 +57,7 @@ * **Dashboards**: Changing templated value from dropdown is causing unsaved changes [#11063](https://github.com/grafana/grafana/issues/11063) * **Prometheus**: Fixes bundled Prometheus 2.0 dashboard [#11016](https://github.com/grafana/grafana/issues/11016), thx [@roidelapluie](https://github.com/roidelapluie) * **Sidemenu**: Profile menu "invisible" when gravatar is disabled [#11097](https://github.com/grafana/grafana/issues/11097) -* **Dashboard**: Fixes a bug with resizeable handles for panels [#11103](https://github.com/grafana/grafana/issues/11103) +* **Dashboard**: Fixes a bug with resizable handles for panels [#11103](https://github.com/grafana/grafana/issues/11103) * **Alerting**: Telegram inline image mode fails when caption too long [#10975](https://github.com/grafana/grafana/issues/10975) * **Alerting**: Fixes silent failing validation [#11145](https://github.com/grafana/grafana/pull/11145) * **OAuth**: Only use jwt token if it contains an email address [#11127](https://github.com/grafana/grafana/pull/11127) @@ -121,7 +121,7 @@ Grafana v5.0 is going to be the biggest and most foundational release Grafana ha ### New Major Features - **Dashboards** Dashboard folders, [#1611](https://github.com/grafana/grafana/issues/1611) - **Teams** User groups (teams) implemented. Can be used in folder & dashboard permission list. -- **Dashboard grid**: Panels are now layed out in a two dimensional grid (with x, y, w, h). [#9093](https://github.com/grafana/grafana/issues/9093). +- **Dashboard grid**: Panels are now laid out in a two dimensional grid (with x, y, w, h). [#9093](https://github.com/grafana/grafana/issues/9093). - **Templating**: Vertical repeat direction for panel repeats. - **UX**: Major update to page header and navigation - **Dashboard settings**: Combine dashboard settings views into one with side menu, [#9750](https://github.com/grafana/grafana/issues/9750) @@ -155,7 +155,7 @@ Dashboard panels and rows are positioned using a gridPos object `{x: 0, y: 0, w: * **Dashboard history**: New config file option versions_to_keep sets how many versions per dashboard to store, [#9671](https://github.com/grafana/grafana/issues/9671) * **Dashboard as cfg**: Load dashboards from file into Grafana on startup/change [#9654](https://github.com/grafana/grafana/issues/9654) [#5269](https://github.com/grafana/grafana/issues/5269) * **Prometheus**: Grafana can now send alerts to Prometheus Alertmanager while firing [#7481](https://github.com/grafana/grafana/issues/7481), thx [@Thib17](https://github.com/Thib17) and [@mtanda](https://github.com/mtanda) -* **Table**: Support multiple table formated queries in table panel [#9170](https://github.com/grafana/grafana/issues/9170), thx [@davkal](https://github.com/davkal) +* **Table**: Support multiple table formatted queries in table panel [#9170](https://github.com/grafana/grafana/issues/9170), thx [@davkal](https://github.com/davkal) * **Security**: Protect against brute force (frequent) login attempts [#7616](https://github.com/grafana/grafana/issues/7616) ## Minor @@ -177,7 +177,7 @@ Dashboard panels and rows are positioned using a gridPos object `{x: 0, y: 0, w: * **Sensu**: Send alert message to sensu output [#9551](https://github.com/grafana/grafana/issues/9551), thx [@cjchand](https://github.com/cjchand) * **Singlestat**: suppress error when result contains no datapoints [#9636](https://github.com/grafana/grafana/issues/9636), thx [@utkarshcmu](https://github.com/utkarshcmu) * **Postgres/MySQL**: Control quoting in SQL-queries when using template variables [#9030](https://github.com/grafana/grafana/issues/9030), thanks [@svenklemm](https://github.com/svenklemm) -* **Pagerduty**: Pagerduty dont auto resolve incidents by default anymore. [#10222](https://github.com/grafana/grafana/issues/10222) +* **Pagerduty**: Pagerduty don't auto resolve incidents by default anymore. [#10222](https://github.com/grafana/grafana/issues/10222) * **Cloudwatch**: Fix for multi-valued templated queries. [#9903](https://github.com/grafana/grafana/issues/9903) ## Tech @@ -255,7 +255,7 @@ The following properties have been deprecated and will be removed in a future re * **Annotations**: Add support for creating annotations from graph panel [#8197](https://github.com/grafana/grafana/pull/8197) * **GCS**: Adds support for Google Cloud Storage [#8370](https://github.com/grafana/grafana/issues/8370) thx [@chuhlomin](https://github.com/chuhlomin) * **Prometheus**: Adds /metrics endpoint for exposing Grafana metrics. [#9187](https://github.com/grafana/grafana/pull/9187) -* **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk) +* **Graph**: Add support for local formatting in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk) * **Jaeger**: Add support for open tracing using jaeger in Grafana. [#9213](https://github.com/grafana/grafana/pull/9213) * **Unit types**: New date & time unit types added, useful in singlestat to show dates & times. [#3678](https://github.com/grafana/grafana/issues/3678), [#6710](https://github.com/grafana/grafana/issues/6710), [#2764](https://github.com/grafana/grafana/issues/2764) * **CLI**: Make it possible to install plugins from any url [#5873](https://github.com/grafana/grafana/issues/5873) @@ -292,7 +292,7 @@ The following properties have been deprecated and will be removed in a future re * **Graphite**: Fix for Grafana internal metrics to Graphite sending NaN values [#9279](https://github.com/grafana/grafana/issues/9279) * **HTTP API**: Fix for HEAD method requests [#9307](https://github.com/grafana/grafana/issues/9307) * **Templating**: Fix for duplicate template variable queries when refresh is set to time range change [#9185](https://github.com/grafana/grafana/issues/9185) -* **Metrics**: dont write NaN values to graphite [#9279](https://github.com/grafana/grafana/issues/9279) +* **Metrics**: don't write NaN values to graphite [#9279](https://github.com/grafana/grafana/issues/9279) # 4.5.1 (2017-09-15) @@ -329,12 +329,12 @@ The following properties have been deprecated and will be removed in a future re ### Breaking change * **InfluxDB/Elasticsearch**: The panel & data source option named "Group by time interval" is now named "Min time interval" and does now always define a lower limit for the auto group by time. Without having to use `>` prefix (that prefix still works). This should in theory have close to zero actual impact on existing dashboards. It does mean that if you used this setting to define a hard group by time interval of, say "1d", if you zoomed to a time range wide enough the time range could increase above the "1d" range as the setting is now always considered a lower limit. -* **Elasticsearch**: Elasticsearch metric queries without date histogram now return table formated data making table panel much easier to use for this use case. Should not break/change existing dashboards with stock panels but external panel plugins can be affected. +* **Elasticsearch**: Elasticsearch metric queries without date histogram now return table formatted data making table panel much easier to use for this use case. Should not break/change existing dashboards with stock panels but external panel plugins can be affected. ## Changes * **InfluxDB**: Change time range filter for absolute time ranges to be inclusive instead of exclusive [#8319](https://github.com/grafana/grafana/issues/8319), thx [@Oxydros](https://github.com/Oxydros) -* **InfluxDB**: Added paranthesis around tag filters in queries [#9131](https://github.com/grafana/grafana/pull/9131) +* **InfluxDB**: Added parenthesis around tag filters in queries [#9131](https://github.com/grafana/grafana/pull/9131) ## Bug Fixes @@ -346,7 +346,7 @@ The following properties have been deprecated and will be removed in a future re ## Bug Fixes -* **Search**: Fix for issue that casued search view to hide when you clicked starred or tags filters, fixes [#8981](https://github.com/grafana/grafana/issues/8981) +* **Search**: Fix for issue that caused search view to hide when you clicked starred or tags filters, fixes [#8981](https://github.com/grafana/grafana/issues/8981) * **Modals**: ESC key now closes modal again, fixes [#8981](https://github.com/grafana/grafana/issues/8988), thx [@j-white](https://github.com/j-white) # 4.4.2 (2017-08-01) @@ -685,12 +685,12 @@ due to too many connections/file handles on the data source backend. This proble ### Enhancements * **Login**: Adds option to disable username/password logins, closes [#4674](https://github.com/grafana/grafana/issues/4674) * **SingleStat**: Add seriename as option in singlestat panel, closes [#4740](https://github.com/grafana/grafana/issues/4740) -* **Localization**: Week start day now dependant on browser locale setting, closes [#3003](https://github.com/grafana/grafana/issues/3003) +* **Localization**: Week start day now dependent on browser locale setting, closes [#3003](https://github.com/grafana/grafana/issues/3003) * **Templating**: Update panel repeats for variables that change on time refresh, closes [#5021](https://github.com/grafana/grafana/issues/5021) * **Templating**: Add support for numeric and alphabetical sorting of variable values, closes [#2839](https://github.com/grafana/grafana/issues/2839) * **Elasticsearch**: Support to set Precision Threshold for Unique Count metric, closes [#4689](https://github.com/grafana/grafana/issues/4689) * **Navigation**: Add search to org swithcer, closes [#2609](https://github.com/grafana/grafana/issues/2609) -* **Database**: Allow database config using one propertie, closes [#5456](https://github.com/grafana/grafana/pull/5456) +* **Database**: Allow database config using one property, closes [#5456](https://github.com/grafana/grafana/pull/5456) * **Graphite**: Add support for groupByNodes, closes [#5613](https://github.com/grafana/grafana/pull/5613) * **Influxdb**: Add support for elapsed(), closes [#5827](https://github.com/grafana/grafana/pull/5827) * **OpenTSDB**: Add support for explicitTags for OpenTSDB>=2.3, closes [#6360](https://github.com/grafana/grafana/pull/6361) @@ -757,7 +757,7 @@ due to too many connections/file handles on the data source backend. This proble * **Datasource**: Pending data source requests are cancelled before new ones are issues (Graphite & Prometheus), closes [#5321](https://github.com/grafana/grafana/issues/5321) ### Breaking changes -* **Logging** : Changed default logging output format (now structured into message, and key value pairs, with logger key acting as component). You can also no change in config to json log ouput. +* **Logging** : Changed default logging output format (now structured into message, and key value pairs, with logger key acting as component). You can also no change in config to json log output. * **Graphite** : The Graph panel no longer have a Graphite PNG option. closes [#5367](https://github.com/grafana/grafana/issues/5367) ### Bug fixes @@ -775,7 +775,7 @@ due to too many connections/file handles on the data source backend. This proble * **Annotations**: Annotations can now use a template variable as data source, closes [#5054](https://github.com/grafana/grafana/issues/5054) * **Time picker**: Fixed issue timepicker and UTC when reading time from URL, fixes [#5078](https://github.com/grafana/grafana/issues/5078) * **CloudWatch**: Support for Multiple Account by AssumeRole, closes [#3522](https://github.com/grafana/grafana/issues/3522) -* **Singlestat**: Fixed alignment and minium height issue, fixes [#5113](https://github.com/grafana/grafana/issues/5113), fixes [#4679](https://github.com/grafana/grafana/issues/4679) +* **Singlestat**: Fixed alignment and minimum height issue, fixes [#5113](https://github.com/grafana/grafana/issues/5113), fixes [#4679](https://github.com/grafana/grafana/issues/4679) * **Share modal**: Fixed link when using grafana under dashboard sub url, fixes [#5109](https://github.com/grafana/grafana/issues/5109) * **Prometheus**: Fixed bug in query editor that caused it not to load when reloading page, fixes [#5107](https://github.com/grafana/grafana/issues/5107) * **Elasticsearch**: Fixed bug when template variable query returns numeric values, fixes [#5097](https://github.com/grafana/grafana/issues/5097), fixes [#5088](https://github.com/grafana/grafana/issues/5088) @@ -792,7 +792,7 @@ due to too many connections/file handles on the data source backend. This proble * **Graph**: Fixed broken PNG rendering in graph panel, fixes [#5025](https://github.com/grafana/grafana/issues/5025) * **Graph**: Fixed broken xaxis on graph panel, fixes [#5024](https://github.com/grafana/grafana/issues/5024) -* **Influxdb**: Fixes crash when hiding middle serie, fixes [#5005](https://github.com/grafana/grafana/issues/5005) +* **Influxdb**: Fixes crash when hiding middle series, fixes [#5005](https://github.com/grafana/grafana/issues/5005) # 3.0.1 Stable (2016-05-11) @@ -804,7 +804,7 @@ due to too many connections/file handles on the data source backend. This proble ### Bug fixes * **Dashboard title**: Fixed max dashboard title width (media query) for large screens, fixes [#4859](https://github.com/grafana/grafana/issues/4859) * **Annotations**: Fixed issue with entering annotation edit view, fixes [#4857](https://github.com/grafana/grafana/issues/4857) -* **Remove query**: Fixed issue with removing query for data sources without collapsable query editors, fixes [#4856](https://github.com/grafana/grafana/issues/4856) +* **Remove query**: Fixed issue with removing query for data sources without collapsible query editors, fixes [#4856](https://github.com/grafana/grafana/issues/4856) * **Graphite PNG**: Fixed issue graphite png rendering option, fixes [#4864](https://github.com/grafana/grafana/issues/4864) * **InfluxDB**: Fixed issue missing plus group by iconn, fixes [#4862](https://github.com/grafana/grafana/issues/4862) * **Graph**: Fixes missing line mode for thresholds, fixes [#4902](https://github.com/grafana/grafana/pull/4902) @@ -820,11 +820,11 @@ due to too many connections/file handles on the data source backend. This proble ### Bug fixes * **InfluxDB 0.12**: Fixed issue templating and `show tag values` query only returning tags for first measurement, fixes [#4726](https://github.com/grafana/grafana/issues/4726) -* **Templating**: Fixed issue with regex formating when matching multiple values, fixes [#4755](https://github.com/grafana/grafana/issues/4755) +* **Templating**: Fixed issue with regex formatting when matching multiple values, fixes [#4755](https://github.com/grafana/grafana/issues/4755) * **Templating**: Fixed issue with custom all value and escaping, fixes [#4736](https://github.com/grafana/grafana/issues/4736) * **Dashlist**: Fixed issue dashboard list panel and caching tags, fixes [#4768](https://github.com/grafana/grafana/issues/4768) * **Graph**: Fixed issue with unneeded scrollbar in legend for Firefox, fixes [#4760](https://github.com/grafana/grafana/issues/4760) -* **Table panel**: Fixed issue table panel formating string array properties, fixes [#4791](https://github.com/grafana/grafana/issues/4791) +* **Table panel**: Fixed issue table panel formatting string array properties, fixes [#4791](https://github.com/grafana/grafana/issues/4791) * **grafana-cli**: Improve error message when failing to install plugins due to corrupt response, fixes [#4651](https://github.com/grafana/grafana/issues/4651) * **Singlestat**: Fixes prefix an postfix for gauges, fixes [#4812](https://github.com/grafana/grafana/issues/4812) * **Singlestat**: Fixes auto-refresh on change for some options, fixes [#4809](https://github.com/grafana/grafana/issues/4809) @@ -916,7 +916,7 @@ slack channel (link to slack channel in readme). ### Bug fixes * **Playlist**: Fix for memory leak when running a playlist, closes [#3794](https://github.com/grafana/grafana/pull/3794) * **InfluxDB**: Fix for InfluxDB and table panel when using Format As Table and having group by time, fixes [#3928](https://github.com/grafana/grafana/issues/3928) -* **Panel Time shift**: Fix for panel time range and using dashboard times liek `Today` and `This Week`, fixes [#3941](https://github.com/grafana/grafana/issues/3941) +* **Panel Time shift**: Fix for panel time range and using dashboard times like `Today` and `This Week`, fixes [#3941](https://github.com/grafana/grafana/issues/3941) * **Row repeat**: Repeated rows will now appear next to each other and not by the bottom of the dashboard, fixes [#3942](https://github.com/grafana/grafana/issues/3942) * **Png renderer**: Fix for phantomjs path on windows, fixes [#3657](https://github.com/grafana/grafana/issues/3657) @@ -940,7 +940,7 @@ slack channel (link to slack channel in readme). ### Bug Fixes * **metric editors**: Fix for clicking typeahead auto dropdown option, fixes [#3428](https://github.com/grafana/grafana/issues/3428) * **influxdb**: Fixed issue showing Group By label only on first query, fixes [#3453](https://github.com/grafana/grafana/issues/3453) -* **logging**: Add more verbose info logging for http reqeusts, closes [#3405](https://github.com/grafana/grafana/pull/3405) +* **logging**: Add more verbose info logging for http requests, closes [#3405](https://github.com/grafana/grafana/pull/3405) # 2.6.0-Beta1 (2015-12-04) @@ -967,7 +967,7 @@ slack channel (link to slack channel in readme). **New Feature: Mix data sources** - A built in data source is now available named `-- Mixed --`, When picked in the metrics tab, -it allows you to add queries of differnet data source types & instances to the same graph/panel! +it allows you to add queries of different data source types & instances to the same graph/panel! [Issue #436](https://github.com/grafana/grafana/issues/436) **New Feature: Elasticsearch Metrics Query Editor and Viz Support** @@ -1006,7 +1006,7 @@ it allows you to add queries of differnet data source types & instances to the s - [Issue #2564](https://github.com/grafana/grafana/issues/2564). Templating: Another atempt at fixing #2534 (Init multi value template var used in repeat panel from url) - [Issue #2620](https://github.com/grafana/grafana/issues/2620). Graph: multi series tooltip did no highlight correct point when stacking was enabled and series were of different resolution - [Issue #2636](https://github.com/grafana/grafana/issues/2636). InfluxDB: Do no show template vars in dropdown for tag keys and group by keys -- [Issue #2604](https://github.com/grafana/grafana/issues/2604). InfluxDB: More alias options, can now use `$[0-9]` syntax to reference part of a measurement name (seperated by dots) +- [Issue #2604](https://github.com/grafana/grafana/issues/2604). InfluxDB: More alias options, can now use `$[0-9]` syntax to reference part of a measurement name (separated by dots) **Breaking Changes** - Notice to makers/users of custom data sources, there is a minor breaking change in 2.2 that @@ -1088,7 +1088,7 @@ Grunt & Watch tasks: - [Issue #1826](https://github.com/grafana/grafana/issues/1826). User role 'Viewer' are now prohibited from entering edit mode (and doing other transient dashboard edits). A new role `Read Only Editor` will replace the old Viewer behavior - [Issue #1928](https://github.com/grafana/grafana/issues/1928). HTTP API: GET /api/dashboards/db/:slug response changed property `model` to `dashboard` to match the POST request nameing - Backend render URL changed from `/render/dashboard/solo` `render/dashboard-solo/` (in order to have consistent dashboard url `/dashboard/:type/:slug`) -- Search HTTP API response has changed (simplified), tags list moved to seperate HTTP resource URI +- Search HTTP API response has changed (simplified), tags list moved to separate HTTP resource URI - Datasource HTTP api breaking change, ADD datasource is now POST /api/datasources/, update is now PUT /api/datasources/:id **Fixes** @@ -1105,7 +1105,7 @@ Grunt & Watch tasks: # 2.0.2 (2015-04-22) **Fixes** -- [Issue #1832](https://github.com/grafana/grafana/issues/1832). Graph Panel + Legend Table mode: Many series casued zero height graph, now legend will never reduce the height of the graph below 50% of row height. +- [Issue #1832](https://github.com/grafana/grafana/issues/1832). Graph Panel + Legend Table mode: Many series caused zero height graph, now legend will never reduce the height of the graph below 50% of row height. - [Issue #1846](https://github.com/grafana/grafana/issues/1846). Snapshots: Fixed issue with snapshoting dashboards with an interval template variable - [Issue #1848](https://github.com/grafana/grafana/issues/1848). Panel timeshift: You can now use panel timeshift without a relative time override @@ -1147,7 +1147,7 @@ Grunt & Watch tasks: **Fixes** - [Issue #1649](https://github.com/grafana/grafana/issues/1649). HTTP API: grafana /render calls nows with api keys -- [Issue #1667](https://github.com/grafana/grafana/issues/1667). Datasource proxy & session timeout fix (casued 401 Unauthorized error after a while) +- [Issue #1667](https://github.com/grafana/grafana/issues/1667). Datasource proxy & session timeout fix (caused 401 Unauthorized error after a while) - [Issue #1707](https://github.com/grafana/grafana/issues/1707). Unsaved changes: Do not show for snapshots, scripted and file based dashboards - [Issue #1703](https://github.com/grafana/grafana/issues/1703). Unsaved changes: Do not show for users with role `Viewer` - [Issue #1675](https://github.com/grafana/grafana/issues/1675). Data source proxy: Fixed issue with Gzip enabled and data source proxy @@ -1160,14 +1160,14 @@ Grunt & Watch tasks: **Important Note** -Grafana 2.x is fundamentally different from 1.x; it now ships with an integrated backend server. Please read the [Documentation](http://docs.grafana.org) for more detailed about this SIGNIFCANT change to Grafana +Grafana 2.x is fundamentally different from 1.x; it now ships with an integrated backend server. Please read the [Documentation](http://docs.grafana.org) for more detailed about this SIGNIFICANT change to Grafana **New features** - [Issue #1623](https://github.com/grafana/grafana/issues/1623). Share Dashboard: Dashboard snapshot sharing (dash and data snapshot), save to local or save to public snapshot dashboard snapshots.raintank.io site - [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embedd a single graph on another web site -- [Issue #718](https://github.com/grafana/grafana/issues/718). Dashboard: When saving a dashboard and another user has made changes inbetween the user is promted with a warning if he really wants to overwrite the other's changes +- [Issue #718](https://github.com/grafana/grafana/issues/718). Dashboard: When saving a dashboard and another user has made changes in between the user is promted with a warning if he really wants to overwrite the other's changes - [Issue #1331](https://github.com/grafana/grafana/issues/1331). Graph & Singlestat: New axis/unit format selector and more units (kbytes, Joule, Watt, eV), and new design for graph axis & grid tab and single stat options tab views -- [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, usefull when you want to ignore last minute because it contains incomplete data +- [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, useful when you want to ignore last minute because it contains incomplete data - [Issue #171](https://github.com/grafana/grafana/issues/171). Panel: Different time periods, panels can override dashboard relative time and/or add a time shift - [Issue #1488](https://github.com/grafana/grafana/issues/1488). Dashboard: Clone dashboard / Save as - [Issue #1458](https://github.com/grafana/grafana/issues/1458). User: persisted user option for dark or light theme (no longer an option on a dashboard) @@ -1198,7 +1198,7 @@ Grafana 2.x is fundamentally different from 1.x; it now ships with an integrated **OpenTSDB breaking change** - [Issue #1438](https://github.com/grafana/grafana/issues/1438). OpenTSDB: Automatic downsample interval passed to OpenTSDB (depends on timespan and graph width) -- NOTICE, Downsampling is now enabled by default, so if you have not picked a downsample aggregator in your metric query do so or your graphs will be missleading +- NOTICE, Downsampling is now enabled by default, so if you have not picked a downsample aggregator in your metric query do so or your graphs will be misleading - This will make Grafana a lot quicker for OpenTSDB users when viewing large time spans without having to change the downsample interval manually. **Tech** @@ -1229,7 +1229,7 @@ Grafana 2.x is fundamentally different from 1.x; it now ships with an integrated - [Issue #1114](https://github.com/grafana/grafana/issues/1114). Graphite: Lexer fix, allow equal sign (=) in metric paths - [Issue #1136](https://github.com/grafana/grafana/issues/1136). Graph: Fix to legend value Max and negative values - [Issue #1150](https://github.com/grafana/grafana/issues/1150). SinglestatPanel: Fixed absolute drilldown link issue -- [Issue #1123](https://github.com/grafana/grafana/issues/1123). Firefox: Workaround for Firefox bug, casued input text fields to not be selectable and not have placeable cursor +- [Issue #1123](https://github.com/grafana/grafana/issues/1123). Firefox: Workaround for Firefox bug, caused input text fields to not be selectable and not have placeable cursor - [Issue #1108](https://github.com/grafana/grafana/issues/1108). Graph: Fix for tooltip series order when series draw order was changed with zindex property # 1.9.0-rc1 (2014-11-17) @@ -1306,7 +1306,7 @@ Read this [blog post](https://grafana.com/blog/2014/09/11/grafana-1.8.0-rc1-rele - [Issue #234](https://github.com/grafana/grafana/issues/234). Templating: Interval variable type for time intervals summarize/group by parameter, included "auto" option, and auto step counts option. - [Issue #262](https://github.com/grafana/grafana/issues/262). Templating: Ability to use template variables for function parameters via custom variable type, can be used as parameter for movingAverage or scaleToSeconds for example - [Issue #312](https://github.com/grafana/grafana/issues/312). Templating: Can now use template variables in panel titles -- [Issue #613](https://github.com/grafana/grafana/issues/613). Templating: Full support for InfluxDB, filter by part of series names, extract series substrings, nested queries, multipe where clauses! +- [Issue #613](https://github.com/grafana/grafana/issues/613). Templating: Full support for InfluxDB, filter by part of series names, extract series substrings, nested queries, multiple where clauses! - Template variables can be initialized from url, with var-my_varname=value, breaking change, before it was just my_varname. - Templating and url state sync has some issues that are not solved for this release, see [Issue #772](https://github.com/grafana/grafana/issues/772) for more details. @@ -1395,7 +1395,7 @@ Read this [blog post](https://grafana.com/blog/2014/09/11/grafana-1.8.0-rc1-rele - [Issue #136](https://github.com/grafana/grafana/issues/136). Graph: New legend display option "Align as table" - [Issue #556](https://github.com/grafana/grafana/issues/556). Graph: New legend display option "Right side", will show legend to the right of the graph - [Issue #604](https://github.com/grafana/grafana/issues/604). Graph: New axis format, 'bps' (SI unit in steps of 1000) useful for network gear metics -- [Issue #626](https://github.com/grafana/grafana/issues/626). Graph: Downscale y axis to more precise unit, value of 0.1 for seconds format will be formated as 100 ms. Thanks @kamaradclimber +- [Issue #626](https://github.com/grafana/grafana/issues/626). Graph: Downscale y axis to more precise unit, value of 0.1 for seconds format will be formatted as 100 ms. Thanks @kamaradclimber - [Issue #618](https://github.com/grafana/grafana/issues/618). OpenTSDB: Series alias option to override metric name returned from opentsdb. Thanks @heldr **Documentation** @@ -1425,13 +1425,13 @@ Read this [blog post](https://grafana.com/blog/2014/09/11/grafana-1.8.0-rc1-rele - [Issue #522](https://github.com/grafana/grafana/issues/522). Series names and column name typeahead cache fix - [Issue #504](https://github.com/grafana/grafana/issues/504). Fixed influxdb issue with raw query that caused wrong value column detection - [Issue #526](https://github.com/grafana/grafana/issues/526). Default property that marks which datasource is default in config.js is now optional -- [Issue #342](https://github.com/grafana/grafana/issues/342). Auto-refresh caused 2 refreshes (and hence mulitple queries) each time (at least in firefox) +- [Issue #342](https://github.com/grafana/grafana/issues/342). Auto-refresh caused 2 refreshes (and hence multiple queries) each time (at least in firefox) # 1.6.0 (2014-06-16) #### New features or improvements - [Issue #427](https://github.com/grafana/grafana/issues/427). New Y-axis formater for metric values that represent seconds, Thanks @jippi -- [Issue #390](https://github.com/grafana/grafana/issues/390). Allow special characters in serie names (influxdb datasource), Thanks @majst01 +- [Issue #390](https://github.com/grafana/grafana/issues/390). Allow special characters in series names (influxdb datasource), Thanks @majst01 - [Issue #428](https://github.com/grafana/grafana/issues/428). Refactoring of filterSrv, Thanks @Tetha - [Issue #445](https://github.com/grafana/grafana/issues/445). New config for playlist feature. Set playlist_timespan to set default playlist interval, Thanks @rmca - [Issue #461](https://github.com/grafana/grafana/issues/461). New graphite function definition added isNonNull, Thanks @tmonk42 @@ -1452,13 +1452,13 @@ Read this [blog post](https://grafana.com/blog/2014/09/11/grafana-1.8.0-rc1-rele - [Issue #475](https://github.com/grafana/grafana/issues/475). Add panel icon and Row edit button is replaced by the Row edit menu - New graphs now have a default empty query - Add Row button now creates a row with default height of 250px (no longer opens dashboard settings modal) -- Clean up of config.sample.js, graphiteUrl removed (still works, but depricated, removed in future) +- Clean up of config.sample.js, graphiteUrl removed (still works, but deprecated, removed in future) Use datasources config instead. panel_names removed from config.js. Use plugins.panels to add custom panels - Graphite panel is now renamed graph (Existing dashboards will still work) #### Fixes - [Issue #126](https://github.com/grafana/grafana/issues/126). Graphite query lexer change, can now handle regex parameters for aliasSub function -- [Issue #447](https://github.com/grafana/grafana/issues/447). Filter option loading when having muliple nested filters now works better. Options are now reloaded correctly and there are no multiple renders/refresh inbetween. +- [Issue #447](https://github.com/grafana/grafana/issues/447). Filter option loading when having muliple nested filters now works better. Options are now reloaded correctly and there are no multiple renders/refresh in between. - [Issue #412](https://github.com/grafana/grafana/issues/412). After a filter option is changed and a nested template param is reloaded, if the current value exists after the options are reloaded the current selected value is kept. - [Issue #460](https://github.com/grafana/grafana/issues/460). Legend Current value did not display when value was zero - [Issue #328](https://github.com/grafana/grafana/issues/328). Fix to series toggling bug that caused annotations to be hidden when toggling/hiding series. From 9a11b574ca994e1ca7eb13f9da54afc56e99b542 Mon Sep 17 00:00:00 2001 From: Mario Trangoni Date: Fri, 13 Apr 2018 19:07:13 +0200 Subject: [PATCH 125/145] blocks: fix codespell issues --- docker/blocks/graphite/files/carbon.conf | 2 +- docker/blocks/graphite1/conf/opt/graphite/conf/carbon.amqp.conf | 2 +- docker/blocks/graphite1/conf/opt/graphite/conf/carbon.conf | 2 +- docker/blocks/graphite1/conf/opt/graphite/conf/dashboard.conf | 2 +- docker/blocks/smtp/bootstrap.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/blocks/graphite/files/carbon.conf b/docker/blocks/graphite/files/carbon.conf index 50762b3fff5..fc03aba6398 100644 --- a/docker/blocks/graphite/files/carbon.conf +++ b/docker/blocks/graphite/files/carbon.conf @@ -38,7 +38,7 @@ CACHE_QUERY_PORT = 7002 LOG_UPDATES = False -# Enable AMQP if you want to receve metrics using an amqp broker +# Enable AMQP if you want to receive metrics using an amqp broker # ENABLE_AMQP = False # Verbose means a line will be logged for every metric received diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.amqp.conf b/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.amqp.conf index fc36328b25f..f8a53a61115 100644 --- a/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.amqp.conf +++ b/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.amqp.conf @@ -41,7 +41,7 @@ PICKLE_RECEIVER_PORT = 2004 CACHE_QUERY_INTERFACE = 0.0.0.0 CACHE_QUERY_PORT = 7002 -# Enable AMQP if you want to receve metrics using you amqp broker +# Enable AMQP if you want to receive metrics using you amqp broker ENABLE_AMQP = True # Verbose means a line will be logged for every metric received diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.conf b/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.conf index 3e10dcec9cf..6741932da37 100644 --- a/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.conf +++ b/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.conf @@ -265,7 +265,7 @@ WHISPER_FALLOCATE_CREATE = True # CARBON_METRIC_PREFIX = carbon # CARBON_METRIC_INTERVAL = 60 -# Enable AMQP if you want to receve metrics using an amqp broker +# Enable AMQP if you want to receive metrics using an amqp broker # ENABLE_AMQP = False # Verbose means a line will be logged for every metric received diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/dashboard.conf b/docker/blocks/graphite1/conf/opt/graphite/conf/dashboard.conf index 2e1b0bc4db3..f558b273f57 100644 --- a/docker/blocks/graphite1/conf/opt/graphite/conf/dashboard.conf +++ b/docker/blocks/graphite1/conf/opt/graphite/conf/dashboard.conf @@ -30,7 +30,7 @@ give_completer_focus = shift-space # pertain only to specific metric types. # # The dashboard presents only metrics that fall into specified naming schemes -# defined in this file. This creates a simpler, more targetted view of the +# defined in this file. This creates a simpler, more targeted view of the # data. The general form for defining a naming scheme is as follows: # #[Metric Type] diff --git a/docker/blocks/smtp/bootstrap.sh b/docker/blocks/smtp/bootstrap.sh index a78f9d6dc16..27f6a2c3ef8 100755 --- a/docker/blocks/smtp/bootstrap.sh +++ b/docker/blocks/smtp/bootstrap.sh @@ -22,6 +22,6 @@ log() { log $RUN_CMD $RUN_CMD -# Exit immidiately in case of any errors or when we have interactive terminal +# Exit immediately in case of any errors or when we have interactive terminal if [[ $? != 0 ]] || test -t 0; then exit $?; fi log From 298ece0a02cf56df770e6088e9528f47ff5ffddb Mon Sep 17 00:00:00 2001 From: Mario Trangoni Date: Fri, 13 Apr 2018 19:10:13 +0200 Subject: [PATCH 126/145] conf: fix codespell issues --- conf/sample.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/sample.ini b/conf/sample.ini index 1af5bbdb62b..9f0c2a73c25 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -64,7 +64,7 @@ #################################### Database #################################### [database] # You can configure the database connection by specifying type, host, name, user and password -# as seperate properties or as on string using the url propertie. +# as separate properties or as on string using the url properties. # Either "mysql", "postgres" or "sqlite3", it's your choice ;type = sqlite3 From e5e6bc56c84f8a397dd872785e07592e0f1c895d Mon Sep 17 00:00:00 2001 From: Mario Trangoni Date: Fri, 13 Apr 2018 19:48:37 +0200 Subject: [PATCH 127/145] public: fix codespell issues --- .../core/components/json_explorer/helpers.ts | 4 ++-- .../components/json_explorer/json_explorer.ts | 2 +- .../app/core/directives/dropdown_typeahead.js | 4 ++-- public/app/core/utils/css_loader.ts | 2 +- public/app/core/utils/kbn.ts | 20 +++++++++---------- public/app/features/alerting/alert_def.ts | 2 +- .../alerting/specs/threshold_mapper_specs.ts | 6 +++--- .../features/annotations/events_processing.ts | 2 +- .../app/features/dashboard/dashboard_model.ts | 4 ++-- .../app/features/dashboard/history/history.ts | 2 +- .../specs/dashboard_import_ctrl.jest.ts | 4 ++-- .../dashboard/specs/time_srv_specs.ts | 4 ++-- .../app/features/dashboard/view_state_srv.ts | 4 ++-- public/app/features/org/partials/newOrg.html | 2 +- .../app/features/panel/metrics_panel_ctrl.ts | 2 +- .../templating/datasource_variable.ts | 2 +- .../templating/specs/adhoc_variable.jest.ts | 2 +- .../templating/specs/template_srv.jest.ts | 2 +- .../app/features/templating/template_srv.ts | 2 +- .../testdata/dashboards/graph_last_1h.json | 4 ++-- .../elasticsearch/elastic_response.ts | 2 +- .../partials/annotations.editor.html | 2 +- .../elasticsearch/specs/datasource_specs.ts | 4 ++-- .../datasource/graphite/add_graphite_func.ts | 2 +- .../graphite/specs/query_ctrl_specs.ts | 6 +++--- .../plugins/datasource/influxdb/datasource.ts | 2 +- .../datasource/influxdb/influx_query.ts | 2 +- .../influxdb/partials/annotations.editor.html | 2 +- .../influxdb/specs/query_builder.jest.ts | 2 +- .../mssql/partials/annotations.editor.html | 2 +- .../mysql/partials/annotations.editor.html | 2 +- .../postgres/img/postgresql_logo.svg | 4 ++-- .../postgres/partials/annotations.editor.html | 2 +- .../plugins/panel/graph/jquery.flot.events.js | 4 ++-- public/app/plugins/panel/graph/legend.ts | 2 +- .../panel/graph/series_overrides_ctrl.ts | 2 +- .../panel/table/specs/transformers.jest.ts | 4 ++-- .../app/plugins/panel/table/transformers.ts | 2 +- public/dashboards/scripted_templated.js | 2 +- 39 files changed, 63 insertions(+), 63 deletions(-) diff --git a/public/app/core/components/json_explorer/helpers.ts b/public/app/core/components/json_explorer/helpers.ts index 5b053792d73..c445e1b0667 100644 --- a/public/app/core/components/json_explorer/helpers.ts +++ b/public/app/core/components/json_explorer/helpers.ts @@ -2,7 +2,7 @@ // Licence MIT, Copyright (c) 2015 Mohsen Azimi /* - * Escapes `"` charachters from string + * Escapes `"` characters from string */ function escapeString(str: string): string { return str.replace('"', '"'); @@ -100,7 +100,7 @@ export function cssClass(className: string): string { } /* - * Creates a new DOM element wiht given type and class + * Creates a new DOM element with given type and class * TODO: move me to helpers */ export function createElement(type: string, className?: string, content?: Element | string): Element { diff --git a/public/app/core/components/json_explorer/json_explorer.ts b/public/app/core/components/json_explorer/json_explorer.ts index 9cc1b53bc82..790ed442d5c 100644 --- a/public/app/core/components/json_explorer/json_explorer.ts +++ b/public/app/core/components/json_explorer/json_explorer.ts @@ -146,7 +146,7 @@ export class JsonExplorer { } /* - * did we recieve a key argument? + * did we receive a key argument? * This means that the formatter was called as a sub formatter of a parent formatter */ private get hasKey(): boolean { diff --git a/public/app/core/directives/dropdown_typeahead.js b/public/app/core/directives/dropdown_typeahead.js index 25772b4638a..9b677c95697 100644 --- a/public/app/core/directives/dropdown_typeahead.js +++ b/public/app/core/directives/dropdown_typeahead.js @@ -108,7 +108,7 @@ function (_, $, coreModule) { $input.val(''); $button.show(); $button.focus(); - // clicking the function dropdown menu wont + // clicking the function dropdown menu won't // work if you remove class at once setTimeout(function() { elem.removeClass('open'); @@ -222,7 +222,7 @@ function (_, $, coreModule) { $input.val(''); $button.show(); $button.focus(); - // clicking the function dropdown menu wont + // clicking the function dropdown menu won't // work if you remove class at once setTimeout(function() { elem.removeClass('open'); diff --git a/public/app/core/utils/css_loader.ts b/public/app/core/utils/css_loader.ts index 42f59a9c27c..ba8623df842 100644 --- a/public/app/core/utils/css_loader.ts +++ b/public/app/core/utils/css_loader.ts @@ -67,7 +67,7 @@ export function fetch(load): any { return ''; } - // dont reload styles loaded in the head + // don't reload styles loaded in the head for (var i = 0; i < linkHrefs.length; i++) { if (load.address === linkHrefs[i]) { return ''; diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts index dcb04a3e38e..8c3e3e72dda 100644 --- a/public/app/core/utils/kbn.ts +++ b/public/app/core/utils/kbn.ts @@ -620,13 +620,13 @@ kbn.valueFormats.ms = function(size, decimals, scaledDecimals) { // 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 + // Less than 1 hour, divide in minutes return kbn.toFixedScaled(size / 60000, decimals, scaledDecimals, 5, ' min'); } else if (Math.abs(size) < 86400000) { - // Less than one day, devide in hours + // Less than one day, divide in hours return kbn.toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, ' hour'); } else if (Math.abs(size) < 31536000000) { - // Less than one year, devide in days + // Less than one year, divide in days return kbn.toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, ' day'); } @@ -638,15 +638,15 @@ kbn.valueFormats.s = function(size, decimals, scaledDecimals) { return ''; } - // Less than 1 µs, devide in ns + // Less than 1 µs, divide 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 + // Less than 1 ms, divide 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 + // Less than 1 second, divide in ms if (Math.abs(size) < 1) { return kbn.toFixedScaled(size * 1e3, decimals, scaledDecimals - decimals, -3, ' ms'); } @@ -654,16 +654,16 @@ kbn.valueFormats.s = function(size, decimals, scaledDecimals) { if (Math.abs(size) < 60) { return kbn.toFixed(size, decimals) + ' s'; } else if (Math.abs(size) < 3600) { - // Less than 1 hour, devide in minutes + // Less than 1 hour, divide in minutes return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 1, ' min'); } else if (Math.abs(size) < 86400) { - // Less than one day, devide in hours + // Less than one day, divide in hours return kbn.toFixedScaled(size / 3600, decimals, scaledDecimals, 4, ' hour'); } else if (Math.abs(size) < 604800) { - // Less than one week, devide in days + // Less than one week, divide in days return kbn.toFixedScaled(size / 86400, decimals, scaledDecimals, 5, ' day'); } else if (Math.abs(size) < 31536000) { - // Less than one year, devide in week + // Less than one year, divide in week return kbn.toFixedScaled(size / 604800, decimals, scaledDecimals, 6, ' week'); } diff --git a/public/app/features/alerting/alert_def.ts b/public/app/features/alerting/alert_def.ts index d86461780ba..797a67abfd8 100644 --- a/public/app/features/alerting/alert_def.ts +++ b/public/app/features/alerting/alert_def.ts @@ -124,7 +124,7 @@ function joinEvalMatches(matches, separator: string) { } function getAlertAnnotationInfo(ah) { - // backward compatability, can be removed in grafana 5.x + // backward compatibility, can be removed in grafana 5.x // old way stored evalMatches in data property directly, // new way stores it in evalMatches property on new data object diff --git a/public/app/features/alerting/specs/threshold_mapper_specs.ts b/public/app/features/alerting/specs/threshold_mapper_specs.ts index 3b284776b8d..1d68fce7050 100644 --- a/public/app/features/alerting/specs/threshold_mapper_specs.ts +++ b/public/app/features/alerting/specs/threshold_mapper_specs.ts @@ -4,7 +4,7 @@ import { ThresholdMapper } from '../threshold_mapper'; describe('ThresholdMapper', () => { describe('with greater than evaluator', () => { - it('can mapp query conditions to thresholds', () => { + it('can map query conditions to thresholds', () => { var panel: any = { type: 'graph', alert: { @@ -25,7 +25,7 @@ describe('ThresholdMapper', () => { }); describe('with outside range evaluator', () => { - it('can mapp query conditions to thresholds', () => { + it('can map query conditions to thresholds', () => { var panel: any = { type: 'graph', alert: { @@ -49,7 +49,7 @@ describe('ThresholdMapper', () => { }); describe('with inside range evaluator', () => { - it('can mapp query conditions to thresholds', () => { + it('can map query conditions to thresholds', () => { var panel: any = { type: 'graph', alert: { diff --git a/public/app/features/annotations/events_processing.ts b/public/app/features/annotations/events_processing.ts index 040bf6425c1..667285d7d43 100644 --- a/public/app/features/annotations/events_processing.ts +++ b/public/app/features/annotations/events_processing.ts @@ -56,7 +56,7 @@ function isStartOfRegion(event): boolean { export function dedupAnnotations(annotations) { let dedup = []; - // Split events by annotationId property existance + // Split events by annotationId property existence let events = _.partition(annotations, 'id'); let eventsById = _.groupBy(events[0], 'id'); diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index 3fa8ed9973a..9130cb7e806 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -129,7 +129,7 @@ export class DashboardModel { this.meta = meta; } - // cleans meta data and other non peristent state + // cleans meta data and other non persistent state getSaveModelClone() { // make clone var copy: any = {}; @@ -606,7 +606,7 @@ export class DashboardModel { if (panel.gridPos.x + panel.gridPos.w * 2 <= GRID_COLUMN_COUNT) { newPanel.gridPos.x += panel.gridPos.w; } else { - // add bellow + // add below newPanel.gridPos.y += panel.gridPos.h; } diff --git a/public/app/features/dashboard/history/history.ts b/public/app/features/dashboard/history/history.ts index d9f0c087438..be6ad5af1ba 100644 --- a/public/app/features/dashboard/history/history.ts +++ b/public/app/features/dashboard/history/history.ts @@ -133,7 +133,7 @@ export class HistoryListCtrl { return this.historySrv .getHistoryList(this.dashboard, options) .then(revisions => { - // set formated dates & default values + // set formatted dates & default values for (let rev of revisions) { rev.createdDateString = this.formatDate(rev.created); rev.ageString = this.formatBasicDate(rev.created); diff --git a/public/app/features/dashboard/specs/dashboard_import_ctrl.jest.ts b/public/app/features/dashboard/specs/dashboard_import_ctrl.jest.ts index 1cb59ef5bac..737eb360461 100644 --- a/public/app/features/dashboard/specs/dashboard_import_ctrl.jest.ts +++ b/public/app/features/dashboard/specs/dashboard_import_ctrl.jest.ts @@ -56,7 +56,7 @@ describe('DashboardImportCtrl', function() { }); }); - describe('when specifing grafana.com url', function() { + describe('when specifying grafana.com url', function() { beforeEach(function() { ctx.ctrl.gnetUrl = 'http://grafana.com/dashboards/123'; // setup api mock @@ -73,7 +73,7 @@ describe('DashboardImportCtrl', function() { }); }); - describe('when specifing dashbord id', function() { + describe('when specifying dashboard id', function() { beforeEach(function() { ctx.ctrl.gnetUrl = '2342'; // setup api mock diff --git a/public/app/features/dashboard/specs/time_srv_specs.ts b/public/app/features/dashboard/specs/time_srv_specs.ts index ca75f0ffcf9..6e180679ff2 100644 --- a/public/app/features/dashboard/specs/time_srv_specs.ts +++ b/public/app/features/dashboard/specs/time_srv_specs.ts @@ -44,7 +44,7 @@ describe('timeSrv', function() { expect(time.raw.to).to.be('now'); }); - it('should handle formated dates', function() { + it('should handle formatted dates', function() { ctx.$location.search({ from: '20140410T052010', to: '20140520T031022' }); ctx.service.init(_dashboard); var time = ctx.service.timeRange(true); @@ -52,7 +52,7 @@ describe('timeSrv', function() { expect(time.to.valueOf()).to.equal(new Date('2014-05-20T03:10:22Z').getTime()); }); - it('should handle formated dates without time', function() { + it('should handle formatted dates without time', function() { ctx.$location.search({ from: '20140410', to: '20140520' }); ctx.service.init(_dashboard); var time = ctx.service.timeRange(true); diff --git a/public/app/features/dashboard/view_state_srv.ts b/public/app/features/dashboard/view_state_srv.ts index fa471b89989..1ed2d61df71 100644 --- a/public/app/features/dashboard/view_state_srv.ts +++ b/public/app/features/dashboard/view_state_srv.ts @@ -38,7 +38,7 @@ export class DashboardViewState { }); // this marks changes to location during this digest cycle as not to add history item - // dont want url changes like adding orgId to add browser history + // don't want url changes like adding orgId to add browser history $location.replace(); this.update(this.getQueryStringState()); } @@ -196,7 +196,7 @@ export class DashboardViewState { this.oldTimeRange = ctrl.range; this.fullscreenPanel = panelScope; - // Firefox doesn't return scrollTop postion properly if 'dash-scroll' is emitted after setViewMode() + // Firefox doesn't return scrollTop position properly if 'dash-scroll' is emitted after setViewMode() this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 }); this.dashboard.setViewMode(ctrl.panel, true, ctrl.editMode); this.$scope.appEvent('panel-fullscreen-enter', { panelId: ctrl.panel.id }); diff --git a/public/app/features/org/partials/newOrg.html b/public/app/features/org/partials/newOrg.html index 424c55d6eb7..9777107c31a 100644 --- a/public/app/features/org/partials/newOrg.html +++ b/public/app/features/org/partials/newOrg.html @@ -5,7 +5,7 @@ New Organization -

Each organization contains their own dashboards, data sources and configuration, and cannot be shared between orgs. While users may belong to more than one, mutiple organization are most frequently used in multi-tenant deployments.

+

Each organization contains their own dashboards, data sources and configuration, and cannot be shared between orgs. While users may belong to more than one, multiple organization are most frequently used in multi-tenant deployments.

diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index 177f0c7bf00..9e9598e1732 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -73,7 +73,7 @@ class MetricsPanelCtrl extends PanelCtrl { if (this.panel.snapshotData) { this.updateTimeRange(); var data = this.panel.snapshotData; - // backward compatability + // backward compatibility if (!_.isArray(data)) { data = data.data; } diff --git a/public/app/features/templating/datasource_variable.ts b/public/app/features/templating/datasource_variable.ts index 0c5b226c372..4c326a94e3b 100644 --- a/public/app/features/templating/datasource_variable.ts +++ b/public/app/features/templating/datasource_variable.ts @@ -29,7 +29,7 @@ export class DatasourceVariable implements Variable { getSaveModel() { assignModelProperties(this.model, this, this.defaults); - // dont persist options + // don't persist options this.model.options = []; return this.model; } diff --git a/public/app/features/templating/specs/adhoc_variable.jest.ts b/public/app/features/templating/specs/adhoc_variable.jest.ts index 863c8401c50..a7b20e8d029 100644 --- a/public/app/features/templating/specs/adhoc_variable.jest.ts +++ b/public/app/features/templating/specs/adhoc_variable.jest.ts @@ -2,7 +2,7 @@ import { AdhocVariable } from '../adhoc_variable'; describe('AdhocVariable', function() { describe('when serializing to url', function() { - it('should set return key value and op seperated by pipe', function() { + it('should set return key value and op separated by pipe', function() { var variable = new AdhocVariable({ filters: [ { key: 'key1', operator: '=', value: 'value1' }, diff --git a/public/app/features/templating/specs/template_srv.jest.ts b/public/app/features/templating/specs/template_srv.jest.ts index f28fbf9ac64..5290a883c48 100644 --- a/public/app/features/templating/specs/template_srv.jest.ts +++ b/public/app/features/templating/specs/template_srv.jest.ts @@ -282,7 +282,7 @@ describe('templateSrv', function() { }); }); - describe('can hightlight variables in string', function() { + describe('can highlight variables in string', function() { beforeEach(function() { initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'oogle' } }]); }); diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts index 5b31072d140..f6274a80165 100644 --- a/public/app/features/templating/template_srv.ts +++ b/public/app/features/templating/template_srv.ts @@ -204,7 +204,7 @@ export class TemplateSrv { value = variable.current.value; if (this.isAllValue(value)) { value = this.getAllValue(variable); - // skip formating of custom all values + // skip formatting of custom all values if (variable.allValue) { return value; } diff --git a/public/app/plugins/app/testdata/dashboards/graph_last_1h.json b/public/app/plugins/app/testdata/dashboards/graph_last_1h.json index c56d9e9216f..5a4459cd62c 100644 --- a/public/app/plugins/app/testdata/dashboards/graph_last_1h.json +++ b/public/app/plugins/app/testdata/dashboards/graph_last_1h.json @@ -392,7 +392,7 @@ "thresholds": [], "timeFrom": null, "timeShift": null, - "title": "2 yaxis and axis lables", + "title": "2 yaxis and axis labels", "tooltip": { "msResolution": false, "shared": true, @@ -894,7 +894,7 @@ "thresholds": [], "timeFrom": null, "timeShift": null, - "title": "Legend Table Single Series Should Take Minium Height", + "title": "Legend Table Single Series Should Take Minimum Height", "tooltip": { "shared": true, "sort": 0, diff --git a/public/app/plugins/datasource/elasticsearch/elastic_response.ts b/public/app/plugins/datasource/elasticsearch/elastic_response.ts index ede5cb0ba3a..a378ab8b55f 100644 --- a/public/app/plugins/datasource/elasticsearch/elastic_response.ts +++ b/public/app/plugins/datasource/elasticsearch/elastic_response.ts @@ -175,7 +175,7 @@ export class ElasticResponse { } // This is quite complex - // neeed to recurise down the nested buckets to build series + // need to recurise down the nested buckets to build series processBuckets(aggs, target, seriesList, table, props, depth) { var bucket, aggDef, esAgg, aggId; var maxDepth = target.bucketAggs.length - 1; diff --git a/public/app/plugins/datasource/elasticsearch/partials/annotations.editor.html b/public/app/plugins/datasource/elasticsearch/partials/annotations.editor.html index d4e1e7d1b1c..a2e903f231c 100644 --- a/public/app/plugins/datasource/elasticsearch/partials/annotations.editor.html +++ b/public/app/plugins/datasource/elasticsearch/partials/annotations.editor.html @@ -27,7 +27,7 @@
- Title (depricated) + Title (deprecated)
diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts index 629621b8e60..558bccf3d0f 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts @@ -53,7 +53,7 @@ describe('ElasticDatasource', function() { }); }); - describe('When issueing metric query with interval pattern', function() { + describe('When issuing metric query with interval pattern', function() { var requestOptions, parts, header; beforeEach(function() { @@ -98,7 +98,7 @@ describe('ElasticDatasource', function() { }); }); - describe('When issueing document query', function() { + describe('When issuing document query', function() { var requestOptions, parts, header; beforeEach(function() { diff --git a/public/app/plugins/datasource/graphite/add_graphite_func.ts b/public/app/plugins/datasource/graphite/add_graphite_func.ts index 6e64b5d12d0..444d30b5453 100644 --- a/public/app/plugins/datasource/graphite/add_graphite_func.ts +++ b/public/app/plugins/datasource/graphite/add_graphite_func.ts @@ -68,7 +68,7 @@ export function graphiteAddFunc($compile) { }); $input.blur(function() { - // clicking the function dropdown menu wont + // clicking the function dropdown menu won't // work if you remove class at once setTimeout(function() { $input.val(''); diff --git a/public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts b/public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts index f8b70b05940..b4f7718930f 100644 --- a/public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts +++ b/public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts @@ -97,7 +97,7 @@ describe('GraphiteQueryCtrl', function() { }); }); - describe('when initalizing target without metric expression and only function', function() { + describe('when initializing target without metric expression and only function', function() { beforeEach(function() { ctx.ctrl.target.target = 'asPercent(#A, #B)'; ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([])); @@ -130,7 +130,7 @@ describe('GraphiteQueryCtrl', function() { }); }); - describe('when initalizing target without metric expression and function with series-ref', function() { + describe('when initializing target without metric expression and function with series-ref', function() { beforeEach(function() { ctx.ctrl.target.target = 'asPercent(metric.node.count, #A)'; ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([])); @@ -146,7 +146,7 @@ describe('GraphiteQueryCtrl', function() { }); }); - describe('when getting altSegments and metricFindQuery retuns empty array', function() { + describe('when getting altSegments and metricFindQuery returns empty array', function() { beforeEach(function() { ctx.ctrl.target.target = 'test.count'; ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([])); diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 1eff9bfa527..4439ca7beaf 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -54,7 +54,7 @@ export default class InfluxDatasource { queryTargets.push(target); - // backward compatability + // backward compatibility scopedVars.interval = scopedVars.__interval; queryModel = new InfluxQuery(target, this.templateSrv, scopedVars); diff --git a/public/app/plugins/datasource/influxdb/influx_query.ts b/public/app/plugins/datasource/influxdb/influx_query.ts index 656647b4413..2ef74170068 100644 --- a/public/app/plugins/datasource/influxdb/influx_query.ts +++ b/public/app/plugins/datasource/influxdb/influx_query.ts @@ -230,7 +230,7 @@ export default class InfluxQuery { for (i = 0; i < this.groupByParts.length; i++) { var part = this.groupByParts[i]; if (i > 0) { - // for some reason fill has no seperator + // for some reason fill has no separator groupBySection += part.def.type === 'fill' ? ' ' : ', '; } groupBySection += part.render(''); diff --git a/public/app/plugins/datasource/influxdb/partials/annotations.editor.html b/public/app/plugins/datasource/influxdb/partials/annotations.editor.html index 2f54ff28275..48991426c1e 100644 --- a/public/app/plugins/datasource/influxdb/partials/annotations.editor.html +++ b/public/app/plugins/datasource/influxdb/partials/annotations.editor.html @@ -17,7 +17,7 @@
- Title (depricated) + Title (deprecated)
diff --git a/public/app/plugins/datasource/influxdb/specs/query_builder.jest.ts b/public/app/plugins/datasource/influxdb/specs/query_builder.jest.ts index 439bf7b1fc5..eeae987b139 100644 --- a/public/app/plugins/datasource/influxdb/specs/query_builder.jest.ts +++ b/public/app/plugins/datasource/influxdb/specs/query_builder.jest.ts @@ -97,7 +97,7 @@ describe('InfluxQueryBuilder', function() { expect(query).toBe('SHOW TAG VALUES FROM "one_week"."cpu" WITH KEY = "app" WHERE "host" = \'server1\''); }); - it('should not includ policy when policy is default', function() { + it('should not include policy when policy is default', function() { var builder = new InfluxQueryBuilder({ measurement: 'cpu', policy: 'default', diff --git a/public/app/plugins/datasource/mssql/partials/annotations.editor.html b/public/app/plugins/datasource/mssql/partials/annotations.editor.html index 8a94c470379..b2c0d7b97a6 100644 --- a/public/app/plugins/datasource/mssql/partials/annotations.editor.html +++ b/public/app/plugins/datasource/mssql/partials/annotations.editor.html @@ -18,7 +18,7 @@
Annotation Query Format
-An annotation is an event that is overlayed on top of graphs. The query can have up to three columns per row, the time column is mandatory. Annotation rendering is expensive so it is important to limit the number of rows returned. +An annotation is an event that is overlaid on top of graphs. The query can have up to three columns per row, the time column is mandatory. Annotation rendering is expensive so it is important to limit the number of rows returned. - column with alias: time for the annotation event time. Use epoch time or any native date data type. - column with alias: text for the annotation text. diff --git a/public/app/plugins/datasource/mysql/partials/annotations.editor.html b/public/app/plugins/datasource/mysql/partials/annotations.editor.html index d142e091fed..23ec726a9f0 100644 --- a/public/app/plugins/datasource/mysql/partials/annotations.editor.html +++ b/public/app/plugins/datasource/mysql/partials/annotations.editor.html @@ -18,7 +18,7 @@
Annotation Query Format
-An annotation is an event that is overlayed on top of graphs. The query can have up to three columns per row, the time or time_sec column is mandatory. Annotation rendering is expensive so it is important to limit the number of rows returned. +An annotation is an event that is overlaid on top of graphs. The query can have up to three columns per row, the time or time_sec column is mandatory. Annotation rendering is expensive so it is important to limit the number of rows returned. - column with alias: time or time_sec for the annotation event time. Use epoch time or any native date data type. - column with alias: text for the annotation text diff --git a/public/app/plugins/datasource/postgres/img/postgresql_logo.svg b/public/app/plugins/datasource/postgres/img/postgresql_logo.svg index d98e3659c39..40a39970070 100644 --- a/public/app/plugins/datasource/postgres/img/postgresql_logo.svg +++ b/public/app/plugins/datasource/postgres/img/postgresql_logo.svg @@ -3,7 +3,7 @@ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> - + @@ -19,4 +19,4 @@ - \ No newline at end of file + diff --git a/public/app/plugins/datasource/postgres/partials/annotations.editor.html b/public/app/plugins/datasource/postgres/partials/annotations.editor.html index 09232d6f8ed..907b1b10be4 100644 --- a/public/app/plugins/datasource/postgres/partials/annotations.editor.html +++ b/public/app/plugins/datasource/postgres/partials/annotations.editor.html @@ -18,7 +18,7 @@
Annotation Query Format
-An annotation is an event that is overlayed on top of graphs. The query can have up to three columns per row, the time column is mandatory. Annotation rendering is expensive so it is important to limit the number of rows returned. +An annotation is an event that is overlaid on top of graphs. The query can have up to three columns per row, the time column is mandatory. Annotation rendering is expensive so it is important to limit the number of rows returned. - column with alias: time for the annotation event time. Use epoch time or any native date data type. - column with alias: text for the annotation text diff --git a/public/app/plugins/panel/graph/jquery.flot.events.js b/public/app/plugins/panel/graph/jquery.flot.events.js index 1aa79c5056f..3ea3ca8f330 100644 --- a/public/app/plugins/panel/graph/jquery.flot.events.js +++ b/public/app/plugins/panel/graph/jquery.flot.events.js @@ -52,14 +52,14 @@ function ($, _, angular, Drop) { var eventManager = plot.getOptions().events.manager; if (eventManager.editorOpen) { // update marker element to attach to (needed in case of legend on the right - // when there is a double render pass and the inital marker element is removed) + // when there is a double render pass and the initial marker element is removed) markerElementToAttachTo = element; return; } // mark as openend eventManager.editorOpened(); - // set marker elment to attache to + // set marker element to attache to markerElementToAttachTo = element; // wait for element to be attached and positioned diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index b668555b6a6..6b6c89444dc 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -129,7 +129,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { elem.empty(); - // Set min-width if side style and there is a value, otherwise remove the CSS propery + // Set min-width if side style and there is a value, otherwise remove the CSS property // Set width so it works with IE11 var width: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + 'px' : ''; var ieWidth: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth - 1 + 'px' : ''; diff --git a/public/app/plugins/panel/graph/series_overrides_ctrl.ts b/public/app/plugins/panel/graph/series_overrides_ctrl.ts index 703c4648716..ecf79a8a4fb 100644 --- a/public/app/plugins/panel/graph/series_overrides_ctrl.ts +++ b/public/app/plugins/panel/graph/series_overrides_ctrl.ts @@ -31,7 +31,7 @@ export class SeriesOverridesCtrl { $scope.override[item.propertyName] = subItem.value; - // automatically disable lines for this series and the fill bellow to series + // automatically disable lines for this series and the fill below to series // can be removed by the user if they still want lines if (item.propertyName === 'fillBelowTo') { $scope.override['lines'] = false; diff --git a/public/app/plugins/panel/table/specs/transformers.jest.ts b/public/app/plugins/panel/table/specs/transformers.jest.ts index a59b3ae48ee..eefe3f9bdc0 100644 --- a/public/app/plugins/panel/table/specs/transformers.jest.ts +++ b/public/app/plugins/panel/table/specs/transformers.jest.ts @@ -221,7 +221,7 @@ describe('when transforming time series table', () => { expect(table.rows[0][2]).toBe(42); }); - it('should return 2 rows for a mulitple queries with same label values plus one extra row', () => { + it('should return 2 rows for a multiple queries with same label values plus one extra row', () => { table = transformDataToTable(multipleQueriesDataSameLabels, panel); expect(table.rows.length).toBe(2); expect(table.rows[0][0]).toBe(time); @@ -238,7 +238,7 @@ describe('when transforming time series table', () => { expect(table.rows[1][5]).toBe(7); }); - it('should return 2 rows for mulitple queries with different label values', () => { + it('should return 2 rows for multiple queries with different label values', () => { table = transformDataToTable(multipleQueriesDataDifferentLabels, panel); expect(table.rows.length).toBe(2); expect(table.columns.length).toBe(6); diff --git a/public/app/plugins/panel/table/transformers.ts b/public/app/plugins/panel/table/transformers.ts index 43088dc22ac..1659ba3e3aa 100644 --- a/public/app/plugins/panel/table/transformers.ts +++ b/public/app/plugins/panel/table/transformers.ts @@ -243,7 +243,7 @@ transformers['table'] = { row[columnIndex] = matchedRow[columnIndex]; } } - // Dont visit this row again + // Don't visit this row again mergedRows[match] = matchedRow; // Keep looking for more rows to merge offset = match + 1; diff --git a/public/dashboards/scripted_templated.js b/public/dashboards/scripted_templated.js index 5a05aa55b5d..f1b0b115fa1 100644 --- a/public/dashboards/scripted_templated.js +++ b/public/dashboards/scripted_templated.js @@ -22,7 +22,7 @@ var dashboard; // All url parameters are available via the ARGS object var ARGS; -// Intialize a skeleton with nothing but a rows array and service object +// Initialize a skeleton with nothing but a rows array and service object dashboard = { rows : [], schemaVersion: 13, From 638f7d23d4c4cb0cbfd839eb6237d79343c4f84d Mon Sep 17 00:00:00 2001 From: Mario Trangoni Date: Fri, 13 Apr 2018 20:02:45 +0200 Subject: [PATCH 128/145] docs: fix codespell issues --- docs/sources/administration/provisioning.md | 2 +- docs/sources/alerting/notifications.md | 2 +- docs/sources/alerting/rules.md | 2 +- docs/sources/contribute/cla.md | 4 ++-- docs/sources/features/datasources/opentsdb.md | 4 ++-- docs/sources/features/panels/alertlist.md | 2 +- docs/sources/features/panels/dashlist.md | 2 +- docs/sources/features/panels/singlestat.md | 4 ++-- docs/sources/guides/whats-new-in-v2-6.md | 2 +- docs/sources/guides/whats-new-in-v4-1.md | 2 +- docs/sources/guides/whats-new-in-v4-5.md | 4 ++-- docs/sources/guides/whats-new-in-v4-6.md | 2 +- docs/sources/http_api/org.md | 4 ++-- docs/sources/installation/configuration.md | 2 +- docs/sources/installation/docker.md | 2 +- docs/sources/installation/upgrading.md | 2 +- docs/sources/reference/templating.md | 4 ++-- docs/sources/tutorials/authproxy.md | 10 +++++----- 18 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/sources/administration/provisioning.md b/docs/sources/administration/provisioning.md index 7936a1708eb..23fbe0c89fd 100644 --- a/docs/sources/administration/provisioning.md +++ b/docs/sources/administration/provisioning.md @@ -206,7 +206,7 @@ When Grafana starts, it will update/insert all dashboards available in the confi ### Reuseable Dashboard Urls -If the dashboard in the json file contains an [uid](/reference/dashboard/#json-fields), Grafana will force insert/update on that uid. This allows you to migrate dashboards betweens Grafana instances and provisioning Grafana from configuration without breaking the urls given since the new dashboard url uses the uid as identifer. +If the dashboard in the json file contains an [uid](/reference/dashboard/#json-fields), Grafana will force insert/update on that uid. This allows you to migrate dashboards betweens Grafana instances and provisioning Grafana from configuration without breaking the urls given since the new dashboard url uses the uid as identifier. When Grafana starts, it will update/insert all dashboards available in the configured folders. If you modify the file, the dashboard will also be updated. By default Grafana will delete dashboards in the database if the file is removed. You can disable this behavior using the `disableDeletion` setting. diff --git a/docs/sources/alerting/notifications.md b/docs/sources/alerting/notifications.md index bb119687750..d279d3af20b 100644 --- a/docs/sources/alerting/notifications.md +++ b/docs/sources/alerting/notifications.md @@ -153,7 +153,7 @@ Prometheus Alertmanager | `prometheus-alertmanager` | no # Enable images in notifications {#external-image-store} -Grafana can render the panel associated with the alert rule and include that in the notification. Most Notification Channels require that this image be publicly accessable (Slack and PagerDuty for example). In order to include images in alert notifications, Grafana can upload the image to an image store. It currently supports +Grafana can render the panel associated with the alert rule and include that in the notification. Most Notification Channels require that this image be publicly accessible (Slack and PagerDuty for example). In order to include images in alert notifications, Grafana can upload the image to an image store. It currently supports Amazon S3, Webdav, Google Cloud Storage and Azure Blob Storage. So to set that up you need to configure the [external image uploader](/installation/configuration/#external-image-storage) in your grafana-server ini config file. Be aware that some notifiers requires public access to the image to be able to include it in the notification. So make sure to enable public access to the images. If your using local image uploader, your Grafana instance need to be accessible by the internet. diff --git a/docs/sources/alerting/rules.md b/docs/sources/alerting/rules.md index 9bbbd70641d..bcca3c6b2fb 100644 --- a/docs/sources/alerting/rules.md +++ b/docs/sources/alerting/rules.md @@ -110,7 +110,7 @@ to `Keep Last State` in order to basically ignore them. ## Notifications -In alert tab you can also specify alert rule notifications along with a detailed messsage about the alert rule. +In alert tab you can also specify alert rule notifications along with a detailed message about the alert rule. The message can contain anything, information about how you might solve the issue, link to runbook, etc. The actual notifications are configured and shared between multiple alerts. Read the diff --git a/docs/sources/contribute/cla.md b/docs/sources/contribute/cla.md index b990187d809..ffb2aaef1b9 100644 --- a/docs/sources/contribute/cla.md +++ b/docs/sources/contribute/cla.md @@ -1,6 +1,6 @@ +++ title = "Contributor Licence Agreement (CLA)" -description = "Contributer Licence Agreement (CLA)" +description = "Contributor Licence Agreement (CLA)" type = "docs" aliases = ["/project/cla", "docs/contributing/cla.html"] [menu.docs] @@ -101,4 +101,4 @@ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU [OR US]


-This CLA aggreement is based on the [Harmony Contributor Aggrement Template (combined)](http://www.harmonyagreements.org/agreements.html), [Creative Commons Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/) +This CLA agreement is based on the [Harmony Contributor Aggrement Template (combined)](http://www.harmonyagreements.org/agreements.html), [Creative Commons Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/) diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index 6333861dca7..0959817c015 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -78,7 +78,7 @@ the existing time series data in OpenTSDB, you need to run `tsdb uid metasync` o ### Nested Templating -One template variable can be used to filter tag values for another template varible. First parameter is the metric name, +One template variable can be used to filter tag values for another template variable. First parameter is the metric name, second parameter is the tag key for which you need to find tag values, and after that all other dependent template variables. Some examples are mentioned below to make nested template queries work successfully. @@ -106,4 +106,4 @@ datasources: jsonData: tsdbResolution: 1 tsdbVersion: 1 -``` \ No newline at end of file +``` diff --git a/docs/sources/features/panels/alertlist.md b/docs/sources/features/panels/alertlist.md index 9307bb71391..58aa2c0966a 100644 --- a/docs/sources/features/panels/alertlist.md +++ b/docs/sources/features/panels/alertlist.md @@ -14,7 +14,7 @@ weight = 4 {{< docs-imagebox img="/img/docs/v45/alert-list-panel.png" max-width="850px" >}} -The alert list panel allows you to display your dashbords alerts. The list can be configured to show current state or recent state changes. You can read more about alerts [here](http://docs.grafana.org/alerting/rules). +The alert list panel allows you to display your dashboards alerts. The list can be configured to show current state or recent state changes. You can read more about alerts [here](http://docs.grafana.org/alerting/rules). ## Alert List Options diff --git a/docs/sources/features/panels/dashlist.md b/docs/sources/features/panels/dashlist.md index 8a4ed60875d..2ee578c5b7e 100644 --- a/docs/sources/features/panels/dashlist.md +++ b/docs/sources/features/panels/dashlist.md @@ -25,7 +25,7 @@ The dashboard list panel allows you to display dynamic links to other dashboards 1. **Starred**: The starred dashboard selection displays starred dashboards in alphabetical order. 2. **Recently Viewed**: The recently viewed dashboard selection displays recently viewed dashboards in alphabetical order. 3. **Search**: The search dashboard selection displays dashboards by search query or tag(s). -4. **Show Headings**: When show headings is ticked the choosen list selection(Starred, Recently Viewed, Search) is shown as a heading. +4. **Show Headings**: When show headings is ticked the chosen list selection(Starred, Recently Viewed, Search) is shown as a heading. 5. **Max Items**: Max items set the maximum of items in a list. 6. **Query**: Here is where you enter your query you want to search by. Queries are case-insensitive, and partial values are accepted. 7. **Tags**: Here is where you enter your tag(s) you want to search by. Note that existing tags will not appear as you type, and *are* case sensitive. To see a list of existing tags, you can always return to the dashboard, open the Dashboard Picker at the top and click `tags` link in the search bar. diff --git a/docs/sources/features/panels/singlestat.md b/docs/sources/features/panels/singlestat.md index 510642337ff..0eb442914f5 100644 --- a/docs/sources/features/panels/singlestat.md +++ b/docs/sources/features/panels/singlestat.md @@ -30,7 +30,7 @@ The singlestat panel has a normal query editor to allow you define your exact me * **total** - The sum of all the non-null values in the series * **first** - The first value in the series * **delta** - The total incremental increase (of a counter) in the series. An attempt is made to account for counter resets, but this will only be accurate for single instance metrics. Used to show total counter increase in time series. - * **diff** - The difference betwen 'current' (last value) and 'first'. + * **diff** - The difference between 'current' (last value) and 'first'. * **range** - The difference between 'min' and 'max'. Useful the show the range of change for a gauge. 2. **Prefix/Postfix**: The Prefix/Postfix fields let you define a custom label to appear *before/after* the value. The `$__name` variable can be used here to use the series name or alias from the metric query. 3. **Units**: Units are appended to the the Singlestat within the panel, and will respect the color and threshold settings for the value. @@ -70,7 +70,7 @@ Gauges gives a clear picture of how high a value is in it's context. It's a grea {{< docs-imagebox img="/img/docs/v45/singlestat-gauge-options.png" max-width="500px" class="docs-image--right docs-image--no-shadow">}} -1. **Show**: The show checkbox will toggle wether the gauge is shown in the panel. When unselected, only the Singlestat value will appear. +1. **Show**: The show checkbox will toggle whether the gauge is shown in the panel. When unselected, only the Singlestat value will appear. 2. **Min/Max**: This sets the start and end point for the gauge. 3. **Threshold Labels**: Check if you want to show the threshold labels. Thresholds are set in the color options. 4. **Threshold Markers**: Check if you want to have a second meter showing the thresholds. diff --git a/docs/sources/guides/whats-new-in-v2-6.md b/docs/sources/guides/whats-new-in-v2-6.md index b8996680ce6..1e6f30c597b 100644 --- a/docs/sources/guides/whats-new-in-v2-6.md +++ b/docs/sources/guides/whats-new-in-v2-6.md @@ -15,7 +15,7 @@ support for multiple Cloudwatch credentials. The new table panel is very flexible, supporting both multiple modes for time series as well as for -table, annotation and raw JSON data. It also provides date formating and value formating and coloring options. +table, annotation and raw JSON data. It also provides date formatting and value formatting and coloring options. ### Time series to rows diff --git a/docs/sources/guides/whats-new-in-v4-1.md b/docs/sources/guides/whats-new-in-v4-1.md index bd2b0f1b75f..217b21b545e 100644 --- a/docs/sources/guides/whats-new-in-v4-1.md +++ b/docs/sources/guides/whats-new-in-v4-1.md @@ -33,7 +33,7 @@ You can enable/disable the shared tooltip from the dashboard settings menu or cy {{< imgbox max-width="60%" img="/img/docs/v41/helptext_for_panel_settings.png" caption="Hovering help text" >}} -You can set a help text in the general tab on any panel. The help text is using Markdown to enable better formating and linking to other sites that can provide more information. +You can set a help text in the general tab on any panel. The help text is using Markdown to enable better formatting and linking to other sites that can provide more information.
diff --git a/docs/sources/guides/whats-new-in-v4-5.md b/docs/sources/guides/whats-new-in-v4-5.md index b2de451308a..a5cd3ca982d 100644 --- a/docs/sources/guides/whats-new-in-v4-5.md +++ b/docs/sources/guides/whats-new-in-v4-5.md @@ -12,7 +12,7 @@ weight = -4 # What's New in Grafana v4.5 -## Hightlights +## Highlights ### New prometheus query editor @@ -62,7 +62,7 @@ Datas source selection & options & help are now above your metric queries. ### Minor Changes * **InfluxDB**: Change time range filter for absolute time ranges to be inclusive instead of exclusive [#8319](https://github.com/grafana/grafana/issues/8319), thx [@Oxydros](https://github.com/Oxydros) -* **InfluxDB**: Added paranthesis around tag filters in queries [#9131](https://github.com/grafana/grafana/pull/9131) +* **InfluxDB**: Added parenthesis around tag filters in queries [#9131](https://github.com/grafana/grafana/pull/9131) ## Bug Fixes diff --git a/docs/sources/guides/whats-new-in-v4-6.md b/docs/sources/guides/whats-new-in-v4-6.md index fd75384761f..09955fa58cc 100644 --- a/docs/sources/guides/whats-new-in-v4-6.md +++ b/docs/sources/guides/whats-new-in-v4-6.md @@ -45,7 +45,7 @@ This makes exploring and filtering Prometheus data much easier. * **GCS**: Adds support for Google Cloud Storage [#8370](https://github.com/grafana/grafana/issues/8370) thx [@chuhlomin](https://github.com/chuhlomin) * **Prometheus**: Adds /metrics endpoint for exposing Grafana metrics. [#9187](https://github.com/grafana/grafana/pull/9187) -* **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk) +* **Graph**: Add support for local formatting in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk) * **Jaeger**: Add support for open tracing using jaeger in Grafana. [#9213](https://github.com/grafana/grafana/pull/9213) * **Unit types**: New date & time unit types added, useful in singlestat to show dates & times. [#3678](https://github.com/grafana/grafana/issues/3678), [#6710](https://github.com/grafana/grafana/issues/6710), [#2764](https://github.com/grafana/grafana/issues/2764) * **CLI**: Make it possible to install plugins from any url [#5873](https://github.com/grafana/grafana/issues/5873) diff --git a/docs/sources/http_api/org.md b/docs/sources/http_api/org.md index 4c1dff904c8..b9a15450786 100644 --- a/docs/sources/http_api/org.md +++ b/docs/sources/http_api/org.md @@ -307,7 +307,7 @@ Content-Type: application/json `PUT /api/orgs/:orgId` -Update Organisation, fields *Adress 1*, *Adress 2*, *City* are not implemented yet. +Update Organisation, fields *Address 1*, *Address 2*, *City* are not implemented yet. **Example Request**: @@ -436,4 +436,4 @@ HTTP/1.1 200 Content-Type: application/json {"message":"User removed from organization"} -``` \ No newline at end of file +``` diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 6169280b798..b7fe9040574 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -482,7 +482,7 @@ Set api_url to the resource that returns [OpenID UserInfo](https://connect2id.co First set up Grafana as an OpenId client "webapplication" in Okta. Then set the Base URIs to `https:///` and set the Login redirect URIs to `https:///login/generic_oauth`. -Finaly set up the generic oauth module like this: +Finally set up the generic oauth module like this: ```bash [auth.generic_oauth] name = Okta diff --git a/docs/sources/installation/docker.md b/docs/sources/installation/docker.md index 3ca5ba06638..f246bd55d33 100644 --- a/docs/sources/installation/docker.md +++ b/docs/sources/installation/docker.md @@ -12,7 +12,7 @@ weight = 4 # Installing using Docker -Grafana is very easy to install and run using the offical docker container. +Grafana is very easy to install and run using the official docker container. ```bash $ docker run -d -p 3000:3000 grafana/grafana diff --git a/docs/sources/installation/upgrading.md b/docs/sources/installation/upgrading.md index 49cdd4ca1d3..c72bb4c0921 100644 --- a/docs/sources/installation/upgrading.md +++ b/docs/sources/installation/upgrading.md @@ -25,7 +25,7 @@ Before upgrading it can be a good idea to backup your Grafana database. This wil If you use sqlite you only need to make a backup of your `grafana.db` file. This is usually located at `/var/lib/grafana/grafana.db` on unix system. If you are unsure what database you use and where it is stored check you grafana configuration file. If you -installed grafana to custom location using a binary tar/zip it is usally in `/data`. +installed grafana to custom location using a binary tar/zip it is usually in `/data`. #### mysql diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index 016d64d9ee9..6dbc9cc9d11 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -168,7 +168,7 @@ Option | Description *Include All option* | Add a special `All` option whose value includes all options. *Custom all value* | By default the `All` value will include all options in combined expression. This can become very long and can have performance problems. Many times it can be better to specify a custom all value, like a wildcard regex. To make it possible to have custom regex, globs or lucene syntax in the **Custom all value** option it is never escaped so you will have to think avbout what is a valid value for your data source. -### Formating multiple values +### Formatting multiple values Interpolating a variable with multiple values selected is tricky as it is not straight forward how to format the multiple values to into a string that is valid in the given context where the variable is used. Grafana tries to solve this by allowing each data source plugin to @@ -186,7 +186,7 @@ break the regex expression. **Elasticsearch** uses lucene query syntax, so the same variable would, in this case, be formatted as `("host1" OR "host2" OR "host3")`. In this case every value needs to be escaped so that the value can contain lucene control words and quotation marks. -#### Formating troubles +#### Formatting troubles Automatic escaping & formatting can cause problems and it can be tricky to grasp the logic is behind it. Especially for InfluxDB and Prometheus where the use of regex syntax requires that the variable is used in regex operator context. diff --git a/docs/sources/tutorials/authproxy.md b/docs/sources/tutorials/authproxy.md index 8003be20644..6f13de85c18 100644 --- a/docs/sources/tutorials/authproxy.md +++ b/docs/sources/tutorials/authproxy.md @@ -108,7 +108,7 @@ In this example we use Apache as a reverseProxy in front of Grafana. Apache hand * The next part of the configuration is the tricky part. We use Apache’s rewrite engine to create our **X-WEBAUTH-USER header**, populated with the authenticated user. - * **RewriteRule .* - [E=PROXY_USER:%{LA-U:REMOTE_USER}, NS]**: This line is a little bit of magic. What it does, is for every request use the rewriteEngines look-ahead (LA-U) feature to determine what the REMOTE_USER variable would be set to after processing the request. Then assign the result to the variable PROXY_USER. This is neccessary as the REMOTE_USER variable is not available to the RequestHeader function. + * **RewriteRule .* - [E=PROXY_USER:%{LA-U:REMOTE_USER}, NS]**: This line is a little bit of magic. What it does, is for every request use the rewriteEngines look-ahead (LA-U) feature to determine what the REMOTE_USER variable would be set to after processing the request. Then assign the result to the variable PROXY_USER. This is necessary as the REMOTE_USER variable is not available to the RequestHeader function. * **RequestHeader set X-WEBAUTH-USER “%{PROXY_USER}e”**: With the authenticated username now stored in the PROXY_USER variable, we create a new HTTP request header that will be sent to our backend Grafana containing the username. @@ -149,7 +149,7 @@ auto_sign_up = true ##### Grafana Container -For this example, we use the offical Grafana docker image available at [Docker Hub](https://hub.docker.com/r/grafana/grafana/) +For this example, we use the official Grafana docker image available at [Docker Hub](https://hub.docker.com/r/grafana/grafana/) * Create a file `grafana.ini` with the following contents @@ -166,7 +166,7 @@ header_property = username auto_sign_up = true ``` -* Launch the Grafana container, using our custom grafana.ini to replace `/etc/grafana/grafana.ini`. We dont expose any ports for this container as it will only be connected to by our Apache container. +* Launch the Grafana container, using our custom grafana.ini to replace `/etc/grafana/grafana.ini`. We don't expose any ports for this container as it will only be connected to by our Apache container. ```bash docker run -i -v $(pwd)/grafana.ini:/etc/grafana/grafana.ini --name grafana grafana/grafana @@ -174,7 +174,7 @@ docker run -i -v $(pwd)/grafana.ini:/etc/grafana/grafana.ini --name grafana graf ### Apache Container -For this example we use the offical Apache docker image available at [Docker Hub](https://hub.docker.com/_/httpd/) +For this example we use the official Apache docker image available at [Docker Hub](https://hub.docker.com/_/httpd/) * Create a file `httpd.conf` with the following contents @@ -244,4 +244,4 @@ ProxyPassReverse / http://grafana:3000/ ### Use grafana. -With our Grafana and Apache containers running, you can now connect to http://localhost/ and log in using the username/password we created in the htpasswd file. \ No newline at end of file +With our Grafana and Apache containers running, you can now connect to http://localhost/ and log in using the username/password we created in the htpasswd file. From e2add988ec9eb7bec985c901002964b1646d4458 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Sat, 14 Apr 2018 09:56:47 -0400 Subject: [PATCH 129/145] Documentation spelling fix --- docs/sources/alerting/notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/alerting/notifications.md b/docs/sources/alerting/notifications.md index bb119687750..19e7d6982fc 100644 --- a/docs/sources/alerting/notifications.md +++ b/docs/sources/alerting/notifications.md @@ -156,7 +156,7 @@ Prometheus Alertmanager | `prometheus-alertmanager` | no Grafana can render the panel associated with the alert rule and include that in the notification. Most Notification Channels require that this image be publicly accessable (Slack and PagerDuty for example). In order to include images in alert notifications, Grafana can upload the image to an image store. It currently supports Amazon S3, Webdav, Google Cloud Storage and Azure Blob Storage. So to set that up you need to configure the [external image uploader](/installation/configuration/#external-image-storage) in your grafana-server ini config file. -Be aware that some notifiers requires public access to the image to be able to include it in the notification. So make sure to enable public access to the images. If your using local image uploader, your Grafana instance need to be accessible by the internet. +Be aware that some notifiers requires public access to the image to be able to include it in the notification. So make sure to enable public access to the images. If you're using local image uploader, your Grafana instance need to be accessible by the internet. Currently only the Email Channels attaches images if no external image store is specified. To include images in alert notifications for other channels then you need to set up an external image store. From 52bd51f2d0afa0e0edae4e4114f25ebcbb89bfb9 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Sat, 14 Apr 2018 17:59:33 +0200 Subject: [PATCH 130/145] changelog: adds note for #11530 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 170d366cb24..9a50a910471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ * **Prometheus**: Escape backslash in labels correctly. [#10555](https://github.com/grafana/grafana/issues/10555), thx [@roidelapluie](https://github.com/roidelapluie) * **Variables**: Case-insensitive sorting for template values [#11128](https://github.com/grafana/grafana/issues/11128) thx [@cross](https://github.com/cross) * **Annotations (native)**: Change default limit from 10 to 100 when querying api [#11569](https://github.com/grafana/grafana/issues/11569), thx [@flopp999](https://github.com/flopp999) +* **MySQL/Postgres/MSSQL**: PostgreSQL datasource generates invalid query with dates before 1970 [#11530](https://github.com/grafana/grafana/issues/11530) thx [@ryantxu](https://github.com/ryantxu) # 5.0.4 (2018-03-28) From ee623e2091677efd68eaf22c1018f07538584b23 Mon Sep 17 00:00:00 2001 From: Matthew McGinn Date: Sun, 15 Apr 2018 13:44:17 -0400 Subject: [PATCH 131/145] Grafana-CLI: mention the plugins directory is not writable on failure --- pkg/cmd/grafana-cli/commands/install_command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index f40bc9c081b..6f6849ccddf 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -33,7 +33,7 @@ func validateInput(c CommandLine, pluginFolder string) error { fileInfo, err := os.Stat(pluginsDir) if err != nil { if err = os.MkdirAll(pluginsDir, os.ModePerm); err != nil { - return errors.New(fmt.Sprintf("pluginsDir (%s) is not a directory", pluginsDir)) + return errors.New(fmt.Sprintf("pluginsDir (%s) is not a writable directory", pluginsDir)) } return nil } From 7534f0bff6e702970daa48695c9143d1124f6e5d Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Sun, 15 Apr 2018 21:37:34 +0200 Subject: [PATCH 132/145] Support deleting empty playlist --- pkg/api/playlist.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/api/playlist.go b/pkg/api/playlist.go index d2413dfbb4c..a90b6425cb6 100644 --- a/pkg/api/playlist.go +++ b/pkg/api/playlist.go @@ -33,7 +33,7 @@ func ValidateOrgPlaylist(c *m.ReqContext) { return } - if len(items) == 0 { + if len(items) == 0 && c.Context.Req.Method != "DELETE" { c.JsonApiErr(404, "Playlist is empty", itemsErr) return } From 738fb29134edc60187d9c27e969a117fbef650fb Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 16 Apr 2018 09:37:55 +0200 Subject: [PATCH 133/145] changelog: adds note about closing #11228 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8449e4e7a20..7d27f15e5e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ * **Variables**: Case-insensitive sorting for template values [#11128](https://github.com/grafana/grafana/issues/11128) thx [@cross](https://github.com/cross) * **Annotations (native)**: Change default limit from 10 to 100 when querying api [#11569](https://github.com/grafana/grafana/issues/11569), thx [@flopp999](https://github.com/flopp999) * **MySQL/Postgres/MSSQL**: PostgreSQL datasource generates invalid query with dates before 1970 [#11530](https://github.com/grafana/grafana/issues/11530) thx [@ryantxu](https://github.com/ryantxu) +* **Kiosk**: Adds url parameter for starting a dashboard in inactive mode [#11228](https://github.com/grafana/grafana/issues/11228), thx [@towolf](https://github.com/towolf) # 5.0.4 (2018-03-28) From 6c6b74fc390fa6281b4caf85059312f8629f9a72 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 16 Apr 2018 09:57:41 +0200 Subject: [PATCH 134/145] removes codecov from front-end tests --- Gruntfile.js | 1 - codecov.yml | 13 ------------- package.json | 1 - scripts/circle-test-frontend.sh | 9 ++------- scripts/grunt/options/exec.js | 7 +------ 5 files changed, 3 insertions(+), 28 deletions(-) delete mode 100644 codecov.yml diff --git a/Gruntfile.js b/Gruntfile.js index a0607ef49dc..03f70565b57 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -22,7 +22,6 @@ module.exports = function (grunt) { } } - config.coverage = grunt.option('coverage'); config.phjs = grunt.option('phjsToRelease'); config.pkg.version = grunt.option('pkgVer') || config.pkg.version; diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 82a86e0232b..00000000000 --- a/codecov.yml +++ /dev/null @@ -1,13 +0,0 @@ -coverage: - precision: 2 - round: down - range: "50...100" - - status: - project: yes - patch: yes - changes: no - -comment: - layout: "diff" - behavior: "once" diff --git a/package.json b/package.json index ce861a25f7b..b74d23f33b2 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,6 @@ "watch": "webpack --progress --colors --watch --config scripts/webpack/webpack.dev.js", "build": "grunt build", "test": "grunt test", - "test:coverage": "grunt test --coverage=true", "lint": "tslint -c tslint.json --project tsconfig.json --type-check", "karma": "grunt karma:dev", "jest": "jest --notify --watch", diff --git a/scripts/circle-test-frontend.sh b/scripts/circle-test-frontend.sh index 9857e00f70d..325c24ae7a9 100755 --- a/scripts/circle-test-frontend.sh +++ b/scripts/circle-test-frontend.sh @@ -10,10 +10,5 @@ function exit_if_fail { fi } -exit_if_fail npm run test:coverage -exit_if_fail npm run build - -# publish code coverage -echo "Publishing javascript code coverage" -bash <(curl -s https://codecov.io/bash) -cF javascript -rm -rf coverage +exit_if_fail npm run test +exit_if_fail npm run build \ No newline at end of file diff --git a/scripts/grunt/options/exec.js b/scripts/grunt/options/exec.js index e22d060ea04..be163581bf6 100644 --- a/scripts/grunt/options/exec.js +++ b/scripts/grunt/options/exec.js @@ -1,14 +1,9 @@ module.exports = function(config, grunt) { 'use strict'; - var coverage = ''; - if (config.coverage) { - coverage = '--coverage --maxWorkers 2'; - } - return { tslint: 'node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json', - jest: 'node ./node_modules/jest-cli/bin/jest.js ' + coverage, + jest: 'node ./node_modules/jest-cli/bin/jest.js --maxWorkers 2', webpack: 'node ./node_modules/webpack/bin/webpack.js --config scripts/webpack/webpack.prod.js', }; }; From 9337972a0fdc11c2ed1d1e0cc89178f0c8182c76 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 16 Apr 2018 11:06:23 +0200 Subject: [PATCH 135/145] sqlds: fix text in comments for tests --- pkg/tsdb/mssql/mssql_test.go | 4 ++-- pkg/tsdb/mysql/mysql_test.go | 5 +++-- pkg/tsdb/postgres/postgres_test.go | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pkg/tsdb/mssql/mssql_test.go b/pkg/tsdb/mssql/mssql_test.go index dc527d09bd9..599f4869f6a 100644 --- a/pkg/tsdb/mssql/mssql_test.go +++ b/pkg/tsdb/mssql/mssql_test.go @@ -16,10 +16,10 @@ import ( ) // To run this test, remove the Skip from SkipConvey -// and set up a MSSQL db named grafanatest and a user/password grafana/Password! +// The tests require a MSSQL db named grafanatest and a user/password grafana/Password! // Use the docker/blocks/mssql_tests/docker-compose.yaml to spin up a // preconfigured MSSQL server suitable for running these tests. -// Thers's also a dashboard.json in same directory that you can import to Grafana +// There is also a dashboard.json in same directory that you can import to Grafana // once you've created a datasource for the test server/database. // If needed, change the variable below to the IP address of the database. var serverIP string = "localhost" diff --git a/pkg/tsdb/mysql/mysql_test.go b/pkg/tsdb/mysql/mysql_test.go index 827ebfa9555..74cedea803a 100644 --- a/pkg/tsdb/mysql/mysql_test.go +++ b/pkg/tsdb/mysql/mysql_test.go @@ -17,10 +17,11 @@ import ( ) // To run this test, set runMySqlTests=true -// and set up a MySQL db named grafana_ds_tests and a user/password grafana/password +// Or from the commandline: GRAFANA_TEST_DB=mysql go test -v ./pkg/tsdb/mysql +// The tests require a MySQL db named grafana_ds_tests and a user/password grafana/password // Use the docker/blocks/mysql_tests/docker-compose.yaml to spin up a // preconfigured MySQL server suitable for running these tests. -// Thers's also a dashboard.json in same directory that you can import to Grafana +// There is also a dashboard.json in same directory that you can import to Grafana // once you've created a datasource for the test server/database. func TestMySQL(t *testing.T) { // change to true to run the MySQL tests diff --git a/pkg/tsdb/postgres/postgres_test.go b/pkg/tsdb/postgres/postgres_test.go index d35ba2b3209..d18251bac7d 100644 --- a/pkg/tsdb/postgres/postgres_test.go +++ b/pkg/tsdb/postgres/postgres_test.go @@ -17,11 +17,12 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -// To run this test, set runMySqlTests=true -// and set up a PostgreSQL db named grafanadstest and a user/password grafanatest/grafanatest! +// To run this test, set runPostgresTests=true +// Or from the commandline: GRAFANA_TEST_DB=postgres go test -v ./pkg/tsdb/postgres +// The tests require a PostgreSQL db named grafanadstest and a user/password grafanatest/grafanatest! // Use the docker/blocks/postgres_tests/docker-compose.yaml to spin up a // preconfigured Postgres server suitable for running these tests. -// Thers's also a dashboard.json in same directory that you can import to Grafana +// There is also a dashboard.json in same directory that you can import to Grafana // once you've created a datasource for the test server/database. func TestPostgres(t *testing.T) { // change to true to run the MySQL tests From 645658d79765a9d9945ca3a3b3d6f46472b5598e Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 16 Apr 2018 13:08:00 +0200 Subject: [PATCH 136/145] changlelog: notes about closing issues/pr's #11053, #11252, #10836, #11185, #11168, #11332, #11391, #11073, #9342, #11001, #11183, #11211, #11384, #11095, #10792, #11138, #11516 [skip ci] --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d27f15e5e3..90b92efc979 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * **Alerting**: Add support for retries on alert queries [#5855](https://github.com/grafana/grafana/issues/5855), thx [@Thib17](https://github.com/Thib17) * **Table**: Table plugin value mappings [#7119](https://github.com/grafana/grafana/issues/7119), thx [infernix](https://github.com/infernix) * **IE11**: IE 11 compatibility [#11165](https://github.com/grafana/grafana/issues/11165) +* **Scrolling**: Better scrolling experience [#11053](https://github.com/grafana/grafana/issues/11053), [#11252](https://github.com/grafana/grafana/issues/11252), [#10836](https://github.com/grafana/grafana/issues/10836), [#11185](https://github.com/grafana/grafana/issues/11185), [#11168](https://github.com/grafana/grafana/issues/11168) ### Minor * **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes) @@ -32,6 +33,21 @@ * **Annotations (native)**: Change default limit from 10 to 100 when querying api [#11569](https://github.com/grafana/grafana/issues/11569), thx [@flopp999](https://github.com/flopp999) * **MySQL/Postgres/MSSQL**: PostgreSQL datasource generates invalid query with dates before 1970 [#11530](https://github.com/grafana/grafana/issues/11530) thx [@ryantxu](https://github.com/ryantxu) * **Kiosk**: Adds url parameter for starting a dashboard in inactive mode [#11228](https://github.com/grafana/grafana/issues/11228), thx [@towolf](https://github.com/towolf) +* **Dashboard**: Enable closing timepicker using escape key [#11332](https://github.com/grafana/grafana/issues/11332) +* **Datasources**: Rename direct access mode in the data source settings [#11391](https://github.com/grafana/grafana/issues/11391) +* **Search**: Display dashboards in folder indented [#11073](https://github.com/grafana/grafana/issues/11073) +* **Units**: Use B/s instead Bps for Bytes per second [#9342](https://github.com/grafana/grafana/pull/9342), thx [@mayli](https://github.com/mayli) +* **Units**: Radiation units [#11001](https://github.com/grafana/grafana/issues/11001), thx [@victorclaessen](https://github.com/victorclaessen) +* **Units**: Timeticks unit [#11183](https://github.com/grafana/grafana/pull/11183), thx [@jtyr](https://github.com/jtyr) +* **Units**: Concentration units and "Normal cubic metre" [#11211](https://github.com/grafana/grafana/issues/11211), thx [@flopp999](https://github.com/flopp999) +* **Units**: New currency - Czech koruna [#11384](https://github.com/grafana/grafana/pull/11384), thx [@Rohlik](https://github.com/Rohlik) +* **Avatar**: Fix DISABLE_GRAVATAR option [#11095](https://github.com/grafana/grafana/issues/11095) +* **Heatmap**: Disable log scale when using time time series buckets [#10792](https://github.com/grafana/grafana/issues/10792) +* **Provisioning**: Remove `id` from json when provisioning dashboards, [#11138](https://github.com/grafana/grafana/issues/11138) +* **Prometheus**: tooltip for legend format not showing properly [#11516](https://github.com/grafana/grafana/issues/11516), thx [@svenklemm](https://github.com/svenklemm) + +### Tech +* Migrated JavaScript files to TypeScript # 5.0.4 (2018-03-28) From 712212d6aa36bab1881ba8697ffce5bd0ad6fb7c Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Mon, 16 Apr 2018 13:37:05 +0200 Subject: [PATCH 137/145] Show Grafana version and build in Help menu * establishes Help as the single place to look for the Grafana version * version is passed as menu sub-title to side menu * added rendering of sub-title, plus styles * sub-title was used by profile menu (its value is the login string), but was not shown; now showing this value on condition that login name is different from user name --- pkg/api/index.go | 8 +++++++- public/app/core/components/sidemenu/sidemenu.html | 5 ++++- public/sass/components/_sidemenu.scss | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pkg/api/index.go b/pkg/api/index.go index a1d21d1c686..0f8b5a6fc78 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -118,9 +118,14 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) { }) if c.IsSignedIn { + // Only set login if it's different from the name + var login string + if c.SignedInUser.Login != c.SignedInUser.NameOrFallback() { + login = c.SignedInUser.Login + } profileNode := &dtos.NavLink{ Text: c.SignedInUser.NameOrFallback(), - SubTitle: c.SignedInUser.Login, + SubTitle: login, Id: "profile", Img: data.User.GravatarUrl, Url: setting.AppSubUrl + "/profile", @@ -284,6 +289,7 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) { data.NavTree = append(data.NavTree, &dtos.NavLink{ Text: "Help", + SubTitle: fmt.Sprintf(`Grafana version: %s+%s`, setting.BuildVersion, setting.BuildCommit), Id: "help", Url: "#", Icon: "gicon gicon-question", diff --git a/public/app/core/components/sidemenu/sidemenu.html b/public/app/core/components/sidemenu/sidemenu.html index 1b301363e62..a9ebbe2681d 100644 --- a/public/app/core/components/sidemenu/sidemenu.html +++ b/public/app/core/components/sidemenu/sidemenu.html @@ -70,9 +70,12 @@ {{::child.text}} +
  • + {{::item.subTitle}} +
  • {{::item.text}}
  • -
    +
    \ No newline at end of file diff --git a/public/sass/components/_sidemenu.scss b/public/sass/components/_sidemenu.scss index d1372484074..dde01c2ba9c 100644 --- a/public/sass/components/_sidemenu.scss +++ b/public/sass/components/_sidemenu.scss @@ -149,6 +149,14 @@ color: #ebedf2; } +.side-menu-subtitle { + padding: 0.5rem 0.5rem 0.5rem 1rem; + font-size: $font-size-sm; + color: $text-color-weak; + border-top: 1px solid $dropdownDividerBottom; + margin-top: 0.25rem; +} + li.sidemenu-org-switcher { border-bottom: 1px solid $dropdownDividerBottom; } From 8d963e27332a6a80fba7f42a3c0726a40f1608f3 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 16 Apr 2018 15:45:55 +0200 Subject: [PATCH 138/145] changelog: improved docker image --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90b92efc979..09336084570 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * **Table**: Table plugin value mappings [#7119](https://github.com/grafana/grafana/issues/7119), thx [infernix](https://github.com/infernix) * **IE11**: IE 11 compatibility [#11165](https://github.com/grafana/grafana/issues/11165) * **Scrolling**: Better scrolling experience [#11053](https://github.com/grafana/grafana/issues/11053), [#11252](https://github.com/grafana/grafana/issues/11252), [#10836](https://github.com/grafana/grafana/issues/10836), [#11185](https://github.com/grafana/grafana/issues/11185), [#11168](https://github.com/grafana/grafana/issues/11168) +* **Docker**: Improved docker image (breaking changes regarding file ownership) [grafana-docker #141](https://github.com/grafana/grafana-docker/issues/141), thx [@Spindel](https://github.com/Spindel), [@ChristianKniep](https://github.com/ChristianKniep), [@brancz](https://github.com/brancz) and [@jangaraj](https://github.com/jangaraj) ### Minor * **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes) From 90ed046ce35a75b020632b6d5704c0fa475e3dec Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 16 Apr 2018 16:03:50 +0200 Subject: [PATCH 139/145] docs: elasticsearch and influxdb docs for group by time interval option (#11609) --- .../features/datasources/elasticsearch.md | 16 ++++++++++++++++ docs/sources/features/datasources/influxdb.md | 16 ++++++++++++++++ .../elasticsearch/partials/config.html | 2 +- .../plugins/datasource/influxdb/query_help.md | 5 ++--- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/docs/sources/features/datasources/elasticsearch.md b/docs/sources/features/datasources/elasticsearch.md index db17aafd271..7e6e281df7e 100644 --- a/docs/sources/features/datasources/elasticsearch.md +++ b/docs/sources/features/datasources/elasticsearch.md @@ -55,6 +55,22 @@ a time pattern for the index name or a wildcard. Be sure to specify your Elasticsearch version in the version selection dropdown. This is very important as there are differences how queries are composed. Currently only 2.x and 5.x are supported. +### Min time interval +A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example `1m` if your data is written every minute. +This option can also be overridden/configured in a dashboard panel under data source options. It's important to note that this value **needs** to be formated as a +number followed by a valid time identifier, e.g. `1m` (1 minute) or `30s` (30 seconds). The following time identifiers are supported: + +Identifier | Description +------------ | ------------- +`y` | year +`M` | month +`w` | week +`d` | day +`h` | hour +`m` | minute +`s` | second +`ms` | millisecond + ## Metric Query editor ![](/img/docs/elasticsearch/query_editor.png) diff --git a/docs/sources/features/datasources/influxdb.md b/docs/sources/features/datasources/influxdb.md index b49e0f9dfc6..fccdd3cc35e 100644 --- a/docs/sources/features/datasources/influxdb.md +++ b/docs/sources/features/datasources/influxdb.md @@ -39,6 +39,22 @@ Proxy access means that the Grafana backend will proxy all requests from the bro `grafana-server`. This means that the URL you specify needs to be accessible from the server you are running Grafana on. Proxy access mode is also more secure as the username & password will never reach the browser. +### Min time interval +A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example `1m` if your data is written every minute. +This option can also be overridden/configured in a dashboard panel under data source options. It's important to note that this value **needs** to be formated as a +number followed by a valid time identifier, e.g. `1m` (1 minute) or `30s` (30 seconds). The following time identifiers are supported: + +Identifier | Description +------------ | ------------- +`y` | year +`M` | month +`w` | week +`d` | day +`h` | hour +`m` | minute +`s` | second +`ms` | millisecond + ## Query Editor {{< docs-imagebox img="/img/docs/v45/influxdb_query_still.png" class="docs-image--no-shadow" animated-gif="/img/docs/v45/influxdb_query.gif" >}} diff --git a/public/app/plugins/datasource/elasticsearch/partials/config.html b/public/app/plugins/datasource/elasticsearch/partials/config.html index da23e9ddab1..def59518624 100644 --- a/public/app/plugins/datasource/elasticsearch/partials/config.html +++ b/public/app/plugins/datasource/elasticsearch/partials/config.html @@ -35,7 +35,7 @@
    - Min interval + Min time interval A lower limit for the auto group by time interval. Recommended to be set to write frequency, diff --git a/public/app/plugins/datasource/influxdb/query_help.md b/public/app/plugins/datasource/influxdb/query_help.md index 0d4fd941ca5..4930ccbc83f 100644 --- a/public/app/plugins/datasource/influxdb/query_help.md +++ b/public/app/plugins/datasource/influxdb/query_help.md @@ -10,7 +10,7 @@ - When stacking is enabled it is important that points align - If there are missing points for one series it can cause gaps or missing bars - You must use fill(0), and select a group by time low limit -- Use the group by time option below your queries and specify for example >10s if your metrics are written every 10 seconds +- Use the group by time option below your queries and specify for example 10s if your metrics are written every 10 seconds - This will insert zeros for series that are missing measurements and will make stacking work properly #### Group by time @@ -18,8 +18,7 @@ - Leave the group by time field empty for each query and it will be calculated based on time range and pixel width of the graph - If you use fill(0) or fill(null) set a low limit for the auto group by time interval - The low limit can only be set in the group by time option below your queries -- You set a low limit by adding a greater sign before the interval -- Example: >60s if you write metrics to InfluxDB every 60 seconds +- Example: 60s if you write metrics to InfluxDB every 60 seconds #### Documentation links: From 5a29c1728225643b3aa9018fd319214889dc8edf Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Mon, 16 Apr 2018 16:25:28 +0200 Subject: [PATCH 140/145] moved version in help menu to top --- pkg/api/index.go | 2 +- public/app/core/components/sidemenu/sidemenu.html | 6 +++--- public/sass/components/_sidemenu.scss | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/api/index.go b/pkg/api/index.go index 0f8b5a6fc78..94094706f68 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -289,7 +289,7 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) { data.NavTree = append(data.NavTree, &dtos.NavLink{ Text: "Help", - SubTitle: fmt.Sprintf(`Grafana version: %s+%s`, setting.BuildVersion, setting.BuildCommit), + SubTitle: fmt.Sprintf(`Grafana v%s (%s)`, setting.BuildVersion, setting.BuildCommit), Id: "help", Url: "#", Icon: "gicon gicon-question", diff --git a/public/app/core/components/sidemenu/sidemenu.html b/public/app/core/components/sidemenu/sidemenu.html index a9ebbe2681d..9de61345cd0 100644 --- a/public/app/core/components/sidemenu/sidemenu.html +++ b/public/app/core/components/sidemenu/sidemenu.html @@ -54,6 +54,9 @@