From ca6dd7392312fb05c15a18d71aba4df09133ab1c Mon Sep 17 00:00:00 2001 From: Athurg Feng Date: Thu, 25 Oct 2018 18:17:05 +0800 Subject: [PATCH 001/244] Add match values into Dingding notification message --- pkg/services/alerting/notifiers/dingding.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index 1ef085c82f1..9ad85d55004 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -1,6 +1,8 @@ package notifiers import ( + "fmt" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/log" @@ -61,6 +63,10 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { message = title } + for i, match := range evalContext.EvalMatches { + message += fmt.Sprintf("\\n%2d. %s value %s", i+1, match.Metric, match.Value) + } + bodyJSON, err := simplejson.NewJson([]byte(`{ "msgtype": "link", "link": { From 7f45afac63b93bacd73d6ada811b5db0178b1723 Mon Sep 17 00:00:00 2001 From: Athurg Feng Date: Thu, 25 Oct 2018 18:24:04 +0800 Subject: [PATCH 002/244] Split text template into variable --- pkg/services/alerting/notifiers/dingding.go | 23 ++++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index 9ad85d55004..bf1b721f753 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -10,19 +10,21 @@ import ( "github.com/grafana/grafana/pkg/services/alerting" ) -func init() { - alerting.RegisterNotifier(&alerting.NotifierPlugin{ - Type: "dingding", - Name: "DingDing", - Description: "Sends HTTP POST request to DingDing", - Factory: NewDingDingNotifier, - OptionsTemplate: ` +const DingdingOptionsTemplate = `

DingDing settings

Url
- `, +` + +func init() { + alerting.RegisterNotifier(&alerting.NotifierPlugin{ + Type: "dingding", + Name: "DingDing", + Description: "Sends HTTP POST request to DingDing", + Factory: NewDingDingNotifier, + OptionsTemplate: DingdingOptionsTemplate, }) } @@ -67,7 +69,7 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { message += fmt.Sprintf("\\n%2d. %s value %s", i+1, match.Metric, match.Value) } - bodyJSON, err := simplejson.NewJson([]byte(`{ + bodyStr := `{ "msgtype": "link", "link": { "text": "` + message + `", @@ -75,7 +77,8 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { "picUrl": "` + picUrl + `", "messageUrl": "` + messageUrl + `" } - }`)) + }` + bodyJSON, err := simplejson.NewJson([]byte(bodyStr)) if err != nil { this.log.Error("Failed to create Json data", "error", err, "dingding", this.Name) From cb86e386289a02f1a8638b2e84030b36c697efa9 Mon Sep 17 00:00:00 2001 From: Athurg Feng Date: Thu, 25 Oct 2018 18:29:47 +0800 Subject: [PATCH 003/244] Add Dingding message type to support mass text notification --- pkg/services/alerting/notifiers/dingding.go | 45 ++++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index bf1b721f753..ce932b7a799 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -10,12 +10,17 @@ import ( "github.com/grafana/grafana/pkg/services/alerting" ) +const DefaultDingdingMsgType = "link" const DingdingOptionsTemplate = `

DingDing settings

Url
+
+ MessageType + +
` func init() { @@ -35,8 +40,11 @@ func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error) return nil, alerting.ValidationError{Reason: "Could not find url property in settings"} } + msgType := model.Settings.Get("msgType").MustString(DefaultDingdingMsgType) + return &DingDingNotifier{ NotifierBase: NewNotifierBase(model), + MsgType: msgType, Url: url, log: log.New("alerting.notifier.dingding"), }, nil @@ -44,8 +52,9 @@ func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error) type DingDingNotifier struct { NotifierBase - Url string - log log.Logger + MsgType string + Url string + log log.Logger } func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { @@ -69,15 +78,29 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { message += fmt.Sprintf("\\n%2d. %s value %s", i+1, match.Metric, match.Value) } - bodyStr := `{ - "msgtype": "link", - "link": { - "text": "` + message + `", - "title": "` + title + `", - "picUrl": "` + picUrl + `", - "messageUrl": "` + messageUrl + `" - } - }` + var bodyStr string + if this.MsgType == "actionCard" { + bodyStr = `{ + "msgtype": "actionCard", + "actionCard": { + "text": "` + message + `", + "title": "` + title + `", + "singleTitle": "More", + "singleURL": "` + messageUrl + `" + } + }` + } else { + bodyStr = `{ + "msgtype": "link", + "link": { + "text": "` + message + `", + "title": "` + title + `", + "picUrl": "` + picUrl + `", + "messageUrl": "` + messageUrl + `" + } + }` + } + bodyJSON, err := simplejson.NewJson([]byte(bodyStr)) if err != nil { From 201dd6bf658501782180ce90111390d4970b16c8 Mon Sep 17 00:00:00 2001 From: Athurg Feng Date: Thu, 25 Oct 2018 18:53:45 +0800 Subject: [PATCH 004/244] Optimize the Dingding match values format --- pkg/services/alerting/notifiers/dingding.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index ce932b7a799..94961e82025 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -75,7 +75,7 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { } for i, match := range evalContext.EvalMatches { - message += fmt.Sprintf("\\n%2d. %s value %s", i+1, match.Metric, match.Value) + message += fmt.Sprintf("\\n%2d. %s: %s", i+1, match.Metric, match.Value) } var bodyStr string From b7787db34e2b71cdc59aef829ea1a3d69b0a1e3c Mon Sep 17 00:00:00 2001 From: Athurg Feng Date: Thu, 8 Nov 2018 18:44:00 +0800 Subject: [PATCH 005/244] Add new option to set where to open the message url --- pkg/services/alerting/notifiers/dingding.go | 37 ++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index 94961e82025..af1063a4c70 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -2,6 +2,7 @@ package notifiers import ( "fmt" + "net/url" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" @@ -15,12 +16,17 @@ const DingdingOptionsTemplate = `

DingDing settings

Url - +
MessageType
+
+ OpenInBrowser + + Open the message url in browser instead of inside of Dingding +
` func init() { @@ -41,20 +47,23 @@ func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error) } msgType := model.Settings.Get("msgType").MustString(DefaultDingdingMsgType) + openInBrowser := model.Settings.Get("openInBrowser").MustBool(true) return &DingDingNotifier{ - NotifierBase: NewNotifierBase(model), - MsgType: msgType, - Url: url, - log: log.New("alerting.notifier.dingding"), + NotifierBase: NewNotifierBase(model), + OpenInBrowser: openInBrowser, + MsgType: msgType, + Url: url, + log: log.New("alerting.notifier.dingding"), }, nil } type DingDingNotifier struct { NotifierBase - MsgType string - Url string - log log.Logger + MsgType string + OpenInBrowser bool //Set whether the message url will open outside of Dingding + Url string + log log.Logger } func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { @@ -65,6 +74,18 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { this.log.Error("Failed to get messageUrl", "error", err, "dingding", this.Name) messageUrl = "" } + + if this.OpenInBrowser { + q := url.Values{ + "pc_slide": {"false"}, + "url": {messageUrl}, + } + + // Use special link to auto open the message url outside of Dingding + // Refer: https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=104972&docType=1#s9 + messageUrl = "dingtalk://dingtalkclient/page/link?" + q.Encode() + } + this.log.Info("messageUrl:" + messageUrl) message := evalContext.Rule.Message From 919d00437e21944d5feea3c6ac175a2d85736784 Mon Sep 17 00:00:00 2001 From: Athurg Feng Date: Mon, 12 Nov 2018 11:18:53 +0800 Subject: [PATCH 006/244] Add pic into actionCard message --- pkg/services/alerting/notifiers/dingding.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index af1063a4c70..3514554a1db 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -101,6 +101,11 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { var bodyStr string if this.MsgType == "actionCard" { + // Embed the pic into the markdown directly because actionCard doesn't have a picUrl field + if picUrl != "" { + message = "![](" + picUrl + ")\\n\\n" + message + } + bodyStr = `{ "msgtype": "actionCard", "actionCard": { From bba92c0746e3bbbb53833dd222cb4f38cca8a2d5 Mon Sep 17 00:00:00 2001 From: Athurg Feng Date: Sat, 2 Feb 2019 13:35:17 +0800 Subject: [PATCH 007/244] Remove option used to control within browser --- pkg/services/alerting/notifiers/dingding.go | 38 ++++++++------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index 3514554a1db..a3934903bd6 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -22,11 +22,6 @@ const DingdingOptionsTemplate = ` MessageType -
- OpenInBrowser - - Open the message url in browser instead of inside of Dingding -
` func init() { @@ -47,23 +42,20 @@ func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error) } msgType := model.Settings.Get("msgType").MustString(DefaultDingdingMsgType) - openInBrowser := model.Settings.Get("openInBrowser").MustBool(true) return &DingDingNotifier{ - NotifierBase: NewNotifierBase(model), - OpenInBrowser: openInBrowser, - MsgType: msgType, - Url: url, - log: log.New("alerting.notifier.dingding"), + NotifierBase: NewNotifierBase(model), + MsgType: msgType, + Url: url, + log: log.New("alerting.notifier.dingding"), }, nil } type DingDingNotifier struct { NotifierBase - MsgType string - OpenInBrowser bool //Set whether the message url will open outside of Dingding - Url string - log log.Logger + MsgType string + Url string + log log.Logger } func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { @@ -75,17 +67,15 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { messageUrl = "" } - if this.OpenInBrowser { - q := url.Values{ - "pc_slide": {"false"}, - "url": {messageUrl}, - } - - // Use special link to auto open the message url outside of Dingding - // Refer: https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=104972&docType=1#s9 - messageUrl = "dingtalk://dingtalkclient/page/link?" + q.Encode() + q := url.Values{ + "pc_slide": {"false"}, + "url": {messageUrl}, } + // Use special link to auto open the message url outside of Dingding + // Refer: https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=104972&docType=1#s9 + messageUrl = "dingtalk://dingtalkclient/page/link?" + q.Encode() + this.log.Info("messageUrl:" + messageUrl) message := evalContext.Rule.Message From 70b23ab73bfbf364c97da23fe9a829ae9099731c Mon Sep 17 00:00:00 2001 From: Athurg Feng Date: Sat, 2 Feb 2019 13:36:10 +0800 Subject: [PATCH 008/244] Add string quote func --- pkg/services/alerting/notifiers/dingding.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index a3934903bd6..3e3496622b7 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -3,6 +3,7 @@ package notifiers import ( "fmt" "net/url" + "strings" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" @@ -99,8 +100,8 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { bodyStr = `{ "msgtype": "actionCard", "actionCard": { - "text": "` + message + `", - "title": "` + title + `", + "text": "` + strings.Replace(message, `"`, "'", -1) + `", + "title": "` + strings.Replace(title, `"`, "'", -1) + `", "singleTitle": "More", "singleURL": "` + messageUrl + `" } From d46bf752939ede21d4dc9bfb0e239a1c229145ae Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Thu, 7 Feb 2019 13:54:25 +0900 Subject: [PATCH 009/244] support /api/v1/labels --- .../sources/features/datasources/prometheus.md | 1 + .../datasource/prometheus/datasource.ts | 18 ++++++++++++++++++ .../datasource/prometheus/metric_find_query.ts | 15 +++++++++++++++ .../prometheus/specs/metric_find_query.test.ts | 18 ++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index 611a3b4d9e2..f3fbcbcd5d9 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -68,6 +68,7 @@ provides the following functions you can use in the `Query` input field. Name | Description ---- | -------- +*label_names()* | Returns a list of label names. *label_values(label)* | Returns a list of label values for the `label` in every metric. *label_values(metric, label)* | Returns a list of label values for the `label` in the specified metric. *metrics(metric)* | Returns a list of metrics matching the specified `metric` regex. diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index be62bd3b9f0..1fa8f6eb661 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -379,6 +379,24 @@ export class PrometheusDatasource implements DataSourceApi { }); } + getTagKeys(options) { + const url = '/api/v1/labels'; + return this.metadataRequest(url).then(result => { + return _.map(result.data.data, value => { + return { text: value }; + }); + }); + } + + getTagValues(options) { + const url = '/api/v1/label/' + options.key + '/values'; + return this.metadataRequest(url).then(result => { + return _.map(result.data.data, value => { + return { text: value }; + }); + }); + } + testDatasource() { const now = new Date().getTime(); return this.performInstantQuery({ expr: '1+1' }, now / 1000).then(response => { diff --git a/public/app/plugins/datasource/prometheus/metric_find_query.ts b/public/app/plugins/datasource/prometheus/metric_find_query.ts index 680f7a8fb98..5992efbf61c 100644 --- a/public/app/plugins/datasource/prometheus/metric_find_query.ts +++ b/public/app/plugins/datasource/prometheus/metric_find_query.ts @@ -12,10 +12,16 @@ export default class PrometheusMetricFindQuery { } process() { + const labelNamesRegex = /^label_names\(\)\s*$/; const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/; const metricNamesRegex = /^metrics\((.+)\)\s*$/; const queryResultRegex = /^query_result\((.+)\)\s*$/; + const labelNamesQuery = this.query.match(labelNamesRegex); + if (labelNamesQuery) { + return this.labelNamesQuery(); + } + const labelValuesQuery = this.query.match(labelValuesRegex); if (labelValuesQuery) { if (labelValuesQuery[1]) { @@ -39,6 +45,15 @@ export default class PrometheusMetricFindQuery { return this.metricNameAndLabelsQuery(this.query); } + labelNamesQuery() { + const url = '/api/v1/labels'; + return this.datasource.metadataRequest(url).then(result => { + return _.map(result.data.data, value => { + return { text: value }; + }); + }); + } + labelValuesQuery(label, metric) { let url; diff --git a/public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts b/public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts index 1466bd8ac96..5e45f56303a 100644 --- a/public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts +++ b/public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts @@ -42,6 +42,24 @@ describe('PrometheusMetricFindQuery', () => { }); describe('When performing metricFindQuery', () => { + it('label_names() should generate label name search query', async () => { + const query = ctx.setupMetricFindQuery({ + query: 'label_names()', + response: { + data: ['name1', 'name2', 'name3'], + }, + }); + const results = await query.process(); + + expect(results).toHaveLength(3); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1); + expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({ + method: 'GET', + url: 'proxied/api/v1/labels', + silent: true, + }); + }); + it('label_values(resource) should generate label search query', async () => { const query = ctx.setupMetricFindQuery({ query: 'label_values(resource)', From c68da40710fd4c837b43bdd0615bd7b7a2abe498 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 15 Feb 2019 15:05:01 +0100 Subject: [PATCH 010/244] run db tests in all packages --- .circleci/config.yml | 4 ++-- scripts/circle-test-mysql.sh | 17 +++++++++++++++++ scripts/circle-test-postgres.sh | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100755 scripts/circle-test-mysql.sh create mode 100755 scripts/circle-test-postgres.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 8144956773b..69cea87dccd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,7 +35,7 @@ jobs: - run: cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h 127.0.0.1 -P 3306 -u root -prootpass - run: name: mysql integration tests - command: 'GRAFANA_TEST_DB=mysql go test ./pkg/services/sqlstore/... ./pkg/tsdb/mysql/... ' + command: './scripts/circle-test-mysql.sh' postgres-integration-test: docker: @@ -54,7 +54,7 @@ jobs: - run: 'PGPASSWORD=grafanatest psql -p 5432 -h 127.0.0.1 -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql' - run: name: postgres integration tests - command: 'GRAFANA_TEST_DB=postgres go test ./pkg/services/sqlstore/... ./pkg/tsdb/postgres/...' + command: './scripts/circle-test-postgres.sh' codespell: docker: diff --git a/scripts/circle-test-mysql.sh b/scripts/circle-test-mysql.sh new file mode 100755 index 00000000000..4d2fea90c4d --- /dev/null +++ b/scripts/circle-test-mysql.sh @@ -0,0 +1,17 @@ +#!/bin/bash +function exit_if_fail { + command=$@ + echo "Executing '$command'" + eval $command + rc=$? + if [ $rc -ne 0 ]; then + echo "'$command' returned $rc." + exit $rc + fi +} + +export GRAFANA_TEST_DB=mysql + +time for d in $(go list ./pkg/...); do + exit_if_fail go test -tags=integration $d +done \ No newline at end of file diff --git a/scripts/circle-test-postgres.sh b/scripts/circle-test-postgres.sh new file mode 100755 index 00000000000..7ddfe6887d9 --- /dev/null +++ b/scripts/circle-test-postgres.sh @@ -0,0 +1,17 @@ +#!/bin/bash +function exit_if_fail { + command=$@ + echo "Executing '$command'" + eval $command + rc=$? + if [ $rc -ne 0 ]; then + echo "'$command' returned $rc." + exit $rc + fi +} + +export GRAFANA_TEST_DB=postgres + +time for d in $(go list ./pkg/...); do + exit_if_fail go test -tags=integration $d +done \ No newline at end of file From df2f33b5b1077bcbf93caaf915c96224c5d98d4f Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 19 Feb 2019 13:08:29 +0100 Subject: [PATCH 011/244] add new issue templates --- .github/ISSUE_TEMPLATE/1-bug_report.md | 31 +++++++++++++++++++++ .github/ISSUE_TEMPLATE/2-feature_request.md | 28 +++++++++++++++++++ .github/ISSUE_TEMPLATE/3-question.md | 10 +++++++ 3 files changed, 69 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/1-bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/2-feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/3-question.md diff --git a/.github/ISSUE_TEMPLATE/1-bug_report.md b/.github/ISSUE_TEMPLATE/1-bug_report.md new file mode 100644 index 00000000000..4414278becc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1-bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +Read before posting: + +- Questions should be posted to https://community.grafana.com +- Search/filter through opened and closed issues for existing similar bug reports: https://github.com/grafana/grafana/issues?utf8=%E2%9C%93&q=is%3Aissue +- Checkout FAQ: https://community.grafana.com/c/howto/faq +- Checkout How to troubleshoot metric query issues: https://community.grafana.com/t/how-to-troubleshoot-metric-query-issues/50 +- Checkout Using Grafana’s Query Inspector to troubleshoot issues: https://community.grafana.com/t/using-grafanas-query-inspector-to-troubleshoot-issues/2630 + +Please answer and include relevant information below: + +### What Grafana version are you using? +### What datasource are you using? +### What OS are you running grafana on? +### What did you do? +### What was the expected result? +### What happened instead? +### If related to metric query / data viz +### Include raw network request & response +See Using Grafana’s Query Inspector to troubleshoot issues and/or How to troubleshoot metric query issues above. + +### Additional context +Add any other relevant context, like browser, platform and/or screenshots about the bug report here. diff --git a/.github/ISSUE_TEMPLATE/2-feature_request.md b/.github/ISSUE_TEMPLATE/2-feature_request.md new file mode 100644 index 00000000000..b0637ebf46f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2-feature_request.md @@ -0,0 +1,28 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +Read before posting: + +- Questions should be posted to https://community.grafana.com +- Search through opened and closed issues for existing similar feature requests: https://github.com/grafana/grafana/issues?utf8=%E2%9C%93&q=is%3Aissue +- Write a short and descriptive title describing your feature, e.g. Support X in area Y + +Please answer and include below information: + +### Is your feature request related to a problem? Please describe. +A clear and concise description of what the problem is, e.g. I'm always frustrated when [...] + +### Describe possible solutions you'd like +A clear and concise description of what you want to happen. + +### Describe alternatives you've considered +A clear and concise description of any alternative solutions or features you've considered. + +### Additional context +Add any other relevant context, like Grafana version, browser, platform and/or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/3-question.md b/.github/ISSUE_TEMPLATE/3-question.md new file mode 100644 index 00000000000..adaf6098a28 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/3-question.md @@ -0,0 +1,10 @@ +--- +name: Question +about: 'Questions should be posted to https://community.grafana.com/' +title: '' +labels: '' +assignees: '' + +--- + +Please ask questions on our community site, https://community.grafana.com/. Github are mainly for feature requests and bug reports. From 6f9edf4a228d2dd568326e96ded065668686a980 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Tue, 19 Feb 2019 16:43:39 +0100 Subject: [PATCH 012/244] fix: Filter out values not supported by Explore yet #15281 --- public/app/plugins/datasource/prometheus/datasource.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index be62bd3b9f0..abcedd2af5c 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -396,6 +396,10 @@ export class PrometheusDatasource implements DataSourceApi { const expandedQueries = queries.map(query => ({ ...query, expr: this.templateSrv.replace(query.expr, {}, this.interpolateQueryExpr), + + // null out values we don't support in Explore yet + legendFormat: null, + step: null, })); state = { ...state, From dc155dfa2f5e0eadffe8f08c1c6fddff997ce7df Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 19 Feb 2019 16:52:47 +0100 Subject: [PATCH 013/244] docs: howto for recreating our debian repositories. --- scripts/build/update_repo/init-deb-repo.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/build/update_repo/init-deb-repo.sh b/scripts/build/update_repo/init-deb-repo.sh index 2b245dc2d42..ff9057ee876 100755 --- a/scripts/build/update_repo/init-deb-repo.sh +++ b/scripts/build/update_repo/init-deb-repo.sh @@ -10,3 +10,6 @@ mkdir -p /deb-repo/db \ aptly repo create -distribution=stable -component=main grafana aptly repo create -distribution=beta -component=main beta + +aptly publish repo -architectures=amd64,i386,arm64,armhf grafana filesystem:repo:grafana +aptly publish repo -architectures=amd64,i386,arm64,armhf beta filesystem:repo:grafana From 8e90899c029cb2212fe918b74af0615f372ffab8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 5 Feb 2019 01:54:14 +0100 Subject: [PATCH 014/244] docs: adds Azure Monitor docs A copy of the docs from the plugin with some additions and new images. --- .../features/datasources/azuremonitor.md | 271 ++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 docs/sources/features/datasources/azuremonitor.md diff --git a/docs/sources/features/datasources/azuremonitor.md b/docs/sources/features/datasources/azuremonitor.md new file mode 100644 index 00000000000..ee8ca62ef0b --- /dev/null +++ b/docs/sources/features/datasources/azuremonitor.md @@ -0,0 +1,271 @@ ++++ +title = "Using Azure Monitor in Grafana" +description = "Guide for using Azure Monitor in Grafana" +keywords = ["grafana", "microsoft", "azure", "monitor", "application", "insights", "log", "analytics", "guide"] +type = "docs" +aliases = ["/datasources/azuremonitor"] +[menu.docs] +name = "AzureMonitor" +parent = "datasources" +weight = 11 ++++ + +# Using Azure Monitor in Grafana + +> Officially released in Grafana v6.0.0 + +As of Grafana 6.0, the Azure Monitor plugin has been moved into Grafana so it now ships with built-in support for Azure Monitor. + +The Azure Monitor Datasource supports multiple services in the Azure cloud: + +- **Azure Monitor** is the platform service that provides a single source for monitoring Azure resources. [Read more about Querying the Azure Monitor Service]({{< relref "#querying-the-azure-monitor-service" >}}). +- **Application Insights** is an extensible Application Performance Management (APM) service for web developers on multiple platforms and can be used to monitor your live web application - it will automatically detect performance anomalies. [Read more about Querying the Application Insights Service]({{< relref "#querying-the-application-insights-service" >}}). +- **Azure Log Analytics** (or Azure Logs) gives you access to log data collected by Azure Monitor. [Read more about Querying the Azure Log Analytics Service]({{< relref "#querying-the-azure-monitor-service" >}}). +- **Application Insights Analytics** allows you to query [Application Insights data](https://docs.microsoft.com/en-us/azure/azure-monitor/app/analytics) using the same query language used for Azure Log Analytics. [Read more about Querying the Application Insights Analytics Service]({{< relref "#writing-analytics-queries-for-the-application-insights-service" >}}). + +## Adding the data source to Grafana + +The datasource can access metrics from four different services. You can configure access to the services that you use. It is also possible to use the same credentials for multiple services if that is how you have set it up in Azure AD. + +- [Guide to setting up an Azure Active Directory Application for Azure Monitor.](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal) +- [Guide to setting up an Azure Active Directory Application for Azure Log Analytics.](https://dev.loganalytics.io/documentation/Authorization/AAD-Setup) +- [Quickstart Guide for Application Insights.](https://dev.applicationinsights.io/quickstart/) + +1. Accessed from the Grafana main menu, newly installed data sources can be added immediately within the Data Sources section. Next, click the "Add data source" button in the upper right. The data source will be available for selection in the Type select box. + +2. Select Azure Monitor from the Type dropdown: +![Data Source Type](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_1_select_type.png) +3. In the name field, fill in a name for the data source. It can be anything. Some suggestions are Azure Monitor or App Insights. + +4. If you are using Azure Monitor, then you need 4 pieces of information from the Azure portal (see link above for detailed instructions): + - **Tenant Id** (Azure Active Directory -> Properties -> Directory ID) + - **Subscription Id** (Subscriptions -> Choose subscription -> Overview -> Subscription ID) + - **Client Id** (Azure Active Directory -> App Registrations -> Choose your app -> Application ID) + - **Client Secret** ( Azure Active Directory -> App Registrations -> Choose your app -> Keys) + +5. Paste these four items into the fields in the Azure Monitor API Details section: + ![Azure Monitor API Details](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_2_azure_monitor_api_details.png) + +6. If you are also using the Azure Log Analytics service, then you need to specify these two config values (or you can reuse the Client Id and Secret from the previous step). + - Client Id (Azure Active Directory -> App Registrations -> Choose your app -> Application ID) + - Client Secret ( Azure Active Directory -> App Registrations -> Choose your app -> Keys -> Create a key -> Use client secret) + +7. If you are are using Application Insights, then you need two pieces of information from the Azure Portal (see link above for detailed instructions): + - Application ID + - API Key + +8. Paste these two items into the appropriate fields in the Application Insights API Details section: + ![Application Insights API Details](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_3_app_insights_api_details.png) + +9. Test that the configuration details are correct by clicking on the "Save & Test" button: + ![Azure Monitor API Details](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_4_save_and_test.png) + +Alternatively on step 4 if creating a new Azure Active Directory App, use the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/?view=azure-cli-latest): + +```bash +az ad sp create-for-rbac -n "http://localhost:3000" +``` + +## Choose a Service + +In the query editor for a panel, after choosing your Azure Monitor datasource, the first option is to choose a service. There are three options here: Azure Monitor, Application Insights and Azure Log Analytics. The query editor will change depending on which one you pick. Azure Monitor is the default. + +## Querying the Azure Monitor Service + +The Azure Monitor service provides metrics for all the Azure services that you have running. It helps you understand how your applications on Azure are performing and to proactively find issues affecting your applications. + +Examples of metrics that you can get from the service are: + +- Microsoft.Compute/virtualMachines - Percentage CPU +- Microsoft.Network/networkInterfaces - Bytes sent +- Microsoft.Storage/storageAccounts - Used Capacity + +{{< docs-imagebox img="/img/docs/v60/azuremonitor-service-query-editor.png" class="docs-image--no-shadow" caption="Azure Monitor Query Editor" >}} + +### Formatting Legend Keys with Aliases for the Azure Monitor Service + +The default legend formatting for the Azure Monitor API is: + +`resourceName{dimensionValue=dimensionName}.metricName` + +These can be quite long but this formatting can be changed using aliases. In the Legend Format field, the aliases which are defined below can be combined any way you want. + +Azure Monitor Examples: + +- `dimension: {{dimensionvalue}}` +- `{{resourcegroup}} - {{resourcename}}` + +### Alias Patterns for Azure Monitor + +- `{{resourcegroup}}` = replaced with the value of the Resource Group +- `{{namespace}}` = replaced with the value of the Namespace (e.g. Microsoft.Compute/virtualMachines) +- `{{resourcename}}` = replaced with the value of the Resource Name +- `{{metric}}` = replaced with metric name (e.g. Percentage CPU) +- `{{dimensionname}}` = replaced with dimension key/label (e.g. blobtype) +- `{{dimensionvalue}}` = replaced with dimension value (e.g. BlockBlob) + +### Templating with Variables for the Azure Monitor Service + +Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data being displayed in your dashboard. + +Note that the Azure Monitor service does not support multiple values yet. If you want to visualize multiple time series (for example, metrics for server1 and server2) then you have to add multiple queries to able to view them on the same graph or in the same table. + +The Azure Monitor Datasource Plugin provides the following queries you can specify in the `Query` field in the Variable edit view. They allow you to fill a variable's options list. + +| Name | Description | +| -------------------------------------------------------- | -------------------------------------------------------------- | +| *ResourceGroups()* | Returns a list of resource groups. | +| *Namespaces(aResourceGroup)* | Returns a list of namespaces for the specified resource group. | +| *ResourceNames(aResourceGroup, aNamespace)* | Returns a list of resource names. | +| *MetricNames(aResourceGroup, aNamespace, aResourceName)* | Returns a list of metric names. | + +Examples: + +- Resource Groups query: `ResourceGroups()` +- Passing in metric name variable: `Namespaces(cosmo)` +- Chaining template variables: `ResourceNames($rg, $ns)` +- Do not quote parameters: `MetricNames(hg, Microsoft.Network/publicIPAddresses, grafanaIP)` + +{{< docs-imagebox img="/img/docs/v60/azuremonitor-service-variables.png" class="docs-image--no-shadow" caption="Nested Azure Monitor Template Variables" >}} + +Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different +types of template variables. + +### Azure Monitor Metrics Whitelist + +Not all metrics returned by the Azure Monitor API have values. The Grafana datasource has a whitelist to only return metric names if it is possible they might have values. This whitelist is updated regularly as new services and metrics are added to the Azure cloud. You can find the current whitelist [here](https://github.com/grafana/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/supported_namespaces.ts). + +### Azure Monitor Alerting + +Grafana alerting is supported for the Azure Monitor service. This is not Azure Alerts support. Read more about how alerting in Grafana works [here]({{< relref "alerting/rules.md" >}}). + +{{< docs-imagebox img="/img/docs/v60/azuremonitor-alerting.png" class="docs-image--no-shadow" caption="Azure Monitor Alerting" >}} + +## Querying the Application Insights Service + +{{< docs-imagebox img="/img/docs/v60/appinsights-service-query-editor.png" class="docs-image--no-shadow" caption="Application Insights Query Editor" >}} + +### Formatting Legend Keys with Aliases for the Application Insights Service + +The default legend formatting is: + +`metric/name{group/by="groupbyvalue"}` + +In the Legend Format field, the aliases which are defined below can be combined any way you want. + +Application Insights Examples: + +- `server: {{groupbyvalue}}` +- `city: {{groupbyvalue}}` +- `{{groupbyname}}: {{groupbyvalue}}` + +### Alias Patterns for Application Insights + +- `{{groupbyvalue}}` = replaced with the value of the group by +- `{{groupbyname}}` = replaced with the name/label of the group by +- `{{metric}}` = replaced with metric name (e.g. requests/count) + +### Filter Expressions for Application Insights + +The filter field takes an OData filter expression. + +Examples: + +- `client/city eq 'Boydton'` +- `client/city ne 'Boydton'` +- `client/city ne 'Boydton' and client/city ne 'Dublin'` +- `client/city eq 'Boydton' or client/city eq 'Dublin'` + +### Templating with Variables for Application Insights + +Use the one of the following queries in the `Query` field in the Variable edit view. + +Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different +types of template variables. + +| Name | Description | +| ---------------------------------- | ---------------------------------------------------------- | +| *AppInsightsMetricNames()* | Returns a list of metric names. | +| *AppInsightsGroupBys(aMetricName)* | Returns a list of group bys for the specified metric name. | + +Examples: + +- Metric Names query: `AppInsightsMetricNames()` +- Passing in metric name variable: `AppInsightsGroupBys(requests/count)` +- Chaining template variables: `AppInsightsGroupBys($metricnames)` + +{{< docs-imagebox img="/img/docs/v60/appinsights-service-variables.png" class="docs-image--no-shadow" caption="Nested Application Insights Template Variables" >}} + +### Application Insights Alerting + +Not implemented yet. + +## Querying the Azure Log Analytics Service + +Queries are written in the new [Azure Log Analytics (or KustoDB) Query Language](https://docs.loganalytics.io/index). A Log Analytics Query can be formatted as Time Series data or as Table data. + +Time Series queries are for the Graph Panel (and other panels like the Single Stat panel) and must contain a datetime column, a metric name column and a value column. Here is an example query that returns the aggregated count grouped by the Category column and grouped by hour: + +``` +AzureActivity +| where $__timeFilter(TimeGenerated) +| summarize count() by Category, bin(TimeGenerated, 1h) +| order by TimeGenerated asc +``` + +Table queries are mainly used in the Table panel and row a list of columns and rows. This example query returns rows with the 6 specified columns: + +``` +AzureActivity +| where $__timeFilter() +| project TimeGenerated, ResourceGroup, Category, OperationName, ActivityStatus, Caller +| order by TimeGenerated desc +``` + +{{< docs-imagebox img="/img/docs/v60/azureloganalytics-service-query-editor.png" class="docs-image--no-shadow" caption="Azure Log Analytics Query Editor" >}} + +### Azure Log Analytics Macros + +To make writing queries easier there are several Grafana macros that can be used in the where clause of a query: + +- `$__timeFilter()` - Expands to + `TimeGenerated ≥ datetime(2018-06-05T18:09:58.907Z) and` + `TimeGenerated ≤ datetime(2018-06-05T20:09:58.907Z)` where the from and to datetimes are from the Grafana time picker. + +- `$__timeFilter(datetimeColumn)` - Expands to + `datetimeColumn ≥ datetime(2018-06-05T18:09:58.907Z) and` + `datetimeColumn ≤ datetime(2018-06-05T20:09:58.907Z)` where the from and to datetimes are from the Grafana time picker. + +- `$__escapeMulti($myVar)` - is to be used with multi-value template variables that contains illegal characters. If $myVar has the value `'\\grafana-vm\Network(eth0)\Total','\\hello!'`, it expands to: `@'\\grafana-vm\Network(eth0)\Total', @'\\hello!'`. If using single value variables there no need for this macro, simply escape the variable inline instead - `@'\$myVar'` + +- `$__contains(colName, $myVar)` - is to be used with multi-value template variables. If $myVar has the value `'value1','value2'`, it expands to: `colName in ('value1','value2')`. + + If using the `All` option, then check the `Include All Option` checkbox and in the `Custom all value` field type in the following value: `all`. If $myVar has value `all` then the macro will instead expand to `1 == 1`. For template variables with a lot of options, this will increase the query performance by not building a large where..in clause. + +### Azure Log Analytics Builtin Variables + +There are also some Grafana variables that can be used in Azure Log Analytics queries: + +- `$__from` - Returns the From datetime from the Grafana picker. Example: `datetime(2018-06-05T18:09:58.907Z)`. +- `$__to` - Returns the From datetime from the Grafana picker. Example: `datetime(2018-06-05T20:09:58.907Z)`. +- `$__interval` - Grafana calculates the minimum time grain that can be used to group by time in queries. More details on how it works [here](http://docs.grafana.org/reference/templating/#the-interval-variable). It returns a time grain like `5m` or `1h` that can be used in the bin function. E.g. `summarize count() by bin(TimeGenerated, $__interval)` + +### Azure Log Analytics Alerting + +Not implemented yet. + +### Writing Analytics Queries For the Application Insights Service + +If you change the service type to "Application Insights", the menu icon to the right adds another option, "Toggle Edit Mode". Once clicked, the query edit mode changes to give you a full text area in which to write log analytics queries. (This is identical to how the InfluxDB datasource lets you write raw queries.) + +Once a query is written, the column names are automatically parsed out of the response data. You can then select them in the "X-axis", "Y-axis", and "Split On" dropdown menus, or just type them out. + +There are some important caveats to remember: + +- You'll want to order your y-axis in the query, eg. `order by timestamp asc`. The graph may come out looking bizarre otherwise. It's better to have Microsoft sort it on their side where it's faster, than to implement this in the plugin. + +- If you copy a log analytics query, typically they'll end with a render instruction, like `render barchart`. This is unnecessary, but harmless. + +- Currently, four default dashboard variables are supported: `$__timeFilter()`, `$__from`, `$__to`, and `$__interval`. If you're searching in timestamped data, replace the beginning of your where clause to `where $__timeFilter()`. Dashboard changes by time region are handled as you'd expect, as long as you leave the name of the `timestamp` column alone. Likewise, `$__interval` will automatically change based on the dashboard's time region _and_ the width of the chart being displayed. Use it in bins, so `bin(timestamp,$__interval)` changes into something like `bin(timestamp,1s)`. Use `$__from` and `$__to` if you just want the formatted dates to be inserted. + +- Templated dashboard variables are not yet supported! They will come in a future version. From bd0f55cbb875b88a6986a70ee08a21fb44a757c6 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 21 Feb 2019 13:34:10 +0100 Subject: [PATCH 015/244] panel: defensive coding that fixes #15563 If a plugin incorrectly uses an attribute in the query-editor-row directive, it should not throw an exception. --- public/app/features/panel/query_editor_row.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/panel/query_editor_row.ts b/public/app/features/panel/query_editor_row.ts index fa25ce832be..3c44fab4db3 100644 --- a/public/app/features/panel/query_editor_row.ts +++ b/public/app/features/panel/query_editor_row.ts @@ -14,7 +14,7 @@ export class QueryRowCtrl { this.target = this.queryCtrl.target; this.panel = this.panelCtrl.panel; - if (this.hasTextEditMode) { + if (this.hasTextEditMode && this.queryCtrl.toggleEditorMode) { // expose this function to react parent component this.panelCtrl.toggleEditorMode = this.queryCtrl.toggleEditorMode.bind(this.queryCtrl); } From 7be18e1a084007d5e9b17889cd6ca47a79e2cedf Mon Sep 17 00:00:00 2001 From: ijin08 Date: Thu, 21 Feb 2019 15:05:17 +0100 Subject: [PATCH 016/244] changed some more color variables to use variables --- .../src/themes/_variables.dark.scss.tmpl.ts | 46 +++++++++---------- .../src/themes/_variables.light.scss.tmpl.ts | 46 +++++++++---------- packages/grafana-ui/src/themes/dark.ts | 4 ++ packages/grafana-ui/src/themes/light.ts | 8 +++- packages/grafana-ui/src/types/theme.ts | 4 ++ public/sass/_variables.dark.generated.scss | 38 +++++++-------- public/sass/_variables.light.generated.scss | 44 +++++++++--------- 7 files changed, 101 insertions(+), 89 deletions(-) diff --git a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts index 3a2d6db458c..d6df6f51374 100644 --- a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts @@ -54,34 +54,34 @@ $orange: ${theme.colors.orange}; $purple: ${theme.colors.purple}; $variable: ${theme.colors.variable}; -$brand-primary: $orange; -$brand-success: $green-base; -$brand-warning: $brand-primary; -$brand-danger: $red-base; +$brand-primary: ${theme.colors.brandPrimary}; +$brand-success: ${theme.colors.brandSuccess}; +$brand-warning: ${theme.colors.brandWarning}; +$brand-danger: ${theme.colors.brandDanger}; -$query-red: $red-base; -$query-green: #74e680; -$query-purple: #fe85fc; -$query-keyword: #66d9ef; -$query-orange: $orange; +$query-red: ${theme.colors.queryRed}; +$query-green: ${theme.colors.queryGreen}; +$query-purple: ${theme.colors.queryPurple}; +$query-orange: ${theme.colors.orange}; +$query-keyword: ${theme.colors.queryKeyword}; // Status colors // ------------------------- -$online: $green-base; -$warn: #f79520; -$critical: $red-base; +$online: ${theme.colors.online}; +$warn: ${theme.colors.warn}; +$critical: ${theme.colors.critical}; // Scaffolding // ------------------------- $body-bg: ${theme.colors.bodyBg}; $page-bg: ${theme.colors.pageBg}; -$body-color: $gray-4; -$text-color: $gray-4; -$text-color-strong: $white; -$text-color-weak: $gray-2; -$text-color-faint: $dark-10; -$text-color-emphasis: $gray-5; +$body-color: ${theme.colors.bodyColor}; +$text-color: ${theme.colors.textColor}; +$text-color-strong: ${theme.colors.textColorStrong}; +$text-color-weak: ${theme.colors.textColorWeak}; +$text-color-faint: ${theme.colors.textColorFaint}; +$text-color-emphasis: ${theme.colors.textColorEmphasis}; $text-shadow-faint: 1px 1px 4px rgb(45, 45, 45); $textShadow: none; @@ -99,14 +99,14 @@ $edit-gradient: linear-gradient(180deg, $dark-2 50%, $input-black); // Links // ------------------------- -$link-color: darken($white, 11%); -$link-color-disabled: darken($link-color, 30%); -$link-hover-color: $white; -$external-link-color: $blue-light; +$link-color: ${theme.colors.linkColor}; +$link-color-disabled: ${theme.colors.linkColorDisabled}; +$link-hover-color: ${theme.colors.linkColorHover}; +$external-link-color: ${theme.colors.linkColorExternal}; // Typography // ------------------------- -$headings-color: darken($white, 11%); +$headings-color: ${theme.colors.headingColor}; $abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; diff --git a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts index 1b017b7eb0d..eb48ab5cc02 100644 --- a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts @@ -46,34 +46,34 @@ $orange: ${theme.colors.orange}; $purple: ${theme.colors.purple}; $variable: ${theme.colors.variable}; -$brand-primary: $orange; -$brand-success: $green-base; -$brand-warning: $orange; -$brand-danger: $red-base; +$brand-primary: ${theme.colors.brandPrimary}; +$brand-success: ${theme.colors.brandSuccess}; +$brand-warning: ${theme.colors.brandWarning}; +$brand-danger: ${theme.colors.brandDanger}; -$query-red: $red-base; -$query-green: $green-base; -$query-purple: $purple; -$query-orange: $orange; -$query-keyword: $blue-base; +$query-red: ${theme.colors.queryRed}; +$query-green: ${theme.colors.queryGreen}; +$query-purple: ${theme.colors.queryPurple}; +$query-orange: ${theme.colors.orange}; +$query-keyword: ${theme.colors.queryKeyword}; // Status colors // ------------------------- -$online: $green-shade; -$warn: #f79520; -$critical: $red-shade; +$online: ${theme.colors.online}; +$warn: ${theme.colors.warn}; +$critical: ${theme.colors.critical}; // Scaffolding // ------------------------- $body-bg: ${theme.colors.bodyBg}; $page-bg: ${theme.colors.pageBg}; -$body-color: $gray-1; -$text-color: $gray-1; -$text-color-strong: $dark-1; -$text-color-weak: $gray-2; -$text-color-faint: $gray-4; -$text-color-emphasis: $dark-2; +$body-color: ${theme.colors.bodyColor}; +$text-color: ${theme.colors.textColor}; +$text-color-strong: ${theme.colors.textColorStrong}; +$text-color-weak: ${theme.colors.textColorWeak}; +$text-color-faint: ${theme.colors.textColorFaint}; +$text-color-emphasis: ${theme.colors.textColorEmphasis}; $text-shadow-faint: none; @@ -85,14 +85,14 @@ $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); // Links // ------------------------- -$link-color: $gray-1; -$link-color-disabled: lighten($link-color, 30%); -$link-hover-color: darken($link-color, 20%); -$external-link-color: $blue-shade; +$link-color: ${theme.colors.linkColor}; +$link-color-disabled: ${theme.colors.linkColorDisabled}; +$link-hover-color: ${theme.colors.linkColorHover}; +$external-link-color: ${theme.colors.linkColorExternal}; // Typography // ------------------------- -$headings-color: $text-color; +$headings-color: ${theme.colors.headingColor}; $abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; diff --git a/packages/grafana-ui/src/themes/dark.ts b/packages/grafana-ui/src/themes/dark.ts index 7c3e81a7d35..3e8057a4acd 100644 --- a/packages/grafana-ui/src/themes/dark.ts +++ b/packages/grafana-ui/src/themes/dark.ts @@ -46,6 +46,10 @@ const darkTheme: GrafanaTheme = { colors: { ...basicColors, inputBlack: '#09090b', + brandPrimary: basicColors.orange, + brandSuccess: basicColors.greenBase, + brandWarning: basicColors.orange, + brandDanger: basicColors.redBase, queryRed: basicColors.redBase, queryGreen: '#74e680', queryPurple: '#fe85fc', diff --git a/packages/grafana-ui/src/themes/light.ts b/packages/grafana-ui/src/themes/light.ts index 7e8f6300d84..2461505d507 100644 --- a/packages/grafana-ui/src/themes/light.ts +++ b/packages/grafana-ui/src/themes/light.ts @@ -47,10 +47,14 @@ const lightTheme: GrafanaTheme = { ...basicColors, variable: basicColors.blue, inputBlack: '#09090b', - queryRed: basicColors.red, + brandPrimary: basicColors.orange, + brandSuccess: basicColors.greenBase, + brandWarning: basicColors.orange, + brandDanger: basicColors.redBase, + queryRed: basicColors.redBase, queryGreen: basicColors.greenBase, queryPurple: basicColors.purple, - queryKeyword: basicColors.blue, + queryKeyword: basicColors.blueBase, queryOrange: basicColors.orange, online: basicColors.greenShade, warn: '#f79520', diff --git a/packages/grafana-ui/src/types/theme.ts b/packages/grafana-ui/src/types/theme.ts index 01886afe3dc..f7adbf80247 100644 --- a/packages/grafana-ui/src/types/theme.ts +++ b/packages/grafana-ui/src/types/theme.ts @@ -113,6 +113,10 @@ export interface GrafanaTheme extends GrafanaThemeCommons { queryPurple: string; queryKeyword: string; queryOrange: string; + brandPrimary: string; + brandSuccess: string; + brandWarning: string; + brandDanger: string; // Status colors online: string; diff --git a/public/sass/_variables.dark.generated.scss b/public/sass/_variables.dark.generated.scss index 3f1e2e4f314..a86134cf4bd 100644 --- a/public/sass/_variables.dark.generated.scss +++ b/public/sass/_variables.dark.generated.scss @@ -57,34 +57,34 @@ $orange: #eb7b18; $purple: #9933cc; $variable: #32d1df; -$brand-primary: $orange; -$brand-success: $green-base; -$brand-warning: $brand-primary; -$brand-danger: $red-base; +$brand-primary: #eb7b18; +$brand-success: #299c46; +$brand-warning: #eb7b18; +$brand-danger: #e02f44; -$query-red: $red-base; +$query-red: #e02f44; $query-green: #74e680; $query-purple: #fe85fc; +$query-orange: #eb7b18; $query-keyword: #66d9ef; -$query-orange: $orange; // Status colors // ------------------------- -$online: $green-base; +$online: #299c46; $warn: #f79520; -$critical: $red-base; +$critical: #e02f44; // Scaffolding // ------------------------- $body-bg: #161719; $page-bg: #161719; -$body-color: $gray-4; -$text-color: $gray-4; -$text-color-strong: $white; -$text-color-weak: $gray-2; -$text-color-faint: $dark-10; -$text-color-emphasis: $gray-5; +$body-color: #d8d9da; +$text-color: #d8d9da; +$text-color-strong: #ffffff; +$text-color-weak: #8e8e8e; +$text-color-faint: #222426; +$text-color-emphasis: #ececec; $text-shadow-faint: 1px 1px 4px rgb(45, 45, 45); $textShadow: none; @@ -102,14 +102,14 @@ $edit-gradient: linear-gradient(180deg, $dark-2 50%, $input-black); // Links // ------------------------- -$link-color: darken($white, 11%); -$link-color-disabled: darken($link-color, 30%); -$link-hover-color: $white; -$external-link-color: $blue-light; +$link-color: #e3e3e3; +$link-color-disabled: #e3e3e3; +$link-hover-color: #ffffff; +$external-link-color: #33b5e5; // Typography // ------------------------- -$headings-color: darken($white, 11%); +$headings-color: #e3e3e3; $abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; diff --git a/public/sass/_variables.light.generated.scss b/public/sass/_variables.light.generated.scss index 4aea0a4f993..4d1dd96bccf 100644 --- a/public/sass/_variables.light.generated.scss +++ b/public/sass/_variables.light.generated.scss @@ -49,34 +49,34 @@ $orange: #ff7941; $purple: #9954bb; $variable: #0083b3; -$brand-primary: $orange; -$brand-success: $green-base; -$brand-warning: $orange; -$brand-danger: $red-base; +$brand-primary: #ff7941; +$brand-success: #3eb15b; +$brand-warning: #ff7941; +$brand-danger: #e02f44; -$query-red: $red-base; -$query-green: $green-base; -$query-purple: $purple; -$query-orange: $orange; -$query-keyword: $blue-base; +$query-red: #e02f44; +$query-green: #3eb15b; +$query-purple: #9954bb; +$query-orange: #ff7941; +$query-keyword: #3274d9; // Status colors // ------------------------- -$online: $green-shade; +$online: #369b4f; $warn: #f79520; -$critical: $red-shade; +$critical: #c4162a; // Scaffolding // ------------------------- $body-bg: #f7f8fa; $page-bg: #f7f8fa; -$body-color: $gray-1; -$text-color: $gray-1; -$text-color-strong: $dark-1; -$text-color-weak: $gray-2; -$text-color-faint: $gray-4; -$text-color-emphasis: $dark-2; +$body-color: #52545c; +$text-color: #52545c; +$text-color-strong: #41444b; +$text-color-weak: #767980; +$text-color-faint: #35373f; +$text-color-emphasis: #dde4ed; $text-shadow-faint: none; @@ -88,14 +88,14 @@ $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); // Links // ------------------------- -$link-color: $gray-1; -$link-color-disabled: lighten($link-color, 30%); -$link-hover-color: darken($link-color, 20%); -$external-link-color: $blue-shade; +$link-color: #52545c; +$link-color-disabled: #9ea0a9; +$link-hover-color: #222326; +$external-link-color: #5794f2; // Typography // ------------------------- -$headings-color: $text-color; +$headings-color: #52545c; $abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; From 529c1ea53d470144aa3c52de9682a256535d5c41 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 10:58:28 +0100 Subject: [PATCH 017/244] Implemented scripts for building and releasing grafana/ui --- .gitignore | 6 + package.json | 10 +- packages/grafana-ui/CHANGELOG.md | 4 + packages/grafana-ui/index.js | 7 + packages/grafana-ui/package.json | 15 +- packages/grafana-ui/rollup.config.ts | 54 ++++ packages/grafana-ui/tsconfig.build.json | 12 + packages/grafana-ui/tsconfig.json | 6 +- scripts/cli/index.d.ts | 1 + scripts/cli/index.ts | 35 +- scripts/cli/{start.ts => tasks/core.start.ts} | 11 +- scripts/cli/tasks/grafanaui.build.ts | 103 ++++++ scripts/cli/tasks/grafanaui.release.ts | 133 ++++++++ scripts/cli/utils/cwd.ts | 14 + scripts/cli/utils/execTask.ts | 6 + scripts/cli/utils/startSpinner.ts | 7 + yarn.lock | 304 ++++++++++++++++-- 17 files changed, 684 insertions(+), 44 deletions(-) create mode 100644 packages/grafana-ui/CHANGELOG.md create mode 100644 packages/grafana-ui/index.js create mode 100644 packages/grafana-ui/rollup.config.ts create mode 100644 packages/grafana-ui/tsconfig.build.json create mode 100644 scripts/cli/index.d.ts rename scripts/cli/{start.ts => tasks/core.start.ts} (78%) create mode 100644 scripts/cli/tasks/grafanaui.build.ts create mode 100644 scripts/cli/tasks/grafanaui.release.ts create mode 100644 scripts/cli/utils/cwd.ts create mode 100644 scripts/cli/utils/execTask.ts create mode 100644 scripts/cli/utils/startSpinner.ts diff --git a/.gitignore b/.gitignore index 2945746832a..a9547973fc8 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,9 @@ debug.test /scripts/build/release_publisher/release_publisher *.patch + + +# Ignoring frontend packages specifics +/packages/**/dist +/packages/**/compiled +/packages/**/.rpt2_cache diff --git a/package.json b/package.json index 71339bee2be..9e0b88804fd 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@types/commander": "^2.12.2", "@types/d3": "^4.10.1", "@types/enzyme": "^3.1.13", + "@types/inquirer": "^0.0.43", "@types/jest": "^23.3.2", "@types/jquery": "^1.10.35", "@types/node": "^8.0.31", @@ -47,6 +48,7 @@ "enzyme-to-json": "^3.3.4", "es6-promise": "^3.0.2", "es6-shim": "^0.35.3", + "execa": "^1.0.0", "expect.js": "~0.2.0", "expose-loader": "^0.7.3", "file-loader": "^1.1.11", @@ -70,6 +72,7 @@ "html-webpack-harddisk-plugin": "^0.2.0", "html-webpack-plugin": "^3.2.0", "husky": "^1.3.1", + "inquirer": "^6.2.2", "jest": "^23.6.0", "jest-date-mock": "^1.0.6", "lint-staged": "^8.1.3", @@ -83,6 +86,7 @@ "node-sass": "^4.11.0", "npm": "^5.4.2", "optimize-css-assets-webpack-plugin": "^4.0.2", + "ora": "^3.1.0", "phantomjs-prebuilt": "^2.1.15", "postcss-browser-reporter": "^0.5.0", "postcss-loader": "^2.0.6", @@ -92,8 +96,10 @@ "react-test-renderer": "^16.5.0", "redux-mock-store": "^1.5.3", "regexp-replace-loader": "^1.0.1", + "rimraf": "^2.6.3", "sass-lint": "^1.10.2", "sass-loader": "^7.0.1", + "semver": "^5.6.0", "sinon": "1.17.6", "style-loader": "^0.21.0", "systemjs": "0.20.19", @@ -129,7 +135,9 @@ "api-tests": "jest --notify --watch --config=tests/api/jest.js", "storybook": "cd packages/grafana-ui && yarn storybook", "themes:generate": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/generateSassVariableFiles.ts", - "prettier:check": "prettier --list-different \"**/*.{ts,tsx,scss}\"" + "prettier:check": "prettier --list-different \"**/*.{ts,tsx,scss}\"", + "gui:build": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --build", + "gui:release": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --release" }, "husky": { "hooks": { diff --git a/packages/grafana-ui/CHANGELOG.md b/packages/grafana-ui/CHANGELOG.md new file mode 100644 index 00000000000..472c2df8ad8 --- /dev/null +++ b/packages/grafana-ui/CHANGELOG.md @@ -0,0 +1,4 @@ +# 1.0.0-alpha.0 (2019-02-21) + +First public release + diff --git a/packages/grafana-ui/index.js b/packages/grafana-ui/index.js new file mode 100644 index 00000000000..d1a4363350e --- /dev/null +++ b/packages/grafana-ui/index.js @@ -0,0 +1,7 @@ +'use strict' + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./index.production.js'); +} else { + module.exports = require('./index.development.js'); +} diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index a0c76f711af..e1c78b1e2ab 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -1,12 +1,14 @@ { - "name": "@grafana/ui", - "version": "1.0.0", + "name": "testing-ignore-this", + "version": "1.0.0-beta.0", "description": "", "main": "src/index.ts", "scripts": { "tslint": "tslint -c tslint.json --project tsconfig.json", "typecheck": "tsc --noEmit", - "storybook": "start-storybook -p 9001 -c .storybook -s ../../public" + "storybook": "start-storybook -p 9001 -c .storybook -s ../../public", + "clean": "rimraf ./dist ./compiled", + "build": "rollup -c rollup.config.ts" }, "author": "Grafana Labs", "license": "Apache-2.0", @@ -53,6 +55,13 @@ "react-docgen-typescript-loader": "^3.0.0", "react-docgen-typescript-webpack-plugin": "^1.1.0", "react-test-renderer": "^16.7.0", + "rollup": "^1.1.2", + "rollup-plugin-commonjs": "^9.2.0", + "rollup-plugin-node-resolve": "^4.0.0", + "rollup-plugin-sourcemaps": "^0.4.2", + "rollup-plugin-terser": "^4.0.4", + "rollup-plugin-typescript2": "^0.19.2", + "rollup-plugin-visualizer": "^0.9.2", "typescript": "^3.2.2" }, "resolutions": { diff --git a/packages/grafana-ui/rollup.config.ts b/packages/grafana-ui/rollup.config.ts new file mode 100644 index 00000000000..28f949d6e4e --- /dev/null +++ b/packages/grafana-ui/rollup.config.ts @@ -0,0 +1,54 @@ +import resolve from 'rollup-plugin-node-resolve'; +import commonjs from 'rollup-plugin-commonjs'; +import sourceMaps from 'rollup-plugin-sourcemaps'; +import { terser } from 'rollup-plugin-terser'; + +const pkg = require('./package.json'); + +const libraryName = pkg.name; + +const buildCjsPackage = ({ env }) => { + return { + input: `compiled/index.js`, + output: [ + { + file: `dist/index.${env}.js`, + name: libraryName, + format: 'cjs', + sourcemap: true, + exports: 'named', + globals: { + react: 'React', + 'prop-types': 'PropTypes', + }, + }, + ], + external: ['react', 'react-dom'], + plugins: [ + commonjs({ + include: /node_modules/, + namedExports: { + '../../node_modules/lodash/lodash.js': [ + 'flatten', + 'find', + 'upperFirst', + 'debounce', + 'isNil', + 'isNumber', + 'flattenDeep', + 'map', + 'chunk', + 'sortBy', + 'uniqueId', + 'zip', + ], + '../../node_modules/react-color/lib/components/common': ['Saturation', 'Hue', 'Alpha'], + }, + }), + resolve(), + sourceMaps(), + env === 'production' && terser(), + ], + }; +}; +export default [buildCjsPackage({ env: 'development' }), buildCjsPackage({ env: 'production' })]; diff --git a/packages/grafana-ui/tsconfig.build.json b/packages/grafana-ui/tsconfig.build.json new file mode 100644 index 00000000000..b63f4dc67b1 --- /dev/null +++ b/packages/grafana-ui/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "dist", + "node_modules", + "src/utils/storybook", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.story.tsx", + "**/*.tmpl.ts" + ] +} diff --git a/packages/grafana-ui/tsconfig.json b/packages/grafana-ui/tsconfig.json index 1dfe6e0b44c..0089f89d194 100644 --- a/packages/grafana-ui/tsconfig.json +++ b/packages/grafana-ui/tsconfig.json @@ -5,13 +5,15 @@ "compilerOptions": { "rootDirs": [".", "stories"], "module": "esnext", - "outDir": "dist", + "outDir": "compiled", "declaration": true, + "declarationDir": "dist", "strict": true, "alwaysStrict": true, "noImplicitAny": true, "strictNullChecks": true, "typeRoots": ["./node_modules/@types", "types"], - "skipLibCheck": true // Temp workaround for Duplicate identifier tsc errors + "skipLibCheck": true, // Temp workaround for Duplicate identifier tsc errors, + "removeComments": false } } diff --git a/scripts/cli/index.d.ts b/scripts/cli/index.d.ts new file mode 100644 index 00000000000..5f7aeecc441 --- /dev/null +++ b/scripts/cli/index.d.ts @@ -0,0 +1 @@ +export type Task = (options: T) => Promise; diff --git a/scripts/cli/index.ts b/scripts/cli/index.ts index 51919f13173..f980944e2cd 100644 --- a/scripts/cli/index.ts +++ b/scripts/cli/index.ts @@ -1,22 +1,33 @@ import program from 'commander'; -import { startTask } from './start'; import chalk from 'chalk'; +import { execTask } from './utils/execTask'; +export type Task = (options: T) => Promise; + +// TODO: Refactor to commander commands +// This will enable us to have command scoped options and limit the ifs below program .option('-h, --hot', 'Runs front-end with hot reload enabled') .option('-t, --theme', 'Watches for theme changes and regenerates variables.scss files') .option('-d, --depreciate ', 'Inform about npm script deprecation', v => v.split(',')) + .option('-b, --build', 'Created @grafana/ui build') + .option('-r, --release', 'Releases @grafana/ui to npm') .parse(process.argv); -if (program.depreciate && program.depreciate.length === 2) { - console.log( - chalk.yellow.bold( - `[NPM script depreciation] ${program.depreciate[0]} is deprecated! Use ${program.depreciate[1]} instead!` - ) - ); +if (program.build) { + execTask('grafanaui.build'); +} else if (program.release) { + execTask('grafanaui.release'); +} else { + if (program.depreciate && program.depreciate.length === 2) { + console.log( + chalk.yellow.bold( + `[NPM script depreciation] ${program.depreciate[0]} is deprecated! Use ${program.depreciate[1]} instead!` + ) + ); + } + execTask('core.start', { + watchThemes: !!program.theme, + hot: !!program.hot, + }); } - -startTask({ - watchThemes: !!program.theme, - hot: !!program.hot, -}); diff --git a/scripts/cli/start.ts b/scripts/cli/tasks/core.start.ts similarity index 78% rename from scripts/cli/start.ts rename to scripts/cli/tasks/core.start.ts index cb68ee7f1dd..4e546c71b8c 100644 --- a/scripts/cli/start.ts +++ b/scripts/cli/tasks/core.start.ts @@ -1,7 +1,14 @@ import concurrently from 'concurrently'; +import { Task } from '..'; -export const startTask = async ({ watchThemes, hot }: { watchThemes: boolean; hot: boolean }) => { +interface StartTaskOptions { + watchThemes: boolean; + hot: boolean; +} + +const startTask: Task = async ({ watchThemes, hot }) => { const jobs = []; + if (watchThemes) { jobs.push({ command: 'nodemon -e ts -w ./packages/grafana-ui/src/themes -x yarn run themes:generate', @@ -30,3 +37,5 @@ export const startTask = async ({ watchThemes, hot }: { watchThemes: boolean; ho process.exit(1); } }; + +export default startTask; diff --git a/scripts/cli/tasks/grafanaui.build.ts b/scripts/cli/tasks/grafanaui.build.ts new file mode 100644 index 00000000000..f892c13e115 --- /dev/null +++ b/scripts/cli/tasks/grafanaui.build.ts @@ -0,0 +1,103 @@ +import execa from 'execa'; +import fs from 'fs'; +import { Task } from '..'; +import { changeCwdToGrafanaUi, restoreCwd } from '../utils/cwd'; +import chalk from 'chalk'; +import { startSpinner } from '../utils/startSpinner'; + +let distDir, cwd; + +const clean = async () => { + const spinner = startSpinner('Cleaning'); + try { + await execa('npm', ['run', 'clean']); + spinner.succeed(); + } catch (e) { + spinner.fail(); + throw e; + } +}; + +const compile = async () => { + const spinner = startSpinner('Compiling sources'); + try { + await execa('tsc', ['-p', './tsconfig.build.json']); + spinner.succeed(); + } catch (e) { + console.log(e); + spinner.fail(); + } +}; + +const rollup = async () => { + const spinner = startSpinner('Bundling'); + + try { + await execa('npm', ['run', 'build']); + spinner.succeed(); + } catch (e) { + spinner.fail(); + } +}; + +export const savePackage = async (path, pkg) => { + const spinner = startSpinner('Updating package.json'); + + return new Promise((resolve, reject) => { + fs.writeFile(path, JSON.stringify(pkg, null, 2), err => { + if (err) { + spinner.fail(); + console.error(err); + reject(err); + return; + } + spinner.succeed(); + resolve(); + }); + }); +}; + +const preparePackage = async pkg => { + pkg.main = 'index.js'; + pkg.types = 'index.d.ts'; + await savePackage(`${cwd}/dist/package.json`, pkg); +}; + +const moveFiles = async () => { + const files = ['README.md', 'CHANGELOG.md', 'index.js']; + const spinner = startSpinner(`Moving ${files.join(', ')} files`); + + const promises = files.map(file => { + return fs.copyFile(`${cwd}/${file}`, `${distDir}/${file}`, err => { + if (err) { + console.error(err); + return; + } + }); + }); + + try { + await Promise.all(promises); + spinner.succeed(); + } catch (e) { + spinner.fail(); + } +}; + +const buildTask: Task = async () => { + cwd = changeCwdToGrafanaUi(); + distDir = `${cwd}/dist`; + const pkg = require(`${cwd}/package.json`); + + console.log(chalk.yellow(`Building ${pkg.name} @ ${pkg.version}`)); + + await clean(); + await compile(); + await rollup(); + await preparePackage(pkg); + await moveFiles(); + + restoreCwd(); +}; + +export default buildTask; diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts new file mode 100644 index 00000000000..b8569858e2b --- /dev/null +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -0,0 +1,133 @@ +import execa from 'execa'; +import { Task } from '..'; +import { execTask } from '../utils/execTask'; +import { changeCwdToGrafanaUiDist, changeCwdToGrafanaUi } from '../utils/cwd'; +import semver from 'semver'; +import inquirer from 'inquirer'; +import chalk from 'chalk'; +import { startSpinner } from '../utils/startSpinner'; +import { savePackage } from './grafanaui.build'; + +type VersionBumpType = 'patch' | 'minor' | 'major'; + +const promptBumpType = async () => { + return inquirer.prompt<{ type: VersionBumpType }>([ + { + type: 'list', + message: 'Select version bump', + name: 'type', + choices: ['patch', 'minor', 'major'], + validate: answer => { + if (answer.length < 1) { + return 'You must choose something'; + } + + return true; + }, + }, + ]); +}; + +const promptPrereleaseId = async () => { + return inquirer.prompt<{ id: string }>([ + { + type: 'list', + message: 'Is this a prerelease?', + name: 'id', + choices: ['no', 'alpha', 'beta'], + validate: answer => { + if (answer.length < 1) { + return 'You must choose something'; + } + + return true; + }, + }, + ]); +}; + +const promptConfirm = async (message?: string) => { + return inquirer.prompt<{ confirmed: boolean }>([ + { + type: 'confirm', + message: message || 'Is that correct?', + name: 'confirmed', + default: false, + }, + ]); +}; + +const bumpVersion = async (version: string) => { + const spinner = startSpinner(`Saving version ${version} to package.json`); + changeCwdToGrafanaUi(); + + try { + await execa('npm', ['version', version]); + spinner.succeed(); + } catch (e) { + console.log(e); + spinner.fail(); + } + + changeCwdToGrafanaUiDist(); + const pkg = require(`${process.cwd()}/package.json`); + pkg.version = version; + await savePackage(`${process.cwd()}/package.json`, pkg); +}; + +const publishPackage = async (name: string, version: string) => { + changeCwdToGrafanaUiDist(); + console.log(chalk.yellowBright.bold(`\nReview dist package.json before proceeding!\n`)); + const { confirmed } = await promptConfirm('Are you ready to publish to npm?'); + + if (!confirmed) { + process.exit(); + } + + const spinner = startSpinner(`Publishing ${name} @ ${version} to npm registry...`); + + try { + await execa('npm', ['publish']); + spinner.succeed(); + } catch (e) { + console.log(e); + spinner.fail(); + process.exit(1); + } +}; + +const releaseTask: Task = async () => { + await execTask('grafanaui.build'); + let releaseConfirmed = false; + let nextVersion; + changeCwdToGrafanaUiDist(); + + const pkg = require(`${process.cwd()}/package.json`); + + console.log(`Current version: ${pkg.version}`); + + do { + const { type } = await promptBumpType(); + const { id } = await promptPrereleaseId(); + + if (id !== 'no') { + nextVersion = semver.inc(pkg.version, `pre${type}`, id); + } else { + nextVersion = semver.inc(pkg.version, type); + } + + console.log(chalk.yellowBright.bold(`You are going to release a new version of ${pkg.name}`)); + console.log(chalk.green(`Version bump: ${pkg.version} ->`), chalk.bold.yellowBright(`${nextVersion}`)); + const { confirmed } = await promptConfirm(); + + releaseConfirmed = confirmed; + } while (!releaseConfirmed); + + await bumpVersion(nextVersion); + await publishPackage(pkg.name, nextVersion); + + console.log(chalk.green(`\nVersion ${nextVersion} of ${pkg.name} succesfully released!`)); + console.log(chalk.yellow(`\nUpdated @grafana/ui/package.json with version bump created - COMMIT THIS FILE!`)); +}; + +export default releaseTask; diff --git a/scripts/cli/utils/cwd.ts b/scripts/cli/utils/cwd.ts new file mode 100644 index 00000000000..9b4241b1369 --- /dev/null +++ b/scripts/cli/utils/cwd.ts @@ -0,0 +1,14 @@ +const cwd = process.cwd(); + +export const changeCwdToGrafanaUi = () => { + process.chdir(`${cwd}/packages/grafana-ui`); + return process.cwd(); +}; + +export const changeCwdToGrafanaUiDist = () => { + process.chdir(`${cwd}/packages/grafana-ui/dist`); +}; + +export const restoreCwd = () => { + process.chdir(cwd); +}; diff --git a/scripts/cli/utils/execTask.ts b/scripts/cli/utils/execTask.ts new file mode 100644 index 00000000000..36071134331 --- /dev/null +++ b/scripts/cli/utils/execTask.ts @@ -0,0 +1,6 @@ +import { Task } from '..'; + +export const execTask = async (taskName, options?: T) => { + const task = await import(`${__dirname}/../tasks/${taskName}.ts`); + return task.default(options) as Task; +}; diff --git a/scripts/cli/utils/startSpinner.ts b/scripts/cli/utils/startSpinner.ts new file mode 100644 index 00000000000..ce895dec722 --- /dev/null +++ b/scripts/cli/utils/startSpinner.ts @@ -0,0 +1,7 @@ +import ora from 'ora'; + +export const startSpinner = (label: string) => { + const spinner = new ora(label); + spinner.start(); + return spinner; +}; diff --git a/yarn.lock b/yarn.lock index b1a2e82e626..3d97ef64374 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1740,11 +1740,24 @@ "@types/cheerio" "*" "@types/react" "*" +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + "@types/geojson@*": version "7946.0.5" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.5.tgz#9aea839ea5af4b1bc079f1d9fa977d48665e02b0" integrity sha512-rLlMXpd3rdlrp0+xsrda/hFfOpIxgqFcRpk005UKbHtcdFK+QXAjhBAPnvO58qF4O1LdDXrcaiJxMgstCIlcaw== +"@types/inquirer@^0.0.43": + version "0.0.43" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-0.0.43.tgz#1eb0bbb4648e6cc568bd396c1e989f620ad01273" + integrity sha512-xgyfKZVMFqE8aIKy1xfFVsX2MxyXUNgjgmbF6dRbR3sL+ZM5K4ka/9L4mmTwX8eTeVYtduyXu0gUVwVJa1HbNw== + dependencies: + "@types/rx" "*" + "@types/through" "*" + "@types/jest@^23.3.2": version "23.3.14" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.14.tgz#37daaf78069e7948520474c87b80092ea912520a" @@ -1756,9 +1769,9 @@ integrity sha512-SVtqEcudm7yjkTwoRA1gC6CNMhGDdMx4Pg8BPdiqI7bXXdCn1BPmtxgeWYQOgDxrq53/5YTlhq5ULxBEAlWIBg== "@types/lodash@^4.14.119": - version "4.14.120" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.120.tgz#cf265d06f6c7a710db087ed07523ab8c1a24047b" - integrity sha512-jQ21kQ120mo+IrDs1nFNVm/AsdFxIx2+vZ347DbogHJPd/JzKNMOqU6HCYin1W6v8l5R9XSO2/e9cxmn7HAnVw== + version "4.14.119" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" + integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw== "@types/node@*": version "11.9.0" @@ -1859,6 +1872,107 @@ dependencies: reselect "*" +"@types/rx-core-binding@*": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/rx-core-binding/-/rx-core-binding-4.0.4.tgz#d969d32f15a62b89e2862c17b3ee78fe329818d3" + integrity sha512-5pkfxnC4w810LqBPUwP5bg7SFR/USwhMSaAeZQQbEHeBp57pjKXRlXmqpMrLJB4y1oglR/c2502853uN0I+DAQ== + dependencies: + "@types/rx-core" "*" + +"@types/rx-core@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-core/-/rx-core-4.0.3.tgz#0b3354b1238cedbe2b74f6326f139dbc7a591d60" + integrity sha1-CzNUsSOM7b4rdPYybxOdvHpZHWA= + +"@types/rx-lite-aggregates@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-aggregates/-/rx-lite-aggregates-4.0.3.tgz#6efb2b7f3d5f07183a1cb2bd4b1371d7073384c2" + integrity sha512-MAGDAHy8cRatm94FDduhJF+iNS5//jrZ/PIfm+QYw9OCeDgbymFHChM8YVIvN2zArwsRftKgE33QfRWvQk4DPg== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-async@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/rx-lite-async/-/rx-lite-async-4.0.2.tgz#27fbf0caeff029f41e2d2aae638b05e91ceb600c" + integrity sha512-vTEv5o8l6702ZwfAM5aOeVDfUwBSDOs+ARoGmWAKQ6LOInQ8J4/zjM7ov12fuTpktUKdMQjkeCp07Vd73mPkxw== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-backpressure@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-backpressure/-/rx-lite-backpressure-4.0.3.tgz#05abb19bdf87cc740196c355e5d0b37bb50b5d56" + integrity sha512-Y6aIeQCtNban5XSAF4B8dffhIKu6aAy/TXFlScHzSxh6ivfQBQw6UjxyEJxIOt3IT49YkS+siuayM2H/Q0cmgA== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-coincidence@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-coincidence/-/rx-lite-coincidence-4.0.3.tgz#80bd69acc4054a15cdc1638e2dc8843498cd85c0" + integrity sha512-1VNJqzE9gALUyMGypDXZZXzR0Tt7LC9DdAZQ3Ou/Q0MubNU35agVUNXKGHKpNTba+fr8GdIdkC26bRDqtCQBeQ== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-experimental@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/rx-lite-experimental/-/rx-lite-experimental-4.0.1.tgz#c532f5cbdf3f2c15da16ded8930d1b2984023cbd" + integrity sha1-xTL1y98/LBXaFt7Ykw0bKYQCPL0= + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-joinpatterns@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/rx-lite-joinpatterns/-/rx-lite-joinpatterns-4.0.1.tgz#f70fe370518a8432f29158cc92ffb56b4e4afc3e" + integrity sha1-9w/jcFGKhDLykVjMkv+1a05K/D4= + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-testing@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/rx-lite-testing/-/rx-lite-testing-4.0.1.tgz#21b19d11f4dfd6ffef5a9d1648e9c8879bfe21e9" + integrity sha1-IbGdEfTf1v/vWp0WSOnIh5v+Iek= + dependencies: + "@types/rx-lite-virtualtime" "*" + +"@types/rx-lite-time@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-time/-/rx-lite-time-4.0.3.tgz#0eda65474570237598f3448b845d2696f2dbb1c4" + integrity sha512-ukO5sPKDRwCGWRZRqPlaAU0SKVxmWwSjiOrLhoQDoWxZWg6vyB9XLEZViKOzIO6LnTIQBlk4UylYV0rnhJLxQw== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-virtualtime@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-virtualtime/-/rx-lite-virtualtime-4.0.3.tgz#4b30cacd0fe2e53af29f04f7438584c7d3959537" + integrity sha512-3uC6sGmjpOKatZSVHI2xB1+dedgml669ZRvqxy+WqmGJDVusOdyxcKfyzjW0P3/GrCiN4nmRkLVMhPwHCc5QLg== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite@*": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/rx-lite/-/rx-lite-4.0.6.tgz#3c02921c4244074234f26b772241bcc20c18c253" + integrity sha512-oYiDrFIcor9zDm0VDUca1UbROiMYBxMLMaM6qzz4ADAfOmA9r1dYEcAFH+2fsPI5BCCjPvV9pWC3X3flbrvs7w== + dependencies: + "@types/rx-core" "*" + "@types/rx-core-binding" "*" + +"@types/rx@*": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@types/rx/-/rx-4.1.1.tgz#598fc94a56baed975f194574e0f572fd8e627a48" + integrity sha1-WY/JSla67ZdfGUV04PVy/Y5iekg= + dependencies: + "@types/rx-core" "*" + "@types/rx-core-binding" "*" + "@types/rx-lite" "*" + "@types/rx-lite-aggregates" "*" + "@types/rx-lite-async" "*" + "@types/rx-lite-backpressure" "*" + "@types/rx-lite-coincidence" "*" + "@types/rx-lite-experimental" "*" + "@types/rx-lite-joinpatterns" "*" + "@types/rx-lite-testing" "*" + "@types/rx-lite-time" "*" + "@types/rx-lite-virtualtime" "*" + "@types/storybook__addon-actions@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@types/storybook__addon-actions/-/storybook__addon-actions-3.4.1.tgz#8f90d76b023b58ee794170f2fe774a3fddda2c1d" @@ -1905,6 +2019,13 @@ resolved "https://registry.yarnpkg.com/@types/tether/-/tether-1.4.4.tgz#0fde1ccbd2f1fad74f8f465fe6227ff3b7bff634" integrity sha512-6qhsFJVMuMqaQRVyQVi3zUBLfKYyryktL0ZP0Z3zegzeQ7WKm0PZNCdl3JsaitJbzqaoQ9qsFKMfaj5MiMfcSQ== +"@types/through@*": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.29.tgz#72943aac922e179339c651fa34a4428a4d722f93" + integrity sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w== + dependencies: + "@types/node" "*" + "@types/tinycolor2@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.1.tgz#2f5670c9d1d6e558897a810ed284b44918fc1253" @@ -4381,6 +4502,11 @@ builtin-modules@^1.0.0, builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= +builtin-modules@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.0.0.tgz#1e587d44b006620d90286cc7a9238bbc6129cab1" + integrity sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg== + builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" @@ -4848,6 +4974,11 @@ cli-cursor@^2.0.0, cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-spinners@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" + integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== + cli-table2@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/cli-table2/-/cli-table2-0.2.0.tgz#2d1ef7f218a0e786e214540562d4bd177fe32d97" @@ -7097,6 +7228,11 @@ estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= +estree-walker@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39" + integrity sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig== + esutils@^2.0.0, esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -7795,6 +7931,15 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@7.0.1, fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^0.30.0: version "0.30.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" @@ -7824,15 +7969,6 @@ fs-extra@^3.0.1: jsonfile "^3.0.0" universalify "^0.1.0" -fs-extra@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -9211,7 +9347,7 @@ inquirer@^5.2.0: strip-ansi "^4.0.0" through "^2.3.6" -inquirer@^6.0.0, inquirer@^6.2.0: +inquirer@^6.0.0, inquirer@^6.2.0, inquirer@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.2.tgz#46941176f65c9eb20804627149b743a218f25406" integrity sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA== @@ -9523,6 +9659,11 @@ is-lower-case@^1.1.0: dependencies: lower-case "^1.1.0" +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + is-my-ip-valid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" @@ -10189,6 +10330,14 @@ jest-worker@^23.2.0: dependencies: merge-stream "^1.0.1" +jest-worker@^24.0.0: + version "24.0.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.0.0.tgz#3d3483b077bf04f412f47654a27bba7e947f8b6d" + integrity sha512-s64/OThpfQvoCeHG963MiEZOAAxu8kHsaL/rCMF7lpdzo7vgF0CtPml9hfguOMgykgH/eOm4jFP4ibfHLruytg== + dependencies: + merge-stream "^1.0.1" + supports-color "^6.1.0" + jest@^23.6.0: version "23.6.0" resolved "https://registry.yarnpkg.com/jest/-/jest-23.6.0.tgz#ad5835e923ebf6e19e7a1d7529a432edfee7813d" @@ -11050,6 +11199,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +magic-string@^0.25.1: + version "0.25.2" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.2.tgz#139c3a729515ec55e96e69e82a11fe890a293ad9" + integrity sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg== + dependencies: + sourcemap-codec "^1.4.4" + make-dir@^1.0.0, make-dir@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -12525,7 +12681,7 @@ opener@~1.4.3: resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg= -opn@5.4.0, opn@^5.1.0, opn@^5.4.0: +opn@5.4.0, opn@^5.1.0, opn@^5.3.0, opn@^5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035" integrity sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw== @@ -12560,6 +12716,18 @@ optionator@^0.8.1: type-check "~0.3.2" wordwrap "~1.0.0" +ora@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.1.0.tgz#dbedd8c03b5d017fb67083e87ee52f5ec89823ed" + integrity sha512-vRBPaNCclUi8pUxRF/G8+5qEQkc6EgzKK1G2ZNJUIGu088Un5qIxFXeDgymvPRM9nmrcUOGzQgS1Vmtz+NtlMw== + dependencies: + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-spinners "^1.3.1" + log-symbols "^2.2.0" + strip-ansi "^5.0.0" + wcwidth "^1.0.1" + ordered-ast-traverse@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ordered-ast-traverse/-/ordered-ast-traverse-1.1.1.tgz#6843a170bc0eee8b520cc8ddc1ddd3aa30fa057c" @@ -13587,12 +13755,7 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= -prettier@1.16.4: - version "1.16.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" - integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== - -prettier@^1.12.1: +prettier@1.16.4, prettier@^1.12.1: version "1.16.4" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== @@ -15022,6 +15185,13 @@ resolve@1.1.7, resolve@~1.1.0: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= +resolve@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== + dependencies: + path-parse "^1.0.5" + resolve@1.x, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: version "1.10.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" @@ -15084,7 +15254,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.8, rimraf@^2.4.4, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@~2.6.2: +rimraf@2, rimraf@^2.2.8, rimraf@^2.4.4, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -15104,6 +15274,80 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rollup-plugin-commonjs@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.2.0.tgz#4604e25069e0c78a09e08faa95dc32dec27f7c89" + integrity sha512-0RM5U4Vd6iHjL6rLvr3lKBwnPsaVml+qxOGaaNUWN1lSq6S33KhITOfHmvxV3z2vy9Mk4t0g4rNlVaJJsNQPWA== + dependencies: + estree-walker "^0.5.2" + magic-string "^0.25.1" + resolve "^1.8.1" + rollup-pluginutils "^2.3.3" + +rollup-plugin-node-resolve@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.0.0.tgz#9bc6b8205e9936cc0e26bba2415f1ecf1e64d9b2" + integrity sha512-7Ni+/M5RPSUBfUaP9alwYQiIKnKeXCOHiqBpKUl9kwp3jX5ZJtgXAait1cne6pGEVUUztPD6skIKH9Kq9sNtfw== + dependencies: + builtin-modules "^3.0.0" + is-module "^1.0.0" + resolve "^1.8.1" + +rollup-plugin-sourcemaps@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz#62125aa94087aadf7b83ef4dfaf629b473135e87" + integrity sha1-YhJaqUCHqt97g+9N+vYptHMTXoc= + dependencies: + rollup-pluginutils "^2.0.1" + source-map-resolve "^0.5.0" + +rollup-plugin-terser@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-4.0.4.tgz#6f661ef284fa7c27963d242601691dc3d23f994e" + integrity sha512-wPANT5XKVJJ8RDUN0+wIr7UPd0lIXBo4UdJ59VmlPCtlFsE20AM+14pe+tk7YunCsWEiuzkDBY3QIkSCjtrPXg== + dependencies: + "@babel/code-frame" "^7.0.0" + jest-worker "^24.0.0" + serialize-javascript "^1.6.1" + terser "^3.14.1" + +rollup-plugin-typescript2@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.19.2.tgz#87d9c799cd6e02efbedbba25af12753a1e92b6c2" + integrity sha512-DRG7SaYX0QzBIz6rII5nm1UkiceS95r8mJjujugybyIueNF3auvzGTHMK62O7As/0q5RHjXsOguWOUv+KJKLFA== + dependencies: + fs-extra "7.0.1" + resolve "1.8.1" + rollup-pluginutils "2.3.3" + tslib "1.9.3" + +rollup-plugin-visualizer@^0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-0.9.2.tgz#bbc8e8e67d5aa3e6c188c5ca0fcfa57234fb9f92" + integrity sha512-EHXHLp9Q8v5QdRTSjgio4Alr2MKxCJroLhJunmcH+pWAM5869nI5mdWjk2jp64rjxzEahrMYmfF/G5sbTHIhKw== + dependencies: + mkdirp "^0.5.1" + opn "^5.3.0" + source-map "^0.7.3" + typeface-oswald "0.0.54" + +rollup-pluginutils@2.3.3, rollup-pluginutils@^2.0.1, rollup-pluginutils@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz#3aad9b1eb3e7fe8262820818840bf091e5ae6794" + integrity sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA== + dependencies: + estree-walker "^0.5.2" + micromatch "^2.3.11" + +rollup@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.1.2.tgz#8d094b85683b810d0c05a16bd7618cf70d48eba7" + integrity sha512-OkdMxqMl8pWoQc5D8y1cIinYQPPLV8ZkfLgCzL6SytXeNA2P7UHynEQXI9tYxuAjAMsSyvRaWnyJDLHMxq0XAg== + dependencies: + "@types/estree" "0.0.39" + "@types/node" "*" + acorn "^6.0.5" + rst-selector-parser@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" @@ -15377,7 +15621,7 @@ sentence-case@^2.1.0: no-case "^2.2.0" upper-case-first "^1.1.2" -serialize-javascript@^1.4.0: +serialize-javascript@^1.4.0, serialize-javascript@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.6.1.tgz#4d1f697ec49429a847ca6f442a2a755126c4d879" integrity sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw== @@ -15908,6 +16152,11 @@ source-map@^0.7.2, source-map@^0.7.3: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +sourcemap-codec@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz#c63ea927c029dd6bd9a2b7fa03b3fec02ad56e9f" + integrity sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg== + space-separated-tokens@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.2.tgz#e95ab9d19ae841e200808cd96bc7bd0adbbb3412" @@ -16562,7 +16811,7 @@ terser-webpack-plugin@^1.1.0: webpack-sources "^1.1.0" worker-farm "^1.5.2" -terser@^3.16.1: +terser@^3.14.1, terser@^3.16.1: version "3.16.1" resolved "https://registry.yarnpkg.com/terser/-/terser-3.16.1.tgz#5b0dd4fa1ffd0b0b43c2493b2c364fd179160493" integrity sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow== @@ -16887,7 +17136,7 @@ ts-node@^8.0.2: source-map-support "^0.5.6" yn "^3.0.0" -tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@1.9.3, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== @@ -16987,6 +17236,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typeface-oswald@0.0.54: + version "0.0.54" + resolved "https://registry.yarnpkg.com/typeface-oswald/-/typeface-oswald-0.0.54.tgz#1e253011622cdd50f580c04e7d625e7f449763d7" + integrity sha512-U1WMNp4qfy4/3khIfHMVAIKnNu941MXUfs3+H9R8PFgnoz42Hh9pboSFztWr86zut0eXC8byalmVhfkiKON/8Q== + typescript@^3.0.3, typescript@^3.2.2: version "3.3.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3.tgz#f1657fc7daa27e1a8930758ace9ae8da31403221" @@ -17539,7 +17793,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wcwidth@^1.0.0: +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= From d4c40e436063d267479d837fb76191d822cf2b03 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 10:59:13 +0100 Subject: [PATCH 018/244] Imports updates --- .../src/components/ColorPicker/ColorInput.tsx | 4 +-- .../components/ColorPicker/ColorPicker.tsx | 27 +++---------------- .../ColorPicker/ColorPickerPopover.test.tsx | 2 +- .../ColorPicker/ColorPickerPopover.tsx | 20 +++++++++++--- .../ColorPicker/NamedColorsGroup.tsx | 3 ++- .../ColorPicker/SeriesColorPickerPopover.tsx | 3 +-- .../warnAboutColorPickerPropsDeprecation.ts | 9 +++++++ .../CustomScrollbar/CustomScrollbar.tsx | 4 +-- .../src/components/FormField/FormField.tsx | 2 +- .../grafana-ui/src/components/Gauge/Gauge.tsx | 8 +++--- .../src/components/Select/Select.tsx | 2 +- .../src/components/Switch/Switch.tsx | 4 +-- .../ValueMappingsEditor/MappingRow.tsx | 4 ++- .../ValueMappingsEditor.tsx | 2 +- packages/grafana-ui/src/index.ts | 1 - .../grafana-ui/src/themes/ThemeContext.tsx | 2 +- packages/grafana-ui/src/themes/index.ts | 3 +++ packages/grafana-ui/src/utils/colors.ts | 20 ++++++++------ .../src/utils/namedColorsPalette.ts | 4 +-- .../grafana-ui/src/utils/processTimeSeries.ts | 4 +-- .../src/utils/storybook/withTheme.tsx | 2 +- public/app/core/utils/explore.ts | 2 +- 22 files changed, 71 insertions(+), 61 deletions(-) create mode 100644 packages/grafana-ui/src/components/ColorPicker/warnAboutColorPickerPropsDeprecation.ts diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx index 4c5df5314b8..9b5f5b98432 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { ColorPickerProps } from './ColorPicker'; +import { ColorPickerProps } from './ColorPickerPopover'; import tinycolor from 'tinycolor2'; -import { debounce } from 'lodash'; +import debounce from 'lodash/debounce'; interface ColorInputState { previousColor: string; diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 321323ac58b..cee562c9474 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -1,32 +1,11 @@ import React, { Component, createRef } from 'react'; import { PopperController } from '../Tooltip/PopperController'; -import { Popper } from '../Tooltip/Popper'; -import { ColorPickerPopover } from './ColorPickerPopover'; -import { Themeable } from '../../types'; +import {Popper} from '../Tooltip/Popper'; +import { ColorPickerPopover, ColorPickerProps, ColorPickerChangeHandler } from './ColorPickerPopover'; import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; -import propDeprecationWarning from '../../utils/propDeprecationWarning'; + import { withTheme } from '../../themes/ThemeContext'; -type ColorPickerChangeHandler = (color: string) => void; - -export interface ColorPickerProps extends Themeable { - color: string; - onChange: ColorPickerChangeHandler; - - /** - * @deprecated Use onChange instead - */ - onColorChange?: ColorPickerChangeHandler; - enableNamedColors?: boolean; - children?: JSX.Element; -} - -export const warnAboutColorPickerPropsDeprecation = (componentName: string, props: ColorPickerProps) => { - const { onColorChange } = props; - if (onColorChange) { - propDeprecationWarning(componentName, 'onColorChange', 'onChange'); - } -}; export const colorPickerFactory = ( popover: React.ComponentType, diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx index 7a5000653d4..df4b5bb24a2 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx @@ -3,7 +3,7 @@ import { mount, ReactWrapper } from 'enzyme'; import { ColorPickerPopover } from './ColorPickerPopover'; import { getColorDefinitionByName, getNamedColorPalette } from '../../utils/namedColorsPalette'; import { ColorSwatch } from './NamedColorsGroup'; -import { flatten } from 'lodash'; +import flatten from 'lodash/flatten'; import { GrafanaThemeType } from '../../types'; import { getTheme } from '../../themes'; diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index ee879f94762..ce9ca5130d4 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -1,23 +1,37 @@ import React from 'react'; import { NamedColorsPalette } from './NamedColorsPalette'; import { getColorName, getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; -import { ColorPickerProps, warnAboutColorPickerPropsDeprecation } from './ColorPicker'; import { PopperContentProps } from '../Tooltip/PopperController'; import SpectrumPalette from './SpectrumPalette'; -import { GrafanaThemeType } from '../../types/theme'; +import { GrafanaThemeType, Themeable } from '../../types/theme'; +import { warnAboutColorPickerPropsDeprecation } from './warnAboutColorPickerPropsDeprecation'; +export type ColorPickerChangeHandler = (color: string) => void; + +export interface ColorPickerProps extends Themeable { + color: string; + onChange: ColorPickerChangeHandler; + + /** + * @deprecated Use onChange instead + */ + onColorChange?: ColorPickerChangeHandler; + enableNamedColors?: boolean; + children?: JSX.Element; +} export interface Props extends ColorPickerProps, PopperContentProps { customPickers?: T; } type PickerType = 'palette' | 'spectrum'; -interface CustomPickersDescriptor { +export interface CustomPickersDescriptor { [key: string]: { tabComponent: React.ComponentType; name: string; }; } + interface State { activePicker: PickerType | keyof T; } diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx index 2b5f7bb9b57..b488f14fd62 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsGroup.tsx @@ -2,7 +2,8 @@ import React, { FunctionComponent } from 'react'; import { Themeable } from '../../types'; import { ColorDefinition, getColorForTheme } from '../../utils/namedColorsPalette'; import { Color } from 'csstype'; -import { find, upperFirst } from 'lodash'; +import upperFirst from 'lodash/upperFirst'; +import find from 'lodash/find'; import { selectThemeVariant } from '../../themes/selectThemeVariant'; type ColorChangeHandler = (color: ColorDefinition) => void; diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index 5fdad9c43cf..219d2dd357a 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -1,7 +1,6 @@ import React, { FunctionComponent } from 'react'; -import { ColorPickerPopover } from './ColorPickerPopover'; -import { ColorPickerProps } from './ColorPicker'; +import { ColorPickerPopover, ColorPickerProps } from './ColorPickerPopover'; import { PopperContentProps } from '../Tooltip/PopperController'; import { Switch } from '../Switch/Switch'; import { withTheme } from '../../themes/ThemeContext'; diff --git a/packages/grafana-ui/src/components/ColorPicker/warnAboutColorPickerPropsDeprecation.ts b/packages/grafana-ui/src/components/ColorPicker/warnAboutColorPickerPropsDeprecation.ts new file mode 100644 index 00000000000..b8919c682e2 --- /dev/null +++ b/packages/grafana-ui/src/components/ColorPicker/warnAboutColorPickerPropsDeprecation.ts @@ -0,0 +1,9 @@ +import propDeprecationWarning from '../../utils/propDeprecationWarning'; +import { ColorPickerProps } from './ColorPickerPopover'; + +export const warnAboutColorPickerPropsDeprecation = (componentName: string, props: ColorPickerProps) => { + const { onColorChange } = props; + if (onColorChange) { + propDeprecationWarning(componentName, 'onColorChange', 'onChange'); + } +}; diff --git a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx index 39bae62e28b..d52d2fea80d 100644 --- a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx +++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import _ from 'lodash'; +import isNil from 'lodash/isNil'; import classNames from 'classnames'; import Scrollbars from 'react-custom-scrollbars'; @@ -41,7 +41,7 @@ export class CustomScrollbar extends PureComponent { updateScroll() { const ref = this.ref.current; - if (ref && !_.isNil(this.props.scrollTop)) { + if (ref && !isNil(this.props.scrollTop)) { if (this.props.scrollTop > 10000) { ref.scrollToBottom(); } else { diff --git a/packages/grafana-ui/src/components/FormField/FormField.tsx b/packages/grafana-ui/src/components/FormField/FormField.tsx index 593678c7383..89e879ccc64 100644 --- a/packages/grafana-ui/src/components/FormField/FormField.tsx +++ b/packages/grafana-ui/src/components/FormField/FormField.tsx @@ -1,5 +1,5 @@ import React, { InputHTMLAttributes, FunctionComponent } from 'react'; -import { FormLabel } from '..'; +import { FormLabel } from '../FormLabel/FormLabel'; export interface Props extends InputHTMLAttributes { label: string; diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx index de09cb8e1f8..d04daae3dab 100644 --- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx +++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx @@ -1,10 +1,10 @@ import React, { PureComponent } from 'react'; import $ from 'jquery'; - -import { ValueMapping, Threshold, BasicGaugeColor, GrafanaThemeType } from '../../types'; import { getMappedValue } from '../../utils/valueMappings'; -import { getColorFromHexRgbOrName, getValueFormat } from '../../utils'; -import { Themeable } from '../../index'; +import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; +import { Themeable, GrafanaThemeType } from '../../types/theme'; +import { ValueMapping, Threshold, BasicGaugeColor } from '../../types/panel'; +import { getValueFormat } from '../../utils/valueFormats/valueFormats'; type TimeSeriesValue = string | number | null; diff --git a/packages/grafana-ui/src/components/Select/Select.tsx b/packages/grafana-ui/src/components/Select/Select.tsx index 06973dc08e8..5528c886e0f 100644 --- a/packages/grafana-ui/src/components/Select/Select.tsx +++ b/packages/grafana-ui/src/components/Select/Select.tsx @@ -16,7 +16,7 @@ import SelectOptionGroup from './SelectOptionGroup'; import IndicatorsContainer from './IndicatorsContainer'; import NoOptionsMessage from './NoOptionsMessage'; import resetSelectStyles from './resetSelectStyles'; -import { CustomScrollbar } from '..'; +import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar'; export interface SelectOptionItem { label?: string; diff --git a/packages/grafana-ui/src/components/Switch/Switch.tsx b/packages/grafana-ui/src/components/Switch/Switch.tsx index f79de95fd2d..e2a25682519 100644 --- a/packages/grafana-ui/src/components/Switch/Switch.tsx +++ b/packages/grafana-ui/src/components/Switch/Switch.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import _ from 'lodash'; +import uniqueId from 'lodash/uniqueId'; export interface Props { label: string; @@ -17,7 +17,7 @@ export interface State { export class Switch extends PureComponent { state = { - id: _.uniqueId('check-'), + id: uniqueId(), }; internalOnChange = (event: React.FormEvent) => { diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/MappingRow.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/MappingRow.tsx index c5704e8bc88..f5cfdfe6cbf 100644 --- a/packages/grafana-ui/src/components/ValueMappingsEditor/MappingRow.tsx +++ b/packages/grafana-ui/src/components/ValueMappingsEditor/MappingRow.tsx @@ -1,7 +1,9 @@ import React, { ChangeEvent, PureComponent } from 'react'; import { MappingType, ValueMapping } from '../../types'; -import { FormField, FormLabel, Select } from '..'; +import { Select } from '../Select/Select'; +import { FormField } from '../FormField/FormField'; +import { FormLabel } from '../FormLabel/FormLabel'; export interface Props { valueMapping: ValueMapping; diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx index c07369fa283..9778fdf13c4 100644 --- a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx +++ b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx @@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; import MappingRow from './MappingRow'; import { MappingType, ValueMapping } from '../../types'; -import { PanelOptionsGroup } from '..'; +import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; export interface Props { valueMappings: ValueMapping[]; diff --git a/packages/grafana-ui/src/index.ts b/packages/grafana-ui/src/index.ts index 216f2f13bad..1583e1e6334 100644 --- a/packages/grafana-ui/src/index.ts +++ b/packages/grafana-ui/src/index.ts @@ -2,4 +2,3 @@ export * from './components'; export * from './types'; export * from './utils'; export * from './themes'; -export * from './themes/ThemeContext'; diff --git a/packages/grafana-ui/src/themes/ThemeContext.tsx b/packages/grafana-ui/src/themes/ThemeContext.tsx index a61a71d8af6..e2d91ce3671 100644 --- a/packages/grafana-ui/src/themes/ThemeContext.tsx +++ b/packages/grafana-ui/src/themes/ThemeContext.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { GrafanaThemeType, Themeable } from '../types'; import { getTheme } from './index'; +import { GrafanaThemeType, Themeable } from '../types/theme'; type Omit = Pick>; type Subtract = Omit; diff --git a/packages/grafana-ui/src/themes/index.ts b/packages/grafana-ui/src/themes/index.ts index 1d8d2f62606..cc8d9335e4c 100644 --- a/packages/grafana-ui/src/themes/index.ts +++ b/packages/grafana-ui/src/themes/index.ts @@ -1,6 +1,7 @@ import darkTheme from './dark'; import lightTheme from './light'; import { GrafanaTheme } from '../types/theme'; +import { ThemeContext, withTheme } from './ThemeContext'; let themeMock: ((name?: string) => GrafanaTheme) | null; @@ -12,3 +13,5 @@ export const mockTheme = (mock: (name?: string) => GrafanaTheme) => { themeMock = null; }; }; + +export { ThemeContext, withTheme }; diff --git a/packages/grafana-ui/src/utils/colors.ts b/packages/grafana-ui/src/utils/colors.ts index 65ee605c33b..a316919a9a8 100644 --- a/packages/grafana-ui/src/utils/colors.ts +++ b/packages/grafana-ui/src/utils/colors.ts @@ -1,4 +1,8 @@ -import _ from 'lodash'; +import map from 'lodash/map'; +import sortBy from 'lodash/sortBy'; +import flattenDeep from 'lodash/flattenDeep'; +import chunk from 'lodash/chunk'; +import zip from 'lodash/zip'; import tinycolor from 'tinycolor2'; export const PALETTE_ROWS = 4; @@ -69,16 +73,16 @@ export const colors = [ ]; function sortColorsByHue(hexColors: string[]) { - const hslColors = _.map(hexColors, hexToHsl); + const hslColors = map(hexColors, hexToHsl); - const sortedHSLColors = _.sortBy(hslColors, ['h']); - const chunkedHSLColors = _.chunk(sortedHSLColors, PALETTE_ROWS); - const sortedChunkedHSLColors = _.map(chunkedHSLColors, chunk => { - return _.sortBy(chunk, 'l'); + const sortedHSLColors = sortBy(hslColors, ['h']); + const chunkedHSLColors = chunk(sortedHSLColors, PALETTE_ROWS); + const sortedChunkedHSLColors = map(chunkedHSLColors, chunk => { + return sortBy(chunk, 'l'); }); - const flattenedZippedSortedChunkedHSLColors = _.flattenDeep(_.zip(...sortedChunkedHSLColors)); + const flattenedZippedSortedChunkedHSLColors = flattenDeep(zip(...sortedChunkedHSLColors)); - return _.map(flattenedZippedSortedChunkedHSLColors, hslToHex); + return map(flattenedZippedSortedChunkedHSLColors, hslToHex); } function hexToHsl(color: string) { diff --git a/packages/grafana-ui/src/utils/namedColorsPalette.ts b/packages/grafana-ui/src/utils/namedColorsPalette.ts index ac1c1be6fa5..deea2374205 100644 --- a/packages/grafana-ui/src/utils/namedColorsPalette.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.ts @@ -1,5 +1,5 @@ -import { flatten } from 'lodash'; -import { GrafanaThemeType } from '../types'; +import flatten from 'lodash/flatten'; +import { GrafanaThemeType } from '../types/theme'; import tinycolor from 'tinycolor2'; type Hue = 'green' | 'yellow' | 'red' | 'blue' | 'orange' | 'purple'; diff --git a/packages/grafana-ui/src/utils/processTimeSeries.ts b/packages/grafana-ui/src/utils/processTimeSeries.ts index 9a18b85fe18..f5e9f96efba 100644 --- a/packages/grafana-ui/src/utils/processTimeSeries.ts +++ b/packages/grafana-ui/src/utils/processTimeSeries.ts @@ -1,5 +1,5 @@ // Libraries -import _ from 'lodash'; +import isNumber from 'lodash/isNumber'; import { colors } from './colors'; @@ -75,7 +75,7 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS } if (currentValue !== null) { - if (_.isNumber(currentValue)) { + if (isNumber(currentValue)) { total += currentValue; allIsNull = false; nonNulls++; diff --git a/packages/grafana-ui/src/utils/storybook/withTheme.tsx b/packages/grafana-ui/src/utils/storybook/withTheme.tsx index af6d63c6542..dbe2c505069 100644 --- a/packages/grafana-ui/src/utils/storybook/withTheme.tsx +++ b/packages/grafana-ui/src/utils/storybook/withTheme.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { RenderFunction } from '@storybook/react'; import { ThemeContext } from '../../themes/ThemeContext'; import { select } from '@storybook/addon-knobs'; -import { getTheme } from '../../themes'; +import { getTheme } from '../../themes/index'; import { GrafanaThemeType } from '../../types'; const ThemableStory: React.FunctionComponent<{}> = ({ children }) => { diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 9c6be4be242..619391d46d1 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -11,7 +11,7 @@ import { colors } from '@grafana/ui'; import TableModel, { mergeTablesIntoModel } from 'app/core/table_model'; // Types -import { RawTimeRange, IntervalValues, DataQuery, DataSourceApi } from '@grafana/ui/src/types'; +import { RawTimeRange, IntervalValues, DataQuery, DataSourceApi } from '@grafana/ui'; import TimeSeries from 'app/core/time_series2'; import { ExploreUrlState, From 75e9c1e8fc375946dec95c809634d5366bc189c5 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 11:25:06 +0100 Subject: [PATCH 019/244] Fix version and name in grafana/ui package.json --- packages/grafana-ui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index e1c78b1e2ab..0fc2686a932 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -1,6 +1,6 @@ { - "name": "testing-ignore-this", - "version": "1.0.0-beta.0", + "name": "@grafana/ui", + "version": "0.0.0", "description": "", "main": "src/index.ts", "scripts": { From 996588528a8b61e65f64702c16425fe73d3262f6 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 11:52:47 +0100 Subject: [PATCH 020/244] Fixed failing tests because of circular dependency --- packages/grafana-ui/src/themes/ThemeContext.tsx | 2 +- packages/grafana-ui/src/themes/getTheme.ts | 15 +++++++++++++++ packages/grafana-ui/src/themes/index.ts | 17 ++--------------- 3 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 packages/grafana-ui/src/themes/getTheme.ts diff --git a/packages/grafana-ui/src/themes/ThemeContext.tsx b/packages/grafana-ui/src/themes/ThemeContext.tsx index e2d91ce3671..11b3d79481a 100644 --- a/packages/grafana-ui/src/themes/ThemeContext.tsx +++ b/packages/grafana-ui/src/themes/ThemeContext.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { getTheme } from './index'; +import { getTheme } from './getTheme'; import { GrafanaThemeType, Themeable } from '../types/theme'; type Omit = Pick>; diff --git a/packages/grafana-ui/src/themes/getTheme.ts b/packages/grafana-ui/src/themes/getTheme.ts new file mode 100644 index 00000000000..9962f1a9a90 --- /dev/null +++ b/packages/grafana-ui/src/themes/getTheme.ts @@ -0,0 +1,15 @@ +import darkTheme from './dark'; +import lightTheme from './light'; +import { GrafanaTheme } from '../types/theme'; + +let themeMock: ((name?: string) => GrafanaTheme) | null; + +export const getTheme = (name?: string) => + (themeMock && themeMock(name)) || (name === 'light' ? lightTheme : darkTheme); + +export const mockTheme = (mock: (name?: string) => GrafanaTheme) => { + themeMock = mock; + return () => { + themeMock = null; + }; +}; diff --git a/packages/grafana-ui/src/themes/index.ts b/packages/grafana-ui/src/themes/index.ts index cc8d9335e4c..2d29576d968 100644 --- a/packages/grafana-ui/src/themes/index.ts +++ b/packages/grafana-ui/src/themes/index.ts @@ -1,17 +1,4 @@ -import darkTheme from './dark'; -import lightTheme from './light'; -import { GrafanaTheme } from '../types/theme'; import { ThemeContext, withTheme } from './ThemeContext'; +import { getTheme, mockTheme } from './getTheme'; -let themeMock: ((name?: string) => GrafanaTheme) | null; - -export let getTheme = (name?: string) => (themeMock && themeMock(name)) || (name === 'light' ? lightTheme : darkTheme); - -export const mockTheme = (mock: (name?: string) => GrafanaTheme) => { - themeMock = mock; - return () => { - themeMock = null; - }; -}; - -export { ThemeContext, withTheme }; +export { ThemeContext, withTheme, mockTheme, getTheme }; From 394ee5dc08e1736962a6399a20216aba4f63baaf Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 11:53:27 +0100 Subject: [PATCH 021/244] Added keywords and description go grafana/ui package --- packages/grafana-ui/package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 0fc2686a932..16a4d5f87b2 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -1,7 +1,12 @@ { "name": "@grafana/ui", "version": "0.0.0", - "description": "", + "description": "Grafana Components Library", + "keywords": [ + "typescript", + "react", + "react-component" + ], "main": "src/index.ts", "scripts": { "tslint": "tslint -c tslint.json --project tsconfig.json", From 58e1a8bd3cabd7aefeba9dfee7c1b2b67e602875 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 15:13:59 +0100 Subject: [PATCH 022/244] Update docs to match current npm scripts --- docs/sources/project/building_from_source.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/project/building_from_source.md b/docs/sources/project/building_from_source.md index d4eb1a917d6..35e840d6e88 100644 --- a/docs/sources/project/building_from_source.md +++ b/docs/sources/project/building_from_source.md @@ -83,7 +83,7 @@ go get github.com/Unknwon/bra bra run ``` -You'll also need to run `yarn watch` to watch for changes to the front-end (typescript, html, sass) +You'll also need to run `yarn start` to watch for changes to the front-end (typescript, html, sass) ### Running tests From db58ab6c0270875991c344f21f0c93254565bc6c Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 15:40:43 +0100 Subject: [PATCH 023/244] Fixed prettier issue in color picker --- packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index cee562c9474..183cbfed67f 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -1,6 +1,6 @@ import React, { Component, createRef } from 'react'; import { PopperController } from '../Tooltip/PopperController'; -import {Popper} from '../Tooltip/Popper'; +import { Popper } from '../Tooltip/Popper'; import { ColorPickerPopover, ColorPickerProps, ColorPickerChangeHandler } from './ColorPickerPopover'; import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; import { SeriesColorPickerPopover } from './SeriesColorPickerPopover'; From df984059dbafc09e4ef28ecb138790c0b1e3c76e Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 21 Feb 2019 15:43:54 +0100 Subject: [PATCH 024/244] docs: fix order of datasources in menu/index and update alert support --- docs/sources/alerting/rules.md | 9 +++++---- docs/sources/features/datasources/azuremonitor.md | 8 ++++---- docs/sources/features/datasources/cloudwatch.md | 8 ++++---- docs/sources/features/datasources/index.md | 13 ++++++++----- docs/sources/features/datasources/influxdb.md | 2 +- docs/sources/features/datasources/loki.md | 2 +- docs/sources/features/datasources/opentsdb.md | 2 +- docs/sources/features/datasources/prometheus.md | 2 +- docs/sources/features/datasources/stackdriver.md | 8 ++++---- docs/sources/index.md | 2 +- 10 files changed, 30 insertions(+), 26 deletions(-) diff --git a/docs/sources/alerting/rules.md b/docs/sources/alerting/rules.md index 036ff97056d..510f466c3eb 100644 --- a/docs/sources/alerting/rules.md +++ b/docs/sources/alerting/rules.md @@ -27,9 +27,10 @@ and the conditions that need to be met for the alert to change state and trigger ## Execution The alert rules are evaluated in the Grafana backend in a scheduler and query execution engine that is part -of core Grafana. Only some data sources are supported right now. They include `Graphite`, `Prometheus`, `Elasticsearch`, `InfluxDB`, `OpenTSDB`, `MySQL`, `Postgres` and `Cloudwatch`. +of core Grafana. Only some data sources are supported right now. They include `Graphite`, `Prometheus`, `InfluxDB`, `Elasticsearch`, +`Stackdriver`, `Cloudwatch`, `Azure Monitor`, `MySQL`, `PostgreSQL`, `MSSQL` and `OpenTSDB`. -> Alerting support for Elasticsearch is only available in Grafana v5.2 and above. +> Alerting support for Azure Monitor is only available in Grafana v6.0 and above. ### Clustering @@ -52,9 +53,9 @@ Here you can specify the name of the alert rule and how often the scheduler shou > This setting is available in Grafana 5.4 and above. -If an alert rule has a configured `For` and the query violates the configured threshold it will first go from `OK` to `Pending`. Going from `OK` to `Pending` Grafana will not send any notifications. Once the alert rule has been firing for more than `For` duration, it will change to `Alerting` and send alert notifications. +If an alert rule has a configured `For` and the query violates the configured threshold it will first go from `OK` to `Pending`. Going from `OK` to `Pending` Grafana will not send any notifications. Once the alert rule has been firing for more than `For` duration, it will change to `Alerting` and send alert notifications. -Typically, it's always a good idea to use this setting since it's often worse to get false positive than wait a few minutes before the alert notification triggers. Looking at the `Alert list` or `Alert list panels` you will be able to see alerts in pending state. +Typically, it's always a good idea to use this setting since it's often worse to get false positive than wait a few minutes before the alert notification triggers. Looking at the `Alert list` or `Alert list panels` you will be able to see alerts in pending state. Below you can see an example timeline of an alert using the `For` setting. At ~16:04 the alert state changes to `Pending` and after 4 minutes it changes to `Alerting` which is when alert notifications are sent. Once the series falls back to normal the alert rule goes back to `OK`. {{< imgbox img="/img/docs/v54/alerting-for-dark-theme.png" caption="Alerting For" >}} diff --git a/docs/sources/features/datasources/azuremonitor.md b/docs/sources/features/datasources/azuremonitor.md index ee8ca62ef0b..1e628e8f2a8 100644 --- a/docs/sources/features/datasources/azuremonitor.md +++ b/docs/sources/features/datasources/azuremonitor.md @@ -5,9 +5,9 @@ keywords = ["grafana", "microsoft", "azure", "monitor", "application", "insights type = "docs" aliases = ["/datasources/azuremonitor"] [menu.docs] -name = "AzureMonitor" +name = "Azure Monitor" parent = "datasources" -weight = 11 +weight = 5 +++ # Using Azure Monitor in Grafana @@ -216,7 +216,7 @@ AzureActivity Table queries are mainly used in the Table panel and row a list of columns and rows. This example query returns rows with the 6 specified columns: ``` -AzureActivity +AzureActivity | where $__timeFilter() | project TimeGenerated, ResourceGroup, Category, OperationName, ActivityStatus, Caller | order by TimeGenerated desc @@ -232,7 +232,7 @@ To make writing queries easier there are several Grafana macros that can be used `TimeGenerated ≥ datetime(2018-06-05T18:09:58.907Z) and` `TimeGenerated ≤ datetime(2018-06-05T20:09:58.907Z)` where the from and to datetimes are from the Grafana time picker. -- `$__timeFilter(datetimeColumn)` - Expands to +- `$__timeFilter(datetimeColumn)` - Expands to `datetimeColumn ≥ datetime(2018-06-05T18:09:58.907Z) and` `datetimeColumn ≤ datetime(2018-06-05T20:09:58.907Z)` where the from and to datetimes are from the Grafana time picker. diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index 6ca10b2f8e8..ed88fcfc509 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -8,7 +8,7 @@ aliases = ["/datasources/cloudwatch"] name = "AWS Cloudwatch" identifier = "cloudwatch" parent = "datasources" -weight = 10 +weight = 5 +++ # Using AWS CloudWatch in Grafana @@ -77,9 +77,9 @@ Here is a minimal policy example: }, { "Sid": "AllowReadingResourcesForTags", - "Effect" : "Allow", - "Action" : "tag:GetResources", - "Resource" : "*" + "Effect" : "Allow", + "Action" : "tag:GetResources", + "Resource" : "*" } ] } diff --git a/docs/sources/features/datasources/index.md b/docs/sources/features/datasources/index.md index 42ce3f84819..9046c986f98 100644 --- a/docs/sources/features/datasources/index.md +++ b/docs/sources/features/datasources/index.md @@ -22,15 +22,18 @@ The query language and capabilities of each Data Source are obviously very diffe The following datasources are officially supported: * [Graphite]({{< relref "graphite.md" >}}) -* [Elasticsearch]({{< relref "elasticsearch.md" >}}) -* [CloudWatch]({{< relref "cloudwatch.md" >}}) -* [InfluxDB]({{< relref "influxdb.md" >}}) -* [OpenTSDB]({{< relref "opentsdb.md" >}}) * [Prometheus]({{< relref "prometheus.md" >}}) +* [InfluxDB]({{< relref "influxdb.md" >}}) +* [Elasticsearch]({{< relref "elasticsearch.md" >}}) +* [Google Stackdriver]({{< relref "stackdriver.md" >}}) +* [AWS CloudWatch]({{< relref "cloudwatch.md" >}}) +* [Azure Monitor]({{< relref "azuremonitor.md" >}}) * [Loki]({{< relref "loki.md" >}}) * [MySQL]({{< relref "mysql.md" >}}) -* [Postgres]({{< relref "postgres.md" >}}) +* [PostgreSQL]({{< relref "postgres.md" >}}) * [Microsoft SQL Server (MSSQL)]({{< relref "mssql.md" >}}) +* [OpenTSDB]({{< relref "opentsdb.md" >}}) +* [Testdata]({{< relref "testdata.md" >}}) ## Data source plugins diff --git a/docs/sources/features/datasources/influxdb.md b/docs/sources/features/datasources/influxdb.md index bc96190e9b1..94ae448f16d 100644 --- a/docs/sources/features/datasources/influxdb.md +++ b/docs/sources/features/datasources/influxdb.md @@ -7,7 +7,7 @@ aliases = ["/datasources/influxdb"] [menu.docs] name = "InfluxDB" parent = "datasources" -weight = 3 +weight = 2 +++ # Using InfluxDB in Grafana diff --git a/docs/sources/features/datasources/loki.md b/docs/sources/features/datasources/loki.md index d43ef982b4b..8dc4eaeaf4c 100644 --- a/docs/sources/features/datasources/loki.md +++ b/docs/sources/features/datasources/loki.md @@ -7,7 +7,7 @@ aliases = ["/datasources/loki"] [menu.docs] name = "Loki" parent = "datasources" -weight = 11 +weight = 6 +++ # Using Loki in Grafana diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index d2cd0b1dc0e..866619d380c 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -7,7 +7,7 @@ aliases = ["/datasources/opentsdb", "docs/features/opentsdb"] [menu.docs] name = "OpenTSDB" parent = "datasources" -weight = 5 +weight = 19 +++ # Using OpenTSDB in Grafana diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index 611a3b4d9e2..2b2704e0d81 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -7,7 +7,7 @@ aliases = ["/datasources/prometheus"] [menu.docs] name = "Prometheus" parent = "datasources" -weight = 2 +weight = 1 +++ # Using Prometheus in Grafana diff --git a/docs/sources/features/datasources/stackdriver.md b/docs/sources/features/datasources/stackdriver.md index d1cc1088276..e0f0e576b8b 100644 --- a/docs/sources/features/datasources/stackdriver.md +++ b/docs/sources/features/datasources/stackdriver.md @@ -5,9 +5,9 @@ keywords = ["grafana", "stackdriver", "google", "guide"] type = "docs" aliases = ["/datasources/stackdriver"] [menu.docs] -name = "Stackdriver" +name = "Google Stackdriver" parent = "datasources" -weight = 11 +weight = 4 +++ # Using Google Stackdriver in Grafana @@ -66,7 +66,7 @@ Click on the links above and click the `Enable` button: 4. Some new fields will appear. Fill in a name for the service account in the `Service account name` field and then choose the `Monitoring Viewer` role from the `Role` dropdown: {{< docs-imagebox img="/img/docs/v53/stackdriver_service_account_choose_role.png" class="docs-image--no-shadow" caption="Choose role" >}} - + 5. Click the Create button. A JSON key file will be created and downloaded to your computer. Store this file in a secure place as it allows access to your Stackdriver data. 6. Upload it to Grafana on the datasource Configuration page. You can either upload the file or paste in the contents of the file. @@ -156,7 +156,7 @@ Example Alias By: `{{metric.type}} - {{metric.labels.instance_name}}` Example Result: `compute.googleapis.com/instance/cpu/usage_time - server1-prod` -It is also possible to resolve the name of the Monitored Resource Type. +It is also possible to resolve the name of the Monitored Resource Type. | Alias Pattern Format | Description | Example Result | | -------------------- | ----------------------------------------------- | -------------- | diff --git a/docs/sources/index.md b/docs/sources/index.md index 3e8e014e606..7b8bfacd6f7 100644 --- a/docs/sources/index.md +++ b/docs/sources/index.md @@ -94,7 +94,7 @@ aliases = ["v1.1", "guides/reference/admin"] }}" class="nav-cards__item nav-cards__item--ds"> -
Cloudwatch
+
AWS CloudWatch
}}" class="nav-cards__item nav-cards__item--ds"> From ae13352956442d578e6b73901803785f33a34b55 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 21 Feb 2019 15:50:18 +0100 Subject: [PATCH 025/244] docs: fix link --- docs/sources/features/datasources/azuremonitor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/features/datasources/azuremonitor.md b/docs/sources/features/datasources/azuremonitor.md index 1e628e8f2a8..4246a7f7c5d 100644 --- a/docs/sources/features/datasources/azuremonitor.md +++ b/docs/sources/features/datasources/azuremonitor.md @@ -20,7 +20,7 @@ The Azure Monitor Datasource supports multiple services in the Azure cloud: - **Azure Monitor** is the platform service that provides a single source for monitoring Azure resources. [Read more about Querying the Azure Monitor Service]({{< relref "#querying-the-azure-monitor-service" >}}). - **Application Insights** is an extensible Application Performance Management (APM) service for web developers on multiple platforms and can be used to monitor your live web application - it will automatically detect performance anomalies. [Read more about Querying the Application Insights Service]({{< relref "#querying-the-application-insights-service" >}}). -- **Azure Log Analytics** (or Azure Logs) gives you access to log data collected by Azure Monitor. [Read more about Querying the Azure Log Analytics Service]({{< relref "#querying-the-azure-monitor-service" >}}). +- **Azure Log Analytics** (or Azure Logs) gives you access to log data collected by Azure Monitor. [Read more about Querying the Azure Log Analytics Service]({{< relref "#querying-the-azure-log-analytics-service" >}}). - **Application Insights Analytics** allows you to query [Application Insights data](https://docs.microsoft.com/en-us/azure/azure-monitor/app/analytics) using the same query language used for Azure Log Analytics. [Read more about Querying the Application Insights Analytics Service]({{< relref "#writing-analytics-queries-for-the-application-insights-service" >}}). ## Adding the data source to Grafana From c1bacd630fe8ee3b2b929e4d0e96fd60eaaafebf Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 15:56:57 +0100 Subject: [PATCH 026/244] Make published package public by default --- scripts/cli/tasks/grafanaui.release.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts index b8569858e2b..412c96e7fb5 100644 --- a/scripts/cli/tasks/grafanaui.release.ts +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -87,7 +87,7 @@ const publishPackage = async (name: string, version: string) => { const spinner = startSpinner(`Publishing ${name} @ ${version} to npm registry...`); try { - await execa('npm', ['publish']); + await execa('npm', ['publish', '--access', 'public']); spinner.succeed(); } catch (e) { console.log(e); From ca9a528a62e1a27249527c14f13b8d24bc2df4c6 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 21 Feb 2019 16:12:25 +0100 Subject: [PATCH 027/244] docs: layout fixes --- docs/sources/features/datasources/azuremonitor.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/sources/features/datasources/azuremonitor.md b/docs/sources/features/datasources/azuremonitor.md index 4246a7f7c5d..940db0973f2 100644 --- a/docs/sources/features/datasources/azuremonitor.md +++ b/docs/sources/features/datasources/azuremonitor.md @@ -33,7 +33,7 @@ The datasource can access metrics from four different services. You can configur 1. Accessed from the Grafana main menu, newly installed data sources can be added immediately within the Data Sources section. Next, click the "Add data source" button in the upper right. The data source will be available for selection in the Type select box. -2. Select Azure Monitor from the Type dropdown: +2. Select Azure Monitor from the Type dropdown:
![Data Source Type](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_1_select_type.png) 3. In the name field, fill in a name for the data source. It can be anything. Some suggestions are Azure Monitor or App Insights. @@ -43,8 +43,8 @@ The datasource can access metrics from four different services. You can configur - **Client Id** (Azure Active Directory -> App Registrations -> Choose your app -> Application ID) - **Client Secret** ( Azure Active Directory -> App Registrations -> Choose your app -> Keys) -5. Paste these four items into the fields in the Azure Monitor API Details section: - ![Azure Monitor API Details](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_2_azure_monitor_api_details.png) +5. Paste these four items into the fields in the Azure Monitor API Details section:
+![Azure Monitor API Details](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_2_azure_monitor_api_details.png) 6. If you are also using the Azure Log Analytics service, then you need to specify these two config values (or you can reuse the Client Id and Secret from the previous step). - Client Id (Azure Active Directory -> App Registrations -> Choose your app -> Application ID) @@ -54,11 +54,11 @@ The datasource can access metrics from four different services. You can configur - Application ID - API Key -8. Paste these two items into the appropriate fields in the Application Insights API Details section: - ![Application Insights API Details](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_3_app_insights_api_details.png) +8. Paste these two items into the appropriate fields in the Application Insights API Details section:
+![Application Insights API Details](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_3_app_insights_api_details.png) -9. Test that the configuration details are correct by clicking on the "Save & Test" button: - ![Azure Monitor API Details](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_4_save_and_test.png) +9. Test that the configuration details are correct by clicking on the "Save & Test" button:
+![Azure Monitor API Details](https://raw.githubusercontent.com/grafana/azure-monitor-datasource/master/src/img/config_4_save_and_test.png) Alternatively on step 4 if creating a new Azure Active Directory App, use the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/?view=azure-cli-latest): From f471be2453dc235b7f3113980d110801f5f250a0 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 16:37:42 +0100 Subject: [PATCH 028/244] Bring back plugins page styles --- public/sass/_grafana.scss | 1 + public/sass/pages/_plugins.scss | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 public/sass/pages/_plugins.scss diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 3170de5ba1a..8928523f2be 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -113,5 +113,6 @@ @import 'pages/styleguide'; @import 'pages/errorpage'; @import 'pages/explore'; +@import 'pages/plugins'; @import 'old_responsive'; @import 'components/view_states.scss'; diff --git a/public/sass/pages/_plugins.scss b/public/sass/pages/_plugins.scss new file mode 100644 index 00000000000..2570eb6c791 --- /dev/null +++ b/public/sass/pages/_plugins.scss @@ -0,0 +1,31 @@ +.get-more-plugins-link { + color: $gray-3; + font-size: $font-size-sm; + position: relative; + top: 1.2rem; + + &:hover { + color: $link-hover-color; + } + + img { + vertical-align: top; + } +} + +@include media-breakpoint-down(sm) { + .get-more-plugins-link { + display: none; + } +} + +.plugin-info-list-item { + white-space: nowrap; + max-width: $page-sidebar-width; + text-overflow: ellipsis; + overflow: hidden; + + img { + width: 16px; + } +} From 7ea361513da646d249daf81355940d18c99722aa Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 21 Feb 2019 17:39:03 +0100 Subject: [PATCH 029/244] docs: landing page update link to what's new in v6.0 add azure monitor and loki to datasource list change stackdriver icon to a bigger one --- docs/sources/index.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/sources/index.md b/docs/sources/index.md index 7b8bfacd6f7..a0b1ff42ea0 100644 --- a/docs/sources/index.md +++ b/docs/sources/index.md @@ -60,9 +60,9 @@ aliases = ["v1.1", "guides/reference/admin"]

Provisioning

A guide to help you automate your Grafana setup & configuration.

- }}" class="nav-cards__item nav-cards__item--guide"> -

What's new in v5.4

-

Article on all the new cool features and enhancements in v5.4

+
}}" class="nav-cards__item nav-cards__item--guide"> +

What's new in v6.0

+

Article on all the new cool features and enhancements in v6.0

}}" class="nav-cards__item nav-cards__item--guide">

Screencasts

@@ -89,13 +89,21 @@ aliases = ["v1.1", "guides/reference/admin"]
Prometheus
}}" class="nav-cards__item nav-cards__item--ds"> - +
Google Stackdriver
}}" class="nav-cards__item nav-cards__item--ds">
AWS CloudWatch
+ }}" class="nav-cards__item nav-cards__item--ds"> + +
Azure Monitor
+
+ }}" class="nav-cards__item nav-cards__item--ds"> + +
Loki
+
}}" class="nav-cards__item nav-cards__item--ds">
MySQL
From 10916d9192f5e702a817dee4bcdea1598d5ab80c Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 17:45:52 +0100 Subject: [PATCH 030/244] Update grafana/ui readme --- packages/grafana-ui/README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/grafana-ui/README.md b/packages/grafana-ui/README.md index 1413965f7da..b62d19d2432 100644 --- a/packages/grafana-ui/README.md +++ b/packages/grafana-ui/README.md @@ -1,3 +1,13 @@ -# Grafana (WIP) shared component library +# Grafana UI components library -Used by internal & external plugins. +@grafana/ui is a collection of components used by [Grafana](https://github.com/grafana/grafana) + +Our goal is to deliver Grafana's common UI elements for plugins developers and contributors. + +See [package source](https://github.com/grafana/grafana/tree/master/packages/grafana-ui) for more details. + +## Installation + +`yarn add @grafana/ui` + +`npm install @grafana/ui` From c1d0f11c470d3e81d2d8cf37be555068fd567913 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 21 Feb 2019 22:22:35 +0100 Subject: [PATCH 031/244] grafana/ui 1.0.0-alpha.0 release --- packages/grafana-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 16a4d5f87b2..2795a4d3090 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/ui", - "version": "0.0.0", + "version": "1.0.0-alpha.0", "description": "Grafana Components Library", "keywords": [ "typescript", From 60fab15335c774b7fec783f920dbf01fdb5a6890 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 22 Feb 2019 00:19:08 +0100 Subject: [PATCH 032/244] update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faa2f49faff..ba2cd6572f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -# 6.0.0-beta3 (unreleased) +# Unreleased + +# 6.0.0-beta3 (2019-02-19) ### Minor * **CLI**: Grafana CLI should preserve permissions for backend binaries for Linux and Darwin [#15500](https://github.com/grafana/grafana/issues/15500) From ad748c05028d49e518653aa6b89cd5972a66effb Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 22 Feb 2019 00:23:10 +0100 Subject: [PATCH 033/244] changelog: add notes about closing #1441 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba2cd6572f3..55292b6d3db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ * **AzureMonitor**: improve autocomplete for Log Analytics and App Insights editor [#15131](https://github.com/grafana/grafana/issues/15131) * **LDAP**: Fix IPA/FreeIPA v4.6.4 does not allow LDAP searches with empty attributes [#14432](https://github.com/grafana/grafana/issues/14432) * **Provisioning**: Allow testing data sources that were added by config [#12164](https://github.com/grafana/grafana/issues/12164) +* **Security**: Fix CSRF Token validation for POSTs [#1441](https://github.com/grafana/grafana/issues/1441) ### Breaking changes From d358088776274496e2829fad3ddfaa111fc7f643 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 22 Feb 2019 00:29:46 +0100 Subject: [PATCH 034/244] changelog: add notes about closing #15303 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55292b6d3db..3fea588259f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ * **Snapshots**: Enable deletion of public snapshot [#14109](https://github.com/grafana/grafana/issues/14109) * **Provisioning**: Provisioning support for alert notifiers [#10487](https://github.com/grafana/grafana/issues/10487), thx [@pbakulev](https://github.com/pbakulev) * **Explore**: A whole new way to do ad-hoc metric queries and exploration. Split view in half and compare metrics & logs and much much more. [Read more here](http://docs.grafana.org/features/explore/) +* **Auth**: Replace remember me cookie solution for Grafana's builtin, LDAP and OAuth authentication with a solution based on short-lived tokens [#15303](https://github.com/grafana/grafana/issues/15303) ### Minor From 05d5e796d76f5af73f7990f8571fe463a091acef Mon Sep 17 00:00:00 2001 From: ijin08 Date: Fri, 22 Feb 2019 08:33:07 +0100 Subject: [PATCH 035/244] removed color in color variables names --- .../src/themes/_variables.dark.scss.tmpl.ts | 20 ++++++++-------- .../src/themes/_variables.light.scss.tmpl.ts | 20 ++++++++-------- packages/grafana-ui/src/themes/dark.ts | 20 ++++++++-------- packages/grafana-ui/src/themes/light.ts | 20 ++++++++-------- packages/grafana-ui/src/types/theme.ts | 24 +++++++++++-------- 5 files changed, 54 insertions(+), 50 deletions(-) diff --git a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts index d6df6f51374..54c00f9237e 100644 --- a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts @@ -76,12 +76,12 @@ $critical: ${theme.colors.critical}; $body-bg: ${theme.colors.bodyBg}; $page-bg: ${theme.colors.pageBg}; -$body-color: ${theme.colors.bodyColor}; -$text-color: ${theme.colors.textColor}; -$text-color-strong: ${theme.colors.textColorStrong}; -$text-color-weak: ${theme.colors.textColorWeak}; -$text-color-faint: ${theme.colors.textColorFaint}; -$text-color-emphasis: ${theme.colors.textColorEmphasis}; +$body-color: ${theme.colors.body}; +$text-color: ${theme.colors.text}; +$text-color-strong: ${theme.colors.textStrong}; +$text-color-weak: ${theme.colors.textWeak}; +$text-color-faint: ${theme.colors.textFaint}; +$text-color-emphasis: ${theme.colors.textEmphasis}; $text-shadow-faint: 1px 1px 4px rgb(45, 45, 45); $textShadow: none; @@ -99,10 +99,10 @@ $edit-gradient: linear-gradient(180deg, $dark-2 50%, $input-black); // Links // ------------------------- -$link-color: ${theme.colors.linkColor}; -$link-color-disabled: ${theme.colors.linkColorDisabled}; -$link-hover-color: ${theme.colors.linkColorHover}; -$external-link-color: ${theme.colors.linkColorExternal}; +$link-color: ${theme.colors.link}; +$link-color-disabled: ${theme.colors.linkDisabled}; +$link-hover-color: ${theme.colors.linkHover}; +$external-link-color: ${theme.colors.linkExternal}; // Typography // ------------------------- diff --git a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts index eb48ab5cc02..e90299f619c 100644 --- a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts @@ -68,12 +68,12 @@ $critical: ${theme.colors.critical}; $body-bg: ${theme.colors.bodyBg}; $page-bg: ${theme.colors.pageBg}; -$body-color: ${theme.colors.bodyColor}; -$text-color: ${theme.colors.textColor}; -$text-color-strong: ${theme.colors.textColorStrong}; -$text-color-weak: ${theme.colors.textColorWeak}; -$text-color-faint: ${theme.colors.textColorFaint}; -$text-color-emphasis: ${theme.colors.textColorEmphasis}; +$body-color: ${theme.colors.body}; +$text-color: ${theme.colors.text}; +$text-color-strong: ${theme.colors.textStrong}; +$text-color-weak: ${theme.colors.textWeak}; +$text-color-faint: ${theme.colors.textFaint}; +$text-color-emphasis: ${theme.colors.textEmphasis}; $text-shadow-faint: none; @@ -85,10 +85,10 @@ $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); // Links // ------------------------- -$link-color: ${theme.colors.linkColor}; -$link-color-disabled: ${theme.colors.linkColorDisabled}; -$link-hover-color: ${theme.colors.linkColorHover}; -$external-link-color: ${theme.colors.linkColorExternal}; +$link-color: ${theme.colors.link}; +$link-color-disabled: ${theme.colors.linkDisabled}; +$link-hover-color: ${theme.colors.linkHover}; +$external-link-color: ${theme.colors.linkExternal}; // Typography // ------------------------- diff --git a/packages/grafana-ui/src/themes/dark.ts b/packages/grafana-ui/src/themes/dark.ts index 3e8057a4acd..1e424c97154 100644 --- a/packages/grafana-ui/src/themes/dark.ts +++ b/packages/grafana-ui/src/themes/dark.ts @@ -60,16 +60,16 @@ const darkTheme: GrafanaTheme = { critical: basicColors.redBase, bodyBg: basicColors.dark2, pageBg: basicColors.dark2, - bodyColor: basicColors.gray4, - textColor: basicColors.gray4, - textColorStrong: basicColors.white, - textColorWeak: basicColors.gray2, - textColorEmphasis: basicColors.gray5, - textColorFaint: basicColors.dark5, - linkColor: new tinycolor(basicColors.white).darken(11).toString(), - linkColorDisabled: new tinycolor(basicColors.white).darken(11).toString(), - linkColorHover: basicColors.white, - linkColorExternal: basicColors.blue, + body: basicColors.gray4, + text: basicColors.gray4, + textStrong: basicColors.white, + textWeak: basicColors.gray2, + textEmphasis: basicColors.gray5, + textFaint: basicColors.dark5, + link: new tinycolor(basicColors.white).darken(11).toString(), + linkDisabled: new tinycolor(basicColors.white).darken(11).toString(), + linkHover: basicColors.white, + linkExternal: basicColors.blue, headingColor: new tinycolor(basicColors.white).darken(11).toString(), }, background: { diff --git a/packages/grafana-ui/src/themes/light.ts b/packages/grafana-ui/src/themes/light.ts index 2461505d507..a3994fc7458 100644 --- a/packages/grafana-ui/src/themes/light.ts +++ b/packages/grafana-ui/src/themes/light.ts @@ -61,16 +61,16 @@ const lightTheme: GrafanaTheme = { critical: basicColors.redShade, bodyBg: basicColors.gray7, pageBg: basicColors.gray7, - bodyColor: basicColors.gray1, - textColor: basicColors.gray1, - textColorStrong: basicColors.dark2, - textColorWeak: basicColors.gray2, - textColorEmphasis: basicColors.gray5, - textColorFaint: basicColors.dark4, - linkColor: basicColors.gray1, - linkColorDisabled: new tinycolor(basicColors.gray1).lighten(30).toString(), - linkColorHover: new tinycolor(basicColors.gray1).darken(20).toString(), - linkColorExternal: basicColors.blueLight, + body: basicColors.gray1, + text: basicColors.gray1, + textStrong: basicColors.dark2, + textWeak: basicColors.gray2, + textEmphasis: basicColors.gray5, + textFaint: basicColors.dark4, + link: basicColors.gray1, + linkDisabled: new tinycolor(basicColors.gray1).lighten(30).toString(), + linkHover: new tinycolor(basicColors.gray1).darken(20).toString(), + linkExternal: basicColors.blueLight, headingColor: basicColors.gray1, }, background: { diff --git a/packages/grafana-ui/src/types/theme.ts b/packages/grafana-ui/src/types/theme.ts index f7adbf80247..30f1bf4685b 100644 --- a/packages/grafana-ui/src/types/theme.ts +++ b/packages/grafana-ui/src/types/theme.ts @@ -123,19 +123,23 @@ export interface GrafanaTheme extends GrafanaThemeCommons { warn: string; critical: string; + // Link colors + link: string; + linkDisabled: string; + linkHover: string; + linkExternal: string; + + // Text colors + body: string; + text: string; + textStrong: string; + textWeak: string; + textFaint: string; + textEmphasis: string; + // TODO: move to background section bodyBg: string; pageBg: string; - bodyColor: string; - textColor: string; - textColorStrong: string; - textColorWeak: string; - textColorFaint: string; - textColorEmphasis: string; - linkColor: string; - linkColorDisabled: string; - linkColorHover: string; - linkColorExternal: string; headingColor: string; }; } From 34f6d9ab8e5246807c80ca5878fc85d509638add Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Fri, 22 Feb 2019 11:30:45 +0100 Subject: [PATCH 036/244] grafana/ui 6.0.0-alpha.0 release version bump --- packages/grafana-ui/CHANGELOG.md | 4 +++- packages/grafana-ui/README.md | 4 ++++ packages/grafana-ui/package.json | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/grafana-ui/CHANGELOG.md b/packages/grafana-ui/CHANGELOG.md index 472c2df8ad8..cbc7afc2609 100644 --- a/packages/grafana-ui/CHANGELOG.md +++ b/packages/grafana-ui/CHANGELOG.md @@ -1,4 +1,6 @@ -# 1.0.0-alpha.0 (2019-02-21) +# 6.0.0-alpha.0 (2019-02-22) +Version update to 6.0.0 to keep @grafana/ui version in sync with [Grafana](https://github.com/grafana/grafana) +# 1.0.0-alpha.0 (2019-02-21) First public release diff --git a/packages/grafana-ui/README.md b/packages/grafana-ui/README.md index b62d19d2432..fa482003253 100644 --- a/packages/grafana-ui/README.md +++ b/packages/grafana-ui/README.md @@ -11,3 +11,7 @@ See [package source](https://github.com/grafana/grafana/tree/master/packages/gra `yarn add @grafana/ui` `npm install @grafana/ui` + +## Versioning +To limit the confusion related to @grafana/ui and Grafana versioning we decided to keep the major version in sync between those two. +This means, that first version of @grafana/ui is taged with 6.0.0-alpha.0 to keep version in sync with Grafana 6.0 release. diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 2795a4d3090..2c4db3e30af 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/ui", - "version": "1.0.0-alpha.0", + "version": "6.0.0-alpha.0", "description": "Grafana Components Library", "keywords": [ "typescript", From 425636ff7082e7a8bf8d3bfb1c20c7a1a5fd81a7 Mon Sep 17 00:00:00 2001 From: Valentin Agachi Date: Fri, 22 Feb 2019 20:13:10 +0800 Subject: [PATCH 037/244] Improve Loki logs render with ANSI colors (#15558) * Improve Loki logs render with ANSI colors * fixup! Improve Loki logs render with ANSI colors * fixup! Improve Loki logs render with ANSI colors * fixup! Improve Loki logs render with ANSI colors --- public/app/core/logs_model.ts | 6 ++- .../app/features/explore/LogMessageAnsi.tsx | 2 +- public/app/features/explore/LogRow.tsx | 18 ++++---- .../loki/result_transformer.test.ts | 46 ++++++++++++++++++- .../datasource/loki/result_transformer.ts | 11 +++-- 5 files changed, 66 insertions(+), 17 deletions(-) diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts index f4ad11e1b0e..a9b96190d9b 100644 --- a/public/app/core/logs_model.ts +++ b/public/app/core/logs_model.ts @@ -22,7 +22,7 @@ export enum LogLevel { dbug = 'debug', debug = 'debug', trace = 'trace', - unkown = 'unkown', + unknown = 'unknown', } export const LogLevelColor = { @@ -32,7 +32,7 @@ export const LogLevelColor = { [LogLevel.info]: colors[0], [LogLevel.debug]: colors[5], [LogLevel.trace]: colors[2], - [LogLevel.unkown]: getThemeColor('#8e8e8e', '#dde4ed'), + [LogLevel.unknown]: getThemeColor('#8e8e8e', '#dde4ed'), }; export interface LogSearchMatch { @@ -44,9 +44,11 @@ export interface LogSearchMatch { export interface LogRowModel { duplicates?: number; entry: string; + hasAnsi: boolean; key: string; // timestamp + labels labels: LogsStreamLabels; logLevel: LogLevel; + raw: string; searchWords?: string[]; timestamp: string; // ISO with nanosec precision timeFromNow: string; diff --git a/public/app/features/explore/LogMessageAnsi.tsx b/public/app/features/explore/LogMessageAnsi.tsx index bed28bc674d..53147656d6a 100644 --- a/public/app/features/explore/LogMessageAnsi.tsx +++ b/public/app/features/explore/LogMessageAnsi.tsx @@ -38,7 +38,7 @@ export class LogMessageAnsi extends PureComponent { prevValue: '', }; - static getDerivedStateFromProps(props, state) { + static getDerivedStateFromProps(props: Props, state: State) { if (props.value === state.prevValue) { return null; } diff --git a/public/app/features/explore/LogRow.tsx b/public/app/features/explore/LogRow.tsx index e5c3ec0558c..d7615446b21 100644 --- a/public/app/features/explore/LogRow.tsx +++ b/public/app/features/explore/LogRow.tsx @@ -5,7 +5,7 @@ import classnames from 'classnames'; import { LogRowModel, LogLabelStatsModel, LogsParser, calculateFieldStats, getParser } from 'app/core/logs_model'; import { LogLabels } from './LogLabels'; -import { findHighlightChunksInText, hasAnsiCodes } from 'app/core/utils/text'; +import { findHighlightChunksInText } from 'app/core/utils/text'; import { LogLabelStats } from './LogLabelStats'; import { LogMessageAnsi } from './LogMessageAnsi'; @@ -130,13 +130,13 @@ export class LogRow extends PureComponent { parsedFieldHighlights, showFieldStats, } = this.state; + const { entry, hasAnsi, raw } = row; const previewHighlights = highlighterExpressions && !_.isEqual(highlighterExpressions, row.searchWords); const highlights = previewHighlights ? highlighterExpressions : row.searchWords; - const needsHighlighter = highlights && highlights.length > 0; + const needsHighlighter = highlights && highlights.length > 0 && highlights[0].length > 0; const highlightClassName = classnames('logs-row__match-highlight', { 'logs-row__match-highlight--preview': previewHighlights, }); - const containsAnsiCodes = hasAnsiCodes(row.entry); return (
@@ -160,25 +160,25 @@ export class LogRow extends PureComponent {
)}
- {containsAnsiCodes && } - {!containsAnsiCodes && parsed && ( + {parsed && ( )} - {!containsAnsiCodes && !parsed && needsHighlighter && ( + {!parsed && needsHighlighter && ( )} - {!containsAnsiCodes && !parsed && !needsHighlighter && row.entry} + {hasAnsi && !parsed && !needsHighlighter && } + {!hasAnsi && !parsed && !needsHighlighter && entry} {showFieldStats && (
{ it('returns no log level on empty line', () => { - expect(getLogLevel('')).toBe(LogLevel.unkown); + expect(getLogLevel('')).toBe(LogLevel.unknown); }); it('returns no log level on when level is part of a word', () => { - expect(getLogLevel('this is information')).toBe(LogLevel.unkown); + expect(getLogLevel('this is information')).toBe(LogLevel.unknown); }); it('returns same log level for long and short version', () => { @@ -158,4 +158,46 @@ describe('mergeStreamsToLogs()', () => { }, ]); }); + + it('detects ANSI codes', () => { + expect( + mergeStreamsToLogs([ + { + labels: '{foo="bar"}', + entries: [ + { + line: "foo: 'bar'", + ts: '1970-01-01T00:00:00Z', + }, + ], + }, + { + labels: '{bar="foo"}', + entries: [ + { + line: "bar: 'foo'", + ts: '1970-01-01T00:00:00Z', + }, + ], + }, + ]).rows + ).toMatchObject([ + { + entry: "bar: 'foo'", + hasAnsi: false, + key: 'EK1970-01-01T00:00:00Z{bar="foo"}', + labels: { bar: 'foo' }, + logLevel: 'unknown', + raw: "bar: 'foo'", + }, + { + entry: "foo: 'bar'", + hasAnsi: true, + key: 'EK1970-01-01T00:00:00Z{foo="bar"}', + labels: { foo: 'bar' }, + logLevel: 'unknown', + raw: "foo: 'bar'", + }, + ]); + }); }); diff --git a/public/app/plugins/datasource/loki/result_transformer.ts b/public/app/plugins/datasource/loki/result_transformer.ts index 9cd4ee0779b..1fbabba1789 100644 --- a/public/app/plugins/datasource/loki/result_transformer.ts +++ b/public/app/plugins/datasource/loki/result_transformer.ts @@ -1,3 +1,4 @@ +import ansicolor from 'ansicolor'; import _ from 'lodash'; import moment from 'moment'; @@ -11,6 +12,7 @@ import { LogsStreamLabels, LogsMetaKind, } from 'app/core/logs_model'; +import { hasAnsiCodes } from 'app/core/utils/text'; import { DEFAULT_MAX_LINES } from './datasource'; /** @@ -21,7 +23,7 @@ import { DEFAULT_MAX_LINES } from './datasource'; */ export function getLogLevel(line: string): LogLevel { if (!line) { - return LogLevel.unkown; + return LogLevel.unknown; } let level: LogLevel; Object.keys(LogLevel).forEach(key => { @@ -33,7 +35,7 @@ export function getLogLevel(line: string): LogLevel { } }); if (!level) { - level = LogLevel.unkown; + level = LogLevel.unknown; } return level; } @@ -125,6 +127,7 @@ export function processEntry( const timeFromNow = time.fromNow(); const timeLocal = time.format('YYYY-MM-DD HH:mm:ss'); const logLevel = getLogLevel(line); + const hasAnsi = hasAnsiCodes(line); return { key, @@ -133,7 +136,9 @@ export function processEntry( timeEpochMs, timeLocal, uniqueLabels, - entry: line, + hasAnsi, + entry: hasAnsi ? ansicolor.strip(line) : line, + raw: line, labels: parsedLabels, searchWords: search ? [search] : [], timestamp: ts, From dafcfd70a709f0111ccb6b3bd74d4659c3f51a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 22 Feb 2019 09:11:21 +0100 Subject: [PATCH 038/244] Fixed bug with getting teams for user --- pkg/api/user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/api/user.go b/pkg/api/user.go index 6db9c6f6baf..9eb7e75eab0 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -121,7 +121,7 @@ func GetUserTeams(c *m.ReqContext) Response { return getUserTeamList(c.OrgId, c.ParamsInt64(":id")) } -func getUserTeamList(userID int64, orgID int64) Response { +func getUserTeamList(orgID int64, userID int64) Response { query := m.GetTeamsByUserQuery{OrgId: orgID, UserId: userID} if err := bus.Dispatch(&query); err != nil { From 09cd173e9249d5e54cf3c304d2430f0390c68f45 Mon Sep 17 00:00:00 2001 From: Maddin-619 Date: Fri, 22 Feb 2019 14:19:15 +0100 Subject: [PATCH 039/244] updates all cols except created so user and password of the database can be chaned to no user and password --- pkg/services/sqlstore/datasource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/sqlstore/datasource.go b/pkg/services/sqlstore/datasource.go index ccab1106880..65d8369d763 100644 --- a/pkg/services/sqlstore/datasource.go +++ b/pkg/services/sqlstore/datasource.go @@ -180,7 +180,7 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error { updateSession = sess.Where("id=? and org_id=?", ds.Id, ds.OrgId) } - affected, err := updateSession.Update(ds) + affected, err := updateSession.AllCols().Omit("created").Update(ds) if err != nil { return err } From 9c9691f7af49f146403a443b78f6e117350399be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 22 Feb 2019 11:18:07 +0100 Subject: [PATCH 040/244] Added feature toggle editors_can_own --- pkg/api/frontendsettings.go | 1 + pkg/setting/setting.go | 2 ++ public/app/core/config.ts | 2 ++ 3 files changed, 5 insertions(+) diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index d13f0a26ab0..48159e568b6 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -167,6 +167,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf "externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl, "externalUserMngLinkName": setting.ExternalUserMngLinkName, "viewersCanEdit": setting.ViewersCanEdit, + "editorsCanOwn": setting.EditorsCanOwn, "disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml, "buildInfo": map[string]interface{}{ "version": setting.BuildVersion, diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 21899482529..a18b1d14e6a 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -118,6 +118,7 @@ var ( ExternalUserMngInfo string OAuthAutoLogin bool ViewersCanEdit bool + EditorsCanOwn bool // Http auth AdminUser string @@ -657,6 +658,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { ExternalUserMngLinkName = users.Key("external_manage_link_name").String() ExternalUserMngInfo = users.Key("external_manage_info").String() ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false) + EditorsCanOwn = users.Key("editors_can_own").MustBool(false) // auth auth := iniFile.Section("auth") diff --git a/public/app/core/config.ts b/public/app/core/config.ts index 8839a6f7942..8fefc1aeb0a 100644 --- a/public/app/core/config.ts +++ b/public/app/core/config.ts @@ -36,6 +36,7 @@ export class Settings { loginHint: any; loginError: any; viewersCanEdit: boolean; + editorsCanOwn: boolean; disableSanitizeHtml: boolean; theme: GrafanaTheme; @@ -57,6 +58,7 @@ export class Settings { isEnterprise: false, }, viewersCanEdit: false, + editorsCanOwn: false, disableSanitizeHtml: false, }; From 769ad21e1691c3f7e5c07e499944daea25c85e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 22 Feb 2019 12:11:26 +0100 Subject: [PATCH 041/244] Moved variable to config struct after PR comments --- pkg/api/frontendsettings.go | 2 +- pkg/setting/setting.go | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 48159e568b6..ff84f290a4f 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -167,7 +167,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf "externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl, "externalUserMngLinkName": setting.ExternalUserMngLinkName, "viewersCanEdit": setting.ViewersCanEdit, - "editorsCanOwn": setting.EditorsCanOwn, + "editorsCanOwn": hs.Cfg.EditorsCanOwn, "disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml, "buildInfo": map[string]interface{}{ "version": setting.BuildVersion, diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index a18b1d14e6a..5d44a3585dc 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -118,7 +118,6 @@ var ( ExternalUserMngInfo string OAuthAutoLogin bool ViewersCanEdit bool - EditorsCanOwn bool // Http auth AdminUser string @@ -238,6 +237,9 @@ type Cfg struct { LoginMaxInactiveLifetimeDays int LoginMaxLifetimeDays int TokenRotationIntervalMinutes int + + // User + EditorsCanOwn bool } type CommandLineArgs struct { @@ -645,6 +647,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { AdminUser = security.Key("admin_user").String() AdminPassword = security.Key("admin_password").String() + // users users := iniFile.Section("users") AllowUserSignUp = users.Key("allow_sign_up").MustBool(true) AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true) @@ -658,7 +661,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { ExternalUserMngLinkName = users.Key("external_manage_link_name").String() ExternalUserMngInfo = users.Key("external_manage_info").String() ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false) - EditorsCanOwn = users.Key("editors_can_own").MustBool(false) + cfg.EditorsCanOwn = users.Key("editors_can_own").MustBool(false) // auth auth := iniFile.Section("auth") From 8f6208248245d13bb165aa9665ed5e5e97e73b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 22 Feb 2019 13:54:15 +0100 Subject: [PATCH 042/244] Added feature toggle to defaults.ini and sample.ini after PR comments --- conf/defaults.ini | 3 +++ conf/sample.ini | 3 +++ 2 files changed, 6 insertions(+) diff --git a/conf/defaults.ini b/conf/defaults.ini index a87aba10adb..df02e01235b 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -243,6 +243,9 @@ external_manage_info = # Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard. viewers_can_edit = false +# Editors can administrate dashboard, folders and teams they create +editors_can_own = false + [auth] # Login cookie name login_cookie_name = grafana_session diff --git a/conf/sample.ini b/conf/sample.ini index dbbb3593f0f..57ff82181de 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -223,6 +223,9 @@ log_queries = # Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard. ;viewers_can_edit = false +# Editors can administrate dashboard, folders and teams they create +;editors_can_own = false + [auth] # Login cookie name ;login_cookie_name = grafana_session From 31ae2813a750667387a4aaf3d3d39ae6c3915353 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 22 Feb 2019 18:45:24 +0100 Subject: [PATCH 043/244] docs: tweaks to AzureMonitor docs --- docs/sources/features/datasources/azuremonitor.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/sources/features/datasources/azuremonitor.md b/docs/sources/features/datasources/azuremonitor.md index 940db0973f2..d72f5273925 100644 --- a/docs/sources/features/datasources/azuremonitor.md +++ b/docs/sources/features/datasources/azuremonitor.md @@ -18,10 +18,10 @@ As of Grafana 6.0, the Azure Monitor plugin has been moved into Grafana so it no The Azure Monitor Datasource supports multiple services in the Azure cloud: -- **Azure Monitor** is the platform service that provides a single source for monitoring Azure resources. [Read more about Querying the Azure Monitor Service]({{< relref "#querying-the-azure-monitor-service" >}}). -- **Application Insights** is an extensible Application Performance Management (APM) service for web developers on multiple platforms and can be used to monitor your live web application - it will automatically detect performance anomalies. [Read more about Querying the Application Insights Service]({{< relref "#querying-the-application-insights-service" >}}). -- **Azure Log Analytics** (or Azure Logs) gives you access to log data collected by Azure Monitor. [Read more about Querying the Azure Log Analytics Service]({{< relref "#querying-the-azure-log-analytics-service" >}}). -- **Application Insights Analytics** allows you to query [Application Insights data](https://docs.microsoft.com/en-us/azure/azure-monitor/app/analytics) using the same query language used for Azure Log Analytics. [Read more about Querying the Application Insights Analytics Service]({{< relref "#writing-analytics-queries-for-the-application-insights-service" >}}). +- **[Azure Monitor]({{< relref "#querying-the-azure-monitor-service" >}})** is the platform service that provides a single source for monitoring Azure resources. +- **[Application Insights]({{< relref "#querying-the-application-insights-service" >}})** is an extensible Application Performance Management (APM) service for web developers on multiple platforms and can be used to monitor your live web application - it will automatically detect performance anomalies. +- **[Azure Log Analytics]({{< relref "#querying-the-azure-log-analytics-service" >}})** (or Azure Logs) gives you access to log data collected by Azure Monitor. +- **[Application Insights Analytics]({{< relref "#writing-analytics-queries-for-the-application-insights-service" >}})** allows you to query [Application Insights data](https://docs.microsoft.com/en-us/azure/azure-monitor/app/analytics) using the same query language used for Azure Log Analytics. ## Adding the data source to Grafana @@ -133,7 +133,7 @@ types of template variables. ### Azure Monitor Metrics Whitelist -Not all metrics returned by the Azure Monitor API have values. The Grafana datasource has a whitelist to only return metric names if it is possible they might have values. This whitelist is updated regularly as new services and metrics are added to the Azure cloud. You can find the current whitelist [here](https://github.com/grafana/grafana/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/supported_namespaces.ts). +Not all metrics returned by the Azure Monitor API have values. The Grafana datasource has a whitelist to only return metric names if it is possible they might have values. This whitelist is updated regularly as new services and metrics are added to the Azure cloud. You can find the current whitelist [here](https://github.com/grafana/grafana/blob/master/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/supported_namespaces.ts). ### Azure Monitor Alerting @@ -248,7 +248,7 @@ There are also some Grafana variables that can be used in Azure Log Analytics qu - `$__from` - Returns the From datetime from the Grafana picker. Example: `datetime(2018-06-05T18:09:58.907Z)`. - `$__to` - Returns the From datetime from the Grafana picker. Example: `datetime(2018-06-05T20:09:58.907Z)`. -- `$__interval` - Grafana calculates the minimum time grain that can be used to group by time in queries. More details on how it works [here](http://docs.grafana.org/reference/templating/#the-interval-variable). It returns a time grain like `5m` or `1h` that can be used in the bin function. E.g. `summarize count() by bin(TimeGenerated, $__interval)` +- `$__interval` - Grafana calculates the minimum time grain that can be used to group by time in queries. More details on how it works [here]({{< relref "reference/templating.md#interval-variables" >}}). It returns a time grain like `5m` or `1h` that can be used in the bin function. E.g. `summarize count() by bin(TimeGenerated, $__interval)` ### Azure Log Analytics Alerting From f768808b6ee7952ca7ac3c5ae2bcc4d07422cc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 23 Feb 2019 08:59:00 +0100 Subject: [PATCH 044/244] Fixed value dropdown not updating when it's current value updates, fixes #15566 --- .../app/core/directives/value_select_dropdown.ts | 16 ++++++++-------- .../dashboard/components/SubMenu/template.html | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/public/app/core/directives/value_select_dropdown.ts b/public/app/core/directives/value_select_dropdown.ts index a75ecd46ad0..0df1b2758be 100644 --- a/public/app/core/directives/value_select_dropdown.ts +++ b/public/app/core/directives/value_select_dropdown.ts @@ -240,7 +240,7 @@ export class ValueSelectDropdownCtrl { /** @ngInject */ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) { return { - scope: { variable: '=', onUpdated: '&' }, + scope: { dashboard: '=', variable: '=', onUpdated: '&' }, templateUrl: 'public/app/partials/valueSelectDropdown.html', controller: 'ValueSelectDropdownCtrl', controllerAs: 'vm', @@ -288,13 +288,13 @@ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) { } }); - const cleanUp = $rootScope.$on('template-variable-value-updated', () => { - scope.vm.updateLinkText(); - }); - - scope.$on('$destroy', () => { - cleanUp(); - }); + scope.vm.dashboard.on( + 'template-variable-value-updated', + () => { + scope.vm.updateLinkText(); + }, + scope + ); scope.vm.init(); }, diff --git a/public/app/features/dashboard/components/SubMenu/template.html b/public/app/features/dashboard/components/SubMenu/template.html index 1ccbfcc915c..fd52deaf403 100644 --- a/public/app/features/dashboard/components/SubMenu/template.html +++ b/public/app/features/dashboard/components/SubMenu/template.html @@ -4,7 +4,7 @@ - +
From 3c911cf20866def351b75c6d521f87f6d104f27f Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Sat, 23 Feb 2019 11:05:11 +0100 Subject: [PATCH 045/244] docs: link to azure monitor from what's new in v6.0 --- docs/sources/guides/whats-new-in-v6-0.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sources/guides/whats-new-in-v6-0.md b/docs/sources/guides/whats-new-in-v6-0.md index 2ba7e86b385..c7091fd855e 100644 --- a/docs/sources/guides/whats-new-in-v6-0.md +++ b/docs/sources/guides/whats-new-in-v6-0.md @@ -126,6 +126,8 @@ One of the goals of the Grafana v6.0 release is to add support for the three maj The Azure Monitor datasource integrates four Azure services with Grafana - Azure Monitor, Azure Log Analytics, Azure Application Insights and Azure Application Insights Analytics. +Please read [Using Azure Monitor in Grafana documentation](/features/datasources/azuremonitor/) for more detailed information on how to get started and use it. + ## Provisioning support for alert notifiers Grafana now added support for provisioning alert notifiers from configuration files. Allowing operators to provision notifiers without using the UI or the API. A new field called `uid` has been introduced which is a string identifier that the administrator can set themselves. Same kind of identifier used for dashboards since v5.0. This feature makes it possible to use the same notifier configuration in multiple environments and refer to notifiers in dashboard json by a string identifier instead of the numeric id which depends on insert order and how many notifiers that exists in the instance. From e5ce7591677976768fa3877eac240a4a3f94d9be Mon Sep 17 00:00:00 2001 From: ryan Date: Sat, 23 Feb 2019 21:53:20 -0800 Subject: [PATCH 046/244] update --- public/app/plugins/panel/text2/TextPanel.tsx | 100 ++++++++++++++++++ .../plugins/panel/text2/TextPanelEditor.tsx | 32 ++++++ public/app/plugins/panel/text2/module.tsx | 18 ++-- public/app/plugins/panel/text2/types.ts | 14 +++ 4 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 public/app/plugins/panel/text2/TextPanel.tsx create mode 100644 public/app/plugins/panel/text2/TextPanelEditor.tsx create mode 100644 public/app/plugins/panel/text2/types.ts diff --git a/public/app/plugins/panel/text2/TextPanel.tsx b/public/app/plugins/panel/text2/TextPanel.tsx new file mode 100644 index 00000000000..9e9210df119 --- /dev/null +++ b/public/app/plugins/panel/text2/TextPanel.tsx @@ -0,0 +1,100 @@ +import React, { Component } from 'react'; + +import Remarkable from 'remarkable'; +import { sanitize } from 'app/core/utils/text'; +import config from 'app/core/config'; +import templateSrv from 'app/features/templating/template_srv'; +import { debounce } from 'lodash'; + +// Types +import { TextOptions } from './types'; +import { PanelProps } from '@grafana/ui/src/types'; + +interface Props extends PanelProps {} +interface State { + html: string; +} + +export class TextPanel extends Component { + remarkable: Remarkable; + + constructor(props) { + super(props); + + // TODO thre must be some better way to start with defualt options! + let opts = props.options; + if (opts && opts['options']) { + opts = opts['options']; + console.log('WEIRD!', opts); + } + + this.state = { + html: this.processContent(opts), + }; + } + + updateHTML = debounce(() => { + const html = this.processContent(this.props.options); + if (html !== this.state.html) { + this.setState({ html }); + } + }, 100); + + componentDidUpdate(prevProps: Props) { + // Since any change could be referenced in a template variable, + // This needs to process everything + this.updateHTML(); + } + + prepareHTML(html: string): string { + const scopedVars = {}; // TODO?? = this.props.; + html = config.disableSanitizeHtml ? html : sanitize(html); + try { + return templateSrv.replace(html, scopedVars); + } catch (e) { + // TODO -- put the error in the header window + console.log('Text panel error: ', e); + return html; + } + } + + prepareText(content: string): string { + return this.prepareHTML( + content + .replace(/&/g, '&') + .replace(/>/g, '>') + .replace(/') + ); + } + + prepareMarkdown(content: string): string { + if (!this.remarkable) { + this.remarkable = new Remarkable(); + } + return this.prepareHTML(this.remarkable.render(content)); + } + + processContent(options: TextOptions): string { + const { mode, content } = options; + + if (!content) { + return ''; + } + + if (mode === 'markdown') { + return this.prepareMarkdown(content); + } + if (mode === 'html') { + return this.prepareHTML(content); + } + + return this.prepareText(content); + } + + render() { + const { html } = this.state; + + return
; + } +} diff --git a/public/app/plugins/panel/text2/TextPanelEditor.tsx b/public/app/plugins/panel/text2/TextPanelEditor.tsx new file mode 100644 index 00000000000..2581384af9a --- /dev/null +++ b/public/app/plugins/panel/text2/TextPanelEditor.tsx @@ -0,0 +1,32 @@ +import React, { PureComponent } from 'react'; +import { PanelEditorProps, PanelOptionsGroup, Select, SelectOptionItem } from '@grafana/ui'; + +import { TextOptions } from './types'; + +export class TextPanelEditor extends PureComponent> { + modes: SelectOptionItem[] = [ + { value: 'markdown', label: 'Markdown' }, + { value: 'text', label: 'Text' }, + { value: 'html', label: 'HTML' }, + ]; + + onModeChange = (item: SelectOptionItem) => this.props.onChange({ ...this.props.options, mode: item.value }); + + onContentChange = evt => this.props.onChange({ ...this.props.options, content: (event.target as any).value }); + + render() { + const { mode, content } = this.props.options; + + return ( + +
+ Mode + '; const buttonTemplate = - ''; @@ -137,9 +137,15 @@ export function dropdownTypeahead2($compile) { menuItems: '=dropdownTypeahead2', dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect', model: '=ngModel', + buttonTemplateClass: '@', }, link: ($scope, elem, attrs) => { const $input = $(inputTemplate); + + if (!$scope.buttonTemplateClass) { + $scope.buttonTemplateClass = 'gf-form-input'; + } + const $button = $(buttonTemplate); const timeoutId = { blur: null, diff --git a/public/app/plugins/datasource/influxdb/partials/query.editor.html b/public/app/plugins/datasource/influxdb/partials/query.editor.html index 658cf18daee..ba99b396e6e 100644 --- a/public/app/plugins/datasource/influxdb/partials/query.editor.html +++ b/public/app/plugins/datasource/influxdb/partials/query.editor.html @@ -1,19 +1,38 @@ - -
+
- +
- +
-
+
- +
@@ -21,108 +40,154 @@
-
+
+
+
+ -
-
- + + +
- - -
+
+ +
-
- -
+
+ +
-
- -
+
+
+
+
-
-
-
-
+
+
+ +
-
-
- -
+
+ + +
-
- - -
+
+ +
-
- -
+
+
+
+
-
-
-
-
+
+
+ -
-
- + + +
- - -
+
+ +
-
- -
- -
-
-
-
+
+
+
+
- +
-
-
+
+
- +
-
-
+
+
- +
-
-
+
+
- +
@@ -133,7 +198,12 @@
- +
@@ -141,15 +211,21 @@
-
+
- +
- diff --git a/public/app/plugins/datasource/mysql/partials/query.editor.html b/public/app/plugins/datasource/mysql/partials/query.editor.html index 0cb47061a9e..25f33b6a534 100644 --- a/public/app/plugins/datasource/mysql/partials/query.editor.html +++ b/public/app/plugins/datasource/mysql/partials/query.editor.html @@ -45,8 +45,10 @@
diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index b5e2b5d87ed..5411427cfe6 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -45,8 +45,10 @@
diff --git a/public/app/plugins/panel/graph/series_overrides_ctrl.ts b/public/app/plugins/panel/graph/series_overrides_ctrl.ts index 90d61a362ab..fdb8f06c270 100644 --- a/public/app/plugins/panel/graph/series_overrides_ctrl.ts +++ b/public/app/plugins/panel/graph/series_overrides_ctrl.ts @@ -11,7 +11,7 @@ export function SeriesOverridesCtrl($scope, $element, popoverSrv) { const option = { text: name, propertyName: propertyName, - index: $scope.overrideMenu.lenght, + index: $scope.overrideMenu.length, values: values, submenu: _.map(values, value => { return { text: String(value), value: value }; diff --git a/public/app/plugins/panel/graph/tab_display.html b/public/app/plugins/panel/graph/tab_display.html index 976b9bcc940..a6287922cfe 100644 --- a/public/app/plugins/panel/graph/tab_display.html +++ b/public/app/plugins/panel/graph/tab_display.html @@ -1,110 +1,194 @@ +
+
+
Draw Modes
+ + + +
+
+
Mode Options
+
+ +
+ +
+
+
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
Hover tooltip
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
-
-
-
Draw Modes
- - - -
-
-
Mode Options
-
- -
- -
-
-
- -
- -
-
- - -
- -
- -
-
-
-
-
Hover tooltip
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
+
+
Stacking & Null value
+ + + + +
+ +
+ +
+
+
+
-
-
Stacking & Null value
- - - - -
- -
- -
-
-
-
+
+
+
+ +
+
+ +
+
+ +
-
-
-
- -
-
- -
-
- -
+
+ + +
-
- - -
- -
-
-
- -
- -
-
-
- -
-
+
+
+
+
+ +
+
+
+ +
+
From 9efe7674d6155f2dae5f4ca52a3452f66b9596cc Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 25 Feb 2019 02:44:36 +0100 Subject: [PATCH 051/244] changelog: adds notes for #14509 and #15179 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fea588259f..dee2adcab4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Unreleased +# 6.0.0 stable (unreleased) + +### Bug Fixes +* **Stackdriver**: fix for float64 bounds for distribution metrics [#14509](https://github.com/grafana/grafana/issues/14509) +* **Stackdriver**: no reducers available for distribution type [#15179](https://github.com/grafana/grafana/issues/15179) + # 6.0.0-beta3 (2019-02-19) ### Minor From 26dcabc2dc6b50abf641c2b20f97d315cce99e16 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 25 Feb 2019 13:00:18 +0100 Subject: [PATCH 052/244] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dee2adcab4d..2c6aaff5444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### Bug Fixes * **Stackdriver**: fix for float64 bounds for distribution metrics [#14509](https://github.com/grafana/grafana/issues/14509) * **Stackdriver**: no reducers available for distribution type [#15179](https://github.com/grafana/grafana/issues/15179) +* **Dashboard**: fixes click after scroll in series override menu [#15621](https://github.com/grafana/grafana/issues/15621) +* **MySQL**: fix mysql query using _interval_ms variable throws error [#14507](https://github.com/grafana/grafana/issues/14507) # 6.0.0-beta3 (2019-02-19) From 0c67194b455a176f132e4139d72415741f897d26 Mon Sep 17 00:00:00 2001 From: bergquist Date: Sat, 23 Feb 2019 23:24:27 +0100 Subject: [PATCH 053/244] moves tracing packge into /infra --- pkg/cmd/grafana-server/server.go | 2 +- pkg/{ => infra}/tracing/tracing.go | 0 pkg/{ => infra}/tracing/tracing_test.go | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename pkg/{ => infra}/tracing/tracing.go (100%) rename pkg/{ => infra}/tracing/tracing_test.go (100%) diff --git a/pkg/cmd/grafana-server/server.go b/pkg/cmd/grafana-server/server.go index a1db5cd4b4c..e27db3d874d 100644 --- a/pkg/cmd/grafana-server/server.go +++ b/pkg/cmd/grafana-server/server.go @@ -30,6 +30,7 @@ import ( _ "github.com/grafana/grafana/pkg/extensions" _ "github.com/grafana/grafana/pkg/infra/metrics" _ "github.com/grafana/grafana/pkg/infra/serverlock" + _ "github.com/grafana/grafana/pkg/infra/tracing" _ "github.com/grafana/grafana/pkg/plugins" _ "github.com/grafana/grafana/pkg/services/alerting" _ "github.com/grafana/grafana/pkg/services/auth" @@ -39,7 +40,6 @@ import ( _ "github.com/grafana/grafana/pkg/services/rendering" _ "github.com/grafana/grafana/pkg/services/search" _ "github.com/grafana/grafana/pkg/services/sqlstore" - _ "github.com/grafana/grafana/pkg/tracing" ) func NewGrafanaServer() *GrafanaServerImpl { diff --git a/pkg/tracing/tracing.go b/pkg/infra/tracing/tracing.go similarity index 100% rename from pkg/tracing/tracing.go rename to pkg/infra/tracing/tracing.go diff --git a/pkg/tracing/tracing_test.go b/pkg/infra/tracing/tracing_test.go similarity index 100% rename from pkg/tracing/tracing_test.go rename to pkg/infra/tracing/tracing_test.go From 60fef31748fde5e97f34dc7b01b817282ee46535 Mon Sep 17 00:00:00 2001 From: bergquist Date: Sat, 23 Feb 2019 23:39:05 +0100 Subject: [PATCH 054/244] moves social package to /login ref #14679 --- pkg/api/login_oauth.go | 2 +- pkg/cmd/grafana-server/server.go | 2 +- pkg/infra/usagestats/service.go | 2 +- pkg/{ => login}/social/common.go | 0 pkg/{ => login}/social/generic_oauth.go | 0 pkg/{ => login}/social/github_oauth.go | 0 pkg/{ => login}/social/gitlab_oauth.go | 0 pkg/{ => login}/social/google_oauth.go | 0 pkg/{ => login}/social/grafana_com_oauth.go | 0 pkg/{ => login}/social/social.go | 0 10 files changed, 3 insertions(+), 3 deletions(-) rename pkg/{ => login}/social/common.go (100%) rename pkg/{ => login}/social/generic_oauth.go (100%) rename pkg/{ => login}/social/github_oauth.go (100%) rename pkg/{ => login}/social/gitlab_oauth.go (100%) rename pkg/{ => login}/social/google_oauth.go (100%) rename pkg/{ => login}/social/grafana_com_oauth.go (100%) rename pkg/{ => login}/social/social.go (100%) diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go index 7d9537b4790..a4c4a064226 100644 --- a/pkg/api/login_oauth.go +++ b/pkg/api/login_oauth.go @@ -19,9 +19,9 @@ import ( "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/login" + "github.com/grafana/grafana/pkg/login/social" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/social" ) var ( diff --git a/pkg/cmd/grafana-server/server.go b/pkg/cmd/grafana-server/server.go index a1db5cd4b4c..b4f0fa3a2d2 100644 --- a/pkg/cmd/grafana-server/server.go +++ b/pkg/cmd/grafana-server/server.go @@ -16,9 +16,9 @@ import ( "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/login" + "github.com/grafana/grafana/pkg/login/social" "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/registry" - "github.com/grafana/grafana/pkg/social" "golang.org/x/sync/errgroup" diff --git a/pkg/infra/usagestats/service.go b/pkg/infra/usagestats/service.go index c2bf0d06349..ccebf6a17f4 100644 --- a/pkg/infra/usagestats/service.go +++ b/pkg/infra/usagestats/service.go @@ -5,8 +5,8 @@ import ( "time" "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/login/social" "github.com/grafana/grafana/pkg/services/sqlstore" - "github.com/grafana/grafana/pkg/social" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/registry" diff --git a/pkg/social/common.go b/pkg/login/social/common.go similarity index 100% rename from pkg/social/common.go rename to pkg/login/social/common.go diff --git a/pkg/social/generic_oauth.go b/pkg/login/social/generic_oauth.go similarity index 100% rename from pkg/social/generic_oauth.go rename to pkg/login/social/generic_oauth.go diff --git a/pkg/social/github_oauth.go b/pkg/login/social/github_oauth.go similarity index 100% rename from pkg/social/github_oauth.go rename to pkg/login/social/github_oauth.go diff --git a/pkg/social/gitlab_oauth.go b/pkg/login/social/gitlab_oauth.go similarity index 100% rename from pkg/social/gitlab_oauth.go rename to pkg/login/social/gitlab_oauth.go diff --git a/pkg/social/google_oauth.go b/pkg/login/social/google_oauth.go similarity index 100% rename from pkg/social/google_oauth.go rename to pkg/login/social/google_oauth.go diff --git a/pkg/social/grafana_com_oauth.go b/pkg/login/social/grafana_com_oauth.go similarity index 100% rename from pkg/social/grafana_com_oauth.go rename to pkg/login/social/grafana_com_oauth.go diff --git a/pkg/social/social.go b/pkg/login/social/social.go similarity index 100% rename from pkg/social/social.go rename to pkg/login/social/social.go From 38a116d0f774d9c486167cd780f44fbec8e22fdd Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 25 Feb 2019 15:45:35 +0100 Subject: [PATCH 055/244] docs: grafana 6.0 has been released. --- docs/sources/guides/whats-new-in-v6-0.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/sources/guides/whats-new-in-v6-0.md b/docs/sources/guides/whats-new-in-v6-0.md index c7091fd855e..24a32311954 100644 --- a/docs/sources/guides/whats-new-in-v6-0.md +++ b/docs/sources/guides/whats-new-in-v6-0.md @@ -14,8 +14,6 @@ weight = -11 This update to Grafana introduces a new way of exploring your data, support for log data and tons of other features. -Grafana v6.0 is out in **Beta**, [Download Now!](https://grafana.com/grafana/download/beta) - The main highlights are: - [Explore]({{< relref "#explore" >}}) - A new query focused workflow for ad-hoc data exploration and troubleshooting. From e1d27bd79afd1cc76e84bdcfdc3beb8de488de49 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 25 Feb 2019 16:24:02 +0100 Subject: [PATCH 056/244] Update CHANGELOG.md --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c6aaff5444..d67030873c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,4 @@ -# Unreleased - -# 6.0.0 stable (unreleased) +# 6.0.0 stable (2019-02-25) ### Bug Fixes * **Stackdriver**: fix for float64 bounds for distribution metrics [#14509](https://github.com/grafana/grafana/issues/14509) From 3c3e06515e0e91b9319de57ec1021f93482aa3c5 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 25 Feb 2019 17:09:49 +0100 Subject: [PATCH 057/244] Updated latest.json with 6.0 --- latest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/latest.json b/latest.json index dc83446f588..7e69b431a4d 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { - "stable": "5.4.3", - "testing": "5.4.3" + "stable": "6.0.0", + "testing": "6.0.0" } From 0bdca7957aa0ce01bccea90f0f9ad7069ad57168 Mon Sep 17 00:00:00 2001 From: Jon Ferreira Date: Fri, 22 Feb 2019 14:56:13 -0500 Subject: [PATCH 058/244] Toggle stack should trigger a render, not a refresh --- public/app/plugins/panel/graph/tab_display.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/tab_display.html b/public/app/plugins/panel/graph/tab_display.html index a6287922cfe..e6f5b597e1b 100644 --- a/public/app/plugins/panel/graph/tab_display.html +++ b/public/app/plugins/panel/graph/tab_display.html @@ -114,7 +114,7 @@ label="Stack" label-class="width-7" checked="ctrl.panel.stack" - on-change="ctrl.refresh()" + on-change="ctrl.render()" > Date: Mon, 25 Feb 2019 19:25:47 +0100 Subject: [PATCH 059/244] docs: 6.0 whats new --- docs/sources/guides/whats-new-in-v6-0.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sources/guides/whats-new-in-v6-0.md b/docs/sources/guides/whats-new-in-v6-0.md index 24a32311954..7c61df1c3c4 100644 --- a/docs/sources/guides/whats-new-in-v6-0.md +++ b/docs/sources/guides/whats-new-in-v6-0.md @@ -105,9 +105,9 @@ continue to refine and start using in other panels. ### React Panels & Query Editors A major part of all the work that has gone into Grafana v6.0 has been on the migration to React. This investment -is part of the future proofing of Grafana and it's code base and ecosystem. Starting in v6.0 **Panels** and **Data +is part of the future proofing of Grafana's code base and ecosystem. Starting in v6.0 **Panels** and **Data source** plugins can be written in React using our published `@grafana/ui` sdk library. More information on this -will be shared closer to or just after release. +will be shared soon. {{< docs-imagebox img="/img/docs/v60/react_panels.png" max-width="600px" caption="React Panel" >}}
@@ -120,7 +120,7 @@ To get started read the guide: [Using Google Stackdriver in Grafana](/features/d ## Azure Monitor Datasource -One of the goals of the Grafana v6.0 release is to add support for the three major clouds. Amazon Cloudwatch has been a core datasource for years and Google Stackdriver is also now supported. We developed an external plugin for Azure Monitor last year and for this release the [plugin](https://grafana.com/plugins/grafana-azure-monitor-datasource) is being moved into Grafana to be one of the built-in datasources. For users of the external plugin, Grafana will automatically start using the built-in version. As a core datasource, the Azure Monitor datasource will get alerting support for the official 6.0 release. +One of the goals of the Grafana v6.0 release is to add support for the three major clouds. Amazon Cloudwatch has been a core datasource for years and Google Stackdriver is also now supported. We developed an external plugin for Azure Monitor last year and for this release the [plugin](https://grafana.com/plugins/grafana-azure-monitor-datasource) is being moved into Grafana to be one of the built-in datasources. For users of the external plugin, Grafana will automatically start using the built-in version. As a core datasource, the Azure Monitor datasource is able to get alerting support, in the 6.0 release alerting is supported for the Azure Monitor service, with the rest to follow. The Azure Monitor datasource integrates four Azure services with Grafana - Azure Monitor, Azure Log Analytics, Azure Application Insights and Azure Application Insights Analytics. From 36788183d84e50ecfaddeaf20ed6eec055921d83 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 26 Feb 2019 11:41:55 +0100 Subject: [PATCH 060/244] service: fix for disabled internal metrics. Update of the internal metrics for Grafana was disabled by mistake when refactoring the code. Fixes #15651 --- pkg/cmd/grafana-server/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/cmd/grafana-server/server.go b/pkg/cmd/grafana-server/server.go index b454b362d90..53218147ae0 100644 --- a/pkg/cmd/grafana-server/server.go +++ b/pkg/cmd/grafana-server/server.go @@ -31,6 +31,7 @@ import ( _ "github.com/grafana/grafana/pkg/infra/metrics" _ "github.com/grafana/grafana/pkg/infra/serverlock" _ "github.com/grafana/grafana/pkg/infra/tracing" + _ "github.com/grafana/grafana/pkg/infra/usagestats" _ "github.com/grafana/grafana/pkg/plugins" _ "github.com/grafana/grafana/pkg/services/alerting" _ "github.com/grafana/grafana/pkg/services/auth" From 1bffde57e35ece5b64532fc0d34cff685bcc777d Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 26 Feb 2019 09:22:21 -0800 Subject: [PATCH 061/244] Need this to be available for plugins --- packages/grafana-ui/src/utils/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index 52a8535f9e1..c5f4b2c5b1b 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -2,3 +2,4 @@ export * from './processTimeSeries'; export * from './valueFormats/valueFormats'; export * from './colors'; export * from './namedColorsPalette'; +export { getMappedValue } from './valueMappings'; From e7b630e633375756529dfad0c66093f35af34061 Mon Sep 17 00:00:00 2001 From: Ben Drucker Date: Tue, 26 Feb 2019 11:34:49 -0800 Subject: [PATCH 062/244] Style and grammar fixes --- docs/sources/guides/whats-new-in-v6-0.md | 38 ++++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/sources/guides/whats-new-in-v6-0.md b/docs/sources/guides/whats-new-in-v6-0.md index 7c61df1c3c4..1506cee6dbd 100644 --- a/docs/sources/guides/whats-new-in-v6-0.md +++ b/docs/sources/guides/whats-new-in-v6-0.md @@ -12,7 +12,7 @@ weight = -11 # What's New in Grafana v6.0 -This update to Grafana introduces a new way of exploring your data, support for log data and tons of other features. +This update to Grafana introduces a new way of exploring your data, support for log data, and tons of other features. The main highlights are: @@ -25,24 +25,24 @@ The main highlights are: - [Azure Monitor]({{< relref "#azure-monitor-datasource" >}}) plugin is ported from being an external plugin to being a core datasource - [React Plugin]({{< relref "#react-panels-query-editors" >}}) support enables an easier way to build plugins. - [Named Colors]({{< relref "#named-colors" >}}) in our new improved color picker. -- [Removal of user session storage]({{< relref "#easier-to-deploy-improved-security" >}}) makes Grafana easier to deploy & improves security. +- [Removal of user session storage]({{< relref "#easier-to-deploy-improved-security" >}}) makes Grafana easier to deploy and improves security. ## Explore {{< docs-imagebox img="/img/docs/v60/explore_prometheus.png" max-width="800px" class="docs-image--right" caption="Screenshot of the new Explore option in the panel menu" >}} -Grafana's dashboard UI is all about building dashboards for visualization. **Explore** strips away all the dashboard and panel options so that you can focus on the query & metric exploration. Iterate until you have a working query and then think about building a dashboard. You can also jump from a dashboard panel into **Explore** and from there do some ad-hoc query exporation with the panel queries as a starting point. +Grafana's dashboard UI is all about building dashboards for visualization. **Explore** strips away all the dashboard and panel options so that you can focus on the query and metric exploration. Iterate until you have a working query and then think about building a dashboard. You can also jump from a dashboard panel into **Explore** and from there do some ad-hoc query exporation with the panel queries as a starting point. For infrastructure monitoring and incident response, you no longer need to switch to other tools to debug what went wrong. **Explore** allows you to dig deeper into your metrics and logs to find the cause. Grafana's new logging datasource, [Loki](https://github.com/grafana/loki) is tightly integrated into Explore and allows you to correlate metrics and logs by viewing them side-by-side. **Explore** is a new paradigm for Grafana. It creates a new interactive debugging workflow that integrates two pillars -of observability - metrics and logs. Explore works with every datasource but for Prometheus we have customized the +of observability—metrics and logs. Explore works with every datasource but for Prometheus we have customized the query editor and the experience to provide the best possible exploration UX. ### Explore and Prometheus Explore features a new [Prometheus query editor](/features/explore/#prometheus-specific-features). This new editor has improved autocomplete, metric tree selector, -integrations with the Explore table view for easy label filtering and useful query hints that can automatically apply +integrations with the Explore table view for easy label filtering, and useful query hints that can automatically apply functions to your query. There is also integration between Prometheus and Grafana Loki (see more about Loki below) that enabled jumping between metrics query and logs query with preserved label filters. @@ -78,8 +78,8 @@ for other log sources to Explore and the next planned integration is Elasticsear ## New Panel Editor Grafana v6.0 has a completely redesigned UX around editing panels. You can now resize the visualization area if you want -more space for queries & options and vice versa. You can now also change visualization (panel type) from within the new -panel edit mode. No need to add a new panel to try out different visualizations! Checkout the +more space for queries/options and vice versa. You can now also change visualization (panel type) from within the new +panel edit mode. No need to add a new panel to try out different visualizations! Check out the video below to see the new Panel Editor in action.
@@ -94,7 +94,7 @@ video below to see the new Panel Editor in action. ### Gauge Panel We have created a new separate Gauge panel as we felt having this visualization be a hidden option in the Singlestat panel -was not ideal. When it supports 100% of the Singlestat Gauge features we plan to add a migration so all +was not ideal. When it supports 100% of the Singlestat Gauge features, we plan to add a migration so all singlestats that use it become Gauge panels instead. This new panel contains a new **Threshold** editor that we will continue to refine and start using in other panels. @@ -105,7 +105,7 @@ continue to refine and start using in other panels. ### React Panels & Query Editors A major part of all the work that has gone into Grafana v6.0 has been on the migration to React. This investment -is part of the future proofing of Grafana's code base and ecosystem. Starting in v6.0 **Panels** and **Data +is part of the future-proofing of Grafana's code base and ecosystem. Starting in v6.0 **Panels** and **Data source** plugins can be written in React using our published `@grafana/ui` sdk library. More information on this will be shared soon. @@ -120,7 +120,7 @@ To get started read the guide: [Using Google Stackdriver in Grafana](/features/d ## Azure Monitor Datasource -One of the goals of the Grafana v6.0 release is to add support for the three major clouds. Amazon Cloudwatch has been a core datasource for years and Google Stackdriver is also now supported. We developed an external plugin for Azure Monitor last year and for this release the [plugin](https://grafana.com/plugins/grafana-azure-monitor-datasource) is being moved into Grafana to be one of the built-in datasources. For users of the external plugin, Grafana will automatically start using the built-in version. As a core datasource, the Azure Monitor datasource is able to get alerting support, in the 6.0 release alerting is supported for the Azure Monitor service, with the rest to follow. +One of the goals of the Grafana v6.0 release is to add support for the three major clouds. Amazon CloudWatch has been a core datasource for years and Google Stackdriver is also now supported. We developed an external plugin for Azure Monitor last year and for this release the [plugin](https://grafana.com/plugins/grafana-azure-monitor-datasource) is being moved into Grafana to be one of the built-in datasources. For users of the external plugin, Grafana will automatically start using the built-in version. As a core datasource, the Azure Monitor datasource is able to get alerting support, in the 6.0 release alerting is supported for the Azure Monitor service, with the rest to follow. The Azure Monitor datasource integrates four Azure services with Grafana - Azure Monitor, Azure Log Analytics, Azure Application Insights and Azure Application Insights Analytics. @@ -128,15 +128,15 @@ Please read [Using Azure Monitor in Grafana documentation](/features/datasources ## Provisioning support for alert notifiers -Grafana now added support for provisioning alert notifiers from configuration files. Allowing operators to provision notifiers without using the UI or the API. A new field called `uid` has been introduced which is a string identifier that the administrator can set themselves. Same kind of identifier used for dashboards since v5.0. This feature makes it possible to use the same notifier configuration in multiple environments and refer to notifiers in dashboard json by a string identifier instead of the numeric id which depends on insert order and how many notifiers that exists in the instance. +Grafana now has support for provisioning alert notifiers from configuration files, allowing operators to provision notifiers without using the UI or the API. A new field called `uid` has been introduced which is a string identifier that the administrator can set themselves. This is the same kind of identifier used for dashboards since v5.0. This feature makes it possible to use the same notifier configuration in multiple environments and refer to notifiers in dashboard json by a string identifier instead of the numeric id which depends on insert order and how many notifiers exist in the instance. ## Easier to deploy & improved security -Grafana 6.0 removes the need of configuring and setup of additional storage for [user sessions](/tutorials/ha_setup/#user-sessions). This should make it easier to deploy and operate Grafana in a -high availability setup and/or if you're using a stateless user session storage like Redis, Memcache, Postgres or MySQL. +Grafana 6.0 removes the need to configure and set up additional storage for [user sessions](/tutorials/ha_setup/#user-sessions). This should make it easier to deploy and operate Grafana in a +high availability setup and/or if you're using a stateless user session store like Redis, Memcache, Postgres or MySQL. -Instead of user sessions a solution based on short-lived tokens that are rotated frequently have been implemented. This also replaces the old "remember me cookie" -solution, which allowed a user to be logged in between browser sessions, and which have been subject to several security holes throughout the years. +Instead of user sessions, we've implemented a solution based on short-lived tokens that are rotated frequently. This also replaces the old "remember me cookie" +solution, which allowed a user to be logged in between browser sessions and which have been subject to several security holes throughout the years. Read more about the short-lived token solution and how to configure it [here](/auth/overview/#login-and-short-lived-tokens). > Please note that due to these changes, all users will be required to login upon next visit after upgrade. @@ -146,15 +146,15 @@ Besides these changes we have also made security improvements regarding Cross-Si * Cookies are per default using the [SameSite](/installation/configuration/#cookie-samesite) attribute to protect against CSRF attacks * Script tags in text panels are per default [disabled](/installation/configuration/#disable-sanitize-html) to protect against XSS attacks -> If you're using [Auth Proxy Authentication](/auth/auth-proxy/) you still need to have user sessions setup and configured -but our goal is to remove this requirements in a near future. +> If you're using [Auth Proxy Authentication](/auth/auth-proxy/) you still need to have user sessions set up and configured +but our goal is to remove this requirement in the near future. ## Named Colors {{< docs-imagebox img="/img/docs/v60/named_colors.png" max-width="400px" class="docs-image--right" caption="Named Colors" >}} We have updated the color picker to show named colors and primary colors. We hope this will improve accessibility and -helps making colors more consistent across dashboards. We hope to do more in this color picker in the future, like show +helps making colors more consistent across dashboards. We hope to do more in this color picker in the future, like showing colors used in the dashboard. Named colors also enables Grafana to adapt colors to the current theme. @@ -163,7 +163,7 @@ Named colors also enables Grafana to adapt colors to the current theme. ## Other features -- The ElasticSearch datasource now supports [bucket script pipeline aggregations](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-pipeline-bucket-script-aggregation.html). This gives the ability to do per bucket computations like the difference or ratio between two metrics. +- The ElasticSearch datasource now supports [bucket script pipeline aggregations](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-pipeline-bucket-script-aggregation.html). This gives the ability to do per-bucket computations like the difference or ratio between two metrics. - Support for Google Hangouts Chat alert notifications - New built in template variables for the current time range in `$__from` and `$__to` From 8bdf2111c49e3aa3df1de38cb6810f865594f8cf Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 26 Feb 2019 11:51:52 -0800 Subject: [PATCH 063/244] Bumping grafana ui version (#15669) grafana/ui 6.0.1-alpha.0 release version bump --- packages/grafana-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 2c4db3e30af..6c28c8abbd3 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/ui", - "version": "6.0.0-alpha.0", + "version": "6.0.1-alpha.0", "description": "Grafana Components Library", "keywords": [ "typescript", From 539333bb0159d0d7bafc6ccb0c61acd975e15608 Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Tue, 26 Feb 2019 14:21:46 -0800 Subject: [PATCH 064/244] Explore: Enable click on name label - click on the name label in a prometheus table was disabled - it was disabled because every query used to have a metric which is no longer true - this change enables it --- .../app/plugins/datasource/prometheus/result_transformer.ts | 2 +- .../datasource/prometheus/specs/result_transformer.test.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/result_transformer.ts b/public/app/plugins/datasource/prometheus/result_transformer.ts index 3c21e0c3d51..c3fbd7ee1d7 100644 --- a/public/app/plugins/datasource/prometheus/result_transformer.ts +++ b/public/app/plugins/datasource/prometheus/result_transformer.ts @@ -100,7 +100,7 @@ export class ResultTransformer { table.columns.push({ text: 'Time', type: 'time' }); _.each(sortedLabels, (label, labelIndex) => { metricLabels[label] = labelIndex + 1; - table.columns.push({ text: label, filterable: !label.startsWith('__') }); + table.columns.push({ text: label, filterable: true }); }); const valueText = resultCount > 1 || valueWithRefId ? `Value #${refId}` : 'Value'; table.columns.push({ text: valueText }); diff --git a/public/app/plugins/datasource/prometheus/specs/result_transformer.test.ts b/public/app/plugins/datasource/prometheus/specs/result_transformer.test.ts index d7e42237f8a..3c376334187 100644 --- a/public/app/plugins/datasource/prometheus/specs/result_transformer.test.ts +++ b/public/app/plugins/datasource/prometheus/specs/result_transformer.test.ts @@ -66,11 +66,12 @@ describe('Prometheus Result Transformer', () => { ]); expect(table.columns).toMatchObject([ { text: 'Time', type: 'time' }, - { text: '__name__' }, - { text: 'instance' }, + { text: '__name__', filterable: true }, + { text: 'instance', filterable: true }, { text: 'job' }, { text: 'Value' }, ]); + expect(table.columns[4].filterable).toBeUndefined(); }); it('should column title include refId if response count is more than 2', () => { From 8ef3aebc29e2ae41200312761924612c41bc8f66 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 27 Feb 2019 00:20:29 -0800 Subject: [PATCH 065/244] Fixed alias in Cloudwatch Expressions --- pkg/tsdb/cloudwatch/cloudwatch.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index 278025db75a..bfd9f85dbf9 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -496,9 +496,9 @@ func parseQuery(model *simplejson.Json) (*CloudWatchQuery, error) { } alias := model.Get("alias").MustString() - if alias == "" { + /*if alias == "" { alias = "{{metric}}_{{stat}}" - } + }*/ returnData := model.Get("returnData").MustBool(false) highResolution := model.Get("highResolution").MustBool(false) @@ -521,7 +521,11 @@ func parseQuery(model *simplejson.Json) (*CloudWatchQuery, error) { func formatAlias(query *CloudWatchQuery, stat string, dimensions map[string]string) string { if len(query.Id) > 0 && len(query.Expression) > 0 { - return query.Id + if len(query.Alias) > 0 { + return query.Alias + } else { + return query.Id + } } data := map[string]string{} From 37033080b7790bc09e749ee922a49f04d5093dd0 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 27 Feb 2019 00:33:40 -0800 Subject: [PATCH 066/244] Removed commented code --- pkg/tsdb/cloudwatch/cloudwatch.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index bfd9f85dbf9..6d68e6902e5 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -496,9 +496,6 @@ func parseQuery(model *simplejson.Json) (*CloudWatchQuery, error) { } alias := model.Get("alias").MustString() - /*if alias == "" { - alias = "{{metric}}_{{stat}}" - }*/ returnData := model.Get("returnData").MustBool(false) highResolution := model.Get("highResolution").MustBool(false) From 8d5ccc7831361bf6159a9850d1bd967d8c1a073e Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Thu, 28 Feb 2019 10:35:53 +0100 Subject: [PATCH 067/244] fix: Return url when query dashboards by tag --- pkg/api/playlist_play.go | 2 ++ pkg/services/search/models.go | 1 + 2 files changed, 3 insertions(+) diff --git a/pkg/api/playlist_play.go b/pkg/api/playlist_play.go index 5ca136c32c4..2757f245060 100644 --- a/pkg/api/playlist_play.go +++ b/pkg/api/playlist_play.go @@ -52,8 +52,10 @@ func populateDashboardsByTag(orgID int64, signedInUser *m.SignedInUser, dashboar for _, item := range searchQuery.Result { result = append(result, dtos.PlaylistDashboard{ Id: item.Id, + Slug: item.Slug, Title: item.Title, Uri: item.Uri, + Url: m.GetDashboardUrl(item.Uid, item.Slug), Order: dashboardTagOrder[tag], }) } diff --git a/pkg/services/search/models.go b/pkg/services/search/models.go index 2da09672f13..475cb4a3777 100644 --- a/pkg/services/search/models.go +++ b/pkg/services/search/models.go @@ -17,6 +17,7 @@ type Hit struct { Title string `json:"title"` Uri string `json:"uri"` Url string `json:"url"` + Slug string `json:"slug"` Type HitType `json:"type"` Tags []string `json:"tags"` IsStarred bool `json:"isStarred"` From 84b2c0447ee108fbb630e2b2c8898be20da1bc95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 28 Feb 2019 07:57:59 -0800 Subject: [PATCH 068/244] Fixed right side scrollbar margin on dashboard page --- .../src/components/CustomScrollbar/CustomScrollbar.tsx | 4 ++-- public/app/features/dashboard/containers/DashboardPage.tsx | 7 ++++++- .../containers/__snapshots__/DashboardPage.test.tsx.snap | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx index d52d2fea80d..61fc584c7ca 100644 --- a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx +++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx @@ -1,4 +1,4 @@ -import React, { PureComponent } from 'react'; +import React, { Component } from 'react'; import isNil from 'lodash/isNil'; import classNames from 'classnames'; import Scrollbars from 'react-custom-scrollbars'; @@ -20,7 +20,7 @@ interface Props { /** * Wraps component into component from `react-custom-scrollbars` */ -export class CustomScrollbar extends PureComponent { +export class CustomScrollbar extends Component { static defaultProps: Partial = { autoHide: false, autoHideTimeout: 200, diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx index c52774f7e0e..ce2d3fa74df 100644 --- a/public/app/features/dashboard/containers/DashboardPage.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.tsx @@ -268,7 +268,12 @@ export class DashboardPage extends PureComponent { onAddPanel={this.onAddPanel} />
- + {editview && } {initError && this.renderInitFailedState()} diff --git a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap index fec0e18c349..0e3720bada0 100644 --- a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap +++ b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap @@ -109,6 +109,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` autoHide={false} autoHideDuration={200} autoHideTimeout={200} + className="custom-scrollbar--page" hideTracksWhenNotNeeded={false} scrollTop={0} setScrollTop={[Function]} @@ -344,6 +345,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti autoHide={false} autoHideDuration={200} autoHideTimeout={200} + className="custom-scrollbar--page" hideTracksWhenNotNeeded={false} scrollTop={0} setScrollTop={[Function]} From 6bfbdbe20bc53eaaa305fde4b9e547fa2c538e2b Mon Sep 17 00:00:00 2001 From: SamuelToh Date: Wed, 9 Jan 2019 11:56:45 +1000 Subject: [PATCH 069/244] 11780: invalid reg value can cause unexpected behaviour --- public/app/core/specs/kbn.test.ts | 15 +++++++++++++++ public/app/core/time_series2.ts | 9 +++++++-- public/app/core/utils/kbn.ts | 5 +++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 public/app/core/specs/kbn.test.ts diff --git a/public/app/core/specs/kbn.test.ts b/public/app/core/specs/kbn.test.ts new file mode 100644 index 00000000000..25f82a5f850 --- /dev/null +++ b/public/app/core/specs/kbn.test.ts @@ -0,0 +1,15 @@ +import kbn from '../utils/kbn'; + +describe('stringToJsRegex', () => { + it('should parse the valid regex value', () => { + const output = kbn.stringToJsRegex("/validRegexp/"); + expect(output).toBeInstanceOf(RegExp); + }); + + it('should throw error on invalid regex value', () => { + const input = "/etc/hostname"; + expect(() => { + kbn.stringToJsRegex(input); + }).toThrow(); + }); +}); diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index 23a0a0c19ea..c24e1f64c43 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -1,4 +1,5 @@ import kbn from 'app/core/utils/kbn'; +import { appEvents } from 'app/core/core'; import { getFlotTickDecimals } from 'app/core/utils/ticks'; import _ from 'lodash'; import { getValueFormat } from '@grafana/ui'; @@ -9,8 +10,12 @@ function matchSeriesOverride(aliasOrRegex, seriesAlias) { } if (aliasOrRegex[0] === '/') { - const regex = kbn.stringToJsRegex(aliasOrRegex); - return seriesAlias.match(regex) != null; + try { + const regex = kbn.stringToJsRegex(aliasOrRegex); + return seriesAlias.match(regex) != null; + } catch (e) { + return appEvents.emit('alert-error', ['Invalid aliasOrRegex value.', e.message]); + } } return aliasOrRegex === seriesAlias; diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts index 887c30229d3..43886fafd07 100644 --- a/public/app/core/utils/kbn.ts +++ b/public/app/core/utils/kbn.ts @@ -234,6 +234,11 @@ kbn.stringToJsRegex = str => { } const match = str.match(new RegExp('^/(.*?)/(g?i?m?y?)$')); + + if (!match) { + throw new Error(`'${str}' is not a valid regular expression.`); + } + return new RegExp(match[1], match[2]); }; From 1bcaaccb963001c0970618f4ec9968e976503804 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 1 Mar 2019 16:07:34 +0100 Subject: [PATCH 070/244] docs: missing field added to example Closes #15715 --- docs/sources/http_api/user.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sources/http_api/user.md b/docs/sources/http_api/user.md index a7b6c96aac5..669e8003247 100644 --- a/docs/sources/http_api/user.md +++ b/docs/sources/http_api/user.md @@ -156,6 +156,7 @@ HTTP/1.1 200 Content-Type: application/json { + "id": 1, "email": "user@mygraf.com", "name": "admin", "login": "admin", From d2c161c2e62c2c803064dda83d9354d1489a56d5 Mon Sep 17 00:00:00 2001 From: SamuelToh Date: Sat, 2 Mar 2019 20:50:25 +1000 Subject: [PATCH 071/244] Catch bad regex exception at controller level --- public/app/core/time_series2.ts | 9 ++------- public/app/plugins/panel/graph/module.ts | 6 +++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index c24e1f64c43..23a0a0c19ea 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -1,5 +1,4 @@ import kbn from 'app/core/utils/kbn'; -import { appEvents } from 'app/core/core'; import { getFlotTickDecimals } from 'app/core/utils/ticks'; import _ from 'lodash'; import { getValueFormat } from '@grafana/ui'; @@ -10,12 +9,8 @@ function matchSeriesOverride(aliasOrRegex, seriesAlias) { } if (aliasOrRegex[0] === '/') { - try { - const regex = kbn.stringToJsRegex(aliasOrRegex); - return seriesAlias.match(regex) != null; - } catch (e) { - return appEvents.emit('alert-error', ['Invalid aliasOrRegex value.', e.message]); - } + const regex = kbn.stringToJsRegex(aliasOrRegex); + return seriesAlias.match(regex) != null; } return aliasOrRegex === seriesAlias; diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 3919c4f69a9..e3e3c4eb588 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -235,7 +235,11 @@ class GraphCtrl extends MetricsPanelCtrl { } for (const series of this.seriesList) { - series.applySeriesOverrides(this.panel.seriesOverrides); + try { + series.applySeriesOverrides(this.panel.seriesOverrides); + } catch (e) { + this.publishAppEvent('alert-error', [e.message]); + } if (series.unit) { this.panel.yaxes[series.yaxis - 1].format = series.unit; From 41024c29bb96b3126d9f48988d5a1c5b771f5eff Mon Sep 17 00:00:00 2001 From: Samuel Date: Sun, 3 Mar 2019 05:34:48 +1000 Subject: [PATCH 072/244] Return 404 on user not found (#15606) Return 404 on user not found closes #10506 --- pkg/api/org_invite.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/api/org_invite.go b/pkg/api/org_invite.go index 86067cd7721..4b731db5d3d 100644 --- a/pkg/api/org_invite.go +++ b/pkg/api/org_invite.go @@ -27,6 +27,10 @@ func GetPendingOrgInvites(c *m.ReqContext) Response { } func AddOrgInvite(c *m.ReqContext, inviteDto dtos.AddInviteForm) Response { + if setting.DisableLoginForm { + return Error(400, "Cannot invite when login is disabled.", nil) + } + if !inviteDto.Role.IsValid() { return Error(400, "Invalid role specified", nil) } @@ -37,10 +41,6 @@ func AddOrgInvite(c *m.ReqContext, inviteDto dtos.AddInviteForm) Response { if err != m.ErrUserNotFound { return Error(500, "Failed to query db for existing user check", err) } - - if setting.DisableLoginForm { - return Error(401, "User could not be found", nil) - } } else { return inviteExistingUserToOrg(c, userQuery.Result, &inviteDto) } From dafd7afb971fd5be7b73704f069710b5d763dfca Mon Sep 17 00:00:00 2001 From: bergquist Date: Sat, 2 Mar 2019 18:54:31 +0100 Subject: [PATCH 073/244] changelog: adds note about closing #15651 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d67030873c3..3001521b2e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 6.0.1 (unreleased) + +### Bug Fixes +* **Metrics**: Fixes broken usagestats metrics for /metrics [#15651](https://github.com/grafana/grafana/issues/15651) + # 6.0.0 stable (2019-02-25) ### Bug Fixes From cde3a21434e664081a024c3f93bbfa3fc8bafaf5 Mon Sep 17 00:00:00 2001 From: bergquist Date: Sat, 2 Mar 2019 20:37:36 +0100 Subject: [PATCH 074/244] changelog: adds note about closing #10506 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3001521b2e0..fd16d06b81a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ + +# 6.1.0 (unreleased) + +### Bug Fixes +* **Api**: Invalid org invite code [#10506](https://github.com/grafana/grafana/issues/10506) + # 6.0.1 (unreleased) ### Bug Fixes From d49f0bedd33b00e1d9dbe0c20fad4f550f669fae Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Sat, 2 Mar 2019 12:18:26 -0800 Subject: [PATCH 075/244] fix: prevent datasource json data stored as nil (#15508) prevent datasource json data stored as nil closes #14239 --- pkg/services/sqlstore/datasource.go | 10 ++++++++++ pkg/services/sqlstore/migrations/datasource_mig.go | 3 +++ 2 files changed, 13 insertions(+) diff --git a/pkg/services/sqlstore/datasource.go b/pkg/services/sqlstore/datasource.go index 1b69cce8c99..071577eb6d6 100644 --- a/pkg/services/sqlstore/datasource.go +++ b/pkg/services/sqlstore/datasource.go @@ -3,6 +3,8 @@ package sqlstore import ( "time" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/bus" @@ -95,6 +97,10 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error { return m.ErrDataSourceNameExists } + if cmd.JsonData == nil { + cmd.JsonData = simplejson.New() + } + ds := &m.DataSource{ OrgId: cmd.OrgId, Name: cmd.Name, @@ -142,6 +148,10 @@ func updateIsDefaultFlag(ds *m.DataSource, sess *DBSession) error { func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error { return inTransaction(func(sess *DBSession) error { + if cmd.JsonData == nil { + cmd.JsonData = simplejson.New() + } + ds := &m.DataSource{ Id: cmd.Id, OrgId: cmd.OrgId, diff --git a/pkg/services/sqlstore/migrations/datasource_mig.go b/pkg/services/sqlstore/migrations/datasource_mig.go index fc617be72a1..54d86d34dba 100644 --- a/pkg/services/sqlstore/migrations/datasource_mig.go +++ b/pkg/services/sqlstore/migrations/datasource_mig.go @@ -130,4 +130,7 @@ func addDataSourceMigration(mg *Migrator) { const migrateLoggingToLoki = `UPDATE data_source SET type = 'loki' WHERE type = 'logging'` mg.AddMigration("Migrate logging ds to loki ds", NewRawSqlMigration(migrateLoggingToLoki)) + + const setEmptyJSONWhereNullJSON = `UPDATE data_source SET json_data = '{}' WHERE json_data is null` + mg.AddMigration("Update json_data with nulls", NewRawSqlMigration(setEmptyJSONWhereNullJSON)) } From 27dc3586b1a7565b588d2e297aa6c286c8c8b602 Mon Sep 17 00:00:00 2001 From: bergquist Date: Sat, 2 Mar 2019 21:27:09 +0100 Subject: [PATCH 076/244] changelog: adds note about closing #14239 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd16d06b81a..a458a07a76e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Bug Fixes * **Api**: Invalid org invite code [#10506](https://github.com/grafana/grafana/issues/10506) +* **Datasource**: Handles nil jsondata field gracefully [#14239](https://github.com/grafana/grafana/issues/14239) # 6.0.1 (unreleased) From a9ca8e9dec8243eb077ab8c9a524e525ff565fab Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Sat, 2 Mar 2019 13:51:55 -0800 Subject: [PATCH 077/244] fix --- packages/grafana-ui/src/utils/valueFormats/categories.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/utils/valueFormats/categories.ts b/packages/grafana-ui/src/utils/valueFormats/categories.ts index e127285b473..806da582bb3 100644 --- a/packages/grafana-ui/src/utils/valueFormats/categories.ts +++ b/packages/grafana-ui/src/utils/valueFormats/categories.ts @@ -125,7 +125,7 @@ export const getCategories = (): ValueFormatCategory[] => [ { name: 'Data (Metric)', formats: [ - { name: 'bits', id: 'decbits', fn: decimalSIPrefix('d') }, + { name: 'bits', id: 'decbits', fn: decimalSIPrefix('b') }, { name: 'bytes', id: 'decbytes', fn: decimalSIPrefix('B') }, { name: 'kilobytes', id: 'deckbytes', fn: decimalSIPrefix('B', 1) }, { name: 'megabytes', id: 'decmbytes', fn: decimalSIPrefix('B', 2) }, From e04218e550da7ae9c14687cd6fa8ed7390e1c411 Mon Sep 17 00:00:00 2001 From: sandeep Date: Sun, 3 Mar 2019 18:10:38 +0530 Subject: [PATCH 078/244] Fix: #14706 Incorrect index pattern padding in alerting queries When weekly index pattern is used, indices names contain single digit week number for week 1-9 This fix makes sure indice names always contain 2 digit week number for weekly pattern --- pkg/tsdb/elasticsearch/client/index_pattern.go | 2 +- pkg/tsdb/elasticsearch/client/index_pattern_test.go | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/tsdb/elasticsearch/client/index_pattern.go b/pkg/tsdb/elasticsearch/client/index_pattern.go index 952b5c4f806..da3c471424d 100644 --- a/pkg/tsdb/elasticsearch/client/index_pattern.go +++ b/pkg/tsdb/elasticsearch/client/index_pattern.go @@ -279,7 +279,7 @@ func formatDate(t time.Time, pattern string) string { isoYearShort := fmt.Sprintf("%d", isoYear)[2:4] formatted = strings.Replace(formatted, "", fmt.Sprintf("%d", isoYear), -1) formatted = strings.Replace(formatted, "", isoYearShort, -1) - formatted = strings.Replace(formatted, "", fmt.Sprintf("%d", isoWeek), -1) + formatted = strings.Replace(formatted, "", fmt.Sprintf("%02d", isoWeek), -1) formatted = strings.Replace(formatted, "", fmt.Sprintf("%d", t.Unix()), -1) diff --git a/pkg/tsdb/elasticsearch/client/index_pattern_test.go b/pkg/tsdb/elasticsearch/client/index_pattern_test.go index ca20b39d532..a3a056da04f 100644 --- a/pkg/tsdb/elasticsearch/client/index_pattern_test.go +++ b/pkg/tsdb/elasticsearch/client/index_pattern_test.go @@ -76,6 +76,15 @@ func TestIndexPattern(t *testing.T) { So(indices, ShouldHaveLength, 1) So(indices[0], ShouldEqual, "2018-data") }) + + Convey("Should return 01 week", func() { + from = fmt.Sprintf("%d", time.Date(2018, 1, 15, 17, 50, 0, 0, time.UTC).UnixNano()/int64(time.Millisecond)) + to = fmt.Sprintf("%d", time.Date(2018, 1, 15, 17, 55, 0, 0, time.UTC).UnixNano()/int64(time.Millisecond)) + indexPatternScenario(intervalWeekly, "[data-]GGGG.WW", tsdb.NewTimeRange(from, to), func(indices []string) { + So(indices, ShouldHaveLength, 1) + So(indices[0], ShouldEqual, "data-2018.03") + }) + }) }) Convey("Hourly interval", t, func() { From 6fd6f893aa5b98ea92f0e05f91311f5e5ee1d709 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Mar 2019 00:38:19 -0800 Subject: [PATCH 079/244] new stable docs version --- docs/versions.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/versions.json b/docs/versions.json index b1e447d5aa0..d1eab9afa51 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,5 +1,6 @@ [ - { "version": "v5.4", "path": "/", "archived": false, "current": true }, + { "version": "v6.0", "path": "/", "archived": false, "current": true }, + { "version": "v5.4", "path": "/v5.4", "archived": true }, { "version": "v5.3", "path": "/v5.3", "archived": true }, { "version": "v5.2", "path": "/v5.2", "archived": true }, { "version": "v5.1", "path": "/v5.1", "archived": true }, From 25b09168ebeb9678589a6396cc42437958a398dc Mon Sep 17 00:00:00 2001 From: parry <45209+thatsparesh@users.noreply.github.com> Date: Mon, 4 Mar 2019 02:56:47 -0600 Subject: [PATCH 080/244] Don't mutate seriesList parameter in mergeSeriesByTime (#15619) * do not mutate seriesList in mergeSeriesByTime * extendedseriesList -> extendedDatapointsList * remove toString() from datapoints, since the value can be null --- public/app/core/specs/file_export.test.ts | 10 +++++++++ public/app/core/utils/file_export.ts | 27 ++++++++++++----------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/public/app/core/specs/file_export.test.ts b/public/app/core/specs/file_export.test.ts index 98f5a5be742..9e2ff0a7ce1 100644 --- a/public/app/core/specs/file_export.test.ts +++ b/public/app/core/specs/file_export.test.ts @@ -60,6 +60,16 @@ describe('file_export', () => { expect(text).toBe(expectedText); }); + + it('should not modify series.datapoints', () => { + const expectedSeries1DataPoints = ctx.seriesList[0].datapoints.slice(); + const expectedSeries2DataPoints = ctx.seriesList[1].datapoints.slice(); + + fileExport.convertSeriesListToCsvColumns(ctx.seriesList, ctx.timeFormat); + + expect(expectedSeries1DataPoints).toEqual(ctx.seriesList[0].datapoints); + expect(expectedSeries2DataPoints).toEqual(ctx.seriesList[1].datapoints); + }); }); describe('when exporting table data to csv', () => { diff --git a/public/app/core/utils/file_export.ts b/public/app/core/utils/file_export.ts index 1f999da72a5..c59d646839f 100644 --- a/public/app/core/utils/file_export.ts +++ b/public/app/core/utils/file_export.ts @@ -88,18 +88,18 @@ export function convertSeriesListToCsvColumns(seriesList, dateTimeFormat = DEFAU ) ); // process data - seriesList = mergeSeriesByTime(seriesList); + const extendedDatapointsList = mergeSeriesByTime(seriesList); // make text - for (let i = 0; i < seriesList[0].datapoints.length; i += 1) { - const timestamp = moment(seriesList[0].datapoints[i][POINT_TIME_INDEX]).format(dateTimeFormat); + for (let i = 0; i < extendedDatapointsList[0].length; i += 1) { + const timestamp = moment(extendedDatapointsList[0][i][POINT_TIME_INDEX]).format(dateTimeFormat); text += formatRow( [timestamp].concat( - seriesList.map(series => { - return series.datapoints[i][POINT_VALUE_INDEX]; + extendedDatapointsList.map(datapoints => { + return datapoints[i][POINT_VALUE_INDEX]; }) ), - i < seriesList[0].datapoints.length - 1 + i < extendedDatapointsList[0].length - 1 ); } @@ -120,22 +120,23 @@ function mergeSeriesByTime(seriesList) { } timestamps = sortedUniq(timestamps.sort()); + const result = []; for (let i = 0; i < seriesList.length; i++) { const seriesPoints = seriesList[i].datapoints; const seriesTimestamps = seriesPoints.map(p => p[POINT_TIME_INDEX]); - const extendedSeries = []; - let pointIndex; + const extendedDatapoints = []; for (let j = 0; j < timestamps.length; j++) { - pointIndex = sortedIndexOf(seriesTimestamps, timestamps[j]); + const timestamp = timestamps[j]; + const pointIndex = sortedIndexOf(seriesTimestamps, timestamp); if (pointIndex !== -1) { - extendedSeries.push(seriesPoints[pointIndex]); + extendedDatapoints.push(seriesPoints[pointIndex]); } else { - extendedSeries.push([null, timestamps[j]]); + extendedDatapoints.push([null, timestamp]); } } - seriesList[i].datapoints = extendedSeries; + result.push(extendedDatapoints); } - return seriesList; + return result; } export function exportSeriesListToCsvColumns(seriesList, dateTimeFormat = DEFAULT_DATETIME_FORMAT, excel = false) { From 3a65e27e83eb0d28b82f6055df46d1a74271f644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Mon, 4 Mar 2019 10:42:59 +0100 Subject: [PATCH 081/244] Fixes #15739 --- packages/grafana-ui/src/types/datasource.ts | 12 +++++++++++- public/app/features/dashboard/dashgrid/DataPanel.tsx | 5 ++++- .../app/features/dashboard/dashgrid/PanelChrome.tsx | 1 + .../dashboard/dashgrid/PanelHeader/PanelHeader.tsx | 3 ++- .../dashgrid/PanelHeader/PanelHeaderCorner.tsx | 5 +++-- public/app/features/dashboard/state/PanelModel.ts | 4 ++-- 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/grafana-ui/src/types/datasource.ts b/packages/grafana-ui/src/types/datasource.ts index a34f39b59c6..79c5b22488d 100644 --- a/packages/grafana-ui/src/types/datasource.ts +++ b/packages/grafana-ui/src/types/datasource.ts @@ -39,6 +39,16 @@ export interface DataQueryError { statusText?: string; } +export interface ScopedVar { + text: any; + value: any; + [key: string]: any; +} + +export interface ScopedVars { + [key: string]: ScopedVar; +} + export interface DataQueryOptions { timezone: string; range: TimeRange; @@ -50,7 +60,7 @@ export interface DataQueryOptions { interval: string; intervalMs: number; maxDataPoints: number; - scopedVars: object; + scopedVars: ScopedVars; } export interface QueryFix { diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index 9718e150e2a..ae3486e40fe 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -15,6 +15,7 @@ import { TableData, TimeRange, TimeSeries, + ScopedVars, } from '@grafana/ui'; interface RenderProps { @@ -33,6 +34,7 @@ export interface Props { refreshCounter: number; minInterval?: string; maxDataPoints?: number; + scopedVars?: ScopedVars; children: (r: RenderProps) => JSX.Element; onDataResponse?: (data: DataQueryResponse) => void; onError: (message: string, error: DataQueryError) => void; @@ -95,6 +97,7 @@ export class DataPanel extends Component { timeRange, widthPixels, maxDataPoints, + scopedVars, onDataResponse, onError, } = this.props; @@ -127,7 +130,7 @@ export class DataPanel extends Component { intervalMs: intervalRes.intervalMs, targets: queries, maxDataPoints: maxDataPoints || widthPixels, - scopedVars: {}, + scopedVars: scopedVars || {}, cacheTimeout: null, }; diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index cb6d1529b94..16f8f0820bb 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -179,6 +179,7 @@ export class PanelChrome extends PureComponent { isVisible={this.isVisible} widthPixels={width} refreshCounter={refreshCounter} + scopedVars={panel.scopedVars} onDataResponse={this.onDataResponse} onError={this.onDataError} > diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx index 0f6563836f0..a8a3560743f 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import classNames from 'classnames'; import { isEqual } from 'lodash'; +import { ScopedVars } from '@grafana/ui'; import PanelHeaderCorner from './PanelHeaderCorner'; import { PanelHeaderMenu } from './PanelHeaderMenu'; @@ -16,7 +17,7 @@ export interface Props { timeInfo: string; title?: string; description?: string; - scopedVars?: string; + scopedVars?: ScopedVars; links?: []; error?: string; isFullscreen: boolean; diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx index 42b89bc1273..12f3b95ca21 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderCorner.tsx @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import Remarkable from 'remarkable'; -import { Tooltip } from '@grafana/ui'; +import { Tooltip, ScopedVars } from '@grafana/ui'; + import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import templateSrv from 'app/features/templating/template_srv'; import { LinkSrv } from 'app/features/panel/panellinks/link_srv'; @@ -16,7 +17,7 @@ interface Props { panel: PanelModel; title?: string; description?: string; - scopedVars?: string; + scopedVars?: ScopedVars; links?: []; error?: string; } diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index ac3722d61c2..c0739b6d8bc 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; // Types import { Emitter } from 'app/core/utils/emitter'; -import { DataQuery, TimeSeries, Threshold } from '@grafana/ui'; +import { DataQuery, TimeSeries, Threshold, ScopedVars } from '@grafana/ui'; import { TableData } from '@grafana/ui/src'; export interface GridPos { @@ -71,7 +71,7 @@ export class PanelModel { type: string; title: string; alert?: any; - scopedVars?: any; + scopedVars?: ScopedVars; repeat?: string; repeatIteration?: number; repeatPanelId?: number; From 818ccb572e703ad23b5e3917d2ddbf9f7fac1a86 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 4 Mar 2019 11:22:30 +0100 Subject: [PATCH 082/244] devenv: fixes incorrect influxdb config. [skip ci] --- devenv/docker/blocks/influxdb/docker-compose.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/devenv/docker/blocks/influxdb/docker-compose.yaml b/devenv/docker/blocks/influxdb/docker-compose.yaml index e3ca81ced22..f34a7ef4978 100644 --- a/devenv/docker/blocks/influxdb/docker-compose.yaml +++ b/devenv/docker/blocks/influxdb/docker-compose.yaml @@ -1,5 +1,3 @@ -version: '2' -services: influxdb: image: influxdb:latest container_name: influxdb From cc40a515be3362e5fd213f794d05579e446aec18 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Mon, 4 Mar 2019 13:46:10 +0100 Subject: [PATCH 083/244] fix: When in tv-mode, autofitpanel should not take space from the navbar #15650 --- public/app/features/dashboard/state/DashboardModel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index 17af2dbd801..2a445c9a58c 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -887,8 +887,8 @@ export class DashboardModel { } // add back navbar height - if (kioskMode === KIOSK_MODE_TV) { - visibleHeight += 55; + if (kioskMode && kioskMode !== KIOSK_MODE_TV) { + visibleHeight += navbarHeight; } const visibleGridHeight = Math.floor(visibleHeight / (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN)); From ae23773db3db95cb5c84fb4b56bb78cb9cc8290b Mon Sep 17 00:00:00 2001 From: "Bryan T. Richardson" Date: Sun, 27 Jan 2019 14:48:40 -0700 Subject: [PATCH 084/244] Added MaximumUsedTransactionIDs metric to list of AWS RDS metrics. --- pkg/tsdb/cloudwatch/metric_find_query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index cb95d39d82d..83fafbd87b5 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -101,7 +101,7 @@ func init() { "AWS/NetworkELB": {"ActiveFlowCount", "ConsumedLCUs", "HealthyHostCount", "NewFlowCount", "ProcessedBytes", "TCP_Client_Reset_Count", "TCP_ELB_Reset_Count", "TCP_Target_Reset_Count", "UnHealthyHostCount"}, "AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"}, "AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "QueriesCompletedPerSecond", "QueryDuration", "QueryRuntimeBreakdown", "ReadIOPS", "ReadLatency", "ReadThroughput", "WLMQueriesCompletedPerSecond", "WLMQueryDuration", "WLMQueueLength", "WriteIOPS", "WriteLatency", "WriteThroughput"}, - "AWS/RDS": {"ActiveTransactions", "AuroraBinlogReplicaLag", "AuroraReplicaLag", "AuroraReplicaLagMaximum", "AuroraReplicaLagMinimum", "BinLogDiskUsage", "BlockedTransactions", "BufferCacheHitRatio", "BurstBalance", "CommitLatency", "CommitThroughput", "BinLogDiskUsage", "CPUCreditBalance", "CPUCreditUsage", "CPUUtilization", "DatabaseConnections", "DDLLatency", "DDLThroughput", "Deadlocks", "DeleteLatency", "DeleteThroughput", "DiskQueueDepth", "DMLLatency", "DMLThroughput", "EngineUptime", "FailedSqlStatements", "FreeableMemory", "FreeLocalStorage", "FreeStorageSpace", "InsertLatency", "InsertThroughput", "LoginFailures", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "NetworkThroughput", "Queries", "ReadIOPS", "ReadLatency", "ReadThroughput", "ReplicaLag", "ResultSetCacheHitRatio", "SelectLatency", "SelectThroughput", "ServerlessDatabaseCapacity", "SwapUsage", "TotalConnections", "UpdateLatency", "UpdateThroughput", "VolumeBytesUsed", "VolumeReadIOPS", "VolumeWriteIOPS", "WriteIOPS", "WriteLatency", "WriteThroughput"}, + "AWS/RDS": {"ActiveTransactions", "AuroraBinlogReplicaLag", "AuroraReplicaLag", "AuroraReplicaLagMaximum", "AuroraReplicaLagMinimum", "BinLogDiskUsage", "BlockedTransactions", "BufferCacheHitRatio", "BurstBalance", "CommitLatency", "CommitThroughput", "BinLogDiskUsage", "CPUCreditBalance", "CPUCreditUsage", "CPUUtilization", "DatabaseConnections", "DDLLatency", "DDLThroughput", "Deadlocks", "DeleteLatency", "DeleteThroughput", "DiskQueueDepth", "DMLLatency", "DMLThroughput", "EngineUptime", "FailedSqlStatements", "FreeableMemory", "FreeLocalStorage", "FreeStorageSpace", "InsertLatency", "InsertThroughput", "LoginFailures", "MaximumUsedTransactionIDs", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "NetworkThroughput", "Queries", "ReadIOPS", "ReadLatency", "ReadThroughput", "ReplicaLag", "ResultSetCacheHitRatio", "SelectLatency", "SelectThroughput", "ServerlessDatabaseCapacity", "SwapUsage", "TotalConnections", "UpdateLatency", "UpdateThroughput", "VolumeBytesUsed", "VolumeReadIOPS", "VolumeWriteIOPS", "WriteIOPS", "WriteLatency", "WriteThroughput"}, "AWS/Route53": {"ChildHealthCheckHealthyCount", "HealthCheckStatus", "HealthCheckPercentageHealthy", "ConnectionTime", "SSLHandshakeTime", "TimeToFirstByte"}, "AWS/S3": {"BucketSizeBytes", "NumberOfObjects", "AllRequests", "GetRequests", "PutRequests", "DeleteRequests", "HeadRequests", "PostRequests", "ListRequests", "BytesDownloaded", "BytesUploaded", "4xxErrors", "5xxErrors", "FirstByteLatency", "TotalRequestLatency"}, "AWS/SES": {"Bounce", "Complaint", "Delivery", "Reject", "Send", "Reputation.BounceRate", "Reputation.ComplaintRate"}, From a29b99b96b1e668640c7d9ce10b78f55e74fba6e Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Mar 2019 15:48:07 +0100 Subject: [PATCH 085/244] only editor/admin should have access to alert list/notifications pages --- pkg/api/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 6da127fb550..82f660a2bd6 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -77,8 +77,8 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/playlists/", reqSignedIn, hs.Index) r.Get("/playlists/*", reqSignedIn, hs.Index) - r.Get("/alerting/", reqSignedIn, hs.Index) - r.Get("/alerting/*", reqSignedIn, hs.Index) + r.Get("/alerting/", reqEditorRole, hs.Index) + r.Get("/alerting/*", reqEditorRole, hs.Index) // sign up r.Get("/signup", hs.Index) From 5638c67be89df2fa72b9821f89d7af15affddf86 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Mar 2019 15:51:18 +0100 Subject: [PATCH 086/244] org admins should only be able to access org admin pages --- pkg/api/api.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 82f660a2bd6..c80129eac6f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -33,17 +33,17 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/profile/", reqSignedIn, hs.Index) r.Get("/profile/password", reqSignedIn, hs.Index) r.Get("/profile/switch-org/:id", reqSignedIn, hs.ChangeActiveOrgAndRedirectToHome) - r.Get("/org/", reqSignedIn, hs.Index) - r.Get("/org/new", reqSignedIn, hs.Index) - r.Get("/datasources/", reqSignedIn, hs.Index) - r.Get("/datasources/new", reqSignedIn, hs.Index) - r.Get("/datasources/edit/*", reqSignedIn, hs.Index) - r.Get("/org/users", reqSignedIn, hs.Index) - r.Get("/org/users/new", reqSignedIn, hs.Index) - r.Get("/org/users/invite", reqSignedIn, hs.Index) - r.Get("/org/teams", reqSignedIn, hs.Index) - r.Get("/org/teams/*", reqSignedIn, hs.Index) - r.Get("/org/apikeys/", reqSignedIn, hs.Index) + r.Get("/org/", reqOrgAdmin, hs.Index) + r.Get("/org/new", reqGrafanaAdmin, hs.Index) + r.Get("/datasources/", reqOrgAdmin, hs.Index) + r.Get("/datasources/new", reqOrgAdmin, hs.Index) + r.Get("/datasources/edit/*", reqOrgAdmin, hs.Index) + r.Get("/org/users", reqOrgAdmin, hs.Index) + r.Get("/org/users/new", reqOrgAdmin, hs.Index) + r.Get("/org/users/invite", reqOrgAdmin, hs.Index) + r.Get("/org/teams", reqOrgAdmin, hs.Index) + r.Get("/org/teams/*", reqOrgAdmin, hs.Index) + r.Get("/org/apikeys/", reqOrgAdmin, hs.Index) r.Get("/dashboard/import/", reqSignedIn, hs.Index) r.Get("/configuration", reqGrafanaAdmin, hs.Index) r.Get("/admin", reqGrafanaAdmin, hs.Index) From c36047674aa90803d550445e32c035000be32fef Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Mar 2019 16:07:51 +0100 Subject: [PATCH 087/244] changelog: add notes about closing #15077 --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a458a07a76e..8095dd1e3be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ - # 6.1.0 (unreleased) +### Minor +* **Cloudwatch**: Add AWS RDS MaximumUsedTransactionIDs metric [#15077](https://github.com/grafana/grafana/pull/15077), thx [@activeshadow](https://github.com/activeshadow) + ### Bug Fixes * **Api**: Invalid org invite code [#10506](https://github.com/grafana/grafana/issues/10506) * **Datasource**: Handles nil jsondata field gracefully [#14239](https://github.com/grafana/grafana/issues/14239) From 92ec8757d3f20e5b5f0d9d530028577cbc4fa94f Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Mon, 4 Mar 2019 16:33:00 +0100 Subject: [PATCH 088/244] fix: Kiosk mode should have &kiosk appended to the url #15765 --- public/app/routes/GrafanaCtrl.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/app/routes/GrafanaCtrl.ts b/public/app/routes/GrafanaCtrl.ts index d327bc0cf7d..479c5e77f3d 100644 --- a/public/app/routes/GrafanaCtrl.ts +++ b/public/app/routes/GrafanaCtrl.ts @@ -75,7 +75,7 @@ export class GrafanaCtrl { } } -function setViewModeBodyClass(body, mode: KioskUrlValue, sidemenuOpen: boolean) { +function setViewModeBodyClass(body: JQuery, mode: KioskUrlValue, sidemenuOpen: boolean) { body.removeClass('view-mode--tv'); body.removeClass('view-mode--kiosk'); body.removeClass('view-mode--inactive'); @@ -174,8 +174,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop }); // handle kiosk mode - appEvents.on('toggle-kiosk-mode', options => { - const search = $location.search(); + appEvents.on('toggle-kiosk-mode', (options: { exit?: boolean }) => { + const search: { kiosk?: KioskUrlValue } = $location.search(); if (options && options.exit) { search.kiosk = '1'; @@ -197,7 +197,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop } } - $location.search(search); + $timeout(() => $location.search(search)); setViewModeBodyClass(body, search.kiosk, sidemenuOpen); }); From a6056ba565b6f8ddd20c287cc498b24405600d4b Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Mar 2019 17:38:27 +0100 Subject: [PATCH 089/244] changelog: add notes about closing #15765 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8095dd1e3be..28da1ff05ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Bug Fixes * **Metrics**: Fixes broken usagestats metrics for /metrics [#15651](https://github.com/grafana/grafana/issues/15651) +* **Dashboard**: Fixes kiosk mode should have &kiosk appended to the url [#15765](https://github.com/grafana/grafana/issues/15765) # 6.0.0 stable (2019-02-25) From 989147a1ce1b076386735d6a1238ca067fd0adde Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Mar 2019 17:47:16 +0100 Subject: [PATCH 090/244] changelog: add notes about closing #15650 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28da1ff05ef..c2a9760dcd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Bug Fixes * **Metrics**: Fixes broken usagestats metrics for /metrics [#15651](https://github.com/grafana/grafana/issues/15651) * **Dashboard**: Fixes kiosk mode should have &kiosk appended to the url [#15765](https://github.com/grafana/grafana/issues/15765) +* **Dashboard**: Fixes kiosk=tv mode with autofitpanels should respect header [#15650](https://github.com/grafana/grafana/issues/15650) # 6.0.0 stable (2019-02-25) From 77d709d9dff5e2bb200b7c5d705eda25ddf72124 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 4 Mar 2019 11:21:05 -0800 Subject: [PATCH 091/244] use updateOptions rather than onChange --- packages/grafana-ui/src/types/panel.ts | 2 +- .../dashboard/panel_editor/VisualizationTab.tsx | 2 +- public/app/plugins/panel/gauge/GaugeOptionsBox.tsx | 8 ++++---- public/app/plugins/panel/gauge/GaugePanelEditor.tsx | 10 +++++----- public/app/plugins/panel/graph2/GraphPanelEditor.tsx | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 2da48b0fec6..0cd4bb8a1a4 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -22,7 +22,7 @@ export interface PanelData { export interface PanelEditorProps { options: T; - onChange: (options: T) => void; + updateOptions: (options: T) => void; } export class ReactPanelPlugin { diff --git a/public/app/features/dashboard/panel_editor/VisualizationTab.tsx b/public/app/features/dashboard/panel_editor/VisualizationTab.tsx index 8a904961a4f..41fc5988256 100644 --- a/public/app/features/dashboard/panel_editor/VisualizationTab.tsx +++ b/public/app/features/dashboard/panel_editor/VisualizationTab.tsx @@ -66,7 +66,7 @@ export class VisualizationTab extends PureComponent { const PanelEditor = plugin.exports.reactPanel.editor; if (PanelEditor) { - return ; + return ; } } diff --git a/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx b/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx index b5d6acca806..ea2f10f4629 100644 --- a/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx +++ b/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx @@ -10,14 +10,14 @@ import { GaugeOptions } from './types'; export class GaugeOptionsBox extends PureComponent> { onToggleThresholdLabels = () => - this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); + this.props.updateOptions({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); onToggleThresholdMarkers = () => - this.props.onChange({ ...this.props.options, showThresholdMarkers: !this.props.options.showThresholdMarkers }); + this.props.updateOptions({ ...this.props.options, showThresholdMarkers: !this.props.options.showThresholdMarkers }); - onMinValueChange = ({ target }) => this.props.onChange({ ...this.props.options, minValue: target.value }); + onMinValueChange = ({ target }) => this.props.updateOptions({ ...this.props.options, minValue: target.value }); - onMaxValueChange = ({ target }) => this.props.onChange({ ...this.props.options, maxValue: target.value }); + onMaxValueChange = ({ target }) => this.props.updateOptions({ ...this.props.options, maxValue: target.value }); render() { const { options } = this.props; diff --git a/public/app/plugins/panel/gauge/GaugePanelEditor.tsx b/public/app/plugins/panel/gauge/GaugePanelEditor.tsx index 63031f9d895..0109de33fc5 100644 --- a/public/app/plugins/panel/gauge/GaugePanelEditor.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelEditor.tsx @@ -14,31 +14,31 @@ import { GaugeOptions, SingleStatValueOptions } from './types'; export class GaugePanelEditor extends PureComponent> { onThresholdsChanged = (thresholds: Threshold[]) => - this.props.onChange({ + this.props.updateOptions({ ...this.props.options, thresholds, }); onValueMappingsChanged = (valueMappings: ValueMapping[]) => - this.props.onChange({ + this.props.updateOptions({ ...this.props.options, valueMappings, }); onValueOptionsChanged = (valueOptions: SingleStatValueOptions) => - this.props.onChange({ + this.props.updateOptions({ ...this.props.options, valueOptions, }); render() { - const { onChange, options } = this.props; + const { updateOptions, options } = this.props; return ( <> - + diff --git a/public/app/plugins/panel/graph2/GraphPanelEditor.tsx b/public/app/plugins/panel/graph2/GraphPanelEditor.tsx index 80b17ccd5c4..9141324274a 100644 --- a/public/app/plugins/panel/graph2/GraphPanelEditor.tsx +++ b/public/app/plugins/panel/graph2/GraphPanelEditor.tsx @@ -8,15 +8,15 @@ import { Options } from './types'; export class GraphPanelEditor extends PureComponent> { onToggleLines = () => { - this.props.onChange({ ...this.props.options, showLines: !this.props.options.showLines }); + this.props.updateOptions({ ...this.props.options, showLines: !this.props.options.showLines }); }; onToggleBars = () => { - this.props.onChange({ ...this.props.options, showBars: !this.props.options.showBars }); + this.props.updateOptions({ ...this.props.options, showBars: !this.props.options.showBars }); }; onTogglePoints = () => { - this.props.onChange({ ...this.props.options, showPoints: !this.props.options.showPoints }); + this.props.updateOptions({ ...this.props.options, showPoints: !this.props.options.showPoints }); }; render() { From 251008f590c1e89672a41aa521851074d79fdeda Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 4 Mar 2019 11:44:38 -0800 Subject: [PATCH 092/244] use replaceVariables rather than onInterpolate --- packages/grafana-ui/src/types/panel.ts | 2 +- public/app/features/dashboard/dashgrid/PanelChrome.tsx | 4 ++-- public/app/plugins/panel/gauge/GaugePanel.tsx | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 2da48b0fec6..6cc56c9287d 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -12,7 +12,7 @@ export interface PanelProps { renderCounter: number; width: number; height: number; - onInterpolate: InterpolateFunction; + replaceVariables: InterpolateFunction; } export interface PanelData { diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 16f8f0820bb..80ce2f39b70 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -85,7 +85,7 @@ export class PanelChrome extends PureComponent { }); }; - onInterpolate = (value: string, format?: string) => { + replaceVariables = (value: string, format?: string) => { return templateSrv.replace(value, this.props.panel.scopedVars, format); }; @@ -158,7 +158,7 @@ export class PanelChrome extends PureComponent { width={width - 2 * variables.panelhorizontalpadding} height={height - PANEL_HEADER_HEIGHT - variables.panelverticalpadding} renderCounter={renderCounter} - onInterpolate={this.onInterpolate} + replaceVariables={this.replaceVariables} />
); diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index e7e60a7c417..2a42e31b9ab 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -15,11 +15,11 @@ interface Props extends PanelProps {} export class GaugePanel extends PureComponent { render() { - const { panelData, width, height, onInterpolate, options } = this.props; + const { panelData, width, height, replaceVariables, options } = this.props; const { valueOptions } = options; - const prefix = onInterpolate(valueOptions.prefix); - const suffix = onInterpolate(valueOptions.suffix); + const prefix = replaceVariables(valueOptions.prefix); + const suffix = replaceVariables(valueOptions.suffix); let value: TimeSeriesValue; if (panelData.timeSeries) { From 3d165284590d25352b0f9969368639859e04e5c6 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 4 Mar 2019 12:35:24 -0800 Subject: [PATCH 093/244] use onOptionsChange --- packages/grafana-ui/src/types/panel.ts | 2 +- .../dashboard/panel_editor/VisualizationTab.tsx | 2 +- public/app/plugins/panel/gauge/GaugeOptionsBox.tsx | 11 +++++++---- public/app/plugins/panel/gauge/GaugePanelEditor.tsx | 10 +++++----- public/app/plugins/panel/graph2/GraphPanelEditor.tsx | 6 +++--- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 0cd4bb8a1a4..81068083803 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -22,7 +22,7 @@ export interface PanelData { export interface PanelEditorProps { options: T; - updateOptions: (options: T) => void; + onOptionsChange: (options: T) => void; } export class ReactPanelPlugin { diff --git a/public/app/features/dashboard/panel_editor/VisualizationTab.tsx b/public/app/features/dashboard/panel_editor/VisualizationTab.tsx index 41fc5988256..5330baf1be6 100644 --- a/public/app/features/dashboard/panel_editor/VisualizationTab.tsx +++ b/public/app/features/dashboard/panel_editor/VisualizationTab.tsx @@ -66,7 +66,7 @@ export class VisualizationTab extends PureComponent { const PanelEditor = plugin.exports.reactPanel.editor; if (PanelEditor) { - return ; + return ; } } diff --git a/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx b/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx index ea2f10f4629..bb3043ddd8b 100644 --- a/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx +++ b/public/app/plugins/panel/gauge/GaugeOptionsBox.tsx @@ -10,14 +10,17 @@ import { GaugeOptions } from './types'; export class GaugeOptionsBox extends PureComponent> { onToggleThresholdLabels = () => - this.props.updateOptions({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); + this.props.onOptionsChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels }); onToggleThresholdMarkers = () => - this.props.updateOptions({ ...this.props.options, showThresholdMarkers: !this.props.options.showThresholdMarkers }); + this.props.onOptionsChange({ + ...this.props.options, + showThresholdMarkers: !this.props.options.showThresholdMarkers, + }); - onMinValueChange = ({ target }) => this.props.updateOptions({ ...this.props.options, minValue: target.value }); + onMinValueChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, minValue: target.value }); - onMaxValueChange = ({ target }) => this.props.updateOptions({ ...this.props.options, maxValue: target.value }); + onMaxValueChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, maxValue: target.value }); render() { const { options } = this.props; diff --git a/public/app/plugins/panel/gauge/GaugePanelEditor.tsx b/public/app/plugins/panel/gauge/GaugePanelEditor.tsx index 0109de33fc5..f226be7328c 100644 --- a/public/app/plugins/panel/gauge/GaugePanelEditor.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelEditor.tsx @@ -14,31 +14,31 @@ import { GaugeOptions, SingleStatValueOptions } from './types'; export class GaugePanelEditor extends PureComponent> { onThresholdsChanged = (thresholds: Threshold[]) => - this.props.updateOptions({ + this.props.onOptionsChange({ ...this.props.options, thresholds, }); onValueMappingsChanged = (valueMappings: ValueMapping[]) => - this.props.updateOptions({ + this.props.onOptionsChange({ ...this.props.options, valueMappings, }); onValueOptionsChanged = (valueOptions: SingleStatValueOptions) => - this.props.updateOptions({ + this.props.onOptionsChange({ ...this.props.options, valueOptions, }); render() { - const { updateOptions, options } = this.props; + const { onOptionsChange, options } = this.props; return ( <> - + diff --git a/public/app/plugins/panel/graph2/GraphPanelEditor.tsx b/public/app/plugins/panel/graph2/GraphPanelEditor.tsx index 9141324274a..1a64290759c 100644 --- a/public/app/plugins/panel/graph2/GraphPanelEditor.tsx +++ b/public/app/plugins/panel/graph2/GraphPanelEditor.tsx @@ -8,15 +8,15 @@ import { Options } from './types'; export class GraphPanelEditor extends PureComponent> { onToggleLines = () => { - this.props.updateOptions({ ...this.props.options, showLines: !this.props.options.showLines }); + this.props.onOptionsChange({ ...this.props.options, showLines: !this.props.options.showLines }); }; onToggleBars = () => { - this.props.updateOptions({ ...this.props.options, showBars: !this.props.options.showBars }); + this.props.onOptionsChange({ ...this.props.options, showBars: !this.props.options.showBars }); }; onTogglePoints = () => { - this.props.updateOptions({ ...this.props.options, showPoints: !this.props.options.showPoints }); + this.props.onOptionsChange({ ...this.props.options, showPoints: !this.props.options.showPoints }); }; render() { From 8f9246dff0d5a9ccaafa42bef202a4a231b1f2a8 Mon Sep 17 00:00:00 2001 From: Julien Pivotto Date: Mon, 4 Mar 2019 23:17:33 +0100 Subject: [PATCH 094/244] Add #15752 in CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2a9760dcd5..c010a279c73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Bug Fixes * **Api**: Invalid org invite code [#10506](https://github.com/grafana/grafana/issues/10506) * **Datasource**: Handles nil jsondata field gracefully [#14239](https://github.com/grafana/grafana/issues/14239) +* **Gauge**: Interpolate scoped variables in repeated gauges [#15732](https://github.com/grafana/grafana/pull/15752) # 6.0.1 (unreleased) From cee5f030dc8d9bdd1ea3b3b4837fadb626603d93 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Mar 2019 07:35:45 +0100 Subject: [PATCH 095/244] Fixed url of back button in datasource edit page, when root_url configured (#15759) * Fixed url of back button in datasource edit page, when root_url configured * Update snapshots * Use app config directly in ButtonRow instead of passing datasources page URL via prop * Snapshots update --- public/app/features/datasources/settings/ButtonRow.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/app/features/datasources/settings/ButtonRow.tsx b/public/app/features/datasources/settings/ButtonRow.tsx index 9f633ee6bcf..5bae5e7f98d 100644 --- a/public/app/features/datasources/settings/ButtonRow.tsx +++ b/public/app/features/datasources/settings/ButtonRow.tsx @@ -1,4 +1,5 @@ import React, { FC } from 'react'; +import config from 'app/core/config'; export interface Props { isReadOnly: boolean; @@ -23,7 +24,7 @@ const ButtonRow: FC = ({ isReadOnly, onDelete, onSubmit, onTest }) => { - + Back
From 73ef864979b4ab21b81c587154bfd5961ade734b Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Mar 2019 08:56:29 +0100 Subject: [PATCH 096/244] Minor refactor of cli tasks (core start, gui publishing) --- package.json | 15 ++-- scripts/cli/index.ts | 66 +++++++++------ scripts/cli/tasks/core.start.ts | 41 +++++---- scripts/cli/tasks/grafanaui.build.ts | 101 +++++++++------------- scripts/cli/tasks/grafanaui.release.ts | 111 +++++++++++++------------ scripts/cli/tasks/task.ts | 23 +++++ scripts/cli/utils/execTask.ts | 17 +++- scripts/cli/utils/startSpinner.ts | 7 -- scripts/cli/utils/useSpinner.ts | 20 +++++ 9 files changed, 219 insertions(+), 182 deletions(-) create mode 100644 scripts/cli/tasks/task.ts delete mode 100644 scripts/cli/utils/startSpinner.ts create mode 100644 scripts/cli/utils/useSpinner.ts diff --git a/package.json b/package.json index 9e0b88804fd..c5136cf5e02 100644 --- a/package.json +++ b/package.json @@ -123,10 +123,10 @@ }, "scripts": { "dev": "webpack --progress --colors --mode development --config scripts/webpack/webpack.dev.js", - "start": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --theme", - "start:hot": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --hot --theme", - "start:ignoreTheme": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --hot", - "watch": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --theme -d watch,start", + "start": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts core:start --watchTheme", + "start:hot": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts core:start --hot --watchTheme", + "start:ignoreTheme": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts core:start --hot", + "watch": "yarn start -d watch,start core:start --watchTheme ", "build": "grunt build", "test": "grunt test", "tslint": "tslint -c tslint.json --project tsconfig.json", @@ -136,8 +136,11 @@ "storybook": "cd packages/grafana-ui && yarn storybook", "themes:generate": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/generateSassVariableFiles.ts", "prettier:check": "prettier --list-different \"**/*.{ts,tsx,scss}\"", - "gui:build": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --build", - "gui:release": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --release" + "gui:build": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:build", + "gui:releasePrepare": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:release", + "gui:publish": "cd packages/grafana-ui/dist && npm publish --access public", + "gui:release": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:release -p", + "cli:help": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --help" }, "husky": { "hooks": { diff --git a/scripts/cli/index.ts b/scripts/cli/index.ts index f980944e2cd..3e54dc97a07 100644 --- a/scripts/cli/index.ts +++ b/scripts/cli/index.ts @@ -1,33 +1,47 @@ import program from 'commander'; -import chalk from 'chalk'; import { execTask } from './utils/execTask'; +import chalk from 'chalk'; +import { startTask } from './tasks/core.start'; +import { buildTask } from './tasks/grafanaui.build'; +import { releaseTask } from './tasks/grafanaui.release'; -export type Task = (options: T) => Promise; +program.option('-d, --depreciate ', 'Inform about npm script deprecation', v => v.split(',')); -// TODO: Refactor to commander commands -// This will enable us to have command scoped options and limit the ifs below program - .option('-h, --hot', 'Runs front-end with hot reload enabled') - .option('-t, --theme', 'Watches for theme changes and regenerates variables.scss files') - .option('-d, --depreciate ', 'Inform about npm script deprecation', v => v.split(',')) - .option('-b, --build', 'Created @grafana/ui build') - .option('-r, --release', 'Releases @grafana/ui to npm') - .parse(process.argv); - -if (program.build) { - execTask('grafanaui.build'); -} else if (program.release) { - execTask('grafanaui.release'); -} else { - if (program.depreciate && program.depreciate.length === 2) { - console.log( - chalk.yellow.bold( - `[NPM script depreciation] ${program.depreciate[0]} is deprecated! Use ${program.depreciate[1]} instead!` - ) - ); - } - execTask('core.start', { - watchThemes: !!program.theme, - hot: !!program.hot, + .command('core:start') + .option('-h, --hot', 'Run front-end with HRM enabled') + .option('-t, --watchTheme', 'Watch for theme changes and regenerate variables.scss files') + .description('Starts Grafana front-end in development mode with watch enabled') + .action(async cmd => { + await execTask(startTask)({ + watchThemes: cmd.theme, + hot: cmd.hot, + }); }); + +program + .command('gui:build') + .description('Builds @grafana/ui package to packages/grafana-ui/dist') + .action(async cmd => { + await execTask(buildTask)(); + }); + +program + .command('gui:release') + .description('Prepares @grafana/ui release (and publishes to npm on demand)') + .option('-p, --publish', 'Publish @grafana/ui to npm registry') + .action(async cmd => { + await execTask(releaseTask)({ + publishToNpm: !!cmd.publish, + }); + }); + +program.parse(process.argv); + +if (program.depreciate && program.depreciate.length === 2) { + console.log( + chalk.yellow.bold( + `[NPM script depreciation] ${program.depreciate[0]} is deprecated! Use ${program.depreciate[1]} instead!` + ) + ); } diff --git a/scripts/cli/tasks/core.start.ts b/scripts/cli/tasks/core.start.ts index 4e546c71b8c..dbd2dfe7119 100644 --- a/scripts/cli/tasks/core.start.ts +++ b/scripts/cli/tasks/core.start.ts @@ -1,35 +1,30 @@ import concurrently from 'concurrently'; -import { Task } from '..'; +import { Task, TaskRunner } from './task'; interface StartTaskOptions { watchThemes: boolean; hot: boolean; } -const startTask: Task = async ({ watchThemes, hot }) => { - const jobs = []; - - if (watchThemes) { - jobs.push({ +const startTaskRunner: TaskRunner = async ({ watchThemes, hot }) => { + const jobs = [ + watchThemes && { command: 'nodemon -e ts -w ./packages/grafana-ui/src/themes -x yarn run themes:generate', name: 'SASS variables generator', - }); - } - - if (!hot) { - jobs.push({ - command: 'webpack --progress --colors --watch --mode development --config scripts/webpack/webpack.dev.js', - name: 'Webpack', - }); - } else { - jobs.push({ - command: 'webpack-dev-server --progress --colors --mode development --config scripts/webpack/webpack.hot.js', - name: 'Dev server', - }); - } + }, + hot + ? { + command: 'webpack-dev-server --progress --colors --mode development --config scripts/webpack/webpack.hot.js', + name: 'Dev server', + } + : { + command: 'webpack --progress --colors --watch --mode development --config scripts/webpack/webpack.dev.js', + name: 'Webpack', + }, + ]; try { - await concurrently(jobs, { + await concurrently(jobs.filter(job => !!job), { killOthers: ['failure', 'failure'], }); } catch (e) { @@ -38,4 +33,6 @@ const startTask: Task = async ({ watchThemes, hot }) => { } }; -export default startTask; +export const startTask = new Task(); +startTask.setName('Core startTask'); +startTask.setRunner(startTaskRunner); diff --git a/scripts/cli/tasks/grafanaui.build.ts b/scripts/cli/tasks/grafanaui.build.ts index f892c13e115..6fce809bef9 100644 --- a/scripts/cli/tasks/grafanaui.build.ts +++ b/scripts/cli/tasks/grafanaui.build.ts @@ -1,95 +1,66 @@ import execa from 'execa'; import fs from 'fs'; -import { Task } from '..'; import { changeCwdToGrafanaUi, restoreCwd } from '../utils/cwd'; import chalk from 'chalk'; -import { startSpinner } from '../utils/startSpinner'; +import { useSpinner } from '../utils/useSpinner'; +import { Task, TaskRunner } from './task'; let distDir, cwd; -const clean = async () => { - const spinner = startSpinner('Cleaning'); - try { - await execa('npm', ['run', 'clean']); - spinner.succeed(); - } catch (e) { - spinner.fail(); - throw e; - } -}; +const clean = useSpinner('Cleaning', async () => await execa('npm', ['run', 'clean'])); -const compile = async () => { - const spinner = startSpinner('Compiling sources'); - try { - await execa('tsc', ['-p', './tsconfig.build.json']); - spinner.succeed(); - } catch (e) { - console.log(e); - spinner.fail(); - } -}; +const compile = useSpinner('Compiling sources', () => execa('tsc', ['-p', './tsconfig.build.json'])); -const rollup = async () => { - const spinner = startSpinner('Bundling'); - - try { - await execa('npm', ['run', 'build']); - spinner.succeed(); - } catch (e) { - spinner.fail(); - } -}; - -export const savePackage = async (path, pkg) => { - const spinner = startSpinner('Updating package.json'); +const rollup = useSpinner('Bundling', () => execa('npm', ['run', 'build'])); +export const savePackage = useSpinner<{ + path: string; + pkg: {}; +}>('Updating package.json', async ({ path, pkg }) => { return new Promise((resolve, reject) => { fs.writeFile(path, JSON.stringify(pkg, null, 2), err => { if (err) { - spinner.fail(); - console.error(err); reject(err); return; } - spinner.succeed(); resolve(); }); }); -}; +}); const preparePackage = async pkg => { pkg.main = 'index.js'; pkg.types = 'index.d.ts'; - await savePackage(`${cwd}/dist/package.json`, pkg); -}; - -const moveFiles = async () => { - const files = ['README.md', 'CHANGELOG.md', 'index.js']; - const spinner = startSpinner(`Moving ${files.join(', ')} files`); - - const promises = files.map(file => { - return fs.copyFile(`${cwd}/${file}`, `${distDir}/${file}`, err => { - if (err) { - console.error(err); - return; - } - }); + await savePackage({ + path: `${cwd}/dist/package.json`, + pkg, }); - - try { - await Promise.all(promises); - spinner.succeed(); - } catch (e) { - spinner.fail(); - } }; -const buildTask: Task = async () => { +const moveFiles = () => { + const files = ['README.md', 'CHANGELOG.md', 'index.js']; + return useSpinner(`Moving ${files.join(', ')} files`, async () => { + const promises = files.map(file => { + return new Promise((resolve, reject) => { + fs.copyFile(`${cwd}/${file}`, `${distDir}/${file}`, err => { + if (err) { + reject(err); + return; + } + resolve(); + }); + }); + }); + + await Promise.all(promises); + })(); +}; + +const buildTaskRunner: TaskRunner = async () => { cwd = changeCwdToGrafanaUi(); distDir = `${cwd}/dist`; const pkg = require(`${cwd}/package.json`); - - console.log(chalk.yellow(`Building ${pkg.name} @ ${pkg.version}`)); + console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`)); await clean(); await compile(); @@ -100,4 +71,6 @@ const buildTask: Task = async () => { restoreCwd(); }; -export default buildTask; +export const buildTask = new Task(); +buildTask.setName('@grafana/ui build'); +buildTask.setRunner(buildTaskRunner); diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts index 412c96e7fb5..b363ace35e0 100644 --- a/scripts/cli/tasks/grafanaui.release.ts +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -1,14 +1,18 @@ import execa from 'execa'; -import { Task } from '..'; import { execTask } from '../utils/execTask'; import { changeCwdToGrafanaUiDist, changeCwdToGrafanaUi } from '../utils/cwd'; import semver from 'semver'; import inquirer from 'inquirer'; import chalk from 'chalk'; -import { startSpinner } from '../utils/startSpinner'; -import { savePackage } from './grafanaui.build'; +import { useSpinner } from '../utils/useSpinner'; +import { savePackage, buildTask } from './grafanaui.build'; +import { TaskRunner, Task } from './task'; -type VersionBumpType = 'patch' | 'minor' | 'major'; +type VersionBumpType = 'prerelease' | 'patch' | 'minor' | 'major'; + +interface ReleaseTaskOptions { + publishToNpm: boolean; +} const promptBumpType = async () => { return inquirer.prompt<{ type: VersionBumpType }>([ @@ -16,7 +20,7 @@ const promptBumpType = async () => { type: 'list', message: 'Select version bump', name: 'type', - choices: ['patch', 'minor', 'major'], + choices: ['prerelease', 'patch', 'minor', 'major'], validate: answer => { if (answer.length < 1) { return 'You must choose something'; @@ -28,13 +32,13 @@ const promptBumpType = async () => { ]); }; -const promptPrereleaseId = async () => { +const promptPrereleaseId = async (message = 'Is this a prerelease?', allowNo = true) => { return inquirer.prompt<{ id: string }>([ { type: 'list', - message: 'Is this a prerelease?', + message: message, name: 'id', - choices: ['no', 'alpha', 'beta'], + choices: allowNo ? ['no', 'alpha', 'beta'] : ['alpha', 'beta'], validate: answer => { if (answer.length < 1) { return 'You must choose something'; @@ -57,47 +61,31 @@ const promptConfirm = async (message?: string) => { ]); }; -const bumpVersion = async (version: string) => { - const spinner = startSpinner(`Saving version ${version} to package.json`); - changeCwdToGrafanaUi(); - - try { +const bumpVersion = (version: string) => + useSpinner(`Saving version ${version} to package.json`, async () => { + changeCwdToGrafanaUi(); await execa('npm', ['version', version]); - spinner.succeed(); - } catch (e) { - console.log(e); - spinner.fail(); - } + changeCwdToGrafanaUiDist(); + const pkg = require(`${process.cwd()}/package.json`); + pkg.version = version; + await savePackage({ path: `${process.cwd()}/package.json`, pkg }); + })(); - changeCwdToGrafanaUiDist(); - const pkg = require(`${process.cwd()}/package.json`); - pkg.version = version; - await savePackage(`${process.cwd()}/package.json`, pkg); -}; +const publishPackage = (name: string, version: string) => + useSpinner(`Publishing ${name} @ ${version} to npm registry...`, async () => { + changeCwdToGrafanaUiDist(); + console.log(chalk.yellowBright.bold(`\nReview dist package.json before proceeding!\n`)); + const { confirmed } = await promptConfirm('Are you ready to publish to npm?'); -const publishPackage = async (name: string, version: string) => { - changeCwdToGrafanaUiDist(); - console.log(chalk.yellowBright.bold(`\nReview dist package.json before proceeding!\n`)); - const { confirmed } = await promptConfirm('Are you ready to publish to npm?'); - - if (!confirmed) { - process.exit(); - } - - const spinner = startSpinner(`Publishing ${name} @ ${version} to npm registry...`); - - try { + if (!confirmed) { + process.exit(); + } await execa('npm', ['publish', '--access', 'public']); - spinner.succeed(); - } catch (e) { - console.log(e); - spinner.fail(); - process.exit(1); - } -}; + })(); + +const releaseTaskRunner: TaskRunner = async ({ publishToNpm }) => { + await execTask(buildTask)(); -const releaseTask: Task = async () => { - await execTask('grafanaui.build'); let releaseConfirmed = false; let nextVersion; changeCwdToGrafanaUiDist(); @@ -108,12 +96,17 @@ const releaseTask: Task = async () => { do { const { type } = await promptBumpType(); - const { id } = await promptPrereleaseId(); - - if (id !== 'no') { - nextVersion = semver.inc(pkg.version, `pre${type}`, id); + console.log(type); + if (type === 'prerelease') { + const { id } = await promptPrereleaseId('What kind of prerelease?', false); + nextVersion = semver.inc(pkg.version, type, id); } else { - nextVersion = semver.inc(pkg.version, type); + const { id } = await promptPrereleaseId(); + if (id !== 'no') { + nextVersion = semver.inc(pkg.version, `pre${type}`, id); + } else { + nextVersion = semver.inc(pkg.version, type); + } } console.log(chalk.yellowBright.bold(`You are going to release a new version of ${pkg.name}`)); @@ -124,10 +117,22 @@ const releaseTask: Task = async () => { } while (!releaseConfirmed); await bumpVersion(nextVersion); - await publishPackage(pkg.name, nextVersion); - console.log(chalk.green(`\nVersion ${nextVersion} of ${pkg.name} succesfully released!`)); - console.log(chalk.yellow(`\nUpdated @grafana/ui/package.json with version bump created - COMMIT THIS FILE!`)); + if (publishToNpm) { + await publishPackage(pkg.name, nextVersion); + console.log(chalk.green(`\nVersion ${nextVersion} of ${pkg.name} succesfully released!`)); + console.log(chalk.yellow(`\nUpdated @grafana/ui/package.json with version bump created - COMMIT THIS FILE!`)); + process.exit(); + } else { + console.log( + chalk.green( + `\nVersion ${nextVersion} of ${pkg.name} succesfully prepared for release. See packages/grafana-ui/dist` + ) + ); + console.log(chalk.green(`\nTo publish to npm registry run`), chalk.bold.blue(`npm run gui:publish`)); + } }; -export default releaseTask; +export const releaseTask = new Task(); +releaseTask.setName('@grafana/ui release'); +releaseTask.setRunner(releaseTaskRunner); diff --git a/scripts/cli/tasks/task.ts b/scripts/cli/tasks/task.ts new file mode 100644 index 00000000000..d88860b7017 --- /dev/null +++ b/scripts/cli/tasks/task.ts @@ -0,0 +1,23 @@ +export type TaskRunner = (options: T) => Promise; + +export class Task { + name: string; + runner: (options: TOptions) => Promise; + options: TOptions; + + setName = name => { + this.name = name; + }; + + setRunner = (runner: TaskRunner) => { + this.runner = runner; + }; + + setOptions = options => { + this.options = options; + }; + + exec = () => { + return this.runner(this.options); + }; +} diff --git a/scripts/cli/utils/execTask.ts b/scripts/cli/utils/execTask.ts index 36071134331..f404206b7a9 100644 --- a/scripts/cli/utils/execTask.ts +++ b/scripts/cli/utils/execTask.ts @@ -1,6 +1,15 @@ -import { Task } from '..'; +import { Task } from '../tasks/task'; +import chalk from 'chalk'; -export const execTask = async (taskName, options?: T) => { - const task = await import(`${__dirname}/../tasks/${taskName}.ts`); - return task.default(options) as Task; +export const execTask = (task: Task) => async (options: TOptions) => { + console.log(chalk.yellow(`Running ${chalk.bold(task.name)} task`)); + task.setOptions(options); + try { + console.group(); + await task.exec(); + console.groupEnd(); + } catch (e) { + console.log(e); + process.exit(1); + } }; diff --git a/scripts/cli/utils/startSpinner.ts b/scripts/cli/utils/startSpinner.ts deleted file mode 100644 index ce895dec722..00000000000 --- a/scripts/cli/utils/startSpinner.ts +++ /dev/null @@ -1,7 +0,0 @@ -import ora from 'ora'; - -export const startSpinner = (label: string) => { - const spinner = new ora(label); - spinner.start(); - return spinner; -}; diff --git a/scripts/cli/utils/useSpinner.ts b/scripts/cli/utils/useSpinner.ts new file mode 100644 index 00000000000..48167e4ec2a --- /dev/null +++ b/scripts/cli/utils/useSpinner.ts @@ -0,0 +1,20 @@ +import ora from 'ora'; + +type FnToSpin = (options: T) => Promise; + +export const useSpinner = (spinnerLabel: string, fn: FnToSpin, killProcess = true) => { + return async (options: T) => { + const spinner = new ora(spinnerLabel); + spinner.start(); + try { + await fn(options); + spinner.succeed(); + } catch (e) { + spinner.fail(); + console.log(e); + if (killProcess) { + process.exit(1); + } + } + }; +}; From 707d0e13bd081a741b700720316e929626888fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 5 Mar 2019 09:22:26 +0100 Subject: [PATCH 097/244] Update frontend.md --- style_guides/frontend.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style_guides/frontend.md b/style_guides/frontend.md index 8d0849506a3..4f4c5dd7a3b 100644 --- a/style_guides/frontend.md +++ b/style_guides/frontend.md @@ -21,7 +21,7 @@ Generally we follow the Airbnb [React Style Guide](https://github.com/airbnb/ja * Components and types that needs to be used by external plugins needs to go into @grafana/ui * Components should get their own folder under features/xxx/components - * Sub components can live in that component folders, so not small component needs their own folder + * Sub components can live in that component folders, so small component do not need their own folder * Place test next to their component file (same dir) * Mocks in __mocks__ dir * Test utils in __tests__ dir From 590450291af2c4c15593923115f8159a786cef7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 5 Mar 2019 09:23:04 +0100 Subject: [PATCH 098/244] Update frontend.md --- style_guides/frontend.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/style_guides/frontend.md b/style_guides/frontend.md index 4f4c5dd7a3b..caef4f711ef 100644 --- a/style_guides/frontend.md +++ b/style_guides/frontend.md @@ -22,9 +22,7 @@ Generally we follow the Airbnb [React Style Guide](https://github.com/airbnb/ja * Components and types that needs to be used by external plugins needs to go into @grafana/ui * Components should get their own folder under features/xxx/components * Sub components can live in that component folders, so small component do not need their own folder - * Place test next to their component file (same dir) - * Mocks in __mocks__ dir - * Test utils in __tests__ dir + * Place test next to their component file (same dir) * Component sass should live in the same folder as component code * State logic & domain models should live in features/xxx/state * Containers (pages) can live in feature root features/xxx From 8b1e25b50a5943b856127a5d01506d7917acf235 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 5 Mar 2019 09:32:02 +0100 Subject: [PATCH 099/244] utils: show string errors. Fixes #15782 --- public/app/core/utils/errors.test.ts | 55 ++++++++++++++++++++++++++++ public/app/core/utils/errors.ts | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 public/app/core/utils/errors.test.ts diff --git a/public/app/core/utils/errors.test.ts b/public/app/core/utils/errors.test.ts new file mode 100644 index 00000000000..a5783fe7204 --- /dev/null +++ b/public/app/core/utils/errors.test.ts @@ -0,0 +1,55 @@ +import { getMessageFromError } from 'app/core/utils/errors'; + +describe('errors functions', () => { + let message; + + describe('when getMessageFromError gets an error string', () => { + beforeEach(() => { + message = getMessageFromError('error string'); + }); + + it('should return the string', () => { + expect(message).toBe('error string'); + }); + }); + + describe('when getMessageFromError gets an error object with message field', () => { + beforeEach(() => { + message = getMessageFromError({ message: 'error string' }); + }); + + it('should return the message text', () => { + expect(message).toBe('error string'); + }); + }); + + describe('when getMessageFromError gets an error object with data.message field', () => { + beforeEach(() => { + message = getMessageFromError({ data: { message: 'error string' } }); + }); + + it('should return the message text', () => { + expect(message).toBe('error string'); + }); + }); + + describe('when getMessageFromError gets an error object with statusText field', () => { + beforeEach(() => { + message = getMessageFromError({ statusText: 'error string' }); + }); + + it('should return the statusText text', () => { + expect(message).toBe('error string'); + }); + }); + + describe('when getMessageFromError gets an error object', () => { + beforeEach(() => { + message = getMessageFromError({ customError: 'error string' }); + }); + + it('should return the stringified error', () => { + expect(message).toBe('{"customError":"error string"}'); + }); + }); +}); diff --git a/public/app/core/utils/errors.ts b/public/app/core/utils/errors.ts index 3f6f1cfbc8d..afdf5270ade 100644 --- a/public/app/core/utils/errors.ts +++ b/public/app/core/utils/errors.ts @@ -13,5 +13,5 @@ export function getMessageFromError(err: any): string | null { } } - return null; + return err; } From fde63fc1cf90059093fc9ad752667478e0765132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 5 Mar 2019 10:47:52 +0100 Subject: [PATCH 100/244] Updated react select fork to 2.4.1 --- package.json | 2 +- yarn.lock | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9e0b88804fd..ad08ddbf6f4 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,7 @@ "license": "Apache-2.0", "dependencies": { "@babel/polyfill": "^7.0.0", - "@torkelo/react-select": "2.1.1", + "@torkelo/react-select": "2.4.1", "@types/reselect": "^2.2.0", "angular": "1.6.6", "angular-bindonce": "0.3.1", diff --git a/yarn.lock b/yarn.lock index 3d97ef64374..cec36884075 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1501,6 +1501,19 @@ react-input-autosize "^2.2.1" react-transition-group "^2.2.1" +"@torkelo/react-select@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@torkelo/react-select/-/react-select-2.4.1.tgz#fb7bcb8f7a12b3453bb817ca9a1294edecd1363b" + integrity sha512-x8798Y7WT4PSyNiEhk8JbsS5/EA+sxrObWkmfAnWNUJCDKoELWDCPrPBinRvITlCQYzLww5RaoNJutI5VBqKOQ== + dependencies: + classnames "^2.2.5" + emotion "^9.1.2" + memoize-one "^5.0.0" + prop-types "^15.6.0" + raf "^3.4.0" + react-input-autosize "^2.2.1" + react-transition-group "^2.2.1" + "@types/chalk@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@types/chalk/-/chalk-2.2.0.tgz#b7f6e446f4511029ee8e3f43075fb5b73fbaa0ba" @@ -11412,6 +11425,11 @@ memoize-one@^4.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== +memoize-one@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.0.tgz#d55007dffefb8de7546659a1722a5d42e128286e" + integrity sha512-7g0+ejkOaI9w5x6LvQwmj68kUj6rxROywPSCqmclG/HBacmFnZqhVscQ8kovkn9FBCNJmOz6SY42+jnvZzDWdw== + memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" From 48570c627299e151ee5538230954cfe9777785d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 5 Mar 2019 10:49:45 +0100 Subject: [PATCH 101/244] Made sure that DataSourceOption displays value and fires onChange/onBlur events (#15757) * Fixed #15682 * fix: Add hideTimeOverride to state since we need to control the Switch * fix: Back the maxDataPoints change, we need to keep it as a string Co-authored-by:johannes.schill@polyester.se --- .../panel_editor/DataSourceOption.tsx | 17 +- .../dashboard/panel_editor/QueryOptions.tsx | 149 ++++++++++-------- 2 files changed, 93 insertions(+), 73 deletions(-) diff --git a/public/app/features/dashboard/panel_editor/DataSourceOption.tsx b/public/app/features/dashboard/panel_editor/DataSourceOption.tsx index 08285960805..a72eaae6ed9 100644 --- a/public/app/features/dashboard/panel_editor/DataSourceOption.tsx +++ b/public/app/features/dashboard/panel_editor/DataSourceOption.tsx @@ -1,16 +1,17 @@ -import React, { FC } from 'react'; +import React, { FC, ChangeEvent } from 'react'; import { FormLabel } from '@grafana/ui'; interface Props { label: string; placeholder?: string; - name?: string; - value?: string; - onChange?: (evt: any) => void; + name: string; + value: string; + onBlur: (event: ChangeEvent) => void; + onChange: (event: ChangeEvent) => void; tooltipInfo?: any; } -export const DataSourceOptions: FC = ({ label, placeholder, name, value, onChange, tooltipInfo }) => { +export const DataSourceOption: FC = ({ label, placeholder, name, value, onBlur, onChange, tooltipInfo }) => { return (
{label} @@ -20,10 +21,10 @@ export const DataSourceOptions: FC = ({ label, placeholder, name, value, placeholder={placeholder} name={name} spellCheck={false} - onBlur={evt => onChange(evt.target.value)} + onBlur={onBlur} + onChange={onChange} + value={value} />
); }; - -export default DataSourceOptions; diff --git a/public/app/features/dashboard/panel_editor/QueryOptions.tsx b/public/app/features/dashboard/panel_editor/QueryOptions.tsx index d203f3bc25f..8c59edf456d 100644 --- a/public/app/features/dashboard/panel_editor/QueryOptions.tsx +++ b/public/app/features/dashboard/panel_editor/QueryOptions.tsx @@ -1,5 +1,5 @@ // Libraries -import React, { PureComponent } from 'react'; +import React, { PureComponent, ChangeEvent, FocusEvent } from 'react'; // Utils import { isValidTimeSpan } from 'app/core/utils/rangeutil'; @@ -9,7 +9,7 @@ import { Switch } from '@grafana/ui'; import { Input } from 'app/core/components/Form'; import { EventsWithValidation } from 'app/core/components/Form/Input'; import { InputStatus } from 'app/core/components/Form/Input'; -import DataSourceOption from './DataSourceOption'; +import { DataSourceOption } from './DataSourceOption'; import { FormLabel } from '@grafana/ui'; // Types @@ -43,32 +43,79 @@ interface Props { interface State { relativeTime: string; timeShift: string; + cacheTimeout: string; + maxDataPoints: string; + interval: string; + hideTimeOverride: boolean; } export class QueryOptions extends PureComponent { + allOptions = { + cacheTimeout: { + label: 'Cache timeout', + placeholder: '60', + name: 'cacheTimeout', + tooltipInfo: ( + <> + If your time series store has a query cache this option can override the default cache timeout. Specify a + numeric value in seconds. + + ), + }, + maxDataPoints: { + label: 'Max data points', + placeholder: 'auto', + name: 'maxDataPoints', + tooltipInfo: ( + <> + The maximum data points the query should return. For graphs this is automatically set to one data point per + pixel. + + ), + }, + minInterval: { + label: 'Min time interval', + placeholder: '0', + name: 'minInterval', + panelKey: 'interval', + tooltipInfo: ( + <> + 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. Access auto interval via variable{' '} + $__interval for time range string and $__interval_ms for numeric variable that can + be used in math expressions. + + ), + }, + }; + constructor(props) { super(props); this.state = { relativeTime: props.panel.timeFrom || '', timeShift: props.panel.timeShift || '', + cacheTimeout: props.panel.cacheTimeout || '', + maxDataPoints: props.panel.maxDataPoints || '', + interval: props.panel.interval || '', + hideTimeOverride: props.panel.hideTimeOverride || false, }; } - onRelativeTimeChange = event => { + onRelativeTimeChange = (event: ChangeEvent) => { this.setState({ relativeTime: event.target.value, }); }; - onTimeShiftChange = event => { + onTimeShiftChange = (event: ChangeEvent) => { this.setState({ timeShift: event.target.value, }); }; - onOverrideTime = (evt, status: InputStatus) => { - const { value } = evt.target; + onOverrideTime = (event: FocusEvent, status: InputStatus) => { + const { value } = event.target; const { panel } = this.props; const emptyToNullValue = emptyToNull(value); if (status === InputStatus.Valid && panel.timeFrom !== emptyToNullValue) { @@ -77,8 +124,8 @@ export class QueryOptions extends PureComponent { } }; - onTimeShift = (evt, status: InputStatus) => { - const { value } = evt.target; + onTimeShift = (event: FocusEvent, status: InputStatus) => { + const { value } = event.target; const { panel } = this.props; const emptyToNullValue = emptyToNull(value); if (status === InputStatus.Valid && panel.timeShift !== emptyToNullValue) { @@ -89,77 +136,49 @@ export class QueryOptions extends PureComponent { onToggleTimeOverride = () => { const { panel } = this.props; - panel.hideTimeOverride = !panel.hideTimeOverride; + this.setState({ hideTimeOverride: !this.state.hideTimeOverride }, () => { + panel.hideTimeOverride = this.state.hideTimeOverride; + panel.refresh(); + }); + }; + + onDataSourceOptionBlur = (panelKey: string) => () => { + const { panel } = this.props; + + panel[panelKey] = this.state[panelKey]; panel.refresh(); }; - renderOptions() { - const { datasource, panel } = this.props; + onDataSourceOptionChange = (panelKey: string) => (event: ChangeEvent) => { + this.setState({ ...this.state, [panelKey]: event.target.value }); + }; + + renderOptions = () => { + const { datasource } = this.props; const { queryOptions } = datasource.meta; if (!queryOptions) { return null; } - const onChangeFn = (panelKey: string) => { - return (value: string | number) => { - panel[panelKey] = value; - panel.refresh(); - }; - }; - - const allOptions = { - cacheTimeout: { - label: 'Cache timeout', - placeholder: '60', - name: 'cacheTimeout', - value: panel.cacheTimeout, - tooltipInfo: ( - <> - If your time series store has a query cache this option can override the default cache timeout. Specify a - numeric value in seconds. - - ), - }, - maxDataPoints: { - label: 'Max data points', - placeholder: 'auto', - name: 'maxDataPoints', - value: panel.maxDataPoints, - tooltipInfo: ( - <> - The maximum data points the query should return. For graphs this is automatically set to one data point per - pixel. - - ), - }, - minInterval: { - label: 'Min time interval', - placeholder: '0', - name: 'minInterval', - value: panel.interval, - panelKey: 'interval', - tooltipInfo: ( - <> - 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. Access auto interval via variable{' '} - $__interval for time range string and $__interval_ms for numeric variable that can - be used in math expressions. - - ), - }, - }; - return Object.keys(queryOptions).map(key => { - const options = allOptions[key]; - return ; + const options = this.allOptions[key]; + const panelKey = options.panelKey || key; + return ( + + ); }); - } + }; render() { - const hideTimeOverride = this.props.panel.hideTimeOverride; + const { hideTimeOverride } = this.state; const { relativeTime, timeShift } = this.state; - return (
{this.renderOptions()} From a5455691b3443df74075d5ebf5919c4fd8a71feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 5 Mar 2019 11:43:25 +0100 Subject: [PATCH 102/244] Hide time info switch when no time options are specified --- .../app/features/dashboard/panel_editor/QueryOptions.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/public/app/features/dashboard/panel_editor/QueryOptions.tsx b/public/app/features/dashboard/panel_editor/QueryOptions.tsx index 8c59edf456d..0d031cb12ba 100644 --- a/public/app/features/dashboard/panel_editor/QueryOptions.tsx +++ b/public/app/features/dashboard/panel_editor/QueryOptions.tsx @@ -210,10 +210,11 @@ export class QueryOptions extends PureComponent { value={timeShift} />
- -
- -
+ {(timeShift || relativeTime) && ( +
+ +
+ )}
); } From 66d9b129811b3cd9c4ded68cf9720e128ad15fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 5 Mar 2019 12:03:48 +0100 Subject: [PATCH 103/244] Turn off verbose output from tar extraction when building docker files, fixes #15528 --- packaging/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile index d783cb14377..d0b2a2f5bdf 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -qq -y tar && \ COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz -RUN mkdir /tmp/grafana && tar xfvz /tmp/grafana.tar.gz --strip-components=1 -C /tmp/grafana +RUN mkdir /tmp/grafana && tar xfz /tmp/grafana.tar.gz --strip-components=1 -C /tmp/grafana ARG BASE_IMAGE=debian:stretch-slim FROM ${BASE_IMAGE} From 978cdfba00e48aa3f02bdb18857bbb6ee5583ea4 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 5 Mar 2019 12:08:57 +0100 Subject: [PATCH 104/244] Wrapperd playlist controls in clickoutsidewrapper --- .../dashboard/components/DashNav/DashNav.tsx | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index 453c5d1f9ac..27ca8a28672 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -16,6 +16,7 @@ import { updateLocation } from 'app/core/actions'; // Types import { DashboardModel } from '../../state'; +import { ClickOutsideWrapper } from '../../../../core/components/ClickOutsideWrapper/ClickOutsideWrapper'; export interface Props { dashboard: DashboardModel; @@ -173,26 +174,28 @@ export class DashNav extends PureComponent { {this.renderDashboardTitleSearchButton()} {this.playlistSrv.isPlaying && ( -
- - - -
+ +
+ + + +
+
)}
From a158a192721c8f89a1be37aafada0ff51c11683a Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 5 Mar 2019 12:10:20 +0100 Subject: [PATCH 105/244] reordered import --- public/app/features/dashboard/components/DashNav/DashNav.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index 27ca8a28672..d2f22d7d010 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -8,6 +8,7 @@ import { appEvents } from 'app/core/app_events'; import { PlaylistSrv } from 'app/features/playlist/playlist_srv'; // Components +import { ClickOutsideWrapper } from 'app/core/components/ClickOutsideWrapper/ClickOutsideWrapper'; import { DashNavButton } from './DashNavButton'; import { Tooltip } from '@grafana/ui'; @@ -16,7 +17,6 @@ import { updateLocation } from 'app/core/actions'; // Types import { DashboardModel } from '../../state'; -import { ClickOutsideWrapper } from '../../../../core/components/ClickOutsideWrapper/ClickOutsideWrapper'; export interface Props { dashboard: DashboardModel; From e6a83bf0e1aba6577544736f5fdec4b5c8508842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 5 Mar 2019 12:37:19 +0100 Subject: [PATCH 106/244] Fixed scrolling issue that caused scroll to be locked to the bottom of a long dashboard, fixes #15712 --- .../src/components/CustomScrollbar/CustomScrollbar.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx index 61fc584c7ca..1bc1fd42792 100644 --- a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx +++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx @@ -42,11 +42,7 @@ export class CustomScrollbar extends Component { const ref = this.ref.current; if (ref && !isNil(this.props.scrollTop)) { - if (this.props.scrollTop > 10000) { - ref.scrollToBottom(); - } else { - ref.scrollTop(this.props.scrollTop); - } + ref.scrollTop(this.props.scrollTop); } } From a81d5486b096c77b4d39ee0483a0c127f4480881 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Tue, 5 Mar 2019 12:41:01 +0100 Subject: [PATCH 107/244] Viewers with viewers_can_edit should be able to access /explore (#15787) * fix: Viewers with viewers_can_edit should be able to access /explore #15773 * refactoring initial PR a bit to simplify function and reduce duplication --- pkg/api/api.go | 2 +- pkg/middleware/auth.go | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index c80129eac6f..81ea83eae61 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -73,7 +73,7 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/dashboards/", reqSignedIn, hs.Index) r.Get("/dashboards/*", reqSignedIn, hs.Index) - r.Get("/explore", reqEditorRole, hs.Index) + r.Get("/explore", reqSignedIn, middleware.EnsureEditorOrViewerCanEdit, hs.Index) r.Get("/playlists/", reqSignedIn, hs.Index) r.Get("/playlists/*", reqSignedIn, hs.Index) diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index 27248342c8d..e06409211eb 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -4,7 +4,7 @@ import ( "net/url" "strings" - "gopkg.in/macaron.v1" + macaron "gopkg.in/macaron.v1" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" @@ -52,6 +52,12 @@ func notAuthorized(c *m.ReqContext) { c.Redirect(setting.AppSubUrl + "/login") } +func EnsureEditorOrViewerCanEdit(c *m.ReqContext) { + if !c.SignedInUser.HasRole(m.ROLE_EDITOR) && !setting.ViewersCanEdit { + accessForbidden(c) + } +} + func RoleAuth(roles ...m.RoleType) macaron.Handler { return func(c *m.ReqContext) { ok := false From e3d08e24f327b56b97448e9ff50e8b5a39f111e7 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 5 Mar 2019 12:58:02 +0100 Subject: [PATCH 108/244] update version to 6.1.0-pre --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ad08ddbf6f4..04a6967fdb9 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "company": "Grafana Labs" }, "name": "grafana", - "version": "6.0.0-pre3", + "version": "6.1.0-pre", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" From d3642a3e91a52df7dd55d1b72ecddfd8ab1ecf53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 5 Mar 2019 13:02:41 +0100 Subject: [PATCH 109/244] Moved Server Admin and children to separate menu item on Side Menu (#15592) * Moved Server Admin and children to separate menu item on Side Menu * Removed style guide after PR comments --- pkg/api/index.go | 41 ++++++++----------- public/app/features/admin/AdminEditOrgCtrl.ts | 2 +- .../app/features/admin/AdminEditUserCtrl.ts | 2 +- .../app/features/admin/AdminListOrgsCtrl.ts | 2 +- .../app/features/admin/AdminListUsersCtrl.ts | 2 +- public/app/features/admin/StyleGuideCtrl.ts | 2 +- public/app/features/admin/index.ts | 4 +- public/app/features/org/NewOrgCtrl.ts | 2 +- 8 files changed, 25 insertions(+), 32 deletions(-) diff --git a/pkg/api/index.go b/pkg/api/index.go index 248ebf63f0f..904a885b171 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -307,33 +307,26 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er } } - if c.OrgRole == m.ROLE_ADMIN && c.IsGrafanaAdmin { - cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{ - Divider: true, HideFromTabs: true, Id: "admin-divider", Text: "Text", - }) - } - - if c.IsGrafanaAdmin { - cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{ - Text: "Server Admin", - HideFromTabs: true, - SubTitle: "Manage all users & orgs", - Id: "admin", - Icon: "gicon gicon-shield", - Url: setting.AppSubUrl + "/admin/users", - Children: []*dtos.NavLink{ - {Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users", Icon: "gicon gicon-user"}, - {Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs", Icon: "gicon gicon-org"}, - {Text: "Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings", Icon: "gicon gicon-preferences"}, - {Text: "Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats", Icon: "fa fa-fw fa-bar-chart"}, - {Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide", Icon: "fa fa-fw fa-eyedropper"}, - }, - }) - } - data.NavTree = append(data.NavTree, cfgNode) } + if c.IsGrafanaAdmin { + data.NavTree = append(data.NavTree, &dtos.NavLink{ + Text: "Server Admin", + SubTitle: "Manage all users & orgs", + HideFromTabs: true, + Id: "admin", + Icon: "gicon gicon-shield", + Url: setting.AppSubUrl + "/admin/users", + Children: []*dtos.NavLink{ + {Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users", Icon: "gicon gicon-user"}, + {Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs", Icon: "gicon gicon-org"}, + {Text: "Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings", Icon: "gicon gicon-preferences"}, + {Text: "Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats", Icon: "fa fa-fw fa-bar-chart"}, + }, + }) + } + data.NavTree = append(data.NavTree, &dtos.NavLink{ Text: "Help", SubTitle: fmt.Sprintf(`%s v%s (%s)`, setting.ApplicationName, setting.BuildVersion, setting.BuildCommit), diff --git a/public/app/features/admin/AdminEditOrgCtrl.ts b/public/app/features/admin/AdminEditOrgCtrl.ts index 60514a86392..4ce0e2366f6 100644 --- a/public/app/features/admin/AdminEditOrgCtrl.ts +++ b/public/app/features/admin/AdminEditOrgCtrl.ts @@ -2,7 +2,7 @@ export default class AdminEditOrgCtrl { /** @ngInject */ constructor($scope, $routeParams, backendSrv, $location, navModelSrv) { $scope.init = () => { - $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs', 1); + $scope.navModel = navModelSrv.getNav('admin', 'global-orgs', 0); if ($routeParams.id) { $scope.getOrg($routeParams.id); diff --git a/public/app/features/admin/AdminEditUserCtrl.ts b/public/app/features/admin/AdminEditUserCtrl.ts index bf72c1746aa..a5dcae52d50 100644 --- a/public/app/features/admin/AdminEditUserCtrl.ts +++ b/public/app/features/admin/AdminEditUserCtrl.ts @@ -6,7 +6,7 @@ export default class AdminEditUserCtrl { $scope.user = {}; $scope.newOrg = { name: '', role: 'Editor' }; $scope.permissions = {}; - $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-users', 1); + $scope.navModel = navModelSrv.getNav('admin', 'global-users', 0); $scope.init = () => { if ($routeParams.id) { diff --git a/public/app/features/admin/AdminListOrgsCtrl.ts b/public/app/features/admin/AdminListOrgsCtrl.ts index 8783efc182c..d6ee4b33dbb 100644 --- a/public/app/features/admin/AdminListOrgsCtrl.ts +++ b/public/app/features/admin/AdminListOrgsCtrl.ts @@ -2,7 +2,7 @@ export default class AdminListOrgsCtrl { /** @ngInject */ constructor($scope, backendSrv, navModelSrv) { $scope.init = () => { - $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs', 1); + $scope.navModel = navModelSrv.getNav('admin', 'global-orgs', 0); $scope.getOrgs(); }; diff --git a/public/app/features/admin/AdminListUsersCtrl.ts b/public/app/features/admin/AdminListUsersCtrl.ts index 1b5b27a4d66..5b5321d13ce 100644 --- a/public/app/features/admin/AdminListUsersCtrl.ts +++ b/public/app/features/admin/AdminListUsersCtrl.ts @@ -10,7 +10,7 @@ export default class AdminListUsersCtrl { /** @ngInject */ constructor(private $scope, private backendSrv, navModelSrv) { - this.navModel = navModelSrv.getNav('cfg', 'admin', 'global-users', 1); + this.navModel = navModelSrv.getNav('admin', 'global-users', 0); this.query = ''; this.getUsers(); } diff --git a/public/app/features/admin/StyleGuideCtrl.ts b/public/app/features/admin/StyleGuideCtrl.ts index e38e1a3344d..6548aa09198 100644 --- a/public/app/features/admin/StyleGuideCtrl.ts +++ b/public/app/features/admin/StyleGuideCtrl.ts @@ -9,7 +9,7 @@ export default class StyleGuideCtrl { /** @ngInject */ constructor(private $routeParams, private backendSrv, navModelSrv) { - this.navModel = navModelSrv.getNav('cfg', 'admin', 'styleguide', 1); + this.navModel = navModelSrv.getNav('admin', 'styleguide', 0); this.theme = config.bootData.user.lightTheme ? 'light' : 'dark'; } diff --git a/public/app/features/admin/index.ts b/public/app/features/admin/index.ts index 7d06155b6f8..fecc04bc410 100644 --- a/public/app/features/admin/index.ts +++ b/public/app/features/admin/index.ts @@ -11,7 +11,7 @@ class AdminSettingsCtrl { /** @ngInject */ constructor($scope, backendSrv, navModelSrv) { - this.navModel = navModelSrv.getNav('cfg', 'admin', 'server-settings', 1); + this.navModel = navModelSrv.getNav('admin', 'server-settings', 0); backendSrv.get('/api/admin/settings').then(settings => { $scope.settings = settings; @@ -24,7 +24,7 @@ class AdminHomeCtrl { /** @ngInject */ constructor(navModelSrv) { - this.navModel = navModelSrv.getNav('cfg', 'admin', 1); + this.navModel = navModelSrv.getNav('admin', 0); } } diff --git a/public/app/features/org/NewOrgCtrl.ts b/public/app/features/org/NewOrgCtrl.ts index 6a8808abfac..46c12b1d5d5 100644 --- a/public/app/features/org/NewOrgCtrl.ts +++ b/public/app/features/org/NewOrgCtrl.ts @@ -4,7 +4,7 @@ import config from 'app/core/config'; export class NewOrgCtrl { /** @ngInject */ constructor($scope, $http, backendSrv, navModelSrv) { - $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs', 1); + $scope.navModel = navModelSrv.getNav('admin', 'global-orgs', 0); $scope.newOrg = { name: '' }; $scope.createOrg = () => { From ff7eaced252ed0cc1bbdd39c727d595b29367d49 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 5 Mar 2019 13:03:46 +0100 Subject: [PATCH 110/244] changelog: add notes about closing #15739 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c010a279c73..24fb134d82a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ### Bug Fixes * **Api**: Invalid org invite code [#10506](https://github.com/grafana/grafana/issues/10506) * **Datasource**: Handles nil jsondata field gracefully [#14239](https://github.com/grafana/grafana/issues/14239) -* **Gauge**: Interpolate scoped variables in repeated gauges [#15732](https://github.com/grafana/grafana/pull/15752) +* **Gauge**: Interpolate scoped variables in repeated gauges [#15739](https://github.com/grafana/grafana/issues/15739) # 6.0.1 (unreleased) From 09b036dc937475e2659bec96b629ac56f1f3bd25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 5 Mar 2019 13:08:31 +0100 Subject: [PATCH 111/244] fix: update datasource in componentDidUpdate Closes #15751 --- .../datasources/settings/DataSourceSettingsPage.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx index 01eea098ea4..27f60865c21 100644 --- a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx +++ b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx @@ -64,6 +64,14 @@ export class DataSourceSettingsPage extends PureComponent { await loadDataSource(pageId); } + componentDidUpdate(prevProps: Props) { + const { dataSource } = this.props; + + if (prevProps.dataSource !== dataSource) { + this.setState({ dataSource }); + } + } + onSubmit = async (evt: React.FormEvent) => { evt.preventDefault(); @@ -95,9 +103,7 @@ export class DataSourceSettingsPage extends PureComponent { }; onModelChange = (dataSource: DataSourceSettings) => { - this.setState({ - dataSource: dataSource, - }); + this.setState({ dataSource }); }; isReadOnly() { From e3ddfccb6185e67178dc72897ad53b1a98e66518 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Tue, 5 Mar 2019 13:29:54 +0100 Subject: [PATCH 112/244] fix: Move chunk splitting from prod to common so we get the same files in dev as prod --- scripts/webpack/webpack.common.js | 14 +++++++++++++- scripts/webpack/webpack.dev.js | 19 ------------------- scripts/webpack/webpack.prod.js | 11 ----------- 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/scripts/webpack/webpack.common.js b/scripts/webpack/webpack.common.js index cdc17b5ec00..600e5b480c8 100644 --- a/scripts/webpack/webpack.common.js +++ b/scripts/webpack/webpack.common.js @@ -10,7 +10,7 @@ module.exports = { path: path.resolve(__dirname, '../../public/build'), filename: '[name].[hash].js', // Keep publicPath relative for host.com/grafana/ deployments - publicPath: "public/build/", + publicPath: 'public/build/', }, resolve: { extensions: ['.ts', '.tsx', '.es6', '.js', '.json', '.svg'], @@ -61,6 +61,18 @@ module.exports = { } ] }, + // https://webpack.js.org/plugins/split-chunks-plugin/#split-chunks-example-3 + optimization: { + splitChunks: { + cacheGroups: { + commons: { + test: /[\\/]node_modules[\\/].*[jt]sx?$/, + name: 'vendor', + chunks: 'all' + } + } + } + }, plugins: [ new ForkTsCheckerWebpackPlugin({ checkSyntacticErrors: true, diff --git a/scripts/webpack/webpack.dev.js b/scripts/webpack/webpack.dev.js index 7e103b32606..c10722b2732 100644 --- a/scripts/webpack/webpack.dev.js +++ b/scripts/webpack/webpack.dev.js @@ -58,25 +58,6 @@ module.exports = merge(common, { ] }, - optimization: { - splitChunks: { - cacheGroups: { - manifest: { - chunks: "initial", - test: "vendor", - name: "vendor", - enforce: true - }, - vendor: { - chunks: "initial", - test: "vendor", - name: "vendor", - enforce: true - } - } - } - }, - plugins: [ new CleanWebpackPlugin('../../public/build', { allowExternal: true }), new MiniCssExtractPlugin({ diff --git a/scripts/webpack/webpack.prod.js b/scripts/webpack/webpack.prod.js index 880b7e08ed2..0a3cf3ba814 100644 --- a/scripts/webpack/webpack.prod.js +++ b/scripts/webpack/webpack.prod.js @@ -47,17 +47,7 @@ module.exports = merge(common, { }) ] }, - optimization: { - splitChunks: { - cacheGroups: { - commons: { - test: /[\\/]node_modules[\\/].*[jt]sx?$/, - name: "vendor", - chunks: "all" - } - } - }, minimizer: [ new UglifyJsPlugin({ cache: true, @@ -67,7 +57,6 @@ module.exports = merge(common, { new OptimizeCSSAssetsPlugin({}) ] }, - plugins: [ new MiniCssExtractPlugin({ filename: "grafana.[name].[hash].css" From ae9327ff3ac5c9f54e5add0ad4629739e34cb9a9 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 5 Mar 2019 13:41:40 +0100 Subject: [PATCH 113/244] remove `UseBool` since we use `AllCols` --- pkg/services/sqlstore/datasource.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkg/services/sqlstore/datasource.go b/pkg/services/sqlstore/datasource.go index 65d8369d763..ef42051f738 100644 --- a/pkg/services/sqlstore/datasource.go +++ b/pkg/services/sqlstore/datasource.go @@ -164,11 +164,6 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error { Version: cmd.Version + 1, } - sess.UseBool("is_default") - sess.UseBool("basic_auth") - sess.UseBool("with_credentials") - sess.UseBool("read_only") - var updateSession *xorm.Session if cmd.Version != 0 { // the reason we allow cmd.version > db.version is make it possible for people to force From 062b5f26fe93fa8a1f377fafe401f3e1cb19d907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 5 Mar 2019 13:46:22 +0100 Subject: [PATCH 114/244] style: add gicon-shield to sidemenu class Closes #15591 --- public/sass/base/_icons.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/sass/base/_icons.scss b/public/sass/base/_icons.scss index 8d171673f6b..4e5751c6919 100644 --- a/public/sass/base/_icons.scss +++ b/public/sass/base/_icons.scss @@ -212,6 +212,9 @@ .gicon-explore { background-image: url('../img/icons_dark_theme/icon_explore.svg'); } + .gicon-shield { + background-image: url('../img/icons_dark_theme/icon_shield.svg'); + } } .fa--permissions-list { From ee4df155261eb65b6c69b705232306f0c06250f0 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 5 Mar 2019 13:55:29 +0100 Subject: [PATCH 115/244] moving --- .../grafana-ui/src/components/UnitPicker}/UnitPicker.tsx | 8 +++++--- packages/grafana-ui/src/components/index.ts | 1 + public/app/plugins/panel/gauge/SingleStatValueEditor.tsx | 3 +-- 3 files changed, 7 insertions(+), 5 deletions(-) rename {public/app/core/components/Select => packages/grafana-ui/src/components/UnitPicker}/UnitPicker.tsx (87%) diff --git a/public/app/core/components/Select/UnitPicker.tsx b/packages/grafana-ui/src/components/UnitPicker/UnitPicker.tsx similarity index 87% rename from public/app/core/components/Select/UnitPicker.tsx rename to packages/grafana-ui/src/components/UnitPicker/UnitPicker.tsx index f9dbc0ae421..3a34df02a39 100644 --- a/public/app/core/components/Select/UnitPicker.tsx +++ b/packages/grafana-ui/src/components/UnitPicker/UnitPicker.tsx @@ -1,6 +1,8 @@ import React, { PureComponent } from 'react'; -import { getValueFormats } from '@grafana/ui'; -import { Select } from '@grafana/ui'; + +import { Select } from '..'; + +import { getValueFormats } from '../../utils'; interface Props { onChange: (item: any) => void; @@ -8,7 +10,7 @@ interface Props { width?: number; } -export default class UnitPicker extends PureComponent { +export class UnitPicker extends PureComponent { static defaultProps = { width: 12, }; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index ca8899bd928..b62fdff6498 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -26,3 +26,4 @@ export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor'; export { Gauge } from './Gauge/Gauge'; export { Switch } from './Switch/Switch'; export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult'; +export { UnitPicker } from './UnitPicker/UnitPicker'; diff --git a/public/app/plugins/panel/gauge/SingleStatValueEditor.tsx b/public/app/plugins/panel/gauge/SingleStatValueEditor.tsx index 86c177bb5e5..e711df6a2d3 100644 --- a/public/app/plugins/panel/gauge/SingleStatValueEditor.tsx +++ b/public/app/plugins/panel/gauge/SingleStatValueEditor.tsx @@ -2,8 +2,7 @@ import React, { PureComponent } from 'react'; // Components -import UnitPicker from 'app/core/components/Select/UnitPicker'; -import { FormField, FormLabel, PanelOptionsGroup, Select } from '@grafana/ui'; +import { FormField, FormLabel, PanelOptionsGroup, Select, UnitPicker } from '@grafana/ui'; // Types import { SingleStatValueOptions } from './types'; From a12960e4769ffe51a8a233f9e4b2b8614204dda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 5 Mar 2019 13:58:43 +0100 Subject: [PATCH 116/244] Added comment to Docker file --- packaging/docker/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile index d0b2a2f5bdf..58fa8dae64c 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -9,6 +9,7 @@ RUN apt-get update && apt-get install -qq -y tar && \ COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz +# Change to tar xfzv to make tar print every file it extracts RUN mkdir /tmp/grafana && tar xfz /tmp/grafana.tar.gz --strip-components=1 -C /tmp/grafana ARG BASE_IMAGE=debian:stretch-slim From cd78f0bef21238fff5ea29f4f80df9be3f69c4ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 5 Mar 2019 14:32:33 +0100 Subject: [PATCH 117/244] Fixed scrollbar not visible due to content being added a bit after mount, fixes #15711 --- .../CustomScrollbar/CustomScrollbar.tsx | 15 +++++++++++++++ .../dashboard/containers/DashboardPage.tsx | 1 + .../__snapshots__/DashboardPage.test.tsx.snap | 2 ++ .../dashboard/panel_editor/EditorTabBody.tsx | 2 +- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx index 1bc1fd42792..945e3d825d8 100644 --- a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx +++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx @@ -15,6 +15,7 @@ interface Props { scrollTop?: number; setScrollTop: (event: any) => void; autoHeightMin?: number | string; + updateAfterMountMs?: number; } /** @@ -48,6 +49,20 @@ export class CustomScrollbar extends Component { componentDidMount() { this.updateScroll(); + + // this logic is to make scrollbar visible when content is added body after mount + if (this.props.updateAfterMountMs) { + setTimeout(() => this.updateAfterMount(), this.props.updateAfterMountMs); + } + } + + updateAfterMount() { + if (this.ref && this.ref.current) { + const scrollbar = this.ref.current as any; + if (scrollbar.update) { + scrollbar.update(); + } + } } componentDidUpdate() { diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx index ce2d3fa74df..1a970d3edc0 100644 --- a/public/app/features/dashboard/containers/DashboardPage.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.tsx @@ -272,6 +272,7 @@ export class DashboardPage extends PureComponent { autoHeightMin={'100%'} setScrollTop={this.setScrollTop} scrollTop={scrollTop} + updateAfterMountMs={500} className="custom-scrollbar--page" > {editview && } diff --git a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap index 0e3720bada0..745bac2a20d 100644 --- a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap +++ b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap @@ -113,6 +113,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` hideTracksWhenNotNeeded={false} scrollTop={0} setScrollTop={[Function]} + updateAfterMountMs={500} >
{ {toolbarItems.map(item => this.renderButton(item))}
- +
{openView && this.renderOpenView(openView)} From d4c718091c0409c0c6999c223031a4455a14cc9a Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 5 Mar 2019 14:41:04 +0100 Subject: [PATCH 118/244] changelog: adds note about closing #15608 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24fb134d82a..cb2a344b014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * **Api**: Invalid org invite code [#10506](https://github.com/grafana/grafana/issues/10506) * **Datasource**: Handles nil jsondata field gracefully [#14239](https://github.com/grafana/grafana/issues/14239) * **Gauge**: Interpolate scoped variables in repeated gauges [#15739](https://github.com/grafana/grafana/issues/15739) +* **Datasource**: Empty user/password was not updated when updating datasources [#15608](https://github.com/grafana/grafana/pull/15608), thx [@Maddin-619](https://github.com/Maddin-619) # 6.0.1 (unreleased) From df60804937ee77c5b3f00905d4aee530ee72f2db Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 5 Mar 2019 15:03:15 +0100 Subject: [PATCH 119/244] use default min interval of 1m for sql datasources --- public/app/plugins/datasource/mssql/datasource.ts | 2 +- public/app/plugins/datasource/mysql/datasource.ts | 2 +- public/app/plugins/datasource/postgres/datasource.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/datasource/mssql/datasource.ts b/public/app/plugins/datasource/mssql/datasource.ts index 303cd0471d7..9343e0f3860 100644 --- a/public/app/plugins/datasource/mssql/datasource.ts +++ b/public/app/plugins/datasource/mssql/datasource.ts @@ -12,7 +12,7 @@ export class MssqlDatasource { this.name = instanceSettings.name; this.id = instanceSettings.id; this.responseParser = new ResponseParser(this.$q); - this.interval = (instanceSettings.jsonData || {}).timeInterval; + this.interval = (instanceSettings.jsonData || {}).timeInterval || '1m'; } interpolateVariable(value, variable) { diff --git a/public/app/plugins/datasource/mysql/datasource.ts b/public/app/plugins/datasource/mysql/datasource.ts index f0381d53b70..a864d42fcc4 100644 --- a/public/app/plugins/datasource/mysql/datasource.ts +++ b/public/app/plugins/datasource/mysql/datasource.ts @@ -15,7 +15,7 @@ export class MysqlDatasource { this.id = instanceSettings.id; this.responseParser = new ResponseParser(this.$q); this.queryModel = new MysqlQuery({}); - this.interval = (instanceSettings.jsonData || {}).timeInterval; + this.interval = (instanceSettings.jsonData || {}).timeInterval || '1m'; } interpolateVariable = (value, variable) => { diff --git a/public/app/plugins/datasource/postgres/datasource.ts b/public/app/plugins/datasource/postgres/datasource.ts index 13948c5d793..be27cdd37b6 100644 --- a/public/app/plugins/datasource/postgres/datasource.ts +++ b/public/app/plugins/datasource/postgres/datasource.ts @@ -17,7 +17,7 @@ export class PostgresDatasource { this.jsonData = instanceSettings.jsonData; this.responseParser = new ResponseParser(this.$q); this.queryModel = new PostgresQuery({}); - this.interval = (instanceSettings.jsonData || {}).timeInterval; + this.interval = (instanceSettings.jsonData || {}).timeInterval || '1m'; } interpolateVariable = (value, variable) => { From 9383b04efdddfb937876e965ff371d2ff066d99f Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 5 Mar 2019 15:53:39 +0100 Subject: [PATCH 120/244] changelog: adds note for #8253 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb2a344b014..c0def4c8487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,12 @@ # 6.1.0 (unreleased) +### New Features +* **Prometheus**: adhoc filter support [#8253](https://github.com/grafana/grafana/issues/8253), thx [@mtanda](https://github.com/mtanda) + ### Minor * **Cloudwatch**: Add AWS RDS MaximumUsedTransactionIDs metric [#15077](https://github.com/grafana/grafana/pull/15077), thx [@activeshadow](https://github.com/activeshadow) + ### Bug Fixes * **Api**: Invalid org invite code [#10506](https://github.com/grafana/grafana/issues/10506) * **Datasource**: Handles nil jsondata field gracefully [#14239](https://github.com/grafana/grafana/issues/14239) From 3b9f0e6ef2cb2c37db20ff94bbdeb10e2c2008bc Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 5 Mar 2019 18:03:07 +0100 Subject: [PATCH 121/244] fix allow anonymous initial bind for ldap search --- pkg/login/ldap.go | 13 ++++++- pkg/login/ldap_test.go | 84 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/pkg/login/ldap.go b/pkg/login/ldap.go index c15cb865bd3..8bb331b7e59 100644 --- a/pkg/login/ldap.go +++ b/pkg/login/ldap.go @@ -18,6 +18,7 @@ import ( type ILdapConn interface { Bind(username, password string) error + UnauthenticatedBind(username string) error Search(*ldap.SearchRequest) (*ldap.SearchResult, error) StartTLS(*tls.Config) error Close() @@ -259,7 +260,17 @@ func (a *ldapAuther) initialBind(username, userPassword string) error { bindPath = fmt.Sprintf(a.server.BindDN, username) } - if err := a.conn.Bind(bindPath, userPassword); err != nil { + bindFn := func() error { + return a.conn.Bind(bindPath, userPassword) + } + + if userPassword == "" { + bindFn = func() error { + return a.conn.UnauthenticatedBind(bindPath) + } + } + + if err := bindFn(); err != nil { a.log.Info("Initial bind failed", "error", err) if ldapErr, ok := err.(*ldap.Error); ok { diff --git a/pkg/login/ldap_test.go b/pkg/login/ldap_test.go index ef20feb1373..dabafee65a6 100644 --- a/pkg/login/ldap_test.go +++ b/pkg/login/ldap_test.go @@ -13,6 +13,70 @@ import ( ) func TestLdapAuther(t *testing.T) { + Convey("initialBind", t, func() { + Convey("Given bind dn and password configured", func() { + conn := &mockLdapConn{} + var actualUsername, actualPassword string + conn.bindProvider = func(username, password string) error { + actualUsername = username + actualPassword = password + return nil + } + ldapAuther := &ldapAuther{ + conn: conn, + server: &LdapServerConf{ + BindDN: "cn=%s,o=users,dc=grafana,dc=org", + BindPassword: "bindpwd", + }, + } + err := ldapAuther.initialBind("user", "pwd") + So(err, ShouldBeNil) + So(ldapAuther.requireSecondBind, ShouldBeTrue) + So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org") + So(actualPassword, ShouldEqual, "bindpwd") + }) + + Convey("Given bind dn configured", func() { + conn := &mockLdapConn{} + var actualUsername, actualPassword string + conn.bindProvider = func(username, password string) error { + actualUsername = username + actualPassword = password + return nil + } + ldapAuther := &ldapAuther{ + conn: conn, + server: &LdapServerConf{ + BindDN: "cn=%s,o=users,dc=grafana,dc=org", + }, + } + err := ldapAuther.initialBind("user", "pwd") + So(err, ShouldBeNil) + So(ldapAuther.requireSecondBind, ShouldBeFalse) + So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org") + So(actualPassword, ShouldEqual, "pwd") + }) + + Convey("Given empty bind dn and password", func() { + conn := &mockLdapConn{} + unauthenticatedBindWasCalled := false + var actualUsername string + conn.unauthenticatedBindProvider = func(username string) error { + unauthenticatedBindWasCalled = true + actualUsername = username + return nil + } + ldapAuther := &ldapAuther{ + conn: conn, + server: &LdapServerConf{}, + } + err := ldapAuther.initialBind("user", "pwd") + So(err, ShouldBeNil) + So(ldapAuther.requireSecondBind, ShouldBeTrue) + So(unauthenticatedBindWasCalled, ShouldBeTrue) + So(actualUsername, ShouldBeEmpty) + }) + }) Convey("When translating ldap user to grafana user", t, func() { @@ -365,12 +429,26 @@ func TestLdapAuther(t *testing.T) { } type mockLdapConn struct { - result *ldap.SearchResult - searchCalled bool - searchAttributes []string + result *ldap.SearchResult + searchCalled bool + searchAttributes []string + bindProvider func(username, password string) error + unauthenticatedBindProvider func(username string) error } func (c *mockLdapConn) Bind(username, password string) error { + if c.bindProvider != nil { + return c.bindProvider(username, password) + } + + return nil +} + +func (c *mockLdapConn) UnauthenticatedBind(username string) error { + if c.unauthenticatedBindProvider != nil { + return c.unauthenticatedBindProvider(username) + } + return nil } From 46fa09fdadc103c225c4ead5ac595d7ed4d32e1f Mon Sep 17 00:00:00 2001 From: Jon Ferreira Date: Mon, 4 Mar 2019 10:57:22 -0500 Subject: [PATCH 122/244] Add a keybinding that toggles all legends in a dashboard --- public/app/core/components/help/help.ts | 1 + public/app/core/services/keybindingSrv.ts | 5 ++++ .../dashboard/state/DashboardModel.test.ts | 28 +++++++++++++++++++ .../dashboard/state/DashboardModel.ts | 14 ++++++++++ .../features/dashboard/state/PanelModel.ts | 1 + 5 files changed, 49 insertions(+) diff --git a/public/app/core/components/help/help.ts b/public/app/core/components/help/help.ts index 8e8a5ed45d2..7ef54339f49 100644 --- a/public/app/core/components/help/help.ts +++ b/public/app/core/components/help/help.ts @@ -27,6 +27,7 @@ export class HelpCtrl { { keys: ['d', 'C'], description: 'Collapse all rows' }, { keys: ['d', 'a'], description: 'Toggle auto fit panels (experimental feature)' }, { keys: ['mod+o'], description: 'Toggle shared graph crosshair' }, + { keys: ['d', 'l'], description: 'Toggle all panel legends' }, ], 'Focused Panel': [ { keys: ['e'], description: 'Toggle panel edit view' }, diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 7dab7cffd6f..da096f261c6 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -256,6 +256,11 @@ export class KeybindingSrv { } }); + // toggle all panel legends + this.bind('d l', () => { + dashboard.toggleLegendsForAll(); + }); + // collapse all rows this.bind('d shift+c', () => { dashboard.collapseRows(); diff --git a/public/app/features/dashboard/state/DashboardModel.test.ts b/public/app/features/dashboard/state/DashboardModel.test.ts index cd30fc2ecdc..0d18a3b5d12 100644 --- a/public/app/features/dashboard/state/DashboardModel.test.ts +++ b/public/app/features/dashboard/state/DashboardModel.test.ts @@ -635,4 +635,32 @@ describe('DashboardModel', () => { expect(saveModel.templating.list[0].filters[0].value).toBe('server 1'); }); }); + + describe('Given a dashboard with one panel legend on and two off', () => { + let model; + + beforeEach(() => { + const data = { + panels: [ + { id: 1, type: 'graph', gridPos: { x: 0, y: 0, w: 24, h: 2 }, legend: { show: true } }, + { id: 3, type: 'graph', gridPos: { x: 0, y: 4, w: 12, h: 2 }, legend: { show: false } }, + { id: 4, type: 'graph', gridPos: { x: 12, y: 4, w: 12, h: 2 }, legend: { show: false } }, + ], + }; + model = new DashboardModel(data); + }); + + it('toggleLegendsForAll should toggle all legends on on first execution', () => { + model.toggleLegendsForAll(); + const legendsOn = model.panels.filter(panel => panel.legend.show === true); + expect(legendsOn.length).toBe(3); + }); + + it('toggleLegendsForAll should toggle all legends off on second execution', () => { + model.toggleLegendsForAll(); + model.toggleLegendsForAll(); + const legendsOn = model.panels.filter(panel => panel.legend.show === true); + expect(legendsOn.length).toBe(0); + }); + }); }); diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index 17af2dbd801..239d0f9d2d8 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -917,4 +917,18 @@ export class DashboardModel { } } } + + toggleLegendsForAll() { + const panels = this.panels.filter(panel => { + return panel.legend !== undefined && panel.legend !== null; + }); + // determine if more panels are displaying legends or not + const onCount = panels.filter(panel => panel.legend.show).length; + const offCount = panels.length - onCount; + const panelLegendsOn = onCount >= offCount; + panels.forEach(panel => { + panel.legend.show = !panelLegendsOn; + panel.render(); + }); + } } diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index c0739b6d8bc..0c3ab44d8e8 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -106,6 +106,7 @@ export class PanelModel { events: Emitter; cacheTimeout?: any; cachedPluginOptions?: any; + legend?: { show: boolean }; constructor(model) { this.events = new Emitter(); From 94b6fc54fff768f402a8cf9e9b72e1ad816e99ac Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 11:14:22 -0800 Subject: [PATCH 123/244] use replaceVariables --- public/app/plugins/panel/text2/TextPanel.tsx | 15 ++++----------- .../app/plugins/panel/text2/TextPanelEditor.tsx | 4 ++-- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/public/app/plugins/panel/text2/TextPanel.tsx b/public/app/plugins/panel/text2/TextPanel.tsx index 9e9210df119..4abb0c105be 100644 --- a/public/app/plugins/panel/text2/TextPanel.tsx +++ b/public/app/plugins/panel/text2/TextPanel.tsx @@ -3,7 +3,6 @@ import React, { Component } from 'react'; import Remarkable from 'remarkable'; import { sanitize } from 'app/core/utils/text'; import config from 'app/core/config'; -import templateSrv from 'app/features/templating/template_srv'; import { debounce } from 'lodash'; // Types @@ -21,15 +20,8 @@ export class TextPanel extends Component { constructor(props) { super(props); - // TODO thre must be some better way to start with defualt options! - let opts = props.options; - if (opts && opts['options']) { - opts = opts['options']; - console.log('WEIRD!', opts); - } - this.state = { - html: this.processContent(opts), + html: this.processContent(props.options), }; } @@ -47,10 +39,11 @@ export class TextPanel extends Component { } prepareHTML(html: string): string { - const scopedVars = {}; // TODO?? = this.props.; + const { replaceVariables } = this.props; + html = config.disableSanitizeHtml ? html : sanitize(html); try { - return templateSrv.replace(html, scopedVars); + return replaceVariables(html); } catch (e) { // TODO -- put the error in the header window console.log('Text panel error: ', e); diff --git a/public/app/plugins/panel/text2/TextPanelEditor.tsx b/public/app/plugins/panel/text2/TextPanelEditor.tsx index 2581384af9a..d604b32c82c 100644 --- a/public/app/plugins/panel/text2/TextPanelEditor.tsx +++ b/public/app/plugins/panel/text2/TextPanelEditor.tsx @@ -10,9 +10,9 @@ export class TextPanelEditor extends PureComponent { value: 'html', label: 'HTML' }, ]; - onModeChange = (item: SelectOptionItem) => this.props.onChange({ ...this.props.options, mode: item.value }); + onModeChange = (item: SelectOptionItem) => this.props.onOptionsChange({ ...this.props.options, mode: item.value }); - onContentChange = evt => this.props.onChange({ ...this.props.options, content: (event.target as any).value }); + onContentChange = evt => this.props.onOptionsChange({ ...this.props.options, content: (event.target as any).value }); render() { const { mode, content } = this.props.options; From b58261100429b5b1751fc9dbbcb07d9fc606658c Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 5 Mar 2019 16:23:41 +0100 Subject: [PATCH 124/244] docker: update prometheus2 block to version 2.7.2 --- devenv/docker/blocks/prometheus2/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devenv/docker/blocks/prometheus2/Dockerfile b/devenv/docker/blocks/prometheus2/Dockerfile index 03edf4c9ee2..c9a2327bd4a 100644 --- a/devenv/docker/blocks/prometheus2/Dockerfile +++ b/devenv/docker/blocks/prometheus2/Dockerfile @@ -1,3 +1,3 @@ -FROM prom/prometheus:v2.2.0 +FROM prom/prometheus:v2.7.2 ADD prometheus.yml /etc/prometheus/ ADD alert.rules /etc/prometheus/ From a3da8dc6739735372397f3f0e7f3c845f62e4f48 Mon Sep 17 00:00:00 2001 From: Jon Ferreira Date: Tue, 5 Mar 2019 15:04:10 -0500 Subject: [PATCH 125/244] Expose onQueryChange to angular plugins --- public/app/features/explore/QueryEditor.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/app/features/explore/QueryEditor.tsx b/public/app/features/explore/QueryEditor.tsx index 1d329f1c56e..d158f6bb9f3 100644 --- a/public/app/features/explore/QueryEditor.tsx +++ b/public/app/features/explore/QueryEditor.tsx @@ -43,6 +43,9 @@ export default class QueryEditor extends PureComponent { this.props.onQueryChange(target); this.props.onExecuteQuery(); }, + onQueryChange: () => { + this.props.onQueryChange(target); + }, events: exploreEvents, panel: { datasource, targets: [target] }, dashboard: {}, From f845a3b841cdd5193c94b24ad29ba3e4933feedc Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Mar 2019 16:57:29 +0100 Subject: [PATCH 126/244] upgrade xorm packages to latest versions --- Gopkg.lock | 312 ++++++- Gopkg.toml | 8 +- pkg/services/sqlstore/alert.go | 4 +- pkg/services/sqlstore/annotation.go | 8 +- pkg/services/sqlstore/dashboard_version.go | 5 +- pkg/services/sqlstore/org_users.go | 2 +- pkg/services/sqlstore/team.go | 2 +- pkg/services/sqlstore/user_test.go | 2 +- vendor/github.com/go-ini/ini/LICENSE | 191 ----- vendor/github.com/go-ini/ini/error.go | 32 - vendor/github.com/go-ini/ini/file.go | 407 ---------- vendor/github.com/go-ini/ini/ini.go | 202 ----- vendor/github.com/go-ini/ini/key.go | 751 ----------------- vendor/github.com/go-ini/ini/parser.go | 477 ----------- vendor/github.com/go-ini/ini/section.go | 257 ------ vendor/github.com/go-ini/ini/struct.go | 512 ------------ vendor/github.com/go-xorm/builder/builder.go | 290 ++++++- .../go-xorm/builder/builder_delete.go | 13 +- .../go-xorm/builder/builder_insert.go | 51 +- .../go-xorm/builder/builder_limit.go | 100 +++ .../go-xorm/builder/builder_select.go | 110 ++- .../go-xorm/builder/builder_union.go | 47 ++ .../go-xorm/builder/builder_update.go | 15 +- vendor/github.com/go-xorm/builder/cond.go | 21 +- .../go-xorm/builder/cond_between.go | 29 +- vendor/github.com/go-xorm/builder/cond_or.go | 4 +- vendor/github.com/go-xorm/builder/error.go | 26 +- vendor/github.com/go-xorm/builder/sql.go | 156 ++++ .../go-xorm/builder/string_builder.go | 119 +++ vendor/github.com/go-xorm/core/cache.go | 14 +- vendor/github.com/go-xorm/core/column.go | 31 +- vendor/github.com/go-xorm/core/converstion.go | 4 + vendor/github.com/go-xorm/core/db.go | 341 +++----- vendor/github.com/go-xorm/core/dialect.go | 11 +- vendor/github.com/go-xorm/core/driver.go | 4 + vendor/github.com/go-xorm/core/error.go | 4 + vendor/github.com/go-xorm/core/filter.go | 10 +- vendor/github.com/go-xorm/core/ilogger.go | 4 + vendor/github.com/go-xorm/core/index.go | 18 +- vendor/github.com/go-xorm/core/mapper.go | 4 + vendor/github.com/go-xorm/core/pk.go | 4 + vendor/github.com/go-xorm/core/rows.go | 68 +- vendor/github.com/go-xorm/core/scan.go | 14 + vendor/github.com/go-xorm/core/stmt.go | 165 ++++ vendor/github.com/go-xorm/core/table.go | 6 +- vendor/github.com/go-xorm/core/tx.go | 153 ++++ vendor/github.com/go-xorm/core/type.go | 54 +- .../github.com/go-xorm/xorm/context_cache.go | 30 + .../github.com/go-xorm/xorm/dialect_mysql.go | 77 ++ .../go-xorm/xorm/dialect_postgres.go | 108 ++- .../go-xorm/xorm/dialect_sqlite3.go | 6 +- vendor/github.com/go-xorm/xorm/engine.go | 209 +++-- vendor/github.com/go-xorm/xorm/engine_cond.go | 5 +- .../github.com/go-xorm/xorm/engine_group.go | 10 + .../github.com/go-xorm/xorm/engine_maxlife.go | 22 - .../github.com/go-xorm/xorm/engine_table.go | 113 +++ vendor/github.com/go-xorm/xorm/error.go | 31 +- vendor/github.com/go-xorm/xorm/helpers.go | 162 ---- vendor/github.com/go-xorm/xorm/interface.go | 15 +- vendor/github.com/go-xorm/xorm/rows.go | 6 +- vendor/github.com/go-xorm/xorm/session.go | 767 +++++++++--------- .../github.com/go-xorm/xorm/session_cols.go | 115 +++ .../github.com/go-xorm/xorm/session_delete.go | 6 +- .../github.com/go-xorm/xorm/session_exist.go | 15 +- .../github.com/go-xorm/xorm/session_find.go | 53 +- vendor/github.com/go-xorm/xorm/session_get.go | 42 +- .../github.com/go-xorm/xorm/session_insert.go | 168 ++-- .../github.com/go-xorm/xorm/session_query.go | 76 +- vendor/github.com/go-xorm/xorm/session_raw.go | 26 +- .../github.com/go-xorm/xorm/session_schema.go | 87 +- vendor/github.com/go-xorm/xorm/session_tx.go | 2 + .../github.com/go-xorm/xorm/session_update.go | 115 ++- vendor/github.com/go-xorm/xorm/statement.go | 286 +++---- vendor/github.com/go-xorm/xorm/transaction.go | 26 + vendor/github.com/go-xorm/xorm/xorm.go | 14 +- vendor/github.com/jmespath/go-jmespath/api.go | 2 +- 76 files changed, 3327 insertions(+), 4329 deletions(-) delete mode 100644 vendor/github.com/go-ini/ini/LICENSE delete mode 100644 vendor/github.com/go-ini/ini/error.go delete mode 100644 vendor/github.com/go-ini/ini/file.go delete mode 100644 vendor/github.com/go-ini/ini/ini.go delete mode 100644 vendor/github.com/go-ini/ini/key.go delete mode 100644 vendor/github.com/go-ini/ini/parser.go delete mode 100644 vendor/github.com/go-ini/ini/section.go delete mode 100644 vendor/github.com/go-ini/ini/struct.go create mode 100644 vendor/github.com/go-xorm/builder/builder_limit.go create mode 100644 vendor/github.com/go-xorm/builder/builder_union.go create mode 100644 vendor/github.com/go-xorm/builder/sql.go create mode 100644 vendor/github.com/go-xorm/builder/string_builder.go create mode 100644 vendor/github.com/go-xorm/core/stmt.go create mode 100644 vendor/github.com/go-xorm/core/tx.go create mode 100644 vendor/github.com/go-xorm/xorm/context_cache.go delete mode 100644 vendor/github.com/go-xorm/xorm/engine_maxlife.go create mode 100644 vendor/github.com/go-xorm/xorm/engine_table.go create mode 100644 vendor/github.com/go-xorm/xorm/transaction.go diff --git a/Gopkg.lock b/Gopkg.lock index dca36f1b3d0..235a315f1e8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,30 +2,39 @@ [[projects]] + digest = "1:f8ad8a53fa865a70efbe215b0ca34735523f50ea39e0efde319ab6fc80089b44" name = "cloud.google.com/go" packages = ["compute/metadata"] + pruneopts = "NUT" revision = "056a55f54a6cc77b440b31a56a5e7c3982d32811" version = "v0.22.0" [[projects]] + digest = "1:167b6f65a6656de568092189ae791253939f076df60231fdd64588ac703892a1" name = "github.com/BurntSushi/toml" packages = ["."] + pruneopts = "NUT" revision = "b26d9c308763d68093482582cea63d69be07a0f0" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:7d23e6e1889b8bb4bbb37a564708fdab4497ce232c3a99d66406c975b642a6ff" name = "github.com/Unknwon/com" packages = ["."] + pruneopts = "NUT" revision = "7677a1d7c1137cd3dd5ba7a076d0c898a1ef4520" [[projects]] branch = "master" + digest = "1:1610787cd9726e29d8fecc2a80e43e4fced008a1f560fec6688fc4d946f17835" name = "github.com/VividCortex/mysqlerr" packages = ["."] + pruneopts = "NUT" revision = "6c6b55f8796f578c870b7e19bafb16103bc40095" [[projects]] + digest = "1:ebe102b61c1615d2954734e3cfe1b6b06a5088c25a41055b38661d41ad7b8f27" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -69,399 +78,507 @@ "service/resourcegroupstaggingapi", "service/resourcegroupstaggingapi/resourcegroupstaggingapiiface", "service/s3", - "service/sts" + "service/sts", ] + pruneopts = "NUT" revision = "62936e15518acb527a1a9cb4a39d96d94d0fd9a2" version = "v1.16.15" [[projects]] branch = "master" + digest = "1:79cad073c7be02632d3fa52f62486848b089f560db1e94536de83a408c0f4726" name = "github.com/benbjohnson/clock" packages = ["."] + pruneopts = "NUT" revision = "7dc76406b6d3c05b5f71a86293cbcf3c4ea03b19" [[projects]] branch = "master" + digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "NUT" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] branch = "master" + digest = "1:433a2ff0ef4e2f8634614aab3174783c5ff80120b487712db96cc3712f409583" name = "github.com/bmizerany/assert" packages = ["."] + pruneopts = "NUT" revision = "b7ed37b82869576c289d7d97fb2bbd8b64a0cb28" [[projects]] branch = "master" + digest = "1:d8f9145c361920507a4f85ffb7f70b96beaedacba2ce8c00aa663adb08689d3e" name = "github.com/bradfitz/gomemcache" packages = ["memcache"] + pruneopts = "NUT" revision = "1952afaa557dc08e8e0d89eafab110fb501c1a2b" [[projects]] branch = "master" + digest = "1:8ecb89af7dfe3ac401bdb0c9390b134ef96a97e85f732d2b0604fb7b3977839f" name = "github.com/codahale/hdrhistogram" packages = ["."] + pruneopts = "NUT" revision = "3a0bb77429bd3a61596f5e8a3172445844342120" [[projects]] + digest = "1:5dba68a1600a235630e208cb7196b24e58fcbb77bb7a6bec08fcd23f081b0a58" name = "github.com/codegangsta/cli" packages = ["."] + pruneopts = "NUT" revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" version = "v1.20.0" [[projects]] + digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "NUT" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:1b318d2dd6cea8a1a8d8ec70348852303bd3e491df74e8bca6e32eb5a4d06970" name = "github.com/denisenkom/go-mssqldb" packages = [ ".", - "internal/cp" + "internal/cp", ] + pruneopts = "NUT" revision = "270bc3860bb94dd3a3ffd047377d746c5e276726" [[projects]] branch = "master" + digest = "1:2da5f11ad66ff01a27a5c3dba4620b7eee2327be75b32c9ee9f87c9a8001ecbf" name = "github.com/facebookgo/inject" packages = ["."] + pruneopts = "NUT" revision = "cc1aa653e50f6a9893bcaef89e673e5b24e1e97b" [[projects]] branch = "master" + digest = "1:1108df7f658c90db041e0d6174d55be689aaeb0585913b9c3c7aab51a3a6b2b1" name = "github.com/facebookgo/structtag" packages = ["."] + pruneopts = "NUT" revision = "217e25fb96916cc60332e399c9aa63f5c422ceed" [[projects]] + digest = "1:ade392a843b2035effb4b4a2efa2c3bab3eb29b992e98bacf9c898b0ecb54e45" name = "github.com/fatih/color" packages = ["."] + pruneopts = "NUT" revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4" version = "v1.7.0" -[[projects]] - name = "github.com/go-ini/ini" - packages = ["."] - revision = "6529cf7c58879c08d927016dde4477f18a0634cb" - version = "v1.36.0" - [[projects]] branch = "master" + digest = "1:682a0aca743a1a4a36697f3d7f86c0ed403c4e3a780db9935f633242855eac9c" name = "github.com/go-macaron/binding" packages = ["."] + pruneopts = "NUT" revision = "ac54ee249c27dca7e76fad851a4a04b73bd1b183" [[projects]] branch = "master" + digest = "1:6326b27f8e0c8e135c8674ddbc619fae879664ac832e8e6fa6a23ce0d279ed4d" name = "github.com/go-macaron/gzip" packages = ["."] + pruneopts = "NUT" revision = "cad1c6580a07c56f5f6bc52d66002a05985c5854" [[projects]] branch = "master" + digest = "1:fb8711b648d1ff03104fc1d9593a13cb1d5120be7ba2b01641c14ccae286a9e3" name = "github.com/go-macaron/inject" packages = ["."] + pruneopts = "NUT" revision = "d8a0b8677191f4380287cfebd08e462217bac7ad" [[projects]] branch = "master" + digest = "1:21577aafe885f088e8086a3415f154c63c0b7ce956a6994df2ac5776bc01b7e3" name = "github.com/go-macaron/session" packages = [ ".", "memcache", "postgres", - "redis" + "redis", ] + pruneopts = "NUT" revision = "068d408f9c54c7fa7fcc5e2bdd3241ab21280c9e" [[projects]] + digest = "1:fddd4bada6100d6fc49a9f32f18ba5718db45a58e4b00aa6377e1cfbf06af34f" name = "github.com/go-sql-driver/mysql" packages = ["."] + pruneopts = "NUT" revision = "2cc627ac8defc45d65066ae98f898166f580f9a4" [[projects]] + digest = "1:a1efdbc2762667c8a41cbf02b19a0549c846bf2c1d08cad4f445e3344089f1f0" name = "github.com/go-stack/stack" packages = ["."] + pruneopts = "NUT" revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" version = "v1.7.0" [[projects]] + digest = "1:06d21295033f211588d0ad7ff391cc1b27e72b60cb6d4b7db0d70cffae4cf228" name = "github.com/go-xorm/builder" packages = ["."] - revision = "bad0a612f0d6277b953910822ab5dfb30dd18237" - version = "v0.2.0" + pruneopts = "NUT" + revision = "1d658d7596c25394aab557ef5b50ef35bf706384" + version = "v0.3.4" [[projects]] + digest = "1:b26928aab0fff92592e8728c5bc9d6e404fa2017d6a8e841ae5e60a42237f6fc" name = "github.com/go-xorm/core" packages = ["."] - revision = "da1adaf7a28ca792961721a34e6e04945200c890" - version = "v0.5.7" + pruneopts = "NUT" + revision = "ccc80c1adf1f6172bbc548877f50a1163041a40a" + version = "v0.6.2" [[projects]] + digest = "1:407316703b32d68ccf5d39bdae57d411b6954e253e07d0fff0988a3f39861f2f" name = "github.com/go-xorm/xorm" packages = ["."] - revision = "1933dd69e294c0a26c0266637067f24dbb25770c" - version = "v0.6.4" + pruneopts = "NUT" + revision = "1f39c590c64924f358c0d89016ac9b2bb84e9125" + version = "v0.7.1" [[projects]] branch = "master" + digest = "1:ffbb19fb66f140b5ea059428d1f84246a055d1bc3d9456c1e5c3d143611f03d0" name = "github.com/golang/protobuf" packages = [ "proto", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp" + "ptypes/timestamp", ] + pruneopts = "NUT" revision = "927b65914520a8b7d44f5c9057611cfec6b2e2d0" [[projects]] branch = "master" + digest = "1:f14d1b50e0075fb00177f12a96dd7addf93d1e2883c25befd17285b779549795" name = "github.com/gopherjs/gopherjs" packages = ["js"] + pruneopts = "NUT" revision = "8dffc02ea1cb8398bb73f30424697c60fcf8d4c5" [[projects]] + digest = "1:3b708ebf63bfa9ba3313bedb8526bc0bb284e51474e65e958481476a9d4a12aa" name = "github.com/gorilla/websocket" packages = ["."] + pruneopts = "NUT" revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" version = "v1.2.0" [[projects]] + digest = "1:4e771d1c6e15ca4516ad971c34205c822b5cff2747179679d7b321e4e1bfe431" name = "github.com/gosimple/slug" packages = ["."] + pruneopts = "NUT" revision = "e9f42fa127660e552d0ad2b589868d403a9be7c6" version = "v1.1.1" [[projects]] branch = "master" + digest = "1:08e53c69cd267ef7d71eeae5d953153d0d2bc1b8e0b498731fe9acaead7001b6" name = "github.com/grafana/grafana-plugin-model" packages = [ "go/datasource", - "go/renderer" + "go/renderer", ] + pruneopts = "NUT" revision = "84176c64269d8060f99e750ee8aba6f062753336" [[projects]] branch = "master" + digest = "1:58ba5285227b0f635652cd4aa82c4cfd00b590191eadd823462f0c9f64e3ae07" name = "github.com/hashicorp/go-hclog" packages = ["."] + pruneopts = "NUT" revision = "69ff559dc25f3b435631604f573a5fa1efdb6433" [[projects]] + digest = "1:532090ffc3b05a7e4c0229dd2698d79149f2e0683df993224a8b202f607fb605" name = "github.com/hashicorp/go-plugin" packages = ["."] + pruneopts = "NUT" revision = "e8d22c780116115ae5624720c9af0c97afe4f551" [[projects]] branch = "master" + digest = "1:8925116d1edcd85fc0c014e1aa69ce12892489b48ee633a605c46d893b8c151f" name = "github.com/hashicorp/go-version" packages = ["."] + pruneopts = "NUT" revision = "23480c0665776210b5fbbac6eaaee40e3e6a96b7" [[projects]] branch = "master" + digest = "1:8deb0c5545c824dfeb0ac77ab8eb67a3d541eab76df5c85ce93064ef02d44cd0" name = "github.com/hashicorp/yamux" packages = ["."] + pruneopts = "NUT" revision = "7221087c3d281fda5f794e28c2ea4c6e4d5c4558" [[projects]] + digest = "1:efbe016b6d198cf44f1db0ed2fbdf1b36ebf1f6956cc9b76d6affa96f022d368" name = "github.com/inconshreveable/log15" packages = ["."] + pruneopts = "NUT" revision = "0decfc6c20d9ca0ad143b0e89dcaa20f810b4fb3" version = "v2.13" [[projects]] + digest = "1:1f2aebae7e7c856562355ec0198d8ca2fa222fb05e5b1b66632a1fce39631885" name = "github.com/jmespath/go-jmespath" packages = ["."] - revision = "0b12d6b5" + pruneopts = "NUT" + revision = "c2b33e84" [[projects]] + digest = "1:6ddab442e52381bab82fb6c07ef3f4b565ff7ec4b8fae96d8dd4b8573a460597" name = "github.com/jtolds/gls" packages = ["."] + pruneopts = "NUT" revision = "77f18212c9c7edc9bd6a33d383a7b545ce62f064" version = "v4.2.1" [[projects]] + digest = "1:1da1796a71eb70f1e3e085984d044f67840bb0326816ec8276231aa87b1b9fc3" name = "github.com/klauspost/compress" packages = [ "flate", - "gzip" + "gzip", ] + pruneopts = "NUT" revision = "6c8db69c4b49dd4df1fff66996cf556176d0b9bf" version = "v1.2.1" [[projects]] + digest = "1:5e55a8699c9ff7aba1e4c8952aeda209685d88d4cb63a8766c338e333b8e65d6" name = "github.com/klauspost/cpuid" packages = ["."] + pruneopts = "NUT" revision = "ae7887de9fa5d2db4eaa8174a7eff2c1ac00f2da" version = "v1.1" [[projects]] + digest = "1:b95da1293525625ef6f07be79d537b9bf2ecd7901efcf9a92193edafbd55b9ef" name = "github.com/klauspost/crc32" packages = ["."] + pruneopts = "NUT" revision = "cb6bfca970f6908083f26f39a79009d608efd5cd" version = "v1.1" [[projects]] + digest = "1:7b21c7fc5551b46d1308b4ffa9e9e49b66c7a8b0ba88c0130474b0e7a20d859f" name = "github.com/kr/pretty" packages = ["."] + pruneopts = "NUT" revision = "73f6ac0b30a98e433b289500d779f50c1a6f0712" version = "v0.1.0" [[projects]] + digest = "1:c3a7836b5904db0f8b609595b619916a6831cb35b8b714aec39f96d00c6155d8" name = "github.com/kr/text" packages = ["."] + pruneopts = "NUT" revision = "e2ffdb16a802fe2bb95e2e35ff34f0e53aeef34f" version = "v0.1.0" [[projects]] branch = "master" + digest = "1:7a1e592f0349d56fac8ce47f28469e4e7f4ce637cb26f40c88da9dff25db1c98" name = "github.com/lib/pq" packages = [ ".", - "oid" + "oid", ] + pruneopts = "NUT" revision = "d34b9ff171c21ad295489235aec8b6626023cd04" [[projects]] + digest = "1:08c231ec84231a7e23d67e4b58f975e1423695a32467a362ee55a803f9de8061" name = "github.com/mattn/go-colorable" packages = ["."] + pruneopts = "NUT" revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072" version = "v0.0.9" [[projects]] + digest = "1:bc4f7eec3b7be8c6cb1f0af6c1e3333d5bb71072951aaaae2f05067b0803f287" name = "github.com/mattn/go-isatty" packages = ["."] + pruneopts = "NUT" revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" version = "v0.0.3" [[projects]] + digest = "1:536979f1c56397dbf91c2785159b37dec37e35d3bffa3cd1cfe66d25f51f8088" name = "github.com/mattn/go-sqlite3" packages = ["."] + pruneopts = "NUT" revision = "323a32be5a2421b8c7087225079c6c900ec397cd" version = "v1.7.0" [[projects]] + digest = "1:5985ef4caf91ece5d54817c11ea25f182697534f8ae6521eadcd628c142ac4b6" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "NUT" revision = "3247c84500bff8d9fb6d579d800f20b3e091582c" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:18b773b92ac82a451c1276bd2776c1e55ce057ee202691ab33c8d6690efcc048" name = "github.com/mitchellh/go-testing-interface" packages = ["."] + pruneopts = "NUT" revision = "a61a99592b77c9ba629d254a693acffaeb4b7e28" [[projects]] + digest = "1:3b517122f3aad1ecce45a630ea912b3092b4729f25532a911d0cb2935a1f9352" name = "github.com/oklog/run" packages = ["."] + pruneopts = "NUT" revision = "4dadeb3030eda0273a12382bb2348ffc7c9d1a39" version = "v1.0.0" [[projects]] + digest = "1:7da29c22bcc5c2ffb308324377dc00b5084650348c2799e573ed226d8cc9faf0" name = "github.com/opentracing/opentracing-go" packages = [ ".", "ext", - "log" + "log", ] + pruneopts = "NUT" revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" version = "v1.0.2" [[projects]] + digest = "1:748946761cf99c8b73cef5a3c0ee3e040859dd713a20cece0d0e0dc04e6ceca7" name = "github.com/patrickmn/go-cache" packages = ["."] + pruneopts = "NUT" revision = "a3647f8e31d79543b2d0f0ae2fe5c379d72cedc0" version = "v2.1.0" [[projects]] + digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "NUT" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:4759bed95e3a52febc18c071db28790a5c6e9e106ee201a37add6f6a056f8f9c" name = "github.com/prometheus/client_golang" packages = [ "api", "api/prometheus/v1", "prometheus", - "prometheus/promhttp" + "prometheus/promhttp", ] + pruneopts = "NUT" revision = "967789050ba94deca04a5e84cce8ad472ce313c1" version = "v0.9.0-pre1" [[projects]] branch = "master" + digest = "1:32d10bdfa8f09ecf13598324dba86ab891f11db3c538b6a34d1c3b5b99d7c36b" name = "github.com/prometheus/client_model" packages = ["go"] + pruneopts = "NUT" revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" [[projects]] branch = "master" + digest = "1:768b555b86742de2f28beb37f1dedce9a75f91f871d75b5717c96399c1a78c08" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", - "model" + "model", ] + pruneopts = "NUT" revision = "d811d2e9bf898806ecfb6ef6296774b13ffc314c" [[projects]] branch = "master" + digest = "1:c4a213a8d73fbb0b13f717ba7996116602ef18ecb42b91d77405877914cb0349" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs" + "xfs", ] + pruneopts = "NUT" revision = "8b1c2da0d56deffdbb9e48d4414b4e674bd8083e" [[projects]] branch = "master" + digest = "1:16e2136a67ec44aa2d1d6b0fd65394b3c4a8b2a1b6730c77967f7b7b06b179b2" name = "github.com/rainycape/unidecode" packages = ["."] + pruneopts = "NUT" revision = "cb7f23ec59bec0d61b19c56cd88cee3d0cc1870c" [[projects]] + digest = "1:d917313f309bda80d27274d53985bc65651f81a5b66b820749ac7f8ef061fd04" name = "github.com/sergi/go-diff" packages = ["diffmatchpatch"] + pruneopts = "NUT" revision = "1744e2970ca51c86172c8190fadad617561ed6e7" version = "v1.0.0" [[projects]] + digest = "1:1f0b284a6858827de4c27c66b49b2b25df3e16b031c2b57b7892273131e7dd2b" name = "github.com/smartystreets/assertions" packages = [ ".", "internal/go-render/render", - "internal/oglematchers" + "internal/oglematchers", ] + pruneopts = "NUT" revision = "7678a5452ebea5b7090a6b163f844c133f523da2" version = "1.8.3" [[projects]] + digest = "1:7efd0b2309cdd6468029fa30c808c50a820c9344df07e1a4bbdaf18f282907aa" name = "github.com/smartystreets/goconvey" packages = [ "convey", "convey/gotest", - "convey/reporting" + "convey/reporting", ] + pruneopts = "NUT" revision = "9e8dc3f972df6c8fcc0375ef492c24d0bb204857" version = "1.6.3" [[projects]] branch = "master" + digest = "1:a66add8dd963bfc72649017c1b321198f596cb4958cb1a11ff91a1be8691020b" name = "github.com/teris-io/shortid" packages = ["."] + pruneopts = "NUT" revision = "771a37caa5cf0c81f585d7b6df4dfc77e0615b5c" [[projects]] + digest = "1:3d48c38e0eca8c66df62379c5ae7a83fb5cd839b94f241354c07ba077da7bc45" name = "github.com/uber/jaeger-client-go" packages = [ ".", @@ -479,45 +596,55 @@ "thrift-gen/jaeger", "thrift-gen/sampling", "thrift-gen/zipkincore", - "utils" + "utils", ] + pruneopts = "NUT" revision = "b043381d944715b469fd6b37addfd30145ca1758" version = "v2.14.0" [[projects]] + digest = "1:0f09db8429e19d57c8346ad76fbbc679341fa86073d3b8fb5ac919f0357d8f4c" name = "github.com/uber/jaeger-lib" packages = ["metrics"] + pruneopts = "NUT" revision = "ed3a127ec5fef7ae9ea95b01b542c47fbd999ce5" version = "v1.5.0" [[projects]] + digest = "1:4c7d12ad3ef47bb03892a52e2609dc9a9cff93136ca9c7d31c00b79fcbc23c7b" name = "github.com/yudai/gojsondiff" packages = [ ".", - "formatter" + "formatter", ] + pruneopts = "NUT" revision = "7b1b7adf999dab73a6eb02669c3d82dbb27a3dd6" version = "1.0.0" [[projects]] branch = "master" + digest = "1:e50cbf8eba568d59b71e08c22c2a77809ed4646ae06ef4abb32b3d3d3fdb1a77" name = "github.com/yudai/golcs" packages = ["."] + pruneopts = "NUT" revision = "ecda9a501e8220fae3b4b600c3db4b0ba22cfc68" [[projects]] branch = "master" + digest = "1:758f363e0dff33cf00b234be2efb12f919d79b42d5ae3909ff9eb69ef2c3cca5" name = "golang.org/x/crypto" packages = [ "ed25519", "ed25519/internal/edwards25519", "md4", - "pbkdf2" + "pbkdf2", ] + pruneopts = "NUT" revision = "1a580b3eff7814fc9b40602fd35256c63b50f491" [[projects]] branch = "master" + digest = "1:0b3fee9c4472022a0982ee0d81e08b3cc3e595f50befd7a4b358b48540d9d8c5" name = "golang.org/x/net" packages = [ "context", @@ -527,35 +654,43 @@ "http2/hpack", "idna", "internal/timeseries", - "trace" + "trace", ] + pruneopts = "NUT" revision = "2491c5de3490fced2f6cff376127c667efeed857" [[projects]] branch = "master" + digest = "1:46bd4e66bfce5e77f08fc2e8dcacc3676e679241ce83d9c150ff0397d686dd44" name = "golang.org/x/oauth2" packages = [ ".", "google", "internal", "jws", - "jwt" + "jwt", ] + pruneopts = "NUT" revision = "cdc340f7c179dbbfa4afd43b7614e8fcadde4269" [[projects]] branch = "master" + digest = "1:39ebcc2b11457b703ae9ee2e8cca0f68df21969c6102cb3b705f76cca0ea0239" name = "golang.org/x/sync" packages = ["errgroup"] + pruneopts = "NUT" revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" [[projects]] branch = "master" + digest = "1:ec21c5bf0572488865b93e30ffd9132afbf85bec0b20c2d6cbcf349cf2031ed5" name = "golang.org/x/sys" packages = ["unix"] + pruneopts = "NUT" revision = "7c87d13f8e835d2fb3a70a2912c811ed0c1d241b" [[projects]] + digest = "1:e7071ed636b5422cc51c0e3a6cebc229d6c9fffc528814b519a980641422d619" name = "golang.org/x/text" packages = [ "collate", @@ -571,12 +706,14 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", ] + pruneopts = "NUT" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] + digest = "1:dbd5568923513ee74aa626d027e2a8a352cf8f35df41d19f4e34491d1858c38b" name = "google.golang.org/appengine" packages = [ ".", @@ -589,18 +726,22 @@ "internal/modules", "internal/remote_api", "internal/urlfetch", - "urlfetch" + "urlfetch", ] + pruneopts = "NUT" revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:3c24554c312721e98fa6b76403e7100cf974eb46b1255ea7fc6471db9a9ce498" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] + pruneopts = "NUT" revision = "7bb2a897381c9c5ab2aeb8614f758d7766af68ff" [[projects]] + digest = "1:840b77b6eb539b830bb760b6e30b688ed2ff484bd83466fce2395835ed9367fe" name = "google.golang.org/grpc" packages = [ ".", @@ -627,78 +768,177 @@ "stats", "status", "tap", - "transport" + "transport", ] + pruneopts = "NUT" revision = "1e2570b1b19ade82d8dbb31bba4e65e9f9ef5b34" version = "v1.11.1" [[projects]] branch = "v3" + digest = "1:1244a9b3856f70d5ffb74bbfd780fc9d47f93f2049fa265c6fb602878f507bf8" name = "gopkg.in/alexcesaro/quotedprintable.v3" packages = ["."] + pruneopts = "NUT" revision = "2caba252f4dc53eaf6b553000885530023f54623" [[projects]] + digest = "1:aea6e9483c167cc6fdf1274c442558c5dda8fd3373372be04d98c79100868da1" name = "gopkg.in/asn1-ber.v1" packages = ["."] + pruneopts = "NUT" revision = "379148ca0225df7a432012b8df0355c2a2063ac0" version = "v1.2" [[projects]] + digest = "1:24bfc2e8bf971485cb5ba0f0e5b08a1b806cca5828134df76b32d1ea50f2ab49" name = "gopkg.in/bufio.v1" packages = ["."] + pruneopts = "NUT" revision = "567b2bfa514e796916c4747494d6ff5132a1dfce" version = "v1" [[projects]] + digest = "1:e05711632e1515319b014e8fe4cbe1d30ab024c473403f60cf0fdeb4c586a474" name = "gopkg.in/ini.v1" packages = ["."] + pruneopts = "NUT" revision = "6529cf7c58879c08d927016dde4477f18a0634cb" version = "v1.36.0" [[projects]] + digest = "1:c847b7fea4c7e6db5281a37dffc4620cb78c1227403a79e5aa290db517657ac1" name = "gopkg.in/ldap.v3" packages = ["."] + pruneopts = "NUT" revision = "5c2c0f997205c29de14cb6c35996370c2c5dfab1" version = "v3" [[projects]] + digest = "1:3b0cf3a465fd07f76e5fc1a9d0783c662dac0de9fc73d713ebe162768fd87b5f" name = "gopkg.in/macaron.v1" packages = ["."] + pruneopts = "NUT" revision = "c1be95e6d21e769e44e1ec33cec9da5837861c10" version = "v1.3.1" [[projects]] branch = "v2" + digest = "1:d52332f9e9f2c6343652e13aa3fd40cfd03353520c9a48d90f21215d3012d50f" name = "gopkg.in/mail.v2" packages = ["."] + pruneopts = "NUT" revision = "5bc5c8bb07bd8d2803831fbaf8cbd630fcde2c68" [[projects]] + digest = "1:00126f697efdcab42f07c89ac8bf0095fb2328aef6464e070055154088cea859" name = "gopkg.in/redis.v2" packages = ["."] + pruneopts = "NUT" revision = "e6179049628164864e6e84e973cfb56335748dea" version = "v2.3.2" [[projects]] + digest = "1:a50fabe7a46692dc7c656310add3d517abe7914df02afd151ef84da884605dc8" name = "gopkg.in/square/go-jose.v2" packages = [ ".", "cipher", - "json" + "json", ] + pruneopts = "NUT" revision = "ef984e69dd356202fd4e4910d4d9c24468bdf0b8" version = "v2.1.9" [[projects]] branch = "v2" + digest = "1:7c95b35057a0ff2e19f707173cc1a947fa43a6eb5c4d300d196ece0334046082" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "NUT" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "88f0eb826b9c154ba46ea3bb64767707d86db75449ec75199eb2b8cf2b337fd4" + input-imports = [ + "github.com/BurntSushi/toml", + "github.com/Unknwon/com", + "github.com/VividCortex/mysqlerr", + "github.com/aws/aws-sdk-go/aws", + "github.com/aws/aws-sdk-go/aws/awserr", + "github.com/aws/aws-sdk-go/aws/awsutil", + "github.com/aws/aws-sdk-go/aws/credentials", + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds", + "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds", + "github.com/aws/aws-sdk-go/aws/defaults", + "github.com/aws/aws-sdk-go/aws/ec2metadata", + "github.com/aws/aws-sdk-go/aws/endpoints", + "github.com/aws/aws-sdk-go/aws/request", + "github.com/aws/aws-sdk-go/aws/session", + "github.com/aws/aws-sdk-go/service/cloudwatch", + "github.com/aws/aws-sdk-go/service/ec2", + "github.com/aws/aws-sdk-go/service/ec2/ec2iface", + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi", + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface", + "github.com/aws/aws-sdk-go/service/s3", + "github.com/aws/aws-sdk-go/service/sts", + "github.com/benbjohnson/clock", + "github.com/bmizerany/assert", + "github.com/codegangsta/cli", + "github.com/davecgh/go-spew/spew", + "github.com/denisenkom/go-mssqldb", + "github.com/facebookgo/inject", + "github.com/fatih/color", + "github.com/go-macaron/binding", + "github.com/go-macaron/gzip", + "github.com/go-macaron/session", + "github.com/go-macaron/session/memcache", + "github.com/go-macaron/session/postgres", + "github.com/go-macaron/session/redis", + "github.com/go-sql-driver/mysql", + "github.com/go-stack/stack", + "github.com/go-xorm/core", + "github.com/go-xorm/xorm", + "github.com/gorilla/websocket", + "github.com/gosimple/slug", + "github.com/grafana/grafana-plugin-model/go/datasource", + "github.com/grafana/grafana-plugin-model/go/renderer", + "github.com/hashicorp/go-hclog", + "github.com/hashicorp/go-plugin", + "github.com/hashicorp/go-version", + "github.com/inconshreveable/log15", + "github.com/lib/pq", + "github.com/mattn/go-isatty", + "github.com/mattn/go-sqlite3", + "github.com/opentracing/opentracing-go", + "github.com/opentracing/opentracing-go/ext", + "github.com/opentracing/opentracing-go/log", + "github.com/patrickmn/go-cache", + "github.com/pkg/errors", + "github.com/prometheus/client_golang/api", + "github.com/prometheus/client_golang/api/prometheus/v1", + "github.com/prometheus/client_golang/prometheus", + "github.com/prometheus/client_golang/prometheus/promhttp", + "github.com/prometheus/client_model/go", + "github.com/prometheus/common/expfmt", + "github.com/prometheus/common/model", + "github.com/smartystreets/goconvey/convey", + "github.com/teris-io/shortid", + "github.com/uber/jaeger-client-go/config", + "github.com/yudai/gojsondiff", + "github.com/yudai/gojsondiff/formatter", + "golang.org/x/net/context/ctxhttp", + "golang.org/x/oauth2", + "golang.org/x/oauth2/google", + "golang.org/x/oauth2/jwt", + "golang.org/x/sync/errgroup", + "gopkg.in/ini.v1", + "gopkg.in/ldap.v3", + "gopkg.in/macaron.v1", + "gopkg.in/mail.v2", + "gopkg.in/square/go-jose.v2", + "gopkg.in/yaml.v2", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 83e6890b0f4..d1bc0f55bae 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -81,11 +81,15 @@ ignored = [ [[constraint]] name = "github.com/go-xorm/core" - version = "=0.5.7" + version = "=0.6.2" + +[[override]] + name = "github.com/go-xorm/builder" + version = "=0.3.4" [[constraint]] name = "github.com/go-xorm/xorm" - version = "=0.6.4" + version = "=0.7.1" [[constraint]] name = "github.com/gorilla/websocket" diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 62ab348664f..7796cfd0dc7 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -309,7 +309,9 @@ func PauseAlert(cmd *m.PauseAlertCommand) error { params = append(params, v) } - res, err := sess.Exec(buffer.String(), params...) + sqlOrArgs := append([]interface{}{buffer.String()}, params...) + + res, err := sess.Exec(sqlOrArgs...) if err != nil { return err } diff --git a/pkg/services/sqlstore/annotation.go b/pkg/services/sqlstore/annotation.go index 274481baeca..a285b231aae 100644 --- a/pkg/services/sqlstore/annotation.go +++ b/pkg/services/sqlstore/annotation.go @@ -258,11 +258,15 @@ func (r *SqlAnnotationRepo) Delete(params *annotations.DeleteParams) error { queryParams = []interface{}{params.DashboardId, params.PanelId, params.OrgId} } - if _, err := sess.Exec(annoTagSql, queryParams...); err != nil { + sqlOrArgs := append([]interface{}{annoTagSql}, queryParams...) + + if _, err := sess.Exec(sqlOrArgs...); err != nil { return err } - if _, err := sess.Exec(sql, queryParams...); err != nil { + sqlOrArgs = append([]interface{}{sql}, queryParams...) + + if _, err := sess.Exec(sqlOrArgs...); err != nil { return err } diff --git a/pkg/services/sqlstore/dashboard_version.go b/pkg/services/sqlstore/dashboard_version.go index 1f2850b2021..7619e2ab269 100644 --- a/pkg/services/sqlstore/dashboard_version.go +++ b/pkg/services/sqlstore/dashboard_version.go @@ -51,7 +51,7 @@ func GetDashboardVersions(query *m.GetDashboardVersionsQuery) error { dashboard_version.message, dashboard_version.data,`+ dialect.Quote("user")+`.login as created_by`). - Join("LEFT", "user", `dashboard_version.created_by = `+dialect.Quote("user")+`.id`). + Join("LEFT", dialect.Quote("user"), `dashboard_version.created_by = `+dialect.Quote("user")+`.id`). Join("LEFT", "dashboard", `dashboard.id = dashboard_version.dashboard_id`). Where("dashboard_version.dashboard_id=? AND dashboard.org_id=?", query.DashboardId, query.OrgId). OrderBy("dashboard_version.version DESC"). @@ -102,7 +102,8 @@ func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { if len(versionIdsToDelete) > 0 { deleteExpiredSql := `DELETE FROM dashboard_version WHERE id IN (?` + strings.Repeat(",?", len(versionIdsToDelete)-1) + `)` - expiredResponse, err := sess.Exec(deleteExpiredSql, versionIdsToDelete...) + sqlOrArgs := append([]interface{}{deleteExpiredSql}, versionIdsToDelete...) + expiredResponse, err := sess.Exec(sqlOrArgs...) if err != nil { return err } diff --git a/pkg/services/sqlstore/org_users.go b/pkg/services/sqlstore/org_users.go index abbc320020e..897ef0ea1ad 100644 --- a/pkg/services/sqlstore/org_users.go +++ b/pkg/services/sqlstore/org_users.go @@ -98,7 +98,7 @@ func GetOrgUsers(query *m.GetOrgUsersQuery) error { query.Result = make([]*m.OrgUserDTO, 0) sess := x.Table("org_user") - sess.Join("INNER", "user", fmt.Sprintf("org_user.user_id=%s.id", x.Dialect().Quote("user"))) + sess.Join("INNER", x.Dialect().Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", x.Dialect().Quote("user"))) whereConditions := make([]string, 0) whereParams := make([]interface{}, 0) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index a3010a086e5..83593e6f2d7 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -280,7 +280,7 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error { func GetTeamMembers(query *m.GetTeamMembersQuery) error { query.Result = make([]*m.TeamMemberDTO, 0) sess := x.Table("team_member") - sess.Join("INNER", "user", fmt.Sprintf("team_member.user_id=%s.id", x.Dialect().Quote("user"))) + sess.Join("INNER", x.Dialect().Quote("user"), fmt.Sprintf("team_member.user_id=%s.id", x.Dialect().Quote("user"))) if query.OrgId != 0 { sess.Where("team_member.org_id=?", query.OrgId) } diff --git a/pkg/services/sqlstore/user_test.go b/pkg/services/sqlstore/user_test.go index 526c17a8256..84640687ed9 100644 --- a/pkg/services/sqlstore/user_test.go +++ b/pkg/services/sqlstore/user_test.go @@ -208,7 +208,7 @@ func TestUserDataAccess(t *testing.T) { func GetOrgUsersForTest(query *m.GetOrgUsersQuery) error { query.Result = make([]*m.OrgUserDTO, 0) sess := x.Table("org_user") - sess.Join("LEFT ", "user", fmt.Sprintf("org_user.user_id=%s.id", x.Dialect().Quote("user"))) + sess.Join("LEFT ", x.Dialect().Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", x.Dialect().Quote("user"))) sess.Where("org_user.org_id=?", query.OrgId) sess.Cols("org_user.org_id", "org_user.user_id", "user.email", "user.login", "org_user.role") diff --git a/vendor/github.com/go-ini/ini/LICENSE b/vendor/github.com/go-ini/ini/LICENSE deleted file mode 100644 index d361bbcdf5c..00000000000 --- a/vendor/github.com/go-ini/ini/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright 2014 Unknwon - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/go-ini/ini/error.go b/vendor/github.com/go-ini/ini/error.go deleted file mode 100644 index 80afe743158..00000000000 --- a/vendor/github.com/go-ini/ini/error.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2016 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "fmt" -) - -type ErrDelimiterNotFound struct { - Line string -} - -func IsErrDelimiterNotFound(err error) bool { - _, ok := err.(ErrDelimiterNotFound) - return ok -} - -func (err ErrDelimiterNotFound) Error() string { - return fmt.Sprintf("key-value delimiter not found: %s", err.Line) -} diff --git a/vendor/github.com/go-ini/ini/file.go b/vendor/github.com/go-ini/ini/file.go deleted file mode 100644 index d7982c32357..00000000000 --- a/vendor/github.com/go-ini/ini/file.go +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright 2017 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "strings" - "sync" -) - -// File represents a combination of a or more INI file(s) in memory. -type File struct { - options LoadOptions - dataSources []dataSource - - // Should make things safe, but sometimes doesn't matter. - BlockMode bool - lock sync.RWMutex - - // To keep data in order. - sectionList []string - // Actual data is stored here. - sections map[string]*Section - - NameMapper - ValueMapper -} - -// newFile initializes File object with given data sources. -func newFile(dataSources []dataSource, opts LoadOptions) *File { - return &File{ - BlockMode: true, - dataSources: dataSources, - sections: make(map[string]*Section), - sectionList: make([]string, 0, 10), - options: opts, - } -} - -// Empty returns an empty file object. -func Empty() *File { - // Ignore error here, we sure our data is good. - f, _ := Load([]byte("")) - return f -} - -// NewSection creates a new section. -func (f *File) NewSection(name string) (*Section, error) { - if len(name) == 0 { - return nil, errors.New("error creating new section: empty section name") - } else if f.options.Insensitive && name != DEFAULT_SECTION { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if inSlice(name, f.sectionList) { - return f.sections[name], nil - } - - f.sectionList = append(f.sectionList, name) - f.sections[name] = newSection(f, name) - return f.sections[name], nil -} - -// NewRawSection creates a new section with an unparseable body. -func (f *File) NewRawSection(name, body string) (*Section, error) { - section, err := f.NewSection(name) - if err != nil { - return nil, err - } - - section.isRawSection = true - section.rawBody = body - return section, nil -} - -// NewSections creates a list of sections. -func (f *File) NewSections(names ...string) (err error) { - for _, name := range names { - if _, err = f.NewSection(name); err != nil { - return err - } - } - return nil -} - -// GetSection returns section by given name. -func (f *File) GetSection(name string) (*Section, error) { - if len(name) == 0 { - name = DEFAULT_SECTION - } - if f.options.Insensitive { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - sec := f.sections[name] - if sec == nil { - return nil, fmt.Errorf("section '%s' does not exist", name) - } - return sec, nil -} - -// Section assumes named section exists and returns a zero-value when not. -func (f *File) Section(name string) *Section { - sec, err := f.GetSection(name) - if err != nil { - // Note: It's OK here because the only possible error is empty section name, - // but if it's empty, this piece of code won't be executed. - sec, _ = f.NewSection(name) - return sec - } - return sec -} - -// Section returns list of Section. -func (f *File) Sections() []*Section { - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - sections := make([]*Section, len(f.sectionList)) - for i, name := range f.sectionList { - sections[i] = f.sections[name] - } - return sections -} - -// ChildSections returns a list of child sections of given section name. -func (f *File) ChildSections(name string) []*Section { - return f.Section(name).ChildSections() -} - -// SectionStrings returns list of section names. -func (f *File) SectionStrings() []string { - list := make([]string, len(f.sectionList)) - copy(list, f.sectionList) - return list -} - -// DeleteSection deletes a section. -func (f *File) DeleteSection(name string) { - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if len(name) == 0 { - name = DEFAULT_SECTION - } - - for i, s := range f.sectionList { - if s == name { - f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) - delete(f.sections, name) - return - } - } -} - -func (f *File) reload(s dataSource) error { - r, err := s.ReadCloser() - if err != nil { - return err - } - defer r.Close() - - return f.parse(r) -} - -// Reload reloads and parses all data sources. -func (f *File) Reload() (err error) { - for _, s := range f.dataSources { - if err = f.reload(s); err != nil { - // In loose mode, we create an empty default section for nonexistent files. - if os.IsNotExist(err) && f.options.Loose { - f.parse(bytes.NewBuffer(nil)) - continue - } - return err - } - } - return nil -} - -// Append appends one or more data sources and reloads automatically. -func (f *File) Append(source interface{}, others ...interface{}) error { - ds, err := parseDataSource(source) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - for _, s := range others { - ds, err = parseDataSource(s) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - } - return f.Reload() -} - -func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { - equalSign := "=" - if PrettyFormat || PrettyEqual { - equalSign = " = " - } - - // Use buffer to make sure target is safe until finish encoding. - buf := bytes.NewBuffer(nil) - for i, sname := range f.sectionList { - sec := f.Section(sname) - if len(sec.Comment) > 0 { - if sec.Comment[0] != '#' && sec.Comment[0] != ';' { - sec.Comment = "; " + sec.Comment - } else { - sec.Comment = sec.Comment[:1] + " " + strings.TrimSpace(sec.Comment[1:]) - } - if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil { - return nil, err - } - } - - if i > 0 || DefaultHeader { - if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil { - return nil, err - } - } else { - // Write nothing if default section is empty - if len(sec.keyList) == 0 { - continue - } - } - - if sec.isRawSection { - if _, err := buf.WriteString(sec.rawBody); err != nil { - return nil, err - } - - if PrettySection { - // Put a line between sections - if _, err := buf.WriteString(LineBreak); err != nil { - return nil, err - } - } - continue - } - - // Count and generate alignment length and buffer spaces using the - // longest key. Keys may be modifed if they contain certain characters so - // we need to take that into account in our calculation. - alignLength := 0 - if PrettyFormat { - for _, kname := range sec.keyList { - keyLength := len(kname) - // First case will surround key by ` and second by """ - if strings.ContainsAny(kname, "\"=:") { - keyLength += 2 - } else if strings.Contains(kname, "`") { - keyLength += 6 - } - - if keyLength > alignLength { - alignLength = keyLength - } - } - } - alignSpaces := bytes.Repeat([]byte(" "), alignLength) - - KEY_LIST: - for _, kname := range sec.keyList { - key := sec.Key(kname) - if len(key.Comment) > 0 { - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - if key.Comment[0] != '#' && key.Comment[0] != ';' { - key.Comment = "; " + key.Comment - } else { - key.Comment = key.Comment[:1] + " " + strings.TrimSpace(key.Comment[1:]) - } - - // Support multiline comments - key.Comment = strings.Replace(key.Comment, "\n", "\n; ", -1) - - if _, err := buf.WriteString(key.Comment + LineBreak); err != nil { - return nil, err - } - } - - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - - switch { - case key.isAutoIncrement: - kname = "-" - case strings.ContainsAny(kname, "\"=:"): - kname = "`" + kname + "`" - case strings.Contains(kname, "`"): - kname = `"""` + kname + `"""` - } - - for _, val := range key.ValueWithShadows() { - if _, err := buf.WriteString(kname); err != nil { - return nil, err - } - - if key.isBooleanType { - if kname != sec.keyList[len(sec.keyList)-1] { - buf.WriteString(LineBreak) - } - continue KEY_LIST - } - - // Write out alignment spaces before "=" sign - if PrettyFormat { - buf.Write(alignSpaces[:alignLength-len(kname)]) - } - - // In case key value contains "\n", "`", "\"", "#" or ";" - if strings.ContainsAny(val, "\n`") { - val = `"""` + val + `"""` - } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") { - val = "`" + val + "`" - } - if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { - return nil, err - } - } - - for _, val := range key.nestedValues { - if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil { - return nil, err - } - } - } - - if PrettySection { - // Put a line between sections - if _, err := buf.WriteString(LineBreak); err != nil { - return nil, err - } - } - } - - return buf, nil -} - -// WriteToIndent writes content into io.Writer with given indention. -// If PrettyFormat has been set to be true, -// it will align "=" sign with spaces under each section. -func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) { - buf, err := f.writeToBuffer(indent) - if err != nil { - return 0, err - } - return buf.WriteTo(w) -} - -// WriteTo writes file content into io.Writer. -func (f *File) WriteTo(w io.Writer) (int64, error) { - return f.WriteToIndent(w, "") -} - -// SaveToIndent writes content to file system with given value indention. -func (f *File) SaveToIndent(filename, indent string) error { - // Note: Because we are truncating with os.Create, - // so it's safer to save to a temporary file location and rename afte done. - buf, err := f.writeToBuffer(indent) - if err != nil { - return err - } - - return ioutil.WriteFile(filename, buf.Bytes(), 0666) -} - -// SaveTo writes content to file system. -func (f *File) SaveTo(filename string) error { - return f.SaveToIndent(filename, "") -} diff --git a/vendor/github.com/go-ini/ini/ini.go b/vendor/github.com/go-ini/ini/ini.go deleted file mode 100644 index d983532299e..00000000000 --- a/vendor/github.com/go-ini/ini/ini.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2014 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -// Package ini provides INI file read and write functionality in Go. -package ini - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "regexp" - "runtime" -) - -const ( - // Name for default section. You can use this constant or the string literal. - // In most of cases, an empty string is all you need to access the section. - DEFAULT_SECTION = "DEFAULT" - - // Maximum allowed depth when recursively substituing variable names. - _DEPTH_VALUES = 99 - _VERSION = "1.36.0" -) - -// Version returns current package version literal. -func Version() string { - return _VERSION -} - -var ( - // Delimiter to determine or compose a new line. - // This variable will be changed to "\r\n" automatically on Windows - // at package init time. - LineBreak = "\n" - - // Variable regexp pattern: %(variable)s - varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`) - - // Indicate whether to align "=" sign with spaces to produce pretty output - // or reduce all possible spaces for compact format. - PrettyFormat = true - - // Place spaces around "=" sign even when PrettyFormat is false - PrettyEqual = false - - // Explicitly write DEFAULT section header - DefaultHeader = false - - // Indicate whether to put a line between sections - PrettySection = true -) - -func init() { - if runtime.GOOS == "windows" { - LineBreak = "\r\n" - } -} - -func inSlice(str string, s []string) bool { - for _, v := range s { - if str == v { - return true - } - } - return false -} - -// dataSource is an interface that returns object which can be read and closed. -type dataSource interface { - ReadCloser() (io.ReadCloser, error) -} - -// sourceFile represents an object that contains content on the local file system. -type sourceFile struct { - name string -} - -func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) { - return os.Open(s.name) -} - -// sourceData represents an object that contains content in memory. -type sourceData struct { - data []byte -} - -func (s *sourceData) ReadCloser() (io.ReadCloser, error) { - return ioutil.NopCloser(bytes.NewReader(s.data)), nil -} - -// sourceReadCloser represents an input stream with Close method. -type sourceReadCloser struct { - reader io.ReadCloser -} - -func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) { - return s.reader, nil -} - -func parseDataSource(source interface{}) (dataSource, error) { - switch s := source.(type) { - case string: - return sourceFile{s}, nil - case []byte: - return &sourceData{s}, nil - case io.ReadCloser: - return &sourceReadCloser{s}, nil - default: - return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s) - } -} - -type LoadOptions struct { - // Loose indicates whether the parser should ignore nonexistent files or return error. - Loose bool - // Insensitive indicates whether the parser forces all section and key names to lowercase. - Insensitive bool - // IgnoreContinuation indicates whether to ignore continuation lines while parsing. - IgnoreContinuation bool - // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value. - IgnoreInlineComment bool - // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing. - // This type of keys are mostly used in my.cnf. - AllowBooleanKeys bool - // AllowShadows indicates whether to keep track of keys with same name under same section. - AllowShadows bool - // AllowNestedValues indicates whether to allow AWS-like nested values. - // Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values - AllowNestedValues bool - // AllowPythonMultilineValues indicates whether to allow Python-like multi-line values. - // Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure - // Relevant quote: Values can also span multiple lines, as long as they are indented deeper - // than the first line of the value. - AllowPythonMultilineValues bool - // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format - // when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value" - UnescapeValueDoubleQuotes bool - // UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format - // when value is NOT surrounded by any quotes. - // Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all. - UnescapeValueCommentSymbols bool - // Some INI formats allow group blocks that store a block of raw content that doesn't otherwise - // conform to key/value pairs. Specify the names of those blocks here. - UnparseableSections []string -} - -func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) { - sources := make([]dataSource, len(others)+1) - sources[0], err = parseDataSource(source) - if err != nil { - return nil, err - } - for i := range others { - sources[i+1], err = parseDataSource(others[i]) - if err != nil { - return nil, err - } - } - f := newFile(sources, opts) - if err = f.Reload(); err != nil { - return nil, err - } - return f, nil -} - -// Load loads and parses from INI data sources. -// Arguments can be mixed of file name with string type, or raw data in []byte. -// It will return error if list contains nonexistent files. -func Load(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{}, source, others...) -} - -// LooseLoad has exactly same functionality as Load function -// except it ignores nonexistent files instead of returning error. -func LooseLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{Loose: true}, source, others...) -} - -// InsensitiveLoad has exactly same functionality as Load function -// except it forces all section and key names to be lowercased. -func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{Insensitive: true}, source, others...) -} - -// InsensitiveLoad has exactly same functionality as Load function -// except it allows have shadow keys. -func ShadowLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{AllowShadows: true}, source, others...) -} diff --git a/vendor/github.com/go-ini/ini/key.go b/vendor/github.com/go-ini/ini/key.go deleted file mode 100644 index 7c8566a1b4c..00000000000 --- a/vendor/github.com/go-ini/ini/key.go +++ /dev/null @@ -1,751 +0,0 @@ -// Copyright 2014 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "bytes" - "errors" - "fmt" - "strconv" - "strings" - "time" -) - -// Key represents a key under a section. -type Key struct { - s *Section - Comment string - name string - value string - isAutoIncrement bool - isBooleanType bool - - isShadow bool - shadows []*Key - - nestedValues []string -} - -// newKey simply return a key object with given values. -func newKey(s *Section, name, val string) *Key { - return &Key{ - s: s, - name: name, - value: val, - } -} - -func (k *Key) addShadow(val string) error { - if k.isShadow { - return errors.New("cannot add shadow to another shadow key") - } else if k.isAutoIncrement || k.isBooleanType { - return errors.New("cannot add shadow to auto-increment or boolean key") - } - - shadow := newKey(k.s, k.name, val) - shadow.isShadow = true - k.shadows = append(k.shadows, shadow) - return nil -} - -// AddShadow adds a new shadow key to itself. -func (k *Key) AddShadow(val string) error { - if !k.s.f.options.AllowShadows { - return errors.New("shadow key is not allowed") - } - return k.addShadow(val) -} - -func (k *Key) addNestedValue(val string) error { - if k.isAutoIncrement || k.isBooleanType { - return errors.New("cannot add nested value to auto-increment or boolean key") - } - - k.nestedValues = append(k.nestedValues, val) - return nil -} - -func (k *Key) AddNestedValue(val string) error { - if !k.s.f.options.AllowNestedValues { - return errors.New("nested value is not allowed") - } - return k.addNestedValue(val) -} - -// ValueMapper represents a mapping function for values, e.g. os.ExpandEnv -type ValueMapper func(string) string - -// Name returns name of key. -func (k *Key) Name() string { - return k.name -} - -// Value returns raw value of key for performance purpose. -func (k *Key) Value() string { - return k.value -} - -// ValueWithShadows returns raw values of key and its shadows if any. -func (k *Key) ValueWithShadows() []string { - if len(k.shadows) == 0 { - return []string{k.value} - } - vals := make([]string, len(k.shadows)+1) - vals[0] = k.value - for i := range k.shadows { - vals[i+1] = k.shadows[i].value - } - return vals -} - -// NestedValues returns nested values stored in the key. -// It is possible returned value is nil if no nested values stored in the key. -func (k *Key) NestedValues() []string { - return k.nestedValues -} - -// transformValue takes a raw value and transforms to its final string. -func (k *Key) transformValue(val string) string { - if k.s.f.ValueMapper != nil { - val = k.s.f.ValueMapper(val) - } - - // Fail-fast if no indicate char found for recursive value - if !strings.Contains(val, "%") { - return val - } - for i := 0; i < _DEPTH_VALUES; i++ { - vr := varPattern.FindString(val) - if len(vr) == 0 { - break - } - - // Take off leading '%(' and trailing ')s'. - noption := strings.TrimLeft(vr, "%(") - noption = strings.TrimRight(noption, ")s") - - // Search in the same section. - nk, err := k.s.GetKey(noption) - if err != nil || k == nk { - // Search again in default section. - nk, _ = k.s.f.Section("").GetKey(noption) - } - - // Substitute by new value and take off leading '%(' and trailing ')s'. - val = strings.Replace(val, vr, nk.value, -1) - } - return val -} - -// String returns string representation of value. -func (k *Key) String() string { - return k.transformValue(k.value) -} - -// Validate accepts a validate function which can -// return modifed result as key value. -func (k *Key) Validate(fn func(string) string) string { - return fn(k.String()) -} - -// parseBool returns the boolean value represented by the string. -// -// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On, -// 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off. -// Any other value returns an error. -func parseBool(str string) (value bool, err error) { - switch str { - case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On": - return true, nil - case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off": - return false, nil - } - return false, fmt.Errorf("parsing \"%s\": invalid syntax", str) -} - -// Bool returns bool type value. -func (k *Key) Bool() (bool, error) { - return parseBool(k.String()) -} - -// Float64 returns float64 type value. -func (k *Key) Float64() (float64, error) { - return strconv.ParseFloat(k.String(), 64) -} - -// Int returns int type value. -func (k *Key) Int() (int, error) { - return strconv.Atoi(k.String()) -} - -// Int64 returns int64 type value. -func (k *Key) Int64() (int64, error) { - return strconv.ParseInt(k.String(), 10, 64) -} - -// Uint returns uint type valued. -func (k *Key) Uint() (uint, error) { - u, e := strconv.ParseUint(k.String(), 10, 64) - return uint(u), e -} - -// Uint64 returns uint64 type value. -func (k *Key) Uint64() (uint64, error) { - return strconv.ParseUint(k.String(), 10, 64) -} - -// Duration returns time.Duration type value. -func (k *Key) Duration() (time.Duration, error) { - return time.ParseDuration(k.String()) -} - -// TimeFormat parses with given format and returns time.Time type value. -func (k *Key) TimeFormat(format string) (time.Time, error) { - return time.Parse(format, k.String()) -} - -// Time parses with RFC3339 format and returns time.Time type value. -func (k *Key) Time() (time.Time, error) { - return k.TimeFormat(time.RFC3339) -} - -// MustString returns default value if key value is empty. -func (k *Key) MustString(defaultVal string) string { - val := k.String() - if len(val) == 0 { - k.value = defaultVal - return defaultVal - } - return val -} - -// MustBool always returns value without error, -// it returns false if error occurs. -func (k *Key) MustBool(defaultVal ...bool) bool { - val, err := k.Bool() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatBool(defaultVal[0]) - return defaultVal[0] - } - return val -} - -// MustFloat64 always returns value without error, -// it returns 0.0 if error occurs. -func (k *Key) MustFloat64(defaultVal ...float64) float64 { - val, err := k.Float64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatFloat(defaultVal[0], 'f', -1, 64) - return defaultVal[0] - } - return val -} - -// MustInt always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt(defaultVal ...int) int { - val, err := k.Int() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatInt(int64(defaultVal[0]), 10) - return defaultVal[0] - } - return val -} - -// MustInt64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt64(defaultVal ...int64) int64 { - val, err := k.Int64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatInt(defaultVal[0], 10) - return defaultVal[0] - } - return val -} - -// MustUint always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint(defaultVal ...uint) uint { - val, err := k.Uint() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatUint(uint64(defaultVal[0]), 10) - return defaultVal[0] - } - return val -} - -// MustUint64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint64(defaultVal ...uint64) uint64 { - val, err := k.Uint64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatUint(defaultVal[0], 10) - return defaultVal[0] - } - return val -} - -// MustDuration always returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration { - val, err := k.Duration() - if len(defaultVal) > 0 && err != nil { - k.value = defaultVal[0].String() - return defaultVal[0] - } - return val -} - -// MustTimeFormat always parses with given format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time { - val, err := k.TimeFormat(format) - if len(defaultVal) > 0 && err != nil { - k.value = defaultVal[0].Format(format) - return defaultVal[0] - } - return val -} - -// MustTime always parses with RFC3339 format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTime(defaultVal ...time.Time) time.Time { - return k.MustTimeFormat(time.RFC3339, defaultVal...) -} - -// In always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) In(defaultVal string, candidates []string) string { - val := k.String() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InFloat64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 { - val := k.MustFloat64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt(defaultVal int, candidates []int) int { - val := k.MustInt() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 { - val := k.MustInt64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint(defaultVal uint, candidates []uint) uint { - val := k.MustUint() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64 { - val := k.MustUint64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTimeFormat always parses with given format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time { - val := k.MustTimeFormat(format) - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTime always parses with RFC3339 format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time { - return k.InTimeFormat(time.RFC3339, defaultVal, candidates) -} - -// RangeFloat64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 { - val := k.MustFloat64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt(defaultVal, min, max int) int { - val := k.MustInt() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt64(defaultVal, min, max int64) int64 { - val := k.MustInt64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeTimeFormat checks if value with given format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time { - val := k.MustTimeFormat(format) - if val.Unix() < min.Unix() || val.Unix() > max.Unix() { - return defaultVal - } - return val -} - -// RangeTime checks if value with RFC3339 format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time { - return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max) -} - -// Strings returns list of string divided by given delimiter. -func (k *Key) Strings(delim string) []string { - str := k.String() - if len(str) == 0 { - return []string{} - } - - runes := []rune(str) - vals := make([]string, 0, 2) - var buf bytes.Buffer - escape := false - idx := 0 - for { - if escape { - escape = false - if runes[idx] != '\\' && !strings.HasPrefix(string(runes[idx:]), delim) { - buf.WriteRune('\\') - } - buf.WriteRune(runes[idx]) - } else { - if runes[idx] == '\\' { - escape = true - } else if strings.HasPrefix(string(runes[idx:]), delim) { - idx += len(delim) - 1 - vals = append(vals, strings.TrimSpace(buf.String())) - buf.Reset() - } else { - buf.WriteRune(runes[idx]) - } - } - idx += 1 - if idx == len(runes) { - break - } - } - - if buf.Len() > 0 { - vals = append(vals, strings.TrimSpace(buf.String())) - } - - return vals -} - -// StringsWithShadows returns list of string divided by given delimiter. -// Shadows will also be appended if any. -func (k *Key) StringsWithShadows(delim string) []string { - vals := k.ValueWithShadows() - results := make([]string, 0, len(vals)*2) - for i := range vals { - if len(vals) == 0 { - continue - } - - results = append(results, strings.Split(vals[i], delim)...) - } - - for i := range results { - results[i] = k.transformValue(strings.TrimSpace(results[i])) - } - return results -} - -// Float64s returns list of float64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Float64s(delim string) []float64 { - vals, _ := k.parseFloat64s(k.Strings(delim), true, false) - return vals -} - -// Ints returns list of int divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Ints(delim string) []int { - vals, _ := k.parseInts(k.Strings(delim), true, false) - return vals -} - -// Int64s returns list of int64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Int64s(delim string) []int64 { - vals, _ := k.parseInt64s(k.Strings(delim), true, false) - return vals -} - -// Uints returns list of uint divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Uints(delim string) []uint { - vals, _ := k.parseUints(k.Strings(delim), true, false) - return vals -} - -// Uint64s returns list of uint64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Uint64s(delim string) []uint64 { - vals, _ := k.parseUint64s(k.Strings(delim), true, false) - return vals -} - -// TimesFormat parses with given format and returns list of time.Time divided by given delimiter. -// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC). -func (k *Key) TimesFormat(format, delim string) []time.Time { - vals, _ := k.parseTimesFormat(format, k.Strings(delim), true, false) - return vals -} - -// Times parses with RFC3339 format and returns list of time.Time divided by given delimiter. -// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC). -func (k *Key) Times(delim string) []time.Time { - return k.TimesFormat(time.RFC3339, delim) -} - -// ValidFloat64s returns list of float64 divided by given delimiter. If some value is not float, then -// it will not be included to result list. -func (k *Key) ValidFloat64s(delim string) []float64 { - vals, _ := k.parseFloat64s(k.Strings(delim), false, false) - return vals -} - -// ValidInts returns list of int divided by given delimiter. If some value is not integer, then it will -// not be included to result list. -func (k *Key) ValidInts(delim string) []int { - vals, _ := k.parseInts(k.Strings(delim), false, false) - return vals -} - -// ValidInt64s returns list of int64 divided by given delimiter. If some value is not 64-bit integer, -// then it will not be included to result list. -func (k *Key) ValidInt64s(delim string) []int64 { - vals, _ := k.parseInt64s(k.Strings(delim), false, false) - return vals -} - -// ValidUints returns list of uint divided by given delimiter. If some value is not unsigned integer, -// then it will not be included to result list. -func (k *Key) ValidUints(delim string) []uint { - vals, _ := k.parseUints(k.Strings(delim), false, false) - return vals -} - -// ValidUint64s returns list of uint64 divided by given delimiter. If some value is not 64-bit unsigned -// integer, then it will not be included to result list. -func (k *Key) ValidUint64s(delim string) []uint64 { - vals, _ := k.parseUint64s(k.Strings(delim), false, false) - return vals -} - -// ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter. -func (k *Key) ValidTimesFormat(format, delim string) []time.Time { - vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false) - return vals -} - -// ValidTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter. -func (k *Key) ValidTimes(delim string) []time.Time { - return k.ValidTimesFormat(time.RFC3339, delim) -} - -// StrictFloat64s returns list of float64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictFloat64s(delim string) ([]float64, error) { - return k.parseFloat64s(k.Strings(delim), false, true) -} - -// StrictInts returns list of int divided by given delimiter or error on first invalid input. -func (k *Key) StrictInts(delim string) ([]int, error) { - return k.parseInts(k.Strings(delim), false, true) -} - -// StrictInt64s returns list of int64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictInt64s(delim string) ([]int64, error) { - return k.parseInt64s(k.Strings(delim), false, true) -} - -// StrictUints returns list of uint divided by given delimiter or error on first invalid input. -func (k *Key) StrictUints(delim string) ([]uint, error) { - return k.parseUints(k.Strings(delim), false, true) -} - -// StrictUint64s returns list of uint64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictUint64s(delim string) ([]uint64, error) { - return k.parseUint64s(k.Strings(delim), false, true) -} - -// StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter -// or error on first invalid input. -func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) { - return k.parseTimesFormat(format, k.Strings(delim), false, true) -} - -// StrictTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter -// or error on first invalid input. -func (k *Key) StrictTimes(delim string) ([]time.Time, error) { - return k.StrictTimesFormat(time.RFC3339, delim) -} - -// parseFloat64s transforms strings to float64s. -func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) { - vals := make([]float64, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseFloat(str, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseInts transforms strings to ints. -func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) { - vals := make([]int, 0, len(strs)) - for _, str := range strs { - val, err := strconv.Atoi(str) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseInt64s transforms strings to int64s. -func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) { - vals := make([]int64, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseInt(str, 10, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseUints transforms strings to uints. -func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) { - vals := make([]uint, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseUint(str, 10, 0) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, uint(val)) - } - } - return vals, nil -} - -// parseUint64s transforms strings to uint64s. -func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) { - vals := make([]uint64, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseUint(str, 10, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseTimesFormat transforms strings to times in given format. -func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) { - vals := make([]time.Time, 0, len(strs)) - for _, str := range strs { - val, err := time.Parse(format, str) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// SetValue changes key value. -func (k *Key) SetValue(v string) { - if k.s.f.BlockMode { - k.s.f.lock.Lock() - defer k.s.f.lock.Unlock() - } - - k.value = v - k.s.keysHash[k.name] = v -} diff --git a/vendor/github.com/go-ini/ini/parser.go b/vendor/github.com/go-ini/ini/parser.go deleted file mode 100644 index 826e893c0d7..00000000000 --- a/vendor/github.com/go-ini/ini/parser.go +++ /dev/null @@ -1,477 +0,0 @@ -// Copyright 2015 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "bufio" - "bytes" - "fmt" - "io" - "regexp" - "strconv" - "strings" - "unicode" -) - -var pythonMultiline = regexp.MustCompile("^(\\s+)([^\n]+)") - -type tokenType int - -const ( - _TOKEN_INVALID tokenType = iota - _TOKEN_COMMENT - _TOKEN_SECTION - _TOKEN_KEY -) - -type parser struct { - buf *bufio.Reader - isEOF bool - count int - comment *bytes.Buffer -} - -func newParser(r io.Reader) *parser { - return &parser{ - buf: bufio.NewReader(r), - count: 1, - comment: &bytes.Buffer{}, - } -} - -// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format. -// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding -func (p *parser) BOM() error { - mask, err := p.buf.Peek(2) - if err != nil && err != io.EOF { - return err - } else if len(mask) < 2 { - return nil - } - - switch { - case mask[0] == 254 && mask[1] == 255: - fallthrough - case mask[0] == 255 && mask[1] == 254: - p.buf.Read(mask) - case mask[0] == 239 && mask[1] == 187: - mask, err := p.buf.Peek(3) - if err != nil && err != io.EOF { - return err - } else if len(mask) < 3 { - return nil - } - if mask[2] == 191 { - p.buf.Read(mask) - } - } - return nil -} - -func (p *parser) readUntil(delim byte) ([]byte, error) { - data, err := p.buf.ReadBytes(delim) - if err != nil { - if err == io.EOF { - p.isEOF = true - } else { - return nil, err - } - } - return data, nil -} - -func cleanComment(in []byte) ([]byte, bool) { - i := bytes.IndexAny(in, "#;") - if i == -1 { - return nil, false - } - return in[i:], true -} - -func readKeyName(in []byte) (string, int, error) { - line := string(in) - - // Check if key name surrounded by quotes. - var keyQuote string - if line[0] == '"' { - if len(line) > 6 && string(line[0:3]) == `"""` { - keyQuote = `"""` - } else { - keyQuote = `"` - } - } else if line[0] == '`' { - keyQuote = "`" - } - - // Get out key name - endIdx := -1 - if len(keyQuote) > 0 { - startIdx := len(keyQuote) - // FIXME: fail case -> """"""name"""=value - pos := strings.Index(line[startIdx:], keyQuote) - if pos == -1 { - return "", -1, fmt.Errorf("missing closing key quote: %s", line) - } - pos += startIdx - - // Find key-value delimiter - i := strings.IndexAny(line[pos+startIdx:], "=:") - if i < 0 { - return "", -1, ErrDelimiterNotFound{line} - } - endIdx = pos + i - return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil - } - - endIdx = strings.IndexAny(line, "=:") - if endIdx < 0 { - return "", -1, ErrDelimiterNotFound{line} - } - return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil -} - -func (p *parser) readMultilines(line, val, valQuote string) (string, error) { - for { - data, err := p.readUntil('\n') - if err != nil { - return "", err - } - next := string(data) - - pos := strings.LastIndex(next, valQuote) - if pos > -1 { - val += next[:pos] - - comment, has := cleanComment([]byte(next[pos:])) - if has { - p.comment.Write(bytes.TrimSpace(comment)) - } - break - } - val += next - if p.isEOF { - return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next) - } - } - return val, nil -} - -func (p *parser) readContinuationLines(val string) (string, error) { - for { - data, err := p.readUntil('\n') - if err != nil { - return "", err - } - next := strings.TrimSpace(string(data)) - - if len(next) == 0 { - break - } - val += next - if val[len(val)-1] != '\\' { - break - } - val = val[:len(val)-1] - } - return val, nil -} - -// hasSurroundedQuote check if and only if the first and last characters -// are quotes \" or \'. -// It returns false if any other parts also contain same kind of quotes. -func hasSurroundedQuote(in string, quote byte) bool { - return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote && - strings.IndexByte(in[1:], quote) == len(in)-2 -} - -func (p *parser) readValue(in []byte, - parserBufferSize int, - ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols, allowPythonMultilines bool) (string, error) { - - line := strings.TrimLeftFunc(string(in), unicode.IsSpace) - if len(line) == 0 { - return "", nil - } - - var valQuote string - if len(line) > 3 && string(line[0:3]) == `"""` { - valQuote = `"""` - } else if line[0] == '`' { - valQuote = "`" - } else if unescapeValueDoubleQuotes && line[0] == '"' { - valQuote = `"` - } - - if len(valQuote) > 0 { - startIdx := len(valQuote) - pos := strings.LastIndex(line[startIdx:], valQuote) - // Check for multi-line value - if pos == -1 { - return p.readMultilines(line, line[startIdx:], valQuote) - } - - if unescapeValueDoubleQuotes && valQuote == `"` { - return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil - } - return line[startIdx : pos+startIdx], nil - } - - lastChar := line[len(line)-1] - // Won't be able to reach here if value only contains whitespace - line = strings.TrimSpace(line) - trimmedLastChar := line[len(line)-1] - - // Check continuation lines when desired - if !ignoreContinuation && trimmedLastChar == '\\' { - return p.readContinuationLines(line[:len(line)-1]) - } - - // Check if ignore inline comment - if !ignoreInlineComment { - i := strings.IndexAny(line, "#;") - if i > -1 { - p.comment.WriteString(line[i:]) - line = strings.TrimSpace(line[:i]) - } - } - - // Trim single and double quotes - if hasSurroundedQuote(line, '\'') || - hasSurroundedQuote(line, '"') { - line = line[1 : len(line)-1] - } else if len(valQuote) == 0 && unescapeValueCommentSymbols { - if strings.Contains(line, `\;`) { - line = strings.Replace(line, `\;`, ";", -1) - } - if strings.Contains(line, `\#`) { - line = strings.Replace(line, `\#`, "#", -1) - } - } else if allowPythonMultilines && lastChar == '\n' { - parserBufferPeekResult, _ := p.buf.Peek(parserBufferSize) - peekBuffer := bytes.NewBuffer(parserBufferPeekResult) - - identSize := -1 - val := line - - for { - peekData, peekErr := peekBuffer.ReadBytes('\n') - if peekErr != nil { - if peekErr == io.EOF { - return val, nil - } - return "", peekErr - } - - peekMatches := pythonMultiline.FindStringSubmatch(string(peekData)) - if len(peekMatches) != 3 { - return val, nil - } - - currentIdentSize := len(peekMatches[1]) - // NOTE: Return if not a python-ini multi-line value. - if currentIdentSize < 0 { - return val, nil - } - identSize = currentIdentSize - - // NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer. - _, err := p.readUntil('\n') - if err != nil { - return "", err - } - - val += fmt.Sprintf("\n%s", peekMatches[2]) - } - - // NOTE: If it was a Python multi-line value, - // return the appended value. - if identSize > 0 { - return val, nil - } - } - - return line, nil -} - -// parse parses data through an io.Reader. -func (f *File) parse(reader io.Reader) (err error) { - p := newParser(reader) - if err = p.BOM(); err != nil { - return fmt.Errorf("BOM: %v", err) - } - - // Ignore error because default section name is never empty string. - name := DEFAULT_SECTION - if f.options.Insensitive { - name = strings.ToLower(DEFAULT_SECTION) - } - section, _ := f.NewSection(name) - - // This "last" is not strictly equivalent to "previous one" if current key is not the first nested key - var isLastValueEmpty bool - var lastRegularKey *Key - - var line []byte - var inUnparseableSection bool - - // NOTE: Iterate and increase `currentPeekSize` until - // the size of the parser buffer is found. - // TODO: When Golang 1.10 is the lowest version supported, - // replace with `parserBufferSize := p.buf.Size()`. - parserBufferSize := 0 - // NOTE: Peek 1kb at a time. - currentPeekSize := 1024 - - if f.options.AllowPythonMultilineValues { - for { - peekBytes, _ := p.buf.Peek(currentPeekSize) - peekBytesLength := len(peekBytes) - - if parserBufferSize >= peekBytesLength { - break - } - - currentPeekSize *= 2 - parserBufferSize = peekBytesLength - } - } - - for !p.isEOF { - line, err = p.readUntil('\n') - if err != nil { - return err - } - - if f.options.AllowNestedValues && - isLastValueEmpty && len(line) > 0 { - if line[0] == ' ' || line[0] == '\t' { - lastRegularKey.addNestedValue(string(bytes.TrimSpace(line))) - continue - } - } - - line = bytes.TrimLeftFunc(line, unicode.IsSpace) - if len(line) == 0 { - continue - } - - // Comments - if line[0] == '#' || line[0] == ';' { - // Note: we do not care ending line break, - // it is needed for adding second line, - // so just clean it once at the end when set to value. - p.comment.Write(line) - continue - } - - // Section - if line[0] == '[' { - // Read to the next ']' (TODO: support quoted strings) - // TODO(unknwon): use LastIndexByte when stop supporting Go1.4 - closeIdx := bytes.LastIndex(line, []byte("]")) - if closeIdx == -1 { - return fmt.Errorf("unclosed section: %s", line) - } - - name := string(line[1:closeIdx]) - section, err = f.NewSection(name) - if err != nil { - return err - } - - comment, has := cleanComment(line[closeIdx+1:]) - if has { - p.comment.Write(comment) - } - - section.Comment = strings.TrimSpace(p.comment.String()) - - // Reset aotu-counter and comments - p.comment.Reset() - p.count = 1 - - inUnparseableSection = false - for i := range f.options.UnparseableSections { - if f.options.UnparseableSections[i] == name || - (f.options.Insensitive && strings.ToLower(f.options.UnparseableSections[i]) == strings.ToLower(name)) { - inUnparseableSection = true - continue - } - } - continue - } - - if inUnparseableSection { - section.isRawSection = true - section.rawBody += string(line) - continue - } - - kname, offset, err := readKeyName(line) - if err != nil { - // Treat as boolean key when desired, and whole line is key name. - if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys { - kname, err := p.readValue(line, - parserBufferSize, - f.options.IgnoreContinuation, - f.options.IgnoreInlineComment, - f.options.UnescapeValueDoubleQuotes, - f.options.UnescapeValueCommentSymbols, - f.options.AllowPythonMultilineValues) - if err != nil { - return err - } - key, err := section.NewBooleanKey(kname) - if err != nil { - return err - } - key.Comment = strings.TrimSpace(p.comment.String()) - p.comment.Reset() - continue - } - return err - } - - // Auto increment. - isAutoIncr := false - if kname == "-" { - isAutoIncr = true - kname = "#" + strconv.Itoa(p.count) - p.count++ - } - - value, err := p.readValue(line[offset:], - parserBufferSize, - f.options.IgnoreContinuation, - f.options.IgnoreInlineComment, - f.options.UnescapeValueDoubleQuotes, - f.options.UnescapeValueCommentSymbols, - f.options.AllowPythonMultilineValues) - if err != nil { - return err - } - isLastValueEmpty = len(value) == 0 - - key, err := section.NewKey(kname, value) - if err != nil { - return err - } - key.isAutoIncrement = isAutoIncr - key.Comment = strings.TrimSpace(p.comment.String()) - p.comment.Reset() - lastRegularKey = key - } - return nil -} diff --git a/vendor/github.com/go-ini/ini/section.go b/vendor/github.com/go-ini/ini/section.go deleted file mode 100644 index d8a40261920..00000000000 --- a/vendor/github.com/go-ini/ini/section.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2014 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "errors" - "fmt" - "strings" -) - -// Section represents a config section. -type Section struct { - f *File - Comment string - name string - keys map[string]*Key - keyList []string - keysHash map[string]string - - isRawSection bool - rawBody string -} - -func newSection(f *File, name string) *Section { - return &Section{ - f: f, - name: name, - keys: make(map[string]*Key), - keyList: make([]string, 0, 10), - keysHash: make(map[string]string), - } -} - -// Name returns name of Section. -func (s *Section) Name() string { - return s.name -} - -// Body returns rawBody of Section if the section was marked as unparseable. -// It still follows the other rules of the INI format surrounding leading/trailing whitespace. -func (s *Section) Body() string { - return strings.TrimSpace(s.rawBody) -} - -// SetBody updates body content only if section is raw. -func (s *Section) SetBody(body string) { - if !s.isRawSection { - return - } - s.rawBody = body -} - -// NewKey creates a new key to given section. -func (s *Section) NewKey(name, val string) (*Key, error) { - if len(name) == 0 { - return nil, errors.New("error creating new key: empty key name") - } else if s.f.options.Insensitive { - name = strings.ToLower(name) - } - - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - if inSlice(name, s.keyList) { - if s.f.options.AllowShadows { - if err := s.keys[name].addShadow(val); err != nil { - return nil, err - } - } else { - s.keys[name].value = val - } - return s.keys[name], nil - } - - s.keyList = append(s.keyList, name) - s.keys[name] = newKey(s, name, val) - s.keysHash[name] = val - return s.keys[name], nil -} - -// NewBooleanKey creates a new boolean type key to given section. -func (s *Section) NewBooleanKey(name string) (*Key, error) { - key, err := s.NewKey(name, "true") - if err != nil { - return nil, err - } - - key.isBooleanType = true - return key, nil -} - -// GetKey returns key in section by given name. -func (s *Section) GetKey(name string) (*Key, error) { - // FIXME: change to section level lock? - if s.f.BlockMode { - s.f.lock.RLock() - } - if s.f.options.Insensitive { - name = strings.ToLower(name) - } - key := s.keys[name] - if s.f.BlockMode { - s.f.lock.RUnlock() - } - - if key == nil { - // Check if it is a child-section. - sname := s.name - for { - if i := strings.LastIndex(sname, "."); i > -1 { - sname = sname[:i] - sec, err := s.f.GetSection(sname) - if err != nil { - continue - } - return sec.GetKey(name) - } else { - break - } - } - return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name) - } - return key, nil -} - -// HasKey returns true if section contains a key with given name. -func (s *Section) HasKey(name string) bool { - key, _ := s.GetKey(name) - return key != nil -} - -// Haskey is a backwards-compatible name for HasKey. -// TODO: delete me in v2 -func (s *Section) Haskey(name string) bool { - return s.HasKey(name) -} - -// HasValue returns true if section contains given raw value. -func (s *Section) HasValue(value string) bool { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - for _, k := range s.keys { - if value == k.value { - return true - } - } - return false -} - -// Key assumes named Key exists in section and returns a zero-value when not. -func (s *Section) Key(name string) *Key { - key, err := s.GetKey(name) - if err != nil { - // It's OK here because the only possible error is empty key name, - // but if it's empty, this piece of code won't be executed. - key, _ = s.NewKey(name, "") - return key - } - return key -} - -// Keys returns list of keys of section. -func (s *Section) Keys() []*Key { - keys := make([]*Key, len(s.keyList)) - for i := range s.keyList { - keys[i] = s.Key(s.keyList[i]) - } - return keys -} - -// ParentKeys returns list of keys of parent section. -func (s *Section) ParentKeys() []*Key { - var parentKeys []*Key - sname := s.name - for { - if i := strings.LastIndex(sname, "."); i > -1 { - sname = sname[:i] - sec, err := s.f.GetSection(sname) - if err != nil { - continue - } - parentKeys = append(parentKeys, sec.Keys()...) - } else { - break - } - - } - return parentKeys -} - -// KeyStrings returns list of key names of section. -func (s *Section) KeyStrings() []string { - list := make([]string, len(s.keyList)) - copy(list, s.keyList) - return list -} - -// KeysHash returns keys hash consisting of names and values. -func (s *Section) KeysHash() map[string]string { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - hash := map[string]string{} - for key, value := range s.keysHash { - hash[key] = value - } - return hash -} - -// DeleteKey deletes a key from section. -func (s *Section) DeleteKey(name string) { - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - for i, k := range s.keyList { - if k == name { - s.keyList = append(s.keyList[:i], s.keyList[i+1:]...) - delete(s.keys, name) - return - } - } -} - -// ChildSections returns a list of child sections of current section. -// For example, "[parent.child1]" and "[parent.child12]" are child sections -// of section "[parent]". -func (s *Section) ChildSections() []*Section { - prefix := s.name + "." - children := make([]*Section, 0, 3) - for _, name := range s.f.sectionList { - if strings.HasPrefix(name, prefix) { - children = append(children, s.f.sections[name]) - } - } - return children -} diff --git a/vendor/github.com/go-ini/ini/struct.go b/vendor/github.com/go-ini/ini/struct.go deleted file mode 100644 index 9719dc6985a..00000000000 --- a/vendor/github.com/go-ini/ini/struct.go +++ /dev/null @@ -1,512 +0,0 @@ -// Copyright 2014 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "bytes" - "errors" - "fmt" - "reflect" - "strings" - "time" - "unicode" -) - -// NameMapper represents a ini tag name mapper. -type NameMapper func(string) string - -// Built-in name getters. -var ( - // AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE. - AllCapsUnderscore NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - } - newstr = append(newstr, unicode.ToUpper(chr)) - } - return string(newstr) - } - // TitleUnderscore converts to format title_underscore. - TitleUnderscore NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - chr -= ('A' - 'a') - } - newstr = append(newstr, chr) - } - return string(newstr) - } -) - -func (s *Section) parseFieldName(raw, actual string) string { - if len(actual) > 0 { - return actual - } - if s.f.NameMapper != nil { - return s.f.NameMapper(raw) - } - return raw -} - -func parseDelim(actual string) string { - if len(actual) > 0 { - return actual - } - return "," -} - -var reflectTime = reflect.TypeOf(time.Now()).Kind() - -// setSliceWithProperType sets proper values to slice based on its type. -func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { - var strs []string - if allowShadow { - strs = key.StringsWithShadows(delim) - } else { - strs = key.Strings(delim) - } - - numVals := len(strs) - if numVals == 0 { - return nil - } - - var vals interface{} - var err error - - sliceOf := field.Type().Elem().Kind() - switch sliceOf { - case reflect.String: - vals = strs - case reflect.Int: - vals, err = key.parseInts(strs, true, false) - case reflect.Int64: - vals, err = key.parseInt64s(strs, true, false) - case reflect.Uint: - vals, err = key.parseUints(strs, true, false) - case reflect.Uint64: - vals, err = key.parseUint64s(strs, true, false) - case reflect.Float64: - vals, err = key.parseFloat64s(strs, true, false) - case reflectTime: - vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false) - default: - return fmt.Errorf("unsupported type '[]%s'", sliceOf) - } - if err != nil && isStrict { - return err - } - - slice := reflect.MakeSlice(field.Type(), numVals, numVals) - for i := 0; i < numVals; i++ { - switch sliceOf { - case reflect.String: - slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i])) - case reflect.Int: - slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i])) - case reflect.Int64: - slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i])) - case reflect.Uint: - slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i])) - case reflect.Uint64: - slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i])) - case reflect.Float64: - slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i])) - case reflectTime: - slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i])) - } - } - field.Set(slice) - return nil -} - -func wrapStrictError(err error, isStrict bool) error { - if isStrict { - return err - } - return nil -} - -// setWithProperType sets proper value to field based on its type, -// but it does not return error for failing parsing, -// because we want to use default value that is already assigned to strcut. -func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { - switch t.Kind() { - case reflect.String: - if len(key.String()) == 0 { - return nil - } - field.SetString(key.String()) - case reflect.Bool: - boolVal, err := key.Bool() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.SetBool(boolVal) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - durationVal, err := key.Duration() - // Skip zero value - if err == nil && int64(durationVal) > 0 { - field.Set(reflect.ValueOf(durationVal)) - return nil - } - - intVal, err := key.Int64() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.SetInt(intVal) - // byte is an alias for uint8, so supporting uint8 breaks support for byte - case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: - durationVal, err := key.Duration() - // Skip zero value - if err == nil && int(durationVal) > 0 { - field.Set(reflect.ValueOf(durationVal)) - return nil - } - - uintVal, err := key.Uint64() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.SetUint(uintVal) - - case reflect.Float32, reflect.Float64: - floatVal, err := key.Float64() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.SetFloat(floatVal) - case reflectTime: - timeVal, err := key.Time() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.Set(reflect.ValueOf(timeVal)) - case reflect.Slice: - return setSliceWithProperType(key, field, delim, allowShadow, isStrict) - default: - return fmt.Errorf("unsupported type '%s'", t) - } - return nil -} - -func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) { - opts := strings.SplitN(tag, ",", 3) - rawName = opts[0] - if len(opts) > 1 { - omitEmpty = opts[1] == "omitempty" - } - if len(opts) > 2 { - allowShadow = opts[2] == "allowshadow" - } - return rawName, omitEmpty, allowShadow -} - -func (s *Section) mapTo(val reflect.Value, isStrict bool) error { - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - typ := val.Type() - - for i := 0; i < typ.NumField(); i++ { - field := val.Field(i) - tpField := typ.Field(i) - - tag := tpField.Tag.Get("ini") - if tag == "-" { - continue - } - - rawName, _, allowShadow := parseTagOptions(tag) - fieldName := s.parseFieldName(tpField.Name, rawName) - if len(fieldName) == 0 || !field.CanSet() { - continue - } - - isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous - isStruct := tpField.Type.Kind() == reflect.Struct - if isAnonymous { - field.Set(reflect.New(tpField.Type.Elem())) - } - - if isAnonymous || isStruct { - if sec, err := s.f.GetSection(fieldName); err == nil { - if err = sec.mapTo(field, isStrict); err != nil { - return fmt.Errorf("error mapping field(%s): %v", fieldName, err) - } - continue - } - } - - if key, err := s.GetKey(fieldName); err == nil { - delim := parseDelim(tpField.Tag.Get("delim")) - if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil { - return fmt.Errorf("error mapping field(%s): %v", fieldName, err) - } - } - } - return nil -} - -// MapTo maps section to given struct. -func (s *Section) MapTo(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot map to non-pointer struct") - } - - return s.mapTo(val, false) -} - -// MapTo maps section to given struct in strict mode, -// which returns all possible error including value parsing error. -func (s *Section) StrictMapTo(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot map to non-pointer struct") - } - - return s.mapTo(val, true) -} - -// MapTo maps file to given struct. -func (f *File) MapTo(v interface{}) error { - return f.Section("").MapTo(v) -} - -// MapTo maps file to given struct in strict mode, -// which returns all possible error including value parsing error. -func (f *File) StrictMapTo(v interface{}) error { - return f.Section("").StrictMapTo(v) -} - -// MapTo maps data sources to given struct with name mapper. -func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { - cfg, err := Load(source, others...) - if err != nil { - return err - } - cfg.NameMapper = mapper - return cfg.MapTo(v) -} - -// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode, -// which returns all possible error including value parsing error. -func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { - cfg, err := Load(source, others...) - if err != nil { - return err - } - cfg.NameMapper = mapper - return cfg.StrictMapTo(v) -} - -// MapTo maps data sources to given struct. -func MapTo(v, source interface{}, others ...interface{}) error { - return MapToWithMapper(v, nil, source, others...) -} - -// StrictMapTo maps data sources to given struct in strict mode, -// which returns all possible error including value parsing error. -func StrictMapTo(v, source interface{}, others ...interface{}) error { - return StrictMapToWithMapper(v, nil, source, others...) -} - -// reflectSliceWithProperType does the opposite thing as setSliceWithProperType. -func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error { - slice := field.Slice(0, field.Len()) - if field.Len() == 0 { - return nil - } - - var buf bytes.Buffer - sliceOf := field.Type().Elem().Kind() - for i := 0; i < field.Len(); i++ { - switch sliceOf { - case reflect.String: - buf.WriteString(slice.Index(i).String()) - case reflect.Int, reflect.Int64: - buf.WriteString(fmt.Sprint(slice.Index(i).Int())) - case reflect.Uint, reflect.Uint64: - buf.WriteString(fmt.Sprint(slice.Index(i).Uint())) - case reflect.Float64: - buf.WriteString(fmt.Sprint(slice.Index(i).Float())) - case reflectTime: - buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339)) - default: - return fmt.Errorf("unsupported type '[]%s'", sliceOf) - } - buf.WriteString(delim) - } - key.SetValue(buf.String()[:buf.Len()-1]) - return nil -} - -// reflectWithProperType does the opposite thing as setWithProperType. -func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error { - switch t.Kind() { - case reflect.String: - key.SetValue(field.String()) - case reflect.Bool: - key.SetValue(fmt.Sprint(field.Bool())) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - key.SetValue(fmt.Sprint(field.Int())) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - key.SetValue(fmt.Sprint(field.Uint())) - case reflect.Float32, reflect.Float64: - key.SetValue(fmt.Sprint(field.Float())) - case reflectTime: - key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339))) - case reflect.Slice: - return reflectSliceWithProperType(key, field, delim) - default: - return fmt.Errorf("unsupported type '%s'", t) - } - return nil -} - -// CR: copied from encoding/json/encode.go with modifications of time.Time support. -// TODO: add more test coverage. -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - case reflectTime: - t, ok := v.Interface().(time.Time) - return ok && t.IsZero() - } - return false -} - -func (s *Section) reflectFrom(val reflect.Value) error { - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - typ := val.Type() - - for i := 0; i < typ.NumField(); i++ { - field := val.Field(i) - tpField := typ.Field(i) - - tag := tpField.Tag.Get("ini") - if tag == "-" { - continue - } - - opts := strings.SplitN(tag, ",", 2) - if len(opts) == 2 && opts[1] == "omitempty" && isEmptyValue(field) { - continue - } - - fieldName := s.parseFieldName(tpField.Name, opts[0]) - if len(fieldName) == 0 || !field.CanSet() { - continue - } - - if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) || - (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") { - // Note: The only error here is section doesn't exist. - sec, err := s.f.GetSection(fieldName) - if err != nil { - // Note: fieldName can never be empty here, ignore error. - sec, _ = s.f.NewSection(fieldName) - } - - // Add comment from comment tag - if len(sec.Comment) == 0 { - sec.Comment = tpField.Tag.Get("comment") - } - - if err = sec.reflectFrom(field); err != nil { - return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) - } - continue - } - - // Note: Same reason as secion. - key, err := s.GetKey(fieldName) - if err != nil { - key, _ = s.NewKey(fieldName, "") - } - - // Add comment from comment tag - if len(key.Comment) == 0 { - key.Comment = tpField.Tag.Get("comment") - } - - if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil { - return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) - } - - } - return nil -} - -// ReflectFrom reflects secion from given struct. -func (s *Section) ReflectFrom(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot reflect from non-pointer struct") - } - - return s.reflectFrom(val) -} - -// ReflectFrom reflects file from given struct. -func (f *File) ReflectFrom(v interface{}) error { - return f.Section("").ReflectFrom(v) -} - -// ReflectFrom reflects data sources from given struct with name mapper. -func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error { - cfg.NameMapper = mapper - return cfg.ReflectFrom(v) -} - -// ReflectFrom reflects data sources from given struct. -func ReflectFrom(cfg *File, v interface{}) error { - return ReflectFromWithMapper(cfg, v, nil) -} diff --git a/vendor/github.com/go-xorm/builder/builder.go b/vendor/github.com/go-xorm/builder/builder.go index 1253b9887e1..ffe86d4dcb5 100644 --- a/vendor/github.com/go-xorm/builder/builder.go +++ b/vendor/github.com/go-xorm/builder/builder.go @@ -4,6 +4,12 @@ package builder +import ( + sql2 "database/sql" + "fmt" + "sort" +) + type optype byte const ( @@ -12,6 +18,15 @@ const ( insertType // insert updateType // update deleteType // delete + unionType // union +) + +const ( + POSTGRES = "postgres" + SQLITE = "sqlite3" + MYSQL = "mysql" + MSSQL = "mssql" + ORACLE = "oracle" ) type join struct { @@ -20,60 +35,115 @@ type join struct { joinCond Cond } +type union struct { + unionType string + builder *Builder +} + +type limit struct { + limitN int + offset int +} + // Builder describes a SQL statement type Builder struct { optype - tableName string - cond Cond - selects []string - joins []join - inserts Eq - updates []Eq + dialect string + isNested bool + into string + from string + subQuery *Builder + cond Cond + selects []string + joins []join + unions []union + limitation *limit + insertCols []string + insertVals []interface{} + updates []Eq + orderBy string + groupBy string + having string } -// Select creates a select Builder -func Select(cols ...string) *Builder { - builder := &Builder{cond: NewCond()} - return builder.Select(cols...) +// Dialect sets the db dialect of Builder. +func Dialect(dialect string) *Builder { + builder := &Builder{cond: NewCond(), dialect: dialect} + return builder } -// Insert creates an insert Builder -func Insert(eq Eq) *Builder { - builder := &Builder{cond: NewCond()} - return builder.Insert(eq) +// MySQL is shortcut of Dialect(MySQL) +func MySQL() *Builder { + return Dialect(MYSQL) } -// Update creates an update Builder -func Update(updates ...Eq) *Builder { - builder := &Builder{cond: NewCond()} - return builder.Update(updates...) +// MsSQL is shortcut of Dialect(MsSQL) +func MsSQL() *Builder { + return Dialect(MSSQL) } -// Delete creates a delete Builder -func Delete(conds ...Cond) *Builder { - builder := &Builder{cond: NewCond()} - return builder.Delete(conds...) +// Oracle is shortcut of Dialect(Oracle) +func Oracle() *Builder { + return Dialect(ORACLE) +} + +// Postgres is shortcut of Dialect(Postgres) +func Postgres() *Builder { + return Dialect(POSTGRES) +} + +// SQLite is shortcut of Dialect(SQLITE) +func SQLite() *Builder { + return Dialect(SQLITE) } // Where sets where SQL func (b *Builder) Where(cond Cond) *Builder { - b.cond = b.cond.And(cond) + if b.cond.IsValid() { + b.cond = b.cond.And(cond) + } else { + b.cond = cond + } return b } -// From sets the table name -func (b *Builder) From(tableName string) *Builder { - b.tableName = tableName +// From sets from subject(can be a table name in string or a builder pointer) and its alias +func (b *Builder) From(subject interface{}, alias ...string) *Builder { + switch subject.(type) { + case *Builder: + b.subQuery = subject.(*Builder) + + if len(alias) > 0 { + b.from = alias[0] + } else { + b.isNested = true + } + case string: + b.from = subject.(string) + + if len(alias) > 0 { + b.from = b.from + " " + alias[0] + } + } + return b } +// TableName returns the table name +func (b *Builder) TableName() string { + if b.optype == insertType { + return b.into + } + return b.from +} + // Into sets insert table name func (b *Builder) Into(tableName string) *Builder { - b.tableName = tableName + b.into = tableName return b } -// Join sets join table and contions +// Join sets join table and conditions func (b *Builder) Join(joinType, joinTable string, joinCond interface{}) *Builder { switch joinCond.(type) { case Cond: @@ -85,6 +155,50 @@ func (b *Builder) Join(joinType, joinTable string, joinCond interface{}) *Builde return b } +// Union sets union conditions +func (b *Builder) Union(unionTp string, unionCond *Builder) *Builder { + var builder *Builder + if b.optype != unionType { + builder = &Builder{cond: NewCond()} + builder.optype = unionType + builder.dialect = b.dialect + builder.selects = b.selects + + currentUnions := b.unions + // erase sub unions (actually append to new Builder.unions) + b.unions = nil + + for e := range currentUnions { + currentUnions[e].builder.dialect = b.dialect + } + + builder.unions = append(append(builder.unions, union{"", b}), currentUnions...) + } else { + builder = b + } + + if unionCond != nil { + if unionCond.dialect == "" && builder.dialect != "" { + unionCond.dialect = builder.dialect + } + + builder.unions = append(builder.unions, union{unionTp, unionCond}) + } + + return builder +} + +// Limit sets limitN condition +func (b *Builder) Limit(limitN int, offset ...int) *Builder { + b.limitation = &limit{limitN: limitN} + + if len(offset) > 0 { + b.limitation.offset = offset[0] + } + + return b +} + // InnerJoin sets inner join func (b *Builder) InnerJoin(joinTable string, joinCond interface{}) *Builder { return b.Join("INNER", joinTable, joinCond) @@ -113,7 +227,9 @@ func (b *Builder) FullJoin(joinTable string, joinCond interface{}) *Builder { // Select sets select SQL func (b *Builder) Select(cols ...string) *Builder { b.selects = cols - b.optype = selectType + if b.optype == condType { + b.optype = selectType + } return b } @@ -129,16 +245,70 @@ func (b *Builder) Or(cond Cond) *Builder { return b } +type insertColsSorter struct { + cols []string + vals []interface{} +} + +func (s insertColsSorter) Len() int { + return len(s.cols) +} +func (s insertColsSorter) Swap(i, j int) { + s.cols[i], s.cols[j] = s.cols[j], s.cols[i] + s.vals[i], s.vals[j] = s.vals[j], s.vals[i] +} + +func (s insertColsSorter) Less(i, j int) bool { + return s.cols[i] < s.cols[j] +} + // Insert sets insert SQL -func (b *Builder) Insert(eq Eq) *Builder { - b.inserts = eq +func (b *Builder) Insert(eq ...interface{}) *Builder { + if len(eq) > 0 { + var paramType = -1 + for _, e := range eq { + switch t := e.(type) { + case Eq: + if paramType == -1 { + paramType = 0 + } + if paramType != 0 { + break + } + for k, v := range t { + b.insertCols = append(b.insertCols, k) + b.insertVals = append(b.insertVals, v) + } + case string: + if paramType == -1 { + paramType = 1 + } + if paramType != 1 { + break + } + b.insertCols = append(b.insertCols, t) + } + } + } + + if len(b.insertCols) == len(b.insertVals) { + sort.Sort(insertColsSorter{ + cols: b.insertCols, + vals: b.insertVals, + }) + } b.optype = insertType return b } // Update sets update SQL func (b *Builder) Update(updates ...Eq) *Builder { - b.updates = updates + b.updates = make([]Eq, 0, len(updates)) + for _, update := range updates { + if update.IsValid() { + b.updates = append(b.updates, update) + } + } b.optype = updateType return b } @@ -153,8 +323,8 @@ func (b *Builder) Delete(conds ...Cond) *Builder { // WriteTo implements Writer interface func (b *Builder) WriteTo(w Writer) error { switch b.optype { - case condType: - return b.cond.WriteTo(w) + /*case condType: + return b.cond.WriteTo(w)*/ case selectType: return b.selectWriteTo(w) case insertType: @@ -163,6 +333,8 @@ func (b *Builder) WriteTo(w Writer) error { return b.updateWriteTo(w) case deleteType: return b.deleteWriteTo(w) + case unionType: + return b.unionWriteTo(w) } return ErrNotSupportType @@ -175,16 +347,48 @@ func (b *Builder) ToSQL() (string, []interface{}, error) { return "", nil, err } - return w.writer.String(), w.args, nil + // in case of sql.NamedArg in args + for e := range w.args { + if namedArg, ok := w.args[e].(sql2.NamedArg); ok { + w.args[e] = namedArg.Value + } + } + + var sql = w.writer.String() + var err error + + switch b.dialect { + case ORACLE, MSSQL: + // This is for compatibility with different sql drivers + for e := range w.args { + w.args[e] = sql2.Named(fmt.Sprintf("p%d", e+1), w.args[e]) + } + + var prefix string + if b.dialect == ORACLE { + prefix = ":p" + } else { + prefix = "@p" + } + + if sql, err = ConvertPlaceholder(sql, prefix); err != nil { + return "", nil, err + } + case POSTGRES: + if sql, err = ConvertPlaceholder(sql, "$"); err != nil { + return "", nil, err + } + } + + return sql, w.args, nil } -// ToSQL convert a builder or condtions to SQL and args -func ToSQL(cond interface{}) (string, []interface{}, error) { - switch cond.(type) { - case Cond: - return condToSQL(cond.(Cond)) - case *Builder: - return cond.(*Builder).ToSQL() +// ToBoundSQL +func (b *Builder) ToBoundSQL() (string, error) { + w := NewWriter() + if err := b.WriteTo(w); err != nil { + return "", err } - return "", nil, ErrNotSupportType + + return ConvertToBoundSQL(w.writer.String(), w.args) } diff --git a/vendor/github.com/go-xorm/builder/builder_delete.go b/vendor/github.com/go-xorm/builder/builder_delete.go index 743f1a4a91b..317cc3ff9e0 100644 --- a/vendor/github.com/go-xorm/builder/builder_delete.go +++ b/vendor/github.com/go-xorm/builder/builder_delete.go @@ -5,16 +5,21 @@ package builder import ( - "errors" "fmt" ) +// Delete creates a delete Builder +func Delete(conds ...Cond) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Delete(conds...) +} + func (b *Builder) deleteWriteTo(w Writer) error { - if len(b.tableName) <= 0 { - return errors.New("no table indicated") + if len(b.from) <= 0 { + return ErrNoTableName } - if _, err := fmt.Fprintf(w, "DELETE FROM %s WHERE ", b.tableName); err != nil { + if _, err := fmt.Fprintf(w, "DELETE FROM %s WHERE ", b.from); err != nil { return err } diff --git a/vendor/github.com/go-xorm/builder/builder_insert.go b/vendor/github.com/go-xorm/builder/builder_insert.go index 9b213ec7317..202cad51d84 100644 --- a/vendor/github.com/go-xorm/builder/builder_insert.go +++ b/vendor/github.com/go-xorm/builder/builder_insert.go @@ -6,39 +6,63 @@ package builder import ( "bytes" - "errors" "fmt" ) -func (b *Builder) insertWriteTo(w Writer) error { - if len(b.tableName) <= 0 { - return errors.New("no table indicated") - } - if len(b.inserts) <= 0 { - return errors.New("no column to be insert") +// Insert creates an insert Builder +func Insert(eq ...interface{}) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Insert(eq...) +} + +func (b *Builder) insertSelectWriteTo(w Writer) error { + if _, err := fmt.Fprintf(w, "INSERT INTO %s ", b.into); err != nil { + return err } - if _, err := fmt.Fprintf(w, "INSERT INTO %s (", b.tableName); err != nil { + if len(b.insertCols) > 0 { + fmt.Fprintf(w, "(") + for _, col := range b.insertCols { + fmt.Fprintf(w, col) + } + fmt.Fprintf(w, ") ") + } + + return b.selectWriteTo(w) +} + +func (b *Builder) insertWriteTo(w Writer) error { + if len(b.into) <= 0 { + return ErrNoTableName + } + if len(b.insertCols) <= 0 && b.from == "" { + return ErrNoColumnToInsert + } + + if b.into != "" && b.from != "" { + return b.insertSelectWriteTo(w) + } + + if _, err := fmt.Fprintf(w, "INSERT INTO %s (", b.into); err != nil { return err } var args = make([]interface{}, 0) var bs []byte var valBuffer = bytes.NewBuffer(bs) - var i = 0 - for _, col := range b.inserts.sortedKeys() { - value := b.inserts[col] + for i, col := range b.insertCols { + value := b.insertVals[i] fmt.Fprint(w, col) if e, ok := value.(expr); ok { - fmt.Fprint(valBuffer, e.sql) + fmt.Fprintf(valBuffer, "(%s)", e.sql) args = append(args, e.args...) } else { fmt.Fprint(valBuffer, "?") args = append(args, value) } - if i != len(b.inserts)-1 { + if i != len(b.insertCols)-1 { if _, err := fmt.Fprint(w, ","); err != nil { return err } @@ -46,7 +70,6 @@ func (b *Builder) insertWriteTo(w Writer) error { return err } } - i = i + 1 } if _, err := fmt.Fprint(w, ") Values ("); err != nil { diff --git a/vendor/github.com/go-xorm/builder/builder_limit.go b/vendor/github.com/go-xorm/builder/builder_limit.go new file mode 100644 index 00000000000..82435dacbd2 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/builder_limit.go @@ -0,0 +1,100 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "fmt" + "strings" +) + +func (b *Builder) limitWriteTo(w Writer) error { + if strings.TrimSpace(b.dialect) == "" { + return ErrDialectNotSetUp + } + + if b.limitation != nil { + limit := b.limitation + if limit.offset < 0 || limit.limitN <= 0 { + return ErrInvalidLimitation + } + // erase limit condition + b.limitation = nil + ow := w.(*BytesWriter) + + switch strings.ToLower(strings.TrimSpace(b.dialect)) { + case ORACLE: + if len(b.selects) == 0 { + b.selects = append(b.selects, "*") + } + + var final *Builder + selects := b.selects + b.selects = append(selects, "ROWNUM RN") + + var wb *Builder + if b.optype == unionType { + wb = Dialect(b.dialect).Select("at.*", "ROWNUM RN"). + From(b, "at") + } else { + wb = b + } + + if limit.offset == 0 { + final = Dialect(b.dialect).Select(selects...).From(wb, "at"). + Where(Lte{"at.RN": limit.limitN}) + } else { + sub := Dialect(b.dialect).Select("*"). + From(b, "at").Where(Lte{"at.RN": limit.offset + limit.limitN}) + + final = Dialect(b.dialect).Select(selects...).From(sub, "att"). + Where(Gt{"att.RN": limit.offset}) + } + + return final.WriteTo(ow) + case SQLITE, MYSQL, POSTGRES: + // if type UNION, we need to write previous content back to current writer + if b.optype == unionType { + if err := b.WriteTo(ow); err != nil { + return err + } + } + + if limit.offset == 0 { + fmt.Fprint(ow, " LIMIT ", limit.limitN) + } else { + fmt.Fprintf(ow, " LIMIT %v OFFSET %v", limit.limitN, limit.offset) + } + case MSSQL: + if len(b.selects) == 0 { + b.selects = append(b.selects, "*") + } + + var final *Builder + selects := b.selects + b.selects = append(append([]string{fmt.Sprintf("TOP %d %v", limit.limitN+limit.offset, b.selects[0])}, + b.selects[1:]...), "ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN") + + var wb *Builder + if b.optype == unionType { + wb = Dialect(b.dialect).Select("*", "ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN"). + From(b, "at") + } else { + wb = b + } + + if limit.offset == 0 { + final = Dialect(b.dialect).Select(selects...).From(wb, "at") + } else { + final = Dialect(b.dialect).Select(selects...).From(wb, "at").Where(Gt{"at.RN": limit.offset}) + } + + return final.WriteTo(ow) + default: + return ErrNotSupportType + } + } + + return nil +} diff --git a/vendor/github.com/go-xorm/builder/builder_select.go b/vendor/github.com/go-xorm/builder/builder_select.go index 3a3967cccc7..c33b38698be 100644 --- a/vendor/github.com/go-xorm/builder/builder_select.go +++ b/vendor/github.com/go-xorm/builder/builder_select.go @@ -5,13 +5,24 @@ package builder import ( - "errors" "fmt" ) +// Select creates a select Builder +func Select(cols ...string) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Select(cols...) +} + func (b *Builder) selectWriteTo(w Writer) error { - if len(b.tableName) <= 0 { - return errors.New("no table indicated") + if len(b.from) <= 0 && !b.isNested { + return ErrNoTableName + } + + // perform limit before writing to writer when b.dialect between ORACLE and MSSQL + // this avoid a duplicate writing problem in simple limit query + if b.limitation != nil && (b.dialect == ORACLE || b.dialect == MSSQL) { + return b.limitWriteTo(w) } if _, err := fmt.Fprint(w, "SELECT "); err != nil { @@ -34,24 +45,101 @@ func (b *Builder) selectWriteTo(w Writer) error { } } - if _, err := fmt.Fprintf(w, " FROM %s", b.tableName); err != nil { - return err + if b.subQuery == nil { + if _, err := fmt.Fprint(w, " FROM ", b.from); err != nil { + return err + } + } else { + if b.cond.IsValid() && len(b.from) <= 0 { + return ErrUnnamedDerivedTable + } + if b.subQuery.dialect != "" && b.dialect != b.subQuery.dialect { + return ErrInconsistentDialect + } + + // dialect of sub-query will inherit from the main one (if not set up) + if b.dialect != "" && b.subQuery.dialect == "" { + b.subQuery.dialect = b.dialect + } + + switch b.subQuery.optype { + case selectType, unionType: + fmt.Fprint(w, " FROM (") + if err := b.subQuery.WriteTo(w); err != nil { + return err + } + + if len(b.from) == 0 { + fmt.Fprintf(w, ")") + } else { + fmt.Fprintf(w, ") %v", b.from) + } + default: + return ErrUnexpectedSubQuery + } } for _, v := range b.joins { - fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable) + if _, err := fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable); err != nil { + return err + } + if err := v.joinCond.WriteTo(w); err != nil { return err } } - if !b.cond.IsValid() { - return nil + if b.cond.IsValid() { + if _, err := fmt.Fprint(w, " WHERE "); err != nil { + return err + } + + if err := b.cond.WriteTo(w); err != nil { + return err + } } - if _, err := fmt.Fprint(w, " WHERE "); err != nil { - return err + if len(b.groupBy) > 0 { + if _, err := fmt.Fprint(w, " GROUP BY ", b.groupBy); err != nil { + return err + } } - return b.cond.WriteTo(w) + if len(b.having) > 0 { + if _, err := fmt.Fprint(w, " HAVING ", b.having); err != nil { + return err + } + } + + if len(b.orderBy) > 0 { + if _, err := fmt.Fprint(w, " ORDER BY ", b.orderBy); err != nil { + return err + } + } + + if b.limitation != nil { + if err := b.limitWriteTo(w); err != nil { + return err + } + } + + return nil +} + +// OrderBy orderBy SQL +func (b *Builder) OrderBy(orderBy string) *Builder { + b.orderBy = orderBy + return b +} + +// GroupBy groupby SQL +func (b *Builder) GroupBy(groupby string) *Builder { + b.groupBy = groupby + return b +} + +// Having having SQL +func (b *Builder) Having(having string) *Builder { + b.having = having + return b } diff --git a/vendor/github.com/go-xorm/builder/builder_union.go b/vendor/github.com/go-xorm/builder/builder_union.go new file mode 100644 index 00000000000..4ba92161787 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/builder_union.go @@ -0,0 +1,47 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "fmt" + "strings" +) + +func (b *Builder) unionWriteTo(w Writer) error { + if b.limitation != nil || b.cond.IsValid() || + b.orderBy != "" || b.having != "" || b.groupBy != "" { + return ErrNotUnexpectedUnionConditions + } + + for idx, u := range b.unions { + current := u.builder + if current.optype != selectType { + return ErrUnsupportedUnionMembers + } + + if len(b.unions) == 1 { + if err := current.selectWriteTo(w); err != nil { + return err + } + } else { + if b.dialect != "" && b.dialect != current.dialect { + return ErrInconsistentDialect + } + + if idx != 0 { + fmt.Fprint(w, fmt.Sprintf(" UNION %v ", strings.ToUpper(u.unionType))) + } + fmt.Fprint(w, "(") + + if err := current.selectWriteTo(w); err != nil { + return err + } + + fmt.Fprint(w, ")") + } + } + + return nil +} diff --git a/vendor/github.com/go-xorm/builder/builder_update.go b/vendor/github.com/go-xorm/builder/builder_update.go index 182af830fdc..37b45515268 100644 --- a/vendor/github.com/go-xorm/builder/builder_update.go +++ b/vendor/github.com/go-xorm/builder/builder_update.go @@ -5,19 +5,24 @@ package builder import ( - "errors" "fmt" ) +// Update creates an update Builder +func Update(updates ...Eq) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Update(updates...) +} + func (b *Builder) updateWriteTo(w Writer) error { - if len(b.tableName) <= 0 { - return errors.New("no table indicated") + if len(b.from) <= 0 { + return ErrNoTableName } if len(b.updates) <= 0 { - return errors.New("no column to be update") + return ErrNoColumnToUpdate } - if _, err := fmt.Fprintf(w, "UPDATE %s SET ", b.tableName); err != nil { + if _, err := fmt.Fprintf(w, "UPDATE %s SET ", b.from); err != nil { return err } diff --git a/vendor/github.com/go-xorm/builder/cond.go b/vendor/github.com/go-xorm/builder/cond.go index 77dd139bfef..e44173bbd5a 100644 --- a/vendor/github.com/go-xorm/builder/cond.go +++ b/vendor/github.com/go-xorm/builder/cond.go @@ -5,7 +5,6 @@ package builder import ( - "bytes" "io" ) @@ -19,15 +18,15 @@ var _ Writer = NewWriter() // BytesWriter implments Writer and save SQL in bytes.Buffer type BytesWriter struct { - writer *bytes.Buffer - buffer []byte + writer *StringBuilder args []interface{} } // NewWriter creates a new string writer func NewWriter() *BytesWriter { - w := &BytesWriter{} - w.writer = bytes.NewBuffer(w.buffer) + w := &BytesWriter{ + writer: &StringBuilder{}, + } return w } @@ -73,15 +72,3 @@ func (condEmpty) Or(conds ...Cond) Cond { func (condEmpty) IsValid() bool { return false } - -func condToSQL(cond Cond) (string, []interface{}, error) { - if cond == nil || !cond.IsValid() { - return "", nil, nil - } - - w := NewWriter() - if err := cond.WriteTo(w); err != nil { - return "", nil, err - } - return w.writer.String(), w.args, nil -} diff --git a/vendor/github.com/go-xorm/builder/cond_between.go b/vendor/github.com/go-xorm/builder/cond_between.go index f2b29ed15bb..10e0b831521 100644 --- a/vendor/github.com/go-xorm/builder/cond_between.go +++ b/vendor/github.com/go-xorm/builder/cond_between.go @@ -17,10 +17,35 @@ var _ Cond = Between{} // WriteTo write data to Writer func (between Between) WriteTo(w Writer) error { - if _, err := fmt.Fprintf(w, "%s BETWEEN ? AND ?", between.Col); err != nil { + if _, err := fmt.Fprintf(w, "%s BETWEEN ", between.Col); err != nil { return err } - w.Append(between.LessVal, between.MoreVal) + if lv, ok := between.LessVal.(expr); ok { + if err := lv.WriteTo(w); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(w, "?"); err != nil { + return err + } + w.Append(between.LessVal) + } + + if _, err := fmt.Fprint(w, " AND "); err != nil { + return err + } + + if mv, ok := between.MoreVal.(expr); ok { + if err := mv.WriteTo(w); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(w, "?"); err != nil { + return err + } + w.Append(between.MoreVal) + } + return nil } diff --git a/vendor/github.com/go-xorm/builder/cond_or.go b/vendor/github.com/go-xorm/builder/cond_or.go index 35c3da0251e..52442653a83 100644 --- a/vendor/github.com/go-xorm/builder/cond_or.go +++ b/vendor/github.com/go-xorm/builder/cond_or.go @@ -27,10 +27,12 @@ func (o condOr) WriteTo(w Writer) error { for i, cond := range o { var needQuote bool switch cond.(type) { - case condAnd: + case condAnd, expr: needQuote = true case Eq: needQuote = (len(cond.(Eq)) > 1) + case Neq: + needQuote = (len(cond.(Neq)) > 1) } if needQuote { diff --git a/vendor/github.com/go-xorm/builder/error.go b/vendor/github.com/go-xorm/builder/error.go index d7ac51ea1fb..d830ee99557 100644 --- a/vendor/github.com/go-xorm/builder/error.go +++ b/vendor/github.com/go-xorm/builder/error.go @@ -8,9 +8,33 @@ import "errors" var ( // ErrNotSupportType not supported SQL type error - ErrNotSupportType = errors.New("not supported SQL type") + ErrNotSupportType = errors.New("Not supported SQL type") // ErrNoNotInConditions no NOT IN params error ErrNoNotInConditions = errors.New("No NOT IN conditions") // ErrNoInConditions no IN params error ErrNoInConditions = errors.New("No IN conditions") + // ErrNeedMoreArguments need more arguments + ErrNeedMoreArguments = errors.New("Need more sql arguments") + // ErrNoTableName no table name + ErrNoTableName = errors.New("No table indicated") + // ErrNoColumnToInsert no column to update + ErrNoColumnToUpdate = errors.New("No column(s) to update") + // ErrNoColumnToInsert no column to update + ErrNoColumnToInsert = errors.New("No column(s) to insert") + // ErrNotSupportDialectType not supported dialect type error + ErrNotSupportDialectType = errors.New("Not supported dialect type") + // ErrNotUnexpectedUnionConditions using union in a wrong way + ErrNotUnexpectedUnionConditions = errors.New("Unexpected conditional fields in UNION query") + // ErrUnsupportedUnionMembers unexpected members in UNION query + ErrUnsupportedUnionMembers = errors.New("Unexpected members in UNION query") + // ErrUnexpectedSubQuery Unexpected sub-query in SELECT query + ErrUnexpectedSubQuery = errors.New("Unexpected sub-query in SELECT query") + // ErrDialectNotSetUp dialect is not setup yet + ErrDialectNotSetUp = errors.New("Dialect is not setup yet, try to use `Dialect(dbType)` at first") + // ErrInvalidLimitation offset or limit is not correct + ErrInvalidLimitation = errors.New("Offset or limit is not correct") + // ErrUnnamedDerivedTable Every derived table must have its own alias + ErrUnnamedDerivedTable = errors.New("Every derived table must have its own alias") + // ErrInconsistentDialect Inconsistent dialect in same builder + ErrInconsistentDialect = errors.New("Inconsistent dialect in same builder") ) diff --git a/vendor/github.com/go-xorm/builder/sql.go b/vendor/github.com/go-xorm/builder/sql.go new file mode 100644 index 00000000000..08342427686 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/sql.go @@ -0,0 +1,156 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + sql2 "database/sql" + "fmt" + "reflect" + "time" +) + +func condToSQL(cond Cond) (string, []interface{}, error) { + if cond == nil || !cond.IsValid() { + return "", nil, nil + } + + w := NewWriter() + if err := cond.WriteTo(w); err != nil { + return "", nil, err + } + return w.writer.String(), w.args, nil +} + +func condToBoundSQL(cond Cond) (string, error) { + if cond == nil || !cond.IsValid() { + return "", nil + } + + w := NewWriter() + if err := cond.WriteTo(w); err != nil { + return "", err + } + return ConvertToBoundSQL(w.writer.String(), w.args) +} + +// ToSQL convert a builder or conditions to SQL and args +func ToSQL(cond interface{}) (string, []interface{}, error) { + switch cond.(type) { + case Cond: + return condToSQL(cond.(Cond)) + case *Builder: + return cond.(*Builder).ToSQL() + } + return "", nil, ErrNotSupportType +} + +// ToBoundSQL convert a builder or conditions to parameters bound SQL +func ToBoundSQL(cond interface{}) (string, error) { + switch cond.(type) { + case Cond: + return condToBoundSQL(cond.(Cond)) + case *Builder: + return cond.(*Builder).ToBoundSQL() + } + return "", ErrNotSupportType +} + +func noSQLQuoteNeeded(a interface{}) bool { + switch a.(type) { + case int, int8, int16, int32, int64: + return true + case uint, uint8, uint16, uint32, uint64: + return true + case float32, float64: + return true + case bool: + return true + case string: + return false + case time.Time, *time.Time: + return false + } + + t := reflect.TypeOf(a) + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.Bool: + return true + case reflect.String: + return false + } + + return false +} + +// ConvertToBoundSQL will convert SQL and args to a bound SQL +func ConvertToBoundSQL(sql string, args []interface{}) (string, error) { + buf := StringBuilder{} + var i, j, start int + for ; i < len(sql); i++ { + if sql[i] == '?' { + _, err := buf.WriteString(sql[start:i]) + if err != nil { + return "", err + } + start = i + 1 + + if len(args) == j { + return "", ErrNeedMoreArguments + } + + arg := args[j] + if namedArg, ok := arg.(sql2.NamedArg); ok { + arg = namedArg.Value + } + + if noSQLQuoteNeeded(arg) { + _, err = fmt.Fprint(&buf, arg) + } else { + _, err = fmt.Fprintf(&buf, "'%v'", arg) + } + if err != nil { + return "", err + } + j = j + 1 + } + } + _, err := buf.WriteString(sql[start:]) + if err != nil { + return "", err + } + return buf.String(), nil +} + +// ConvertPlaceholder replaces ? to $1, $2 ... or :1, :2 ... according prefix +func ConvertPlaceholder(sql, prefix string) (string, error) { + buf := StringBuilder{} + var i, j, start int + for ; i < len(sql); i++ { + if sql[i] == '?' { + if _, err := buf.WriteString(sql[start:i]); err != nil { + return "", err + } + + start = i + 1 + j = j + 1 + + if _, err := buf.WriteString(fmt.Sprintf("%v%d", prefix, j)); err != nil { + return "", err + } + } + } + + if _, err := buf.WriteString(sql[start:]); err != nil { + return "", err + } + + return buf.String(), nil +} diff --git a/vendor/github.com/go-xorm/builder/string_builder.go b/vendor/github.com/go-xorm/builder/string_builder.go new file mode 100644 index 00000000000..d4de8717e77 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/string_builder.go @@ -0,0 +1,119 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "unicode/utf8" + "unsafe" +) + +// A StringBuilder is used to efficiently build a string using Write methods. +// It minimizes memory copying. The zero value is ready to use. +// Do not copy a non-zero Builder. +type StringBuilder struct { + addr *StringBuilder // of receiver, to detect copies by value + buf []byte +} + +// noescape hides a pointer from escape analysis. noescape is +// the identity function but escape analysis doesn't think the +// output depends on the input. noescape is inlined and currently +// compiles down to zero instructions. +// USE CAREFULLY! +// This was copied from the runtime; see issues 23382 and 7921. +//go:nosplit +func noescape(p unsafe.Pointer) unsafe.Pointer { + x := uintptr(p) + return unsafe.Pointer(x ^ 0) +} + +func (b *StringBuilder) copyCheck() { + if b.addr == nil { + // This hack works around a failing of Go's escape analysis + // that was causing b to escape and be heap allocated. + // See issue 23382. + // TODO: once issue 7921 is fixed, this should be reverted to + // just "b.addr = b". + b.addr = (*StringBuilder)(noescape(unsafe.Pointer(b))) + } else if b.addr != b { + panic("strings: illegal use of non-zero Builder copied by value") + } +} + +// String returns the accumulated string. +func (b *StringBuilder) String() string { + return *(*string)(unsafe.Pointer(&b.buf)) +} + +// Len returns the number of accumulated bytes; b.Len() == len(b.String()). +func (b *StringBuilder) Len() int { return len(b.buf) } + +// Reset resets the Builder to be empty. +func (b *StringBuilder) Reset() { + b.addr = nil + b.buf = nil +} + +// grow copies the buffer to a new, larger buffer so that there are at least n +// bytes of capacity beyond len(b.buf). +func (b *StringBuilder) grow(n int) { + buf := make([]byte, len(b.buf), 2*cap(b.buf)+n) + copy(buf, b.buf) + b.buf = buf +} + +// Grow grows b's capacity, if necessary, to guarantee space for +// another n bytes. After Grow(n), at least n bytes can be written to b +// without another allocation. If n is negative, Grow panics. +func (b *StringBuilder) Grow(n int) { + b.copyCheck() + if n < 0 { + panic("strings.Builder.Grow: negative count") + } + if cap(b.buf)-len(b.buf) < n { + b.grow(n) + } +} + +// Write appends the contents of p to b's buffer. +// Write always returns len(p), nil. +func (b *StringBuilder) Write(p []byte) (int, error) { + b.copyCheck() + b.buf = append(b.buf, p...) + return len(p), nil +} + +// WriteByte appends the byte c to b's buffer. +// The returned error is always nil. +func (b *StringBuilder) WriteByte(c byte) error { + b.copyCheck() + b.buf = append(b.buf, c) + return nil +} + +// WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer. +// It returns the length of r and a nil error. +func (b *StringBuilder) WriteRune(r rune) (int, error) { + b.copyCheck() + if r < utf8.RuneSelf { + b.buf = append(b.buf, byte(r)) + return 1, nil + } + l := len(b.buf) + if cap(b.buf)-l < utf8.UTFMax { + b.grow(utf8.UTFMax) + } + n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax], r) + b.buf = b.buf[:l+n] + return n, nil +} + +// WriteString appends the contents of s to b's buffer. +// It returns the length of s and a nil error. +func (b *StringBuilder) WriteString(s string) (int, error) { + b.copyCheck() + b.buf = append(b.buf, s...) + return len(s), nil +} diff --git a/vendor/github.com/go-xorm/core/cache.go b/vendor/github.com/go-xorm/core/cache.go index bf81bd52ba4..dc4992dfb11 100644 --- a/vendor/github.com/go-xorm/core/cache.go +++ b/vendor/github.com/go-xorm/core/cache.go @@ -1,11 +1,16 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( - "errors" - "fmt" - "time" "bytes" "encoding/gob" + "errors" + "fmt" + "strings" + "time" ) const ( @@ -55,11 +60,10 @@ func encodeIds(ids []PK) (string, error) { return buf.String(), err } - func decodeIds(s string) ([]PK, error) { pks := make([]PK, 0) - dec := gob.NewDecoder(bytes.NewBufferString(s)) + dec := gob.NewDecoder(strings.NewReader(s)) err := dec.Decode(&pks) return pks, err diff --git a/vendor/github.com/go-xorm/core/column.go b/vendor/github.com/go-xorm/core/column.go index d9362e98578..40d8f9268d7 100644 --- a/vendor/github.com/go-xorm/core/column.go +++ b/vendor/github.com/go-xorm/core/column.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -41,6 +45,7 @@ type Column struct { Comment string } +// NewColumn creates a new column func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column { return &Column{ Name: name, @@ -66,7 +71,7 @@ func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable } } -// generate column description string according dialect +// String generate column description string according dialect func (col *Column) String(d Dialect) string { sql := d.QuoteStr() + col.Name + d.QuoteStr() + " " @@ -79,6 +84,10 @@ func (col *Column) String(d Dialect) string { } } + if col.Default != "" { + sql += "DEFAULT " + col.Default + " " + } + if d.ShowCreateNull() { if col.Nullable { sql += "NULL " @@ -87,18 +96,19 @@ func (col *Column) String(d Dialect) string { } } - if col.Default != "" { - sql += "DEFAULT " + col.Default + " " - } - return sql } +// StringNoPk generate column description string according dialect without primary keys func (col *Column) StringNoPk(d Dialect) string { sql := d.QuoteStr() + col.Name + d.QuoteStr() + " " sql += d.SqlType(col) + " " + if col.Default != "" { + sql += "DEFAULT " + col.Default + " " + } + if d.ShowCreateNull() { if col.Nullable { sql += "NULL " @@ -107,19 +117,16 @@ func (col *Column) StringNoPk(d Dialect) string { } } - if col.Default != "" { - sql += "DEFAULT " + col.Default + " " - } - return sql } -// return col's filed of struct's value +// ValueOf returns column's filed of struct's value func (col *Column) ValueOf(bean interface{}) (*reflect.Value, error) { dataStruct := reflect.Indirect(reflect.ValueOf(bean)) return col.ValueOfV(&dataStruct) } +// ValueOfV returns column's filed of struct's value accept reflevt value func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { var fieldValue reflect.Value fieldPath := strings.Split(col.FieldName, ".") @@ -147,12 +154,12 @@ func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { } fieldValue = fieldValue.Elem().FieldByName(fieldPath[i+1]) } else { - return nil, fmt.Errorf("field %v is not valid", col.FieldName) + return nil, fmt.Errorf("field %v is not valid", col.FieldName) } } if !fieldValue.IsValid() { - return nil, fmt.Errorf("field %v is not valid", col.FieldName) + return nil, fmt.Errorf("field %v is not valid", col.FieldName) } return &fieldValue, nil diff --git a/vendor/github.com/go-xorm/core/converstion.go b/vendor/github.com/go-xorm/core/converstion.go index 18522fbeebd..9703c36e085 100644 --- a/vendor/github.com/go-xorm/core/converstion.go +++ b/vendor/github.com/go-xorm/core/converstion.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core // Conversion is an interface. A type implements Conversion will according diff --git a/vendor/github.com/go-xorm/core/db.go b/vendor/github.com/go-xorm/core/db.go index 6111c4b332f..3e50a14795d 100644 --- a/vendor/github.com/go-xorm/core/db.go +++ b/vendor/github.com/go-xorm/core/db.go @@ -1,12 +1,21 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( + "context" "database/sql" "database/sql/driver" - "errors" "fmt" "reflect" "regexp" + "sync" +) + +var ( + DefaultCacheSize = 200 ) func MapToSlice(query string, mp interface{}) (string, []interface{}, error) { @@ -58,189 +67,129 @@ func StructToSlice(query string, st interface{}) (string, []interface{}, error) return query, args, nil } -type DB struct { - *sql.DB - Mapper IMapper +type cacheStruct struct { + value reflect.Value + idx int } +// DB is a wrap of sql.DB with extra contents +type DB struct { + *sql.DB + Mapper IMapper + reflectCache map[reflect.Type]*cacheStruct + reflectCacheMutex sync.RWMutex +} + +// Open opens a database func Open(driverName, dataSourceName string) (*DB, error) { db, err := sql.Open(driverName, dataSourceName) if err != nil { return nil, err } - return &DB{db, NewCacheMapper(&SnakeMapper{})}, nil + return &DB{ + DB: db, + Mapper: NewCacheMapper(&SnakeMapper{}), + reflectCache: make(map[reflect.Type]*cacheStruct), + }, nil } +// FromDB creates a DB from a sql.DB func FromDB(db *sql.DB) *DB { - return &DB{db, NewCacheMapper(&SnakeMapper{})} + return &DB{ + DB: db, + Mapper: NewCacheMapper(&SnakeMapper{}), + reflectCache: make(map[reflect.Type]*cacheStruct), + } } -func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { - rows, err := db.DB.Query(query, args...) +func (db *DB) reflectNew(typ reflect.Type) reflect.Value { + db.reflectCacheMutex.Lock() + defer db.reflectCacheMutex.Unlock() + cs, ok := db.reflectCache[typ] + if !ok || cs.idx+1 > DefaultCacheSize-1 { + cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), DefaultCacheSize, DefaultCacheSize), 0} + db.reflectCache[typ] = cs + } else { + cs.idx = cs.idx + 1 + } + return cs.value.Index(cs.idx).Addr() +} + +// QueryContext overwrites sql.DB.QueryContext +func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { + rows, err := db.DB.QueryContext(ctx, query, args...) if err != nil { if rows != nil { rows.Close() } return nil, err } - return &Rows{rows, db.Mapper}, nil + return &Rows{rows, db}, nil } -func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) { +// Query overwrites sql.DB.Query +func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { + return db.QueryContext(context.Background(), query, args...) +} + +func (db *DB) QueryMapContext(ctx context.Context, query string, mp interface{}) (*Rows, error) { query, args, err := MapToSlice(query, mp) if err != nil { return nil, err } - return db.Query(query, args...) + return db.QueryContext(ctx, query, args...) } -func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) { +func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) { + return db.QueryMapContext(context.Background(), query, mp) +} + +func (db *DB) QueryStructContext(ctx context.Context, query string, st interface{}) (*Rows, error) { query, args, err := StructToSlice(query, st) if err != nil { return nil, err } - return db.Query(query, args...) + return db.QueryContext(ctx, query, args...) } -func (db *DB) QueryRow(query string, args ...interface{}) *Row { - rows, err := db.Query(query, args...) +func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) { + return db.QueryStructContext(context.Background(), query, st) +} + +func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row { + rows, err := db.QueryContext(ctx, query, args...) if err != nil { return &Row{nil, err} } return &Row{rows, nil} } -func (db *DB) QueryRowMap(query string, mp interface{}) *Row { +func (db *DB) QueryRow(query string, args ...interface{}) *Row { + return db.QueryRowContext(context.Background(), query, args...) +} + +func (db *DB) QueryRowMapContext(ctx context.Context, query string, mp interface{}) *Row { query, args, err := MapToSlice(query, mp) if err != nil { return &Row{nil, err} } - return db.QueryRow(query, args...) + return db.QueryRowContext(ctx, query, args...) } -func (db *DB) QueryRowStruct(query string, st interface{}) *Row { +func (db *DB) QueryRowMap(query string, mp interface{}) *Row { + return db.QueryRowMapContext(context.Background(), query, mp) +} + +func (db *DB) QueryRowStructContext(ctx context.Context, query string, st interface{}) *Row { query, args, err := StructToSlice(query, st) if err != nil { return &Row{nil, err} } - return db.QueryRow(query, args...) + return db.QueryRowContext(ctx, query, args...) } -type Stmt struct { - *sql.Stmt - Mapper IMapper - names map[string]int -} - -func (db *DB) Prepare(query string) (*Stmt, error) { - names := make(map[string]int) - var i int - query = re.ReplaceAllStringFunc(query, func(src string) string { - names[src[1:]] = i - i += 1 - return "?" - }) - - stmt, err := db.DB.Prepare(query) - if err != nil { - return nil, err - } - return &Stmt{stmt, db.Mapper, names}, nil -} - -func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) { - vv := reflect.ValueOf(mp) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return nil, errors.New("mp should be a map's pointer") - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() - } - return s.Stmt.Exec(args...) -} - -func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) { - vv := reflect.ValueOf(st) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return nil, errors.New("mp should be a map's pointer") - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().FieldByName(k).Interface() - } - return s.Stmt.Exec(args...) -} - -func (s *Stmt) Query(args ...interface{}) (*Rows, error) { - rows, err := s.Stmt.Query(args...) - if err != nil { - return nil, err - } - return &Rows{rows, s.Mapper}, nil -} - -func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) { - vv := reflect.ValueOf(mp) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return nil, errors.New("mp should be a map's pointer") - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() - } - - return s.Query(args...) -} - -func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) { - vv := reflect.ValueOf(st) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return nil, errors.New("mp should be a map's pointer") - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().FieldByName(k).Interface() - } - - return s.Query(args...) -} - -func (s *Stmt) QueryRow(args ...interface{}) *Row { - rows, err := s.Query(args...) - return &Row{rows, err} -} - -func (s *Stmt) QueryRowMap(mp interface{}) *Row { - vv := reflect.ValueOf(mp) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return &Row{nil, errors.New("mp should be a map's pointer")} - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() - } - - return s.QueryRow(args...) -} - -func (s *Stmt) QueryRowStruct(st interface{}) *Row { - vv := reflect.ValueOf(st) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return &Row{nil, errors.New("st should be a struct's pointer")} - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().FieldByName(k).Interface() - } - - return s.QueryRow(args...) +func (db *DB) QueryRowStruct(query string, st interface{}) *Row { + return db.QueryRowStructContext(context.Background(), query, st) } var ( @@ -249,120 +198,26 @@ var ( // insert into (name) values (?) // insert into (name) values (?name) -func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) { +func (db *DB) ExecMapContext(ctx context.Context, query string, mp interface{}) (sql.Result, error) { query, args, err := MapToSlice(query, mp) if err != nil { return nil, err } - return db.DB.Exec(query, args...) + return db.DB.ExecContext(ctx, query, args...) +} + +func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) { + return db.ExecMapContext(context.Background(), query, mp) +} + +func (db *DB) ExecStructContext(ctx context.Context, query string, st interface{}) (sql.Result, error) { + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err + } + return db.DB.ExecContext(ctx, query, args...) } func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) { - query, args, err := StructToSlice(query, st) - if err != nil { - return nil, err - } - return db.DB.Exec(query, args...) -} - -type EmptyScanner struct { -} - -func (EmptyScanner) Scan(src interface{}) error { - return nil -} - -type Tx struct { - *sql.Tx - Mapper IMapper -} - -func (db *DB) Begin() (*Tx, error) { - tx, err := db.DB.Begin() - if err != nil { - return nil, err - } - return &Tx{tx, db.Mapper}, nil -} - -func (tx *Tx) Prepare(query string) (*Stmt, error) { - names := make(map[string]int) - var i int - query = re.ReplaceAllStringFunc(query, func(src string) string { - names[src[1:]] = i - i += 1 - return "?" - }) - - stmt, err := tx.Tx.Prepare(query) - if err != nil { - return nil, err - } - return &Stmt{stmt, tx.Mapper, names}, nil -} - -func (tx *Tx) Stmt(stmt *Stmt) *Stmt { - // TODO: - return stmt -} - -func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) { - query, args, err := MapToSlice(query, mp) - if err != nil { - return nil, err - } - return tx.Tx.Exec(query, args...) -} - -func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) { - query, args, err := StructToSlice(query, st) - if err != nil { - return nil, err - } - return tx.Tx.Exec(query, args...) -} - -func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { - rows, err := tx.Tx.Query(query, args...) - if err != nil { - return nil, err - } - return &Rows{rows, tx.Mapper}, nil -} - -func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) { - query, args, err := MapToSlice(query, mp) - if err != nil { - return nil, err - } - return tx.Query(query, args...) -} - -func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) { - query, args, err := StructToSlice(query, st) - if err != nil { - return nil, err - } - return tx.Query(query, args...) -} - -func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { - rows, err := tx.Query(query, args...) - return &Row{rows, err} -} - -func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row { - query, args, err := MapToSlice(query, mp) - if err != nil { - return &Row{nil, err} - } - return tx.QueryRow(query, args...) -} - -func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row { - query, args, err := StructToSlice(query, st) - if err != nil { - return &Row{nil, err} - } - return tx.QueryRow(query, args...) + return db.ExecStructContext(context.Background(), query, st) } diff --git a/vendor/github.com/go-xorm/core/dialect.go b/vendor/github.com/go-xorm/core/dialect.go index 6f2e81d017b..5d35a4f11d9 100644 --- a/vendor/github.com/go-xorm/core/dialect.go +++ b/vendor/github.com/go-xorm/core/dialect.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -74,6 +78,7 @@ type Dialect interface { GetIndexes(tableName string) (map[string]*Index, error) Filters() []Filter + SetParams(params map[string]string) } func OpenDialect(dialect Dialect) (*DB, error) { @@ -148,7 +153,8 @@ func (db *Base) SupportDropIfExists() bool { } func (db *Base) DropTableSql(tableName string) string { - return fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName) + quote := db.dialect.Quote + return fmt.Sprintf("DROP TABLE IF EXISTS %s", quote(tableName)) } func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) { @@ -289,6 +295,9 @@ func (b *Base) LogSQL(sql string, args []interface{}) { } } +func (b *Base) SetParams(params map[string]string) { +} + var ( dialects = map[string]func() Dialect{} ) diff --git a/vendor/github.com/go-xorm/core/driver.go b/vendor/github.com/go-xorm/core/driver.go index 0f1020b403b..ceef4ba6182 100644 --- a/vendor/github.com/go-xorm/core/driver.go +++ b/vendor/github.com/go-xorm/core/driver.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core type Driver interface { diff --git a/vendor/github.com/go-xorm/core/error.go b/vendor/github.com/go-xorm/core/error.go index 640e6036e66..63ea53e466c 100644 --- a/vendor/github.com/go-xorm/core/error.go +++ b/vendor/github.com/go-xorm/core/error.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import "errors" diff --git a/vendor/github.com/go-xorm/core/filter.go b/vendor/github.com/go-xorm/core/filter.go index 60caaf29026..6aeed4244c1 100644 --- a/vendor/github.com/go-xorm/core/filter.go +++ b/vendor/github.com/go-xorm/core/filter.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -37,9 +41,9 @@ func (q *Quoter) Quote(content string) string { func (i *IdFilter) Do(sql string, dialect Dialect, table *Table) string { quoter := NewQuoter(dialect) if table != nil && len(table.PrimaryKeys) == 1 { - sql = strings.Replace(sql, "`(id)`", quoter.Quote(table.PrimaryKeys[0]), -1) - sql = strings.Replace(sql, quoter.Quote("(id)"), quoter.Quote(table.PrimaryKeys[0]), -1) - return strings.Replace(sql, "(id)", quoter.Quote(table.PrimaryKeys[0]), -1) + sql = strings.Replace(sql, " `(id)` ", " "+quoter.Quote(table.PrimaryKeys[0])+" ", -1) + sql = strings.Replace(sql, " "+quoter.Quote("(id)")+" ", " "+quoter.Quote(table.PrimaryKeys[0])+" ", -1) + return strings.Replace(sql, " (id) ", " "+quoter.Quote(table.PrimaryKeys[0])+" ", -1) } return sql } diff --git a/vendor/github.com/go-xorm/core/ilogger.go b/vendor/github.com/go-xorm/core/ilogger.go index c8d78496054..348ab88f4f0 100644 --- a/vendor/github.com/go-xorm/core/ilogger.go +++ b/vendor/github.com/go-xorm/core/ilogger.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core type LogLevel int diff --git a/vendor/github.com/go-xorm/core/index.go b/vendor/github.com/go-xorm/core/index.go index 73b95175adc..ac97b685053 100644 --- a/vendor/github.com/go-xorm/core/index.go +++ b/vendor/github.com/go-xorm/core/index.go @@ -1,8 +1,11 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( "fmt" - "sort" "strings" ) @@ -22,6 +25,8 @@ type Index struct { func (index *Index) XName(tableName string) string { if !strings.HasPrefix(index.Name, "UQE_") && !strings.HasPrefix(index.Name, "IDX_") { + tableName = strings.Replace(tableName, `"`, "", -1) + tableName = strings.Replace(tableName, `.`, "_", -1) if index.Type == UniqueType { return fmt.Sprintf("UQE_%v_%v", tableName, index.Name) } @@ -44,11 +49,16 @@ func (index *Index) Equal(dst *Index) bool { if len(index.Cols) != len(dst.Cols) { return false } - sort.StringSlice(index.Cols).Sort() - sort.StringSlice(dst.Cols).Sort() for i := 0; i < len(index.Cols); i++ { - if index.Cols[i] != dst.Cols[i] { + var found bool + for j := 0; j < len(dst.Cols); j++ { + if index.Cols[i] == dst.Cols[j] { + found = true + break + } + } + if !found { return false } } diff --git a/vendor/github.com/go-xorm/core/mapper.go b/vendor/github.com/go-xorm/core/mapper.go index bb72a156624..ec44ea0db9b 100644 --- a/vendor/github.com/go-xorm/core/mapper.go +++ b/vendor/github.com/go-xorm/core/mapper.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( diff --git a/vendor/github.com/go-xorm/core/pk.go b/vendor/github.com/go-xorm/core/pk.go index 1810dd944be..05a7672d86b 100644 --- a/vendor/github.com/go-xorm/core/pk.go +++ b/vendor/github.com/go-xorm/core/pk.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( diff --git a/vendor/github.com/go-xorm/core/rows.go b/vendor/github.com/go-xorm/core/rows.go index 4a4acaa4c26..2b046d84cc7 100644 --- a/vendor/github.com/go-xorm/core/rows.go +++ b/vendor/github.com/go-xorm/core/rows.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -9,7 +13,7 @@ import ( type Rows struct { *sql.Rows - Mapper IMapper + db *DB } func (rs *Rows) ToMapString() ([]map[string]string, error) { @@ -105,7 +109,7 @@ func (rs *Rows) ScanStructByName(dest interface{}) error { newDest := make([]interface{}, len(cols)) var v EmptyScanner for j, name := range cols { - f := fieldByName(vv.Elem(), rs.Mapper.Table2Obj(name)) + f := fieldByName(vv.Elem(), rs.db.Mapper.Table2Obj(name)) if f.IsValid() { newDest[j] = f.Addr().Interface() } else { @@ -116,36 +120,6 @@ func (rs *Rows) ScanStructByName(dest interface{}) error { return rs.Rows.Scan(newDest...) } -type cacheStruct struct { - value reflect.Value - idx int -} - -var ( - reflectCache = make(map[reflect.Type]*cacheStruct) - reflectCacheMutex sync.RWMutex -) - -func ReflectNew(typ reflect.Type) reflect.Value { - reflectCacheMutex.RLock() - cs, ok := reflectCache[typ] - reflectCacheMutex.RUnlock() - - const newSize = 200 - - if !ok || cs.idx+1 > newSize-1 { - cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), newSize, newSize), 0} - reflectCacheMutex.Lock() - reflectCache[typ] = cs - reflectCacheMutex.Unlock() - } else { - reflectCacheMutex.Lock() - cs.idx = cs.idx + 1 - reflectCacheMutex.Unlock() - } - return cs.value.Index(cs.idx).Addr() -} - // scan data to a slice's pointer, slice's length should equal to columns' number func (rs *Rows) ScanSlice(dest interface{}) error { vv := reflect.ValueOf(dest) @@ -197,9 +171,7 @@ func (rs *Rows) ScanMap(dest interface{}) error { vvv := vv.Elem() for i, _ := range cols { - newDest[i] = ReflectNew(vvv.Type().Elem()).Interface() - //v := reflect.New(vvv.Type().Elem()) - //newDest[i] = v.Interface() + newDest[i] = rs.db.reflectNew(vvv.Type().Elem()).Interface() } err = rs.Rows.Scan(newDest...) @@ -215,32 +187,6 @@ func (rs *Rows) ScanMap(dest interface{}) error { return nil } -/*func (rs *Rows) ScanMap(dest interface{}) error { - vv := reflect.ValueOf(dest) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return errors.New("dest should be a map's pointer") - } - - cols, err := rs.Columns() - if err != nil { - return err - } - - newDest := make([]interface{}, len(cols)) - err = rs.ScanSlice(newDest) - if err != nil { - return err - } - - vvv := vv.Elem() - - for i, name := range cols { - vname := reflect.ValueOf(name) - vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem()) - } - - return nil -}*/ type Row struct { rows *Rows // One of these two will be non-nil: diff --git a/vendor/github.com/go-xorm/core/scan.go b/vendor/github.com/go-xorm/core/scan.go index 7da338d8645..897b534159e 100644 --- a/vendor/github.com/go-xorm/core/scan.go +++ b/vendor/github.com/go-xorm/core/scan.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -44,9 +48,19 @@ func convertTime(dest *NullTime, src interface{}) error { } *dest = NullTime(t) return nil + case time.Time: + *dest = NullTime(s) + return nil case nil: default: return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest) } return nil } + +type EmptyScanner struct { +} + +func (EmptyScanner) Scan(src interface{}) error { + return nil +} diff --git a/vendor/github.com/go-xorm/core/stmt.go b/vendor/github.com/go-xorm/core/stmt.go new file mode 100644 index 00000000000..20ee202b9b7 --- /dev/null +++ b/vendor/github.com/go-xorm/core/stmt.go @@ -0,0 +1,165 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package core + +import ( + "context" + "database/sql" + "errors" + "reflect" +) + +type Stmt struct { + *sql.Stmt + db *DB + names map[string]int +} + +func (db *DB) PrepareContext(ctx context.Context, query string) (*Stmt, error) { + names := make(map[string]int) + var i int + query = re.ReplaceAllStringFunc(query, func(src string) string { + names[src[1:]] = i + i += 1 + return "?" + }) + + stmt, err := db.DB.PrepareContext(ctx, query) + if err != nil { + return nil, err + } + return &Stmt{stmt, db, names}, nil +} + +func (db *DB) Prepare(query string) (*Stmt, error) { + return db.PrepareContext(context.Background(), query) +} + +func (s *Stmt) ExecMapContext(ctx context.Context, mp interface{}) (sql.Result, error) { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() + } + return s.Stmt.ExecContext(ctx, args...) +} + +func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) { + return s.ExecMapContext(context.Background(), mp) +} + +func (s *Stmt) ExecStructContext(ctx context.Context, st interface{}) (sql.Result, error) { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().FieldByName(k).Interface() + } + return s.Stmt.ExecContext(ctx, args...) +} + +func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) { + return s.ExecStructContext(context.Background(), st) +} + +func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*Rows, error) { + rows, err := s.Stmt.QueryContext(ctx, args...) + if err != nil { + return nil, err + } + return &Rows{rows, s.db}, nil +} + +func (s *Stmt) Query(args ...interface{}) (*Rows, error) { + return s.QueryContext(context.Background(), args...) +} + +func (s *Stmt) QueryMapContext(ctx context.Context, mp interface{}) (*Rows, error) { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() + } + + return s.QueryContext(ctx, args...) +} + +func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) { + return s.QueryMapContext(context.Background(), mp) +} + +func (s *Stmt) QueryStructContext(ctx context.Context, st interface{}) (*Rows, error) { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().FieldByName(k).Interface() + } + + return s.Query(args...) +} + +func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) { + return s.QueryStructContext(context.Background(), st) +} + +func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row { + rows, err := s.QueryContext(ctx, args...) + return &Row{rows, err} +} + +func (s *Stmt) QueryRow(args ...interface{}) *Row { + return s.QueryRowContext(context.Background(), args...) +} + +func (s *Stmt) QueryRowMapContext(ctx context.Context, mp interface{}) *Row { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return &Row{nil, errors.New("mp should be a map's pointer")} + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() + } + + return s.QueryRowContext(ctx, args...) +} + +func (s *Stmt) QueryRowMap(mp interface{}) *Row { + return s.QueryRowMapContext(context.Background(), mp) +} + +func (s *Stmt) QueryRowStructContext(ctx context.Context, st interface{}) *Row { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return &Row{nil, errors.New("st should be a struct's pointer")} + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().FieldByName(k).Interface() + } + + return s.QueryRowContext(ctx, args...) +} + +func (s *Stmt) QueryRowStruct(st interface{}) *Row { + return s.QueryRowStructContext(context.Background(), st) +} diff --git a/vendor/github.com/go-xorm/core/table.go b/vendor/github.com/go-xorm/core/table.go index 88199bedd61..d129e60f8b9 100644 --- a/vendor/github.com/go-xorm/core/table.go +++ b/vendor/github.com/go-xorm/core/table.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -49,7 +53,6 @@ func NewTable(name string, t reflect.Type) *Table { } func (table *Table) columnsByName(name string) []*Column { - n := len(name) for k := range table.columnsMap { @@ -75,7 +78,6 @@ func (table *Table) GetColumn(name string) *Column { } func (table *Table) GetColumnIdx(name string, idx int) *Column { - cols := table.columnsByName(name) if cols != nil && idx < len(cols) { diff --git a/vendor/github.com/go-xorm/core/tx.go b/vendor/github.com/go-xorm/core/tx.go new file mode 100644 index 00000000000..a56b70063eb --- /dev/null +++ b/vendor/github.com/go-xorm/core/tx.go @@ -0,0 +1,153 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package core + +import ( + "context" + "database/sql" +) + +type Tx struct { + *sql.Tx + db *DB +} + +func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) { + tx, err := db.DB.BeginTx(ctx, opts) + if err != nil { + return nil, err + } + return &Tx{tx, db}, nil +} + +func (db *DB) Begin() (*Tx, error) { + tx, err := db.DB.Begin() + if err != nil { + return nil, err + } + return &Tx{tx, db}, nil +} + +func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) { + names := make(map[string]int) + var i int + query = re.ReplaceAllStringFunc(query, func(src string) string { + names[src[1:]] = i + i += 1 + return "?" + }) + + stmt, err := tx.Tx.PrepareContext(ctx, query) + if err != nil { + return nil, err + } + return &Stmt{stmt, tx.db, names}, nil +} + +func (tx *Tx) Prepare(query string) (*Stmt, error) { + return tx.PrepareContext(context.Background(), query) +} + +func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt { + stmt.Stmt = tx.Tx.StmtContext(ctx, stmt.Stmt) + return stmt +} + +func (tx *Tx) Stmt(stmt *Stmt) *Stmt { + return tx.StmtContext(context.Background(), stmt) +} + +func (tx *Tx) ExecMapContext(ctx context.Context, query string, mp interface{}) (sql.Result, error) { + query, args, err := MapToSlice(query, mp) + if err != nil { + return nil, err + } + return tx.Tx.ExecContext(ctx, query, args...) +} + +func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) { + return tx.ExecMapContext(context.Background(), query, mp) +} + +func (tx *Tx) ExecStructContext(ctx context.Context, query string, st interface{}) (sql.Result, error) { + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err + } + return tx.Tx.ExecContext(ctx, query, args...) +} + +func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) { + return tx.ExecStructContext(context.Background(), query, st) +} + +func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { + rows, err := tx.Tx.QueryContext(ctx, query, args...) + if err != nil { + return nil, err + } + return &Rows{rows, tx.db}, nil +} + +func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { + return tx.QueryContext(context.Background(), query, args...) +} + +func (tx *Tx) QueryMapContext(ctx context.Context, query string, mp interface{}) (*Rows, error) { + query, args, err := MapToSlice(query, mp) + if err != nil { + return nil, err + } + return tx.QueryContext(ctx, query, args...) +} + +func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) { + return tx.QueryMapContext(context.Background(), query, mp) +} + +func (tx *Tx) QueryStructContext(ctx context.Context, query string, st interface{}) (*Rows, error) { + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err + } + return tx.QueryContext(ctx, query, args...) +} + +func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) { + return tx.QueryStructContext(context.Background(), query, st) +} + +func (tx *Tx) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row { + rows, err := tx.QueryContext(ctx, query, args...) + return &Row{rows, err} +} + +func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { + return tx.QueryRowContext(context.Background(), query, args...) +} + +func (tx *Tx) QueryRowMapContext(ctx context.Context, query string, mp interface{}) *Row { + query, args, err := MapToSlice(query, mp) + if err != nil { + return &Row{nil, err} + } + return tx.QueryRowContext(ctx, query, args...) +} + +func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row { + return tx.QueryRowMapContext(context.Background(), query, mp) +} + +func (tx *Tx) QueryRowStructContext(ctx context.Context, query string, st interface{}) *Row { + query, args, err := StructToSlice(query, st) + if err != nil { + return &Row{nil, err} + } + return tx.QueryRowContext(ctx, query, args...) +} + +func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row { + return tx.QueryRowStructContext(context.Background(), query, st) +} diff --git a/vendor/github.com/go-xorm/core/type.go b/vendor/github.com/go-xorm/core/type.go index 8010a2220fc..8164953602e 100644 --- a/vendor/github.com/go-xorm/core/type.go +++ b/vendor/github.com/go-xorm/core/type.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -69,24 +73,31 @@ var ( Enum = "ENUM" Set = "SET" - Char = "CHAR" - Varchar = "VARCHAR" - NVarchar = "NVARCHAR" - TinyText = "TINYTEXT" - Text = "TEXT" - Clob = "CLOB" - MediumText = "MEDIUMTEXT" - LongText = "LONGTEXT" - Uuid = "UUID" + Char = "CHAR" + Varchar = "VARCHAR" + NChar = "NCHAR" + NVarchar = "NVARCHAR" + TinyText = "TINYTEXT" + Text = "TEXT" + NText = "NTEXT" + Clob = "CLOB" + MediumText = "MEDIUMTEXT" + LongText = "LONGTEXT" + Uuid = "UUID" + UniqueIdentifier = "UNIQUEIDENTIFIER" + SysName = "SYSNAME" Date = "DATE" DateTime = "DATETIME" + SmallDateTime = "SMALLDATETIME" Time = "TIME" TimeStamp = "TIMESTAMP" TimeStampz = "TIMESTAMPZ" Decimal = "DECIMAL" Numeric = "NUMERIC" + Money = "MONEY" + SmallMoney = "SMALLMONEY" Real = "REAL" Float = "FLOAT" @@ -124,35 +135,42 @@ var ( Jsonb: TEXT_TYPE, Char: TEXT_TYPE, + NChar: TEXT_TYPE, Varchar: TEXT_TYPE, NVarchar: TEXT_TYPE, TinyText: TEXT_TYPE, Text: TEXT_TYPE, + NText: TEXT_TYPE, MediumText: TEXT_TYPE, LongText: TEXT_TYPE, Uuid: TEXT_TYPE, Clob: TEXT_TYPE, + SysName: TEXT_TYPE, Date: TIME_TYPE, DateTime: TIME_TYPE, Time: TIME_TYPE, TimeStamp: TIME_TYPE, TimeStampz: TIME_TYPE, + SmallDateTime: TIME_TYPE, Decimal: NUMERIC_TYPE, Numeric: NUMERIC_TYPE, Real: NUMERIC_TYPE, Float: NUMERIC_TYPE, Double: NUMERIC_TYPE, + Money: NUMERIC_TYPE, + SmallMoney: NUMERIC_TYPE, Binary: BLOB_TYPE, VarBinary: BLOB_TYPE, - TinyBlob: BLOB_TYPE, - Blob: BLOB_TYPE, - MediumBlob: BLOB_TYPE, - LongBlob: BLOB_TYPE, - Bytea: BLOB_TYPE, + TinyBlob: BLOB_TYPE, + Blob: BLOB_TYPE, + MediumBlob: BLOB_TYPE, + LongBlob: BLOB_TYPE, + Bytea: BLOB_TYPE, + UniqueIdentifier: BLOB_TYPE, Bool: NUMERIC_TYPE, @@ -289,15 +307,15 @@ func SQLType2Type(st SQLType) reflect.Type { return reflect.TypeOf(float32(1)) case Double: return reflect.TypeOf(float64(1)) - case Char, Varchar, NVarchar, TinyText, Text, MediumText, LongText, Enum, Set, Uuid, Clob: + case Char, NChar, Varchar, NVarchar, TinyText, Text, NText, MediumText, LongText, Enum, Set, Uuid, Clob, SysName: return reflect.TypeOf("") - case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary: + case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary, UniqueIdentifier: return reflect.TypeOf([]byte{}) case Bool: return reflect.TypeOf(true) - case DateTime, Date, Time, TimeStamp, TimeStampz: + case DateTime, Date, Time, TimeStamp, TimeStampz, SmallDateTime: return reflect.TypeOf(c_TIME_DEFAULT) - case Decimal, Numeric: + case Decimal, Numeric, Money, SmallMoney: return reflect.TypeOf("") default: return reflect.TypeOf("") diff --git a/vendor/github.com/go-xorm/xorm/context_cache.go b/vendor/github.com/go-xorm/xorm/context_cache.go new file mode 100644 index 00000000000..1bc22884968 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/context_cache.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +// ContextCache is the interface that operates the cache data. +type ContextCache interface { + // Put puts value into cache with key. + Put(key string, val interface{}) + // Get gets cached value by given key. + Get(key string) interface{} +} + +type memoryContextCache map[string]interface{} + +// NewMemoryContextCache return memoryContextCache +func NewMemoryContextCache() memoryContextCache { + return make(map[string]interface{}) +} + +// Put puts value into cache with key. +func (m memoryContextCache) Put(key string, val interface{}) { + m[key] = val +} + +// Get gets cached value by given key. +func (m memoryContextCache) Get(key string) interface{} { + return m[key] +} diff --git a/vendor/github.com/go-xorm/xorm/dialect_mysql.go b/vendor/github.com/go-xorm/xorm/dialect_mysql.go index 99100b23251..9f5ae3b2e50 100644 --- a/vendor/github.com/go-xorm/xorm/dialect_mysql.go +++ b/vendor/github.com/go-xorm/xorm/dialect_mysql.go @@ -172,12 +172,33 @@ type mysql struct { allowAllFiles bool allowOldPasswords bool clientFoundRows bool + rowFormat string } func (db *mysql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { return db.Base.Init(d, db, uri, drivername, dataSourceName) } +func (db *mysql) SetParams(params map[string]string) { + rowFormat, ok := params["rowFormat"] + if ok { + var t = strings.ToUpper(rowFormat) + switch t { + case "COMPACT": + fallthrough + case "REDUNDANT": + fallthrough + case "DYNAMIC": + fallthrough + case "COMPRESSED": + db.rowFormat = t + break + default: + break + } + } +} + func (db *mysql) SqlType(c *core.Column) string { var res string switch t := c.SQLType.Name; t { @@ -487,6 +508,62 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) { return indexes, nil } +func (db *mysql) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string { + var sql string + sql = "CREATE TABLE IF NOT EXISTS " + if tableName == "" { + tableName = table.Name + } + + sql += db.Quote(tableName) + sql += " (" + + if len(table.ColumnsSeq()) > 0 { + pkList := table.PrimaryKeys + + for _, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + if col.IsPrimaryKey && len(pkList) == 1 { + sql += col.String(db) + } else { + sql += col.StringNoPk(db) + } + sql = strings.TrimSpace(sql) + if len(col.Comment) > 0 { + sql += " COMMENT '" + col.Comment + "'" + } + sql += ", " + } + + if len(pkList) > 1 { + sql += "PRIMARY KEY ( " + sql += db.Quote(strings.Join(pkList, db.Quote(","))) + sql += " ), " + } + + sql = sql[:len(sql)-2] + } + sql += ")" + + if storeEngine != "" { + sql += " ENGINE=" + storeEngine + } + + if len(charset) == 0 { + charset = db.URI().Charset + } + if len(charset) != 0 { + sql += " DEFAULT CHARSET " + charset + } + + + + if db.rowFormat != "" { + sql += " ROW_FORMAT=" + db.rowFormat + } + return sql +} + func (db *mysql) Filters() []core.Filter { return []core.Filter{&core.IdFilter{}} } diff --git a/vendor/github.com/go-xorm/xorm/dialect_postgres.go b/vendor/github.com/go-xorm/xorm/dialect_postgres.go index 83e9a1015c4..1f74bd312e8 100644 --- a/vendor/github.com/go-xorm/xorm/dialect_postgres.go +++ b/vendor/github.com/go-xorm/xorm/dialect_postgres.go @@ -764,14 +764,26 @@ var ( "YES": true, "ZONE": true, } + + // DefaultPostgresSchema default postgres schema + DefaultPostgresSchema = "public" ) +const postgresPublicSchema = "public" + type postgres struct { core.Base } func (db *postgres) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { - return db.Base.Init(d, db, uri, drivername, dataSourceName) + err := db.Base.Init(d, db, uri, drivername, dataSourceName) + if err != nil { + return err + } + if db.Schema == "" { + db.Schema = DefaultPostgresSchema + } + return nil } func (db *postgres) SqlType(c *core.Column) string { @@ -868,32 +880,42 @@ func (db *postgres) IndexOnTable() bool { } func (db *postgres) IndexCheckSql(tableName, idxName string) (string, []interface{}) { - args := []interface{}{tableName, idxName} + if len(db.Schema) == 0 { + args := []interface{}{tableName, idxName} + return `SELECT indexname FROM pg_indexes WHERE tablename = ? AND indexname = ?`, args + } + + args := []interface{}{db.Schema, tableName, idxName} return `SELECT indexname FROM pg_indexes ` + - `WHERE tablename = ? AND indexname = ?`, args + `WHERE schemaname = ? AND tablename = ? AND indexname = ?`, args } func (db *postgres) TableCheckSql(tableName string) (string, []interface{}) { - args := []interface{}{tableName} - return `SELECT tablename FROM pg_tables WHERE tablename = ?`, args + if len(db.Schema) == 0 { + args := []interface{}{tableName} + return `SELECT tablename FROM pg_tables WHERE tablename = ?`, args + } + + args := []interface{}{db.Schema, tableName} + return `SELECT tablename FROM pg_tables WHERE schemaname = ? AND tablename = ?`, args } -/*func (db *postgres) ColumnCheckSql(tableName, colName string) (string, []interface{}) { - args := []interface{}{tableName, colName} - return "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = ?" + - " AND column_name = ?", args -}*/ - func (db *postgres) ModifyColumnSql(tableName string, col *core.Column) string { - return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s", - tableName, col.Name, db.SqlType(col)) + if len(db.Schema) == 0 { + return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s", + tableName, col.Name, db.SqlType(col)) + } + return fmt.Sprintf("alter table %s.%s ALTER COLUMN %s TYPE %s", + db.Schema, tableName, col.Name, db.SqlType(col)) } func (db *postgres) DropIndexSql(tableName string, index *core.Index) string { - //var unique string quote := db.Quote idxName := index.Name + tableName = strings.Replace(tableName, `"`, "", -1) + tableName = strings.Replace(tableName, `.`, "_", -1) + if !strings.HasPrefix(idxName, "UQE_") && !strings.HasPrefix(idxName, "IDX_") { if index.Type == core.UniqueType { @@ -902,13 +924,21 @@ func (db *postgres) DropIndexSql(tableName string, index *core.Index) string { idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name) } } + if db.Uri.Schema != "" { + idxName = db.Uri.Schema + "." + idxName + } return fmt.Sprintf("DROP INDEX %v", quote(idxName)) } func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) { - args := []interface{}{tableName, colName} - query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" + - " AND column_name = $2" + args := []interface{}{db.Schema, tableName, colName} + query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = $1 AND table_name = $2" + + " AND column_name = $3" + if len(db.Schema) == 0 { + args = []interface{}{tableName, colName} + query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" + + " AND column_name = $2" + } db.LogSQL(query, args) rows, err := db.DB().Query(query, args...) @@ -921,8 +951,7 @@ func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) { } func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { - // FIXME: the schema should be replaced by user custom's - args := []interface{}{tableName, "public"} + args := []interface{}{tableName} s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix , CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey, CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey @@ -933,7 +962,15 @@ FROM pg_attribute f LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey) LEFT JOIN pg_class AS g ON p.confrelid = g.oid LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name -WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.attnum > 0 ORDER BY f.attnum;` +WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.attnum;` + + var f string + if len(db.Schema) != 0 { + args = append(args, db.Schema) + f = " AND s.table_schema = $2" + } + s = fmt.Sprintf(s, f) + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) @@ -1023,9 +1060,13 @@ WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.att } func (db *postgres) GetTables() ([]*core.Table, error) { - // FIXME: replace public to user customrize schema - args := []interface{}{"public"} - s := fmt.Sprintf("SELECT tablename FROM pg_tables WHERE schemaname = $1") + args := []interface{}{} + s := "SELECT tablename FROM pg_tables" + if len(db.Schema) != 0 { + args = append(args, db.Schema) + s = s + " WHERE schemaname = $1" + } + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) @@ -1049,9 +1090,12 @@ func (db *postgres) GetTables() ([]*core.Table, error) { } func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) { - // FIXME: replace the public schema to user specify schema - args := []interface{}{"public", tableName} - s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE schemaname=$1 AND tablename=$2") + args := []interface{}{tableName} + s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1") + if len(db.Schema) != 0 { + args = append(args, db.Schema) + s = s + " AND schemaname=$2" + } db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) @@ -1179,3 +1223,15 @@ func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { return db, nil } + +type pqDriverPgx struct { + pqDriver +} + +func (pgx *pqDriverPgx) Parse(driverName, dataSourceName string) (*core.Uri, error) { + // Remove the leading characters for driver to work + if len(dataSourceName) >= 9 && dataSourceName[0] == 0 { + dataSourceName = dataSourceName[9:] + } + return pgx.pqDriver.Parse(driverName, dataSourceName) +} diff --git a/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go b/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go index a55b1615e71..e129481466e 100644 --- a/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go +++ b/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go @@ -233,7 +233,7 @@ func (db *sqlite3) TableCheckSql(tableName string) (string, []interface{}) { } func (db *sqlite3) DropIndexSql(tableName string, index *core.Index) string { - //var unique string + // var unique string quote := db.Quote idxName := index.Name @@ -452,5 +452,9 @@ type sqlite3Driver struct { } func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { + if strings.Contains(dataSourceName, "?") { + dataSourceName = dataSourceName[:strings.Index(dataSourceName, "?")] + } + return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil } diff --git a/vendor/github.com/go-xorm/xorm/engine.go b/vendor/github.com/go-xorm/xorm/engine.go index 444611afb16..89a96d9fdb5 100644 --- a/vendor/github.com/go-xorm/xorm/engine.go +++ b/vendor/github.com/go-xorm/xorm/engine.go @@ -49,6 +49,35 @@ type Engine struct { tagHandlers map[string]tagHandler engineGroup *EngineGroup + + cachers map[string]core.Cacher + cacherLock sync.RWMutex +} + +func (engine *Engine) setCacher(tableName string, cacher core.Cacher) { + engine.cacherLock.Lock() + engine.cachers[tableName] = cacher + engine.cacherLock.Unlock() +} + +func (engine *Engine) SetCacher(tableName string, cacher core.Cacher) { + engine.setCacher(tableName, cacher) +} + +func (engine *Engine) getCacher(tableName string) core.Cacher { + var cacher core.Cacher + var ok bool + engine.cacherLock.RLock() + cacher, ok = engine.cachers[tableName] + engine.cacherLock.RUnlock() + if !ok && !engine.disableGlobalCache { + cacher = engine.Cacher + } + return cacher +} + +func (engine *Engine) GetCacher(tableName string) core.Cacher { + return engine.getCacher(tableName) } // BufferSize sets buffer size for iterate @@ -148,6 +177,14 @@ func (engine *Engine) QuoteStr() string { return engine.dialect.QuoteStr() } +func (engine *Engine) quoteColumns(columnStr string) string { + columns := strings.Split(columnStr, ",") + for i := 0; i < len(columns); i++ { + columns[i] = engine.Quote(strings.TrimSpace(columns[i])) + } + return strings.Join(columns, ",") +} + // Quote Use QuoteStr quote the string sql func (engine *Engine) Quote(value string) string { value = strings.TrimSpace(value) @@ -165,7 +202,7 @@ func (engine *Engine) Quote(value string) string { } // QuoteTo quotes string and writes into the buffer -func (engine *Engine) QuoteTo(buf *bytes.Buffer, value string) { +func (engine *Engine) QuoteTo(buf *builder.StringBuilder, value string) { if buf == nil { return } @@ -208,6 +245,11 @@ func (engine *Engine) AutoIncrStr() string { return engine.dialect.AutoIncrStr() } +// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. +func (engine *Engine) SetConnMaxLifetime(d time.Duration) { + engine.db.SetConnMaxLifetime(d) +} + // SetMaxOpenConns is only available for go 1.2+ func (engine *Engine) SetMaxOpenConns(conns int) { engine.db.SetMaxOpenConns(conns) @@ -245,13 +287,7 @@ func (engine *Engine) NoCascade() *Session { // MapCacher Set a table use a special cacher func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) error { - v := rValue(bean) - tb, err := engine.autoMapType(v) - if err != nil { - return err - } - - tb.Cacher = cacher + engine.setCacher(engine.TableName(bean, true), cacher) return nil } @@ -536,33 +572,6 @@ func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.D return nil } -func (engine *Engine) tableName(beanOrTableName interface{}) (string, error) { - v := rValue(beanOrTableName) - if v.Type().Kind() == reflect.String { - return beanOrTableName.(string), nil - } else if v.Type().Kind() == reflect.Struct { - return engine.tbName(v), nil - } - return "", errors.New("bean should be a struct or struct's point") -} - -func (engine *Engine) tbName(v reflect.Value) string { - if tb, ok := v.Interface().(TableName); ok { - return tb.TableName() - } - - if v.Type().Kind() == reflect.Ptr { - if tb, ok := reflect.Indirect(v).Interface().(TableName); ok { - return tb.TableName() - } - } else if v.CanAddr() { - if tb, ok := v.Addr().Interface().(TableName); ok { - return tb.TableName() - } - } - return engine.TableMapper.Obj2Table(reflect.Indirect(v).Type().Name()) -} - // Cascade use cascade or not func (engine *Engine) Cascade(trueOrFalse ...bool) *Session { session := engine.NewSession() @@ -846,7 +855,7 @@ func (engine *Engine) TableInfo(bean interface{}) *Table { if err != nil { engine.logger.Error(err) } - return &Table{tb, engine.tbName(v)} + return &Table{tb, engine.TableName(bean)} } func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) { @@ -861,15 +870,6 @@ func addIndex(indexName string, table *core.Table, col *core.Column, indexType i } } -func (engine *Engine) newTable() *core.Table { - table := core.NewEmptyTable() - - if !engine.disableGlobalCache { - table.Cacher = engine.Cacher - } - return table -} - // TableName table name interface to define customerize table name type TableName interface { TableName() string @@ -881,21 +881,9 @@ var ( func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) { t := v.Type() - table := engine.newTable() - if tb, ok := v.Interface().(TableName); ok { - table.Name = tb.TableName() - } else { - if v.CanAddr() { - if tb, ok = v.Addr().Interface().(TableName); ok { - table.Name = tb.TableName() - } - } - if table.Name == "" { - table.Name = engine.TableMapper.Obj2Table(t.Name()) - } - } - + table := core.NewEmptyTable() table.Type = t + table.Name = engine.tbNameForMap(v) var idFieldColName string var hasCacheTag, hasNoCacheTag bool @@ -1049,15 +1037,15 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) { if hasCacheTag { if engine.Cacher != nil { // !nash! use engine's cacher if provided engine.logger.Info("enable cache on table:", table.Name) - table.Cacher = engine.Cacher + engine.setCacher(table.Name, engine.Cacher) } else { engine.logger.Info("enable LRU cache on table:", table.Name) - table.Cacher = NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) // !nashtsai! HACK use LRU cacher for now + engine.setCacher(table.Name, NewLRUCacher2(NewMemoryStore(), time.Hour, 10000)) } } if hasNoCacheTag { - engine.logger.Info("no cache on table:", table.Name) - table.Cacher = nil + engine.logger.Info("disable cache on table:", table.Name) + engine.setCacher(table.Name, nil) } return table, nil @@ -1116,7 +1104,25 @@ func (engine *Engine) idOfV(rv reflect.Value) (core.PK, error) { pk := make([]interface{}, len(table.PrimaryKeys)) for i, col := range table.PKColumns() { var err error - pkField := v.FieldByName(col.FieldName) + + fieldName := col.FieldName + for { + parts := strings.SplitN(fieldName, ".", 2) + if len(parts) == 1 { + break + } + + v = v.FieldByName(parts[0]) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return nil, ErrUnSupportedType + } + fieldName = parts[1] + } + + pkField := v.FieldByName(fieldName) switch pkField.Kind() { case reflect.String: pk[i], err = engine.idTypeAssertion(col, pkField.String()) @@ -1162,26 +1168,10 @@ func (engine *Engine) CreateUniques(bean interface{}) error { return session.CreateUniques(bean) } -func (engine *Engine) getCacher2(table *core.Table) core.Cacher { - return table.Cacher -} - // ClearCacheBean if enabled cache, clear the cache bean func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { - v := rValue(bean) - t := v.Type() - if t.Kind() != reflect.Struct { - return errors.New("error params") - } - tableName := engine.tbName(v) - table, err := engine.autoMapType(v) - if err != nil { - return err - } - cacher := table.Cacher - if cacher == nil { - cacher = engine.Cacher - } + tableName := engine.TableName(bean) + cacher := engine.getCacher(tableName) if cacher != nil { cacher.ClearIds(tableName) cacher.DelBean(tableName, id) @@ -1192,21 +1182,8 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { // ClearCache if enabled cache, clear some tables' cache func (engine *Engine) ClearCache(beans ...interface{}) error { for _, bean := range beans { - v := rValue(bean) - t := v.Type() - if t.Kind() != reflect.Struct { - return errors.New("error params") - } - tableName := engine.tbName(v) - table, err := engine.autoMapType(v) - if err != nil { - return err - } - - cacher := table.Cacher - if cacher == nil { - cacher = engine.Cacher - } + tableName := engine.TableName(bean) + cacher := engine.getCacher(tableName) if cacher != nil { cacher.ClearIds(tableName) cacher.ClearBeans(tableName) @@ -1224,13 +1201,13 @@ func (engine *Engine) Sync(beans ...interface{}) error { for _, bean := range beans { v := rValue(bean) - tableName := engine.tbName(v) + tableNameNoSchema := engine.TableName(bean) table, err := engine.autoMapType(v) if err != nil { return err } - isExist, err := session.Table(bean).isTableExist(tableName) + isExist, err := session.Table(bean).isTableExist(tableNameNoSchema) if err != nil { return err } @@ -1256,12 +1233,12 @@ func (engine *Engine) Sync(beans ...interface{}) error { } } else { for _, col := range table.Columns() { - isExist, err := engine.dialect.IsColumnExist(tableName, col.Name) + isExist, err := engine.dialect.IsColumnExist(tableNameNoSchema, col.Name) if err != nil { return err } if !isExist { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } err = session.addColumn(col.Name) @@ -1272,35 +1249,35 @@ func (engine *Engine) Sync(beans ...interface{}) error { } for name, index := range table.Indexes { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } if index.Type == core.UniqueType { - isExist, err := session.isIndexExist2(tableName, index.Cols, true) + isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, true) if err != nil { return err } if !isExist { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } - err = session.addUnique(tableName, name) + err = session.addUnique(tableNameNoSchema, name) if err != nil { return err } } } else if index.Type == core.IndexType { - isExist, err := session.isIndexExist2(tableName, index.Cols, false) + isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, false) if err != nil { return err } if !isExist { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } - err = session.addIndex(tableName, name) + err = session.addIndex(tableNameNoSchema, name) if err != nil { return err } @@ -1369,10 +1346,10 @@ func (engine *Engine) DropIndexes(bean interface{}) error { } // Exec raw sql -func (engine *Engine) Exec(sql string, args ...interface{}) (sql.Result, error) { +func (engine *Engine) Exec(sqlorArgs ...interface{}) (sql.Result, error) { session := engine.NewSession() defer session.Close() - return session.Exec(sql, args...) + return session.Exec(sqlorArgs...) } // Query a raw sql and return records as []map[string][]byte @@ -1453,6 +1430,13 @@ func (engine *Engine) Find(beans interface{}, condiBeans ...interface{}) error { return session.Find(beans, condiBeans...) } +// FindAndCount find the results and also return the counts +func (engine *Engine) FindAndCount(rowsSlicePtr interface{}, condiBean ...interface{}) (int64, error) { + session := engine.NewSession() + defer session.Close() + return session.FindAndCount(rowsSlicePtr, condiBean...) +} + // Iterate record by record handle records from table, bean's non-empty fields // are conditions. func (engine *Engine) Iterate(bean interface{}, fun IterFunc) error { @@ -1629,6 +1613,11 @@ func (engine *Engine) SetTZDatabase(tz *time.Location) { engine.DatabaseTZ = tz } +// SetSchema sets the schema of database +func (engine *Engine) SetSchema(schema string) { + engine.dialect.URI().Schema = schema +} + // Unscoped always disable struct tag "deleted" func (engine *Engine) Unscoped() *Session { session := engine.NewSession() diff --git a/vendor/github.com/go-xorm/xorm/engine_cond.go b/vendor/github.com/go-xorm/xorm/engine_cond.go index 6c8e3879cee..4dde8662e13 100644 --- a/vendor/github.com/go-xorm/xorm/engine_cond.go +++ b/vendor/github.com/go-xorm/xorm/engine_cond.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "time" "github.com/go-xorm/builder" @@ -51,7 +52,9 @@ func (engine *Engine) buildConds(table *core.Table, bean interface{}, fieldValuePtr, err := col.ValueOf(bean) if err != nil { - engine.logger.Error(err) + if !strings.Contains(err.Error(), "is not valid") { + engine.logger.Warn(err) + } continue } diff --git a/vendor/github.com/go-xorm/xorm/engine_group.go b/vendor/github.com/go-xorm/xorm/engine_group.go index 1de425f372c..5eee3e61833 100644 --- a/vendor/github.com/go-xorm/xorm/engine_group.go +++ b/vendor/github.com/go-xorm/xorm/engine_group.go @@ -5,6 +5,8 @@ package xorm import ( + "time" + "github.com/go-xorm/core" ) @@ -99,6 +101,14 @@ func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) { } } +// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. +func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) { + eg.Engine.SetConnMaxLifetime(d) + for i := 0; i < len(eg.slaves); i++ { + eg.slaves[i].SetConnMaxLifetime(d) + } +} + // SetDefaultCacher set the default cacher func (eg *EngineGroup) SetDefaultCacher(cacher core.Cacher) { eg.Engine.SetDefaultCacher(cacher) diff --git a/vendor/github.com/go-xorm/xorm/engine_maxlife.go b/vendor/github.com/go-xorm/xorm/engine_maxlife.go deleted file mode 100644 index 22666c5f44c..00000000000 --- a/vendor/github.com/go-xorm/xorm/engine_maxlife.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2017 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.6 - -package xorm - -import "time" - -// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. -func (engine *Engine) SetConnMaxLifetime(d time.Duration) { - engine.db.SetConnMaxLifetime(d) -} - -// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. -func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) { - eg.Engine.SetConnMaxLifetime(d) - for i := 0; i < len(eg.slaves); i++ { - eg.slaves[i].SetConnMaxLifetime(d) - } -} diff --git a/vendor/github.com/go-xorm/xorm/engine_table.go b/vendor/github.com/go-xorm/xorm/engine_table.go new file mode 100644 index 00000000000..94871a4bce5 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/engine_table.go @@ -0,0 +1,113 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "fmt" + "reflect" + "strings" + + "github.com/go-xorm/core" +) + +// TableNameWithSchema will automatically add schema prefix on table name +func (engine *Engine) tbNameWithSchema(v string) string { + // Add schema name as prefix of table name. + // Only for postgres database. + if engine.dialect.DBType() == core.POSTGRES && + engine.dialect.URI().Schema != "" && + engine.dialect.URI().Schema != postgresPublicSchema && + strings.Index(v, ".") == -1 { + return engine.dialect.URI().Schema + "." + v + } + return v +} + +// TableName returns table name with schema prefix if has +func (engine *Engine) TableName(bean interface{}, includeSchema ...bool) string { + tbName := engine.tbNameNoSchema(bean) + if len(includeSchema) > 0 && includeSchema[0] { + tbName = engine.tbNameWithSchema(tbName) + } + + return tbName +} + +// tbName get some table's table name +func (session *Session) tbNameNoSchema(table *core.Table) string { + if len(session.statement.AltTableName) > 0 { + return session.statement.AltTableName + } + + return table.Name +} + +func (engine *Engine) tbNameForMap(v reflect.Value) string { + if v.Type().Implements(tpTableName) { + return v.Interface().(TableName).TableName() + } + if v.Kind() == reflect.Ptr { + v = v.Elem() + if v.Type().Implements(tpTableName) { + return v.Interface().(TableName).TableName() + } + } + + return engine.TableMapper.Obj2Table(v.Type().Name()) +} + +func (engine *Engine) tbNameNoSchema(tablename interface{}) string { + switch tablename.(type) { + case []string: + t := tablename.([]string) + if len(t) > 1 { + return fmt.Sprintf("%v AS %v", engine.Quote(t[0]), engine.Quote(t[1])) + } else if len(t) == 1 { + return engine.Quote(t[0]) + } + case []interface{}: + t := tablename.([]interface{}) + l := len(t) + var table string + if l > 0 { + f := t[0] + switch f.(type) { + case string: + table = f.(string) + case TableName: + table = f.(TableName).TableName() + default: + v := rValue(f) + t := v.Type() + if t.Kind() == reflect.Struct { + table = engine.tbNameForMap(v) + } else { + table = engine.Quote(fmt.Sprintf("%v", f)) + } + } + } + if l > 1 { + return fmt.Sprintf("%v AS %v", engine.Quote(table), + engine.Quote(fmt.Sprintf("%v", t[1]))) + } else if l == 1 { + return engine.Quote(table) + } + case TableName: + return tablename.(TableName).TableName() + case string: + return tablename.(string) + case reflect.Value: + v := tablename.(reflect.Value) + return engine.tbNameForMap(v) + default: + v := rValue(tablename) + t := v.Type() + if t.Kind() == reflect.Struct { + return engine.tbNameForMap(v) + } + return engine.Quote(fmt.Sprintf("%v", tablename)) + } + return "" +} diff --git a/vendor/github.com/go-xorm/xorm/error.go b/vendor/github.com/go-xorm/xorm/error.go index cfeefc31e8e..a223fc4a861 100644 --- a/vendor/github.com/go-xorm/xorm/error.go +++ b/vendor/github.com/go-xorm/xorm/error.go @@ -6,23 +6,44 @@ package xorm import ( "errors" + "fmt" ) var ( // ErrParamsType params error ErrParamsType = errors.New("Params type error") // ErrTableNotFound table not found error - ErrTableNotFound = errors.New("Not found table") + ErrTableNotFound = errors.New("Table not found") // ErrUnSupportedType unsupported error ErrUnSupportedType = errors.New("Unsupported type error") - // ErrNotExist record is not exist error - ErrNotExist = errors.New("Not exist error") + // ErrNotExist record does not exist error + ErrNotExist = errors.New("Record does not exist") // ErrCacheFailed cache failed error ErrCacheFailed = errors.New("Cache failed") // ErrNeedDeletedCond delete needs less one condition error - ErrNeedDeletedCond = errors.New("Delete need at least one condition") + ErrNeedDeletedCond = errors.New("Delete action needs at least one condition") // ErrNotImplemented not implemented ErrNotImplemented = errors.New("Not implemented") // ErrConditionType condition type unsupported - ErrConditionType = errors.New("Unsupported conditon type") + ErrConditionType = errors.New("Unsupported condition type") ) + +// ErrFieldIsNotExist columns does not exist +type ErrFieldIsNotExist struct { + FieldName string + TableName string +} + +func (e ErrFieldIsNotExist) Error() string { + return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName) +} + +// ErrFieldIsNotValid is not valid +type ErrFieldIsNotValid struct { + FieldName string + TableName string +} + +func (e ErrFieldIsNotValid) Error() string { + return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName) +} diff --git a/vendor/github.com/go-xorm/xorm/helpers.go b/vendor/github.com/go-xorm/xorm/helpers.go index f39ed472560..f1705782e3d 100644 --- a/vendor/github.com/go-xorm/xorm/helpers.go +++ b/vendor/github.com/go-xorm/xorm/helpers.go @@ -11,7 +11,6 @@ import ( "sort" "strconv" "strings" - "time" "github.com/go-xorm/core" ) @@ -293,19 +292,6 @@ func structName(v reflect.Type) string { return v.Name() } -func col2NewCols(columns ...string) []string { - newColumns := make([]string, 0, len(columns)) - for _, col := range columns { - col = strings.Replace(col, "`", "", -1) - col = strings.Replace(col, `"`, "", -1) - ccols := strings.Split(col, ",") - for _, c := range ccols { - newColumns = append(newColumns, strings.TrimSpace(c)) - } - } - return newColumns -} - func sliceEq(left, right []string) bool { if len(left) != len(right) { return false @@ -320,154 +306,6 @@ func sliceEq(left, right []string) bool { return true } -func setColumnInt(bean interface{}, col *core.Column, t int64) { - v, err := col.ValueOf(bean) - if err != nil { - return - } - if v.CanSet() { - switch v.Type().Kind() { - case reflect.Int, reflect.Int64, reflect.Int32: - v.SetInt(t) - case reflect.Uint, reflect.Uint64, reflect.Uint32: - v.SetUint(uint64(t)) - } - } -} - -func setColumnTime(bean interface{}, col *core.Column, t time.Time) { - v, err := col.ValueOf(bean) - if err != nil { - return - } - if v.CanSet() { - switch v.Type().Kind() { - case reflect.Struct: - v.Set(reflect.ValueOf(t).Convert(v.Type())) - case reflect.Int, reflect.Int64, reflect.Int32: - v.SetInt(t.Unix()) - case reflect.Uint, reflect.Uint64, reflect.Uint32: - v.SetUint(uint64(t.Unix())) - } - } -} - -func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) { - colNames := make([]string, 0, len(table.ColumnsSeq())) - args := make([]interface{}, 0, len(table.ColumnsSeq())) - - for _, col := range table.Columns() { - if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated { - if _, ok := getFlagForColumn(session.statement.columnMap, col); !ok { - continue - } - } - if col.MapType == core.ONLYFROMDB { - continue - } - - fieldValuePtr, err := col.ValueOf(bean) - if err != nil { - return nil, nil, err - } - fieldValue := *fieldValuePtr - - if col.IsAutoIncrement { - switch fieldValue.Type().Kind() { - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: - if fieldValue.Int() == 0 { - continue - } - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: - if fieldValue.Uint() == 0 { - continue - } - case reflect.String: - if len(fieldValue.String()) == 0 { - continue - } - case reflect.Ptr: - if fieldValue.Pointer() == 0 { - continue - } - } - } - - if col.IsDeleted { - continue - } - - if session.statement.ColumnStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); !ok { - continue - } else if _, ok := session.statement.incrColumns[col.Name]; ok { - continue - } else if _, ok := session.statement.decrColumns[col.Name]; ok { - continue - } - } - if session.statement.OmitStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); ok { - continue - } - } - - // !evalphobia! set fieldValue as nil when column is nullable and zero-value - if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok { - if col.Nullable && isZero(fieldValue.Interface()) { - var nilValue *int - fieldValue = reflect.ValueOf(nilValue) - } - } - - if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { - // if time is non-empty, then set to auto time - val, t := session.engine.nowTime(col) - args = append(args, val) - - var colName = col.Name - session.afterClosures = append(session.afterClosures, func(bean interface{}) { - col := table.GetColumn(colName) - setColumnTime(bean, col, t) - }) - } else if col.IsVersion && session.statement.checkVersion { - args = append(args, 1) - } else { - arg, err := session.value2Interface(col, fieldValue) - if err != nil { - return colNames, args, err - } - args = append(args, arg) - } - - if includeQuote { - colNames = append(colNames, session.engine.Quote(col.Name)+" = ?") - } else { - colNames = append(colNames, col.Name) - } - } - return colNames, args, nil -} - func indexName(tableName, idxName string) string { return fmt.Sprintf("IDX_%v_%v", tableName, idxName) } - -func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) { - if len(m) == 0 { - return false, false - } - - n := len(col.Name) - - for mk := range m { - if len(mk) != n { - continue - } - if strings.EqualFold(mk, col.Name) { - return m[mk], true - } - } - - return false, false -} diff --git a/vendor/github.com/go-xorm/xorm/interface.go b/vendor/github.com/go-xorm/xorm/interface.go index 9a3b6da0b2b..33d2078e44e 100644 --- a/vendor/github.com/go-xorm/xorm/interface.go +++ b/vendor/github.com/go-xorm/xorm/interface.go @@ -27,9 +27,10 @@ type Interface interface { Delete(interface{}) (int64, error) Distinct(columns ...string) *Session DropIndexes(bean interface{}) error - Exec(string, ...interface{}) (sql.Result, error) + Exec(sqlOrAgrs ...interface{}) (sql.Result, error) Exist(bean ...interface{}) (bool, error) Find(interface{}, ...interface{}) error + FindAndCount(interface{}, ...interface{}) (int64, error) Get(interface{}) (bool, error) GroupBy(keys string) *Session ID(interface{}) *Session @@ -41,6 +42,7 @@ type Interface interface { IsTableExist(beanOrTableName interface{}) (bool, error) Iterate(interface{}, IterFunc) error Limit(int, ...int) *Session + MustCols(columns ...string) *Session NoAutoCondition(...bool) *Session NotIn(string, ...interface{}) *Session Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session @@ -70,29 +72,40 @@ type EngineInterface interface { Before(func(interface{})) *Session Charset(charset string) *Session + ClearCache(...interface{}) error CreateTables(...interface{}) error DBMetas() ([]*core.Table, error) Dialect() core.Dialect DropTables(...interface{}) error DumpAllToFile(fp string, tp ...core.DbType) error + GetCacher(string) core.Cacher GetColumnMapper() core.IMapper GetDefaultCacher() core.Cacher GetTableMapper() core.IMapper GetTZDatabase() *time.Location GetTZLocation() *time.Location + MapCacher(interface{}, core.Cacher) error NewSession() *Session NoAutoTime() *Session Quote(string) string + SetCacher(string, core.Cacher) + SetConnMaxLifetime(time.Duration) SetDefaultCacher(core.Cacher) + SetLogger(logger core.ILogger) SetLogLevel(core.LogLevel) SetMapper(core.IMapper) + SetMaxOpenConns(int) + SetMaxIdleConns(int) + SetSchema(string) SetTZDatabase(tz *time.Location) SetTZLocation(tz *time.Location) + ShowExecTime(...bool) ShowSQL(show ...bool) Sync(...interface{}) error Sync2(...interface{}) error StoreEngine(storeEngine string) *Session TableInfo(bean interface{}) *Table + TableName(interface{}, ...bool) string UnMapType(reflect.Type) } diff --git a/vendor/github.com/go-xorm/xorm/rows.go b/vendor/github.com/go-xorm/xorm/rows.go index 31e29ae26f6..54ec7f37a28 100644 --- a/vendor/github.com/go-xorm/xorm/rows.go +++ b/vendor/github.com/go-xorm/xorm/rows.go @@ -32,7 +32,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { var args []interface{} var err error - if err = rows.session.statement.setRefValue(rValue(bean)); err != nil { + if err = rows.session.statement.setRefBean(bean); err != nil { return nil, err } @@ -94,8 +94,7 @@ func (rows *Rows) Scan(bean interface{}) error { return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) } - dataStruct := rValue(bean) - if err := rows.session.statement.setRefValue(dataStruct); err != nil { + if err := rows.session.statement.setRefBean(bean); err != nil { return err } @@ -104,6 +103,7 @@ func (rows *Rows) Scan(bean interface{}) error { return err } + dataStruct := rValue(bean) _, err = rows.session.slice2Bean(scanResults, rows.fields, bean, &dataStruct, rows.session.statement.RefTable) if err != nil { return err diff --git a/vendor/github.com/go-xorm/xorm/session.go b/vendor/github.com/go-xorm/xorm/session.go index 5c6cb5f9def..b475089191c 100644 --- a/vendor/github.com/go-xorm/xorm/session.go +++ b/vendor/github.com/go-xorm/xorm/session.go @@ -102,6 +102,12 @@ func (session *Session) Close() { } } +// ContextCache enable context cache or not +func (session *Session) ContextCache(context ContextCache) *Session { + session.statement.context = context + return session +} + // IsClosed returns if session is closed func (session *Session) IsClosed() bool { return session.db == nil @@ -278,24 +284,22 @@ func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt, return } -func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table, idx int) *reflect.Value { +func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table, idx int) (*reflect.Value, error) { var col *core.Column if col = table.GetColumnIdx(key, idx); col == nil { - //session.engine.logger.Warnf("table %v has no column %v. %v", table.Name, key, table.ColumnsSeq()) - return nil + return nil, ErrFieldIsNotExist{key, table.Name} } fieldValue, err := col.ValueOfV(dataStruct) if err != nil { - session.engine.logger.Error(err) - return nil + return nil, err } if !fieldValue.IsValid() || !fieldValue.CanSet() { - session.engine.logger.Warnf("table %v's column %v is not valid or cannot set", table.Name, key) - return nil + return nil, ErrFieldIsNotValid{key, table.Name} } - return fieldValue + + return fieldValue, nil } // Cell cell is a result of one column field @@ -407,409 +411,417 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b } tempMap[lKey] = idx - if fieldValue := session.getField(dataStruct, key, table, idx); fieldValue != nil { - rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii])) - - // if row is null then ignore - if rawValue.Interface() == nil { - continue + fieldValue, err := session.getField(dataStruct, key, table, idx) + if err != nil { + if !strings.Contains(err.Error(), "is not valid") { + session.engine.logger.Warn(err) } + continue + } + if fieldValue == nil { + continue + } + rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii])) - if fieldValue.CanAddr() { - if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { - if data, err := value2Bytes(&rawValue); err == nil { - if err := structConvert.FromDB(data); err != nil { - return nil, err - } - } else { + // if row is null then ignore + if rawValue.Interface() == nil { + continue + } + + if fieldValue.CanAddr() { + if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { + if data, err := value2Bytes(&rawValue); err == nil { + if err := structConvert.FromDB(data); err != nil { return nil, err } - continue - } - } - - if _, ok := fieldValue.Interface().(core.Conversion); ok { - if data, err := value2Bytes(&rawValue); err == nil { - if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { - fieldValue.Set(reflect.New(fieldValue.Type().Elem())) - } - fieldValue.Interface().(core.Conversion).FromDB(data) } else { return nil, err } continue } + } - rawValueType := reflect.TypeOf(rawValue.Interface()) - vv := reflect.ValueOf(rawValue.Interface()) - col := table.GetColumnIdx(key, idx) - if col.IsPrimaryKey { - pk = append(pk, rawValue.Interface()) + if _, ok := fieldValue.Interface().(core.Conversion); ok { + if data, err := value2Bytes(&rawValue); err == nil { + if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { + fieldValue.Set(reflect.New(fieldValue.Type().Elem())) + } + fieldValue.Interface().(core.Conversion).FromDB(data) + } else { + return nil, err } - fieldType := fieldValue.Type() - hasAssigned := false + continue + } - if col.SQLType.IsJson() { - var bs []byte - if rawValueType.Kind() == reflect.String { - bs = []byte(vv.String()) - } else if rawValueType.ConvertibleTo(core.BytesType) { - bs = vv.Bytes() + rawValueType := reflect.TypeOf(rawValue.Interface()) + vv := reflect.ValueOf(rawValue.Interface()) + col := table.GetColumnIdx(key, idx) + if col.IsPrimaryKey { + pk = append(pk, rawValue.Interface()) + } + fieldType := fieldValue.Type() + hasAssigned := false + + if col.SQLType.IsJson() { + var bs []byte + if rawValueType.Kind() == reflect.String { + bs = []byte(vv.String()) + } else if rawValueType.ConvertibleTo(core.BytesType) { + bs = vv.Bytes() + } else { + return nil, fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind()) + } + + hasAssigned = true + + if len(bs) > 0 { + if fieldType.Kind() == reflect.String { + fieldValue.SetString(string(bs)) + continue + } + if fieldValue.CanAddr() { + err := json.Unmarshal(bs, fieldValue.Addr().Interface()) + if err != nil { + return nil, err + } } else { - return nil, fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind()) - } - - hasAssigned = true - - if len(bs) > 0 { - if fieldType.Kind() == reflect.String { - fieldValue.SetString(string(bs)) - continue - } - if fieldValue.CanAddr() { - err := json.Unmarshal(bs, fieldValue.Addr().Interface()) - if err != nil { - return nil, err - } - } else { - x := reflect.New(fieldType) - err := json.Unmarshal(bs, x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) + x := reflect.New(fieldType) + err := json.Unmarshal(bs, x.Interface()) + if err != nil { + return nil, err } + fieldValue.Set(x.Elem()) } - - continue } - switch fieldType.Kind() { - case reflect.Complex64, reflect.Complex128: - // TODO: reimplement this - var bs []byte - if rawValueType.Kind() == reflect.String { - bs = []byte(vv.String()) - } else if rawValueType.ConvertibleTo(core.BytesType) { - bs = vv.Bytes() - } + continue + } - hasAssigned = true - if len(bs) > 0 { - if fieldValue.CanAddr() { - err := json.Unmarshal(bs, fieldValue.Addr().Interface()) - if err != nil { - return nil, err - } - } else { - x := reflect.New(fieldType) - err := json.Unmarshal(bs, x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) + switch fieldType.Kind() { + case reflect.Complex64, reflect.Complex128: + // TODO: reimplement this + var bs []byte + if rawValueType.Kind() == reflect.String { + bs = []byte(vv.String()) + } else if rawValueType.ConvertibleTo(core.BytesType) { + bs = vv.Bytes() + } + + hasAssigned = true + if len(bs) > 0 { + if fieldValue.CanAddr() { + err := json.Unmarshal(bs, fieldValue.Addr().Interface()) + if err != nil { + return nil, err } + } else { + x := reflect.New(fieldType) + err := json.Unmarshal(bs, x.Interface()) + if err != nil { + return nil, err + } + fieldValue.Set(x.Elem()) } + } + case reflect.Slice, reflect.Array: + switch rawValueType.Kind() { case reflect.Slice, reflect.Array: - switch rawValueType.Kind() { - case reflect.Slice, reflect.Array: - switch rawValueType.Elem().Kind() { - case reflect.Uint8: - if fieldType.Elem().Kind() == reflect.Uint8 { - hasAssigned = true - if col.SQLType.IsText() { - x := reflect.New(fieldType) - err := json.Unmarshal(vv.Bytes(), x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) - } else { - if fieldValue.Len() > 0 { - for i := 0; i < fieldValue.Len(); i++ { - if i < vv.Len() { - fieldValue.Index(i).Set(vv.Index(i)) - } - } - } else { - for i := 0; i < vv.Len(); i++ { - fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i))) - } - } - } - } - } - } - case reflect.String: - if rawValueType.Kind() == reflect.String { - hasAssigned = true - fieldValue.SetString(vv.String()) - } - case reflect.Bool: - if rawValueType.Kind() == reflect.Bool { - hasAssigned = true - fieldValue.SetBool(vv.Bool()) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - switch rawValueType.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - hasAssigned = true - fieldValue.SetInt(vv.Int()) - } - case reflect.Float32, reflect.Float64: - switch rawValueType.Kind() { - case reflect.Float32, reflect.Float64: - hasAssigned = true - fieldValue.SetFloat(vv.Float()) - } - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - switch rawValueType.Kind() { - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - hasAssigned = true - fieldValue.SetUint(vv.Uint()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - hasAssigned = true - fieldValue.SetUint(uint64(vv.Int())) - } - case reflect.Struct: - if fieldType.ConvertibleTo(core.TimeType) { - dbTZ := session.engine.DatabaseTZ - if col.TimeZone != nil { - dbTZ = col.TimeZone - } - - if rawValueType == core.TimeType { + switch rawValueType.Elem().Kind() { + case reflect.Uint8: + if fieldType.Elem().Kind() == reflect.Uint8 { hasAssigned = true - - t := vv.Convert(core.TimeType).Interface().(time.Time) - - z, _ := t.Zone() - // set new location if database don't save timezone or give an incorrect timezone - if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location - session.engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) - t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), - t.Minute(), t.Second(), t.Nanosecond(), dbTZ) - } - - t = t.In(session.engine.TZLocation) - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } else if rawValueType == core.IntType || rawValueType == core.Int64Type || - rawValueType == core.Int32Type { - hasAssigned = true - - t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation) - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } else { - if d, ok := vv.Interface().([]uint8); ok { - hasAssigned = true - t, err := session.byte2Time(col, d) - if err != nil { - session.engine.logger.Error("byte2Time error:", err.Error()) - hasAssigned = false - } else { - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } - } else if d, ok := vv.Interface().(string); ok { - hasAssigned = true - t, err := session.str2Time(col, d) - if err != nil { - session.engine.logger.Error("byte2Time error:", err.Error()) - hasAssigned = false - } else { - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } - } else { - return nil, fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface()) - } - } - } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { - // !! 增加支持sql.Scanner接口的结构,如sql.NullString - hasAssigned = true - if err := nulVal.Scan(vv.Interface()); err != nil { - session.engine.logger.Error("sql.Sanner error:", err.Error()) - hasAssigned = false - } - } else if col.SQLType.IsJson() { - if rawValueType.Kind() == reflect.String { - hasAssigned = true - x := reflect.New(fieldType) - if len([]byte(vv.String())) > 0 { - err := json.Unmarshal([]byte(vv.String()), x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) - } - } else if rawValueType.Kind() == reflect.Slice { - hasAssigned = true - x := reflect.New(fieldType) - if len(vv.Bytes()) > 0 { + if col.SQLType.IsText() { + x := reflect.New(fieldType) err := json.Unmarshal(vv.Bytes(), x.Interface()) if err != nil { return nil, err } fieldValue.Set(x.Elem()) - } - } - } else if session.statement.UseCascade { - table, err := session.engine.autoMapType(*fieldValue) - if err != nil { - return nil, err - } - - hasAssigned = true - if len(table.PrimaryKeys) != 1 { - return nil, errors.New("unsupported non or composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) - pk[0], err = asKind(vv, rawValueType) - if err != nil { - return nil, err - } - - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - structInter := reflect.New(fieldValue.Type()) - has, err := session.ID(pk).NoCascade().get(structInter.Interface()) - if err != nil { - return nil, err - } - if has { - fieldValue.Set(structInter.Elem()) } else { - return nil, errors.New("cascade obj is not exist") + if fieldValue.Len() > 0 { + for i := 0; i < fieldValue.Len(); i++ { + if i < vv.Len() { + fieldValue.Index(i).Set(vv.Index(i)) + } + } + } else { + for i := 0; i < vv.Len(); i++ { + fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i))) + } + } } } } - case reflect.Ptr: - // !nashtsai! TODO merge duplicated codes above - switch fieldType { - // following types case matching ptr's native type, therefore assign ptr directly - case core.PtrStringType: - if rawValueType.Kind() == reflect.String { - x := vv.String() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrBoolType: - if rawValueType.Kind() == reflect.Bool { - x := vv.Bool() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrTimeType: - if rawValueType == core.PtrTimeType { - hasAssigned = true - var x = rawValue.Interface().(time.Time) - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrFloat64Type: - if rawValueType.Kind() == reflect.Float64 { - x := vv.Float() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrUint64Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint64(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrInt64Type: - if rawValueType.Kind() == reflect.Int64 { - x := vv.Int() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrFloat32Type: - if rawValueType.Kind() == reflect.Float64 { - var x = float32(vv.Float()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrIntType: - if rawValueType.Kind() == reflect.Int64 { - var x = int(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrInt32Type: - if rawValueType.Kind() == reflect.Int64 { - var x = int32(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrInt8Type: - if rawValueType.Kind() == reflect.Int64 { - var x = int8(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrInt16Type: - if rawValueType.Kind() == reflect.Int64 { - var x = int16(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrUintType: - if rawValueType.Kind() == reflect.Int64 { - var x = uint(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrUint32Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint32(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.Uint8Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint8(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.Uint16Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint16(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.Complex64Type: - var x complex64 - if len([]byte(vv.String())) > 0 { - err := json.Unmarshal([]byte(vv.String()), &x) - if err != nil { - return nil, err - } - fieldValue.Set(reflect.ValueOf(&x)) - } - hasAssigned = true - case core.Complex128Type: - var x complex128 - if len([]byte(vv.String())) > 0 { - err := json.Unmarshal([]byte(vv.String()), &x) - if err != nil { - return nil, err - } - fieldValue.Set(reflect.ValueOf(&x)) - } - hasAssigned = true - } // switch fieldType - } // switch fieldType.Kind() + } + case reflect.String: + if rawValueType.Kind() == reflect.String { + hasAssigned = true + fieldValue.SetString(vv.String()) + } + case reflect.Bool: + if rawValueType.Kind() == reflect.Bool { + hasAssigned = true + fieldValue.SetBool(vv.Bool()) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch rawValueType.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + hasAssigned = true + fieldValue.SetInt(vv.Int()) + } + case reflect.Float32, reflect.Float64: + switch rawValueType.Kind() { + case reflect.Float32, reflect.Float64: + hasAssigned = true + fieldValue.SetFloat(vv.Float()) + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + switch rawValueType.Kind() { + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + hasAssigned = true + fieldValue.SetUint(vv.Uint()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + hasAssigned = true + fieldValue.SetUint(uint64(vv.Int())) + } + case reflect.Struct: + if fieldType.ConvertibleTo(core.TimeType) { + dbTZ := session.engine.DatabaseTZ + if col.TimeZone != nil { + dbTZ = col.TimeZone + } - // !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value - if !hasAssigned { - data, err := value2Bytes(&rawValue) + if rawValueType == core.TimeType { + hasAssigned = true + + t := vv.Convert(core.TimeType).Interface().(time.Time) + + z, _ := t.Zone() + // set new location if database don't save timezone or give an incorrect timezone + if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location + session.engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) + t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), + t.Minute(), t.Second(), t.Nanosecond(), dbTZ) + } + + t = t.In(session.engine.TZLocation) + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } else if rawValueType == core.IntType || rawValueType == core.Int64Type || + rawValueType == core.Int32Type { + hasAssigned = true + + t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation) + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } else { + if d, ok := vv.Interface().([]uint8); ok { + hasAssigned = true + t, err := session.byte2Time(col, d) + if err != nil { + session.engine.logger.Error("byte2Time error:", err.Error()) + hasAssigned = false + } else { + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } + } else if d, ok := vv.Interface().(string); ok { + hasAssigned = true + t, err := session.str2Time(col, d) + if err != nil { + session.engine.logger.Error("byte2Time error:", err.Error()) + hasAssigned = false + } else { + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } + } else { + return nil, fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface()) + } + } + } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { + // !! 增加支持sql.Scanner接口的结构,如sql.NullString + hasAssigned = true + if err := nulVal.Scan(vv.Interface()); err != nil { + session.engine.logger.Error("sql.Sanner error:", err.Error()) + hasAssigned = false + } + } else if col.SQLType.IsJson() { + if rawValueType.Kind() == reflect.String { + hasAssigned = true + x := reflect.New(fieldType) + if len([]byte(vv.String())) > 0 { + err := json.Unmarshal([]byte(vv.String()), x.Interface()) + if err != nil { + return nil, err + } + fieldValue.Set(x.Elem()) + } + } else if rawValueType.Kind() == reflect.Slice { + hasAssigned = true + x := reflect.New(fieldType) + if len(vv.Bytes()) > 0 { + err := json.Unmarshal(vv.Bytes(), x.Interface()) + if err != nil { + return nil, err + } + fieldValue.Set(x.Elem()) + } + } + } else if session.statement.UseCascade { + table, err := session.engine.autoMapType(*fieldValue) if err != nil { return nil, err } - if err = session.bytes2Value(col, fieldValue, data); err != nil { + hasAssigned = true + if len(table.PrimaryKeys) != 1 { + return nil, errors.New("unsupported non or composited primary key cascade") + } + var pk = make(core.PK, len(table.PrimaryKeys)) + pk[0], err = asKind(vv, rawValueType) + if err != nil { return nil, err } + + if !isPKZero(pk) { + // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch + // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne + // property to be fetched lazily + structInter := reflect.New(fieldValue.Type()) + has, err := session.ID(pk).NoCascade().get(structInter.Interface()) + if err != nil { + return nil, err + } + if has { + fieldValue.Set(structInter.Elem()) + } else { + return nil, errors.New("cascade obj is not exist") + } + } + } + case reflect.Ptr: + // !nashtsai! TODO merge duplicated codes above + switch fieldType { + // following types case matching ptr's native type, therefore assign ptr directly + case core.PtrStringType: + if rawValueType.Kind() == reflect.String { + x := vv.String() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrBoolType: + if rawValueType.Kind() == reflect.Bool { + x := vv.Bool() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrTimeType: + if rawValueType == core.PtrTimeType { + hasAssigned = true + var x = rawValue.Interface().(time.Time) + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrFloat64Type: + if rawValueType.Kind() == reflect.Float64 { + x := vv.Float() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrUint64Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint64(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrInt64Type: + if rawValueType.Kind() == reflect.Int64 { + x := vv.Int() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrFloat32Type: + if rawValueType.Kind() == reflect.Float64 { + var x = float32(vv.Float()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrIntType: + if rawValueType.Kind() == reflect.Int64 { + var x = int(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrInt32Type: + if rawValueType.Kind() == reflect.Int64 { + var x = int32(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrInt8Type: + if rawValueType.Kind() == reflect.Int64 { + var x = int8(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrInt16Type: + if rawValueType.Kind() == reflect.Int64 { + var x = int16(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrUintType: + if rawValueType.Kind() == reflect.Int64 { + var x = uint(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrUint32Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint32(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.Uint8Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint8(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.Uint16Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint16(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.Complex64Type: + var x complex64 + if len([]byte(vv.String())) > 0 { + err := json.Unmarshal([]byte(vv.String()), &x) + if err != nil { + return nil, err + } + fieldValue.Set(reflect.ValueOf(&x)) + } + hasAssigned = true + case core.Complex128Type: + var x complex128 + if len([]byte(vv.String())) > 0 { + err := json.Unmarshal([]byte(vv.String()), &x) + if err != nil { + return nil, err + } + fieldValue.Set(reflect.ValueOf(&x)) + } + hasAssigned = true + } // switch fieldType + } // switch fieldType.Kind() + + // !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value + if !hasAssigned { + data, err := value2Bytes(&rawValue) + if err != nil { + return nil, err + } + + if err = session.bytes2Value(col, fieldValue, data); err != nil { + return nil, err } } } @@ -828,15 +840,6 @@ func (session *Session) LastSQL() (string, []interface{}) { return session.lastSQL, session.lastSQLArgs } -// tbName get some table's table name -func (session *Session) tbNameNoSchema(table *core.Table) string { - if len(session.statement.AltTableName) > 0 { - return session.statement.AltTableName - } - - return table.Name -} - // Unscoped always disable struct tag "deleted" func (session *Session) Unscoped() *Session { session.statement.Unscoped() diff --git a/vendor/github.com/go-xorm/xorm/session_cols.go b/vendor/github.com/go-xorm/xorm/session_cols.go index 9972cb0ae4b..47d109c6cbb 100644 --- a/vendor/github.com/go-xorm/xorm/session_cols.go +++ b/vendor/github.com/go-xorm/xorm/session_cols.go @@ -4,6 +4,121 @@ package xorm +import ( + "reflect" + "strings" + "time" + + "github.com/go-xorm/core" +) + +type incrParam struct { + colName string + arg interface{} +} + +type decrParam struct { + colName string + arg interface{} +} + +type exprParam struct { + colName string + expr string +} + +type columnMap []string + +func (m columnMap) contain(colName string) bool { + if len(m) == 0 { + return false + } + + n := len(colName) + for _, mk := range m { + if len(mk) != n { + continue + } + if strings.EqualFold(mk, colName) { + return true + } + } + + return false +} + +func (m *columnMap) add(colName string) bool { + if m.contain(colName) { + return false + } + *m = append(*m, colName) + return true +} + +func setColumnInt(bean interface{}, col *core.Column, t int64) { + v, err := col.ValueOf(bean) + if err != nil { + return + } + if v.CanSet() { + switch v.Type().Kind() { + case reflect.Int, reflect.Int64, reflect.Int32: + v.SetInt(t) + case reflect.Uint, reflect.Uint64, reflect.Uint32: + v.SetUint(uint64(t)) + } + } +} + +func setColumnTime(bean interface{}, col *core.Column, t time.Time) { + v, err := col.ValueOf(bean) + if err != nil { + return + } + if v.CanSet() { + switch v.Type().Kind() { + case reflect.Struct: + v.Set(reflect.ValueOf(t).Convert(v.Type())) + case reflect.Int, reflect.Int64, reflect.Int32: + v.SetInt(t.Unix()) + case reflect.Uint, reflect.Uint64, reflect.Uint32: + v.SetUint(uint64(t.Unix())) + } + } +} + +func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) { + if len(m) == 0 { + return false, false + } + + n := len(col.Name) + + for mk := range m { + if len(mk) != n { + continue + } + if strings.EqualFold(mk, col.Name) { + return m[mk], true + } + } + + return false, false +} + +func col2NewCols(columns ...string) []string { + newColumns := make([]string, 0, len(columns)) + for _, col := range columns { + col = strings.Replace(col, "`", "", -1) + col = strings.Replace(col, `"`, "", -1) + ccols := strings.Split(col, ",") + for _, c := range ccols { + newColumns = append(newColumns, strings.TrimSpace(c)) + } + } + return newColumns +} + // Incr provides a query string like "count = count + 1" func (session *Session) Incr(column string, arg ...interface{}) *Session { session.statement.Incr(column, arg...) diff --git a/vendor/github.com/go-xorm/xorm/session_delete.go b/vendor/github.com/go-xorm/xorm/session_delete.go index 688b122ca6d..d9cf3ea9373 100644 --- a/vendor/github.com/go-xorm/xorm/session_delete.go +++ b/vendor/github.com/go-xorm/xorm/session_delete.go @@ -27,7 +27,7 @@ func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, return ErrCacheFailed } - cacher := session.engine.getCacher2(table) + cacher := session.engine.getCacher(tableName) pkColumns := table.PKColumns() ids, err := core.GetCacheSql(cacher, tableName, newsql, args) if err != nil { @@ -79,7 +79,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) { defer session.Close() } - if err := session.statement.setRefValue(rValue(bean)); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return 0, err } @@ -199,7 +199,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) { }) } - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { + if cacher := session.engine.getCacher(tableName); cacher != nil && session.statement.UseCache { session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) } diff --git a/vendor/github.com/go-xorm/xorm/session_exist.go b/vendor/github.com/go-xorm/xorm/session_exist.go index 049c1ddff14..74a660e852b 100644 --- a/vendor/github.com/go-xorm/xorm/session_exist.go +++ b/vendor/github.com/go-xorm/xorm/session_exist.go @@ -10,6 +10,7 @@ import ( "reflect" "github.com/go-xorm/builder" + "github.com/go-xorm/core" ) // Exist returns true if the record exist otherwise return false @@ -35,10 +36,18 @@ func (session *Session) Exist(bean ...interface{}) (bool, error) { return false, err } - sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) + if session.engine.dialect.DBType() == core.MSSQL { + sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s WHERE %s", tableName, condSQL) + } else { + sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) + } args = condArgs } else { - sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) + if session.engine.dialect.DBType() == core.MSSQL { + sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s", tableName) + } else { + sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) + } args = []interface{}{} } } else { @@ -48,7 +57,7 @@ func (session *Session) Exist(bean ...interface{}) (bool, error) { } if beanValue.Elem().Kind() == reflect.Struct { - if err := session.statement.setRefValue(beanValue.Elem()); err != nil { + if err := session.statement.setRefBean(bean[0]); err != nil { return false, err } } diff --git a/vendor/github.com/go-xorm/xorm/session_find.go b/vendor/github.com/go-xorm/xorm/session_find.go index f95dcfef2cb..b75f83479f1 100644 --- a/vendor/github.com/go-xorm/xorm/session_find.go +++ b/vendor/github.com/go-xorm/xorm/session_find.go @@ -29,6 +29,39 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) return session.find(rowsSlicePtr, condiBean...) } +// FindAndCount find the results and also return the counts +func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...interface{}) (int64, error) { + if session.isAutoClose { + defer session.Close() + } + + session.autoResetStatement = false + err := session.find(rowsSlicePtr, condiBean...) + if err != nil { + return 0, err + } + + sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) + if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map { + return 0, errors.New("needs a pointer to a slice or a map") + } + + sliceElementType := sliceValue.Type().Elem() + if sliceElementType.Kind() == reflect.Ptr { + sliceElementType = sliceElementType.Elem() + } + session.autoResetStatement = true + + if session.statement.selectStr != "" { + session.statement.selectStr = "" + } + if session.statement.OrderStr != "" { + session.statement.OrderStr = "" + } + + return session.Count(reflect.New(sliceElementType).Interface()) +} + func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) error { sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map { @@ -42,7 +75,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) if sliceElementType.Kind() == reflect.Ptr { if sliceElementType.Elem().Kind() == reflect.Struct { pv := reflect.New(sliceElementType.Elem()) - if err := session.statement.setRefValue(pv.Elem()); err != nil { + if err := session.statement.setRefValue(pv); err != nil { return err } } else { @@ -50,7 +83,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } } else if sliceElementType.Kind() == reflect.Struct { pv := reflect.New(sliceElementType) - if err := session.statement.setRefValue(pv.Elem()); err != nil { + if err := session.statement.setRefValue(pv); err != nil { return err } } else { @@ -102,7 +135,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) if session.statement.JoinStr == "" { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) + columnStr = session.engine.quoteColumns(session.statement.GroupByStr) } else { columnStr = session.statement.genColumnStr() } @@ -110,7 +143,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } else { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) + columnStr = session.engine.quoteColumns(session.statement.GroupByStr) } else { columnStr = "*" } @@ -128,7 +161,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } args = append(session.statement.joinArgs, condArgs...) - sqlStr, err = session.statement.genSelectSQL(columnStr, condSQL) + sqlStr, err = session.statement.genSelectSQL(columnStr, condSQL, true, true) if err != nil { return err } @@ -143,7 +176,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } if session.canCache() { - if cacher := session.engine.getCacher2(table); cacher != nil && + if cacher := session.engine.getCacher(table.Name); cacher != nil && !session.statement.IsDistinct && !session.statement.unscoped { err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...) @@ -288,6 +321,12 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in return ErrCacheFailed } + tableName := session.statement.TableName() + cacher := session.engine.getCacher(tableName) + if cacher == nil { + return nil + } + for _, filter := range session.engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.engine.dialect, session.statement.RefTable) } @@ -297,9 +336,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in return ErrCacheFailed } - tableName := session.statement.TableName() table := session.statement.RefTable - cacher := session.engine.getCacher2(table) ids, err := core.GetCacheSql(cacher, tableName, newsql, args) if err != nil { rows, err := session.queryRows(newsql, args...) diff --git a/vendor/github.com/go-xorm/xorm/session_get.go b/vendor/github.com/go-xorm/xorm/session_get.go index 8faf53c02c7..887a0aebdc8 100644 --- a/vendor/github.com/go-xorm/xorm/session_get.go +++ b/vendor/github.com/go-xorm/xorm/session_get.go @@ -5,7 +5,9 @@ package xorm import ( + "database/sql" "errors" + "fmt" "reflect" "strconv" @@ -30,7 +32,7 @@ func (session *Session) get(bean interface{}) (bool, error) { } if beanValue.Elem().Kind() == reflect.Struct { - if err := session.statement.setRefValue(beanValue.Elem()); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return false, err } } @@ -56,7 +58,7 @@ func (session *Session) get(bean interface{}) (bool, error) { table := session.statement.RefTable if session.canCache() && beanValue.Elem().Kind() == reflect.Struct { - if cacher := session.engine.getCacher2(table); cacher != nil && + if cacher := session.engine.getCacher(table.Name); cacher != nil && !session.statement.unscoped { has, err := session.cacheGet(bean, sqlStr, args...) if err != ErrCacheFailed { @@ -65,7 +67,28 @@ func (session *Session) get(bean interface{}) (bool, error) { } } - return session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...) + context := session.statement.context + if context != nil { + res := context.Get(fmt.Sprintf("%v-%v", sqlStr, args)) + if res != nil { + structValue := reflect.Indirect(reflect.ValueOf(bean)) + structValue.Set(reflect.Indirect(reflect.ValueOf(res))) + session.lastSQL = "" + session.lastSQLArgs = nil + return true, nil + } + } + + has, err := session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...) + if err != nil || !has { + return has, err + } + + if context != nil { + context.Put(fmt.Sprintf("%v-%v", sqlStr, args), bean) + } + + return true, nil } func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bean interface{}, sqlStr string, args ...interface{}) (bool, error) { @@ -76,9 +99,19 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bea defer rows.Close() if !rows.Next() { + if rows.Err() != nil { + return false, rows.Err() + } return false, nil } + switch bean.(type) { + case sql.NullInt64, sql.NullBool, sql.NullFloat64, sql.NullString: + return true, rows.Scan(&bean) + case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString: + return true, rows.Scan(bean) + } + switch beanKind { case reflect.Struct: fields, err := rows.Columns() @@ -126,8 +159,9 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf return false, ErrCacheFailed } - cacher := session.engine.getCacher2(session.statement.RefTable) tableName := session.statement.TableName() + cacher := session.engine.getCacher(tableName) + session.engine.logger.Debug("[cacheGet] find sql:", newsql, args) table := session.statement.RefTable ids, err := core.GetCacheSql(cacher, tableName, newsql, args) diff --git a/vendor/github.com/go-xorm/xorm/session_insert.go b/vendor/github.com/go-xorm/xorm/session_insert.go index 129ee23098a..2ea58fdaf95 100644 --- a/vendor/github.com/go-xorm/xorm/session_insert.go +++ b/vendor/github.com/go-xorm/xorm/session_insert.go @@ -66,11 +66,12 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error return 0, errors.New("could not insert a empty slice") } - if err := session.statement.setRefValue(reflect.ValueOf(sliceValue.Index(0).Interface())); err != nil { + if err := session.statement.setRefBean(sliceValue.Index(0).Interface()); err != nil { return 0, err } - if len(session.statement.TableName()) <= 0 { + tableName := session.statement.TableName() + if len(tableName) <= 0 { return 0, ErrTableNotFound } @@ -115,15 +116,11 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error if col.IsDeleted { continue } - if session.statement.ColumnStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); !ok { - continue - } + if session.statement.omitColumnMap.contain(col.Name) { + continue } - if session.statement.OmitStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); ok { - continue - } + if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) { + continue } if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { val, t := session.engine.nowTime(col) @@ -170,15 +167,11 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error if col.IsDeleted { continue } - if session.statement.ColumnStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); !ok { - continue - } + if session.statement.omitColumnMap.contain(col.Name) { + continue } - if session.statement.OmitStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); ok { - continue - } + if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) { + continue } if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { val, t := session.engine.nowTime(col) @@ -211,38 +204,33 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error } cleanupProcessorsClosures(&session.beforeClosures) - var sql = "INSERT INTO %s (%v%v%v) VALUES (%v)" - var statement string - var tableName = session.statement.TableName() + var sql string if session.engine.dialect.DBType() == core.ORACLE { - sql = "INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL" temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (", session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), session.engine.QuoteStr()) - statement = fmt.Sprintf(sql, + sql = fmt.Sprintf("INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL", session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), session.engine.QuoteStr(), strings.Join(colMultiPlaces, temp)) } else { - statement = fmt.Sprintf(sql, + sql = fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), session.engine.QuoteStr(), strings.Join(colMultiPlaces, "),(")) } - res, err := session.exec(statement, args...) + res, err := session.exec(sql, args...) if err != nil { return 0, err } - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(table, tableName) - } + session.cacheInsert(tableName) lenAfterClosures := len(session.afterClosures) for i := 0; i < size; i++ { @@ -298,7 +286,7 @@ func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) { } func (session *Session) innerInsert(bean interface{}) (int64, error) { - if err := session.statement.setRefValue(rValue(bean)); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return 0, err } if len(session.statement.TableName()) <= 0 { @@ -316,8 +304,8 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { if processor, ok := interface{}(bean).(BeforeInsertProcessor); ok { processor.BeforeInsert() } - // -- - colNames, args, err := genCols(session.statement.RefTable, session, bean, false, false) + + colNames, args, err := session.genInsertColumns(bean) if err != nil { return 0, err } @@ -402,9 +390,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { defer handleAfterInsertProcessorFunc(bean) - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(table, tableName) - } + session.cacheInsert(tableName) if table.Version != "" && session.statement.checkVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -447,9 +433,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { } defer handleAfterInsertProcessorFunc(bean) - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(table, tableName) - } + session.cacheInsert(tableName) if table.Version != "" && session.statement.checkVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -490,9 +474,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { defer handleAfterInsertProcessorFunc(bean) - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(table, tableName) - } + session.cacheInsert(tableName) if table.Version != "" && session.statement.checkVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -539,16 +521,104 @@ func (session *Session) InsertOne(bean interface{}) (int64, error) { return session.innerInsert(bean) } -func (session *Session) cacheInsert(table *core.Table, tables ...string) error { - if table == nil { - return ErrCacheFailed +func (session *Session) cacheInsert(table string) error { + if !session.statement.UseCache { + return nil } - - cacher := session.engine.getCacher2(table) - for _, t := range tables { - session.engine.logger.Debug("[cache] clear sql:", t) - cacher.ClearIds(t) + cacher := session.engine.getCacher(table) + if cacher == nil { + return nil } - + session.engine.logger.Debug("[cache] clear sql:", table) + cacher.ClearIds(table) return nil } + +// genInsertColumns generates insert needed columns +func (session *Session) genInsertColumns(bean interface{}) ([]string, []interface{}, error) { + table := session.statement.RefTable + colNames := make([]string, 0, len(table.ColumnsSeq())) + args := make([]interface{}, 0, len(table.ColumnsSeq())) + + for _, col := range table.Columns() { + if col.MapType == core.ONLYFROMDB { + continue + } + + if col.IsDeleted { + continue + } + + if session.statement.omitColumnMap.contain(col.Name) { + continue + } + + if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) { + continue + } + + if _, ok := session.statement.incrColumns[col.Name]; ok { + continue + } else if _, ok := session.statement.decrColumns[col.Name]; ok { + continue + } + + fieldValuePtr, err := col.ValueOf(bean) + if err != nil { + return nil, nil, err + } + fieldValue := *fieldValuePtr + + if col.IsAutoIncrement { + switch fieldValue.Type().Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: + if fieldValue.Int() == 0 { + continue + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: + if fieldValue.Uint() == 0 { + continue + } + case reflect.String: + if len(fieldValue.String()) == 0 { + continue + } + case reflect.Ptr: + if fieldValue.Pointer() == 0 { + continue + } + } + } + + // !evalphobia! set fieldValue as nil when column is nullable and zero-value + if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok { + if col.Nullable && isZero(fieldValue.Interface()) { + var nilValue *int + fieldValue = reflect.ValueOf(nilValue) + } + } + + if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { + // if time is non-empty, then set to auto time + val, t := session.engine.nowTime(col) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } else if col.IsVersion && session.statement.checkVersion { + args = append(args, 1) + } else { + arg, err := session.value2Interface(col, fieldValue) + if err != nil { + return colNames, args, err + } + args = append(args, arg) + } + + colNames = append(colNames, col.Name) + } + return colNames, args, nil +} diff --git a/vendor/github.com/go-xorm/xorm/session_query.go b/vendor/github.com/go-xorm/xorm/session_query.go index 5b4e0dc45d0..6d597cc4592 100644 --- a/vendor/github.com/go-xorm/xorm/session_query.go +++ b/vendor/github.com/go-xorm/xorm/session_query.go @@ -17,7 +17,7 @@ import ( func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interface{}, error) { if len(sqlorArgs) > 0 { - return sqlorArgs[0].(string), sqlorArgs[1:], nil + return convertSQLOrArgs(sqlorArgs...) } if session.statement.RawSQL != "" { @@ -35,7 +35,7 @@ func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interfa if session.statement.JoinStr == "" { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) + columnStr = session.engine.quoteColumns(session.statement.GroupByStr) } else { columnStr = session.statement.genColumnStr() } @@ -43,7 +43,7 @@ func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interfa } else { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) + columnStr = session.engine.quoteColumns(session.statement.GroupByStr) } else { columnStr = "*" } @@ -54,13 +54,17 @@ func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interfa } } + if err := session.statement.processIDParam(); err != nil { + return "", nil, err + } + condSQL, condArgs, err := builder.ToSQL(session.statement.cond) if err != nil { return "", nil, err } args := append(session.statement.joinArgs, condArgs...) - sqlStr, err := session.statement.genSelectSQL(columnStr, condSQL) + sqlStr, err := session.statement.genSelectSQL(columnStr, condSQL, true, true) if err != nil { return "", nil, err } @@ -162,6 +166,34 @@ func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, return result, nil } +func row2sliceStr(rows *core.Rows, fields []string) (results []string, err error) { + result := make([]string, 0, len(fields)) + scanResultContainers := make([]interface{}, len(fields)) + for i := 0; i < len(fields); i++ { + var scanResultContainer interface{} + scanResultContainers[i] = &scanResultContainer + } + if err := rows.Scan(scanResultContainers...); err != nil { + return nil, err + } + + for i := 0; i < len(fields); i++ { + rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[i])) + // if row is null then as empty string + if rawValue.Interface() == nil { + result = append(result, "") + continue + } + + if data, err := value2String(&rawValue); err == nil { + result = append(result, data) + } else { + return nil, err + } + } + return result, nil +} + func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) { fields, err := rows.Columns() if err != nil { @@ -178,6 +210,22 @@ func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) return resultsSlice, nil } +func rows2SliceString(rows *core.Rows) (resultsSlice [][]string, err error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + for rows.Next() { + record, err := row2sliceStr(rows, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, record) + } + + return resultsSlice, nil +} + // QueryString runs a raw sql and return records as []map[string]string func (session *Session) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) { if session.isAutoClose { @@ -198,6 +246,26 @@ func (session *Session) QueryString(sqlorArgs ...interface{}) ([]map[string]stri return rows2Strings(rows) } +// QuerySliceString runs a raw sql and return records as [][]string +func (session *Session) QuerySliceString(sqlorArgs ...interface{}) ([][]string, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.genQuerySQL(sqlorArgs...) + if err != nil { + return nil, err + } + + rows, err := session.queryRows(sqlStr, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + return rows2SliceString(rows) +} + func row2mapInterface(rows *core.Rows, fields []string) (resultsMap map[string]interface{}, err error) { resultsMap = make(map[string]interface{}, len(fields)) scanResultContainers := make([]interface{}, len(fields)) diff --git a/vendor/github.com/go-xorm/xorm/session_raw.go b/vendor/github.com/go-xorm/xorm/session_raw.go index 69bf9b3c6bf..47823d67063 100644 --- a/vendor/github.com/go-xorm/xorm/session_raw.go +++ b/vendor/github.com/go-xorm/xorm/session_raw.go @@ -9,6 +9,7 @@ import ( "reflect" "time" + "github.com/go-xorm/builder" "github.com/go-xorm/core" ) @@ -193,11 +194,34 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er return session.DB().Exec(sqlStr, args...) } +func convertSQLOrArgs(sqlorArgs ...interface{}) (string, []interface{}, error) { + switch sqlorArgs[0].(type) { + case string: + return sqlorArgs[0].(string), sqlorArgs[1:], nil + case *builder.Builder: + return sqlorArgs[0].(*builder.Builder).ToSQL() + case builder.Builder: + bd := sqlorArgs[0].(builder.Builder) + return bd.ToSQL() + } + + return "", nil, ErrUnSupportedType +} + // Exec raw sql -func (session *Session) Exec(sqlStr string, args ...interface{}) (sql.Result, error) { +func (session *Session) Exec(sqlorArgs ...interface{}) (sql.Result, error) { if session.isAutoClose { defer session.Close() } + if len(sqlorArgs) == 0 { + return nil, ErrUnSupportedType + } + + sqlStr, args, err := convertSQLOrArgs(sqlorArgs...) + if err != nil { + return nil, err + } + return session.exec(sqlStr, args...) } diff --git a/vendor/github.com/go-xorm/xorm/session_schema.go b/vendor/github.com/go-xorm/xorm/session_schema.go index a2708b736c0..369ec72a4d8 100644 --- a/vendor/github.com/go-xorm/xorm/session_schema.go +++ b/vendor/github.com/go-xorm/xorm/session_schema.go @@ -6,9 +6,7 @@ package xorm import ( "database/sql" - "errors" "fmt" - "reflect" "strings" "github.com/go-xorm/core" @@ -34,8 +32,7 @@ func (session *Session) CreateTable(bean interface{}) error { } func (session *Session) createTable(bean interface{}) error { - v := rValue(bean) - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } @@ -54,8 +51,7 @@ func (session *Session) CreateIndexes(bean interface{}) error { } func (session *Session) createIndexes(bean interface{}) error { - v := rValue(bean) - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } @@ -78,8 +74,7 @@ func (session *Session) CreateUniques(bean interface{}) error { } func (session *Session) createUniques(bean interface{}) error { - v := rValue(bean) - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } @@ -103,8 +98,7 @@ func (session *Session) DropIndexes(bean interface{}) error { } func (session *Session) dropIndexes(bean interface{}) error { - v := rValue(bean) - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } @@ -128,11 +122,7 @@ func (session *Session) DropTable(beanOrTableName interface{}) error { } func (session *Session) dropTable(beanOrTableName interface{}) error { - tableName, err := session.engine.tableName(beanOrTableName) - if err != nil { - return err - } - + tableName := session.engine.TableName(beanOrTableName) var needDrop = true if !session.engine.dialect.SupportDropIfExists() { sqlStr, args := session.engine.dialect.TableCheckSql(tableName) @@ -144,8 +134,8 @@ func (session *Session) dropTable(beanOrTableName interface{}) error { } if needDrop { - sqlStr := session.engine.Dialect().DropTableSql(tableName) - _, err = session.exec(sqlStr) + sqlStr := session.engine.Dialect().DropTableSql(session.engine.TableName(tableName, true)) + _, err := session.exec(sqlStr) return err } return nil @@ -157,10 +147,7 @@ func (session *Session) IsTableExist(beanOrTableName interface{}) (bool, error) defer session.Close() } - tableName, err := session.engine.tableName(beanOrTableName) - if err != nil { - return false, err - } + tableName := session.engine.TableName(beanOrTableName) return session.isTableExist(tableName) } @@ -173,24 +160,15 @@ func (session *Session) isTableExist(tableName string) (bool, error) { // IsTableEmpty if table have any records func (session *Session) IsTableEmpty(bean interface{}) (bool, error) { - v := rValue(bean) - t := v.Type() - - if t.Kind() == reflect.String { - if session.isAutoClose { - defer session.Close() - } - return session.isTableEmpty(bean.(string)) - } else if t.Kind() == reflect.Struct { - rows, err := session.Count(bean) - return rows == 0, err + if session.isAutoClose { + defer session.Close() } - return false, errors.New("bean should be a struct or struct's point") + return session.isTableEmpty(session.engine.TableName(bean)) } func (session *Session) isTableEmpty(tableName string) (bool, error) { var total int64 - sqlStr := fmt.Sprintf("select count(*) from %s", session.engine.Quote(tableName)) + sqlStr := fmt.Sprintf("select count(*) from %s", session.engine.Quote(session.engine.TableName(tableName, true))) err := session.queryRow(sqlStr).Scan(&total) if err != nil { if err == sql.ErrNoRows { @@ -255,6 +233,12 @@ func (session *Session) Sync2(beans ...interface{}) error { return err } + session.autoResetStatement = false + defer func() { + session.autoResetStatement = true + session.resetStatement() + }() + var structTables []*core.Table for _, bean := range beans { @@ -264,7 +248,8 @@ func (session *Session) Sync2(beans ...interface{}) error { return err } structTables = append(structTables, table) - var tbName = session.tbNameNoSchema(table) + tbName := engine.TableName(bean) + tbNameWithSchema := engine.TableName(tbName, true) var oriTable *core.Table for _, tb := range tables { @@ -309,32 +294,32 @@ func (session *Session) Sync2(beans ...interface{}) error { if engine.dialect.DBType() == core.MYSQL || engine.dialect.DBType() == core.POSTGRES { engine.logger.Infof("Table %s column %s change type from %s to %s\n", - tbName, col.Name, curType, expectedType) - _, err = session.exec(engine.dialect.ModifyColumnSql(table.Name, col)) + tbNameWithSchema, col.Name, curType, expectedType) + _, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) } else { engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n", - tbName, col.Name, curType, expectedType) + tbNameWithSchema, col.Name, curType, expectedType) } } else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) { if engine.dialect.DBType() == core.MYSQL { if oriCol.Length < col.Length { engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", - tbName, col.Name, oriCol.Length, col.Length) - _, err = session.exec(engine.dialect.ModifyColumnSql(table.Name, col)) + tbNameWithSchema, col.Name, oriCol.Length, col.Length) + _, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) } } } else { if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') { engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s", - tbName, col.Name, curType, expectedType) + tbNameWithSchema, col.Name, curType, expectedType) } } } else if expectedType == core.Varchar { if engine.dialect.DBType() == core.MYSQL { if oriCol.Length < col.Length { engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", - tbName, col.Name, oriCol.Length, col.Length) - _, err = session.exec(engine.dialect.ModifyColumnSql(table.Name, col)) + tbNameWithSchema, col.Name, oriCol.Length, col.Length) + _, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) } } } @@ -348,7 +333,7 @@ func (session *Session) Sync2(beans ...interface{}) error { } } else { session.statement.RefTable = table - session.statement.tableName = tbName + session.statement.tableName = tbNameWithSchema err = session.addColumn(col.Name) } if err != nil { @@ -371,7 +356,7 @@ func (session *Session) Sync2(beans ...interface{}) error { if oriIndex != nil { if oriIndex.Type != index.Type { - sql := engine.dialect.DropIndexSql(tbName, oriIndex) + sql := engine.dialect.DropIndexSql(tbNameWithSchema, oriIndex) _, err = session.exec(sql) if err != nil { return err @@ -387,7 +372,7 @@ func (session *Session) Sync2(beans ...interface{}) error { for name2, index2 := range oriTable.Indexes { if _, ok := foundIndexNames[name2]; !ok { - sql := engine.dialect.DropIndexSql(tbName, index2) + sql := engine.dialect.DropIndexSql(tbNameWithSchema, index2) _, err = session.exec(sql) if err != nil { return err @@ -398,12 +383,12 @@ func (session *Session) Sync2(beans ...interface{}) error { for name, index := range addedNames { if index.Type == core.UniqueType { session.statement.RefTable = table - session.statement.tableName = tbName - err = session.addUnique(tbName, name) + session.statement.tableName = tbNameWithSchema + err = session.addUnique(tbNameWithSchema, name) } else if index.Type == core.IndexType { session.statement.RefTable = table - session.statement.tableName = tbName - err = session.addIndex(tbName, name) + session.statement.tableName = tbNameWithSchema + err = session.addIndex(tbNameWithSchema, name) } if err != nil { return err @@ -428,7 +413,7 @@ func (session *Session) Sync2(beans ...interface{}) error { for _, colName := range table.ColumnsSeq() { if oriTable.GetColumn(colName) == nil { - engine.logger.Warnf("Table %s has column %s but struct has not related field", table.Name, colName) + engine.logger.Warnf("Table %s has column %s but struct has not related field", engine.TableName(table.Name, true), colName) } } } diff --git a/vendor/github.com/go-xorm/xorm/session_tx.go b/vendor/github.com/go-xorm/xorm/session_tx.go index 84d2f7f9dcf..c8d759a31ac 100644 --- a/vendor/github.com/go-xorm/xorm/session_tx.go +++ b/vendor/github.com/go-xorm/xorm/session_tx.go @@ -24,6 +24,7 @@ func (session *Session) Rollback() error { if !session.isAutoCommit && !session.isCommitedOrRollbacked { session.saveLastSQL(session.engine.dialect.RollBackStr()) session.isCommitedOrRollbacked = true + session.isAutoCommit = true return session.tx.Rollback() } return nil @@ -34,6 +35,7 @@ func (session *Session) Commit() error { if !session.isAutoCommit && !session.isCommitedOrRollbacked { session.saveLastSQL("COMMIT") session.isCommitedOrRollbacked = true + session.isAutoCommit = true var err error if err = session.tx.Commit(); err == nil { // handle processors after tx committed diff --git a/vendor/github.com/go-xorm/xorm/session_update.go b/vendor/github.com/go-xorm/xorm/session_update.go index f558745667f..42dfaacd0c9 100644 --- a/vendor/github.com/go-xorm/xorm/session_update.go +++ b/vendor/github.com/go-xorm/xorm/session_update.go @@ -40,7 +40,7 @@ func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string, } } - cacher := session.engine.getCacher2(table) + cacher := session.engine.getCacher(tableName) session.engine.logger.Debug("[cacheUpdate] get cache sql", newsql, args[nStart:]) ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:]) if err != nil { @@ -167,7 +167,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 var isMap = t.Kind() == reflect.Map var isStruct = t.Kind() == reflect.Struct if isStruct { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return 0, err } @@ -176,12 +176,10 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } if session.statement.ColumnStr == "" { - colNames, args = buildUpdates(session.engine, session.statement.RefTable, bean, false, false, - false, false, session.statement.allUseBool, session.statement.useAllCols, - session.statement.mustColumnMap, session.statement.nullableMap, - session.statement.columnMap, true, session.statement.unscoped) + colNames, args = session.statement.buildUpdates(bean, false, false, + false, false, true) } else { - colNames, args, err = genCols(session.statement.RefTable, session, bean, true, true) + colNames, args, err = session.genUpdateColumns(bean) if err != nil { return 0, err } @@ -202,7 +200,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 table := session.statement.RefTable if session.statement.UseAutoTime && table != nil && table.Updated != "" { - if _, ok := session.statement.columnMap[strings.ToLower(table.Updated)]; !ok { + if !session.statement.columnMap.contain(table.Updated) && + !session.statement.omitColumnMap.contain(table.Updated) { colNames = append(colNames, session.engine.Quote(table.Updated)+" = ?") col := table.UpdatedColumn() val, t := session.engine.nowTime(col) @@ -362,12 +361,11 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } - if table != nil { - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - //session.cacheUpdate(table, tableName, sqlStr, args...) - cacher.ClearIds(tableName) - cacher.ClearBeans(tableName) - } + if cacher := session.engine.getCacher(tableName); cacher != nil && session.statement.UseCache { + //session.cacheUpdate(table, tableName, sqlStr, args...) + session.engine.logger.Debug("[cacheUpdate] clear table ", tableName) + cacher.ClearIds(tableName) + cacher.ClearBeans(tableName) } // handle after update processors @@ -402,3 +400,92 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 return res.RowsAffected() } + +func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interface{}, error) { + table := session.statement.RefTable + colNames := make([]string, 0, len(table.ColumnsSeq())) + args := make([]interface{}, 0, len(table.ColumnsSeq())) + + for _, col := range table.Columns() { + if !col.IsVersion && !col.IsCreated && !col.IsUpdated { + if session.statement.omitColumnMap.contain(col.Name) { + continue + } + } + if col.MapType == core.ONLYFROMDB { + continue + } + + fieldValuePtr, err := col.ValueOf(bean) + if err != nil { + return nil, nil, err + } + fieldValue := *fieldValuePtr + + if col.IsAutoIncrement { + switch fieldValue.Type().Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: + if fieldValue.Int() == 0 { + continue + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: + if fieldValue.Uint() == 0 { + continue + } + case reflect.String: + if len(fieldValue.String()) == 0 { + continue + } + case reflect.Ptr: + if fieldValue.Pointer() == 0 { + continue + } + } + } + + if (col.IsDeleted && !session.statement.unscoped) || col.IsCreated { + continue + } + + if len(session.statement.columnMap) > 0 { + if !session.statement.columnMap.contain(col.Name) { + continue + } else if _, ok := session.statement.incrColumns[col.Name]; ok { + continue + } else if _, ok := session.statement.decrColumns[col.Name]; ok { + continue + } + } + + // !evalphobia! set fieldValue as nil when column is nullable and zero-value + if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok { + if col.Nullable && isZero(fieldValue.Interface()) { + var nilValue *int + fieldValue = reflect.ValueOf(nilValue) + } + } + + if col.IsUpdated && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { + // if time is non-empty, then set to auto time + val, t := session.engine.nowTime(col) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } else if col.IsVersion && session.statement.checkVersion { + args = append(args, 1) + } else { + arg, err := session.value2Interface(col, fieldValue) + if err != nil { + return colNames, args, err + } + args = append(args, arg) + } + + colNames = append(colNames, session.engine.Quote(col.Name)+" = ?") + } + return colNames, args, nil +} diff --git a/vendor/github.com/go-xorm/xorm/statement.go b/vendor/github.com/go-xorm/xorm/statement.go index 6400425b20e..a7f7010ad2b 100644 --- a/vendor/github.com/go-xorm/xorm/statement.go +++ b/vendor/github.com/go-xorm/xorm/statement.go @@ -5,7 +5,6 @@ package xorm import ( - "bytes" "database/sql/driver" "encoding/json" "errors" @@ -18,21 +17,6 @@ import ( "github.com/go-xorm/core" ) -type incrParam struct { - colName string - arg interface{} -} - -type decrParam struct { - colName string - arg interface{} -} - -type exprParam struct { - colName string - expr string -} - // Statement save all the sql info for executing SQL type Statement struct { RefTable *core.Table @@ -47,7 +31,6 @@ type Statement struct { HavingStr string ColumnStr string selectStr string - columnMap map[string]bool useAllCols bool OmitStr string AltTableName string @@ -67,6 +50,8 @@ type Statement struct { allUseBool bool checkVersion bool unscoped bool + columnMap columnMap + omitColumnMap columnMap mustColumnMap map[string]bool nullableMap map[string]bool incrColumns map[string]incrParam @@ -74,6 +59,7 @@ type Statement struct { exprColumns map[string]exprParam cond builder.Cond bufferSize int + context ContextCache } // Init reset all the statement's fields @@ -89,7 +75,8 @@ func (statement *Statement) Init() { statement.HavingStr = "" statement.ColumnStr = "" statement.OmitStr = "" - statement.columnMap = make(map[string]bool) + statement.columnMap = columnMap{} + statement.omitColumnMap = columnMap{} statement.AltTableName = "" statement.tableName = "" statement.idParam = nil @@ -113,6 +100,7 @@ func (statement *Statement) Init() { statement.exprColumns = make(map[string]exprParam) statement.cond = builder.NewCond() statement.bufferSize = 0 + statement.context = nil } // NoAutoCondition if you do not want convert bean's field as query condition, then use this function @@ -221,34 +209,33 @@ func (statement *Statement) setRefValue(v reflect.Value) error { if err != nil { return err } - statement.tableName = statement.Engine.tbName(v) + statement.tableName = statement.Engine.TableName(v, true) return nil } -// Table tempororily set table name, the parameter could be a string or a pointer of struct -func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { - v := rValue(tableNameOrBean) - t := v.Type() - if t.Kind() == reflect.String { - statement.AltTableName = tableNameOrBean.(string) - } else if t.Kind() == reflect.Struct { - var err error - statement.RefTable, err = statement.Engine.autoMapType(v) - if err != nil { - statement.Engine.logger.Error(err) - return statement - } - statement.AltTableName = statement.Engine.tbName(v) +func (statement *Statement) setRefBean(bean interface{}) error { + var err error + statement.RefTable, err = statement.Engine.autoMapType(rValue(bean)) + if err != nil { + return err } - return statement + statement.tableName = statement.Engine.TableName(bean, true) + return nil } // Auto generating update columnes and values according a struct -func buildUpdates(engine *Engine, table *core.Table, bean interface{}, - includeVersion bool, includeUpdated bool, includeNil bool, - includeAutoIncr bool, allUseBool bool, useAllCols bool, - mustColumnMap map[string]bool, nullableMap map[string]bool, - columnMap map[string]bool, update, unscoped bool) ([]string, []interface{}) { +func (statement *Statement) buildUpdates(bean interface{}, + includeVersion, includeUpdated, includeNil, + includeAutoIncr, update bool) ([]string, []interface{}) { + engine := statement.Engine + table := statement.RefTable + allUseBool := statement.allUseBool + useAllCols := statement.useAllCols + mustColumnMap := statement.mustColumnMap + nullableMap := statement.nullableMap + columnMap := statement.columnMap + omitColumnMap := statement.omitColumnMap + unscoped := statement.unscoped var colNames = make([]string, 0) var args = make([]interface{}, 0) @@ -268,7 +255,14 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if col.IsDeleted && !unscoped { continue } - if use, ok := columnMap[strings.ToLower(col.Name)]; ok && !use { + if omitColumnMap.contain(col.Name) { + continue + } + if len(columnMap) > 0 && !columnMap.contain(col.Name) { + continue + } + + if col.MapType == core.ONLYFROMDB { continue } @@ -604,17 +598,10 @@ func (statement *Statement) col2NewColsWithQuote(columns ...string) []string { } func (statement *Statement) colmap2NewColsWithQuote() []string { - newColumns := make([]string, 0, len(statement.columnMap)) - for col := range statement.columnMap { - fields := strings.Split(strings.TrimSpace(col), ".") - if len(fields) == 1 { - newColumns = append(newColumns, statement.Engine.quote(fields[0])) - } else if len(fields) == 2 { - newColumns = append(newColumns, statement.Engine.quote(fields[0])+"."+ - statement.Engine.quote(fields[1])) - } else { - panic(errors.New("unwanted colnames")) - } + newColumns := make([]string, len(statement.columnMap), len(statement.columnMap)) + copy(newColumns, statement.columnMap) + for i := 0; i < len(statement.columnMap); i++ { + newColumns[i] = statement.Engine.Quote(newColumns[i]) } return newColumns } @@ -642,10 +629,11 @@ func (statement *Statement) Select(str string) *Statement { func (statement *Statement) Cols(columns ...string) *Statement { cols := col2NewCols(columns...) for _, nc := range cols { - statement.columnMap[strings.ToLower(nc)] = true + statement.columnMap.add(nc) } newColumns := statement.colmap2NewColsWithQuote() + statement.ColumnStr = strings.Join(newColumns, ", ") statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.quote("*"), "*", -1) return statement @@ -680,7 +668,7 @@ func (statement *Statement) UseBool(columns ...string) *Statement { func (statement *Statement) Omit(columns ...string) { newColumns := col2NewCols(columns...) for _, nc := range newColumns { - statement.columnMap[strings.ToLower(nc)] = false + statement.omitColumnMap = append(statement.omitColumnMap, nc) } statement.OmitStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", "))) } @@ -719,10 +707,9 @@ func (statement *Statement) OrderBy(order string) *Statement { // Desc generate `ORDER BY xx DESC` func (statement *Statement) Desc(colNames ...string) *Statement { - var buf bytes.Buffer - fmt.Fprintf(&buf, statement.OrderStr) + var buf builder.StringBuilder if len(statement.OrderStr) > 0 { - fmt.Fprint(&buf, ", ") + fmt.Fprint(&buf, statement.OrderStr, ", ") } newColNames := statement.col2NewColsWithQuote(colNames...) fmt.Fprintf(&buf, "%v DESC", strings.Join(newColNames, " DESC, ")) @@ -732,10 +719,9 @@ func (statement *Statement) Desc(colNames ...string) *Statement { // Asc provide asc order by query condition, the input parameters are columns. func (statement *Statement) Asc(colNames ...string) *Statement { - var buf bytes.Buffer - fmt.Fprintf(&buf, statement.OrderStr) + var buf builder.StringBuilder if len(statement.OrderStr) > 0 { - fmt.Fprint(&buf, ", ") + fmt.Fprint(&buf, statement.OrderStr, ", ") } newColNames := statement.col2NewColsWithQuote(colNames...) fmt.Fprintf(&buf, "%v ASC", strings.Join(newColNames, " ASC, ")) @@ -743,48 +729,35 @@ func (statement *Statement) Asc(colNames ...string) *Statement { return statement } +// Table tempororily set table name, the parameter could be a string or a pointer of struct +func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { + v := rValue(tableNameOrBean) + t := v.Type() + if t.Kind() == reflect.Struct { + var err error + statement.RefTable, err = statement.Engine.autoMapType(v) + if err != nil { + statement.Engine.logger.Error(err) + return statement + } + } + + statement.AltTableName = statement.Engine.TableName(tableNameOrBean, true) + return statement +} + // Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement { - var buf bytes.Buffer + var buf builder.StringBuilder if len(statement.JoinStr) > 0 { fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP) } else { fmt.Fprintf(&buf, "%v JOIN ", joinOP) } - switch tablename.(type) { - case []string: - t := tablename.([]string) - if len(t) > 1 { - fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(t[0]), statement.Engine.Quote(t[1])) - } else if len(t) == 1 { - fmt.Fprintf(&buf, statement.Engine.Quote(t[0])) - } - case []interface{}: - t := tablename.([]interface{}) - l := len(t) - var table string - if l > 0 { - f := t[0] - v := rValue(f) - t := v.Type() - if t.Kind() == reflect.String { - table = f.(string) - } else if t.Kind() == reflect.Struct { - table = statement.Engine.tbName(v) - } - } - if l > 1 { - fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(table), - statement.Engine.Quote(fmt.Sprintf("%v", t[1]))) - } else if l == 1 { - fmt.Fprintf(&buf, statement.Engine.Quote(table)) - } - default: - fmt.Fprintf(&buf, statement.Engine.Quote(fmt.Sprintf("%v", tablename))) - } + tbName := statement.Engine.TableName(tablename, true) - fmt.Fprintf(&buf, " ON %v", condition) + fmt.Fprintf(&buf, "%s ON %v", tbName, condition) statement.JoinStr = buf.String() statement.joinArgs = append(statement.joinArgs, args...) return statement @@ -809,18 +782,20 @@ func (statement *Statement) Unscoped() *Statement { } func (statement *Statement) genColumnStr() string { - var buf bytes.Buffer if statement.RefTable == nil { return "" } + var buf builder.StringBuilder columns := statement.RefTable.Columns() for _, col := range columns { - if statement.OmitStr != "" { - if _, ok := getFlagForColumn(statement.columnMap, col); ok { - continue - } + if statement.omitColumnMap.contain(col.Name) { + continue + } + + if len(statement.columnMap) > 0 && !statement.columnMap.contain(col.Name) { + continue } if col.MapType == core.ONLYTODB { @@ -831,10 +806,6 @@ func (statement *Statement) genColumnStr() string { buf.WriteString(", ") } - if col.IsPrimaryKey && statement.Engine.Dialect().DBType() == "ql" { - buf.WriteString("id() AS ") - } - if statement.JoinStr != "" { if statement.TableAlias != "" { buf.WriteString(statement.TableAlias) @@ -859,11 +830,13 @@ func (statement *Statement) genCreateTableSQL() string { func (statement *Statement) genIndexSQL() []string { var sqls []string tbName := statement.TableName() - quote := statement.Engine.Quote - for idxName, index := range statement.RefTable.Indexes { + for _, index := range statement.RefTable.Indexes { if index.Type == core.IndexType { - sql := fmt.Sprintf("CREATE INDEX %v ON %v (%v);", quote(indexName(tbName, idxName)), - quote(tbName), quote(strings.Join(index.Cols, quote(",")))) + sql := statement.Engine.dialect.CreateIndexSql(tbName, index) + /*idxTBName := strings.Replace(tbName, ".", "_", -1) + idxTBName = strings.Replace(idxTBName, `"`, "", -1) + sql := fmt.Sprintf("CREATE INDEX %v ON %v (%v);", quote(indexName(idxTBName, idxName)), + quote(tbName), quote(strings.Join(index.Cols, quote(","))))*/ sqls = append(sqls, sql) } } @@ -889,16 +862,18 @@ func (statement *Statement) genUniqueSQL() []string { func (statement *Statement) genDelIndexSQL() []string { var sqls []string tbName := statement.TableName() + idxPrefixName := strings.Replace(tbName, `"`, "", -1) + idxPrefixName = strings.Replace(idxPrefixName, `.`, "_", -1) for idxName, index := range statement.RefTable.Indexes { var rIdxName string if index.Type == core.UniqueType { - rIdxName = uniqueName(tbName, idxName) + rIdxName = uniqueName(idxPrefixName, idxName) } else if index.Type == core.IndexType { - rIdxName = indexName(tbName, idxName) + rIdxName = indexName(idxPrefixName, idxName) } - sql := fmt.Sprintf("DROP INDEX %v", statement.Engine.Quote(rIdxName)) + sql := fmt.Sprintf("DROP INDEX %v", statement.Engine.Quote(statement.Engine.TableName(rIdxName, true))) if statement.Engine.dialect.IndexOnTable() { - sql += fmt.Sprintf(" ON %v", statement.Engine.Quote(statement.TableName())) + sql += fmt.Sprintf(" ON %v", statement.Engine.Quote(tbName)) } sqls = append(sqls, sql) } @@ -949,7 +924,7 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, v := rValue(bean) isStruct := v.Kind() == reflect.Struct if isStruct { - statement.setRefValue(v) + statement.setRefBean(bean) } var columnStr = statement.ColumnStr @@ -960,7 +935,7 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, if len(statement.JoinStr) == 0 { if len(columnStr) == 0 { if len(statement.GroupByStr) > 0 { - columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) + columnStr = statement.Engine.quoteColumns(statement.GroupByStr) } else { columnStr = statement.genColumnStr() } @@ -968,7 +943,7 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, } else { if len(columnStr) == 0 { if len(statement.GroupByStr) > 0 { - columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) + columnStr = statement.Engine.quoteColumns(statement.GroupByStr) } } } @@ -982,13 +957,17 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, if err := statement.mergeConds(bean); err != nil { return "", nil, err } + } else { + if err := statement.processIDParam(); err != nil { + return "", nil, err + } } condSQL, condArgs, err := builder.ToSQL(statement.cond) if err != nil { return "", nil, err } - sqlStr, err := statement.genSelectSQL(columnStr, condSQL) + sqlStr, err := statement.genSelectSQL(columnStr, condSQL, true, true) if err != nil { return "", nil, err } @@ -1001,7 +980,7 @@ func (statement *Statement) genCountSQL(beans ...interface{}) (string, []interfa var condArgs []interface{} var err error if len(beans) > 0 { - statement.setRefValue(rValue(beans[0])) + statement.setRefBean(beans[0]) condSQL, condArgs, err = statement.genConds(beans[0]) } else { condSQL, condArgs, err = builder.ToSQL(statement.cond) @@ -1018,7 +997,7 @@ func (statement *Statement) genCountSQL(beans ...interface{}) (string, []interfa selectSQL = "count(*)" } } - sqlStr, err := statement.genSelectSQL(selectSQL, condSQL) + sqlStr, err := statement.genSelectSQL(selectSQL, condSQL, false, false) if err != nil { return "", nil, err } @@ -1027,7 +1006,7 @@ func (statement *Statement) genCountSQL(beans ...interface{}) (string, []interfa } func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (string, []interface{}, error) { - statement.setRefValue(rValue(bean)) + statement.setRefBean(bean) var sumStrs = make([]string, 0, len(columns)) for _, colName := range columns { @@ -1043,7 +1022,7 @@ func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (stri return "", nil, err } - sqlStr, err := statement.genSelectSQL(sumSelect, condSQL) + sqlStr, err := statement.genSelectSQL(sumSelect, condSQL, true, true) if err != nil { return "", nil, err } @@ -1051,27 +1030,20 @@ func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (stri return sqlStr, append(statement.joinArgs, condArgs...), nil } -func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string, err error) { - var distinct string +func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, needOrderBy bool) (string, error) { + var ( + distinct string + dialect = statement.Engine.Dialect() + quote = statement.Engine.Quote + fromStr = " FROM " + top, mssqlCondi, whereStr string + ) if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") { distinct = "DISTINCT " } - - var dialect = statement.Engine.Dialect() - var quote = statement.Engine.Quote - var top string - var mssqlCondi string - - if err := statement.processIDParam(); err != nil { - return "", err - } - - var buf bytes.Buffer if len(condSQL) > 0 { - fmt.Fprintf(&buf, " WHERE %v", condSQL) + whereStr = " WHERE " + condSQL } - var whereStr = buf.String() - var fromStr = " FROM " if dialect.DBType() == core.MSSQL && strings.Contains(statement.TableName(), "..") { fromStr += statement.TableName() @@ -1118,9 +1090,10 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string, e } var orderStr string - if len(statement.OrderStr) > 0 { + if needOrderBy && len(statement.OrderStr) > 0 { orderStr = " ORDER BY " + statement.OrderStr } + var groupStr string if len(statement.GroupByStr) > 0 { groupStr = " GROUP BY " + statement.GroupByStr @@ -1130,45 +1103,50 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string, e } } - // !nashtsai! REVIEW Sprintf is considered slowest mean of string concatnation, better to work with builder pattern - a = fmt.Sprintf("SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr) + var buf builder.StringBuilder + fmt.Fprintf(&buf, "SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr) if len(mssqlCondi) > 0 { if len(whereStr) > 0 { - a += " AND " + mssqlCondi + fmt.Fprint(&buf, " AND ", mssqlCondi) } else { - a += " WHERE " + mssqlCondi + fmt.Fprint(&buf, " WHERE ", mssqlCondi) } } if statement.GroupByStr != "" { - a = fmt.Sprintf("%v GROUP BY %v", a, statement.GroupByStr) + fmt.Fprint(&buf, " GROUP BY ", statement.GroupByStr) } if statement.HavingStr != "" { - a = fmt.Sprintf("%v %v", a, statement.HavingStr) + fmt.Fprint(&buf, " ", statement.HavingStr) } - if statement.OrderStr != "" { - a = fmt.Sprintf("%v ORDER BY %v", a, statement.OrderStr) + if needOrderBy && statement.OrderStr != "" { + fmt.Fprint(&buf, " ORDER BY ", statement.OrderStr) } - if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE { - if statement.Start > 0 { - a = fmt.Sprintf("%v LIMIT %v OFFSET %v", a, statement.LimitN, statement.Start) - } else if statement.LimitN > 0 { - a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN) - } - } else if dialect.DBType() == core.ORACLE { - if statement.Start != 0 || statement.LimitN != 0 { - a = fmt.Sprintf("SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, columnStr, a, statement.Start+statement.LimitN, statement.Start) + if needLimit { + if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE { + if statement.Start > 0 { + fmt.Fprintf(&buf, " LIMIT %v OFFSET %v", statement.LimitN, statement.Start) + } else if statement.LimitN > 0 { + fmt.Fprint(&buf, " LIMIT ", statement.LimitN) + } + } else if dialect.DBType() == core.ORACLE { + if statement.Start != 0 || statement.LimitN != 0 { + oldString := buf.String() + buf.Reset() + fmt.Fprintf(&buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", + columnStr, columnStr, oldString, statement.Start+statement.LimitN, statement.Start) + } } } if statement.IsForUpdate { - a = dialect.ForUpdateSql(a) + return dialect.ForUpdateSql(buf.String()), nil } - return + return buf.String(), nil } func (statement *Statement) processIDParam() error { - if statement.idParam == nil { + if statement.idParam == nil || statement.RefTable == nil { return nil } diff --git a/vendor/github.com/go-xorm/xorm/transaction.go b/vendor/github.com/go-xorm/xorm/transaction.go new file mode 100644 index 00000000000..4104103fd53 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/transaction.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +// Transaction Execute sql wrapped in a transaction(abbr as tx), tx will automatic commit if no errors occurred +func (engine *Engine) Transaction(f func(*Session) (interface{}, error)) (interface{}, error) { + session := engine.NewSession() + defer session.Close() + + if err := session.Begin(); err != nil { + return nil, err + } + + result, err := f(session) + if err != nil { + return nil, err + } + + if err := session.Commit(); err != nil { + return nil, err + } + + return result, nil +} diff --git a/vendor/github.com/go-xorm/xorm/xorm.go b/vendor/github.com/go-xorm/xorm/xorm.go index 4fdadf2fade..739de8d4292 100644 --- a/vendor/github.com/go-xorm/xorm/xorm.go +++ b/vendor/github.com/go-xorm/xorm/xorm.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.8 + package xorm import ( @@ -17,7 +19,7 @@ import ( const ( // Version show the xorm's version - Version string = "0.6.4.0910" + Version string = "0.7.0.0504" ) func regDrvsNDialects() bool { @@ -31,7 +33,7 @@ func regDrvsNDialects() bool { "mysql": {"mysql", func() core.Driver { return &mysqlDriver{} }, func() core.Dialect { return &mysql{} }}, "mymysql": {"mysql", func() core.Driver { return &mymysqlDriver{} }, func() core.Dialect { return &mysql{} }}, "postgres": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }}, - "pgx": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }}, + "pgx": {"postgres", func() core.Driver { return &pqDriverPgx{} }, func() core.Dialect { return &postgres{} }}, "sqlite3": {"sqlite3", func() core.Driver { return &sqlite3Driver{} }, func() core.Dialect { return &sqlite3{} }}, "oci8": {"oracle", func() core.Driver { return &oci8Driver{} }, func() core.Dialect { return &oracle{} }}, "goracle": {"oracle", func() core.Driver { return &goracleDriver{} }, func() core.Dialect { return &oracle{} }}, @@ -90,6 +92,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { TagIdentifier: "xorm", TZLocation: time.Local, tagHandlers: defaultTagHandlers, + cachers: make(map[string]core.Cacher), } if uri.DbType == core.SQLITE { @@ -108,6 +111,13 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { return engine, nil } +// NewEngineWithParams new a db manager with params. The params will be passed to dialect. +func NewEngineWithParams(driverName string, dataSourceName string, params map[string]string) (*Engine, error) { + engine, err := NewEngine(driverName, dataSourceName) + engine.dialect.SetParams(params) + return engine, err +} + // Clone clone an engine func (engine *Engine) Clone() (*Engine, error) { return NewEngine(engine.DriverName(), engine.DataSourceName()) diff --git a/vendor/github.com/jmespath/go-jmespath/api.go b/vendor/github.com/jmespath/go-jmespath/api.go index 9cfa988bc5b..8e26ffeecff 100644 --- a/vendor/github.com/jmespath/go-jmespath/api.go +++ b/vendor/github.com/jmespath/go-jmespath/api.go @@ -2,7 +2,7 @@ package jmespath import "strconv" -// JmesPath is the epresentation of a compiled JMES path query. A JmesPath is +// JMESPath is the epresentation of a compiled JMES path query. A JMESPath is // safe for concurrent use by multiple goroutines. type JMESPath struct { ast ASTNode From 18999df716df44337406bb6c47157be4527e61cb Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Mar 2019 21:12:21 +0100 Subject: [PATCH 127/244] Ensuring master branch when performing release --- scripts/cli/tasks/grafanaui.release.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts index b363ace35e0..0a36a00bca7 100644 --- a/scripts/cli/tasks/grafanaui.release.ts +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -83,7 +83,19 @@ const publishPackage = (name: string, version: string) => await execa('npm', ['publish', '--access', 'public']); })(); +const ensureMasterBranch = async () => { + const currentBranch = await execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']); + const status = await execa.stdout('git', ['status', '--porcelain']); + console.log(status === ''); + + if (currentBranch !== 'master' && status !== '') { + console.error(chalk.red.bold('You need to be on clean master branch to release @grafana/ui')); + process.exit(1); + } +}; + const releaseTaskRunner: TaskRunner = async ({ publishToNpm }) => { + await ensureMasterBranch(); await execTask(buildTask)(); let releaseConfirmed = false; From b816b4e2596b3e612d6bc4837ddb7f09f95eea2d Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Mar 2019 21:16:34 +0100 Subject: [PATCH 128/244] Remove log --- scripts/cli/tasks/grafanaui.release.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts index 0a36a00bca7..2ebd2162e5b 100644 --- a/scripts/cli/tasks/grafanaui.release.ts +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -86,7 +86,6 @@ const publishPackage = (name: string, version: string) => const ensureMasterBranch = async () => { const currentBranch = await execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']); const status = await execa.stdout('git', ['status', '--porcelain']); - console.log(status === ''); if (currentBranch !== 'master' && status !== '') { console.error(chalk.red.bold('You need to be on clean master branch to release @grafana/ui')); From 358f9cbeaeebc5a52ee3a770bd8fcd4a2b40ecdd Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Mar 2019 21:18:26 +0100 Subject: [PATCH 129/244] Ensure clean master only when publishing package to npm --- scripts/cli/tasks/grafanaui.release.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts index 2ebd2162e5b..ef3430eaf9e 100644 --- a/scripts/cli/tasks/grafanaui.release.ts +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -94,7 +94,10 @@ const ensureMasterBranch = async () => { }; const releaseTaskRunner: TaskRunner = async ({ publishToNpm }) => { - await ensureMasterBranch(); + if (publishToNpm) { + await ensureMasterBranch(); + } + await execTask(buildTask)(); let releaseConfirmed = false; From 3f27d3ea23f5a6911d6529b1e839b35ef0828175 Mon Sep 17 00:00:00 2001 From: Jeremy Doupe Date: Tue, 5 Mar 2019 15:51:16 -0600 Subject: [PATCH 130/244] Make datasource variables multiselect and dashboard repeatable closes #7492 (and #7030) --- .../app/features/panel/metrics_panel_ctrl.ts | 21 ++++++++++++++++++- public/app/features/plugins/datasource_srv.ts | 4 ++++ .../templating/datasource_variable.ts | 15 +++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index ceebfd82335..0d73d6211e4 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -79,9 +79,28 @@ class MetricsPanelCtrl extends PanelCtrl { delete this.error; this.loading = true; + // set "mydatasource" to whatever the panel has defined + let mydatasource = this.panel.datasource; + let datasourceVarName = ''; + + // look for data source variables + for (let i = 0; i < this.templateSrv.variables.length; i++) { + const variable = this.templateSrv.variables[i]; + if (variable.type !== 'datasource') { + continue; + } + + datasourceVarName = variable.name; + } + + // if a data source variable was found, use its value + if (datasourceVarName !== '' && this.panel.scopedVars && this.panel.scopedVars[datasourceVarName]) { + mydatasource = this.panel.scopedVars[datasourceVarName].value; + } + // load datasource service this.datasourceSrv - .get(this.panel.datasource) + .get(mydatasource) .then(this.updateTimeRange.bind(this)) .then(this.issueQueries.bind(this)) .then(this.handleQueryResult.bind(this)) diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index f7dc0da32c4..aca87d8c63b 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -40,6 +40,10 @@ export class DatasourceSrv { } loadDatasource(name: string): Promise { + // if there are multiple datasources provided, just use the first one + const re = /{([^,}]+).*/; + name = name.replace(re, '$1'); + const dsConfig = config.datasources[name]; if (!dsConfig) { return this.$q.reject({ message: 'Datasource named ' + name + ' was not found' }); diff --git a/public/app/features/templating/datasource_variable.ts b/public/app/features/templating/datasource_variable.ts index 4424720c7f8..37b9e707444 100644 --- a/public/app/features/templating/datasource_variable.ts +++ b/public/app/features/templating/datasource_variable.ts @@ -6,6 +6,8 @@ export class DatasourceVariable implements Variable { query: string; options: any; current: any; + multi: boolean; + includeAll: boolean; refresh: any; skipUrlSync: boolean; @@ -18,6 +20,8 @@ export class DatasourceVariable implements Variable { regex: '', options: [], query: '', + multi: false, + includeAll: false, refresh: 1, skipUrlSync: false, }; @@ -69,9 +73,16 @@ export class DatasourceVariable implements Variable { } this.options = options; + if (this.includeAll) { + this.addAllOption(); + } return this.variableSrv.validateVariableSelectionState(this); } + addAllOption() { + this.options.unshift({ text: 'All', value: '$__all' }); + } + dependsOn(variable) { if (this.regex) { return containsVariable(this.regex, variable.name); @@ -84,6 +95,9 @@ export class DatasourceVariable implements Variable { } getValueForUrl() { + if (this.current.text === 'All') { + return 'All'; + } return this.current.value; } } @@ -91,5 +105,6 @@ export class DatasourceVariable implements Variable { variableTypes['datasource'] = { name: 'Datasource', ctor: DatasourceVariable, + supportsMulti: true, description: 'Enabled you to dynamically switch the datasource for multiple panels', }; From f5aba3681485c0b56294fb3a75a7b1bd4537efae Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 21:56:52 -0800 Subject: [PATCH 131/244] add ScopedVars to replace function --- packages/grafana-ui/src/types/panel.ts | 3 +- .../dashboard/dashgrid/PanelChrome.test.tsx | 35 +++++++++++++++++++ .../dashboard/dashgrid/PanelChrome.tsx | 10 ++++-- 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 public/app/features/dashboard/dashgrid/PanelChrome.test.tsx diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index ae205100c13..260ff78df76 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -1,8 +1,9 @@ import { ComponentClass } from 'react'; import { TimeSeries, LoadingState, TableData } from './data'; import { TimeRange } from './time'; +import { ScopedVars } from './datasource'; -export type InterpolateFunction = (value: string, format?: string | Function) => string; +export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string; export interface PanelProps { panelData: PanelData; diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx new file mode 100644 index 00000000000..d6242b9db22 --- /dev/null +++ b/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx @@ -0,0 +1,35 @@ +import { PanelChrome } from './PanelChrome'; + +jest.mock('sass/_variables.generated.scss', () => ({ + panelhorizontalpadding: 10, + panelVerticalPadding: 10, +})); + +describe('PanelChrome', () => { + let chrome: PanelChrome; + + beforeEach(() => { + chrome = new PanelChrome({ + panel: { + scopedVars: { + aaa: { value: 'AAA', text: 'upperA' }, + bbb: { value: 'BBB', text: 'upperB' }, + }, + }, + dashboard: {}, + plugin: {}, + isFullscreen: false, + }); + }); + + it('Should replace a panel variable', () => { + const out = chrome.replaceVariables('hello $aaa'); + expect(out).toBe('hello AAA'); + }); + + it('It should prefer the diret variables', () => { + const extra = { aaa: { text: '???', value: 'XXX' } }; + const out = chrome.replaceVariables('hello $aaa and $bbb', extra); + expect(out).toBe('hello XXX and BBB'); + }); +}); diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 80ce2f39b70..149d0f3deee 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -19,6 +19,7 @@ import { profiler } from 'app/core/profiler'; import { DashboardModel, PanelModel } from '../state'; import { PanelPlugin } from 'app/types'; import { DataQueryResponse, TimeRange, LoadingState, PanelData, DataQueryError } from '@grafana/ui'; +import { ScopedVars } from '@grafana/ui'; import variables from 'sass/_variables.generated.scss'; import templateSrv from 'app/features/templating/template_srv'; @@ -85,8 +86,13 @@ export class PanelChrome extends PureComponent { }); }; - replaceVariables = (value: string, format?: string) => { - return templateSrv.replace(value, this.props.panel.scopedVars, format); + replaceVariables = (value: string, extraVars?: ScopedVars, format?: string) => { + let vars = this.props.panel.scopedVars; + if (extraVars) { + vars = vars ? { ...vars, ...extraVars } : extraVars; + } + console.log('VARiables', vars); + return templateSrv.replace(value, vars, format); }; onDataResponse = (dataQueryResponse: DataQueryResponse) => { From 948729e951978d7ecae1970b7ece2712bb689e77 Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 22:00:12 -0800 Subject: [PATCH 132/244] remove console.log --- public/app/features/dashboard/dashgrid/PanelChrome.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 149d0f3deee..0a9d1d44ceb 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -91,7 +91,6 @@ export class PanelChrome extends PureComponent { if (extraVars) { vars = vars ? { ...vars, ...extraVars } : extraVars; } - console.log('VARiables', vars); return templateSrv.replace(value, vars, format); }; From aa38a9e0b49c17e37b0b246a558fe791457dd89e Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 22:14:28 -0800 Subject: [PATCH 133/244] typescript functions on replace --- public/app/features/templating/template_srv.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts index 11e90cbb5f7..e0d35295556 100644 --- a/public/app/features/templating/template_srv.ts +++ b/public/app/features/templating/template_srv.ts @@ -1,7 +1,7 @@ import kbn from 'app/core/utils/kbn'; import _ from 'lodash'; import { variableRegex } from 'app/features/templating/variable'; -import { TimeRange } from '@grafana/ui/src'; +import { TimeRange, ScopedVars } from '@grafana/ui/src'; function luceneEscape(value) { return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1'); @@ -220,7 +220,7 @@ export class TemplateSrv { return values; } - replace(target, scopedVars?, format?) { + replace(target: string, scopedVars?: ScopedVars, format?: string | Function) { if (!target) { return target; } From 3dd7d407183c5d0e574d96d7f46a2290e983976b Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 22:18:42 -0800 Subject: [PATCH 134/244] fix comments --- public/app/features/dashboard/dashgrid/PanelChrome.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx index d6242b9db22..7136a14a907 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx @@ -27,7 +27,7 @@ describe('PanelChrome', () => { expect(out).toBe('hello AAA'); }); - it('It should prefer the diret variables', () => { + it('But it should prefer the local variable value', () => { const extra = { aaa: { text: '???', value: 'XXX' } }; const out = chrome.replaceVariables('hello $aaa and $bbb', extra); expect(out).toBe('hello XXX and BBB'); From 5524dacc9d28a601745f9b44a12cf60347e3f07e Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 23:00:43 -0800 Subject: [PATCH 135/244] use explore icon --- public/app/features/panel/metrics_panel_ctrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index ceebfd82335..028585ae21e 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -224,7 +224,7 @@ class MetricsPanelCtrl extends PanelCtrl { items.push({ text: 'Explore', click: 'ctrl.explore();', - icon: 'fa fa-fw fa-rocket', + icon: 'gicon gicon-explore', shortcut: 'x', }); } From 909d425008a74c93ea2fb58f5a4a69a7205b4e40 Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 23:40:03 -0800 Subject: [PATCH 136/244] cleanup plugin versions --- public/app/features/plugins/partials/plugin_edit.html | 4 ++-- public/app/plugins/datasource/cloudwatch/plugin.json | 3 +-- public/app/plugins/datasource/elasticsearch/plugin.json | 5 +---- public/app/plugins/datasource/graphite/plugin.json | 3 +-- public/app/plugins/datasource/influxdb/plugin.json | 3 +-- public/app/plugins/datasource/mysql/plugin.json | 3 +-- public/app/plugins/datasource/opentsdb/plugin.json | 3 +-- public/app/plugins/datasource/postgres/plugin.json | 3 +-- public/app/plugins/datasource/prometheus/plugin.json | 3 +-- public/app/plugins/panel/alertlist/plugin.json | 3 +-- public/app/plugins/panel/dashlist/plugin.json | 3 +-- public/app/plugins/panel/graph/plugin.json | 3 +-- public/app/plugins/panel/heatmap/plugin.json | 3 +-- public/app/plugins/panel/pluginlist/plugin.json | 3 +-- public/app/plugins/panel/singlestat/plugin.json | 3 +-- public/app/plugins/panel/table/plugin.json | 3 +-- public/app/plugins/panel/text/plugin.json | 3 +-- 17 files changed, 18 insertions(+), 36 deletions(-) diff --git a/public/app/features/plugins/partials/plugin_edit.html b/public/app/features/plugins/partials/plugin_edit.html index 16cdfc1d1b2..d84196c47b0 100644 --- a/public/app/features/plugins/partials/plugin_edit.html +++ b/public/app/features/plugins/partials/plugin_edit.html @@ -25,7 +25,7 @@
- If an alert rule has a configured For and the query violates the configured diff --git a/public/app/features/datasources/partials/http_settings.html b/public/app/features/datasources/partials/http_settings.html index b6f2c4fc0dd..b4cf1084843 100644 --- a/public/app/features/datasources/partials/http_settings.html +++ b/public/app/features/datasources/partials/http_settings.html @@ -4,7 +4,7 @@
URL - @@ -59,7 +59,7 @@
Whitelisted Cookies - + Grafana Proxy deletes forwarded cookies by default. Specify cookies by name that should be forwarded to the data source. diff --git a/public/app/plugins/datasource/cloudwatch/partials/config.html b/public/app/plugins/datasource/cloudwatch/partials/config.html index 40249d32b7e..0f74901ee19 100644 --- a/public/app/plugins/datasource/cloudwatch/partials/config.html +++ b/public/app/plugins/datasource/cloudwatch/partials/config.html @@ -8,7 +8,7 @@
- + Credentials profile name, as specified in ~/.aws/credentials, leave blank for default @@ -30,7 +30,7 @@
- + ARN of Assume Role @@ -47,7 +47,7 @@
- + Namespaces of Custom Metrics diff --git a/public/app/plugins/datasource/elasticsearch/partials/config.html b/public/app/plugins/datasource/elasticsearch/partials/config.html index def59518624..9085dfa5d4a 100644 --- a/public/app/plugins/datasource/elasticsearch/partials/config.html +++ b/public/app/plugins/datasource/elasticsearch/partials/config.html @@ -36,7 +36,7 @@
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. diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/config.html b/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/config.html index 46ac8798680..b3d7ad0f092 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/config.html +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/config.html @@ -16,7 +16,7 @@
Subscription Id - +

In the Azure Portal, navigate to Subscriptions -> Choose subscription -> Overview -> Subscription ID.

**Click @@ -27,7 +27,7 @@
Tenant Id - +

In the Azure Portal, navigate to Azure Active Directory -> Properties -> Directory ID.

**Click @@ -38,7 +38,7 @@
Client Id - +

In the Azure Portal, navigate to Azure Active Directory -> App Registrations -> Choose your app -> Application ID.

@@ -50,7 +50,7 @@
Client Secret - +

To create a new key, log in to Azure Portal, navigate to Azure Active Directory -> App Registrations -> Choose your @@ -83,7 +83,7 @@

Subscription Id -

In the Azure Portal, navigate to Subscriptions -> Choose subscription -> Overview -> Subscription ID.

@@ -95,7 +95,7 @@
Tenant Id -

In the Azure Portal, navigate to Azure Active Directory -> Properties -> Directory ID.

@@ -107,7 +107,7 @@
Client Id -

In the Azure Portal, navigate to Azure Active Directory -> App Registrations -> Choose your app -> @@ -121,7 +121,7 @@

Client Secret -

To create a new key, log in to Azure Portal, navigate to Azure Active Directory -> App Registrations -> @@ -168,7 +168,7 @@

API Key -

Section 2 of the Quickstart guide shows where to find/create the API Key:

@@ -185,7 +185,7 @@
Application Id - +

Section 2 of the Quickstart guide shows where to find the Application ID:

**Click here to open the Application diff --git a/public/app/plugins/datasource/influxdb/partials/config.html b/public/app/plugins/datasource/influxdb/partials/config.html index 4de2fadd52d..0229d01e8c8 100644 --- a/public/app/plugins/datasource/influxdb/partials/config.html +++ b/public/app/plugins/datasource/influxdb/partials/config.html @@ -41,7 +41,7 @@
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. diff --git a/public/app/plugins/datasource/loki/partials/config.html b/public/app/plugins/datasource/loki/partials/config.html index d209b51730a..425e460a240 100644 --- a/public/app/plugins/datasource/loki/partials/config.html +++ b/public/app/plugins/datasource/loki/partials/config.html @@ -5,7 +5,7 @@
Maximum lines - + Loki queries must contain a limit of the maximum number of lines returned (default: 1000). Increase this limit to have a bigger result set for ad-hoc analysis. diff --git a/public/app/plugins/datasource/mssql/partials/config.html b/public/app/plugins/datasource/mssql/partials/config.html index db76f60e5e3..f3ac7e87064 100644 --- a/public/app/plugins/datasource/mssql/partials/config.html +++ b/public/app/plugins/datasource/mssql/partials/config.html @@ -50,7 +50,7 @@
Max open - + The maximum number of open connections to the database. If Max idle connections is greater than 0 and the Max open connections is less than Max idle connections, then Max idle connections will be @@ -60,7 +60,7 @@
Max idle - + The maximum number of connections in the idle connection pool. If Max open connections is greater than 0 but less than the Max idle connections, then the Max idle connections will be reduced to match the @@ -69,7 +69,7 @@
Max lifetime - + The maximum amount of time in seconds a connection may be reused. If set to 0, connections are reused forever. @@ -82,7 +82,7 @@
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. diff --git a/public/app/plugins/datasource/mysql/partials/config.html b/public/app/plugins/datasource/mysql/partials/config.html index 8221a06e1ee..f08d9b3da15 100644 --- a/public/app/plugins/datasource/mysql/partials/config.html +++ b/public/app/plugins/datasource/mysql/partials/config.html @@ -44,7 +44,7 @@
Max open - + The maximum number of open connections to the database. If Max idle connections is greater than 0 and the Max open connections is less than Max idle connections, then Max idle connections will be @@ -54,7 +54,7 @@
Max idle - + The maximum number of connections in the idle connection pool. If Max open connections is greater than 0 but less than the Max idle connections, then the Max idle connections will be reduced to match the @@ -63,7 +63,7 @@
Max lifetime - + The maximum amount of time in seconds a connection may be reused. If set to 0, connections are reused forever.

This should always be lower than configured wait_timeout in MySQL. @@ -77,7 +77,7 @@
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. diff --git a/public/app/plugins/datasource/opentsdb/partials/query.editor.html b/public/app/plugins/datasource/opentsdb/partials/query.editor.html index a2db8a68840..44ee8ebb57f 100644 --- a/public/app/plugins/datasource/opentsdb/partials/query.editor.html +++ b/public/app/plugins/datasource/opentsdb/partials/query.editor.html @@ -49,7 +49,7 @@
- diff --git a/public/app/plugins/datasource/postgres/partials/config.html b/public/app/plugins/datasource/postgres/partials/config.html index 13b74d6b20e..c0e5a30496d 100644 --- a/public/app/plugins/datasource/postgres/partials/config.html +++ b/public/app/plugins/datasource/postgres/partials/config.html @@ -43,7 +43,7 @@
Max open - + The maximum number of open connections to the database. If Max idle connections is greater than 0 and the Max open connections is less than Max idle connections, then Max idle connections will be @@ -53,7 +53,7 @@
Max idle - + The maximum number of connections in the idle connection pool. If Max open connections is greater than 0 but less than the Max idle connections, then the Max idle connections will be reduced to match the @@ -62,7 +62,7 @@
Max lifetime - + The maximum amount of time in seconds a connection may be reused. If set to 0, connections are reused forever. @@ -95,7 +95,7 @@
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. diff --git a/public/app/plugins/datasource/prometheus/partials/config.html b/public/app/plugins/datasource/prometheus/partials/config.html index a7a85e696b0..aaeec7f5336 100644 --- a/public/app/plugins/datasource/prometheus/partials/config.html +++ b/public/app/plugins/datasource/prometheus/partials/config.html @@ -5,7 +5,7 @@
Scrape interval - + Set this to your global scrape interval defined in your Prometheus config file. This will be used as a lower limit for the Prometheus step query parameter. @@ -16,7 +16,7 @@
Query timeout - + Set the Prometheus query timeout. diff --git a/public/app/plugins/datasource/prometheus/partials/query.editor.html b/public/app/plugins/datasource/prometheus/partials/query.editor.html index f63beeedbd3..94819582f6d 100644 --- a/public/app/plugins/datasource/prometheus/partials/query.editor.html +++ b/public/app/plugins/datasource/prometheus/partials/query.editor.html @@ -10,7 +10,7 @@
- @@ -21,7 +21,7 @@
- diff --git a/public/sass/components/_gf-form.scss b/public/sass/components/_gf-form.scss index eef341217e4..c341c686143 100644 --- a/public/sass/components/_gf-form.scss +++ b/public/sass/components/_gf-form.scss @@ -250,6 +250,10 @@ $input-border: 1px solid $input-border-color; &--plaintext { white-space: unset; } + + &--has-help-icon { + padding-right: $input-padding-x * 3; + } } .gf-form-hint { diff --git a/public/sass/components/_tagsinput.scss b/public/sass/components/_tagsinput.scss index e8cf9ea44e9..5c511d44e71 100644 --- a/public/sass/components/_tagsinput.scss +++ b/public/sass/components/_tagsinput.scss @@ -15,6 +15,10 @@ height: 100%; width: 5rem; box-sizing: border-box; + + &.gf-form-input--has-help-icon { + padding-right: $input-padding-x * 3; + } } .tag { From b63ef540886b79d957717a11ee413568253b0e42 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 6 Mar 2019 14:49:10 +0100 Subject: [PATCH 152/244] changelog: add notes about closing #14509 #15179 --- CHANGELOG.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 964d0014e83..080f54e5056 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ ### Minor * **Cloudwatch**: Add AWS RDS MaximumUsedTransactionIDs metric [#15077](https://github.com/grafana/grafana/pull/15077), thx [@activeshadow](https://github.com/activeshadow) - ### Bug Fixes * **Api**: Invalid org invite code [#10506](https://github.com/grafana/grafana/issues/10506) * **Datasource**: Handles nil jsondata field gracefully [#14239](https://github.com/grafana/grafana/issues/14239) @@ -29,14 +28,12 @@ * **Security** fix: limit access to org admin and alerting pages. [#15761](https://github.com/grafana/grafana/pull/15761), [@marefr](https://github.com/marefr) * **Panel Edit** minInterval changes did not persist [#15757](https://github.com/grafana/grafana/pull/15757), [@hugohaggmark](https://github.com/hugohaggmark) * **Teams**: Fixed bug when getting teams for user. [#15595](https://github.com/grafana/grafana/pull/15595), [@hugohaggmark](https://github.com/hugohaggmark) - - +* **Stackdriver**: fix for float64 bounds for distribution metrics [#14509](https://github.com/grafana/grafana/issues/14509) +* **Stackdriver**: no reducers available for distribution type [#15179](https://github.com/grafana/grafana/issues/15179) # 6.0.0 stable (2019-02-25) ### Bug Fixes -* **Stackdriver**: fix for float64 bounds for distribution metrics [#14509](https://github.com/grafana/grafana/issues/14509) -* **Stackdriver**: no reducers available for distribution type [#15179](https://github.com/grafana/grafana/issues/15179) * **Dashboard**: fixes click after scroll in series override menu [#15621](https://github.com/grafana/grafana/issues/15621) * **MySQL**: fix mysql query using _interval_ms variable throws error [#14507](https://github.com/grafana/grafana/issues/14507) From 2c1be2c37e4f5079d283ddc53cdc5f6fceef7106 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 6 Mar 2019 15:19:58 +0100 Subject: [PATCH 153/244] position from add panel, dimensions from copied panel --- .../components/AddPanelWidget/AddPanelWidget.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.tsx b/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.tsx index 1349069059f..bdcc3c9d165 100644 --- a/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.tsx +++ b/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.tsx @@ -95,11 +95,17 @@ export class AddPanelWidget extends React.Component { onPasteCopiedPanel = panelPluginInfo => { const dashboard = this.props.dashboard; + const { gridPos } = this.props.panel; const newPanel: any = { type: panelPluginInfo.id, title: 'Panel Title', - gridPos: panelPluginInfo.defaults.gridPos, + gridPos: { + x: gridPos.x, + y: gridPos.y, + w: panelPluginInfo.defaults.gridPos.w, + h: panelPluginInfo.defaults.gridPos.h, + }, }; // apply panel template / defaults From bfde2cb46f881f9335140ca4f5bac8e0f720d438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 6 Mar 2019 16:01:22 +0100 Subject: [PATCH 154/244] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 080f54e5056..343e3ccfbb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ * **Gauge**: Interpolate scoped variables in repeated gauges [#15739](https://github.com/grafana/grafana/issues/15739) * **Datasource**: Empty user/password was not updated when updating datasources [#15608](https://github.com/grafana/grafana/pull/15608), thx [@Maddin-619](https://github.com/Maddin-619) -# 6.0.1 (unreleased) +# 6.0.1 (2019-03-06) ### Bug Fixes * **Metrics**: Fixes broken usagestats metrics for /metrics [#15651](https://github.com/grafana/grafana/issues/15651) From c2598e498a481b88f3361f4187e6ed397e3a9853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 6 Mar 2019 16:10:49 +0100 Subject: [PATCH 155/244] Update latest.json --- latest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/latest.json b/latest.json index 7e69b431a4d..e19a0f8550d 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { - "stable": "6.0.0", - "testing": "6.0.0" + "stable": "6.0.1", + "testing": "6.0.1" } From 03e0e1783dcd65658ec1b5dd7dbafdb6b71f083a Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Wed, 6 Mar 2019 16:24:49 +0100 Subject: [PATCH 156/244] fix: Consistency in unit labels #15709 --- packages/grafana-ui/src/utils/valueFormats/categories.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grafana-ui/src/utils/valueFormats/categories.ts b/packages/grafana-ui/src/utils/valueFormats/categories.ts index 806da582bb3..bf89191c04c 100644 --- a/packages/grafana-ui/src/utils/valueFormats/categories.ts +++ b/packages/grafana-ui/src/utils/valueFormats/categories.ts @@ -137,7 +137,7 @@ export const getCategories = (): ValueFormatCategory[] => [ formats: [ { name: 'packets/sec', id: 'pps', fn: decimalSIPrefix('pps') }, { name: 'bits/sec', id: 'bps', fn: decimalSIPrefix('bps') }, - { name: 'bytes/sec', id: 'Bps', fn: decimalSIPrefix('B/s') }, + { name: 'bytes/sec', id: 'Bps', fn: decimalSIPrefix('Bs') }, { name: 'kilobytes/sec', id: 'KBs', fn: decimalSIPrefix('Bs', 1) }, { name: 'kilobits/sec', id: 'Kbits', fn: decimalSIPrefix('bps', 1) }, { name: 'megabytes/sec', id: 'MBs', fn: decimalSIPrefix('Bs', 2) }, From f21c976b27dc7ee5d1957662fc8e2e94b7ca6a42 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 6 Mar 2019 16:58:01 +0100 Subject: [PATCH 157/244] fix discord notifier so it doesn't crash when there are no image generated --- pkg/services/alerting/notifiers/discord.go | 101 +++++++++++---------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/pkg/services/alerting/notifiers/discord.go b/pkg/services/alerting/notifiers/discord.go index 57d9d438fa2..c7178211f0e 100644 --- a/pkg/services/alerting/notifiers/discord.go +++ b/pkg/services/alerting/notifiers/discord.go @@ -111,57 +111,20 @@ func (this *DiscordNotifier) Notify(evalContext *alerting.EvalContext) error { json, _ := bodyJSON.MarshalJSON() - content_type := "application/json" - - var body []byte - - if embeddedImage { - - var b bytes.Buffer - - w := multipart.NewWriter(&b) - - f, err := os.Open(evalContext.ImageOnDiskPath) - - if err != nil { - this.log.Error("Can't open graph file", err) - return err - } - - defer f.Close() - - fw, err := w.CreateFormField("payload_json") - if err != nil { - return err - } - - if _, err = fw.Write([]byte(string(json))); err != nil { - return err - } - - fw, err = w.CreateFormFile("file", "graph.png") - if err != nil { - return err - } - - if _, err = io.Copy(fw, f); err != nil { - return err - } - - w.Close() - - body = b.Bytes() - content_type = w.FormDataContentType() - - } else { - body = json - } - cmd := &m.SendWebhookSync{ Url: this.WebhookURL, - Body: string(body), HttpMethod: "POST", - ContentType: content_type, + ContentType: "application/json", + } + + if !embeddedImage { + cmd.Body = string(json) + } else { + err := this.embedImage(cmd, evalContext.ImageOnDiskPath, json) + if err != nil { + this.log.Error("failed to embed image", "error", err) + return err + } } if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { @@ -171,3 +134,45 @@ func (this *DiscordNotifier) Notify(evalContext *alerting.EvalContext) error { return nil } + +func (this *DiscordNotifier) embedImage(cmd *m.SendWebhookSync, imagePath string, existingJSONBody []byte) error { + f, err := os.Open(imagePath) + defer f.Close() + if err != nil { + if os.IsNotExist(err) { + cmd.Body = string(existingJSONBody) + return nil + } + if !os.IsNotExist(err) { + return err + } + } + + var b bytes.Buffer + w := multipart.NewWriter(&b) + + fw, err := w.CreateFormField("payload_json") + if err != nil { + return err + } + + if _, err = fw.Write([]byte(string(existingJSONBody))); err != nil { + return err + } + + fw, err = w.CreateFormFile("file", "graph.png") + if err != nil { + return err + } + + if _, err = io.Copy(fw, f); err != nil { + return err + } + + w.Close() + + cmd.Body = string(b.Bytes()) + cmd.ContentType = w.FormDataContentType() + + return nil +} From 3375c72d563affe757fc01cdeead726d23bc18bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 6 Mar 2019 16:58:19 +0100 Subject: [PATCH 158/244] Alternative fix to detecting when to stop a playlist, fixes #15701 and #15702 --- public/app/features/playlist/playlist_srv.ts | 25 +++++++++++- .../playlist/specs/playlist_srv.test.ts | 39 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/public/app/features/playlist/playlist_srv.ts b/public/app/features/playlist/playlist_srv.ts index cb2763129ef..97ebfff7320 100644 --- a/public/app/features/playlist/playlist_srv.ts +++ b/public/app/features/playlist/playlist_srv.ts @@ -7,6 +7,7 @@ import coreModule from '../../core/core_module'; import appEvents from 'app/core/app_events'; import locationUtil from 'app/core/utils/location_util'; import kbn from 'app/core/utils/kbn'; +import { store } from 'app/store/store'; export class PlaylistSrv { private cancelPromise: any; @@ -15,6 +16,8 @@ export class PlaylistSrv { private interval: number; private startUrl: string; private numberOfLoops = 0; + private storeUnsub: () => void; + private validPlaylistUrl: string; isPlaying: boolean; /** @ngInject */ @@ -39,15 +42,16 @@ export class PlaylistSrv { const dash = this.dashboards[this.index]; const queryParams = this.$location.search(); const filteredParams = _.pickBy(queryParams, value => value !== null); + const nextDashboardUrl = locationUtil.stripBaseFromUrl(dash.url); // this is done inside timeout to make sure digest happens after // as this can be called from react this.$timeout(() => { - const stripedUrl = locationUtil.stripBaseFromUrl(dash.url); - this.$location.url(stripedUrl + '?' + toUrlParams(filteredParams)); + this.$location.url(nextDashboardUrl + '?' + toUrlParams(filteredParams)); }); this.index++; + this.validPlaylistUrl = nextDashboardUrl; this.cancelPromise = this.$timeout(() => this.next(), this.interval); } @@ -56,6 +60,15 @@ export class PlaylistSrv { this.next(); } + // Detect url changes not caused by playlist srv and stop playlist + storeUpdated() { + const state = store.getState(); + + if (state.location.path !== this.validPlaylistUrl) { + this.stop(); + } + } + start(playlistId) { this.stop(); @@ -63,6 +76,10 @@ export class PlaylistSrv { this.index = 0; this.isPlaying = true; + // setup location tracking + this.storeUnsub = store.subscribe(() => this.storeUpdated()); + this.validPlaylistUrl = this.$location.path(); + appEvents.emit('playlist-started'); return this.backendSrv.get(`/api/playlists/${playlistId}`).then(playlist => { @@ -85,6 +102,10 @@ export class PlaylistSrv { this.index = 0; this.isPlaying = false; + if (this.storeUnsub) { + this.storeUnsub(); + } + if (this.cancelPromise) { this.$timeout.cancel(this.cancelPromise); } diff --git a/public/app/features/playlist/specs/playlist_srv.test.ts b/public/app/features/playlist/specs/playlist_srv.test.ts index bfb1732d9c6..628818fd716 100644 --- a/public/app/features/playlist/specs/playlist_srv.test.ts +++ b/public/app/features/playlist/specs/playlist_srv.test.ts @@ -1,4 +1,14 @@ +import configureMockStore from 'redux-mock-store'; import { PlaylistSrv } from '../playlist_srv'; +import { setStore } from 'app/store/store'; + +const mockStore = configureMockStore(); + +setStore( + mockStore({ + location: {}, + }) +); const dashboards = [{ url: 'dash1' }, { url: 'dash2' }]; @@ -19,6 +29,7 @@ const createPlaylistSrv = (): [PlaylistSrv, { url: jest.MockInstance } const mockLocation = { url: jest.fn(), search: () => ({}), + path: () => '/playlists/1', }; const mockTimeout = jest.fn(); @@ -96,4 +107,32 @@ describe('PlaylistSrv', () => { expect(hrefMock).toHaveBeenCalledTimes(3); expect(hrefMock).toHaveBeenLastCalledWith(initialUrl); }); + + it('storeUpdated should stop playlist when navigating away', async () => { + await srv.start(1); + + srv.storeUpdated(); + + expect(srv.isPlaying).toBe(false); + }); + + it('storeUpdated should not stop playlist when navigating to next dashboard', async () => { + await srv.start(1); + + srv.next(); + + setStore( + mockStore({ + location: { + path: 'dash2', + }, + }) + ); + + expect((srv as any).validPlaylistUrl).toBe('dash2'); + + srv.storeUpdated(); + + expect(srv.isPlaying).toBe(true); + }); }); From 3895294f972ea8b1ecf09a1870919559dc7a149b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 6 Mar 2019 17:39:18 +0100 Subject: [PATCH 159/244] Revert "Fix for leaving playlist mode" --- .../dashboard/components/DashNav/DashNav.tsx | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index d2f22d7d010..453c5d1f9ac 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -8,7 +8,6 @@ import { appEvents } from 'app/core/app_events'; import { PlaylistSrv } from 'app/features/playlist/playlist_srv'; // Components -import { ClickOutsideWrapper } from 'app/core/components/ClickOutsideWrapper/ClickOutsideWrapper'; import { DashNavButton } from './DashNavButton'; import { Tooltip } from '@grafana/ui'; @@ -174,28 +173,26 @@ export class DashNav extends PureComponent { {this.renderDashboardTitleSearchButton()} {this.playlistSrv.isPlaying && ( - -
- - - -
-
+
+ + + +
)}
From 2604e9e433a565ca7286156c2178cb44d870b09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 6 Mar 2019 17:53:14 +0100 Subject: [PATCH 160/244] Minor refactoring of PR #15770 --- .../app/features/dashboard/state/DashboardModel.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index 416155ce2e6..cde4227d3ec 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -919,16 +919,18 @@ export class DashboardModel { } toggleLegendsForAll() { - const panels = this.panels.filter(panel => { + const panelsWithLegends = this.panels.filter(panel => { return panel.legend !== undefined && panel.legend !== null; }); + // determine if more panels are displaying legends or not - const onCount = panels.filter(panel => panel.legend.show).length; - const offCount = panels.length - onCount; + const onCount = panelsWithLegends.filter(panel => panel.legend.show).length; + const offCount = panelsWithLegends.length - onCount; const panelLegendsOn = onCount >= offCount; - panels.forEach(panel => { + + for (const panel of panelsWithLegends) { panel.legend.show = !panelLegendsOn; panel.render(); - }); + } } } From a59e0f1b5586b233fdf0ce93f4eda4b936f4659f Mon Sep 17 00:00:00 2001 From: Andrej Ocenas Date: Wed, 6 Mar 2019 22:30:40 +0100 Subject: [PATCH 161/244] Map dataSourceTypeSearchQuery state from redux to search input. --- public/app/features/datasources/NewDataSourcePage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/features/datasources/NewDataSourcePage.tsx b/public/app/features/datasources/NewDataSourcePage.tsx index 7b028a4e3ea..b572bacc1a1 100644 --- a/public/app/features/datasources/NewDataSourcePage.tsx +++ b/public/app/features/datasources/NewDataSourcePage.tsx @@ -70,6 +70,7 @@ function mapStateToProps(state: StoreState) { return { navModel: getNavModel(state.navIndex, 'datasources'), dataSourceTypes: getDataSourceTypes(state.dataSources), + dataSourceTypeSearchQuery: state.dataSources.dataSourceTypeSearchQuery, isLoading: state.dataSources.isLoadingDataSources, }; } From 82326fed42053a2926baacdb0810d75c4740467f Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 6 Mar 2019 15:18:24 -0800 Subject: [PATCH 162/244] return the same panelData unless it changes --- .../features/dashboard/dashgrid/DataPanel.tsx | 12 +++--- public/app/plugins/panel/gauge/GaugePanel.tsx | 43 +++++++++++++------ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index ae3486e40fe..09864d85960 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -44,6 +44,7 @@ export interface State { isFirstLoad: boolean; loading: LoadingState; response: DataQueryResponse; + panelData: PanelData; } export class DataPanel extends Component { @@ -63,6 +64,7 @@ export class DataPanel extends Component { response: { data: [], }, + panelData: {}, isFirstLoad: true, }; } @@ -147,6 +149,7 @@ export class DataPanel extends Component { this.setState({ loading: LoadingState.Done, response: resp, + panelData: this.getPanelData(resp), isFirstLoad: false, }); } catch (err) { @@ -169,9 +172,7 @@ export class DataPanel extends Component { } }; - getPanelData = () => { - const { response } = this.state; - + getPanelData(response: DataQueryResponse) { if (response.data.length > 0 && (response.data[0] as TableData).type === 'table') { return { tableData: response.data[0] as TableData, @@ -183,12 +184,11 @@ export class DataPanel extends Component { timeSeries: response.data as TimeSeries[], tableData: null, }; - }; + } render() { const { queries } = this.props; - const { loading, isFirstLoad } = this.state; - const panelData = this.getPanelData(); + const { loading, isFirstLoad, panelData } = this.state; // do not render component until we have first data if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) { diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index 2a42e31b9ab..b75d4a1c7f3 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -1,5 +1,5 @@ // Libraries -import React, { PureComponent } from 'react'; +import React, { Component } from 'react'; // Services & Utils import { processTimeSeries, ThemeContext } from '@grafana/ui'; @@ -12,16 +12,28 @@ import { GaugeOptions } from './types'; import { PanelProps, NullValueMode, TimeSeriesValue } from '@grafana/ui/src/types'; interface Props extends PanelProps {} +interface State { + value: TimeSeriesValue; +} -export class GaugePanel extends PureComponent { - render() { - const { panelData, width, height, replaceVariables, options } = this.props; +export class GaugePanel extends Component { + constructor(props: Props) { + super(props); + this.state = { + value: this.findValue(props), + }; + } + + componentDidUpdate(prevProps: Props) { + if (this.props.panelData !== prevProps.panelData) { + this.setState({ value: this.findValue(this.props) }); + } + } + + findValue(props: Props): number | null { + const { panelData, options } = props; const { valueOptions } = options; - const prefix = replaceVariables(valueOptions.prefix); - const suffix = replaceVariables(valueOptions.suffix); - let value: TimeSeriesValue; - if (panelData.timeSeries) { const vmSeries = processTimeSeries({ timeSeries: panelData.timeSeries, @@ -29,14 +41,21 @@ export class GaugePanel extends PureComponent { }); if (vmSeries[0]) { - value = vmSeries[0].stats[valueOptions.stat]; - } else { - value = null; + return vmSeries[0].stats[valueOptions.stat]; } } else if (panelData.tableData) { - value = panelData.tableData.rows[0].find(prop => prop > 0); + return panelData.tableData.rows[0].find(prop => prop > 0); } + return null; + } + render() { + const { width, height, replaceVariables, options } = this.props; + const { valueOptions } = options; + const { value } = this.state; + + const prefix = replaceVariables(valueOptions.prefix); + const suffix = replaceVariables(valueOptions.suffix); return ( {theme => ( From 2e8dd19636cea399c69c116d58a26702da651fbb Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 6 Mar 2019 15:54:19 -0800 Subject: [PATCH 163/244] use pure component --- public/app/plugins/panel/text2/TextPanel.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/panel/text2/TextPanel.tsx b/public/app/plugins/panel/text2/TextPanel.tsx index 4abb0c105be..26bb2b2dcb9 100644 --- a/public/app/plugins/panel/text2/TextPanel.tsx +++ b/public/app/plugins/panel/text2/TextPanel.tsx @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import Remarkable from 'remarkable'; import { sanitize } from 'app/core/utils/text'; @@ -14,7 +14,7 @@ interface State { html: string; } -export class TextPanel extends Component { +export class TextPanel extends PureComponent { remarkable: Remarkable; constructor(props) { @@ -30,11 +30,11 @@ export class TextPanel extends Component { if (html !== this.state.html) { this.setState({ html }); } - }, 100); + }, 150); componentDidUpdate(prevProps: Props) { // Since any change could be referenced in a template variable, - // This needs to process everything + // This needs to process everytime (with debounce) this.updateHTML(); } From 272b561958604d97cf21890cd665812c6d637cf4 Mon Sep 17 00:00:00 2001 From: Shaffer John Date: Thu, 7 Mar 2019 11:09:17 +0800 Subject: [PATCH 164/244] Update upgrading.md for wrong spell --- docs/sources/installation/upgrading.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/installation/upgrading.md b/docs/sources/installation/upgrading.md index e235f25b9e9..511d6117eb3 100644 --- a/docs/sources/installation/upgrading.md +++ b/docs/sources/installation/upgrading.md @@ -120,7 +120,7 @@ If you're using systemd and have a large amount of annotations consider temporar ## Upgrading to v6.0 -If you have text panels with script tags they will no longer work due to a new setting that per default disallow unsanitzied HTML. +If you have text panels with script tags they will no longer work due to a new setting that per default disallow unsanitized HTML. Read more [here](/installation/configuration/#disable-sanitize-html) about this new setting. ### Authentication and security @@ -147,4 +147,4 @@ login_maximum_inactive_lifetime_days = 1 login_maximum_lifetime_days = 1 ``` -The default cookie name for storing the auth token is `grafana_session`. you can configure this with `login_cookie_name` in `[auth]` settings. \ No newline at end of file +The default cookie name for storing the auth token is `grafana_session`. you can configure this with `login_cookie_name` in `[auth]` settings. From d6449ee629ce10b79aa822c80e7a42154533a4d1 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Thu, 7 Mar 2019 09:36:40 +0100 Subject: [PATCH 165/244] fix: Logo goes Home instead of toggling side menu #15482 --- .../components/sidemenu/SideMenu.test.tsx | 16 --------------- .../app/core/components/sidemenu/SideMenu.tsx | 20 +++++-------------- public/app/core/services/context_srv.ts | 10 +--------- public/app/routes/GrafanaCtrl.ts | 5 ----- 4 files changed, 6 insertions(+), 45 deletions(-) diff --git a/public/app/core/components/sidemenu/SideMenu.test.tsx b/public/app/core/components/sidemenu/SideMenu.test.tsx index f7b7df69cfc..6352833490a 100644 --- a/public/app/core/components/sidemenu/SideMenu.test.tsx +++ b/public/app/core/components/sidemenu/SideMenu.test.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { shallow } from 'enzyme'; import { SideMenu } from './SideMenu'; import appEvents from '../../app_events'; -import { contextSrv } from 'app/core/services/context_srv'; jest.mock('../../app_events', () => ({ emit: jest.fn(), @@ -26,7 +25,6 @@ jest.mock('app/core/services/context_srv', () => ({ isGrafanaAdmin: false, isEditor: false, hasEditPermissionFolders: false, - toggleSideMenu: jest.fn(), }, })); @@ -54,20 +52,6 @@ describe('Render', () => { }); describe('Functions', () => { - describe('toggle side menu', () => { - const wrapper = setup(); - const instance = wrapper.instance() as SideMenu; - instance.toggleSideMenu(); - - it('should call contextSrv.toggleSideMenu', () => { - expect(contextSrv.toggleSideMenu).toHaveBeenCalled(); - }); - - it('should emit toggle sidemenu event', () => { - expect(appEvents.emit).toHaveBeenCalledWith('toggle-sidemenu'); - }); - }); - describe('toggle side menu on mobile', () => { const wrapper = setup(); const instance = wrapper.instance() as SideMenu; diff --git a/public/app/core/components/sidemenu/SideMenu.tsx b/public/app/core/components/sidemenu/SideMenu.tsx index 1428ae181f5..b0ef053f746 100644 --- a/public/app/core/components/sidemenu/SideMenu.tsx +++ b/public/app/core/components/sidemenu/SideMenu.tsx @@ -1,31 +1,21 @@ import React, { PureComponent } from 'react'; import appEvents from '../../app_events'; -import { contextSrv } from 'app/core/services/context_srv'; import TopSection from './TopSection'; import BottomSection from './BottomSection'; -import { store } from 'app/store/store'; +import config from 'app/core/config'; + +const homeUrl = config.appSubUrl || '/'; export class SideMenu extends PureComponent { - toggleSideMenu = () => { - // ignore if we just made a location change, stops hiding sidemenu on double clicks of back button - const timeSinceLocationChanged = new Date().getTime() - store.getState().location.lastUpdated; - if (timeSinceLocationChanged < 1000) { - return; - } - - contextSrv.toggleSideMenu(); - appEvents.emit('toggle-sidemenu'); - }; - toggleSideMenuSmallBreakpoint = () => { appEvents.emit('toggle-sidemenu-mobile'); }; render() { return [ -
+ Grafana -
, + ,
diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts index 7bb753e6f71..b186d1fde87 100644 --- a/public/app/core/services/context_srv.ts +++ b/public/app/core/services/context_srv.ts @@ -1,7 +1,6 @@ import config from 'app/core/config'; import _ from 'lodash'; import coreModule from 'app/core/core_module'; -import store from 'app/core/store'; export class User { isGrafanaAdmin: any; @@ -29,13 +28,11 @@ export class ContextSrv { isSignedIn: any; isGrafanaAdmin: any; isEditor: any; - sidemenu: any; + sidemenu = true; sidemenuSmallBreakpoint = false; hasEditPermissionInFolders: boolean; constructor() { - this.sidemenu = store.getBool('grafana.sidemenu', true); - if (!config.bootData) { config.bootData = { user: {}, settings: {} }; } @@ -55,11 +52,6 @@ export class ContextSrv { return !!(document.visibilityState === undefined || document.visibilityState === 'visible'); } - toggleSideMenu() { - this.sidemenu = !this.sidemenu; - store.set('grafana.sidemenu', this.sidemenu); - } - hasAccessToExplore() { return (this.isEditor || config.viewersCanEdit) && config.exploreEnabled; } diff --git a/public/app/routes/GrafanaCtrl.ts b/public/app/routes/GrafanaCtrl.ts index 479c5e77f3d..45a06706cd9 100644 --- a/public/app/routes/GrafanaCtrl.ts +++ b/public/app/routes/GrafanaCtrl.ts @@ -116,11 +116,6 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop sidemenuOpen = scope.contextSrv.sidemenu; body.toggleClass('sidemenu-open', sidemenuOpen); - appEvents.on('toggle-sidemenu', () => { - sidemenuOpen = scope.contextSrv.sidemenu; - body.toggleClass('sidemenu-open'); - }); - appEvents.on('toggle-sidemenu-mobile', () => { body.toggleClass('sidemenu-open--xs'); }); From 06e9c116af053422904fdf59f63f5194faf7b591 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Thu, 7 Mar 2019 09:54:39 +0100 Subject: [PATCH 166/244] fix: Update test snapshot --- .../sidemenu/__snapshots__/SideMenu.test.tsx.snap | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/core/components/sidemenu/__snapshots__/SideMenu.test.tsx.snap b/public/app/core/components/sidemenu/__snapshots__/SideMenu.test.tsx.snap index ec2fa845c6d..8d23cdc1565 100644 --- a/public/app/core/components/sidemenu/__snapshots__/SideMenu.test.tsx.snap +++ b/public/app/core/components/sidemenu/__snapshots__/SideMenu.test.tsx.snap @@ -2,16 +2,16 @@ exports[`Render should render component 1`] = ` Array [ -
Grafana -
, + ,
Date: Thu, 7 Mar 2019 10:32:36 +0100 Subject: [PATCH 167/244] fix: Make sure we dont add &autofitpanels to the url if it already exists #15849 --- public/app/core/services/keybindingSrv.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index da096f261c6..2473c1b8e36 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -4,6 +4,7 @@ import _ from 'lodash'; import coreModule from 'app/core/core_module'; import appEvents from 'app/core/app_events'; import { getExploreUrl } from 'app/core/utils/explore'; +import { store } from 'app/store/store'; import Mousetrap from 'mousetrap'; import 'mousetrap-global-bind'; @@ -294,7 +295,9 @@ export class KeybindingSrv { //Autofit panels this.bind('d a', () => { // this has to be a full page reload - window.location.href = window.location.href + '&autofitpanels'; + const queryParams = store.getState().location.query; + const newUrlParam = queryParams.autofitpanels ? '' : '&autofitpanels'; + window.location.href = window.location.href + newUrlParam; }); } } From d937f1ff2110612c92d33e4123945ed6d3c36d1c Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Thu, 7 Mar 2019 10:52:24 +0100 Subject: [PATCH 168/244] fix: Update error message and replace npm with yarn #15851 --- public/views/index-template.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/views/index-template.html b/public/views/index-template.html index 895b0e4ae19..ad28821230f 100644 --- a/public/views/index-template.html +++ b/public/views/index-template.html @@ -179,8 +179,7 @@

1. This could be caused by your reverse proxy settings.

2. If you host grafana under subpath make sure your grafana.ini root_url setting includes subpath

- 3. If you have a local dev build make sure you build frontend using: npm run dev, npm run watch, or npm run - build

+ 3. If you have a local dev build make sure you build frontend using: yarn start, yarn start:hot, or yarn build

4. Sometimes restarting grafana-server can help

From da49fecc30d5fe181dc03649a6fa6c063a02a952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Mar 2019 10:55:16 +0100 Subject: [PATCH 169/244] Updated templates --- .github/ISSUE_TEMPLATE.md | 16 --------- .github/ISSUE_TEMPLATE/1-bug_report.md | 40 ++++++++++----------- .github/ISSUE_TEMPLATE/2-feature_request.md | 27 ++++---------- .github/ISSUE_TEMPLATE/3-accessibility.md | 26 ++++++++++++++ .github/ISSUE_TEMPLATE/3-question.md | 10 ------ .github/ISSUE_TEMPLATE/4-question.md | 14 ++++++++ 6 files changed, 65 insertions(+), 68 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/3-accessibility.md delete mode 100644 .github/ISSUE_TEMPLATE/3-question.md create mode 100644 .github/ISSUE_TEMPLATE/4-question.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 8086a6b86e5..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,16 +0,0 @@ -Read before posting: - -- Questions should be posted to https://community.grafana.com. Please search there and here on GitHub for similar issues before creating a new issue. -- Checkout FAQ: https://community.grafana.com/c/howto/faq -- Checkout How to troubleshoot metric query issues: https://community.grafana.com/t/how-to-troubleshoot-metric-query-issues/50 - -Please include this information: -### What Grafana version are you using? -### What datasource are you using? -### What OS are you running grafana on? -### What did you do? -### What was the expected result? -### What happened instead? -### If related to metric query / data viz: -### Include raw network request & response: get by opening Chrome Dev Tools (F12, Ctrl+Shift+I on windows, Cmd+Opt+I on Mac), go the network tab. - diff --git a/.github/ISSUE_TEMPLATE/1-bug_report.md b/.github/ISSUE_TEMPLATE/1-bug_report.md index 4414278becc..544ddab89e9 100644 --- a/.github/ISSUE_TEMPLATE/1-bug_report.md +++ b/.github/ISSUE_TEMPLATE/1-bug_report.md @@ -1,31 +1,29 @@ --- name: Bug report -about: Create a report to help us improve +about: Report a bug you found when using Grafana title: '' -labels: '' +labels: 'type: bug' assignees: '' - --- -Read before posting: + -- Questions should be posted to https://community.grafana.com -- Search/filter through opened and closed issues for existing similar bug reports: https://github.com/grafana/grafana/issues?utf8=%E2%9C%93&q=is%3Aissue -- Checkout FAQ: https://community.grafana.com/c/howto/faq -- Checkout How to troubleshoot metric query issues: https://community.grafana.com/t/how-to-troubleshoot-metric-query-issues/50 -- Checkout Using Grafana’s Query Inspector to troubleshoot issues: https://community.grafana.com/t/using-grafanas-query-inspector-to-troubleshoot-issues/2630 +**What happened**: -Please answer and include relevant information below: +**What you expected to happen**: -### What Grafana version are you using? -### What datasource are you using? -### What OS are you running grafana on? -### What did you do? -### What was the expected result? -### What happened instead? -### If related to metric query / data viz -### Include raw network request & response -See Using Grafana’s Query Inspector to troubleshoot issues and/or How to troubleshoot metric query issues above. +**How to reproduce it (as minimally and precisely as possible)**: -### Additional context -Add any other relevant context, like browser, platform and/or screenshots about the bug report here. +**Anything else we need to know?**: + +**Environment**: +- Grafana version: +- Data source type & version: +- OS Grafana is installed on: +- User OS & Browser: +- Grafana plugins: +- Others: diff --git a/.github/ISSUE_TEMPLATE/2-feature_request.md b/.github/ISSUE_TEMPLATE/2-feature_request.md index b0637ebf46f..17e72c6f1ca 100644 --- a/.github/ISSUE_TEMPLATE/2-feature_request.md +++ b/.github/ISSUE_TEMPLATE/2-feature_request.md @@ -1,28 +1,13 @@ --- -name: Feature request -about: Suggest an idea for this project +name: Enhancement Request +about: Suggest an enhancement or new feature for the Grafana project title: '' -labels: '' +labels: 'type: feature request' assignees: '' - --- -Read before posting: + -- Questions should be posted to https://community.grafana.com -- Search through opened and closed issues for existing similar feature requests: https://github.com/grafana/grafana/issues?utf8=%E2%9C%93&q=is%3Aissue -- Write a short and descriptive title describing your feature, e.g. Support X in area Y +**What would you like to be added**: -Please answer and include below information: - -### Is your feature request related to a problem? Please describe. -A clear and concise description of what the problem is, e.g. I'm always frustrated when [...] - -### Describe possible solutions you'd like -A clear and concise description of what you want to happen. - -### Describe alternatives you've considered -A clear and concise description of any alternative solutions or features you've considered. - -### Additional context -Add any other relevant context, like Grafana version, browser, platform and/or screenshots about the feature request here. \ No newline at end of file +**Why is this needed**: diff --git a/.github/ISSUE_TEMPLATE/3-accessibility.md b/.github/ISSUE_TEMPLATE/3-accessibility.md new file mode 100644 index 00000000000..c8a2d899ff6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/3-accessibility.md @@ -0,0 +1,26 @@ +--- +name: Accessibility problem or improvement request +about: Help make Grafana be better at keyboard navigation, screen-readable and accessible to all. +labels: 'type: accessibility' +--- + + + +**Steps to reproduce**: + +**Actual Result**: + +**Expected Result** + +**Relevant WCAG Criteria:** [#.#.# WCAG Criterion](link to https://www.w3.org/WAI/WCAG21/quickref/?versions=2.0) + +**Environment**: +- Grafana version: +- Data source type & version: +- User OS & Browser: +- Others: diff --git a/.github/ISSUE_TEMPLATE/3-question.md b/.github/ISSUE_TEMPLATE/3-question.md deleted file mode 100644 index adaf6098a28..00000000000 --- a/.github/ISSUE_TEMPLATE/3-question.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Question -about: 'Questions should be posted to https://community.grafana.com/' -title: '' -labels: '' -assignees: '' - ---- - -Please ask questions on our community site, https://community.grafana.com/. Github are mainly for feature requests and bug reports. diff --git a/.github/ISSUE_TEMPLATE/4-question.md b/.github/ISSUE_TEMPLATE/4-question.md new file mode 100644 index 00000000000..7ee98327e4c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/4-question.md @@ -0,0 +1,14 @@ +--- +name: Question / Support request +about: 'Question or support request relating to using Grafana' +title: '' +labels: '' +assignees: '' +--- + +STOP -- PLEASE READ! + +GitHub is not the right place for questions and support requests. + +Please ask questions on our community site: [https://community.grafana.com/](https://community.grafana.com/) + From ec1bb7b995dfddb4dfa613d3829f4fdad4742996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Mar 2019 11:07:01 +0100 Subject: [PATCH 170/244] Updated issue templates --- .github/ISSUE_TEMPLATE/1-bug_report.md | 4 +--- .github/ISSUE_TEMPLATE/2-feature_request.md | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/1-bug_report.md b/.github/ISSUE_TEMPLATE/1-bug_report.md index 544ddab89e9..eb87a7fc1c9 100644 --- a/.github/ISSUE_TEMPLATE/1-bug_report.md +++ b/.github/ISSUE_TEMPLATE/1-bug_report.md @@ -1,15 +1,13 @@ --- name: Bug report about: Report a bug you found when using Grafana -title: '' labels: 'type: bug' -assignees: '' --- **What happened**: diff --git a/.github/ISSUE_TEMPLATE/2-feature_request.md b/.github/ISSUE_TEMPLATE/2-feature_request.md index 17e72c6f1ca..0a8b2a63a72 100644 --- a/.github/ISSUE_TEMPLATE/2-feature_request.md +++ b/.github/ISSUE_TEMPLATE/2-feature_request.md @@ -1,9 +1,7 @@ --- name: Enhancement Request about: Suggest an enhancement or new feature for the Grafana project -title: '' labels: 'type: feature request' -assignees: '' --- From 067f0561d6c07413a9fe653c74fa680597bfc6e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Mar 2019 11:09:16 +0100 Subject: [PATCH 171/244] Updated issue template titles --- .github/ISSUE_TEMPLATE/3-accessibility.md | 2 +- .github/ISSUE_TEMPLATE/4-question.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/3-accessibility.md b/.github/ISSUE_TEMPLATE/3-accessibility.md index c8a2d899ff6..51b3ecc255b 100644 --- a/.github/ISSUE_TEMPLATE/3-accessibility.md +++ b/.github/ISSUE_TEMPLATE/3-accessibility.md @@ -1,5 +1,5 @@ --- -name: Accessibility problem or improvement request +name: Accessibility issue about: Help make Grafana be better at keyboard navigation, screen-readable and accessible to all. labels: 'type: accessibility' --- diff --git a/.github/ISSUE_TEMPLATE/4-question.md b/.github/ISSUE_TEMPLATE/4-question.md index 7ee98327e4c..3e3a20e49ed 100644 --- a/.github/ISSUE_TEMPLATE/4-question.md +++ b/.github/ISSUE_TEMPLATE/4-question.md @@ -1,5 +1,5 @@ --- -name: Question / Support request +name: Support request about: 'Question or support request relating to using Grafana' title: '' labels: '' From 708fd6df21ce86add7593677d58e223a90c3019c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Mar 2019 11:12:43 +0100 Subject: [PATCH 172/244] Removed title case from issue template title --- .github/ISSUE_TEMPLATE/2-feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/2-feature_request.md b/.github/ISSUE_TEMPLATE/2-feature_request.md index 0a8b2a63a72..ccb9f3cc391 100644 --- a/.github/ISSUE_TEMPLATE/2-feature_request.md +++ b/.github/ISSUE_TEMPLATE/2-feature_request.md @@ -1,5 +1,5 @@ --- -name: Enhancement Request +name: Enhancement request about: Suggest an enhancement or new feature for the Grafana project labels: 'type: feature request' --- From 8892b933e8fbbf8e0158b5eb3c318605a588da2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Mar 2019 11:41:07 +0100 Subject: [PATCH 173/244] Updated pull request rtemplate --- .github/PULL_REQUEST_TEMPLATE.md | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 69ce3168730..4420908c6b8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,26 @@ -* Follow the contribution guidelines in [`CONTRIBUTING.md`](https://github.com/grafana/grafana/blob/master/CONTRIBUTING.md) -* Rebase your PR if it gets out of sync with master -* Include `closes #` or a link to the issue in the description + + +**What this PR does / why we need it**: + +**Which issue(s) this PR fixes**: + +Fixes # + +**Special notes for your reviewer**: + +**Release note**: + +```release-note + +``` From f71d45b1c172de332a30cbdc9fb68defd3beb9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Mar 2019 12:10:25 +0100 Subject: [PATCH 174/244] Updated pull request template --- .github/PULL_REQUEST_TEMPLATE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4420908c6b8..5ecbc8397df 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,7 +3,8 @@ 1. If this is your first time, please read our [`CONTRIBUTING.md`](https://github.com/grafana/grafana/blob/master/CONTRIBUTING.md) guide. 2. Ensure you have added or ran the appropriate tests for your PR. 3. If it's a new feature or config option it will need a docs update. Docs are under the docs folder in repo root. -6. If the PR is unfinished, mark it as a draft PR. +4. If the PR is unfinished, mark it as a draft PR. +5. Rebase your PR if it gets out of sync with master --> **What this PR does / why we need it**: From 312ce88e2528b8f6ef04b2b0299d4a54981c595c Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 7 Mar 2019 13:12:10 +0100 Subject: [PATCH 175/244] Update core:start cli command to watch theme changes again (#15856) --- scripts/cli/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cli/index.ts b/scripts/cli/index.ts index ced56e1eacd..301592315bc 100644 --- a/scripts/cli/index.ts +++ b/scripts/cli/index.ts @@ -16,7 +16,7 @@ program .description('Starts Grafana front-end in development mode with watch enabled') .action(async cmd => { await execTask(startTask)({ - watchThemes: cmd.theme, + watchThemes: cmd.watchTheme, hot: cmd.hot, }); }); From c5f423970397fc6c4adf3a4de6255d9eb29d9220 Mon Sep 17 00:00:00 2001 From: ijin08 Date: Thu, 7 Mar 2019 15:37:17 +0100 Subject: [PATCH 176/244] changed root font to 100%(default 16px), changed font-size from px to rem, updated rem sizes in template and default.ts files, removed display classes and variables since not used, removed lead class and variables since not usedremoved serif font since not used and probably never should be used --- .../src/themes/_variables.scss.tmpl.ts | 66 ++++++++----------- packages/grafana-ui/src/themes/default.ts | 24 +++---- packages/grafana-ui/src/types/theme.ts | 2 +- public/sass/_variables.generated.scss | 52 ++++++--------- public/sass/base/_type.scss | 23 ------- public/sass/mixins/_mixins.scss | 9 --- 6 files changed, 58 insertions(+), 118 deletions(-) diff --git a/packages/grafana-ui/src/themes/_variables.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.scss.tmpl.ts index ca210902ced..260f165f499 100644 --- a/packages/grafana-ui/src/themes/_variables.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.scss.tmpl.ts @@ -17,7 +17,7 @@ $enable-hover-media-query: false !default; // Control the default styling of most Bootstrap elements by modifying these // variables. Mostly focused on spacing. -$spacer: 1rem !default; +$spacer: .875rem !default; $spacer-x: $spacer !default; $spacer-y: $spacer !default; $spacers: ( @@ -84,46 +84,32 @@ $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: ${theme.typography.fontFamily.sansSerif}; +$font-family-monospace: ${theme.typography.fontFamily.monospace}; $font-family-base: $font-family-sans-serif !default; -$font-size-root: 14px !default; -$font-size-base: 13px !default; +$font-size-root: ${theme.typography.size.root} !default; +$font-size-base: ${theme.typography.size.base} !default; -$font-size-lg: 18px !default; -$font-size-md: 14px !default; -$font-size-sm: 12px !default; -$font-size-xs: 10px !default; +$font-size-lg: ${theme.typography.size.l} !default; +$font-size-md: ${theme.typography.size.m} !default; +$font-size-sm: ${theme.typography.size.s} !default; +$font-size-xs: ${theme.typography.size.xs} !default; -$line-height-base: 1.5 !default; -$font-weight-semi-bold: 500; +$line-height-base: ${theme.typography.lineHeight.l} !default; +$font-weight-semi-bold: ${theme.typography.weight.semibold}; -$font-size-h1: 2rem !default; -$font-size-h2: 1.75rem !default; -$font-size-h3: 1.5rem !default; -$font-size-h4: 1.3rem !default; -$font-size-h5: 1.2rem !default; -$font-size-h6: 1rem !default; - -$display1-size: 6rem !default; -$display2-size: 5.5rem !default; -$display3-size: 4.5rem !default; -$display4-size: 3.5rem !default; - -$display1-weight: 400 !default; -$display2-weight: 400 !default; -$display3-weight: 400 !default; -$display4-weight: 400 !default; - -$lead-font-size: 1.25rem !default; -$lead-font-weight: 300 !default; +$font-size-h1: ${theme.typography.heading.h1} !default; +$font-size-h2: ${theme.typography.heading.h2} !default; +$font-size-h3: ${theme.typography.heading.h3} !default; +$font-size-h4: ${theme.typography.heading.h4} !default; +$font-size-h5: ${theme.typography.heading.h5} !default; +$font-size-h6: ${theme.typography.heading.h6} !default; $headings-margin-bottom: ($spacer / 2) !default; $headings-font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; -$headings-font-weight: 400 !default; -$headings-line-height: 1.1 !default; +$headings-font-weight: ${theme.typography.weight.normal} !default; +$headings-line-height: ${theme.typography.lineHeight.s} !default; $hr-border-width: $border-width !default; $dt-font-weight: bold !default; @@ -141,8 +127,8 @@ $border-radius-sm: 2px !default; // Page -$page-sidebar-width: 11rem; -$page-sidebar-margin: 4rem; +$page-sidebar-width: 9.625rem; +$page-sidebar-margin: 3.5rem; // Links // ------------------------- @@ -174,7 +160,7 @@ $input-padding-y-lg: 10px !default; $input-height: 35px !default; -$gf-form-margin: 0.2rem; +$gf-form-margin: 0.175rem; $gf-form-input-height: 35px; $cursor-disabled: not-allowed !default; @@ -199,12 +185,12 @@ $zindex-typeahead: 1060; // Buttons // -$btn-padding-x: 1rem !default; -$btn-padding-y: 0.7rem !default; +$btn-padding-x: .875rem !default; +$btn-padding-y: 0.6125rem !default; $btn-line-height: 1 !default; -$btn-font-weight: 500 !default; +$btn-font-weight: ${theme.typography.weight.semibold} !default; -$btn-padding-x-sm: 0.5rem !default; +$btn-padding-x-sm: 0.4375rem !default; $btn-padding-y-sm: 0.25rem !default; $btn-padding-x-lg: 21px !default; diff --git a/packages/grafana-ui/src/themes/default.ts b/packages/grafana-ui/src/themes/default.ts index 104edced000..72f09ea1325 100644 --- a/packages/grafana-ui/src/themes/default.ts +++ b/packages/grafana-ui/src/themes/default.ts @@ -5,23 +5,23 @@ const theme: GrafanaThemeCommons = { typography: { fontFamily: { sansSerif: "'Roboto', Helvetica, Arial, sans-serif", - serif: "Georgia, 'Times New Roman', Times, serif", monospace: "Menlo, Monaco, Consolas, 'Courier New', monospace", }, size: { - base: '13px', - xs: '10px', - s: '12px', - m: '14px', - l: '18px', + root: '100%', + base: '.8125rem', + xs: '.625rem', + s: '.75rem', + m: '.875rem', + l: '1.125rem', }, heading: { - h1: '2rem', - h2: '1.75rem', - h3: '1.5rem', - h4: '1.3rem', - h5: '1.2rem', - h6: '1rem', + h1: '1.75rem', + h2: '1.5rem', + h3: '1.3125rem', + h4: '1.125rem', + h5: '1rem', + h6: '.875rem', }, weight: { light: 300, diff --git a/packages/grafana-ui/src/types/theme.ts b/packages/grafana-ui/src/types/theme.ts index 30f1bf4685b..e3a73d90ea7 100644 --- a/packages/grafana-ui/src/types/theme.ts +++ b/packages/grafana-ui/src/types/theme.ts @@ -16,10 +16,10 @@ export interface GrafanaThemeCommons { typography: { fontFamily: { sansSerif: string; - serif: string; monospace: string; }; size: { + root: string; base: string; xs: string; s: string; diff --git a/public/sass/_variables.generated.scss b/public/sass/_variables.generated.scss index 713bfb7a336..a6f3c874aa1 100644 --- a/public/sass/_variables.generated.scss +++ b/public/sass/_variables.generated.scss @@ -20,7 +20,7 @@ $enable-hover-media-query: false !default; // Control the default styling of most Bootstrap elements by modifying these // variables. Mostly focused on spacing. -$spacer: 1rem !default; +$spacer: 0.875rem !default; $spacer-x: $spacer !default; $spacer-y: $spacer !default; $spacers: ( @@ -88,40 +88,26 @@ $enable-flex: true; // ------------------------- $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; -$font-size-base: 13px !default; +$font-size-root: 100% !default; +$font-size-base: 0.8125rem !default; -$font-size-lg: 18px !default; -$font-size-md: 14px !default; -$font-size-sm: 12px !default; -$font-size-xs: 10px !default; +$font-size-lg: 1.125rem !default; +$font-size-md: 0.875rem !default; +$font-size-sm: 0.75rem !default; +$font-size-xs: 0.625rem !default; $line-height-base: 1.5 !default; $font-weight-semi-bold: 500; -$font-size-h1: 2rem !default; -$font-size-h2: 1.75rem !default; -$font-size-h3: 1.5rem !default; -$font-size-h4: 1.3rem !default; -$font-size-h5: 1.2rem !default; -$font-size-h6: 1rem !default; - -$display1-size: 6rem !default; -$display2-size: 5.5rem !default; -$display3-size: 4.5rem !default; -$display4-size: 3.5rem !default; - -$display1-weight: 400 !default; -$display2-weight: 400 !default; -$display3-weight: 400 !default; -$display4-weight: 400 !default; - -$lead-font-size: 1.25rem !default; -$lead-font-weight: 300 !default; +$font-size-h1: 1.75rem !default; +$font-size-h2: 1.5rem !default; +$font-size-h3: 1.3125rem !default; +$font-size-h4: 1.125rem !default; +$font-size-h5: 1rem !default; +$font-size-h6: 0.875rem !default; $headings-margin-bottom: ($spacer / 2) !default; $headings-font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; @@ -144,8 +130,8 @@ $border-radius-sm: 2px !default; // Page -$page-sidebar-width: 11rem; -$page-sidebar-margin: 4rem; +$page-sidebar-width: 9.625rem; +$page-sidebar-margin: 3.5rem; // Links // ------------------------- @@ -177,7 +163,7 @@ $input-padding-y-lg: 10px !default; $input-height: 35px !default; -$gf-form-margin: 0.2rem; +$gf-form-margin: 0.175rem; $gf-form-input-height: 35px; $cursor-disabled: not-allowed !default; @@ -202,12 +188,12 @@ $zindex-typeahead: 1060; // Buttons // -$btn-padding-x: 1rem !default; -$btn-padding-y: 0.7rem !default; +$btn-padding-x: 0.875rem !default; +$btn-padding-y: 0.6125rem !default; $btn-line-height: 1 !default; $btn-font-weight: 500 !default; -$btn-padding-x-sm: 0.5rem !default; +$btn-padding-x-sm: 0.4375rem !default; $btn-padding-y-sm: 0.25rem !default; $btn-padding-x-lg: 21px !default; diff --git a/public/sass/base/_type.scss b/public/sass/base/_type.scss index ea24116795a..ab4fbc33a55 100644 --- a/public/sass/base/_type.scss +++ b/public/sass/base/_type.scss @@ -141,29 +141,6 @@ h6, font-size: $font-size-h6; } -.lead { - font-size: $lead-font-size; - font-weight: $lead-font-weight; -} - -// Type display classes -.display-1 { - font-size: $display1-size; - font-weight: $display1-weight; -} -.display-2 { - font-size: $display2-size; - font-weight: $display2-weight; -} -.display-3 { - font-size: $display3-size; - font-weight: $display3-weight; -} -.display-4 { - font-size: $display4-size; - font-weight: $display4-weight; -} - // // Horizontal rules // diff --git a/public/sass/mixins/_mixins.scss b/public/sass/mixins/_mixins.scss index 89285cc2496..298ea0c64ad 100644 --- a/public/sass/mixins/_mixins.scss +++ b/public/sass/mixins/_mixins.scss @@ -79,10 +79,6 @@ // FONTS // -------------------------------------------------- -@mixin font-family-serif() { - font-family: $font-family-serif; -} - @mixin font-family-sans-serif() { font-family: $font-family-sans-serif; } @@ -97,11 +93,6 @@ line-height: $lineHeight; } -@mixin font-serif($size: $font-size-base, $weight: normal, $lineHeight: $line-height-base) { - @include font-family-serif(); - @include font-shorthand($size, $weight, $lineHeight); -} - @mixin font-sans-serif($size: $font-size-base, $weight: normal, $lineHeight: $line-height-base) { @include font-family-sans-serif(); @include font-shorthand($size, $weight, $lineHeight); From c104f31149cb3b0bea25aade7ad0860ddb8984b8 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 7 Mar 2019 18:57:18 +0300 Subject: [PATCH 177/244] heatmap: fix legend for small values, #14019 #15683 --- public/app/plugins/panel/heatmap/color_legend.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index c36fad45cba..a62589a6bf9 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -95,10 +95,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendHeight = legendElem.attr('height'); - let rangeStep = 1; - if (rangeTo - rangeFrom > legendWidth) { - rangeStep = Math.floor((rangeTo - rangeFrom) / legendWidth); - } + const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * 2; const widthFactor = legendWidth / (rangeTo - rangeFrom); const valuesRange = d3.range(rangeFrom, rangeTo, rangeStep); @@ -115,7 +112,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal .attr('stroke-width', 0) .attr('fill', d => colorScale(d)); - drawLegendValues(elem, colorScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth); + drawLegendValues(elem, rangeFrom, rangeTo, maxValue, minValue, legendWidth, valuesRange); } function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue) { @@ -126,10 +123,7 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendHeight = legendElem.attr('height'); - let rangeStep = 1; - if (rangeTo - rangeFrom > legendWidth) { - rangeStep = Math.floor((rangeTo - rangeFrom) / legendWidth); - } + const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * 2; const widthFactor = legendWidth / (rangeTo - rangeFrom); const valuesRange = d3.range(rangeFrom, rangeTo, rangeStep); @@ -147,10 +141,10 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue .attr('fill', options.cardColor) .style('opacity', d => opacityScale(d)); - drawLegendValues(elem, opacityScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth); + drawLegendValues(elem, rangeFrom, rangeTo, maxValue, minValue, legendWidth, valuesRange); } -function drawLegendValues(elem, colorScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth) { +function drawLegendValues(elem, rangeFrom, rangeTo, maxValue, minValue, legendWidth, valuesRange) { const legendElem = $(elem).find('svg'); const legend = d3.select(legendElem.get(0)); From 55d5219a053cbe294b3168f5f749d3de83aade4b Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 7 Mar 2019 19:49:14 +0300 Subject: [PATCH 178/244] heatmap: fix legend padding --- public/app/plugins/panel/heatmap/color_legend.ts | 9 ++++++++- public/sass/components/_panel_heatmap.scss | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index a62589a6bf9..b397d0b3eee 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -11,6 +11,7 @@ const LEGEND_HEIGHT_PX = 6; const LEGEND_WIDTH_PX = 100; const LEGEND_TICK_SIZE = 0; const LEGEND_VALUE_MARGIN = 0; +const LEGEND_PADDING_LEFT = 10; /** * Color legend for heatmap editor. @@ -101,6 +102,9 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal const colorScale = getColorScale(colorScheme, contextSrv.user.lightTheme, maxValue, minValue); legend + .append('g') + .attr('class', 'legend-color-bar') + .attr('transform', 'translate(' + LEGEND_PADDING_LEFT + ',0)') .selectAll('.heatmap-color-legend-rect') .data(valuesRange) .enter() @@ -129,6 +133,9 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue const opacityScale = getOpacityScale(options, maxValue, minValue); legend + .append('g') + .attr('class', 'legend-color-bar') + .attr('transform', 'translate(' + LEGEND_PADDING_LEFT + ',0)') .selectAll('.heatmap-opacity-legend-rect') .data(valuesRange) .enter() @@ -165,7 +172,7 @@ function drawLegendValues(elem, rangeFrom, rangeTo, maxValue, minValue, legendWi const colorRect = legendElem.find(':first-child'); const posY = getSvgElemHeight(legendElem) + LEGEND_VALUE_MARGIN; - const posX = getSvgElemX(colorRect); + const posX = getSvgElemX(colorRect) + LEGEND_PADDING_LEFT; d3.select(legendElem.get(0)) .append('g') diff --git a/public/sass/components/_panel_heatmap.scss b/public/sass/components/_panel_heatmap.scss index 279b9392caa..dad1dc3235b 100644 --- a/public/sass/components/_panel_heatmap.scss +++ b/public/sass/components/_panel_heatmap.scss @@ -66,7 +66,6 @@ $font-size-heatmap-tick: 11px; height: 18px; float: left; white-space: nowrap; - padding-left: 10px; } .heatmap-legend-values { From 7167fa9d07e9f23307b37bf949cb32ec1ef7d432 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 7 Mar 2019 20:05:32 +0300 Subject: [PATCH 179/244] heatmap: reduce number of legend segments to reasonable value and round x values to prevent gaps --- public/app/plugins/panel/heatmap/color_legend.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index b397d0b3eee..4b4926c01bd 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -12,6 +12,7 @@ const LEGEND_WIDTH_PX = 100; const LEGEND_TICK_SIZE = 0; const LEGEND_VALUE_MARGIN = 0; const LEGEND_PADDING_LEFT = 10; +const LEGEND_SEGMENT_WIDTH = 10; /** * Color legend for heatmap editor. @@ -96,7 +97,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendHeight = legendElem.attr('height'); - const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * 2; + const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH; const widthFactor = legendWidth / (rangeTo - rangeFrom); const valuesRange = d3.range(rangeFrom, rangeTo, rangeStep); @@ -109,9 +110,9 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal .data(valuesRange) .enter() .append('rect') - .attr('x', d => d * widthFactor) + .attr('x', d => Math.round(d * widthFactor)) .attr('y', 0) - .attr('width', rangeStep * widthFactor + 1) // Overlap rectangles to prevent gaps + .attr('width', Math.round(rangeStep * widthFactor + 1)) // Overlap rectangles to prevent gaps .attr('height', legendHeight) .attr('stroke-width', 0) .attr('fill', d => colorScale(d)); @@ -127,7 +128,7 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendHeight = legendElem.attr('height'); - const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * 2; + const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH; const widthFactor = legendWidth / (rangeTo - rangeFrom); const valuesRange = d3.range(rangeFrom, rangeTo, rangeStep); @@ -140,9 +141,9 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue .data(valuesRange) .enter() .append('rect') - .attr('x', d => d * widthFactor) + .attr('x', d => Math.round(d * widthFactor)) .attr('y', 0) - .attr('width', rangeStep * widthFactor) + .attr('width', Math.round(rangeStep * widthFactor)) .attr('height', legendHeight) .attr('stroke-width', 0) .attr('fill', options.cardColor) From f9e3c33ebdffbd930ddcc776753a76c4254c72b2 Mon Sep 17 00:00:00 2001 From: Matt Toback Date: Thu, 7 Mar 2019 14:33:37 -0500 Subject: [PATCH 180/244] Update README.md --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 550e7facfa8..8c84bfd0e87 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,6 @@ Grafana is an open source, feature rich metrics dashboard and graph editor for Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB. -![](https://www.grafanacon.org/2019/images/grafanacon_la_nav-logo.png) - -Join us Feb 25-26 in Los Angeles, California for GrafanaCon - a two-day event with talks focused on Grafana and the surrounding open source monitoring ecosystem. Get deep dives into Loki, the Explore workflow and all of the new features of Grafana 6, plus participate in hands on workshops to help you get the most out of your data. - -Time is running out - grab your ticket now! http://grafanacon.org - From 946e54241297ef171c5bdf4199de393d38854f2f Mon Sep 17 00:00:00 2001 From: "Woodward, Joshua" Date: Thu, 7 Mar 2019 14:00:04 -0800 Subject: [PATCH 181/244] Make password hint configurable from settings/defaults.ini --- conf/defaults.ini | 1 + pkg/api/login.go | 1 + pkg/setting/setting.go | 2 ++ public/app/core/config.ts | 1 + public/app/core/controllers/login_ctrl.ts | 1 + public/app/partials/login.html | 2 +- 6 files changed, 7 insertions(+), 1 deletion(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index df02e01235b..33fe17a2aa6 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -231,6 +231,7 @@ verify_email_enabled = false # Background text for the user field on the login page login_hint = email or username +passowrd_hint = password # Default UI theme ("dark" or "light") default_theme = dark diff --git a/pkg/api/login.go b/pkg/api/login.go index 1445463852b..f6c3e802988 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -36,6 +36,7 @@ func (hs *HTTPServer) LoginView(c *m.ReqContext) { viewData.Settings["oauth"] = enabledOAuths viewData.Settings["disableUserSignUp"] = !setting.AllowUserSignUp viewData.Settings["loginHint"] = setting.LoginHint + viewData.Settings["passwordHint"] = setting.PasswordHint viewData.Settings["disableLoginForm"] = setting.DisableLoginForm if loginError, ok := tryGetEncryptedCookie(c, LoginErrorCookieName); ok { diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 5d44a3585dc..7b6e99255aa 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -109,6 +109,7 @@ var ( AutoAssignOrgRole string VerifyEmailEnabled bool LoginHint string + PasswordHint string DefaultTheme string DisableLoginForm bool DisableSignoutMenu bool @@ -656,6 +657,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { AutoAssignOrgRole = users.Key("auto_assign_org_role").In("Editor", []string{"Editor", "Admin", "Viewer"}) VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false) LoginHint = users.Key("login_hint").String() + PasswordHint = users.Key("password_hint").String() DefaultTheme = users.Key("default_theme").String() ExternalUserMngLinkUrl = users.Key("external_manage_link_url").String() ExternalUserMngLinkName = users.Key("external_manage_link_name").String() diff --git a/public/app/core/config.ts b/public/app/core/config.ts index 8fefc1aeb0a..bbf7fb88d62 100644 --- a/public/app/core/config.ts +++ b/public/app/core/config.ts @@ -34,6 +34,7 @@ export class Settings { oauth: any; disableUserSignUp: boolean; loginHint: any; + passwordHint: any; loginError: any; viewersCanEdit: boolean; editorsCanOwn: boolean; diff --git a/public/app/core/controllers/login_ctrl.ts b/public/app/core/controllers/login_ctrl.ts index de4e3415dfb..3a72379aa81 100644 --- a/public/app/core/controllers/login_ctrl.ts +++ b/public/app/core/controllers/login_ctrl.ts @@ -25,6 +25,7 @@ export class LoginCtrl { $scope.disableLoginForm = config.disableLoginForm; $scope.disableUserSignUp = config.disableUserSignUp; $scope.loginHint = config.loginHint; + $scope.passwordHint = config.passwordHint; $scope.loginMode = true; $scope.submitBtnText = 'Log in'; diff --git a/public/app/partials/login.html b/public/app/partials/login.html index 674c9581ce5..33872c7d6d5 100644 --- a/public/app/partials/login.html +++ b/public/app/partials/login.html @@ -13,7 +13,7 @@
- - {/* TODO: ); From e3b3062107841517a832a420325f7ed5dd6a3004 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 8 Mar 2019 13:31:46 +0100 Subject: [PATCH 185/244] add nil/length check when delete old login attempts --- pkg/services/sqlstore/login_attempt.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/services/sqlstore/login_attempt.go b/pkg/services/sqlstore/login_attempt.go index ceff2394dce..fe77dd7e914 100644 --- a/pkg/services/sqlstore/login_attempt.go +++ b/pkg/services/sqlstore/login_attempt.go @@ -44,6 +44,10 @@ func DeleteOldLoginAttempts(cmd *m.DeleteOldLoginAttemptsCommand) error { return err } + if result == nil || len(result) == 0 || result[0] == nil { + return nil + } + maxId = toInt64(result[0]["id"]) if maxId == 0 { From d7d968412ba087dabd3b86c0d9886266379e1b19 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 8 Mar 2019 13:46:05 +0100 Subject: [PATCH 186/244] fix typo in pr template --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5ecbc8397df..22642808fa4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -20,7 +20,7 @@ Fixes # **Release note**: ```release-note From 878da68c90688094eac50fbabab7db7ad5afbe91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 8 Mar 2019 13:49:48 +0100 Subject: [PATCH 187/244] Refactoring of PR #14772 --- public/app/core/specs/kbn.test.ts | 4 ++-- public/app/plugins/panel/graph/module.ts | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/public/app/core/specs/kbn.test.ts b/public/app/core/specs/kbn.test.ts index 25f82a5f850..c97e2e1101a 100644 --- a/public/app/core/specs/kbn.test.ts +++ b/public/app/core/specs/kbn.test.ts @@ -2,12 +2,12 @@ import kbn from '../utils/kbn'; describe('stringToJsRegex', () => { it('should parse the valid regex value', () => { - const output = kbn.stringToJsRegex("/validRegexp/"); + const output = kbn.stringToJsRegex('/validRegexp/'); expect(output).toBeInstanceOf(RegExp); }); it('should throw error on invalid regex value', () => { - const input = "/etc/hostname"; + const input = '/etc/hostname'; expect(() => { kbn.stringToJsRegex(input); }).toThrow(); diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index e3e3c4eb588..3919c4f69a9 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -235,11 +235,7 @@ class GraphCtrl extends MetricsPanelCtrl { } for (const series of this.seriesList) { - try { - series.applySeriesOverrides(this.panel.seriesOverrides); - } catch (e) { - this.publishAppEvent('alert-error', [e.message]); - } + series.applySeriesOverrides(this.panel.seriesOverrides); if (series.unit) { this.panel.yaxes[series.yaxis - 1].format = series.unit; From 74421ceb139e16c6b120d128816e33cda7665cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 8 Mar 2019 13:56:21 +0100 Subject: [PATCH 188/244] Updated prettierignore --- .prettierignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.prettierignore b/.prettierignore index b7a33870ddd..336d03e2551 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,4 +5,5 @@ pkg/ node_modules public/vendor/ vendor/ +data/ From 60272d8a77a92c408ce8a6dfc12dd882c019e4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 14 Feb 2019 18:15:51 +0100 Subject: [PATCH 189/244] Simple implementation for preserve tags, closes #11627 --- .../SaveModals/SaveDashboardAsModalCtrl.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/public/app/features/dashboard/components/SaveModals/SaveDashboardAsModalCtrl.ts b/public/app/features/dashboard/components/SaveModals/SaveDashboardAsModalCtrl.ts index 60fa031f71c..59cfa22e468 100644 --- a/public/app/features/dashboard/components/SaveModals/SaveDashboardAsModalCtrl.ts +++ b/public/app/features/dashboard/components/SaveModals/SaveDashboardAsModalCtrl.ts @@ -16,19 +16,19 @@ const template = `